diff --git a/bootstrap/Compilation.zig b/bootstrap/Compilation.zig index c802285..d810a46 100644 --- a/bootstrap/Compilation.zig +++ b/bootstrap/Compilation.zig @@ -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) = .{}, diff --git a/bootstrap/backend/llvm.zig b/bootstrap/backend/llvm.zig index 50777e8..d2abfe0 100644 --- a/bootstrap/backend/llvm.zig +++ b/bootstrap/backend/llvm.zig @@ -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); } diff --git a/test/standalone/enum_metaprogramming/main.nat b/test/standalone/enum_metaprogramming/main.nat index 001ef36..20563e1 100644 --- a/test/standalone/enum_metaprogramming/main.nat +++ b/test/standalone/enum_metaprogramming/main.nat @@ -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); }