More enum metaprogramming
This commit is contained in:
parent
6b63c21e03
commit
767c9d3b7a
@ -3916,6 +3916,7 @@ pub const Function = struct {
|
||||
body: Debug.Block.Index,
|
||||
return_pointer: Instruction.Index = .null,
|
||||
alloca_index: u32 = 1,
|
||||
has_debug_info: bool,
|
||||
|
||||
pub const List = BlockList(@This(), enum {});
|
||||
pub usingnamespace @This().List.Index;
|
||||
@ -3933,9 +3934,6 @@ pub const Function = struct {
|
||||
attributes: Attributes = .{},
|
||||
calling_convention: CallingConvention = .auto,
|
||||
has_polymorphic_parameters: bool = false,
|
||||
// comptime_parameter_declarations: []const ComptimeParameterDeclaration = &.{},
|
||||
// comptime_parameter_instantiations: []const V.Comptime = &.{},
|
||||
// is_member: bool = false,
|
||||
|
||||
const Attributes = struct {
|
||||
naked: bool = false,
|
||||
@ -4872,10 +4870,244 @@ pub const Builder = struct {
|
||||
}),
|
||||
};
|
||||
},
|
||||
.name => {
|
||||
assert(argument_node_list.len == 1);
|
||||
const v = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, argument_node_list[0], .right);
|
||||
switch (v.value) {
|
||||
.runtime => switch (unit.types.get(v.type).*) {
|
||||
.integer => |*integer| switch (integer.kind) {
|
||||
.@"enum" => {
|
||||
const name_function = try builder.get_name_function(unit, context, v.type);
|
||||
var args = try UnpinnedArray(V).initialize_with_capacity(context.my_allocator, 1);
|
||||
args.append_with_capacity(v);
|
||||
const call = try unit.instructions.append(context.my_allocator, .{
|
||||
.call = .{
|
||||
.callable = .{
|
||||
.value = .{
|
||||
.@"comptime" = .{
|
||||
.global = name_function,
|
||||
},
|
||||
},
|
||||
.type = name_function.declaration.type,
|
||||
},
|
||||
.function_type = name_function.declaration.type,
|
||||
.arguments = args.slice(),
|
||||
},
|
||||
});
|
||||
try builder.appendInstruction(unit, context, call);
|
||||
return V{
|
||||
.value = .{
|
||||
.runtime = call,
|
||||
},
|
||||
.type = unit.function_prototypes.get(unit.types.get(name_function.declaration.type).function).return_type,
|
||||
};
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_name_function(builder: *Builder, unit: *Unit, context: *const Context, type_index: Type.Index) !*Debug.Declaration.Global {
|
||||
if (unit.name_functions.get(type_index)) |result| return result else {
|
||||
var argument_types = try UnpinnedArray(Type.Index).initialize_with_capacity(context.my_allocator, 1);
|
||||
argument_types.append_with_capacity(type_index);
|
||||
const return_type_index = try unit.getSliceType(context, .{
|
||||
.child_pointer_type = try unit.getPointerType(context, .{
|
||||
.type = .u8,
|
||||
// TODO: zero-terminate?
|
||||
.termination = .none,
|
||||
.mutability = .@"const",
|
||||
.many = true,
|
||||
.nullable = false,
|
||||
}),
|
||||
.child_type = .u8,
|
||||
// TODO: zero-terminate?
|
||||
.termination = .none,
|
||||
.mutability = .@"const",
|
||||
.nullable = false,
|
||||
});
|
||||
const function_prototype_index = try unit.function_prototypes.append(context.my_allocator, .{
|
||||
.argument_types = argument_types.slice(),
|
||||
.return_type = return_type_index,
|
||||
.abi = .{
|
||||
.return_type = return_type_index,
|
||||
.parameter_types = argument_types.slice(),
|
||||
},
|
||||
});
|
||||
const function_type_index = try unit.types.append(context.my_allocator, .{
|
||||
.function = function_prototype_index,
|
||||
});
|
||||
const function_definition_index = try unit.function_definitions.append(context.my_allocator, .{
|
||||
.scope = .{
|
||||
.scope = .{
|
||||
.file = builder.current_file,
|
||||
.line = 0,
|
||||
.column = 0,
|
||||
.kind = .function,
|
||||
.local = true,
|
||||
.level = builder.current_scope.level + 1,
|
||||
},
|
||||
},
|
||||
.type = function_type_index,
|
||||
.body = .null,
|
||||
.has_debug_info = false,
|
||||
});
|
||||
|
||||
const function_definition = unit.function_definitions.get(function_definition_index);
|
||||
const old_scope = builder.current_scope;
|
||||
builder.current_scope = &function_definition.scope.scope;
|
||||
defer builder.current_scope = old_scope;
|
||||
|
||||
const old_function = builder.current_function;
|
||||
builder.current_function = function_definition_index;
|
||||
defer builder.current_function = old_function;
|
||||
|
||||
const old_basic_block = builder.current_basic_block;
|
||||
defer builder.current_basic_block = old_basic_block;
|
||||
|
||||
const argument_name_hash = try unit.processIdentifier(context, "_enum_value_");
|
||||
const argument_declaration_index = try unit.argument_declarations.append(context.my_allocator, .{
|
||||
.declaration = .{
|
||||
.scope = builder.current_scope,
|
||||
.name = argument_name_hash,
|
||||
.type = type_index,
|
||||
.mutability = .@"const",
|
||||
.line = 0,
|
||||
.column = 0,
|
||||
.kind = .argument,
|
||||
},
|
||||
.index = 0,
|
||||
});
|
||||
comptime assert(@TypeOf(argument_declaration_index) == Debug.Declaration.Argument.Index);
|
||||
const argument = unit.argument_declarations.get(argument_declaration_index);
|
||||
|
||||
try builder.current_scope.declarations.put_no_clobber(context.my_allocator, argument_name_hash, &argument.declaration);
|
||||
|
||||
const entry_block = try builder.newBasicBlock(unit, context);
|
||||
const exit_block = try builder.newBasicBlock(unit, context);
|
||||
builder.current_basic_block = entry_block;
|
||||
|
||||
const argument_instruction = try unit.instructions.append(context.my_allocator, .{
|
||||
.abi_argument = 0,
|
||||
});
|
||||
try builder.appendInstruction(unit, context, argument_instruction);
|
||||
const switch_instruction_index = try unit.instructions.append(context.my_allocator, .{
|
||||
.@"switch" = .{
|
||||
.condition = .{
|
||||
.value = .{
|
||||
.runtime = argument_instruction,
|
||||
},
|
||||
.type = type_index,
|
||||
},
|
||||
.block_type = return_type_index,
|
||||
},
|
||||
});
|
||||
try builder.appendInstruction(unit, context, switch_instruction_index);
|
||||
const switch_instruction = &unit.instructions.get(switch_instruction_index).@"switch";
|
||||
|
||||
const phi_instruction_index = try unit.instructions.append(context.my_allocator, .{
|
||||
.phi = .{
|
||||
.type = return_type_index,
|
||||
},
|
||||
});
|
||||
const phi = &unit.instructions.get(phi_instruction_index).phi;
|
||||
|
||||
const cases = switch (unit.types.get(type_index).*) {
|
||||
.integer => |*integer| switch (integer.kind) {
|
||||
.@"enum" => |*enum_type| b: {
|
||||
var cases = try UnpinnedArray(Instruction.Switch.Case).initialize_with_capacity(context.my_allocator, enum_type.fields.length);
|
||||
for (enum_type.fields.slice()) |enum_field_index| {
|
||||
builder.current_basic_block = entry_block;
|
||||
const enum_field = unit.enum_fields.get(enum_field_index);
|
||||
const case_block = try builder.newBasicBlock(unit, context);
|
||||
builder.current_basic_block = case_block;
|
||||
const identifier = unit.getIdentifier(enum_field.name);
|
||||
const identifier_z = try context.allocator.dupeZ(u8, identifier);
|
||||
const string_literal = try builder.processStringLiteralFromStringAndDebugInfo(unit, context, identifier_z, .{
|
||||
.line = 0,
|
||||
.column = 0,
|
||||
});
|
||||
const slice = try unit.constant_slices.append(context.my_allocator,.{
|
||||
.array = string_literal,
|
||||
.start = 0,
|
||||
.end = identifier_z.len,
|
||||
.type = return_type_index,
|
||||
});
|
||||
const v = V{
|
||||
.value = .{
|
||||
.@"comptime" = .{
|
||||
.constant_slice = slice,
|
||||
},
|
||||
},
|
||||
.type = return_type_index,
|
||||
};
|
||||
try phi.addIncoming(context, v, builder.current_basic_block);
|
||||
try builder.jump(unit, context, exit_block);
|
||||
|
||||
const case = Instruction.Switch.Case{
|
||||
.condition = .{
|
||||
.enum_value = enum_field_index,
|
||||
},
|
||||
.basic_block = case_block,
|
||||
};
|
||||
cases.append_with_capacity(case);
|
||||
}
|
||||
|
||||
break :b cases;
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
};
|
||||
|
||||
switch_instruction.cases = cases;
|
||||
switch_instruction.else_block = try builder.create_unreachable_block(unit, context);
|
||||
|
||||
builder.current_basic_block = exit_block;
|
||||
try builder.appendInstruction(unit, context, phi_instruction_index);
|
||||
|
||||
const ret = try unit.instructions.append(context.my_allocator, .{
|
||||
.ret = .{
|
||||
.type = return_type_index,
|
||||
.value = .{
|
||||
.runtime = phi_instruction_index,
|
||||
},
|
||||
},
|
||||
});
|
||||
try builder.appendInstruction(unit, context, ret);
|
||||
|
||||
const global_index = try unit.global_declarations.append(context.my_allocator, .{
|
||||
.declaration = .{
|
||||
.scope = builder.current_scope,
|
||||
.name = try unit.processIdentifier(context, try std.fmt.allocPrint(context.allocator, "get_enum_name_{}", .{@intFromEnum(type_index)})),
|
||||
.type = function_type_index,
|
||||
.line = 0,
|
||||
.column = 0,
|
||||
.mutability = .@"const",
|
||||
.kind = .global,
|
||||
},
|
||||
.initial_value = .{
|
||||
.function_definition = function_definition_index,
|
||||
},
|
||||
.type_node_index = .null,
|
||||
.attributes = .{},
|
||||
});
|
||||
|
||||
const global = unit.global_declarations.get(global_index);
|
||||
|
||||
try unit.code_to_emit.put_no_clobber(context.my_allocator, function_definition_index, global);
|
||||
try unit.name_functions.put_no_clobber(context.my_allocator, type_index, global);
|
||||
|
||||
return global;
|
||||
}
|
||||
}
|
||||
|
||||
fn get_fields_array(builder: *Builder, unit: *Unit, context: *const Context, container_type_index: Type.Index, token: Token.Index) !*Debug.Declaration.Global{
|
||||
if (unit.fields_array.get(container_type_index)) |result| return result else {
|
||||
const container_type = unit.types.get(container_type_index);
|
||||
@ -8662,6 +8894,7 @@ pub const Builder = struct {
|
||||
.file = builder.current_file,
|
||||
},
|
||||
},
|
||||
.has_debug_info = true,
|
||||
});
|
||||
|
||||
defer builder.current_function = old_function;
|
||||
@ -14626,11 +14859,7 @@ pub const Builder = struct {
|
||||
}
|
||||
|
||||
if (switch_instruction.else_block == .null) {
|
||||
switch_instruction.else_block = try builder.newBasicBlock(unit, context);
|
||||
const old_block = builder.current_basic_block;
|
||||
builder.current_basic_block = switch_instruction.else_block;
|
||||
try builder.buildUnreachable(unit, context);
|
||||
builder.current_basic_block = old_block;
|
||||
switch_instruction.else_block = try builder.create_unreachable_block(unit, context);
|
||||
}
|
||||
|
||||
if (phi_info) |phi| {
|
||||
@ -14685,6 +14914,16 @@ pub const Builder = struct {
|
||||
}
|
||||
}
|
||||
|
||||
fn create_unreachable_block(builder: *Builder, unit: *Unit, context: *const Context) !BasicBlock.Index {
|
||||
const block = try builder.newBasicBlock(unit, context);
|
||||
const old_block = builder.current_basic_block;
|
||||
builder.current_basic_block = block;
|
||||
try builder.buildUnreachable(unit, context);
|
||||
builder.current_basic_block = old_block;
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
fn resolveFieldAccess(builder: *Builder, unit: *Unit, context: *const Context, type_expect: Type.Expect, node_index: Node.Index, side: Side, new_parameters: []const V.Comptime) !V {
|
||||
const node = unit.getNode(node_index);
|
||||
const right_node = unit.getNode(node.right);
|
||||
@ -14804,6 +15043,23 @@ pub const Builder = struct {
|
||||
|
||||
break :b result;
|
||||
},
|
||||
.global => |global| switch (unit.types.get(global.declaration.type).*) {
|
||||
.array => |array| if (byte_equal(identifier, length_field_name)) switch (type_expect) {
|
||||
.none => V{
|
||||
.value = .{
|
||||
.@"comptime" = .{
|
||||
.comptime_int = .{
|
||||
.value = array.count,
|
||||
.signedness = .unsigned,
|
||||
},
|
||||
},
|
||||
},
|
||||
.type = .comptime_int,
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
} else unreachable,
|
||||
else => |t| @panic(@tagName(t)),
|
||||
},
|
||||
else => |t| @panic(@tagName(t)),
|
||||
},
|
||||
.runtime => |_| b: {
|
||||
@ -16015,6 +16271,7 @@ pub const Unit = struct {
|
||||
error_unions: MyHashMap(Type.Error.Union.Descriptor, Type.Index) = .{},
|
||||
two_structs: MyHashMap([2]Type.Index, Type.Index) = .{},
|
||||
fields_array: MyHashMap(Type.Index, *Debug.Declaration.Global) = .{},
|
||||
name_functions: MyHashMap(Type.Index, *Debug.Declaration.Global) = .{},
|
||||
error_count: u32 = 0,
|
||||
|
||||
code_to_emit: MyHashMap(Function.Definition.Index, *Debug.Declaration.Global) = .{},
|
||||
|
@ -2270,29 +2270,6 @@ pub const LLVM = struct {
|
||||
try llvm.setCallOrFunctionAttributes(unit, context, function_prototype, .{
|
||||
.function = function,
|
||||
});
|
||||
// const calling_convention = getCallingConvention(function_prototype.calling_convention);
|
||||
// function.setCallingConvention(calling_convention);
|
||||
//
|
||||
// const function_attribute_set = llvm.getFunctionAttributes(unit, context, function_prototype);
|
||||
//
|
||||
// var parameter_attribute_sets = try UnpinnedArray(*const LLVM.Attribute.Set).initialize_with_capacity(context.my_allocator, @intCast(function_prototype.abi.parameter_types_abi.len + @intFromBool(function_prototype.abi.return_type_abi.kind == .indirect)));
|
||||
// const return_attribute_set = blk: {
|
||||
// const attribute_set = try llvm.emitParameterAttributes(unit, context, function_prototype.abi.return_type_abi, true);
|
||||
// break :blk switch (function_prototype.abi.return_type_abi.kind) {
|
||||
// .indirect => b: {
|
||||
// parameter_attribute_sets.append_with_capacity(attribute_set);
|
||||
// break :b llvm.context.getAttributeSet(null, 0);
|
||||
// },
|
||||
// else => attribute_set,
|
||||
// };
|
||||
// };
|
||||
//
|
||||
// for (function_prototype.abi.parameter_types_abi) |parameter_type_abi| {
|
||||
// const parameter_attribute_set = try llvm.emitParameterAttributes(unit, context, parameter_type_abi, false);
|
||||
// parameter_attribute_sets.append_with_capacity(parameter_attribute_set);
|
||||
// }
|
||||
|
||||
// function.setAttributes(llvm.context, function_attribute_set, return_attribute_set, parameter_attribute_sets.pointer, parameter_attribute_sets.length);
|
||||
|
||||
switch (declaration.initial_value) {
|
||||
.function_declaration => try llvm.function_declaration_map.put_no_clobber(context.my_allocator, declaration, function),
|
||||
@ -2300,7 +2277,13 @@ pub const LLVM = struct {
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
if (unit.descriptor.generate_debug_information) {
|
||||
const generate_debug_information = unit.descriptor.generate_debug_information and switch (declaration.initial_value) {
|
||||
.function_declaration => true,
|
||||
.function_definition => |function_definition_index| unit.function_definitions.get(function_definition_index).has_debug_info,
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
if (generate_debug_information) {
|
||||
// if (data_structures.byte_equal(name, "nat_split_struct_ints")) @breakpoint();
|
||||
const debug_file = try llvm.getDebugInfoFile(unit, context, declaration.declaration.scope.file);
|
||||
var parameter_types = try UnpinnedArray(*LLVM.DebugInfo.Type).initialize_with_capacity(context.my_allocator, @intCast(function_prototype.argument_types.len));
|
||||
@ -2522,7 +2505,9 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
|
||||
llvm.sema_function = function_declaration;
|
||||
llvm.inside_branch = false;
|
||||
|
||||
if (unit.descriptor.generate_debug_information) {
|
||||
const generate_debug_information = unit.descriptor.generate_debug_information and function_definition.has_debug_info;
|
||||
|
||||
if (generate_debug_information) {
|
||||
const subprogram = llvm.function.getSubprogram() orelse unreachable;
|
||||
llvm.file = subprogram.getFile() orelse unreachable;
|
||||
llvm.scope = subprogram.toLocalScope().toScope();
|
||||
@ -2552,7 +2537,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
|
||||
|
||||
switch (sema_instruction.*) {
|
||||
.push_scope => |push_scope| {
|
||||
if (unit.descriptor.generate_debug_information) {
|
||||
if (generate_debug_information) {
|
||||
const old_scope = try llvm.getScope(unit, context, push_scope.old);
|
||||
assert(@intFromEnum(push_scope.old.kind) >= @intFromEnum(Compilation.Debug.Scope.Kind.function));
|
||||
|
||||
@ -2562,7 +2547,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
|
||||
}
|
||||
},
|
||||
.pop_scope => |pop_scope| {
|
||||
if (unit.descriptor.generate_debug_information) {
|
||||
if (generate_debug_information) {
|
||||
const new = try llvm.getScope(unit, context, pop_scope.new);
|
||||
if (pop_scope.new.kind == .function) {
|
||||
assert(new.toSubprogram() orelse unreachable == llvm.function.getSubprogram() orelse unreachable);
|
||||
@ -2577,7 +2562,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
|
||||
}
|
||||
},
|
||||
.debug_checkpoint => |debug_checkpoint| {
|
||||
if (unit.descriptor.generate_debug_information) {
|
||||
if (generate_debug_information) {
|
||||
const scope = try llvm.getScope(unit, context, debug_checkpoint.scope);
|
||||
llvm.builder.setCurrentDebugLocation(llvm.context, debug_checkpoint.line + 1, debug_checkpoint.column + 1, scope, llvm.function);
|
||||
}
|
||||
@ -2921,7 +2906,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
|
||||
try llvm.llvm_instruction_map.put_no_clobber(context.my_allocator, instruction_index, argument.toValue());
|
||||
},
|
||||
.debug_declare_argument => |debug_declare| {
|
||||
if (unit.descriptor.generate_debug_information) {
|
||||
if (generate_debug_information) {
|
||||
const argument_index: c_uint = debug_declare.argument.index;
|
||||
const declaration = &debug_declare.argument.declaration;
|
||||
const debug_declaration_type = try llvm.getDebugType(unit, context, declaration.type);
|
||||
@ -2966,7 +2951,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
|
||||
}
|
||||
},
|
||||
.debug_declare_local_variable => |declare_local_variable| {
|
||||
if (unit.descriptor.generate_debug_information) {
|
||||
if (generate_debug_information) {
|
||||
const local_variable = declare_local_variable.variable;
|
||||
const debug_declaration_type = try llvm.getDebugType(unit, context, local_variable.declaration.type);
|
||||
const always_preserve = true;
|
||||
@ -3190,7 +3175,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo
|
||||
@panic("Function block with no termination");
|
||||
}
|
||||
|
||||
if (unit.descriptor.generate_debug_information) {
|
||||
if (generate_debug_information) {
|
||||
llvm.debug_info_builder.finalizeSubprogram(llvm.function.getSubprogram() orelse unreachable, llvm.function);
|
||||
}
|
||||
|
||||
|
@ -10,15 +10,15 @@ const Enum = enum(u32) {
|
||||
const main = fn () *!void {
|
||||
var result: u32 = 0;
|
||||
for (#fields(Enum)) |e| {
|
||||
//print(#name(e));
|
||||
//print(": ");
|
||||
print(#name(e));
|
||||
print(": ");
|
||||
const value: u32 = #cast(e);
|
||||
print_usize(value);
|
||||
print("\n");
|
||||
result += value;
|
||||
}
|
||||
|
||||
//try expect(#fields(Enum).length == 3);
|
||||
try expect(#fields(Enum).length == 3);
|
||||
|
||||
try expect(result == 3);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user