From e9991b8d9a610b8116f2e0665a1e368f7e776398 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Sun, 18 Feb 2024 08:26:17 -0600 Subject: [PATCH] pass more tests --- bootstrap/Compilation.zig | 3732 ++++++++++++----- bootstrap/backend/llvm.cpp | 166 +- bootstrap/backend/llvm.zig | 3115 ++++++-------- bootstrap/backend/llvm_bindings.zig | 63 +- bootstrap/frontend/parser.zig | 1 + build.zig | 7 +- build/test_runner.zig | 2 +- lib/std/os.nat | 4 +- lib/std/os/linux.nat | 4 +- lib/std/std.nat | 6 +- test/standalone/bit_struct/main.nat | 27 + test/standalone/bit_struct_call/main.nat | 28 + {todo_test => test}/standalone/fork/main.nat | 0 .../standalone/fork_exec/main.nat | 2 +- test/standalone/function_pointer/main.nat | 11 + .../function_pointer_struct/main.nat | 23 + .../standalone/self_exe_path/main.nat | 0 .../standalone/virtual_memory/main.nat | 0 18 files changed, 4188 insertions(+), 3003 deletions(-) create mode 100644 test/standalone/bit_struct/main.nat create mode 100644 test/standalone/bit_struct_call/main.nat rename {todo_test => test}/standalone/fork/main.nat (100%) rename {todo_test => test}/standalone/fork_exec/main.nat (81%) create mode 100644 test/standalone/function_pointer/main.nat create mode 100644 test/standalone/function_pointer_struct/main.nat rename {todo_test => test}/standalone/self_exe_path/main.nat (100%) rename {todo_test => test}/standalone/virtual_memory/main.nat (100%) diff --git a/bootstrap/Compilation.zig b/bootstrap/Compilation.zig index 497c0a9..0c05ad4 100644 --- a/bootstrap/Compilation.zig +++ b/bootstrap/Compilation.zig @@ -295,7 +295,7 @@ fn getLoggerScopeType(comptime logger_scope: LoggerScope) type { var logger_bitset = std.EnumSet(LoggerScope).initEmpty(); -fn getWriter() !std.fs.File.Writer{ +fn getWriter() !std.fs.File.Writer { const stdout = std.io.getStdOut(); return stdout.writer(); } @@ -348,6 +348,33 @@ const ImportPackageResult = struct { is_package: bool, }; +fn getTypeBitSize(ty: *Type, unit: *Unit) u32 { + return switch (ty.*) { + .bool => 1, + .integer => |integer| integer.bit_count, + .@"struct" => |struct_index| { + const struct_type = unit.structs.get(struct_index); + switch (struct_type.optional) { + false => switch (struct_type.backing_type) { + .null => { + var bit_size: u32 = 0; + for (struct_type.fields.items) |field_index| { + const field = unit.struct_fields.get(field_index); + const field_type = unit.types.get(field.type); + const field_bit_size = field_type.getBitSize(unit); + bit_size += field_bit_size; + } + return bit_size; + }, + else => unreachable, + }, + true => unreachable, + } + }, + .pointer => 64, + else => |t| @panic(@tagName(t)), + }; +} pub const Type = union(enum) { void, @@ -363,6 +390,10 @@ pub const Type = union(enum) { slice: Type.Slice, array: Type.Array, + pub fn getBitSize(ty: *Type, unit: *Unit) u32 { + return getTypeBitSize(ty, unit); + } + fn getByteSize(ty: *Type, unit: *Unit) u32 { _ = unit; // autofix return switch (ty.*) { @@ -379,11 +410,11 @@ pub const Type = union(enum) { }; } - const Expect = union(enum){ + const Expect = union(enum) { none, type: Type.Index, optional, - array: struct{ + array: struct { count: ?usize, type: Type.Index, termination: Termination, @@ -400,7 +431,7 @@ pub const Type = union(enum) { }; }; - pub const Pointer = struct{ + pub const Pointer = struct { type: Type.Index, termination: Termination, mutability: Mutability, @@ -408,7 +439,7 @@ pub const Type = union(enum) { nullable: bool, }; - const Slice = struct{ + const Slice = struct { child_pointer_type: Type.Index, child_type: Type.Index, termination: Termination, @@ -416,7 +447,7 @@ pub const Type = union(enum) { nullable: bool, }; - const Array = struct{ + const Array = struct { count: usize, type: Type.Index, termination: Termination, @@ -428,7 +459,7 @@ pub const Type = union(enum) { zero, }; - const Common = enum{ + const Common = enum { void, noreturn, type, @@ -512,7 +543,7 @@ pub const Type = union(enum) { .signedness = .signed, }, }, - .@"usize" = .{ + .usize = .{ .integer = .{ .bit_count = 64, .signedness = .unsigned, @@ -532,17 +563,11 @@ pub const Instruction = union(enum) { // TODO call: Instruction.Call, cast: Cast, - // TODO: remove? - constant_int: struct{ - value: u64, - type: Type.Index, - }, debug_checkpoint: DebugCheckPoint, debug_declare_local_variable: DebugDeclareLocalVariable, extract_value: ExtractValue, insert_value: InsertValue, get_element_pointer: GEP, - global: *Debug.Declaration.Global, inline_assembly: InlineAssembly.Index, integer_compare: IntegerCompare, integer_binary_operation: Instruction.IntegerBinaryOperation, @@ -550,10 +575,6 @@ pub const Instruction = union(enum) { load: Load, umin: Min, smin: Min, - // optional_wrap: V, - // optional_unwrap_unchecked: Instruction.Index, - // optional_unwrap_checked: V, - // optional_check: Instruction.Index, phi: Phi, pop_scope: Instruction.Scope, push_scope: Instruction.Scope, @@ -562,74 +583,73 @@ pub const Instruction = union(enum) { stack_slot: Instruction.StackSlot, store: Store, syscall: Syscall, + trap, @"unreachable", - const Phi = struct{ + const Phi = struct { values: ArrayList(V) = .{}, basic_blocks: ArrayList(BasicBlock.Index) = .{}, type: Type.Index, }; - const Min = struct{ + const Min = struct { left: V, right: V, type: Type.Index, }; - const GEP = struct{ + pub const GEP = struct { pointer: Instruction.Index, base_type: Type.Index, + is_struct: bool, index: V, }; - const ExtractValue = struct{ + const ExtractValue = struct { expression: V, index: u32, }; - const InsertValue = struct{ + const InsertValue = struct { expression: V, index: u32, new_value: V, }; - const Branch = struct{ + const Branch = struct { condition: Instruction.Index, from: BasicBlock.Index, taken: BasicBlock.Index, not_taken: BasicBlock.Index, }; - const Jump = struct{ + + const Jump = struct { from: BasicBlock.Index, to: BasicBlock.Index, }; - const DebugDeclareLocalVariable = struct{ + const DebugDeclareLocalVariable = struct { variable: *Debug.Declaration.Local, stack: Instruction.Index, }; - const Syscall = struct{ + const Syscall = struct { arguments: []const V, }; - const Callable = union(enum) { - function_definition: *Debug.Declaration.Global, - }; - - const Call = struct{ - callable: Callable, + const Call = struct { + callable: V, function_type: Type.Index, arguments: []const V, }; - const IntegerCompare = struct{ + const IntegerCompare = struct { left: V, right: V, type: Type.Index, id: Id, - const Id = enum{ + const Id = enum { equal, not_equal, unsigned_less, @@ -643,13 +663,13 @@ pub const Instruction = union(enum) { }; }; - const IntegerBinaryOperation = struct{ + const IntegerBinaryOperation = struct { left: V, right: V, id: Id, signedness: Type.Integer.Signedness, - const Id = enum{ + const Id = enum { add, div, mod, @@ -668,7 +688,7 @@ pub const Instruction = union(enum) { new: *Debug.Scope, }; - const ArgumentDeclaration = struct{ + const ArgumentDeclaration = struct { name: u32, type: Type.Index, }; @@ -678,7 +698,7 @@ pub const Instruction = union(enum) { value: V, type: Type.Index, - const Id = enum{ + const Id = enum { bitcast, enum_to_int, int_to_pointer, @@ -686,65 +706,70 @@ pub const Instruction = union(enum) { sign_extend, zero_extend, pointer_var_to_const, + pointer_const_to_var, + pointer_to_nullable, slice_var_to_const, + slice_to_nullable, + slice_to_not_null, truncate, + pointer_to_array_to_pointer_to_many, }; }; - const DebugCheckPoint = struct{ + const DebugCheckPoint = struct { scope: *Debug.Scope, line: u32, column: u32, }; - const Load = struct{ + const Load = struct { value: V, - }; - - const StackSlot = struct{ type: Type.Index, }; - const Store = struct{ + const StackSlot = struct { + type: Type.Index, + }; + + const Store = struct { // TODO: destination: V, source: V, }; - pub const List = BlockList(@This(), enum{}); + pub const List = BlockList(@This(), enum {}); pub usingnamespace @This().List.Index; }; -pub const BasicBlock = struct{ +pub const BasicBlock = struct { instructions: ArrayList(Instruction.Index) = .{}, - useful_instructions: usize = 0, predecessor: BasicBlock.Index = .null, // TODO: not use a bool terminated: bool = false, - pub const List = BlockList(@This(), enum{}); + pub const List = BlockList(@This(), enum {}); pub usingnamespace @This().List.Index; }; -pub const Function = struct{ - pub const Attribute = enum{ +pub const Function = struct { + pub const Attribute = enum { cc, naked, @"extern", }; - pub const Definition = struct{ + pub const Definition = struct { scope: Debug.Scope.Function, basic_blocks: ArrayList(BasicBlock.Index) = .{}, // TODO: make this more efficient type: Type.Index, body: Debug.Block.Index, - pub const List = BlockList(@This(), enum{}); + pub const List = BlockList(@This(), enum {}); pub usingnamespace @This().List.Index; }; - const CallingConvention = enum{ + pub const CallingConvention = enum { c, auto, }; @@ -755,33 +780,32 @@ pub const Function = struct{ attributes: Attributes, calling_convention: CallingConvention, - const Attributes = struct{ + const Attributes = struct { naked: bool, }; - const List = BlockList(@This(), enum{}); + const List = BlockList(@This(), enum {}); pub usingnamespace @This().List.Index; }; - }; -pub const Struct = struct{ +pub const Struct = struct { fields: ArrayList(Struct.Field.Index) = .{}, scope: Debug.Scope.Global, backing_type: Type.Index, type: Type.Index, optional: bool, - pub const Field = struct{ + pub const Field = struct { name: u32, type: Type.Index, default_value: ?V.Comptime, - - const List = BlockList(@This(), enum{}); + + const List = BlockList(@This(), enum {}); pub usingnamespace @This().List.Index; }; - const List = BlockList(@This(), enum{}); + const List = BlockList(@This(), enum {}); pub usingnamespace @This().List.Index; }; @@ -801,16 +825,15 @@ pub const Context = struct { } }; -pub const V = struct{ +pub const V = struct { value: union(enum) { unresolved: Node.Index, runtime: Instruction.Index, @"comptime": Comptime, - function_reference: *Debug.Declaration.Global, }, type: Type.Index, - pub const Comptime = union(enum){ + pub const Comptime = union(enum) { unresolved: Node.Index, undefined, void, @@ -820,61 +843,64 @@ pub const V = struct{ constant_int: ConstantInt, enum_value: Enum.Field.Index, function_definition: Function.Definition.Index, + global: *Debug.Declaration.Global, + constant_backed_struct: u64, constant_struct: ConstantStruct.Index, constant_array: ConstantArray.Index, constant_slice: ConstantSlice.Index, string_literal: u32, + null_pointer, - pub const ConstantSlice = struct{ + pub const ConstantSlice = struct { ptr: *Debug.Declaration.Global, len: usize, type: Type.Index, - - pub const List = BlockList(@This(), enum{}); + + pub const List = BlockList(@This(), enum {}); pub usingnamespace List.Index; }; - pub const ConstantArray = struct{ + pub const ConstantArray = struct { values: []const V.Comptime, type: Type.Index, - - pub const List = BlockList(@This(), enum{}); + + pub const List = BlockList(@This(), enum {}); pub usingnamespace List.Index; }; - pub const ConstantStruct = struct{ + pub const ConstantStruct = struct { fields: []const V.Comptime, type: Type.Index, - - pub const List = BlockList(@This(), enum{}); + + pub const List = BlockList(@This(), enum {}); pub usingnamespace List.Index; }; - pub const ComptimeInt = struct{ + pub const ComptimeInt = struct { value: u64, signedness: Type.Integer.Signedness, }; - pub const ConstantInt = struct{ + pub const ConstantInt = struct { value: u64, }; - fn getType(v: Comptime, unit: *Unit) Type.Index{ + fn getType(v: Comptime, unit: *Unit) Type.Index { return switch (v) { .type => .type, .bool => .bool, .enum_value => |enum_field_index| unit.enum_fields.get(enum_field_index).parent, .function_definition => |function_definition_index| unit.function_definitions.get(function_definition_index).type, .comptime_int => .comptime_int, + .constant_struct => |constant_struct| unit.constant_structs.get(constant_struct).type, else => |t| @panic(@tagName(t)), }; } }; }; - -pub const Debug = struct{ - pub const Declaration = struct{ +pub const Debug = struct { + pub const Declaration = struct { scope: *Scope, type: Type.Index, name: u32, @@ -883,13 +909,13 @@ pub const Debug = struct{ mutability: Mutability, kind: Kind, - const Kind = enum{ + const Kind = enum { global, local, argument, }; - pub const Global = struct{ + pub const Global = struct { declaration: Declaration, initial_value: V.Comptime, type_node_index: Node.Index, @@ -897,34 +923,34 @@ pub const Debug = struct{ const Attributes = std.EnumSet(Attribute); - pub const Attribute = enum{ + pub const Attribute = enum { @"export", }; - pub const List = BlockList(@This(), enum{}); + pub const List = BlockList(@This(), enum {}); pub usingnamespace List.Index; - pub fn getFunctionDefinitionIndex(global: *Global) Function.Definition.Index{ + pub fn getFunctionDefinitionIndex(global: *Global) Function.Definition.Index { return global.initial_value.function_definition; } }; - pub const Local = struct{ + pub const Local = struct { declaration: Declaration, init_value: V, - pub const List = BlockList(@This(), enum{}); + pub const List = BlockList(@This(), enum {}); pub usingnamespace List.Index; }; - pub const Argument = struct{ + pub const Argument = struct { declaration: Declaration, - pub const List = BlockList(@This(), enum{}); + pub const List = BlockList(@This(), enum {}); pub usingnamespace List.Index; }; }; - pub const Scope = struct{ - declarations: AutoArrayHashMap(u32, *Declaration) =.{}, + pub const Scope = struct { + declarations: AutoArrayHashMap(u32, *Declaration) = .{}, parent: ?*Scope = null, file: File.Index, line: u32, @@ -933,26 +959,26 @@ pub const Debug = struct{ local: bool, level: u8, - const Lookup = struct{ + const Lookup = struct { scope: *Scope, declaration: *Declaration, }; - pub const Local = struct{ + pub const Local = struct { scope: Scope, local_declaration_map: AutoArrayHashMap(*Debug.Declaration.Local, Instruction.Index) = .{}, }; - pub const Global = struct{ + pub const Global = struct { scope: Scope, }; - pub const Function = struct{ + pub const Function = struct { scope: Scope, argument_map: AutoArrayHashMap(*Debug.Declaration.Argument, Instruction.Index) = .{}, }; - fn lookupDeclaration(s: *Scope, name: u32, look_in_parent_scopes: bool) ?Lookup{ + fn lookupDeclaration(s: *Scope, name: u32, look_in_parent_scopes: bool) ?Lookup { var scope_it: ?*Scope = s; while (scope_it) |scope| : (scope_it = scope.parent) { if (scope.declarations.get(name)) |declaration| { @@ -963,7 +989,7 @@ pub const Debug = struct{ } if (!look_in_parent_scopes) break; - } + } return null; } @@ -977,22 +1003,21 @@ pub const Debug = struct{ return file_index; } } else @panic("No parent file scope"); - } - pub const Kind = enum{ + pub const Kind = enum { compilation_unit, file, file_container, container, - function,// Arguments + function, // Arguments block, }; }; - pub const Block = struct{ + pub const Block = struct { scope: Scope.Local, - pub const List = BlockList(@This(), enum{}); + pub const List = BlockList(@This(), enum {}); pub usingnamespace List.Index; }; @@ -1007,7 +1032,7 @@ pub const Debug = struct{ type: Type.Index = .null, scope: Scope.Global, - pub const List = BlockList(@This(), enum{}); + pub const List = BlockList(@This(), enum {}); pub usingnamespace List.Index; pub const Status = enum { @@ -1017,7 +1042,6 @@ pub const Debug = struct{ parsed, }; }; - }; pub const Mutability = enum(u1) { @@ -1025,7 +1049,7 @@ pub const Mutability = enum(u1) { @"var", }; -pub const IntrinsicId = enum{ +pub const IntrinsicId = enum { assert, @"asm", //this is processed separately as it need special parsing cast, @@ -1062,7 +1086,7 @@ pub const Builder = struct { loop_exit_block: BasicBlock.Index = .null, return_phi: Instruction.Index = .null, return_block: BasicBlock.Index = .null, - last_check_point: struct{ + last_check_point: struct { line: u32 = 0, column: u32 = 0, scope: ?*Debug.Scope = null, @@ -1258,7 +1282,7 @@ pub const Builder = struct { .runtime = inline_asm, }, // TODO: WARN fix - .type = .@"noreturn", + .type = .noreturn, }; }, .cast => { @@ -1268,20 +1292,30 @@ pub const Builder = struct { const v = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, argument_node_index, .right); const source_type = unit.types.get(v.type); - // + // const cast_id: Instruction.Cast.Id = switch (type_expect) { .type => |type_index| b: { assert(type_index != v.type); const destination_type = unit.types.get(type_index); switch (destination_type.*) { .pointer => |destination_pointer| { - _ = destination_pointer; // autofix switch (source_type.*) { .integer => |source_integer| { _ = source_integer; // autofix // TODO: break :b .int_to_pointer; }, + .pointer => |source_pointer| { + if (destination_pointer.type == source_pointer.type) { + if (destination_pointer.mutability == source_pointer.mutability) { + unreachable; + } else { + break :b .pointer_const_to_var; + } + } else { + unreachable; + } + }, else => |t| @panic(@tagName(t)), } }, @@ -1332,9 +1366,33 @@ pub const Builder = struct { break :b .pointer_to_int; }, + .@"struct" => |struct_index| { + const struct_type = unit.structs.get(struct_index); + if (struct_type.backing_type != .null) { + if (struct_type.backing_type == type_index) { + break :b .bitcast; + } else { + unreachable; + } + } else { + unreachable; + } + }, else => |t| @panic(@tagName(t)), } }, + .@"struct" => |struct_index| { + const struct_type = unit.structs.get(struct_index); + if (struct_type.optional) { + assert(struct_type.backing_type == .null); + unreachable; + } else { + switch (struct_type.backing_type) { + .null => unreachable, + else => unreachable, + } + } + }, else => |t| @panic(@tagName(t)), } }, @@ -1348,7 +1406,7 @@ pub const Builder = struct { .id = cast_id, }, }); - + try builder.appendInstruction(unit, context, instruction); return .{ @@ -1478,7 +1536,7 @@ pub const Builder = struct { try builder.branch(unit, context, instruction_index, true_block, false_block); builder.current_basic_block = false_block; - try builder.buildUnreachable(unit, context); + try builder.buildTrap(unit, context); builder.current_basic_block = true_block; }, @@ -1523,7 +1581,7 @@ pub const Builder = struct { .old = old_scope, .new = new_scope, }, - }); + }); try builder.appendInstruction(unit, context, instruction); } @@ -1580,23 +1638,23 @@ pub const Builder = struct { const main_node_index = file.parser.main_node_index; // File type already assigned - _ = try builder.resolveContainerType(unit, context, main_node_index, .@"struct"); + _ = try builder.resolveContainerType(unit, context, main_node_index, .@"struct", null); assert(file.type != .null); } - const CastResult = enum{ + const CastResult = enum { int_to_pointer, enum_to_int, sign_extend, zero_extend, }; - const TokenDebugInfo = struct{ + const TokenDebugInfo = struct { line: u32, column: u32, }; - fn getTokenDebugInfo(builder: *Builder, unit: *Unit, token: Token.Index) TokenDebugInfo{ + fn getTokenDebugInfo(builder: *Builder, unit: *Unit, token: Token.Index) TokenDebugInfo { const file = unit.files.get(builder.current_file); const line_offset_index = unit.token_buffer.tokens.items(.line)[Token.unwrap(token)]; const line = line_offset_index - file.lexer.line_offset; @@ -1637,40 +1695,15 @@ pub const Builder = struct { } fn appendInstruction(builder: *Builder, unit: *Unit, context: *const Context, instruction_index: Instruction.Index) !void { + switch (unit.instructions.get(instruction_index).*) { + .extract_value => |extract_value| switch (unit.types.get(extract_value.expression.type).*) { + .pointer => unreachable, + else => {}, + }, + else => {}, + } const basic_block = unit.basic_blocks.get(builder.current_basic_block); if (!basic_block.terminated) { - basic_block.useful_instructions += @intFromBool(switch (unit.instructions.get(instruction_index).*) { - .argument_declaration, - .branch, - .call, - .cast, - .constant_int, - .extract_value, - .insert_value, - .get_element_pointer, - .global, - .inline_assembly, - .integer_compare, - .integer_binary_operation, - .jump, - .load, - .umin, - .smin, - .phi, - .ret, - .ret_void, - .stack_slot, - .store, - .syscall, - .@"unreachable", - => true, - .block, - .pop_scope, - .push_scope, - .debug_checkpoint, - .debug_declare_local_variable, - => false, - }); try basic_block.instructions.append(context.allocator, instruction_index); } else { const instruction = unit.instructions.get(instruction_index); @@ -1679,16 +1712,16 @@ pub const Builder = struct { } } - const If = struct{ + const If = struct { condition: Condition, - const Condition = union(enum){ + const Condition = union(enum) { true, false, runtime, }; }; - fn referenceGlobalDeclaration(builder: *Builder, unit: *Unit, context: *const Context, scope: *Debug.Scope, declaration: *Debug.Declaration) !V{ + fn referenceGlobalDeclaration(builder: *Builder, unit: *Unit, context: *const Context, scope: *Debug.Scope, declaration: *Debug.Declaration) !*Debug.Declaration.Global { // TODO: implement this assert(declaration.kind == .global); const old_context = builder.startContextSwitch(.{ @@ -1715,7 +1748,7 @@ pub const Builder = struct { }, }; - global_declaration.initial_value = try builder.resolveComptimeValue(unit, context, type_expect, global_declaration.attributes, declaration_node_index); + global_declaration.initial_value = try builder.resolveComptimeValue(unit, context, type_expect, global_declaration.attributes, declaration_node_index, global_declaration); switch (declaration.type) { .null => { @@ -1726,8 +1759,16 @@ pub const Builder = struct { } switch (global_declaration.initial_value) { - .function_definition => { - try unit.code_to_emit.append(context.allocator, global_declaration); + .function_definition => |function_definition_index| { + switch (unit.getNode(declaration_node_index).id) { + .function_definition => try unit.code_to_emit.putNoClobber(context.allocator, function_definition_index, global_declaration), + else => { + const actual_function_declaration = unit.code_to_emit.get(function_definition_index).?; + global_declaration.initial_value = .{ + .global = actual_function_declaration, + }; + }, + } }, .type => |type_index| { assert(declaration.type == .type); @@ -1747,39 +1788,10 @@ pub const Builder = struct { builder.endContextSwitch(old_context); - switch (global_declaration.initial_value) { - .function_definition => { - return .{ - .value = .{ - .function_reference = global_declaration, - }, - .type = global_declaration.declaration.type, - }; - }, - else => { - if (declaration.mutability == .@"const") { - return .{ - .value = .{ - .@"comptime" = global_declaration.initial_value, - }, - .type = declaration.type, - }; - } else { - const instruction = try unit.instructions.append(context.allocator, .{ - .global = global_declaration, - }); - return .{ - .value = .{ - .runtime = instruction, - }, - .type = declaration.type,// TODO: fetch proper type - }; - } - }, - } + return global_declaration; } - const ContextSwitch = struct{ + const ContextSwitch = struct { scope: *Debug.Scope, file: Debug.File.Index, basic_block: BasicBlock.Index, @@ -1842,7 +1854,8 @@ pub const Builder = struct { cannot_evaluate, }; - fn resolveComptimeValue(builder: *Builder, unit: *Unit, context: *const Context, type_expect: Type.Expect, global_attributes: Debug.Declaration.Global.Attributes, node_index: Node.Index) anyerror!V.Comptime{ + /// Last value is used to cache types being analyzed so we dont hit stack overflow + fn resolveComptimeValue(builder: *Builder, unit: *Unit, context: *const Context, type_expect: Type.Expect, global_attributes: Debug.Declaration.Global.Attributes, node_index: Node.Index, maybe_global: ?*Debug.Declaration.Global) anyerror!V.Comptime { const node = unit.getNode(node_index); switch (node.id) { .intrinsic => { @@ -1860,15 +1873,16 @@ pub const Builder = struct { } }, .field_access => { - const result = try builder.resolveFieldAccess(unit, context, type_expect, node_index); + const result = try builder.resolveFieldAccess(unit, context, type_expect, node_index, .right); return switch (result.value) { .@"comptime" => |ct| ct, else => @panic("Expected comptime value, found runtime value"), }; }, .keyword_false, - .keyword_true, => return .{ - .@"bool" = node.id == .keyword_true, + .keyword_true, + => return .{ + .bool = node.id == .keyword_true, }, .function_definition => { const current_basic_block = builder.current_basic_block; @@ -1902,12 +1916,12 @@ pub const Builder = struct { .body = undefined, .scope = .{ .scope = Debug.Scope{ - .line = token_debug_info.line, - .column = token_debug_info.column, - .kind = .function, - .local = true, - .level = builder.current_scope.level + 1, - .file = builder.current_file, + .line = token_debug_info.line, + .column = token_debug_info.column, + .kind = .function, + .local = true, + .level = builder.current_scope.level + 1, + .file = builder.current_file, }, }, }); @@ -2002,7 +2016,6 @@ pub const Builder = struct { else => unreachable, }, } - } } @@ -2017,18 +2030,6 @@ pub const Builder = struct { }, .number_literal => switch (std.zig.parseNumberLiteral(unit.getExpectedTokenBytes(node.token, .number_literal))) { .int => |integer| { - // const type_index = switch (type_expect) { - // .type => |type_index| b: { - // const ty = unit.types.get(type_index); - // break :b switch (ty.*) { - // .integer => type_index, - // else => |t| @panic(@tagName(t)), - // }; - // }, - // .none => Type.Index.comptime_int, - // }; - // _ = type_index; // autofix - return .{ .comptime_int = .{ .value = integer, @@ -2042,13 +2043,13 @@ pub const Builder = struct { return .undefined; }, .enum_type => { - const type_index = try builder.resolveContainerType(unit, context, node_index, .@"enum"); + const type_index = try builder.resolveContainerType(unit, context, node_index, .@"enum", maybe_global); return .{ .type = type_index, }; }, .struct_type => { - const type_index = try builder.resolveContainerType(unit, context, node_index, .@"struct"); + const type_index = try builder.resolveContainerType(unit, context, node_index, .@"struct", maybe_global); return .{ .type = type_index, }; @@ -2065,7 +2066,15 @@ pub const Builder = struct { }, .identifier => { const identifier = unit.getExpectedTokenBytes(node.token, .identifier); - const resolved_value = try builder.resolveIdentifier(unit, context, type_expect, identifier, .left); + const side: Side = switch (type_expect) { + .none => unreachable, + .type => |type_index| switch (unit.types.get(type_index).*) { + .type => .right, + else => |t| @panic(@tagName(t)), + }, + else => unreachable, + }; + const resolved_value = try builder.resolveIdentifier(unit, context, type_expect, identifier, side); return switch (resolved_value.value) { .@"comptime" => |ct| ct, .runtime => return error.cannot_evaluate, @@ -2073,26 +2082,77 @@ pub const Builder = struct { }; }, .signed_integer_type => { - const result = try builder.resolveIntegerType(unit, context, node_index); + const result = try builder.resolveIntegerType(unit, context, node_index); return .{ .type = result, }; }, .compare_greater_equal => { - const left = try builder.resolveComptimeValue(unit, context, Type.Expect.none, .{}, node.left); + const left = try builder.resolveComptimeValue(unit, context, Type.Expect.none, .{}, node.left, null); const left_type = left.getType(unit); - const right = try builder.resolveComptimeValue(unit, context, Type.Expect{ .type = left_type }, .{}, node.right); + const right = try builder.resolveComptimeValue(unit, context, Type.Expect{ .type = left_type }, .{}, node.right, null); _ = right; // autofix unreachable; }, + .add => { + const left = try builder.resolveComptimeValue(unit, context, Type.Expect.none, .{}, node.left, null); + const left_type = left.getType(unit); + const right = try builder.resolveComptimeValue(unit, context, Type.Expect{ .type = left_type }, .{}, node.right, null); + switch (left) { + .comptime_int => |left_ct_int| { + assert(left_ct_int.signedness == .unsigned); + const left_value = left_ct_int.value; + switch (right) { + .comptime_int => |right_ct_int| { + assert(right_ct_int.signedness == .unsigned); + const right_value = right_ct_int.value; + const result = left_value + right_value; + return .{ + .comptime_int = .{ + .value = result, + .signedness = .unsigned, + }, + }; + }, + else => |t| @panic(@tagName(t)), + } + }, + else => |t| @panic(@tagName(t)), + } + }, + .empty_container_literal_guess => { + assert(node.left != .null); + assert(node.right != .null); + const container_type = try builder.resolveType(unit, context, node.left); + const node_list = unit.getNodeList(node.right); + assert(node_list.len == 0); + const result = try builder.resolveContainerLiteral(unit, context, node_list, container_type); + return switch (result.value) { + .@"comptime" => |ct| ct, + else => |t| @panic(@tagName(t)), + }; + }, + .anonymous_container_literal => { + assert(node.left == .null); + assert(node.right != .null); + switch (type_expect) { + .type => |type_index| { + const node_list = unit.getNodeList(node.right); + const result = try builder.resolveContainerLiteral(unit, context, node_list, type_index); + return switch (result.value) { + .@"comptime" => |ct| ct, + else => |t| @panic(@tagName(t)), + }; + }, + else => |t| @panic(@tagName(t)), + } + }, else => |t| @panic(@tagName(t)), } } fn referenceArgumentDeclaration(builder: *Builder, unit: *Unit, context: *const Context, scope: *Debug.Scope, declaration: *Debug.Declaration) !V { _ = builder; // autofix - _ = unit; // autofix - _ = context; // autofix assert(declaration.kind == .argument); assert(scope.kind == .function); @@ -2104,15 +2164,18 @@ pub const Builder = struct { .value = .{ .runtime = instruction_index, }, - .type = declaration.type, + .type = try unit.getPointerType(context, .{ + .type = declaration.type, + .termination = .none, + .mutability = .@"const", + .many = false, + .nullable = false, + }), }; - } - fn referenceLocalDeclaration(builder: *Builder, unit: *Unit, context: *const Context, scope: *Debug.Scope, declaration: *Debug.Declaration) !V { + fn referenceLocalDeclaration(builder: *Builder, unit: *Unit, context: *const Context, scope: *Debug.Scope, declaration: *Debug.Declaration) !V { _ = builder; // autofix - _ = unit; // autofix - _ = context; // autofix assert(declaration.kind == .local); assert(scope.kind == .block); @@ -2123,29 +2186,34 @@ pub const Builder = struct { .value = .{ .runtime = instruction_index, }, - .type = declaration.type, + .type = try unit.getPointerType(context, .{ + .type = declaration.type, + .termination = .none, + .mutability = declaration.mutability, + .many = false, + .nullable = false, + }), }; } else { return local_declaration.init_value; } } - const TypeCheckResult = enum{ + const TypeCheckResult = enum { success, pointer_var_to_const, + pointer_to_nullable, slice_var_to_const, + slice_to_nullable, materialize_int, optional_wrap, sign_extend, zero_extend, }; - const TypecheckError = error{ - }; + const TypecheckError = error{}; fn typecheck(builder: *Builder, unit: *Unit, context: *const Context, destination_type_index: Type.Index, source_type_index: Type.Index) TypecheckError!TypeCheckResult { - _ = builder; // autofix - _ = context; // autofix if (destination_type_index == source_type_index) { return .success; } else { @@ -2155,23 +2223,38 @@ pub const Builder = struct { .pointer => |destination_pointer| { switch (source.*) { .pointer => |source_pointer| { - if (destination_pointer.type == source_pointer.type) { - if (destination_pointer.many == source_pointer.many) { - if (destination_pointer.termination == source_pointer.termination) { - if (destination_pointer.nullable == source_pointer.nullable) { - assert(destination_pointer.mutability != source_pointer.mutability); - assert(destination_pointer.mutability == .@"const"); - assert(source_pointer.mutability == .@"var"); + const result = try builder.typecheck(unit, context, destination_pointer.type, source_pointer.type); + switch (result) { + .success => { + if (destination_pointer.many == source_pointer.many) { + if (destination_pointer.termination == source_pointer.termination) { + if (destination_pointer.nullable == source_pointer.nullable) { + if (destination_pointer.mutability == source_pointer.mutability) { + return .success; + } else { + assert(destination_pointer.mutability == .@"const"); + assert(source_pointer.mutability == .@"var"); - return .pointer_var_to_const; + return .pointer_var_to_const; + } + } else { + assert(destination_pointer.mutability == source_pointer.mutability); + + if (!destination_pointer.nullable) { + unreachable; + } + + return .pointer_to_nullable; + } } } - } - } - unreachable; + unreachable; + }, + else => |t| @panic(@tagName(t)), + } }, - else =>|t| @panic(@tagName(t)), + else => |t| @panic(@tagName(t)), } }, .integer => |destination_integer| { @@ -2179,12 +2262,24 @@ pub const Builder = struct { .integer => |source_integer| { if (destination_integer.signedness == source_integer.signedness) { if (destination_integer.bit_count == source_integer.bit_count) { - unreachable; + if (destination_type_index == .usize and source_type_index == .u64) { + return .success; + } else if (destination_type_index == .u64 and source_type_index == .usize) { + return .success; + } else if (destination_type_index == .ssize and source_type_index == .s64) { + return .success; + } else if (destination_type_index == .s64 and source_type_index == .ssize) { + return .success; + } else { + unreachable; + } } else if (destination_integer.bit_count > source_integer.bit_count) { return switch (destination_integer.signedness) { .signed => .sign_extend, .unsigned => .zero_extend, }; + } else { + unreachable; } } else { unreachable; @@ -2208,7 +2303,6 @@ pub const Builder = struct { } else { unreachable; } - }, .slice => |destination_slice| { switch (source.*) { @@ -2220,30 +2314,60 @@ pub const Builder = struct { if (destination_slice.mutability == .@"const" and source_slice.mutability == .@"var") { return .slice_var_to_const; } + } else { + if (destination_slice.nullable and !source_slice.nullable) { + assert(destination_slice.mutability == source_slice.mutability); + return .slice_to_nullable; + } } } - unreachable; } + unreachable; }, else => |t| @panic(@tagName(t)), } }, - // .optional => |destination_optional_element_type_index| { - // if (destination_optional_element_type_index == source_type_index) { - // return .optional_wrap; - // } else { - // unreachable; - // } - // }, + .function => |destination_function_prototype_index| { + const destination_function_prototype = unit.function_prototypes.get(destination_function_prototype_index); + switch (source.*) { + .function => |source_function_prototype_index| { + // We are not that good yet + assert(destination_function_prototype_index != source_function_prototype_index); + const source_function_prototype = unit.function_prototypes.get(source_function_prototype_index); + if (destination_function_prototype.calling_convention != source_function_prototype.calling_convention) { + unreachable; + } + + if (!std.meta.eql(destination_function_prototype.attributes, source_function_prototype.attributes)) { + unreachable; + } + + if (destination_function_prototype.return_type != source_function_prototype.return_type) { + unreachable; + } + + if (destination_function_prototype.argument_types.len != source_function_prototype.argument_types.len) { + unreachable; + } + + for (destination_function_prototype.argument_types, source_function_prototype.argument_types) |dst_arg_type, src_arg_type| { + if (dst_arg_type != src_arg_type) { + unreachable; + } + } + + return .success; + }, + else => |t| @panic(@tagName(t)), + } + }, else => |t| @panic(@tagName(t)), } - - unreachable; } } - const Side = enum{ + const Side = enum { left, right, }; @@ -2255,41 +2379,134 @@ pub const Builder = struct { if (builder.current_scope.lookupDeclaration(hash, look_in_parent_scopes)) |lookup| { // TODO: we could do this better // const scope = lookup.scope; - const preliminary_result: V = switch (lookup.scope.kind) { - .file_container, .file => try builder.referenceGlobalDeclaration(unit, context, lookup.scope, lookup.declaration), - .function => try builder.referenceArgumentDeclaration(unit, context, lookup.scope, lookup.declaration), - .block => try builder.referenceLocalDeclaration(unit, context, lookup.scope, lookup.declaration), - else => |t| @panic(@tagName(t)), - }; - - const v: V = switch (preliminary_result.value) { - .runtime => switch (side) { - .left => preliminary_result, - .right => b: { - const instruction = try unit.instructions.append(context.allocator, .{ - .load = .{ - .value = preliminary_result, + const v: V = switch (lookup.scope.kind) { + .file_container, + .file, + .container, + => b: { + const global = try builder.referenceGlobalDeclaration(unit, context, lookup.scope, lookup.declaration); + const pointer_to_global = try unit.getPointerType(context, .{ + .type = global.declaration.type, + .termination = switch (type_expect) { + .none => .none, + .type => |type_index| switch (unit.types.get(type_index).*) { + .pointer => |pointer| pointer.termination, + else => .none, }, - }); - - try builder.appendInstruction(unit, context, instruction); - - break :b .{ - .value = .{ - .runtime = instruction, + else => unreachable, + }, + .mutability = switch (type_expect) { + .none => .@"var", + .type => |type_index| switch (unit.types.get(type_index).*) { + .pointer => |pointer| pointer.mutability, + else => .@"var", }, - .type = lookup.declaration.type, - }; - }, + else => unreachable, + }, + .many = false, + .nullable = false, + }); + + break :b switch (side) { + .left => switch (global.declaration.type) { + .type => .{ + .value = .{ + .@"comptime" = .{ + .type = global.initial_value.type, + }, + }, + .type = .type, + }, + else => .{ + .value = .{ + .@"comptime" = .{ + .global = global, + }, + }, + .type = switch (type_expect) { + .none => pointer_to_global, + .type => |type_index| switch (try builder.typecheck(unit, context, type_index, pointer_to_global)) { + .success => type_index, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }, + }, + }, + .right => switch (global.declaration.mutability) { + .@"const" => .{ + .value = .{ + .@"comptime" = global.initial_value, + }, + .type = global.declaration.type, + }, + .@"var" => blk: { + const load = try unit.instructions.append(context.allocator, .{ + .load = .{ + .value = .{ + .value = .{ + .@"comptime" = .{ + .global = global, + }, + }, + .type = pointer_to_global, + }, + .type = global.declaration.type, + }, + }); + + try builder.appendInstruction(unit, context, load); + + break :blk .{ + .value = .{ + .runtime = load, + }, + .type = global.declaration.type, + }; + }, + }, + }; + }, + .function, .block => |kind| blk: { + const preliminary_result: V = switch (kind) { + .function => try builder.referenceArgumentDeclaration(unit, context, lookup.scope, lookup.declaration), + .block => try builder.referenceLocalDeclaration(unit, context, lookup.scope, lookup.declaration), + else => unreachable, + }; + const v: V = switch (preliminary_result.value) { + .runtime => switch (side) { + .left => preliminary_result, + .right => b: { + const instruction = try unit.instructions.append(context.allocator, .{ + .load = .{ + .value = preliminary_result, + .type = lookup.declaration.type, + }, + }); + + try builder.appendInstruction(unit, context, instruction); + + break :b .{ + .value = .{ + .runtime = instruction, + }, + .type = lookup.declaration.type, + }; + }, + }, + .@"comptime" => preliminary_result, + else => |t| @panic(@tagName(t)), + }; + + break :blk v; }, - .@"comptime", .function_reference => preliminary_result, else => |t| @panic(@tagName(t)), }; switch (type_expect) { .none => return v, .type => |expected_type_index| { - const typecheck_result = try builder.typecheck(unit, context, expected_type_index, lookup.declaration.type); + const typecheck_result = try builder.typecheck(unit, context, expected_type_index, v.type); switch (typecheck_result) { .success => return v, .zero_extend => { @@ -2359,6 +2576,40 @@ pub const Builder = struct { .type = expected_type_index, }; }, + .slice_to_nullable => { + const cast = try unit.instructions.append(context.allocator, .{ + .cast = .{ + .id = .slice_to_nullable, + .value = v, + .type = expected_type_index, + }, + }); + + try builder.appendInstruction(unit, context, cast); + return .{ + .value = .{ + .runtime = cast, + }, + .type = expected_type_index, + }; + }, + .pointer_to_nullable => { + const cast = try unit.instructions.append(context.allocator, .{ + .cast = .{ + .id = .pointer_to_nullable, + .value = v, + .type = expected_type_index, + }, + }); + + try builder.appendInstruction(unit, context, cast); + return .{ + .value = .{ + .runtime = cast, + }, + .type = expected_type_index, + }; + }, .materialize_int => { const destination_integer_type = unit.types.get(expected_type_index).integer; const ct_int = v.value.@"comptime".comptime_int; @@ -2407,7 +2658,7 @@ pub const Builder = struct { .index = 0, .new_value = v, }, - }); + }); try builder.appendInstruction(unit, context, insert_value_to_optional); @@ -2426,11 +2677,11 @@ pub const Builder = struct { .@"comptime" = .{ .bool = true, }, - }, + }, .type = .bool, }, - }, - }); + }, + }); try builder.appendInstruction(unit, context, final_insert); @@ -2457,7 +2708,6 @@ pub const Builder = struct { .type = expected_array_descriptor.type, }); if (array_type == lookup.declaration.type) { - assert(v.type == lookup.declaration.type); return v; } else { unreachable; @@ -2472,7 +2722,7 @@ pub const Builder = struct { var file_path: []const u8 = ""; while (scope_it) |scope| : (scope_it = scope.parent) { - for (0..indentation * indentation_size) |_|{ + for (0..indentation * indentation_size) |_| { std.debug.print(" ", .{}); } std.debug.print("> Scope {s} ", .{@tagName(scope.kind)}); @@ -2493,7 +2743,7 @@ pub const Builder = struct { indentation += 1; } - std.debug.panic("Identifier '{s}' not found in file {s}", .{identifier, file_path}); + std.debug.panic("Identifier '{s}' not found in file {s}", .{ identifier, file_path }); } } @@ -2502,17 +2752,21 @@ pub const Builder = struct { switch (node.id) { .assign, .add_assign, .sub_assign, .div_assign => { if (unit.getNode(node.left).id == .discard) { - const r = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, node.right, .right); + const r = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, node.right, .right); return r; } else { const left = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, node.left, .left); const expected_right_type = switch (left.value) { .runtime => |instr_index| switch (unit.instructions.get(instr_index).*) { - .global => |global| global.declaration.type, + // .global => |global| global.declaration.type, .stack_slot => |stack_slot| stack_slot.type, .get_element_pointer => |gep| gep.base_type, else => |t| @panic(@tagName(t)), }, + .@"comptime" => |ct| switch (ct) { + .global => |global| global.declaration.type, + else => |t| @panic(@tagName(t)), + }, else => |t| @panic(@tagName(t)), }; const right = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = expected_right_type }, node.right, .right); @@ -2522,12 +2776,13 @@ pub const Builder = struct { const left_load = try unit.instructions.append(context.allocator, .{ .load = .{ .value = left, + .type = expected_right_type, }, }); try builder.appendInstruction(unit, context, left_load); - const instruction = switch (unit.types.get(left.type).*) { + switch (unit.types.get(expected_right_type).*) { .integer => |integer| { const instruction = try unit.instructions.append(context.allocator, .{ .integer_binary_operation = .{ @@ -2535,7 +2790,7 @@ pub const Builder = struct { .value = .{ .runtime = left_load, }, - .type = left.type, + .type = expected_right_type, }, .right = right, .signedness = integer.signedness, @@ -2553,13 +2808,11 @@ pub const Builder = struct { .value = .{ .runtime = instruction, }, - .type = left.type, + .type = expected_right_type, }; }, else => |t| @panic(@tagName(t)), - }; - _ = instruction; // autofix - unreachable; + } }, }; const store = try unit.instructions.append(context.allocator, .{ @@ -2582,7 +2835,7 @@ pub const Builder = struct { } } - fn newBasicBlock(builder: *Builder, unit: *Unit, context: *const Context) !BasicBlock.Index{ + fn newBasicBlock(builder: *Builder, unit: *Unit, context: *const Context) !BasicBlock.Index { const function = unit.function_definitions.get(builder.current_function); const entry_basic_block = try unit.basic_blocks.append(context.allocator, .{}); try function.basic_blocks.append(context.allocator, entry_basic_block); @@ -2594,7 +2847,9 @@ pub const Builder = struct { _ = builder; // autofix const node = unit.getNode(node_index); const result: Type.Index = switch (node.id) { - .signed_integer_type, .unsigned_integer_type, => b: { + .signed_integer_type, + .unsigned_integer_type, + => b: { const token_bytes = unit.getExpectedTokenBytes(node.token, switch (node.id) { .signed_integer_type => .keyword_signed_integer, .unsigned_integer_type => .keyword_unsigned_integer, @@ -2618,20 +2873,35 @@ pub const Builder = struct { return result; } - fn resolveArrayType(builder: *Builder, unit: *Unit, context: *const Context, node_index: Node.Index, size_hint: ?usize) !Type.Index{ + fn resolveArrayType(builder: *Builder, unit: *Unit, context: *const Context, node_index: Node.Index, size_hint: ?usize) !Type.Index { const node = unit.getNode(node_index); const attribute_node_list = unit.getNodeList(node.left); - assert(attribute_node_list.len == 2); - const termination = Type.Termination.none; + var termination = Type.Termination.none; const len_node = unit.getNode(attribute_node_list[0]); const len = switch (len_node.id) { - else => switch (try builder.resolveComptimeValue(unit, context, Type.Expect{ .type = .usize }, .{}, attribute_node_list[0])) { + else => switch (try builder.resolveComptimeValue(unit, context, Type.Expect{ .type = .usize }, .{}, attribute_node_list[0], null)) { .comptime_int => |ct_int| ct_int.value, else => |t| @panic(@tagName(t)), }, .discard => size_hint orelse unreachable, }; - const element_type = try builder.resolveType(unit, context, attribute_node_list[1]); + + if (attribute_node_list.len == 3) { + switch (unit.getNode(attribute_node_list[1]).id) { + .zero_terminated => { + assert(termination == .none); + termination = .zero; + }, + .null_terminated => { + assert(termination == .none); + termination = .null; + }, + else => |t| @panic(@tagName(t)), + } + } + + const element_type_index = @as(usize, 1) + @intFromBool(attribute_node_list.len == 3); + const element_type = try builder.resolveType(unit, context, attribute_node_list[element_type_index]); const array_type = try unit.getArrayType(context, .{ .count = len, .type = element_type, @@ -2648,12 +2918,14 @@ pub const Builder = struct { .usize_type => .usize, .void_type => .void, .identifier, .field_access => { - const resolved_type_value = try builder.resolveComptimeValue(unit, context, Type.Expect{ .type = .type }, .{}, node_index); + const resolved_type_value = try builder.resolveComptimeValue(unit, context, Type.Expect{ .type = .type }, .{}, node_index, null); return resolved_type_value.type; }, .bool_type => .bool, .ssize_type => .ssize, - .signed_integer_type, .unsigned_integer_type, => b: { + .signed_integer_type, + .unsigned_integer_type, + => b: { break :b try builder.resolveIntegerType(unit, context, node_index); }, .pointer_type => b: { @@ -2789,6 +3061,14 @@ pub const Builder = struct { }; break :blk r; }, + .function_prototype => blk: { + const is_export = false; + const function_prototype_index = try builder.resolveFunctionPrototype(unit, context, node_index, is_export); + const function_type = try unit.types.append(context.allocator, .{ + .function = function_prototype_index, + }); + break :blk function_type; + }, else => |t| @panic(@tagName(t)), }; @@ -2800,7 +3080,7 @@ pub const Builder = struct { assert(node.id == .function_prototype); const attribute_and_return_type_node_list = unit.getNodeList(node.right); assert(attribute_and_return_type_node_list.len >= 1); - const attribute_node_list = attribute_and_return_type_node_list[0..attribute_and_return_type_node_list.len - 1]; + const attribute_node_list = attribute_and_return_type_node_list[0 .. attribute_and_return_type_node_list.len - 1]; const return_type_node_index = attribute_and_return_type_node_list[attribute_and_return_type_node_list.len - 1]; const function_prototype_index = try unit.function_prototypes.append(context.allocator, .{ @@ -2816,7 +3096,6 @@ pub const Builder = struct { }, }); - var is_naked: bool = false; // Resolve attributes @@ -2854,7 +3133,7 @@ pub const Builder = struct { return function_prototype_index; } - fn resolveContainerType(builder: *Builder, unit: *Unit, context: *const Context, container_node_index: Node.Index, container_type: ContainerType) !Type.Index { + fn resolveContainerType(builder: *Builder, unit: *Unit, context: *const Context, container_node_index: Node.Index, container_type: ContainerType, maybe_global: ?*Debug.Declaration.Global) !Type.Index { const current_basic_block = builder.current_basic_block; defer builder.current_basic_block = current_basic_block; builder.current_basic_block = .null; @@ -2862,7 +3141,7 @@ pub const Builder = struct { const container_node = unit.getNode(container_node_index); const container_nodes = unit.getNodeList(container_node.left); - const Data = struct{ + const Data = struct { scope: *Debug.Scope.Global, type: Type.Index, }; @@ -2929,11 +3208,6 @@ pub const Builder = struct { file.type = type_index; }, .file_container => {}, - // .file_container => { - // const global_scope = @fieldParentPtr(Debug.Scope.Global, "scope", builder.current_scope); - // const file = @fieldParentPtr(Debug.File, "scope", global_scope); - // file.type = type_index; - // }, else => |t| @panic(@tagName(t)), } @@ -2947,16 +3221,14 @@ pub const Builder = struct { .@"enum" => b: { assert(container_node.id == .enum_type); const enum_index = try unit.enums.append(context.allocator, .{ - .scope = .{ - .scope = .{ - .kind = .container, - .line = token_debug_info.line, - .column = token_debug_info.column, - .level = builder.current_scope.level + 1, - .local = false, - .file = builder.current_file, - } - }, + .scope = .{ .scope = .{ + .kind = .container, + .line = token_debug_info.line, + .column = token_debug_info.column, + .level = builder.current_scope.level + 1, + .local = false, + .file = builder.current_file, + } }, .backing_type = backing_type, }); @@ -2973,6 +3245,12 @@ pub const Builder = struct { const scope = data.scope; const type_index = data.type; + if (maybe_global) |global| { + global.declaration.type = .type; + global.initial_value = .{ + .type = type_index, + }; + } try builder.pushScope(unit, context, &scope.scope); defer builder.popScope(unit, context) catch unreachable; @@ -2989,9 +3267,7 @@ pub const Builder = struct { .@"struct" => assert(member.id != .enum_field), .@"enum" => assert(member.id != .container_field), } - // const token_offset = file.lexer.token_offsets.items[Token.unwrap(member.token)]; - // const slice = file.source_code[token_offset..@min(token_offset + 100, file.source_code.len)]; - // std.debug.print("Member: `{s}`\n", .{slice}); + const member_type = getContainerMemberType(member.id); switch (member_type) { @@ -3071,7 +3347,7 @@ pub const Builder = struct { } break :b res; - } + }, }; const value_node_index = declaration_node.right; @@ -3082,7 +3358,7 @@ pub const Builder = struct { .variable_symbol_declaration => .@"var", else => unreachable, }; - // + const global_declaration_index = try unit.global_declarations.append(context.allocator, .{ .declaration = .{ .scope = &scope.scope, @@ -3139,7 +3415,7 @@ pub const Builder = struct { switch (container_type) { .@"enum" => { - assert(field_node.id == .@"enum_field"); + assert(field_node.id == .enum_field); const enum_type = unit.enums.get(ty.@"enum"); const enum_value: usize = switch (field_node.left) { @@ -3147,30 +3423,28 @@ pub const Builder = struct { else => b: { const enum_value = try builder.resolveComptimeValue(unit, context, Type.Expect{ .type = enum_type.backing_type, - }, .{}, - //Debug.Declaration.Global.Attributes.initEmpty(), - field_node.left); + }, .{}, field_node.left, null); assert(enum_value.comptime_int.signedness == .unsigned); break :b enum_value.comptime_int.value; }, }; - const enum_field_index = try unit.enum_fields.append(context.allocator, .{ - .name = hash, - .value = enum_value, - .parent = type_index, - }); - enum_type.fields.appendAssumeCapacity(enum_field_index); + const enum_field_index = try unit.enum_fields.append(context.allocator, .{ + .name = hash, + .value = enum_value, + .parent = type_index, + }); + enum_type.fields.appendAssumeCapacity(enum_field_index); }, - .@"struct" => { - assert(field_node.id == .@"container_field"); + .@"struct" => { + assert(field_node.id == .container_field); const struct_type = unit.structs.get(ty.@"struct"); const field_name = unit.getExpectedTokenBytes(field_node.token, .identifier); - const field_name_hash = try unit.processIdentifier(context, field_name); + const field_name_hash = try unit.processIdentifier(context, field_name); const field_type = try builder.resolveType(unit, context, field_node.left); const field_default_value: ?V.Comptime = switch (field_node.right) { .null => null, - else => |default_value_node_index| try builder.resolveComptimeValue(unit, context, Type.Expect{ .type = field_type }, .{}, default_value_node_index), + else => |default_value_node_index| try builder.resolveComptimeValue(unit, context, Type.Expect{ .type = field_type }, .{}, default_value_node_index, null), }; const struct_field = try unit.struct_fields.append(context.allocator, .{ @@ -3179,7 +3453,7 @@ pub const Builder = struct { .default_value = field_default_value, }); struct_type.fields.appendAssumeCapacity(struct_field); - }, + }, } } } @@ -3210,10 +3484,48 @@ pub const Builder = struct { return type_index; } - fn resolveRuntimeValue(builder: *Builder, unit: *Unit, context: *const Context, type_expect: Type.Expect, node_index: Node.Index, side: Side) anyerror!V{ + fn emitIntegerCompare(builder: *Builder, unit: *Unit, context: *const Context, left_value: V, right_value: V, integer: Type.Integer, compare_node_id: Node.Id) anyerror!V { + const compare = try unit.instructions.append(context.allocator, .{ + .integer_compare = .{ + .left = left_value, + .right = right_value, + .type = left_value.type, + .id = switch (compare_node_id) { + .compare_equal => .equal, + .compare_not_equal => .not_equal, + else => switch (integer.signedness) { + .unsigned => switch (compare_node_id) { + .compare_less => .unsigned_less, + .compare_less_equal => .unsigned_less_equal, + .compare_greater => .unsigned_greater, + .compare_greater_equal => .unsigned_greater_equal, + else => unreachable, + }, + .signed => switch (compare_node_id) { + .compare_less => .signed_less, + .compare_less_equal => .signed_less_equal, + .compare_greater => .signed_greater, + .compare_greater_equal => .signed_greater_equal, + else => unreachable, + }, + }, + }, + }, + }); + try builder.appendInstruction(unit, context, compare); + + return .{ + .value = .{ + .runtime = compare, + }, + .type = .bool, + }; + } + + fn resolveRuntimeValue(builder: *Builder, unit: *Unit, context: *const Context, type_expect: Type.Expect, node_index: Node.Index, side: Side) anyerror!V { const node = unit.getNode(node_index); - const v : V = switch (node.id) { + const v: V = switch (node.id) { .identifier => block: { const identifier = unit.getExpectedTokenBytes(node.token, .identifier); const result = try builder.resolveIdentifier(unit, context, type_expect, identifier, side); @@ -3221,9 +3533,9 @@ pub const Builder = struct { }, .intrinsic => try builder.resolveIntrinsic(unit, context, type_expect, node_index), .pointer_dereference => block: { - // TODO: + // TODO: const pointer_type_expect = switch (type_expect) { - .none => unreachable,//type_expect, + .none => unreachable, //type_expect, .type => |type_index| b: { const pointer_type = try unit.getPointerType(context, .{ .type = type_index, @@ -3236,26 +3548,27 @@ pub const Builder = struct { .type = pointer_type, }; break :b result; - }, + }, else => unreachable, }; // TODO: is this right? .right const pointer_value = try builder.resolveRuntimeValue(unit, context, pointer_type_expect, node.left, .right); - const load = try unit.instructions.append(context.allocator, .{ - .load = .{ - .value = pointer_value, - }, - }); - try builder.appendInstruction(unit, context, load); - const load_type = switch (type_expect) { .none => unreachable, .type => |type_index| type_index, else => unreachable, }; + const load = try unit.instructions.append(context.allocator, .{ + .load = .{ + .value = pointer_value, + .type = load_type, + }, + }); + try builder.appendInstruction(unit, context, load); + break :block .{ .value = .{ .runtime = load, @@ -3263,7 +3576,13 @@ pub const Builder = struct { .type = load_type, }; }, - .compare_equal, .compare_not_equal, .compare_greater, .compare_greater_equal, .compare_less, .compare_less_equal, => |cmp_node_id| block: { + .compare_equal, + .compare_not_equal, + .compare_greater, + .compare_greater_equal, + .compare_less, + .compare_less_equal, + => |cmp_node_id| block: { const left_node_index = node.left; const right_node_index = node.right; const left_expect_type = Type.Expect.none; @@ -3272,46 +3591,14 @@ pub const Builder = struct { const right_expect_type = Type.Expect{ .type = left_type }; const right_value = try builder.resolveRuntimeValue(unit, context, right_expect_type, right_node_index, .right); - switch (unit.types.get(left_type).*) { - .integer => |integer| { - const compare = try unit.instructions.append(context.allocator, .{ - .integer_compare = .{ - .left = left_value, - .right = right_value, - .type = left_type, - .id = switch (cmp_node_id) { - .compare_equal => .equal, - .compare_not_equal => .not_equal, - else => switch (integer.signedness) { - .unsigned => switch (cmp_node_id) { - .compare_less => .unsigned_less, - .compare_less_equal => .unsigned_less_equal, - .compare_greater => .unsigned_greater, - .compare_greater_equal => .unsigned_greater_equal, - else => unreachable, - }, - .signed => switch (cmp_node_id) { - .compare_less => .signed_less, - .compare_less_equal => .signed_less_equal, - .compare_greater => .signed_greater, - .compare_greater_equal => .signed_greater_equal, - else => unreachable, - }, - } - }, - }, - }); - try builder.appendInstruction(unit, context, compare); - - break :block .{ - .value = .{ - .runtime = compare, - }, - .type = .bool, - }; - }, + break :block switch (unit.types.get(left_type).*) { + .integer => |integer| try builder.emitIntegerCompare(unit, context, left_value, right_value, integer, cmp_node_id), + .bool => try builder.emitIntegerCompare(unit, context, left_value, right_value, .{ + .bit_count = 1, + .signedness = .unsigned, + }, cmp_node_id), else => |t| @panic(@tagName(t)), - } + }; }, .add, .sub, .mul, .div, .mod, .bit_and, .bit_or, .bit_xor, .shift_left, .shift_right => block: { const left_node_index = node.left; @@ -3417,7 +3704,7 @@ pub const Builder = struct { }; }, .call => try builder.resolveCall(unit, context, node_index), - .field_access => try builder.resolveFieldAccess(unit, context, type_expect, node_index), + .field_access => try builder.resolveFieldAccess(unit, context, type_expect, node_index, side), .number_literal => switch (std.zig.parseNumberLiteral(unit.getExpectedTokenBytes(node.token, .number_literal))) { .int => |integer| switch (type_expect) { .type => |type_index| switch (unit.types.get(type_index).*) { @@ -3460,10 +3747,6 @@ pub const Builder = struct { .block = block, }); - // if (builder.current_basic_block != .null) { - // try builder.appendInstruction(unit, context, block_i); - // } - break :block .{ .value = .{ .runtime = block_i, @@ -3474,92 +3757,11 @@ pub const Builder = struct { .container_literal => block: { assert(node.left != .null); assert(node.right != .null); - - const container_type_index = try builder.resolveType(unit, context, node.left); - const container_type = unit.types.get(container_type_index); - const initialization_nodes = unit.getNodeList(node.right); + const container_type_index = try builder.resolveType(unit, context, node.left); - switch (container_type.*) { - .@"struct" => |struct_index| { - const struct_type = unit.structs.get( struct_index); - const fields = struct_type.fields.items; - var list = try ArrayList(V).initCapacity(context.allocator, fields.len); - var is_comptime = true; - - for (fields) |field_index| { - const field = unit.struct_fields.get(field_index); - - for (initialization_nodes) |initialization_node_index| { - const initialization_node = unit.getNode(initialization_node_index); - assert(initialization_node.id == .container_field_initialization); - assert(initialization_node.left != .null); - assert(initialization_node.right == .null); - const field_name = unit.getExpectedTokenBytes(Token.addInt(initialization_node.token, 1), .identifier); - const field_name_hash = try unit.processIdentifier(context, field_name); - if (field_name_hash == field.name) { - const expected_type = field.type; - const field_initialization = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = expected_type }, initialization_node.left, .right); - is_comptime = is_comptime and field_initialization.value == .@"comptime"; - list.appendAssumeCapacity(field_initialization); - break; - } - unreachable; - } else if (field.default_value) |default_value| { - _ = default_value; // autofix - unreachable; - } else { - @panic("Missing field"); - } - } - - if (is_comptime) { - var comptime_list = try ArrayList(V.Comptime).initCapacity(context.allocator, fields.len); - for (list.items) |item| { - comptime_list.appendAssumeCapacity(item.value.@"comptime"); - } - - break :block .{ - .value = .{ - .@"comptime" = .{ - .constant_struct = try unit.constant_structs.append(context.allocator, .{ - .fields = comptime_list.items, - .type = container_type_index, - }), - }, - }, - .type = container_type_index, - }; - } else { - var struct_initialization = V{ - .value = .{ - .@"comptime" = .undefined, - }, - .type = container_type_index, - }; - - for (list.items, 0..) |field, index| { - const struct_initialization_instruction = try unit.instructions.append(context.allocator, .{ - .insert_value = .{ - .expression = struct_initialization, - .index = @intCast(index), - .new_value = field, - }, - }); - - try builder.appendInstruction(unit, context, struct_initialization_instruction); - - struct_initialization.value = .{ - .runtime = struct_initialization_instruction, - }; - - } - - break :block struct_initialization; - } - }, - else => |t| @panic(@tagName(t)), - } + const result = try builder.resolveContainerLiteral(unit, context, initialization_nodes, container_type_index); + break :block result; }, .enum_literal => block: { switch (type_expect) { @@ -3568,7 +3770,7 @@ pub const Builder = struct { switch (expected_type.*) { .@"enum" => |enum_index| { const enum_type = unit.enums.get(enum_index); - const identifier = unit.getExpectedTokenBytes(Token.addInt( node.token, 1), .identifier); + const identifier = unit.getExpectedTokenBytes(Token.addInt(node.token, 1), .identifier); const hash = try unit.processIdentifier(context, identifier); for (enum_type.fields.items) |field_index| { const field = unit.enum_fields.get(field_index); @@ -3594,8 +3796,9 @@ pub const Builder = struct { }, .null_literal => switch (type_expect) { .type => |type_index| switch (unit.types.get(type_index).*) { - .@"struct" => |struct_index| { + .@"struct" => |struct_index| blk: { const struct_type = unit.structs.get(struct_index); + if (struct_type.optional) { const optional_undefined = V{ .value = .{ @@ -3612,7 +3815,7 @@ pub const Builder = struct { .new_value = .{ .value = .{ .@"comptime" = .{ - .bool = true, + .bool = false, }, }, .type = .bool, @@ -3622,7 +3825,7 @@ pub const Builder = struct { try builder.appendInstruction(unit, context, final_insert); - return .{ + break :blk .{ .value = .{ .runtime = final_insert, }, @@ -3632,12 +3835,69 @@ pub const Builder = struct { unreachable; } }, - // .optional => .{ - // .value = .{ - // .@"comptime" = .optional_null_literal, - // }, - // .type = type_index, - // }, + .slice => |slice| blk: { + const optional_undefined = V{ + .value = .{ + .@"comptime" = .undefined, + }, + .type = type_index, + }; + + const slice_builder = try unit.instructions.append(context.allocator, .{ + .insert_value = .{ + .expression = optional_undefined, + .index = 0, + .new_value = .{ + .value = .{ + .@"comptime" = .null_pointer, + }, + .type = slice.child_pointer_type, + }, + }, + }); + + try builder.appendInstruction(unit, context, slice_builder); + + const final_slice = try unit.instructions.append(context.allocator, .{ + .insert_value = .{ + .expression = .{ + .value = .{ + .runtime = slice_builder, + }, + .type = type_index, + }, + .index = 1, + .new_value = .{ + .value = .{ + .@"comptime" = .{ + .constant_int = .{ + .value = 0, + }, + }, + }, + .type = .usize, + }, + }, + }); + try builder.appendInstruction(unit, context, final_slice); + + break :blk .{ + .value = .{ + .runtime = final_slice, + }, + .type = type_index, + }; + }, + .pointer => |pointer| blk: { + assert(pointer.nullable); + + break :blk .{ + .value = .{ + .@"comptime" = .null_pointer, + }, + .type = type_index, + }; + }, else => |t| @panic(@tagName(t)), }, else => |t| @panic(@tagName(t)), @@ -3665,7 +3925,7 @@ pub const Builder = struct { .type = .usize, }; }, - .pointer => |pointer| switch (unit.types.get( pointer.type).*) { + .pointer => |pointer| switch (unit.types.get(pointer.type).*) { .array => |array| .{ .value = .{ .@"comptime" = .{ @@ -3682,7 +3942,6 @@ pub const Builder = struct { }, else => try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = .usize }, range_node.right, .right), }; - switch (unit.types.get(expression_to_slice.type).*) { .slice => |slice| { const extract_value = try unit.instructions.append(context.allocator, .{ @@ -3697,6 +3956,7 @@ pub const Builder = struct { const pointer_gep = try unit.instructions.append(context.allocator, .{ .get_element_pointer = .{ .pointer = extract_value, + .is_struct = false, .base_type = slice.child_type, .index = range_start, }, @@ -3761,13 +4021,94 @@ pub const Builder = struct { }; }, .pointer => |pointer| switch (pointer.many) { - true => unreachable, + true => { + const pointer_gep = try unit.instructions.append(context.allocator, .{ + .get_element_pointer = .{ + .pointer = expression_to_slice.value.runtime, + .is_struct = false, + .base_type = pointer.type, + .index = range_start, + }, + }); + try builder.appendInstruction(unit, context, pointer_gep); + + const pointer_type = try unit.getPointerType(context, .{ + .type = pointer.type, + .termination = pointer.termination, + .mutability = pointer.mutability, + .many = true, + .nullable = false, + }); + const slice_builder = try unit.instructions.append(context.allocator, .{ + .insert_value = .{ + .expression = V{ + .value = .{ + .@"comptime" = .undefined, + }, + .type = switch (type_expect) { + .type => |type_index| type_index, + else => |t| @panic(@tagName(t)), + }, + }, + .index = 0, + .new_value = .{ + .value = .{ + .runtime = pointer_gep, + }, + .type = pointer_type, + }, + }, + }); + try builder.appendInstruction(unit, context, slice_builder); + + const final_slice = try unit.instructions.append(context.allocator, .{ + .insert_value = .{ + .expression = V{ + .value = .{ + .runtime = slice_builder, + }, + .type = expression_to_slice.type, + }, + .index = 1, + .new_value = b: { + const range_compute = try unit.instructions.append(context.allocator, .{ + .integer_binary_operation = .{ + .id = .sub, + .left = range_end, + .right = range_start, + .signedness = .unsigned, + }, + }); + try builder.appendInstruction(unit, context, range_compute); + + break :b V{ + .value = .{ + .runtime = range_compute, + }, + .type = .usize, + }; + }, + }, + }); + try builder.appendInstruction(unit, context, final_slice); + + break :blk .{ + .value = .{ + .runtime = final_slice, + }, + .type = switch (type_expect) { + .type => |type_index| type_index, + else => |t| @panic(@tagName(t)), + }, + }; + }, false => switch (unit.types.get(pointer.type).*) { .array => |array| { const pointer_gep = try unit.instructions.append(context.allocator, .{ .get_element_pointer = .{ .pointer = expression_to_slice.value.runtime, .base_type = array.type, + .is_struct = false, .index = range_start, }, }); @@ -3848,7 +4189,7 @@ pub const Builder = struct { }; const range = array.count - range_start_value; - + break :b V{ .value = .{ .@"comptime" = .{ @@ -3879,9 +4220,12 @@ pub const Builder = struct { }, else => |t| @panic(@tagName(t)), }, + // else => |t| @panic(@tagName(t)), + // }, }, else => |t| @panic(@tagName(t)), } + if (true) break :blk undefined; }, .keyword_false, .keyword_true => .{ .value = .{ @@ -3901,14 +4245,12 @@ pub const Builder = struct { const string_global = try builder.processStringLiteral(unit, context, node.token); const pointer_type = slice.child_pointer_type; - const instruction = try unit.instructions.append(context.allocator, .{ - .global = string_global, - }); - // try builder.appendInstruction(instruction); const global_string_pointer = .{ .value = .{ - .runtime = instruction, + .@"comptime" = .{ + .global = string_global, + }, }, .type = pointer_type, }; @@ -3960,6 +4302,43 @@ pub const Builder = struct { .type = type_index, }; }, + .pointer => |pointer| blk: { + const string_global = try builder.processStringLiteral(unit, context, node.token); + switch (pointer.many) { + true => { + const pointer_type = try unit.getPointerType(context, .{ + .type = string_global.declaration.type, + .termination = .none, + .mutability = pointer.mutability, + .many = false, + .nullable = false, + }); + const cast = try unit.instructions.append(context.allocator, .{ + .cast = .{ + .id = .pointer_to_array_to_pointer_to_many, + .value = .{ + .value = .{ + .@"comptime" = .{ + .global = string_global, + }, + }, + .type = pointer_type, + }, + .type = type_index, + }, + }); + try builder.appendInstruction(unit, context, cast); + + break :blk .{ + .value = .{ + .runtime = cast, + }, + .type = type_index, + }; + }, + false => unreachable, + } + }, else => |t| @panic(@tagName(t)), }, else => |t| @panic(@tagName(t)), @@ -3969,6 +4348,7 @@ pub const Builder = struct { const array_type_expect = switch (type_expect) { .type => type_expect, .array => type_expect, + .none => @panic("Anonymous array literal requires type specification"), else => |t| @panic(@tagName(t)), }; const array_literal = try builder.resolveArrayLiteral(unit, context, array_type_expect, unit.getNodeList(node.right), .null); @@ -3988,189 +4368,251 @@ pub const Builder = struct { }, }, node.left, .left); - const array_type = unit.types.get(value_pointer.type).array; + switch (unit.types.get(value_pointer.type).*) { + .array => |array| switch (value_pointer.value) { + .runtime => |ii| switch (unit.instructions.get(ii).*) { + .insert_value => { + const name = try std.fmt.allocPrintZ(context.allocator, "__anon_local_arr_{}", .{unit.anon_arr}); + unit.anon_arr += 1; + const emit = true; + const stack_slot = try builder.emitLocalVariableDeclaration(unit, context, unit.getNode(node.left).token, .@"const", value_pointer.type, value_pointer, emit, name); - switch (value_pointer.value) { - // TODO: hash identical constants - .runtime => |instruction_index| switch (unit.instructions.get(instruction_index).*) { - .stack_slot => |stack_slot| { - const slice_builder = try unit.instructions.append(context.allocator, .{ - .insert_value = .{ - .expression = .{ - .value = .{ - .@"comptime" = .undefined, - }, - .type = type_index, - }, - .index = 0, - .new_value = value_pointer, - }, - }); - try builder.appendInstruction(unit, context, slice_builder); + const pointer_type = try unit.getPointerType(context, .{ + .type = value_pointer.type, + .many = false, + .nullable = false, + .mutability = .@"const", + .termination = .none, + }); - const final_slice = try unit.instructions.append(context.allocator, .{ - .insert_value = .{ - .expression = .{ + const cast = try unit.instructions.append(context.allocator, .{ + .cast = .{ + .id = .pointer_to_array_to_pointer_to_many, .value = .{ - .runtime = slice_builder, + .value = .{ + .runtime = stack_slot, + }, + .type = pointer_type, }, - .type = type_index, + .type = slice.child_pointer_type, }, - .index = 1, - .new_value = .{ - .value = .{ - .@"comptime" = .{ - .constant_int = .{ - .value = unit.types.get(stack_slot.type).array.count, + }); + try builder.appendInstruction(unit, context, cast); + const slice_builder = try unit.instructions.append(context.allocator, .{ + .insert_value = .{ + .expression = .{ + .value = .{ + .@"comptime" = .undefined, + }, + .type = type_index, + }, + .index = 0, + .new_value = .{ + .value = .{ + .runtime = cast, + }, + .type = slice.child_pointer_type, + }, + }, + }); + try builder.appendInstruction(unit, context, slice_builder); + + const final_slice = try unit.instructions.append(context.allocator, .{ + .insert_value = .{ + .expression = .{ + .value = .{ + .runtime = slice_builder, + }, + .type = type_index, + }, + .index = 1, + .new_value = .{ + .value = .{ + .@"comptime" = .{ + .constant_int = .{ + .value = array.count, + }, }, }, + .type = .usize, }, - .type = .usize, }, - }, - }); - try builder.appendInstruction(unit, context, final_slice); + }); + try builder.appendInstruction(unit, context, final_slice); - break :blk .{ - .value = .{ - .runtime = final_slice, - }, - .type = type_index, - }; + break :blk .{ + .value = .{ + .runtime = final_slice, + }, + .type = type_index, + }; + }, + else => |t| @panic(@tagName(t)), }, - .insert_value => { - const name = try std.fmt.allocPrintZ(context.allocator, "__anon_local_arr_{}", .{unit.anon_arr}); - unit.anon_arr += 1; - const emit = true; - const stack_slot = try builder.emitLocalVariableDeclaration(unit, context, unit.getNode(node.left).token, .@"const", value_pointer.type, value_pointer, emit, name); - const slice_builder = try unit.instructions.append(context.allocator, .{ - .insert_value = .{ - .expression = .{ - .value = .{ - .@"comptime" = .undefined, + .@"comptime" => |ct| switch (ct) { + .constant_array => { + const name = try std.fmt.allocPrintZ(context.allocator, "__anon_local_arr_{}", .{unit.anon_arr}); + unit.anon_arr += 1; + const emit = true; + const stack_slot = try builder.emitLocalVariableDeclaration(unit, context, unit.getNode(node.left).token, .@"const", value_pointer.type, value_pointer, emit, name); + const slice_builder = try unit.instructions.append(context.allocator, .{ + .insert_value = .{ + .expression = .{ + .value = .{ + .@"comptime" = .undefined, + }, + .type = type_index, }, - .type = type_index, - }, - .index = 0, - .new_value = .{ - .value = .{ - .runtime = stack_slot, + .index = 0, + .new_value = .{ + .value = .{ + .runtime = stack_slot, + }, + .type = unit.instructions.get(stack_slot).stack_slot.type, }, - .type = unit.instructions.get(stack_slot).stack_slot.type, }, - }, - }); - try builder.appendInstruction(unit, context, slice_builder); + }); + try builder.appendInstruction(unit, context, slice_builder); - const final_slice = try unit.instructions.append(context.allocator, .{ - .insert_value = .{ - .expression = .{ - .value = .{ - .runtime = slice_builder, + const final_slice = try unit.instructions.append(context.allocator, .{ + .insert_value = .{ + .expression = .{ + .value = .{ + .runtime = slice_builder, + }, + .type = type_index, }, - .type = type_index, - }, - .index = 1, - .new_value = .{ - .value = .{ - .@"comptime" = .{ - .constant_int = .{ - .value = array_type.count, + .index = 1, + .new_value = .{ + .value = .{ + .@"comptime" = .{ + .constant_int = .{ + .value = array.count, + }, }, }, + .type = .usize, }, - .type = .usize, }, - }, - }); - try builder.appendInstruction(unit, context, final_slice); + }); + try builder.appendInstruction(unit, context, final_slice); - break :blk .{ - .value = .{ - .runtime = final_slice, - }, - .type = type_index, - }; + break :blk .{ + .value = .{ + .runtime = final_slice, + }, + .type = type_index, + }; + }, + else => |t| @panic(@tagName(t)), }, else => |t| @panic(@tagName(t)), }, - .@"comptime" => |ct| switch (ct) { - .constant_array => { - const name = try std.fmt.allocPrintZ(context.allocator, "__anon_local_arr_{}", .{unit.anon_arr}); - unit.anon_arr += 1; - const emit = true; - const stack_slot = try builder.emitLocalVariableDeclaration(unit, context, unit.getNode(node.left).token, .@"const", value_pointer.type, value_pointer, emit, name); - const slice_builder = try unit.instructions.append(context.allocator, .{ - .insert_value = .{ - .expression = .{ - .value = .{ - .@"comptime" = .undefined, - }, - .type = type_index, - }, - .index = 0, - .new_value = .{ - .value = .{ - .runtime = stack_slot, - }, - .type = unit.instructions.get(stack_slot).stack_slot.type, - }, + .pointer => |pointer| { + switch (unit.types.get(pointer.type).*) { + .array => |array| { + const cast = try unit.instructions.append(context.allocator, .{ + .cast = .{ + .id = .pointer_to_array_to_pointer_to_many, + .value = value_pointer, + .type = slice.child_pointer_type, }, }); - try builder.appendInstruction(unit, context, slice_builder); + try builder.appendInstruction(unit, context, cast); - const final_slice = try unit.instructions.append(context.allocator, .{ - .insert_value = .{ - .expression = .{ - .value = .{ - .runtime = slice_builder, + const slice_builder = try unit.instructions.append(context.allocator, .{ + .insert_value = .{ + .expression = .{ + .value = .{ + .@"comptime" = .undefined, + }, + .type = type_index, + }, + .index = 0, + .new_value = .{ + .value = .{ + .runtime = cast, + }, + .type = slice.child_pointer_type, }, - .type = type_index, }, - .index = 1, - .new_value = .{ - .value = .{ - .@"comptime" = .{ - .constant_int = .{ - .value = array_type.count, + }); + try builder.appendInstruction(unit, context, slice_builder); + + const final_slice = try unit.instructions.append(context.allocator, .{ + .insert_value = .{ + .expression = .{ + .value = .{ + .runtime = slice_builder, + }, + .type = type_index, + }, + .index = 1, + .new_value = .{ + .value = .{ + .@"comptime" = .{ + .constant_int = .{ + .value = array.count, + }, }, }, + .type = .usize, }, - .type = .usize, }, - }, - }); - try builder.appendInstruction(unit, context, final_slice); + }); + try builder.appendInstruction(unit, context, final_slice); - break :blk .{ - .value = .{ - .runtime = final_slice, - }, - .type = type_index, - }; - }, - else => |t| @panic(@tagName(t)), + break :blk .{ + .value = .{ + .runtime = final_slice, + }, + .type = type_index, + }; + }, + else => |t| @panic(@tagName(t)), + } }, else => |t| @panic(@tagName(t)), } }, .pointer => |pointer| switch (pointer.many) { - true => unreachable, - false => { - const v = try builder.resolveRuntimeValue(unit, context, Type.Expect{ - .type = pointer.type, - }, node.left, .left); - break :blk switch (v.value) { - .runtime => |instruction_index| switch (unit.instructions.get(instruction_index).*) { - .stack_slot => v, + true => { + const v = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, node.left, .left); + switch (unit.types.get(v.type).*) { + .pointer => |left_pointer| switch (unit.types.get(left_pointer.type).*) { + .array => |array| { + assert(array.type == pointer.type); + const cast = try unit.instructions.append(context.allocator, .{ + .cast = .{ + .id = .pointer_to_array_to_pointer_to_many, //.array_to_pointer, + .type = type_index, + .value = v, + }, + }); + try builder.appendInstruction(unit, context, cast); + break :blk .{ + .value = .{ + .runtime = cast, + }, + .type = type_index, + }; + }, else => |t| @panic(@tagName(t)), }, else => |t| @panic(@tagName(t)), - }; + } + }, + false => { + const v = try builder.resolveRuntimeValue(unit, context, type_expect, node.left, .left); + break :blk v; }, }, else => |t| @panic(@tagName(t)), }, + .none => { + const value_pointer = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, node.left, .left); + break :blk value_pointer; + }, else => |t| @panic(@tagName(t)), } }, @@ -4203,71 +4645,75 @@ pub const Builder = struct { true => unreachable, false => switch (unit.types.get(pointer.type).*) { .slice => |slice| b: { - const load = try unit.instructions.append(context.allocator, .{ - .load = .{ - .value = array_like_expression, - }, - }); - try builder.appendInstruction(unit, context, load); - - const pointer_extract_value = try unit.instructions.append(context.allocator, .{ - .extract_value = .{ - .expression = .{ - .value = .{ - .runtime = load, - }, - .type = pointer.type, - }, - .index = 0, - }, - }); - try builder.appendInstruction(unit, context, pointer_extract_value); - const gep = try unit.instructions.append(context.allocator, .{ .get_element_pointer = .{ - .pointer = pointer_extract_value, //pointer_extract_value, - .base_type = slice.child_type, - .index = index, + .pointer = array_like_expression.value.runtime, + .base_type = pointer.type, + .is_struct = true, + .index = .{ + .value = .{ + .@"comptime" = .{ + .constant_int = .{ + .value = 0, + }, + }, + }, + .type = .u32, + }, }, }); try builder.appendInstruction(unit, context, gep); - const gep_type = try unit.getPointerType(context, .{ - .type = slice.child_type, - .termination = .none, + const pointer_to_slice_pointer = try unit.getPointerType(context, .{ + .type = slice.child_pointer_type, .mutability = pointer.mutability, + .termination = .none, .many = false, .nullable = false, }); + const pointer_load = try unit.instructions.append(context.allocator, .{ + .load = .{ + .value = .{ + .value = .{ + .runtime = gep, + }, + .type = pointer_to_slice_pointer, + }, + .type = slice.child_pointer_type, + }, + }); + try builder.appendInstruction(unit, context, pointer_load); + + const slice_pointer_gep = try unit.instructions.append(context.allocator, .{ + .get_element_pointer = .{ + .pointer = pointer_load, + .base_type = slice.child_type, + .is_struct = false, + .index = index, + }, + }); + try builder.appendInstruction(unit, context, slice_pointer_gep); + break :b .{ .value = .{ - .runtime = gep, + .runtime = slice_pointer_gep, }, - .type = gep_type, + .type = try unit.getPointerType(context, .{ + .type = slice.child_type, + .mutability = slice.mutability, + .many = false, + .nullable = false, + .termination = .none, + }), }; }, .array => |array| b: { - const loaded_array_like = switch (array_like_expression.value) { - .runtime => |instruction_index| switch (unit.instructions.get(instruction_index).*) { - .argument_declaration, .stack_slot => arg: { - const load = try unit.instructions.append(context.allocator, .{ - .load = .{ - .value = array_like_expression, - }, - }); - try builder.appendInstruction(unit, context, load); - break :arg load; - }, - else => |t| @panic(@tagName(t)), - }, - else => |t| @panic(@tagName(t)), - }; - const gep = try unit.instructions.append(context.allocator, .{ .get_element_pointer = .{ - .pointer = loaded_array_like, + .pointer = array_like_expression.value.runtime, .base_type = array.type, + .is_struct = false, .index = index, }, }); @@ -4288,96 +4734,45 @@ pub const Builder = struct { .type = gep_type, }; }, - else => |t| @panic(@tagName(t)), - }, - }, - .slice => |slice| b: { - const array_like_loaded = switch (array_like_expression.value) { - .runtime => |instruction_index| switch (unit.instructions.get(instruction_index).*) { - .stack_slot => stack_slot: { + .pointer => |child_pointer| switch (unit.types.get(child_pointer.type).*) { + .array => |array| b: { const load = try unit.instructions.append(context.allocator, .{ .load = .{ .value = array_like_expression, + .type = pointer.type, }, }); - try builder.appendInstruction(unit, context, load); - break :stack_slot V{ - .value = .{ - .runtime = load, + const gep = try unit.instructions.append(context.allocator, .{ + .get_element_pointer = .{ + .pointer = load, + .base_type = array.type, + .is_struct = false, + .index = index, }, - .type = array_like_expression.type, + }); + try builder.appendInstruction(unit, context, gep); + + const gep_type = try unit.getPointerType(context, .{ + .type = array.type, + .termination = .none, + .mutability = pointer.mutability, + .many = false, + .nullable = false, + }); + + break :b .{ + .value = .{ + .runtime = gep, + }, + .type = gep_type, }; }, else => |t| @panic(@tagName(t)), }, else => |t| @panic(@tagName(t)), - }; - - const pointer_extract_value = try unit.instructions.append(context.allocator, .{ - .extract_value = .{ - .expression = array_like_loaded, - .index = 0, - }, - }); - try builder.appendInstruction(unit, context, pointer_extract_value); - - const instruction = try unit.instructions.append(context.allocator, .{ - .get_element_pointer = .{ - .pointer = pointer_extract_value, - .base_type = slice.child_type, - .index = index, - }, - }); - try builder.appendInstruction(unit, context, instruction); - - const gep_type = try unit.getPointerType(context, .{ - .type = slice.child_type, - .termination = .none, - .mutability = slice.mutability, - .many = false, - .nullable = false, - }); - - break :b .{ - .value = .{ - .runtime = instruction, - }, - .type = gep_type, - }; - }, - .array => |array| b: { - switch (array_like_expression.value) { - .runtime => |instruction_index| switch (unit.instructions.get(instruction_index).*) { - .stack_slot => {}, - else => |t| @panic(@tagName(t)), - }, - else => |t| @panic(@tagName(t)), - } - - const gep = try unit.instructions.append(context.allocator, .{ - .get_element_pointer = .{ - .pointer = array_like_expression.value.runtime, - .base_type = array.type, - .index = index, - }, - }); - - const gep_type = try unit.getPointerType(context, .{ - .type = array.type, - .termination = .none, - .mutability = .@"var", - .many = false, - .nullable = false, - }); - - break :b .{ - .value = .{ - .runtime = gep, - }, - .type = gep_type, - }; + }, }, else => |t| @panic(@tagName(t)), }; @@ -4388,6 +4783,7 @@ pub const Builder = struct { const load = try unit.instructions.append(context.allocator, .{ .load = .{ .value = gep, + .type = unit.types.get(gep.type).pointer.type, }, }); try builder.appendInstruction(unit, context, load); @@ -4469,12 +4865,265 @@ pub const Builder = struct { else => |t| @panic(@tagName(t)), } }, + .negation => { + assert(node.left != .null); + assert(node.right == .null); + const value = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, node.left, .right); + switch (value.value) { + .@"comptime" => |ct| switch (ct) { + .comptime_int => |ct_int| switch (type_expect) { + .type => |type_index| switch (unit.types.get(type_index).*) { + .integer => |integer| { + assert(integer.signedness == .signed); + + var v: i64 = @bitCast(ct_int.value); + if (ct_int.signedness == .signed) { + v = -v; + } + + v = 0 - v; + + return .{ + .value = .{ + .@"comptime" = .{ + .constant_int = .{ + .value = @bitCast(v), + }, + }, + }, + .type = type_index, + }; + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + } + }, else => |t| @panic(@tagName(t)), }; return v; } + fn resolveContainerLiteral(builder: *Builder, unit: *Unit, context: *const Context, nodes: []const Node.Index, type_index: Type.Index) !V { + const container_type = unit.types.get(type_index); + + switch (container_type.*) { + .@"struct" => |struct_index| { + const struct_type = unit.structs.get(struct_index); + const fields = struct_type.fields.items; + var list = try ArrayList(V).initCapacity(context.allocator, fields.len); + var is_comptime = true; + + for (fields) |field_index| { + const field = unit.struct_fields.get(field_index); + + for (nodes) |initialization_node_index| { + const initialization_node = unit.getNode(initialization_node_index); + assert(initialization_node.id == .container_field_initialization); + assert(initialization_node.left != .null); + assert(initialization_node.right == .null); + const field_name = unit.getExpectedTokenBytes(Token.addInt(initialization_node.token, 1), .identifier); + const field_name_hash = try unit.processIdentifier(context, field_name); + if (field_name_hash == field.name) { + const expected_type = field.type; + const field_initialization = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = expected_type }, initialization_node.left, .right); + is_comptime = is_comptime and field_initialization.value == .@"comptime"; + list.appendAssumeCapacity(field_initialization); + break; + } + } else if (field.default_value) |default_value| { + list.appendAssumeCapacity(.{ + .value = .{ + .@"comptime" = default_value, + }, + .type = field.type, + }); + } else { + @panic("Missing field"); + } + } + + switch (struct_type.backing_type) { + .null => { + if (is_comptime) { + var comptime_list = try ArrayList(V.Comptime).initCapacity(context.allocator, fields.len); + for (list.items) |item| { + comptime_list.appendAssumeCapacity(item.value.@"comptime"); + } + + return .{ + .value = .{ + .@"comptime" = .{ + .constant_struct = try unit.constant_structs.append(context.allocator, .{ + .fields = comptime_list.items, + .type = type_index, + }), + }, + }, + .type = type_index, + }; + } else { + var struct_initialization = V{ + .value = .{ + .@"comptime" = .undefined, + }, + .type = type_index, + }; + + for (list.items, 0..) |field, index| { + const struct_initialization_instruction = try unit.instructions.append(context.allocator, .{ + .insert_value = .{ + .expression = struct_initialization, + .index = @intCast(index), + .new_value = field, + }, + }); + + try builder.appendInstruction(unit, context, struct_initialization_instruction); + + struct_initialization.value = .{ + .runtime = struct_initialization_instruction, + }; + } + + return struct_initialization; + } + }, + else => { + const backing_integer_type = unit.types.get(struct_type.backing_type).integer; + assert(backing_integer_type.signedness == .unsigned); + if (is_comptime) { + var bit_offset: u32 = 0; + var value: u64 = 0; + for (list.items) |field| { + const field_type = unit.types.get(field.type); + const bit_size = field_type.getBitSize(unit); + const field_value: u64 = switch (field.value.@"comptime") { + .constant_int => |constant_int| constant_int.value, + .bool => |boolean| @intFromBool(boolean), + .comptime_int => |ct_int| switch (ct_int.signedness) { + .unsigned => ct_int.value, + .signed => unreachable, + }, + else => |t| @panic(@tagName(t)), + }; + const value_with_offset = field_value << @as(u6, @intCast(bit_offset)); + value |= value_with_offset; + bit_offset += bit_size; + } + + return .{ + .value = .{ + .@"comptime" = .{ + .constant_backed_struct = value, + }, + }, + .type = type_index, + }; + } else { + const zero_extend = try unit.instructions.append(context.allocator, .{ + .cast = .{ + .id = .zero_extend, + .value = list.items[0], + .type = struct_type.backing_type, + }, + }); + try builder.appendInstruction(unit, context, zero_extend); + var value = V{ + .value = .{ + .runtime = zero_extend, + }, + .type = struct_type.backing_type, + }; + + const first_field_type = unit.types.get(list.items[0].type); + var bit_offset = first_field_type.getBitSize(unit); + + for (list.items[1..]) |field| { + const field_type = unit.types.get(field.type); + const field_bit_size = field_type.getBitSize(unit); + defer bit_offset += field_bit_size; + + switch (field.value) { + .@"comptime" => |ct| { + _ = ct; // autofix + unreachable; + }, + .runtime => { + const field_zero_extend = try unit.instructions.append(context.allocator, .{ + .cast = .{ + .id = .zero_extend, + .value = field, + .type = struct_type.backing_type, + }, + }); + try builder.appendInstruction(unit, context, field_zero_extend); + + const shift_left = try unit.instructions.append(context.allocator, .{ + .integer_binary_operation = .{ + .id = .shift_left, + .left = .{ + .value = .{ + .runtime = field_zero_extend, + }, + .type = struct_type.backing_type, + }, + .right = .{ + .value = .{ + .@"comptime" = .{ + .constant_int = .{ + .value = bit_offset, + }, + }, + }, + .type = struct_type.backing_type, + }, + .signedness = backing_integer_type.signedness, + }, + }); + + try builder.appendInstruction(unit, context, shift_left); + + const merge_or = try unit.instructions.append(context.allocator, .{ + .integer_binary_operation = .{ + .id = .bit_or, + .signedness = backing_integer_type.signedness, + .left = .{ + .value = .{ + .runtime = shift_left, + }, + .type = struct_type.backing_type, + }, + .right = value, + }, + }); + try builder.appendInstruction(unit, context, merge_or); + + value = .{ + .value = .{ + .runtime = merge_or, + }, + .type = struct_type.backing_type, + }; + }, + else => |t| @panic(@tagName(t)), + } + } + + return value; + } + }, + } + }, + else => |t| @panic(@tagName(t)), + } + } + fn resolveArrayLiteral(builder: *Builder, unit: *Unit, context: *const Context, array_type_expect: Type.Expect, nodes: []const Node.Index, type_node_index: Node.Index) !V { const expression_element_count = nodes.len; const array_type_index = switch (array_type_expect) { @@ -4500,9 +5149,9 @@ pub const Builder = struct { if (array_type.count != expression_element_count) @panic("Array element count mismatch"); var is_comptime = true; - var values = try ArrayList(V).initCapacity(context.allocator, nodes.len); + var values = try ArrayList(V).initCapacity(context.allocator, nodes.len); for (nodes) |node_index| { - const value = try builder.resolveRuntimeValue(unit, context, Type.Expect { .type = array_type.type }, node_index, .right); + const value = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = array_type.type }, node_index, .right); // assert(value.value == .@"comptime"); is_comptime = is_comptime and value.value == .@"comptime"; values.appendAssumeCapacity(value); @@ -4527,7 +5176,7 @@ pub const Builder = struct { .@"comptime" = .{ .constant_array = constant_array, }, - }, + }, // TODO: avoid hash lookup .type = try unit.getArrayType(context, array_type), }; @@ -4560,7 +5209,7 @@ pub const Builder = struct { } } - fn resolveCall(builder: *Builder, unit: *Unit, context: *const Context, node_index: Node.Index) !V{ + fn resolveCall(builder: *Builder, unit: *Unit, context: *const Context, node_index: Node.Index) !V { const node = unit.getNode(node_index); assert(node.left != .null); @@ -4568,14 +5217,15 @@ pub const Builder = struct { const left_node = unit.getNode(node.left); var argument_list = ArrayList(V){}; - const callable = switch (left_node.id) { - .field_access => b: { - const field_access_left = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, left_node.left, .right); + const callable: V = switch (left_node.id) { + .field_access => blk: { const right_identifier_node = unit.getNode(left_node.right); assert(right_identifier_node.id == .identifier); const right_identifier = unit.getExpectedTokenBytes(right_identifier_node.token, .identifier); const right_identifier_hash = try unit.processIdentifier(context, right_identifier); + const field_access_left = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, left_node.left, .left); + switch (field_access_left.value) { .@"comptime" => |ct| switch (ct) { .type => |type_index| switch (unit.types.get(type_index).*) { @@ -4583,58 +5233,290 @@ pub const Builder = struct { const container_type = unit.types.get(type_index); const container_scope = container_type.getScope(unit); const look_in_parent_scopes = false; + if (container_scope.lookupDeclaration(right_identifier_hash, look_in_parent_scopes)) |lookup| { - const global_decl_ref = try builder.referenceGlobalDeclaration(unit, context, lookup.scope, lookup.declaration); - break :b global_decl_ref; + const global = try builder.referenceGlobalDeclaration(unit, context, lookup.scope, lookup.declaration); + switch (global.initial_value) { + .function_definition => break :blk .{ + .value = .{ + .@"comptime" = .{ + .global = global, + }, + }, + .type = global.declaration.type, + }, + else => |t| @panic(@tagName(t)), + } } else { unreachable; } }, else => |t| @panic(@tagName(t)), }, - .constant_struct => |constant_struct_index| { - _ = constant_struct_index; // autofix - // const constant_struct = unit.constant_structs.get(constant_struct_index); - const struct_type = unit.structs.get(unit.types.get(field_access_left.type).@"struct" ); - for (struct_type.fields.items) |field_index| { - const field = unit.struct_fields.get(field_index); - if (field.name == right_identifier_hash) { - unreachable; - } - } else { - const look_in_parent_scopes = false; - if (struct_type.scope.scope.lookupDeclaration(right_identifier_hash, look_in_parent_scopes)) |lookup| { - const global_decl_ref = try builder.referenceGlobalDeclaration(unit, context, lookup.scope, lookup.declaration); - switch (global_decl_ref.value) { - .function_reference => |function_declaration| { - const function_prototype_type = unit.types.get( function_declaration.declaration.type); - const function_prototype = unit.function_prototypes.get(function_prototype_type.function); + .global => |global| { + switch (unit.types.get(global.declaration.type).*) { + .@"struct" => |struct_index| { + const struct_type = unit.structs.get(struct_index); + for (struct_type.fields.items) |field_index| { + const field = unit.struct_fields.get(field_index); + if (field.name == right_identifier_hash) { + unreachable; + } + } else { + const look_in_parent_scopes = false; + if (struct_type.scope.scope.lookupDeclaration(right_identifier_hash, look_in_parent_scopes)) |lookup| { + const right_symbol = try builder.referenceGlobalDeclaration(unit, context, lookup.scope, lookup.declaration); + switch (right_symbol.initial_value) { + .function_definition => { + const function_type_index = right_symbol.declaration.type; + const function_prototype_index = unit.types.get(function_type_index).function; + const function_prototype = unit.function_prototypes.get(function_prototype_index); + if (function_prototype.argument_types.len == 0) { + unreachable; + } - if (function_prototype.argument_types.len == 0) { - unreachable; + const first_argument_type_index = function_prototype.argument_types[0]; + if (first_argument_type_index == field_access_left.type) { + try argument_list.append(context.allocator, field_access_left); + break :blk V{ + .value = .{ + .@"comptime" = .{ + .global = right_symbol, + }, + }, + .type = function_type_index, + }; + } else { + unreachable; + } + }, + else => |t| @panic(@tagName(t)), } - - const first_argument_type_index = function_prototype.argument_types[0]; - if (first_argument_type_index == field_access_left.type) { - try argument_list.append(context.allocator, field_access_left); - break :b global_decl_ref; - } else { - unreachable; - } - }, - else => |t| @panic(@tagName(t)), + } else { + unreachable; + } } - unreachable; - // break :b global_decl_ref; - } else { - unreachable; - } + }, + else => |t| @panic(@tagName(t)), } }, else => |t| @panic(@tagName(t)), }, .runtime => |instruction_index| { switch (unit.types.get(field_access_left.type).*) { + .pointer => |pointer| switch (unit.types.get(pointer.type).*) { + .@"struct" => |struct_index| { + const struct_type = unit.structs.get(struct_index); + + for (struct_type.fields.items, 0..) |field_index, index| { + const field = unit.struct_fields.get(field_index); + if (field.name == right_identifier_hash) { + switch (unit.types.get(field.type).*) { + .pointer => |field_pointer_type| switch (unit.types.get(field_pointer_type.type).*) { + .function => { + assert(field_pointer_type.mutability == .@"const"); + assert(!field_pointer_type.nullable); + const gep = try unit.instructions.append(context.allocator, .{ + .get_element_pointer = .{ + .pointer = instruction_index, + .base_type = pointer.type, + .is_struct = true, + .index = .{ + .value = .{ + .@"comptime" = .{ + .constant_int = .{ + .value = index, + }, + }, + }, + .type = .u32, + }, + }, + }); + try builder.appendInstruction(unit, context, gep); + + const load = try unit.instructions.append(context.allocator, .{ + .load = .{ + .value = .{ + .value = .{ + .runtime = gep, + }, + .type = try unit.getPointerType(context, .{ + .type = field.type, + .many = false, + .nullable = false, + .mutability = .@"const", + .termination = .none, + }), + }, + .type = field.type, + }, + }); + + try builder.appendInstruction(unit, context, load); + break :blk .{ + .value = .{ + .runtime = load, + }, + .type = field.type, + }; + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + } + unreachable; + } + } else { + const look_in_parent_scopes = false; + if (struct_type.scope.scope.lookupDeclaration(right_identifier_hash, look_in_parent_scopes)) |lookup| { + const right_symbol = try builder.referenceGlobalDeclaration(unit, context, lookup.scope, lookup.declaration); + switch (right_symbol.initial_value) { + .function_definition => { + const function_type_index = right_symbol.declaration.type; + const function_prototype_index = unit.types.get(function_type_index).function; + const function_prototype = unit.function_prototypes.get(function_prototype_index); + if (function_prototype.argument_types.len == 0) { + unreachable; + } + + const first_argument_type_index = function_prototype.argument_types[0]; + if (first_argument_type_index == field_access_left.type) { + try argument_list.append(context.allocator, field_access_left); + break :blk V{ + .value = .{ + .@"comptime" = .{ + .global = right_symbol, + }, + }, + .type = function_type_index, + }; + } else if (first_argument_type_index == pointer.type) { + const load = try unit.instructions.append(context.allocator, .{ + .load = .{ + .value = field_access_left, + .type = first_argument_type_index, + }, + }); + try builder.appendInstruction(unit, context, load); + + try argument_list.append(context.allocator, .{ + .value = .{ + .runtime = load, + }, + .type = first_argument_type_index, + }); + + break :blk V{ + .value = .{ + .@"comptime" = .{ + .global = right_symbol, + }, + }, + .type = function_type_index, + }; + } else { + const symbol_name = unit.getIdentifier(right_symbol.declaration.name); + _ = symbol_name; // autofix + const decl_arg_type_index = first_argument_type_index; + const field_access_left_type_index = field_access_left.type; + const result = try builder.typecheck(unit, context, decl_arg_type_index, field_access_left_type_index); + switch (result) { + else => |t| @panic(@tagName(t)), + } + } + }, + else => |t| @panic(@tagName(t)), + } + } else { + unreachable; + } + } + }, + .pointer => |child_pointer| switch (unit.types.get(child_pointer.type).*) { + .@"struct" => |struct_index| { + const struct_type = unit.structs.get(struct_index); + for (struct_type.fields.items, 0..) |field_index, index| { + const field = unit.struct_fields.get(field_index); + if (field.name == right_identifier_hash) { + switch (unit.types.get(field.type).*) { + .pointer => |field_pointer_type| switch (unit.types.get(field_pointer_type.type).*) { + .function => { + const first_load = try unit.instructions.append(context.allocator, .{ + .load = .{ + .value = field_access_left, + .type = pointer.type, + }, + }); + try builder.appendInstruction(unit, context, first_load); + + assert(field_pointer_type.mutability == .@"const"); + assert(!field_pointer_type.nullable); + + const gep = try unit.instructions.append(context.allocator, .{ + .get_element_pointer = .{ + .pointer = first_load, + .base_type = child_pointer.type, + .is_struct = true, + .index = .{ + .value = .{ + .@"comptime" = .{ + .constant_int = .{ + .value = index, + }, + }, + }, + .type = .u32, + }, + }, + }); + try builder.appendInstruction(unit, context, gep); + + const load = try unit.instructions.append(context.allocator, .{ + .load = .{ + .value = .{ + .value = .{ + .runtime = gep, + }, + .type = try unit.getPointerType(context, .{ + .type = field.type, + .many = false, + .nullable = false, + .mutability = .@"const", + .termination = .none, + }), + }, + .type = field.type, + }, + }); + try builder.appendInstruction(unit, context, load); + + break :blk .{ + .value = .{ + .runtime = load, + }, + .type = field.type, + }; + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + } + } + } else { + const look_in_parent_scopes = false; + if (struct_type.scope.scope.lookupDeclaration(right_identifier_hash, look_in_parent_scopes)) |lookup| { + _ = lookup; // autofix + unreachable; + } else { + unreachable; + } + } + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }, .@"struct" => |struct_index| { const struct_type = unit.structs.get(struct_index); for (struct_type.fields.items) |field_index| { @@ -4645,12 +5527,12 @@ pub const Builder = struct { } else { const look_in_parent_scopes = false; if (struct_type.scope.scope.lookupDeclaration(right_identifier_hash, look_in_parent_scopes)) |lookup| { - const global_decl_ref = try builder.referenceGlobalDeclaration(unit, context, lookup.scope, lookup.declaration); - switch (global_decl_ref.value) { - .function_reference => |function_declaration| { - const function_prototype_type = unit.types.get( function_declaration.declaration.type); - const function_prototype = unit.function_prototypes.get(function_prototype_type.function); - + const right_symbol = try builder.referenceGlobalDeclaration(unit, context, lookup.scope, lookup.declaration); + switch (right_symbol.initial_value) { + .function_definition => { + const function_type_index = right_symbol.declaration.type; + const function_prototype_index = unit.types.get(function_type_index).function; + const function_prototype = unit.function_prototypes.get(function_prototype_index); if (function_prototype.argument_types.len == 0) { unreachable; } @@ -4658,15 +5540,20 @@ pub const Builder = struct { const first_argument_type_index = function_prototype.argument_types[0]; if (first_argument_type_index == field_access_left.type) { try argument_list.append(context.allocator, field_access_left); - break :b global_decl_ref; + break :blk V{ + .value = .{ + .@"comptime" = .{ + .global = right_symbol, + }, + }, + .type = function_type_index, + }; } else { unreachable; } }, else => |t| @panic(@tagName(t)), } - unreachable; - // break :b global_decl_ref; } else { unreachable; } @@ -4674,7 +5561,6 @@ pub const Builder = struct { }, else => |t| @panic(@tagName(t)), } - _ = instruction_index; // autofix }, else => |t| @panic(@tagName(t)), } @@ -4682,62 +5568,135 @@ pub const Builder = struct { .identifier => blk: { const identifier = unit.getExpectedTokenBytes(left_node.token, .identifier); const result = try builder.resolveIdentifier(unit, context, Type.Expect.none, identifier, .left); - break :blk result; + break :blk switch (result.value) { + .@"comptime" => |ct| switch (ct) { + .global => |global| switch (global.initial_value) { + .function_definition => .{ + .value = .{ + .@"comptime" = .{ + .global = global, + }, + }, + .type = global.declaration.type, + }, + // This is a comptime alias + .global => |function_declaration| switch (function_declaration.initial_value) { + .function_definition => .{ + .value = .{ + .@"comptime" = .{ + .global = function_declaration, + }, + }, + .type = function_declaration.declaration.type, + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }, + .runtime => switch (unit.types.get(result.type).*) { + .pointer => |pointer| switch (unit.types.get(pointer.type).*) { + .pointer => |child_pointer| switch (unit.types.get(child_pointer.type).*) { + .function => b: { + const load = try unit.instructions.append(context.allocator, .{ + .load = .{ + .value = result, + .type = pointer.type, + }, + }); + try builder.appendInstruction(unit, context, load); + + break :b .{ + .value = .{ + .runtime = load, + }, + .type = pointer.type, + }; + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }; }, else => |t| @panic(@tagName(t)), }; - switch (callable.value) { - .function_reference => |function_declaration| { - const function_definition_index = function_declaration.getFunctionDefinitionIndex(); - const function = unit.function_definitions.get(function_definition_index); - const function_type = unit.types.get( function.type); - const function_prototype = unit.function_prototypes.get(function_type.function); - const argument_nodes = unit.getNodeList(node.right); - const argument_declaration_count = function.scope.scope.declarations.count(); - - // Argument list holds already the value of the member value - if (argument_nodes.len + argument_list.items.len != argument_declaration_count) { - @panic("Argument count mismatch"); - } - - try argument_list.ensureTotalCapacity(context.allocator, argument_declaration_count); - - const argument_offset = argument_list.items.len; - for (argument_nodes, function.scope.scope.declarations.values()[argument_offset..]) |arg_ni, argument_declaration| { - const argument_node = unit.getNode(arg_ni); - const arg_type_expect = Type.Expect{ - .type = argument_declaration.type, - }; - const argument_node_index = switch (argument_node.id) { - .named_argument => argument_node.right, - else => arg_ni, - }; - const argument_value = try builder.resolveRuntimeValue(unit, context, arg_type_expect, argument_node_index, .right); - argument_list.appendAssumeCapacity(argument_value); - } - - const instruction = try unit.instructions.append(context.allocator, .{ - .call = .{ - .callable = .{ - .function_definition = function_declaration, - }, - .function_type = function.type, - .arguments = argument_list.items, - }, - }); - try builder.appendInstruction(unit, context, instruction); - - return .{ - .value = .{ - .runtime = instruction, - }, - .type = function_prototype.return_type, - }; + const function_type_index = switch (unit.types.get(callable.type).*) { + .function => callable.type, + .pointer => |pointer| switch (unit.types.get(pointer.type).*) { + .function => pointer.type, + else => |t| @panic(@tagName(t)), }, - .runtime => unreachable, - else => unreachable, + else => |t| @panic(@tagName(t)), + }; + + const function_prototype = unit.function_prototypes.get(unit.types.get(function_type_index).function); + + const argument_nodes = unit.getNodeList(node.right); + const argument_declaration_count = function_prototype.argument_types.len; + + // Argument list holds already the value of the member value + if (argument_nodes.len + argument_list.items.len != argument_declaration_count) { + @panic("Argument count mismatch"); } + + try argument_list.ensureTotalCapacity(context.allocator, argument_declaration_count); + + const argument_offset = argument_list.items.len; + for (argument_nodes, function_prototype.argument_types[argument_offset..]) |arg_ni, argument_type_index| { + const argument_node = unit.getNode(arg_ni); + const arg_type_expect = Type.Expect{ + .type = argument_type_index, + }; + const argument_node_index = switch (argument_node.id) { + .named_argument => argument_node.right, + else => arg_ni, + }; + const argument_value = try builder.resolveRuntimeValue(unit, context, arg_type_expect, argument_node_index, .right); + argument_list.appendAssumeCapacity(argument_value); + } + + for (function_prototype.argument_types, argument_list.items, 0..) |argument_type, argument_value, i| { + _ = i; // autofix + if (argument_type != argument_value.type) { + switch (unit.types.get(argument_type).*) { + .pointer => |dst_ptr| switch (unit.types.get(argument_value.type).*) { + .pointer => |src_ptr| { + std.debug.print("Declaration: {}\nCall: {}\n", .{ dst_ptr, src_ptr }); + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + } + + @panic("Type mismatch"); + } + } + + const instruction = try unit.instructions.append(context.allocator, .{ + .call = .{ + .callable = callable, + .function_type = function_type_index, + .arguments = argument_list.items, + }, + }); + try builder.appendInstruction(unit, context, instruction); + + if (function_prototype.return_type == .noreturn) { + try builder.buildUnreachable(unit, context); + } + + return .{ + .value = .{ + .runtime = instruction, + }, + .type = function_prototype.return_type, + }; } fn emitLocalVariableDeclaration(builder: *Builder, unit: *Unit, context: *const Context, token: Token.Index, mutability: Mutability, declaration_type: Type.Index, initialization: V, emit: bool, maybe_name: ?[]const u8) !Instruction.Index { @@ -4792,13 +5751,13 @@ pub const Builder = struct { assert(builder.current_scope.kind == .block); const local_scope = @fieldParentPtr(Debug.Scope.Local, "scope", builder.current_scope); try local_scope.local_declaration_map.putNoClobber(context.allocator, local_declaration, stack); - + const debug_declare_local = try unit.instructions.append(context.allocator, .{ .debug_declare_local_variable = .{ .variable = local_declaration, .stack = stack, }, - }); + }); try builder.appendInstruction(unit, context, debug_declare_local); @@ -4812,7 +5771,7 @@ pub const Builder = struct { }, .source = initialization, }, - }); + }); try builder.appendInstruction(unit, context, store); @@ -4836,8 +5795,8 @@ pub const Builder = struct { .local = builder.current_scope.local, .file = builder.current_file, }, - }, - }); + }, + }); const block = unit.blocks.get(block_index); if (builder.current_basic_block != .null) { @@ -4870,7 +5829,8 @@ pub const Builder = struct { }, statement_node_index); }, .constant_symbol_declaration, - .variable_symbol_declaration, => { + .variable_symbol_declaration, + => { // All variables here are local assert(builder.current_scope.local); const expected_identifier_token_index = Token.addInt(statement_node.token, 1); @@ -4928,7 +5888,7 @@ pub const Builder = struct { assert(builder.current_basic_block != builder.return_block); - try builder.jump(unit, context, builder.return_block); + try builder.jump(unit, context, builder.return_block); } else if (builder.exit_blocks.items.len > 0) { builder.return_phi = try unit.instructions.append(context.allocator, .{ .phi = .{ @@ -4939,9 +5899,9 @@ pub const Builder = struct { builder.return_block = try builder.newBasicBlock(unit, context); const current_basic_block = builder.current_basic_block; builder.current_basic_block = builder.return_block; - + try builder.appendInstruction(unit, context, builder.return_phi); - + const phi = &unit.instructions.get(builder.return_phi).phi; try phi.values.append(context.allocator, return_value); try phi.basic_blocks.append(context.allocator, current_basic_block); @@ -4952,16 +5912,16 @@ pub const Builder = struct { }, .type = return_type, }); - + builder.current_basic_block = current_basic_block; try builder.jump(unit, context, builder.return_block); } else { try builder.buildRet(unit, context, return_value); } }, - .call => { + .call => { const result = try builder.resolveCall(unit, context, statement_node_index); - assert(result.type == .void or result.type == .@"noreturn"); + assert(result.type == .void or result.type == .noreturn); }, .@"switch" => { const expression_to_switch_on = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, statement_node.left, .right); @@ -4985,7 +5945,7 @@ pub const Builder = struct { }; } else typecheck_enum_result.else_switch_case_group_index orelse unreachable; const true_switch_case_node = unit.getNode(case_nodes[group_index]); - _ = try builder.resolveRuntimeValue(unit, context, Type.Expect { .type = .void }, true_switch_case_node.right, .right); + _ = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = .void }, true_switch_case_node.right, .right); }, else => |t| @panic(@tagName(t)), }, @@ -5004,9 +5964,9 @@ pub const Builder = struct { try builder.jump(unit, context, loop_header_block); builder.current_basic_block = loop_header_block; - const condition = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = .bool }, statement_node.left, .right); - const body_block = try builder.newBasicBlock(unit, context); - const exit_block = try builder.newBasicBlock(unit, context); + const condition = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = .bool }, statement_node.left, .right); + const body_block = try builder.newBasicBlock(unit, context); + const exit_block = try builder.newBasicBlock(unit, context); const old_loop_exit_block = builder.loop_exit_block; defer builder.loop_exit_block = old_loop_exit_block; @@ -5020,9 +5980,8 @@ pub const Builder = struct { const body_value = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = .void }, statement_node.right, .right); _ = body_value; // autofix - // _ = body_value; // autofix try builder.jump(unit, context, loop_header_block); - + builder.current_basic_block = exit_block; }, .@"comptime" => |ct| switch (ct) { @@ -5044,68 +6003,6 @@ pub const Builder = struct { else => unreachable, } }, - .if_else_payload => { - assert(statement_node.left != .null); - assert(statement_node.right != .null); - const payload_node = unit.getNode(statement_node.right); - assert(payload_node.id == .identifier); - - const if_else_node = unit.getNode(statement_node.left); - assert(if_else_node.id == .if_else); - assert(if_else_node.left != .null); - assert(if_else_node.right != .null); - - const if_node = unit.getNode(if_else_node.left); - assert(if_node.id == .@"if"); - assert(if_node.left != .null); - assert(if_node.right != .null); - - const optional_expression = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, if_node.left, .right); - - const taken_expression_node_index = if_node.right; - const not_taken_expression_node_index = if_else_node.right; - - switch (optional_expression.value) { - .runtime => { - switch (unit.types.get(optional_expression.type).*) { - .@"struct" => |struct_index| { - const struct_type = unit.structs.get(struct_index); - if (struct_type.optional) { - const condition = try unit.instructions.append(context.allocator, .{ - .extract_value = .{ - .expression = optional_expression, - .index = 1, - }, - }); - try builder.appendInstruction(unit, context, condition); - - try builder.resolveBranch(unit, context, Type.Expect{ .type = .void }, condition, taken_expression_node_index, not_taken_expression_node_index, payload_node.token); - } else { - unreachable; - } - }, - // .optional => |optional_element_type_index| { - // }, - else => |t| @panic(@tagName(t)), - } - }, - else => |t| @panic(@tagName(t)), - } - }, - .@"if" => { - assert(statement_node.left != .null); - assert(statement_node.right != .null); - const condition = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = .bool }, statement_node.left, .right); - const taken_expression_node_index = statement_node.right; - const not_taken_expression_node_index = .null; - switch (condition.value) { - .@"comptime" => unreachable, - .runtime => |condition_instruction| { - try builder.resolveBranch(unit, context, Type.Expect{ .type = .void }, condition_instruction, taken_expression_node_index, not_taken_expression_node_index, .null); - }, - else => unreachable, - } - }, .for_loop => { assert(statement_node.left != .null); assert(statement_node.right != .null); @@ -5121,20 +6018,18 @@ pub const Builder = struct { } const count = slices_and_range_node.len; - // var slice_init_instructions = ArrayList(Instruction.Index){}; - // var slice_init_values = ArrayList(V){}; var slices = ArrayList(V){}; const last_element_node_index = slices_and_range_node[count - 1]; const last_element_node = unit.getNode(last_element_node_index); const last_element_payload = unit.getNode(payloads[count - 1]); - const LoopCounter = struct{ + const LoopCounter = struct { stack_slot: Instruction.Index, end: V, }; - for (slices_and_range_node[0..count - 1]) |slice_or_range_node_index| { + for (slices_and_range_node[0 .. count - 1]) |slice_or_range_node_index| { const slice = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, slice_or_range_node_index, .right); try slices.append(context.allocator, slice); } @@ -5147,10 +6042,8 @@ pub const Builder = struct { const emit = true; const stack_slot = try builder.emitLocalVariableDeclaration(unit, context, last_element_payload.token, .@"var", .usize, range_start, emit, null); // This is put up here so that the length is constant throughout the loop and we dont have to load the variable unnecessarily - - const range_end = switch (last_element_node.right) { - .null => switch (unit.types.get( slices.items[0].type).*) { + .null => switch (unit.types.get(slices.items[0].type).*) { .slice => b: { const len_extract_instruction = try unit.instructions.append(context.allocator, .{ .extract_value = .{ @@ -5225,14 +6118,23 @@ pub const Builder = struct { try builder.jump(unit, context, loop_header); builder.current_basic_block = loop_header; + const pointer_to_usize = try unit.getPointerType(context, .{ + .type = .usize, + .mutability = .@"const", + .nullable = false, + .many = false, + .termination = .none, + }); + const load = try unit.instructions.append(context.allocator, .{ .load = .{ .value = .{ .value = .{ .runtime = loop_counter.stack_slot, }, - .type = .usize, + .type = pointer_to_usize, }, + .type = .usize, }, }); @@ -5253,8 +6155,8 @@ pub const Builder = struct { }); try builder.appendInstruction(unit, context, compare); - const body_block = try builder.newBasicBlock(unit, context); - const exit_block = try builder.newBasicBlock(unit, context); + const body_block = try builder.newBasicBlock(unit, context); + const exit_block = try builder.newBasicBlock(unit, context); try builder.branch(unit, context, compare, body_block, exit_block); builder.current_basic_block = body_block; @@ -5271,12 +6173,13 @@ pub const Builder = struct { .value = .{ .runtime = loop_counter.stack_slot, }, - .type = .usize, + .type = pointer_to_usize, }, - }, - }); + .type = .usize, + }, + }); try builder.appendInstruction(unit, context, load_i); - + for (payloads[0..not_range_len], slices.items) |payload_node_index, slice| { const pointer_extract_value = try unit.instructions.append(context.allocator, .{ .extract_value = .{ @@ -5285,18 +6188,19 @@ pub const Builder = struct { }, }); try builder.appendInstruction(unit, context, pointer_extract_value); - + const slice_type = unit.types.get(slice.type).slice; const gep = try unit.instructions.append(context.allocator, .{ .get_element_pointer = .{ .pointer = pointer_extract_value, .base_type = slice_type.child_type, + .is_struct = false, .index = .{ .value = .{ .runtime = load_i, }, - .type = .usize, + .type = .u32, }, }, }); @@ -5313,8 +6217,9 @@ pub const Builder = struct { }, .type = slice_type.child_pointer_type, }, - }, - }); + .type = slice_type.child_type, + }, + }); try builder.appendInstruction(unit, context, load_gep); break :vblk load_gep; }, @@ -5346,8 +6251,9 @@ pub const Builder = struct { .value = .{ .runtime = loop_counter.stack_slot, }, - .type = .usize, + .type = pointer_to_usize, }, + .type = .usize, }, }); @@ -5367,14 +6273,14 @@ pub const Builder = struct { .constant_int = .{ .value = 1, }, - }, }, - .type = .usize, }, - .id = .add, - .signedness = .unsigned, + .type = .usize, }, - }); + .id = .add, + .signedness = .unsigned, + }, + }); try builder.appendInstruction(unit, context, increment); @@ -5392,8 +6298,8 @@ pub const Builder = struct { }, .type = .usize, }, - }, - }); + }, + }); try builder.appendInstruction(unit, context, increment_store); @@ -5404,6 +6310,56 @@ pub const Builder = struct { .break_expression => { try builder.jump(unit, context, builder.loop_exit_block); }, + .@"if" => { + assert(statement_node.left != .null); + assert(statement_node.right != .null); + const condition = try builder.resolveRuntimeValue(unit, context, Type.Expect{ .type = .bool }, statement_node.left, .right); + const taken_expression_node_index = statement_node.right; + const not_taken_expression_node_index = .null; + switch (condition.value) { + .@"comptime" => unreachable, + .runtime => |condition_instruction| { + try builder.resolveBranch(unit, context, Type.Expect{ .type = .void }, condition_instruction, taken_expression_node_index, not_taken_expression_node_index, .null, null); + }, + else => unreachable, + } + }, + .if_else_payload => { + assert(statement_node.left != .null); + assert(statement_node.right != .null); + + const if_else_node = unit.getNode(statement_node.left); + assert(if_else_node.id == .if_else); + assert(if_else_node.left != .null); + assert(if_else_node.right != .null); + + const if_node = unit.getNode(if_else_node.left); + assert(if_node.id == .@"if"); + assert(if_node.left != .null); + assert(if_node.right != .null); + + try builder.resolveBranchPayload(unit, context, .{ + .payload_node_index = statement_node.right, + .optional_node_index = if_node.left, + .taken_expression_node_index = if_node.right, + .not_taken_expression_node_index = if_else_node.right, + }); + }, + .if_payload => { + assert(statement_node.left != .null); + assert(statement_node.right != .null); + const if_node = unit.getNode(statement_node.left); + assert(if_node.id == .@"if"); + assert(if_node.left != .null); + assert(if_node.right != .null); + + try builder.resolveBranchPayload(unit, context, .{ + .payload_node_index = statement_node.right, + .optional_node_index = if_node.left, + .taken_expression_node_index = if_node.right, + .not_taken_expression_node_index = .null, + }); + }, else => |t| @panic(@tagName(t)), } } @@ -5411,7 +6367,99 @@ pub const Builder = struct { return block_index; } - fn resolveBranch(builder: *Builder, unit: *Unit, context: *const Context, type_expect: Type.Expect, condition: Instruction.Index, taken_node_index: Node.Index, not_taken_node_index: Node.Index, optional_payload_token: Token.Index) !void { + fn resolveBranchPayload(builder: *Builder, unit: *Unit, context: *const Context, arguments: struct { + payload_node_index: Node.Index, + optional_node_index: Node.Index, + taken_expression_node_index: Node.Index, + not_taken_expression_node_index: Node.Index, + }) !void { + const optional_expression = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, arguments.optional_node_index, .right); + const payload_node = unit.getNode(arguments.payload_node_index); + + switch (optional_expression.value) { + .runtime => { + switch (unit.types.get(optional_expression.type).*) { + .@"struct" => |struct_index| { + const struct_type = unit.structs.get(struct_index); + if (struct_type.optional) { + assert(struct_type.backing_type == .null); + const condition = try unit.instructions.append(context.allocator, .{ + .extract_value = .{ + .expression = optional_expression, + .index = 1, + }, + }); + try builder.appendInstruction(unit, context, condition); + + try builder.resolveBranch(unit, context, Type.Expect{ .type = .void }, condition, arguments.taken_expression_node_index, arguments.not_taken_expression_node_index, payload_node.token, optional_expression); + } else { + unreachable; + } + }, + .slice => |slice| { + if (slice.nullable) { + const pointer_value = try unit.instructions.append(context.allocator, .{ + .extract_value = .{ + .expression = optional_expression, + .index = 0, + }, + }); + + try builder.appendInstruction(unit, context, pointer_value); + + const condition = try unit.instructions.append(context.allocator, .{ + .integer_compare = .{ + .id = .not_equal, + .left = .{ + .value = .{ + .runtime = pointer_value, + }, + .type = slice.child_pointer_type, + }, + .right = .{ + .value = .{ + .@"comptime" = .null_pointer, + }, + .type = slice.child_pointer_type, + }, + .type = slice.child_pointer_type, + }, + }); + try builder.appendInstruction(unit, context, condition); + try builder.resolveBranch(unit, context, Type.Expect{ .type = .void }, condition, arguments.taken_expression_node_index, arguments.not_taken_expression_node_index, payload_node.token, optional_expression); + } else { + unreachable; + } + }, + .pointer => |pointer| { + if (pointer.nullable) { + const condition = try unit.instructions.append(context.allocator, .{ + .integer_compare = .{ + .id = .not_equal, + .left = optional_expression, + .right = .{ + .value = .{ + .@"comptime" = .null_pointer, + }, + .type = optional_expression.type, + }, + .type = optional_expression.type, + }, + }); + try builder.appendInstruction(unit, context, condition); + try builder.resolveBranch(unit, context, Type.Expect{ .type = .void }, condition, arguments.taken_expression_node_index, arguments.not_taken_expression_node_index, payload_node.token, optional_expression); + } else { + unreachable; + } + }, + else => |t| @panic(@tagName(t)), + } + }, + else => |t| @panic(@tagName(t)), + } + } + + fn resolveBranch(builder: *Builder, unit: *Unit, context: *const Context, type_expect: Type.Expect, condition: Instruction.Index, taken_node_index: Node.Index, not_taken_node_index: Node.Index, optional_payload_token: Token.Index, maybe_optional_value: ?V) !void { const taken_block = try builder.newBasicBlock(unit, context); const exit_block = try builder.newBasicBlock(unit, context); const not_taken_block = if (not_taken_node_index != .null) try builder.newBasicBlock(unit, context) else exit_block; @@ -5420,28 +6468,94 @@ pub const Builder = struct { builder.current_basic_block = taken_block; - if (optional_payload_token != .null) { - const conditional_instruction = unit.instructions.get(condition); - const optional_expression = conditional_instruction.extract_value.expression; - // TODO: avoid local symbol name collisions - const unwrap = try unit.instructions.append(context.allocator, .{ - .extract_value = .{ - .expression = optional_expression, - .index = 0, + if (maybe_optional_value) |optional_value| { + assert(optional_payload_token != .null); + switch (unit.types.get(optional_value.type).*) { + .@"struct" => |struct_index| { + const optional_struct = unit.structs.get(struct_index); + assert(optional_struct.optional); + assert(optional_struct.backing_type == .null); + // TODO: avoid local symbol name collisions + const unwrap = try unit.instructions.append(context.allocator, .{ + .extract_value = .{ + .expression = optional_value, + .index = 0, + }, + }); + try builder.appendInstruction(unit, context, unwrap); + const emit = true; + const optional_payload = unit.struct_fields.get(optional_struct.fields.items[0]); + _ = try builder.emitLocalVariableDeclaration(unit, context, optional_payload_token, .@"const", optional_payload.type, .{ + .value = .{ + .runtime = unwrap, + }, + .type = optional_payload.type, + }, emit, null); }, - }); - try builder.appendInstruction(unit, context, unwrap); - const emit = true; - const optional_type_index = optional_expression.type; - const optional_type = unit.types.get(optional_type_index); - const optional_struct = unit.structs.get(optional_type.@"struct"); - const optional_payload = unit.struct_fields.get( optional_struct.fields.items[0]); - _ = try builder.emitLocalVariableDeclaration(unit, context, optional_payload_token, .@"const", optional_payload.type, .{ - .value = .{ - .runtime = unwrap, + .slice => |slice| { + const not_null_slice = try unit.getSliceType(context, .{ + .child_pointer_type = blk: { + const child_pointer_type = unit.types.get(slice.child_pointer_type).pointer; + + break :blk try unit.getPointerType(context, .{ + .type = child_pointer_type.type, + .termination = child_pointer_type.termination, + .mutability = child_pointer_type.mutability, + .many = child_pointer_type.many, + .nullable = false, + }); + }, + .child_type = slice.child_type, + .termination = slice.termination, + .mutability = slice.mutability, + .nullable = false, + }); + + const unwrap = try unit.instructions.append(context.allocator, .{ + .cast = .{ + .id = .slice_to_not_null, + .value = optional_value, + .type = not_null_slice, + }, + }); + try builder.appendInstruction(unit, context, unwrap); + + const emit = true; + _ = try builder.emitLocalVariableDeclaration(unit, context, optional_payload_token, .@"const", not_null_slice, .{ + .value = .{ + .runtime = unwrap, + }, + .type = not_null_slice, + }, emit, null); }, - .type = optional_payload.type, - }, emit, null); + .pointer => |pointer| { + const pointer_type = try unit.getPointerType(context, .{ + .type = pointer.type, + .termination = pointer.termination, + .mutability = pointer.mutability, + .many = pointer.many, + .nullable = false, + }); + + const unwrap = try unit.instructions.append(context.allocator, .{ + .cast = .{ + .id = .slice_to_not_null, + .value = optional_value, + .type = pointer_type, + }, + }); + try builder.appendInstruction(unit, context, unwrap); + + const emit = true; + _ = try builder.emitLocalVariableDeclaration(unit, context, optional_payload_token, .@"const", pointer_type, .{ + .value = .{ + .runtime = unwrap, + }, + .type = pointer_type, + }, emit, null); + }, + else => |t| @panic(@tagName(t)), + } } _ = try builder.resolveRuntimeValue(unit, context, type_expect, taken_node_index, .right); @@ -5457,7 +6571,6 @@ pub const Builder = struct { } } - builder.current_basic_block = exit_block; } @@ -5469,7 +6582,7 @@ pub const Builder = struct { .taken = taken_block, .not_taken = non_taken_block, }, - }); + }); try builder.appendInstruction(unit, context, br); @@ -5482,9 +6595,9 @@ pub const Builder = struct { const instruction = try unit.instructions.append(context.allocator, .{ .jump = .{ .from = builder.current_basic_block, - .to = new_basic_block, + .to = new_basic_block, }, - }); + }); try builder.appendInstruction(unit, context, instruction); @@ -5525,15 +6638,15 @@ pub const Builder = struct { } } - fn resolveFieldAccess(builder: *Builder, unit: *Unit, context: *const Context, type_expect: Type.Expect, node_index: Node.Index) !V { + fn resolveFieldAccess(builder: *Builder, unit: *Unit, context: *const Context, type_expect: Type.Expect, node_index: Node.Index, side: Side) !V { const node = unit.getNode(node_index); const right_node = unit.getNode(node.right); assert(right_node.id == .identifier); - const identifier = unit.getExpectedTokenBytes(right_node.token,.identifier); + const identifier = unit.getExpectedTokenBytes(right_node.token, .identifier); const identifier_hash = try unit.processIdentifier(context, identifier); const left_node_index = node.left; - const left = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, left_node_index, .right); + const left = try builder.resolveRuntimeValue(unit, context, Type.Expect.none, left_node_index, .left); const result: V = switch (left.value) { .@"comptime" => |ct| switch (ct) { @@ -5542,9 +6655,66 @@ pub const Builder = struct { const scope = left_type.getScope(unit); const look_in_parent_scopes = false; - const result = if (scope.lookupDeclaration(identifier_hash, look_in_parent_scopes)) |lookup| blk: { - const global_decl_ref = try builder.referenceGlobalDeclaration(unit, context, lookup.scope, lookup.declaration); - break :blk global_decl_ref; + const result: V = if (scope.lookupDeclaration(identifier_hash, look_in_parent_scopes)) |lookup| blk: { + const global = try builder.referenceGlobalDeclaration(unit, context, lookup.scope, lookup.declaration); + const pointer_type = try unit.getPointerType(context, .{ + .type = global.declaration.type, + .termination = .none, + .mutability = .@"var", + .many = false, + .nullable = false, + }); + break :blk switch (side) { + .left => switch (global.initial_value) { + .type => |ti| .{ + .value = .{ + .@"comptime" = .{ + .type = ti, + }, + }, + .type = .type, + }, + else => .{ + .value = .{ + .@"comptime" = .{ + .global = global, + }, + }, + .type = pointer_type, + }, + }, + .right => switch (global.declaration.mutability) { + .@"const" => .{ + .value = .{ + .@"comptime" = global.initial_value, + }, + .type = global.declaration.type, + }, + .@"var" => v: { + const load = try unit.instructions.append(context.allocator, .{ + .load = .{ + .value = .{ + .value = .{ + .@"comptime" = .{ + .global = global, + }, + }, + .type = pointer_type, + }, + .type = global.declaration.type, + }, + }); + + try builder.appendInstruction(unit, context, load); + break :v .{ + .value = .{ + .runtime = load, + }, + .type = global.declaration.type, + }; + }, + }, + }; } else switch (left_type.*) { .@"enum" => |enum_index| blk: { const enum_type = unit.enums.get(enum_index); @@ -5559,7 +6729,7 @@ pub const Builder = struct { .@"comptime" = .{ .enum_value = field_index, }, - }, + }, .type = type_index, }; }, @@ -5568,110 +6738,15 @@ pub const Builder = struct { break :b result; }, - .constant_struct => |constant_struct_index| b: { - const constant_struct = unit.constant_structs.get(constant_struct_index); - const type_index = constant_struct.type; - const left_type = unit.types.get(type_index); - const scope = left_type.getScope(unit); - const look_in_parent_scopes = false; - const result = if (scope.lookupDeclaration(identifier_hash, look_in_parent_scopes)) |lookup| blk: { - const global_decl_ref = try builder.referenceGlobalDeclaration(unit, context, lookup.scope, lookup.declaration); - break :blk global_decl_ref; - } else unreachable; - - break :b result; - }, - .constant_array => |constant_array_index| b: { - assert(equal(u8, identifier, "len")); - const constant_array = unit.constant_arrays.get(constant_array_index); - // TODO: typecheck - break :b switch (type_expect) { - .type => |type_index| .{ - .value = .{ - .@"comptime" = .{ - .constant_int = .{ - .value = constant_array.values.len, - }, - }, - }, - .type = type_index, - }, - else => |t| @panic(@tagName(t)), - }; - }, else => |t| @panic(@tagName(t)), }, - .runtime => |instruction_index| b: { - _ = instruction_index; + .runtime => |_| b: { const left_type = unit.types.get(left.type); switch (left_type.*) { - .@"struct" => |struct_index| { - const struct_type = unit.structs.get(struct_index); - const fields = struct_type.fields.items; - - for (fields, 0..) |field_index, i| { - const field = unit.struct_fields.get(field_index); - if (field.name == identifier_hash) { - const extract_value = try unit.instructions.append(context.allocator, .{ - .extract_value = .{ - .expression = left, - .index = @intCast(i), - }, - }); - - try builder.appendInstruction(unit, context, extract_value); - break :b V{ - .value = .{ - .runtime = extract_value, - }, - .type = field.type, - }; - } - } else { - const scope = left_type.getScope(unit); - const look_in_parent_scopes = false; - if (scope.lookupDeclaration(identifier_hash, look_in_parent_scopes)) |lookup| { - const global_decl_ref = try builder.referenceGlobalDeclaration(unit, context, lookup.scope, lookup.declaration); - break :b global_decl_ref; - } else { - unreachable; - } - } - }, - .@"enum" => { - unreachable; - }, - .slice => |slice| { - const slice_field : enum{ - ptr, - len, - } = if (equal(u8, "ptr", identifier)) .ptr else if (equal(u8, "len", identifier)) .len else unreachable; - const field_type = switch (slice_field) { - .ptr => slice.child_pointer_type, - .len => Type.Index.usize, - }; - const field_index = @intFromEnum(slice_field); - - const extract_value = try unit.instructions.append(context.allocator, .{ - .extract_value = .{ - .expression = left, - .index = field_index, - }, - }); - - try builder.appendInstruction(unit, context, extract_value); - - break :b .{ - .value = .{ - .runtime = extract_value, - }, - .type = field_type, - }; - }, .pointer => |pointer| switch (unit.types.get(pointer.type).*) { .array => |array| { + assert(side == .right); assert(equal(u8, identifier, "len")); - break :b switch (type_expect) { .type => |type_index| V{ .value = .{ @@ -5686,23 +6761,321 @@ pub const Builder = struct { else => |t| @panic(@tagName(t)), }; }, - else => |t| @panic(@tagName(t)), - }, - .array => |array| { - assert(equal(u8, identifier, "len")); - break :b switch (type_expect) { - .type => |type_index| V{ - .value = .{ - .@"comptime" = .{ - .constant_int = .{ - .value = array.count, + .slice => |slice| { + const slice_field: enum { + ptr, + len, + } = if (equal(u8, "ptr", identifier)) .ptr else if (equal(u8, "len", identifier)) .len else unreachable; + const field_type = switch (slice_field) { + .ptr => slice.child_pointer_type, + .len => Type.Index.usize, + }; + const field_index = @intFromEnum(slice_field); + + const gep = try unit.instructions.append(context.allocator, .{ + .get_element_pointer = .{ + .pointer = left.value.runtime, + .base_type = pointer.type, + .is_struct = true, + .index = .{ + .value = .{ + .@"comptime" = .{ + .constant_int = .{ + .value = field_index, + }, + }, }, + .type = .u32, }, }, - .type = type_index, + }); + try builder.appendInstruction(unit, context, gep); + + const gep_value = V{ + .value = .{ + .runtime = gep, + }, + .type = try unit.getPointerType(context, .{ + .type = .usize, + .many = false, + .nullable = false, + .termination = .none, + .mutability = .@"const", + }), + }; + + switch (side) { + .left => break :b gep_value, + .right => { + const load = try unit.instructions.append(context.allocator, .{ + .load = .{ + .value = gep_value, + .type = field_type, + }, + }); + try builder.appendInstruction(unit, context, load); + + break :b .{ + .value = .{ + .runtime = load, + }, + .type = field_type, + }; + }, + } + }, + .pointer => |child_pointer| switch (unit.types.get(child_pointer.type).*) { + .array => |array| { + assert(equal(u8, identifier, "len")); + + break :b switch (type_expect) { + .type => |type_index| V{ + .value = .{ + .@"comptime" = .{ + .constant_int = .{ + .value = array.count, + }, + }, + }, + .type = type_index, + }, + else => |t| @panic(@tagName(t)), + }; + }, + .@"struct" => |struct_index| { + const struct_type = unit.structs.get(struct_index); + const fields = struct_type.fields.items; + + for (fields, 0..) |field_index, i| { + const field = unit.struct_fields.get(field_index); + if (field.name == identifier_hash) { + assert(struct_type.backing_type == .null); + + const load = try unit.instructions.append(context.allocator, .{ + .load = .{ + .value = left, + .type = pointer.type, + }, + }); + try builder.appendInstruction(unit, context, load); + + // GEP because this is still a pointer + const gep = try unit.instructions.append(context.allocator, .{ + .get_element_pointer = .{ + .pointer = load, + .base_type = child_pointer.type, + .is_struct = true, + .index = .{ + .value = .{ + .@"comptime" = .{ + .constant_int = .{ + .value = i, + }, + }, + }, + .type = .u32, + }, + }, + }); + try builder.appendInstruction(unit, context, gep); + + const mutability = child_pointer.mutability; + const gep_pointer_type = try unit.getPointerType(context, .{ + .type = field.type, + .termination = .none, + .mutability = mutability, + .many = false, + .nullable = false, + }); + const gep_value = V{ + .value = .{ + .runtime = gep, + }, + .type = gep_pointer_type, + }; + + break :b switch (side) { + .left => gep_value, + .right => right: { + const field_load = try unit.instructions.append(context.allocator, .{ + .load = .{ + .value = gep_value, + .type = field.type, + }, + }); + try builder.appendInstruction(unit, context, field_load); + + break :right .{ + .value = .{ + .runtime = field_load, + }, + .type = field.type, + }; + }, + }; + } + } else { + const scope = left_type.getScope(unit); + _ = scope; // autofix + unreachable; + } }, else => |t| @panic(@tagName(t)), - }; + }, + .@"struct" => |struct_index| { + const struct_type = unit.structs.get(struct_index); + const fields = struct_type.fields.items; + + for (fields, 0..) |field_index, i| { + const field = unit.struct_fields.get(field_index); + if (field.name == identifier_hash) { + switch (struct_type.backing_type) { + .null => { + const gep = try unit.instructions.append(context.allocator, .{ + .get_element_pointer = .{ + .pointer = left.value.runtime, + .base_type = pointer.type, + .is_struct = true, + .index = .{ + .value = .{ + .@"comptime" = .{ + .constant_int = .{ + .value = i, + }, + }, + }, + .type = .u32, + }, + }, + }); + try builder.appendInstruction(unit, context, gep); + + const gep_value = V{ + .value = .{ + .runtime = gep, + }, + .type = try unit.getPointerType(context, .{ + .type = field.type, + .mutability = .@"const", + .nullable = false, + .many = false, + .termination = .none, + }), + }; + switch (side) { + .left => break :b gep_value, + .right => { + const load = try unit.instructions.append(context.allocator, .{ + .load = .{ + .value = gep_value, + .type = field.type, + }, + }); + + try builder.appendInstruction(unit, context, load); + + break :b V{ + .value = .{ + .runtime = load, + }, + .type = field.type, + }; + }, + } + }, + else => { + assert(side == .right); + + const load = try unit.instructions.append(context.allocator, .{ + .load = .{ + .value = left, + .type = struct_type.backing_type, + }, + }); + try builder.appendInstruction(unit, context, load); + + var bit_offset: u32 = 0; + for (fields[0..i]) |fi| { + const f = unit.struct_fields.get(fi); + const f_type = unit.types.get(f.type); + const bit_size = f_type.getBitSize(unit); + bit_offset += bit_size; + } + + const backing_type = unit.types.get(struct_type.backing_type); + const instruction_to_truncate = switch (bit_offset) { + 0 => load, + else => shl: { + const shl = try unit.instructions.append(context.allocator, .{ + .integer_binary_operation = .{ + .id = .shift_right, + .left = .{ + .value = .{ + .runtime = load, + }, + .type = struct_type.backing_type, + }, + .right = .{ + .value = .{ + .@"comptime" = .{ + .constant_int = .{ + .value = bit_offset, + }, + }, + }, + .type = struct_type.backing_type, + }, + .signedness = backing_type.integer.signedness, + }, + }); + try builder.appendInstruction(unit, context, shl); + + break :shl shl; + }, + }; + + const f_type = unit.types.get(field.type); + const f_bit_size = f_type.getBitSize(unit); + + const backing_type_size = backing_type.getBitSize(unit); + + switch (f_bit_size == backing_type_size) { + true => { + //instruction_to_truncate, + unreachable; + }, + false => { + const truncate = try unit.instructions.append(context.allocator, .{ + .cast = .{ + .id = .truncate, + .value = .{ + .value = .{ + .runtime = instruction_to_truncate, + }, + .type = left.type, + }, + .type = field.type, + }, + }); + try builder.appendInstruction(unit, context, truncate); + break :b V{ + .value = .{ + .runtime = truncate, + }, + .type = field.type, + }; + }, + } + }, + } + } + } else { + const scope = left_type.getScope(unit); + _ = scope; // autofix + unreachable; + } + }, + else => |t| @panic(@tagName(t)), }, else => |t| @panic(@tagName(t)), } @@ -5716,6 +7089,23 @@ pub const Builder = struct { const typecheck_result = try builder.typecheck(unit, context, ti, result.type); switch (typecheck_result) { .success => return result, + .pointer_var_to_const => { + const cast = try unit.instructions.append(context.allocator, .{ + .cast = .{ + .id = .pointer_var_to_const, + .value = result, + .type = ti, + }, + }); + try builder.appendInstruction(unit, context, cast); + + return .{ + .value = .{ + .runtime = cast, + }, + .type = ti, + }; + }, else => |t| @panic(@tagName(t)), } }, @@ -5723,7 +7113,7 @@ pub const Builder = struct { } } - fn resolveIfElse(builder: *Builder, unit: *Unit, context: *const Context, type_expect: Type.Expect, node_index: Node.Index) !V{ + fn resolveIfElse(builder: *Builder, unit: *Unit, context: *const Context, type_expect: Type.Expect, node_index: Node.Index) !V { const node = unit.getNode(node_index); assert(node.left != .null); assert(node.right != .null); @@ -5743,7 +7133,7 @@ pub const Builder = struct { false => try builder.resolveRuntimeValue(unit, context, type_expect, not_taken_expression_node_index, .right), }, .runtime => |condition_instruction| { - try builder.resolveBranch(unit, context, type_expect, condition_instruction, taken_expression_node_index, not_taken_expression_node_index, .null); + try builder.resolveBranch(unit, context, type_expect, condition_instruction, taken_expression_node_index, not_taken_expression_node_index, .null, null); // TODO WARN SAFETY: return undefined; }, @@ -5759,6 +7149,13 @@ pub const Builder = struct { unit.basic_blocks.get(builder.current_basic_block).terminated = true; } + fn buildTrap(builder: *Builder, unit: *Unit, context: *const Context) !void { + const instruction = try unit.instructions.append(context.allocator, .trap); + try builder.appendInstruction(unit, context, instruction); + + try builder.buildUnreachable(unit, context); + } + fn buildRet(builder: *Builder, unit: *Unit, context: *const Context, value: V) !void { const ret = try unit.instructions.append(context.allocator, .{ .ret = value, @@ -5778,18 +7175,17 @@ pub const Enum = struct { name: u32, parent: Type.Index, - pub const List = BlockList(@This(), enum{}); + pub const List = BlockList(@This(), enum {}); pub usingnamespace @This().List.Index; }; - pub const List = BlockList(@This(), enum{}); + pub const List = BlockList(@This(), enum {}); pub usingnamespace @This().List.Index; }; pub const Unit = struct { node_buffer: Node.List = .{}, files: Debug.File.List = .{}, - // values: Value.List = .{}, types: Type.List = .{}, structs: Struct.List = .{}, struct_fields: Struct.Field.List = .{}, @@ -5823,12 +7219,9 @@ pub const Unit = struct { integers: AutoHashMap(Type.Integer, Type.Index) = .{}, global_array_constants: AutoHashMap(V.Comptime.ConstantArray.Index, *Debug.Declaration.Global) = .{}, - code_to_emit: ArrayList(*Debug.Declaration.Global) = .{}, + code_to_emit: AutoArrayHashMap(Function.Definition.Index, *Debug.Declaration.Global) = .{}, data_to_emit: ArrayList(*Debug.Declaration.Global) = .{}, type_declarations: AutoHashMap(Type.Index, *Debug.Declaration.Global) = .{}, - // function_declaration_map: AutoHashMap(Function.Definition.Index, Declaration.Index) = .{}, - // type_declaration_map: AutoHashMap(Type.Index, Declaration.Index) = .{}, - // TODO struct_type_map: AutoHashMap(Struct.Index, Type.Index) = .{}, scope: Debug.Scope.Global = .{ .scope = .{ @@ -5855,9 +7248,29 @@ pub const Unit = struct { for (basic_block.instructions.items) |instruction_index| { const instruction = unit.instructions.get(instruction_index); - log(.compilation, .ir, " %{}: {s} ", .{Instruction.unwrap(instruction_index), @tagName(instruction.*)}); + log(.compilation, .ir, " %{}: {s} ", .{ Instruction.unwrap(instruction_index), @tagName(instruction.*) }); switch (instruction.*) { + .call => |call| { + switch (call.callable.value) { + .@"comptime" => |ct| switch (ct) { + .global => |global| log(.compilation, .ir, "{s}(", .{unit.getIdentifier(global.declaration.name)}), + else => unreachable, + }, + .runtime => |ii| log(.compilation, .ir, "%{}(", .{Instruction.unwrap(ii)}), + else => |t| @panic(@tagName(t)), + } + + for (call.arguments) |arg| { + switch (arg.value) { + .@"comptime" => log(.compilation, .ir, "comptime", .{}), + .runtime => |ii| log(.compilation, .ir, "%{}, ", .{Instruction.unwrap(ii)}), + else => |t| @panic(@tagName(t)), + } + } + + log(.compilation, .ir, ")", .{}); + }, .insert_value => |insert_value| { log(.compilation, .ir, "aggregate ", .{}); switch (insert_value.expression.value) { @@ -5891,7 +7304,10 @@ pub const Unit = struct { }, .load => |load| { switch (load.value.value) { - .@"comptime" => unreachable, + .@"comptime" => |ct| switch (ct) { + .global => |global| log(.compilation, .ir, "{s}", .{unit.getIdentifier(global.declaration.name)}), + else => |t| @panic(@tagName(t)), + }, .runtime => |ii| { log(.compilation, .ir, "%{}", .{@intFromEnum(ii)}); }, @@ -5899,15 +7315,15 @@ pub const Unit = struct { } }, .push_scope => |push_scope| { - log(.compilation, .ir, "0x{x} -> 0x{x}", .{@as(u24, @truncate(@intFromPtr(push_scope.old))), @as(u24, @truncate(@intFromPtr(push_scope.new)))}); + log(.compilation, .ir, "0x{x} -> 0x{x}", .{ @as(u24, @truncate(@intFromPtr(push_scope.old))), @as(u24, @truncate(@intFromPtr(push_scope.new))) }); }, .pop_scope => |pop_scope| { - log(.compilation, .ir, "0x{x} <- 0x{x}", .{@as(u24, @truncate(@intFromPtr(pop_scope.new))), @as(u24, @truncate(@intFromPtr(pop_scope.old)))}); + log(.compilation, .ir, "0x{x} <- 0x{x}", .{ @as(u24, @truncate(@intFromPtr(pop_scope.new))), @as(u24, @truncate(@intFromPtr(pop_scope.old))) }); }, .debug_checkpoint => |checkpoint| { - log(.compilation, .ir, "{}, {}", .{checkpoint.line, checkpoint.column}); + log(.compilation, .ir, "{}, {}", .{ checkpoint.line, checkpoint.column }); }, - .argument_declaration => |arg|{ + .argument_declaration => |arg| { log(.compilation, .ir, "\"{s}\"", .{unit.getIdentifier(arg.declaration.name)}); }, .cast => |cast| { @@ -5917,15 +7333,15 @@ pub const Unit = struct { log(.compilation, .ir, "[#{}]", .{BasicBlock.unwrap(jump.to)}); }, .branch => |branch| { - log(.compilation, .ir, "bool %{}, [#{}, #{}]", .{Instruction.unwrap(branch.condition), BasicBlock.unwrap(branch.taken), BasicBlock.unwrap(branch.not_taken)}); + log(.compilation, .ir, "bool %{}, [#{}, #{}]", .{ Instruction.unwrap(branch.condition), BasicBlock.unwrap(branch.taken), BasicBlock.unwrap(branch.not_taken) }); }, .phi => |phi| { for (phi.values.items, phi.basic_blocks.items) |value, bb| { - log(.compilation, .ir, "(%{}, #{}), ", .{switch (value.value) { + log(.compilation, .ir, "(%{}, #{}), ", .{ switch (value.value) { .@"comptime" => 0xffff_ffff, .runtime => |ii| @intFromEnum(ii), else => unreachable, - }, @intFromEnum(bb)}); + }, @intFromEnum(bb) }); } }, .integer_compare => |compare| { @@ -5950,14 +7366,14 @@ pub const Unit = struct { else => unreachable, } }, - else => {} + else => {}, } - logln(.compilation, .ir, "", .{}); + logln(.compilation, .ir, "", .{}); } } } - fn getReturnType(unit: *Unit, function_index: Function.Definition.Index) Type.Index{ + fn getReturnType(unit: *Unit, function_index: Function.Definition.Index) Type.Index { const function = unit.function_definitions.get(function_index); const function_type = unit.types.get(function.type); const function_prototype = unit.function_prototypes.get(function_type.function); @@ -6067,7 +7483,7 @@ pub const Unit = struct { } // TODO: make this fast - fn findTokenFile(unit: *Unit, token_index: Token.Index) Debug.File.Index{ + fn findTokenFile(unit: *Unit, token_index: Token.Index) Debug.File.Index { const ti = @intFromEnum(token_index); for (unit.file_token_offsets.keys(), unit.file_token_offsets.values()) |range, file_index| { const i = @intFromEnum(range.start); @@ -6091,8 +7507,8 @@ pub const Unit = struct { const bytes = file.source_code[offset..][0..len]; return bytes; } - - fn getOptionalType(unit: *Unit, context: *const Context, element_type: Type.Index) !Type.Index{ + + fn getOptionalType(unit: *Unit, context: *const Context, element_type: Type.Index) !Type.Index { if (unit.optionals.get(element_type)) |optional| { return optional; } else { @@ -6115,8 +7531,8 @@ pub const Unit = struct { }); const optional_struct = unit.structs.get(optional_struct_index); try optional_struct.fields.ensureTotalCapacity(context.allocator, 2); - const types = [_]Type.Index{element_type, .bool}; - const names = [_][]const u8{"payload", "is_valid"}; + const types = [_]Type.Index{ element_type, .bool }; + const names = [_][]const u8{ "payload", "is_valid" }; for (types, names) |t, name| { const field = try unit.struct_fields.append(context.allocator, .{ .name = try unit.processIdentifier(context, name), @@ -6132,7 +7548,7 @@ pub const Unit = struct { }); try unit.optionals.putNoClobber(context.allocator, element_type, optional_type_index); - + return optional_type_index; } } @@ -6150,7 +7566,7 @@ pub const Unit = struct { } } - fn getSliceType(unit: *Unit, context: *const Context, slice: Type.Slice) !Type.Index{ + fn getSliceType(unit: *Unit, context: *const Context, slice: Type.Slice) !Type.Index { if (unit.slices.get(slice)) |existing_type_index| { return existing_type_index; } else { @@ -6171,7 +7587,7 @@ pub const Unit = struct { .array = array, }); try unit.arrays.putNoClobber(context.allocator, array, array_type); - + return array_type; } } @@ -6264,9 +7680,9 @@ pub const Unit = struct { try builder.analyzePackage(unit, context, main_package); - for (unit.code_to_emit.items) |function_declaration| { + for (unit.code_to_emit.values()) |function_declaration| { const function_definition_index = function_declaration.initial_value.function_definition; - logln(.compilation, .ir, "Function #{} {s}", .{Function.Definition.unwrap(function_definition_index), unit.getIdentifier(function_declaration.declaration.name) }); + logln(.compilation, .ir, "Function #{} {s}", .{ Function.Definition.unwrap(function_definition_index), unit.getIdentifier(function_declaration.declaration.name) }); unit.dumpFunctionDefinition(function_definition_index); } @@ -6277,7 +7693,7 @@ pub const Unit = struct { const source_file = file.package.directory.handle.openFile(file.relative_path, .{}) catch |err| { std.debug.panic("Can't find file {s} in directory {s} for error {s}", .{ file.relative_path, file.package.directory.path, @errorName(err) }); }; - + const file_size = try source_file.getEndPos(); var file_buffer = try context.allocator.alloc(u8, file_size); @@ -6316,7 +7732,7 @@ pub const Unit = struct { }; } - pub fn importFile(unit: *Unit, context:*const Context, current_file_index: Debug.File.Index, import_name: []const u8) !ImportPackageResult { + pub fn importFile(unit: *Unit, context: *const Context, current_file_index: Debug.File.Index, import_name: []const u8) !ImportPackageResult { logln(.compilation, .import, "import: '{s}'\n", .{import_name}); if (equal(u8, import_name, "std")) { @@ -6348,8 +7764,6 @@ pub const Unit = struct { const import_file = try unit.getFile(context, full_path, file_relative_path, package); _ = @intFromPtr(unit.files.get(import_file.index).package); - // try unit.files.get(import_file.index).file_references.append(context.allocator, current_file); - const result = ImportPackageResult{ .file = import_file, .is_package = false, @@ -6366,16 +7780,14 @@ pub const Unit = struct { const file_index = try unit.files.append(context.allocator, Debug.File{ .relative_path = relative_path, .package = package, - .scope = .{ - .scope = .{ - .file = .null, - .kind = .file, - .line = 0, - .column = 0, - .local = false, - .level = 1, - } - }, + .scope = .{ .scope = .{ + .file = .null, + .kind = .file, + .line = 0, + .column = 0, + .local = false, + .level = 1, + } }, }); logln(.compilation, .new_file, "Adding file #{}: {s}\n", .{ file_index, full_path }); path_lookup.value_ptr.* = file_index; @@ -6549,7 +7961,7 @@ pub const Token = struct { length: u32, id: Token.Id, - pub const Buffer = struct{ + pub const Buffer = struct { tokens: std.MultiArrayList(Token) = .{}, line_offsets: ArrayList(u32) = .{}, @@ -6562,7 +7974,7 @@ pub const Token = struct { } }; - pub const Range = struct{ + pub const Range = struct { start: Token.Index, count: u32, }; @@ -6713,20 +8125,20 @@ pub const Token = struct { } }; - pub usingnamespace data_structures.getIndexForType(@This(), enum{}); + pub usingnamespace data_structures.getIndexForType(@This(), enum {}); }; pub const InlineAssembly = struct { instructions: []const InlineAssembly.Instruction.Index, - pub const List = BlockList(@This(), enum{}); + pub const List = BlockList(@This(), enum {}); pub usingnamespace List.Index; pub const Instruction = struct { id: u32, operands: []const Operand, - pub const List = BlockList(@This(), enum{}); + pub const List = BlockList(@This(), enum {}); pub usingnamespace @This().List.Index; }; diff --git a/bootstrap/backend/llvm.cpp b/bootstrap/backend/llvm.cpp index 90aa56f..c88cc99 100644 --- a/bootstrap/backend/llvm.cpp +++ b/bootstrap/backend/llvm.cpp @@ -157,9 +157,17 @@ extern "C" DICompositeType* NativityLLVMDebugInfoBuilderCreateStructType(DIBuild auto type_array = ArrayRef(reinterpret_cast(element_type_ptr), element_type_count); auto* struct_type = builder.createStructType(scope, name, file, line_number, bit_count, alignment, flags, derived_from, builder.getOrCreateArray(type_array)); + return struct_type; } +extern "C" void NativityLLVMDebugInfoBuilderCompositeTypeReplaceTypes(DIBuilder& builder, DICompositeType& type, DIType** element_type_ptr, size_t element_type_count) +{ + auto type_array = ArrayRef(reinterpret_cast(element_type_ptr), element_type_count); + auto node_array = builder.getOrCreateArray(type_array); + type.replaceElements(node_array); +} + extern "C" DICompositeType* NativityLLVMDebugInfoBuilderCreateArrayType(DIBuilder& builder, uint64_t bit_size, uint32_t alignment, DIType* type, size_t element_count) { Metadata* subranges[1] = { @@ -173,8 +181,6 @@ extern "C" DICompositeType* NativityLLVMDebugInfoBuilderCreateArrayType(DIBuilde extern "C" DIEnumerator* NativityLLVMDebugInfoBuilderCreateEnumerator(DIBuilder& builder, const char* name_ptr, size_t name_len, uint64_t value, bool is_unsigned) { - - // DIEnumerator *DIBuilder::createEnumerator(StringRef Name, uint64_t Val, auto name = StringRef(name_ptr, name_len); auto* enumerator = builder.createEnumerator(name, value, is_unsigned); return enumerator; @@ -194,6 +200,19 @@ extern "C" DICompositeType* NativityLLVMDebugInfoBuilderCreateReplaceableComposi auto* composite_type = builder.createReplaceableCompositeType(tag, name, scope, file, line); return composite_type; } + +extern "C" DIDerivedType* NativityLLVMDebugInfoBuilderCreateMemberType(DIBuilder& builder, DIScope *scope, const char* name_ptr, size_t name_len, DIFile* file, unsigned line_number, uint64_t bit_size, uint32_t alignment, uint64_t bit_offset, DINode::DIFlags flags, DIType* type) +{ + auto name = StringRef(name_ptr, name_len); + auto* member_type = builder.createMemberType(scope, name, file, line_number, bit_size, alignment, bit_offset, flags, type); + return member_type; +} + +extern "C" bool NativityLLLVMDITypeIsResolved(DIType* type) +{ + return type->isResolved(); +} + extern "C" DISubprogram* NativityLLVMDebugInfoScopeToSubprogram(DIScope* scope) { auto* subprogram = dyn_cast(scope); @@ -418,9 +437,6 @@ extern "C" Value* NativityLLVMBuilderCreateCast(IRBuilder<>& builder, Instructio extern "C" CallInst* NativityLLVMBuilderCreateCall(IRBuilder<>& builder, FunctionType* function_type, Value* callee, Value** argument_ptr, size_t argument_count, const char* name_ptr, size_t name_len, MDNode* fp_math_tag) { - if (auto* foo = static_cast(callee->getType())) { - int k = 0; - } auto arguments = ArrayRef(argument_ptr, argument_count); auto name = StringRef(name_ptr, name_len); auto* call = builder.CreateCall(function_type, callee, arguments, name, fp_math_tag); @@ -539,6 +555,13 @@ extern "C" Value* NativityLLVMBuilderCreateGEP(IRBuilder<>& builder, Type* type, return GEP; } +extern "C" Value* NativityLLVMBuilderCreateStructGEP(IRBuilder<>& builder, Type* type, Value* pointer, unsigned index, const char* name_ptr, size_t name_len) +{ + auto name = StringRef(name_ptr, name_len); + auto* gep = builder.CreateStructGEP(type, pointer, index, name); + return gep; +} + extern "C" BranchInst* NativityLLVMBuilderCreateBranch(IRBuilder<>& builder, BasicBlock* basic_block) { auto* conditional_branch = builder.CreateBr(basic_block); @@ -558,14 +581,6 @@ extern "C" Intrinsic::ID NativityLLVMLookupIntrinsic(const char* name_ptr, size_ return id; } -extern "C" FunctionType* NativityLLVMContextGetIntrinsicType(LLVMContext& context, Intrinsic::ID intrinsic_id, Type** parameter_type_ptr, size_t parameter_type_count) -{ - assert(intrinsic_id < Intrinsic::num_intrinsics); - auto parameter_types = ArrayRef(parameter_type_ptr, parameter_type_count); - auto* function_type = Intrinsic::getType(context, intrinsic_id, parameter_types); - return function_type; -} - extern "C" Function* NativityLLVMModuleGetIntrinsicDeclaration(Module* module, Intrinsic::ID intrinsic_id, Type** parameter_types_ptr, size_t parameter_type_count) { auto parameter_types = ArrayRef(parameter_types_ptr, parameter_type_count); @@ -574,6 +589,23 @@ extern "C" Function* NativityLLVMModuleGetIntrinsicDeclaration(Module* module, I return function; } +extern "C" FunctionType* NativityLLVMFunctionGetType(Function& function) +{ + auto* function_type = function.getFunctionType(); + return function_type; +} + +extern "C" Type* NativityLLVMFunctionTypeGetReturnType(FunctionType& function_type) +{ + auto* return_type = function_type.getReturnType(); + return return_type; +} + +extern "C" bool NativityLLVMTypeIsVoid(Type& type) +{ + bool is_void_type = type.isVoidTy(); + return is_void_type; +} extern "C" Value* NativityLLVMBuilderCreateExtractValue(IRBuilder<>& builder, Value* aggregate, unsigned* indices_ptr, size_t indices_len, const char* name_ptr, size_t name_len) { @@ -672,12 +704,6 @@ extern "C" bool NativityLLVMVerifyModule(const Module& module, const char** mess return !result; } -extern "C" Type* NativityLLVMFunctionGetReturnType(const Function& function) -{ - auto* return_type = function.getReturnType(); - return return_type; -} - extern "C" const char* NativityLLVMFunctionToString(const Function& function, size_t* len) { std::string buf; @@ -740,6 +766,12 @@ extern "C" FunctionType* NativityLLVMTypeToFunction(Type* type) return function_type; } +extern "C" Type* NativityLLVMFunctionTypeGetArgumentType(FunctionType& function_type, unsigned argument_index) +{ + auto* type = function_type.getParamType(argument_index); + return type; +} + extern "C" ArrayType* NativityLLVMTypeToArray(Type* type) { auto* array_type = dyn_cast(type); @@ -752,6 +784,12 @@ extern "C" PointerType* NativityLLVMTypeToPointer(Type* type) return pointer_type; } +extern "C" ConstantPointerNull* NativityLLVMPointerTypeGetNull(PointerType* pointer_type) +{ + auto* constant_pointer_null = ConstantPointerNull::get(pointer_type); + return constant_pointer_null; +} + extern "C" Type* NativityLLVMArrayTypeGetElementType(ArrayType* array_type) { auto* element_type = array_type->getElementType(); @@ -769,6 +807,17 @@ extern "C" const char* NativityLLVMModuleToString(const Module& module, size_t* return result; } +extern "C" const char* NativityLLVMValueToString(const Value& value, size_t* len) +{ + std::string buf; + raw_string_ostream os(buf); + value.print(os, true); + os.flush(); + *len = buf.size(); + auto* result = strdup(buf.c_str()); + return result; +} + extern "C" BasicBlock* NativityLLVMBuilderGetInsertBlock(IRBuilder<>& builder) { return builder.GetInsertBlock(); @@ -820,45 +869,76 @@ namespace lld { } } -extern "C" bool NativityLLVMGenerateMachineCode(Module& module, const char* object_file_path_ptr, size_t object_file_path_len, const char* file_path_ptr, size_t file_path_len) +extern "C" void NativityLLVMInitializeCodeGeneration() { InitializeAllTargetInfos(); InitializeAllTargets(); - InitializeAllTargetMCs(); InitializeAllAsmParsers(); + InitializeAllTargetMCs(); + InitializeAllAsmParsers(); InitializeAllAsmPrinters(); +} - auto target_triple = "x86_64-linux-none"; - auto cpu = "generic"; - auto features = ""; - TargetOptions target_options; +extern "C" const Target* NativityLLVMGetTarget(const char* target_triple_ptr, size_t target_triple_len, const char** message_ptr, size_t* message_len) +{ + auto target_triple = StringRef(target_triple_ptr, target_triple_len); + std::string error_message; + const Target* target = TargetRegistry::lookupTarget(target_triple, error_message); - std::string error; - auto* target = TargetRegistry::lookupTarget(target_triple, error); - assert(target); + if (!target) + { + char* result = new char[error_message.length()]; + memcpy(result, error_message.c_str(), error_message.length()); - auto target_machine = target->createTargetMachine(target_triple, cpu, features, target_options, Reloc::Static); - assert(target_machine); - - module.setDataLayout(target_machine->createDataLayout()); - module.setTargetTriple(target_triple); - - std::error_code EC; - auto object_file_path = StringRef(object_file_path_ptr, object_file_path_len); - raw_fd_ostream stream(object_file_path, EC, sys::fs::OF_None); - if (EC) { - return false; + *message_ptr = result; + *message_len = error_message.length(); } + return target; +} + +extern "C" TargetMachine* NativityLLVMTargetCreateTargetMachine(Target& target, const char* target_triple_ptr, size_t target_triple_len, const char* cpu_ptr, size_t cpu_len, const char* features_ptr, size_t features_len, Reloc::Model relocation_model, CodeModel::Model maybe_code_model, bool is_code_model_present, CodeGenOpt::Level optimization_level, bool jit) +{ + auto target_triple = StringRef(target_triple_ptr, target_triple_len); + auto cpu = StringRef(cpu_ptr, cpu_len); + auto features = StringRef(features_ptr, features_len); + TargetOptions target_options; + std::optional code_model = std::nullopt; + if (is_code_model_present) { + code_model = maybe_code_model; + } + TargetMachine* target_machine = target.createTargetMachine(target_triple, cpu, features, target_options, relocation_model, code_model, optimization_level, jit); + return target_machine; +} + +extern "C" void NativityLLVMModuleSetTargetMachineDataLayout(Module& module, TargetMachine& target_machine) +{ + module.setDataLayout(target_machine.createDataLayout()); +} + +extern "C" void NativityLLVMModuleSetTargetTriple(Module& module, const char* target_triple_ptr, size_t target_triple_len) +{ + auto target_triple = StringRef(target_triple_ptr, target_triple_len); + module.setTargetTriple(target_triple); +} + +extern "C" bool NativityLLVMModuleAddPassesToEmitFile(Module& module, TargetMachine& target_machine, const char* object_file_path_ptr, size_t object_file_path_len, CodeGenFileType codegen_file_type, bool disable_verify) +{ + std::error_code error_code; + auto object_file_path = StringRef(object_file_path_ptr, object_file_path_len); + raw_fd_ostream stream(object_file_path, error_code, sys::fs::OF_None); + if (error_code) { + return false; + } + legacy::PassManager pass; - bool result = target_machine->addPassesToEmitFile(pass, stream, nullptr, llvm::CGFT_ObjectFile, false); - if (result) { - // We invert the condition because LLVM conventions are just stupid + // We invert the condition because LLVM conventions are just stupid + if (target_machine.addPassesToEmitFile(pass, stream, nullptr, codegen_file_type, disable_verify)) { return false; } pass.run(module); stream.flush(); - + return true; } diff --git a/bootstrap/backend/llvm.zig b/bootstrap/backend/llvm.zig index 8b0a709..c9d2758 100644 --- a/bootstrap/backend/llvm.zig +++ b/bootstrap/backend/llvm.zig @@ -32,14 +32,14 @@ pub const LLVM = struct { debug_type_map: AutoHashMap(Compilation.Type.Index, *LLVM.DebugInfo.Type) = .{}, type_name_map: AutoHashMap(Compilation.Type.Index, []const u8) = .{}, type_map: AutoHashMap(Compilation.Type.Index, *LLVM.Type) = .{}, - function_definition_map: AutoArrayHashMap(*Compilation.Debug.Declaration.Global, *LLVM.Value.Function) = .{}, + function_definition_map: AutoArrayHashMap(*Compilation.Debug.Declaration.Global, *LLVM.Value.Constant.Function) = .{}, llvm_instruction_map: AutoHashMap(Compilation.Instruction.Index, *LLVM.Value) = .{}, llvm_value_map: AutoArrayHashMap(Compilation.V, *LLVM.Value) = .{}, llvm_block_map: AutoHashMap(Compilation.BasicBlock.Index, *LLVM.Value.BasicBlock) = .{}, global_variable_map: AutoArrayHashMap(*Compilation.Debug.Declaration.Global, *LLVM.Value.Constant.GlobalVariable) = .{}, scope_map: AutoHashMap(*Compilation.Debug.Scope, *LLVM.DebugInfo.Scope) = .{}, pointer_type: ?*LLVM.Type.Pointer = null, - function: *LLVM.Value.Function = undefined, + function: *LLVM.Value.Constant.Function = undefined, exit_block: *LLVM.Value.BasicBlock = undefined, sema_function: *Compilation.Debug.Declaration.Global = undefined, alloca_map: AutoHashMap(Compilation.Instruction.Index, *LLVM.Value) = .{}, @@ -49,6 +49,7 @@ pub const LLVM = struct { file: *LLVM.DebugInfo.File = undefined, subprogram: *LLVM.DebugInfo.Subprogram = undefined, arg_index: u32 = 0, + tag_count: c_uint = 0, inside_branch: bool = false, pub const Linkage = enum(c_uint) { @@ -91,7 +92,9 @@ pub const LLVM = struct { const toString = bindings.NativityLLVMModuleToString; const getIntrinsicDeclaration = bindings.NativityLLVMModuleGetIntrinsicDeclaration; const createDebugInfoBuilder = bindings.NativityLLVMModuleCreateDebugInfoBuilder; - const generateMachineCode = bindings.NativityLLVMGenerateMachineCode; + const setTargetMachineDataLayout = bindings.NativityLLVMModuleSetTargetMachineDataLayout; + const setTargetTriple = bindings.NativityLLVMModuleSetTargetTriple; + const addPassesToEmitFile = bindings.NativityLLVMModuleAddPassesToEmitFile; }; pub const Builder = opaque { @@ -106,6 +109,7 @@ pub const LLVM = struct { const createBranch = bindings.NativityLLVMBuilderCreateBranch; const createConditionalBranch = bindings.NativityLLVMBuilderCreateConditionalBranch; const createGEP = bindings.NativityLLVMBuilderCreateGEP; + const createStructGEP = bindings.NativityLLVMBuilderCreateStructGEP; const createICmp = bindings.NativityLLVMBuilderCreateICmp; const createLoad = bindings.NativityLLVMBuilderCreateLoad; const createMultiply = bindings.NativityLLVMBuilderCreateMultiply; @@ -203,9 +207,11 @@ pub const LLVM = struct { const createEnumerationType = bindings.NativityLLVMDebugInfoBuilderCreateEnumerationType; const createEnumerator = bindings.NativityLLVMDebugInfoBuilderCreateEnumerator; const createReplaceableCompositeType = bindings.NativityLLVMDebugInfoBuilderCreateReplaceableCompositeType; + const createMemberType = bindings.NativityLLVMDebugInfoBuilderCreateMemberType; const insertDeclare = bindings.NativityLLVMDebugInfoBuilderInsertDeclare; const finalizeSubprogram = bindings.NativityLLVMDebugInfoBuilderFinalizeSubprogram; const finalize = bindings.NativityLLVMDebugInfoBuilderFinalize; + const replaceCompositeTypes = bindings.NativityLLVMDebugInfoBuilderCompositeTypeReplaceTypes; }; pub const CompileUnit = opaque { @@ -227,9 +233,9 @@ pub const LLVM = struct { }; }; - pub const Expression = opaque{}; + pub const Expression = opaque {}; - pub const GlobalVariableExpression = opaque{}; + pub const GlobalVariableExpression = opaque {}; pub const LocalVariable = opaque {}; pub const LexicalBlock = opaque { @@ -331,8 +337,8 @@ pub const LLVM = struct { }; }; - pub const SubroutineType = opaque {}; pub const Type = opaque { + const isResolved = bindings.NativityLLLVMDITypeIsResolved; fn toScope(this: *@This()) *Scope { return @ptrCast(this); } @@ -350,6 +356,191 @@ pub const LLVM = struct { }; pub const Enumerator = opaque {}; + pub const Subroutine = opaque { + fn toType(this: *@This()) *LLVM.DebugInfo.Type { + return @ptrCast(this); + } + }; + }; + }; + + pub const FloatAbi = enum(c_uint) { + default = 0, + soft = 1, + hard = 2, + }; + + pub const FloatOperationFusionMode = enum(c_uint) { + fast = 0, + standard = 1, + strict = 2, + }; + + pub const JumpTableType = enum(c_uint) { + single = 0, + arity = 1, + simplified = 2, + full = 3, + }; + + pub const ThreadModel = enum(c_uint) { + posix = 0, + single = 1, + }; + + pub const BasicBlockSection = enum(c_uint) { + all = 0, + list = 1, + labels = 2, + preset = 3, + none = 4, + }; + + pub const EAbi = enum(c_uint) { + unknown = 0, + default = 1, + eabi4 = 2, + eabi5 = 3, + gnu = 4, + }; + + pub const DebuggerKind = enum(c_uint) { + default = 0, + gdb = 1, + lldb = 2, + sce = 3, + dbx = 4, + }; + + pub const GlobalISelAbortMode = enum(c_uint) { + disable = 0, + enable = 1, + disable_with_diagnostic = 2, + }; + + pub const DebugCompressionType = enum(c_uint) { + none = 0, + zlib = 1, + zstd = 2, + }; + + pub const RelocationModel = enum(c_uint) { + static = 0, + pic = 1, + dynamic_no_pic = 2, + ropi = 3, + rwpi = 4, + ropi_rwpi = 5, + }; + + pub const CodeModel = enum(c_uint) { + tiny = 0, + small = 1, + kernel = 2, + medium = 3, + large = 4, + }; + + pub const PicLevel = enum(c_uint) { + not_pic = 0, + small_pic = 1, + big_pic = 2, + }; + + pub const PieLevel = enum(c_uint) { + default = 0, + small = 1, + large = 2, + }; + + pub const TlsModel = enum(c_uint) { + general_dynamic = 0, + local_dynamic = 1, + initial_exec = 2, + local_exec = 3, + }; + + pub const OptimizationLevel = enum(c_int) { + none = 0, + less = 1, + default = 2, + aggressive = 3, + }; + + pub const FramePointerKind = enum(c_uint) { + none = 0, + non_leaf = 1, + all = 2, + }; + + pub const CodeGenFileType = enum(c_uint) { + assembly = 0, + object = 1, + null = 2, + }; + + pub const Target = opaque { + const createTargetMachine = bindings.NativityLLVMTargetCreateTargetMachine; + + pub const Machine = opaque {}; + + // This is a non-LLVM struct + const Options = extern struct { + bin_utils_version: struct { i32, i32 }, + fp_math: extern struct { + unsafe: bool, + no_infs: bool, + no_nans: bool, + no_traping: bool, + no_signed_zeroes: bool, + approx_func: bool, + enable_aix_extended_altivec_abi: bool, + honor_sign_dependent_rounding: bool, + }, + no_zeroes_in_bss: bool, + guaranteed_tail_call_optimization: bool, + stack_symbol_ordering: bool, + enable_fast_isel: bool, + enable_global_isel: bool, + global_isel_abort_mode: GlobalISelAbortMode, + use_init_array: bool, + disable_integrated_assembler: bool, + debug_compression_type: DebugCompressionType, + relax_elf_relocations: bool, + function_sections: bool, + data_sections: bool, + ignore_xcoff_visibility: bool, + xcoff_traceback_table: bool, + unique_section_names: bool, + unique_basic_block_section_names: bool, + trap_unreachable: bool, + no_trap_after_noreturn: bool, + tls_size: u8, + emulated_tls: bool, + enable_ipra: bool, + emit_stack_size_section: bool, + enable_machine_outliner: bool, + enable_machine_function_splitter: bool, + support_default_outlining: bool, + emit_address_significance_table: bool, + bb_sections: BasicBlockSection, + emit_call_site_info: bool, + support_debug_entry_values: bool, + enable_debug_entry_values: bool, + value_tracking_variable_locations: bool, + force_dwarf_frame_section: bool, + xray_function_index: bool, + debug_strict_dwarf: bool, + hotpatch: bool, + ppc_gen_scalar_mass_entries: bool, + jmc_instrument: bool, + cfi_fixup: bool, + loop_alignment: u32 = 0, + float_abi_type: FloatAbi, + fp_operation_fusion: FloatOperationFusionMode, + thread_model: ThreadModel, + eabi_version: EAbi, + debugger_tuning: DebuggerKind, }; }; @@ -358,7 +549,7 @@ pub const LLVM = struct { pub const Metadata = opaque { pub const Node = opaque {}; - pub const Tuple = opaque{}; + pub const Tuple = opaque {}; }; pub const Attribute = enum(u32) { @@ -455,6 +646,7 @@ pub const LLVM = struct { const toPointer = bindings.NativityLLVMTypeToPointer; const isPointer = bindings.NativityLLVMTypeIsPointer; const isInteger = bindings.NativityLLVMTypeIsInteger; + const isVoid = bindings.NativityLLVMTypeIsVoid; pub const Array = opaque { fn toType(integer: *@This()) *Type { @@ -475,12 +667,17 @@ pub const LLVM = struct { fn toType(integer: *@This()) *Type { return @ptrCast(integer); } + + const getArgumentType = bindings.NativityLLVMFunctionTypeGetArgumentType; + const getReturnType = bindings.NativityLLVMFunctionTypeGetReturnType; }; pub const Pointer = opaque { fn toType(integer: *@This()) *Type { return @ptrCast(integer); } + + const getNull = bindings.NativityLLVMPointerTypeGetNull; }; pub const Struct = opaque { @@ -500,7 +697,6 @@ pub const LLVM = struct { array, }; - // const getUndefined = bindings.NativityLLVMGetUndefined; const getPoison = bindings.NativityLLVMGetPoisonValue; }; @@ -510,247 +706,13 @@ pub const LLVM = struct { const toConstant = bindings.NativityLLVMValueToConstant; const toFunction = bindings.NativityLLVMValueToFunction; const toAlloca = bindings.NativityLLVMValueToAlloca; + const toString = bindings.NativityLLVMValueToString; pub const IntrinsicID = enum(u32) { none = 0, _, }; - pub const Function = opaque { - const getArguments = bindings.NativityLLVMFunctionGetArguments; - const getReturnType = bindings.NativityLLVMFunctionGetReturnType; - const addAttributeKey = bindings.NativityLLVMFunctionAddAttributeKey; - const verify = bindings.NativityLLVMVerifyFunction; - const toString = bindings.NativityLLVMFunctionToString; - const setCallingConvention = bindings.NativityLLVMFunctionSetCallingConvention; - const getCallingConvention = bindings.NativityLLVMFunctionGetCallingConvention; - const setSubprogram = bindings.NativityLLVMFunctionSetSubprogram; - const getSubprogram = bindings.NativityLLVMFunctionGetSubprogram; - - fn toValue(this: *@This()) *Value { - return @ptrCast(this); - } - - pub const CallingConvention = enum(c_uint) { - /// The default llvm calling convention, compatible with C. This convention - /// is the only one that supports varargs calls. As with typical C calling - /// conventions, the callee/caller have to tolerate certain amounts of - /// prototype mismatch. - C = 0, - - // Generic LLVM calling conventions. None of these support varargs calls, - // and all assume that the caller and callee prototype exactly match. - - /// Attempts to make calls as fast as possible (e.g. by passing things in - /// registers). - Fast = 8, - - /// Attempts to make code in the caller as efficient as possible under the - /// assumption that the call is not commonly executed. As such, these calls - /// often preserve all registers so that the call does not break any live - /// ranges in the caller side. - Cold = 9, - - /// Used by the Glasgow Haskell Compiler (GHC). - GHC = 10, - - /// Used by the High-Performance Erlang Compiler (HiPE). - HiPE = 11, - - /// Used for stack based JavaScript calls - WebKit_JS = 12, - - /// Used for dynamic register based calls (e.g. stackmap and patchpoint - /// intrinsics). - AnyReg = 13, - - /// Used for runtime calls that preserves most registers. - PreserveMost = 14, - - /// Used for runtime calls that preserves (almost) all registers. - PreserveAll = 15, - - /// Calling convention for Swift. - Swift = 16, - - /// Used for access functions. - CXX_FAST_TLS = 17, - - /// Attemps to make calls as fast as possible while guaranteeing that tail - /// call optimization can always be performed. - Tail = 18, - - /// Special calling convention on Windows for calling the Control Guard - /// Check ICall funtion. The function takes exactly one argument (address of - /// the target function) passed in the first argument register, and has no - /// return value. All register values are preserved. - CFGuard_Check = 19, - - /// This follows the Swift calling convention in how arguments are passed - /// but guarantees tail calls will be made by making the callee clean up - /// their stack. - SwiftTail = 20, - - /// This is the start of the target-specific calling conventions, e.g. - /// fastcall and thiscall on X86. - // FirstTargetCC = 64, - - /// stdcall is mostly used by the Win32 API. It is basically the same as the - /// C convention with the difference in that the callee is responsible for - /// popping the arguments from the stack. - X86_StdCall = 64, - - /// 'fast' analog of X86_StdCall. Passes first two arguments in ECX:EDX - /// registers, others - via stack. Callee is responsible for stack cleaning. - X86_FastCall = 65, - - /// ARM Procedure Calling Standard (obsolete, but still used on some - /// targets). - ARM_APCS = 66, - - /// ARM Architecture Procedure Calling Standard calling convention (aka - /// EABI). Soft float variant. - ARM_AAPCS = 67, - - /// Same as ARM_AAPCS, but uses hard floating point ABI. - ARM_AAPCS_VFP = 68, - - /// Used for MSP430 interrupt routines. - MSP430_INTR = 69, - - /// Similar to X86_StdCall. Passes first argument in ECX, others via stack. - /// Callee is responsible for stack cleaning. MSVC uses this by default for - /// methods in its ABI. - X86_ThisCall = 70, - - /// Call to a PTX kernel. Passes all arguments in parameter space. - PTX_Kernel = 71, - - /// Call to a PTX device function. Passes all arguments in register or - /// parameter space. - PTX_Device = 72, - - /// Used for SPIR non-kernel device functions. No lowering or expansion of - /// arguments. Structures are passed as a pointer to a struct with the - /// byval attribute. Functions can only call SPIR_FUNC and SPIR_KERNEL - /// functions. Functions can only have zero or one return values. Variable - /// arguments are not allowed, except for printf. How arguments/return - /// values are lowered are not specified. Functions are only visible to the - /// devices. - SPIR_FUNC = 75, - - /// Used for SPIR kernel functions. Inherits the restrictions of SPIR_FUNC, - /// except it cannot have non-void return values, it cannot have variable - /// arguments, it can also be called by the host or it is externally - /// visible. - SPIR_KERNEL = 76, - - /// Used for Intel OpenCL built-ins. - Intel_OCL_BI = 77, - - /// The C convention as specified in the x86-64 supplement to the System V - /// ABI, used on most non-Windows systems. - X86_64_SysV = 78, - - /// The C convention as implemented on Windows/x86-64 and AArch64. It - /// differs from the more common \c X86_64_SysV convention in a number of - /// ways, most notably in that XMM registers used to pass arguments are - /// shadowed by GPRs, and vice versa. On AArch64, this is identical to the - /// normal C (AAPCS) calling convention for normal functions, but floats are - /// passed in integer registers to variadic functions. - Win64 = 79, - - /// MSVC calling convention that passes vectors and vector aggregates in SSE - /// registers. - X86_VectorCall = 80, - - /// Used by HipHop Virtual Machine (HHVM) to perform calls to and from - /// translation cache, and for calling PHP functions. HHVM calling - /// convention supports tail/sibling call elimination. - HHVM = 81, - - /// HHVM calling convention for invoking C/C++ helpers. - HHVM_C = 82, - - /// x86 hardware interrupt context. Callee may take one or two parameters, - /// where the 1st represents a pointer to hardware context frame and the 2nd - /// represents hardware error code, the presence of the later depends on the - /// interrupt vector taken. Valid for both 32- and 64-bit subtargets. - X86_INTR = 83, - - /// Used for AVR interrupt routines. - AVR_INTR = 84, - - /// Used for AVR signal routines. - AVR_SIGNAL = 85, - - /// Used for special AVR rtlib functions which have an "optimized" - /// convention to preserve registers. - AVR_BUILTIN = 86, - - /// Used for Mesa vertex shaders, or AMDPAL last shader stage before - /// rasterization (vertex shader if tessellation and geometry are not in - /// use, or otherwise copy shader if one is needed). - AMDGPU_VS = 87, - - /// Used for Mesa/AMDPAL geometry shaders. - AMDGPU_GS = 88, - - /// Used for Mesa/AMDPAL pixel shaders. - AMDGPU_PS = 89, - - /// Used for Mesa/AMDPAL compute shaders. - AMDGPU_CS = 90, - - /// Used for AMDGPU code object kernels. - AMDGPU_KERNEL = 91, - - /// Register calling convention used for parameters transfer optimization - X86_RegCall = 92, - - /// Used for Mesa/AMDPAL hull shaders (= tessellation control shaders). - AMDGPU_HS = 93, - - /// Used for special MSP430 rtlib functions which have an "optimized" - /// convention using additional registers. - MSP430_BUILTIN = 94, - - /// Used for AMDPAL vertex shader if tessellation is in use. - AMDGPU_LS = 95, - - /// Used for AMDPAL shader stage before geometry shader if geometry is in - /// use. So either the domain (= tessellation evaluation) shader if - /// tessellation is in use, or otherwise the vertex shader. - AMDGPU_ES = 96, - - /// Used between AArch64 Advanced SIMD functions - AArch64_VectorCall = 97, - - /// Used between AArch64 SVE functions - AArch64_SVE_VectorCall = 98, - - /// For emscripten __invoke_* functions. The first argument is required to - /// be the function ptr being indirectly called. The remainder matches the - /// regular calling convention. - WASM_EmscriptenInvoke = 99, - - /// Used for AMD graphics targets. - AMDGPU_Gfx = 100, - - /// Used for M68k interrupt routines. - M68k_INTR = 101, - - /// Preserve X0-X13, X19-X29, SP, Z0-Z31, P0-P15. - AArch64_SME_ABI_Support_Routines_PreserveMost_From_X0 = 102, - - /// Preserve X2-X15, X19-X29, SP, Z0-Z31, P0-P15. - AArch64_SME_ABI_Support_Routines_PreserveMost_From_X2 = 103, - - /// The highest possible ID. Must be some 2^k - 1. - MaxID = 1023, - }; - }; - pub const BasicBlock = opaque { const remove = bindings.NativityLLVMBasicBlockRemoveFromParent; fn toValue(this: *@This()) *Value { @@ -891,6 +853,245 @@ pub const LLVM = struct { }; pub const Constant = opaque { + pub const Function = opaque { + const getArguments = bindings.NativityLLVMFunctionGetArguments; + const getType = bindings.NativityLLVMFunctionGetType; + const addAttributeKey = bindings.NativityLLVMFunctionAddAttributeKey; + const verify = bindings.NativityLLVMVerifyFunction; + const toString = bindings.NativityLLVMFunctionToString; + const setCallingConvention = bindings.NativityLLVMFunctionSetCallingConvention; + const getCallingConvention = bindings.NativityLLVMFunctionGetCallingConvention; + const setSubprogram = bindings.NativityLLVMFunctionSetSubprogram; + const getSubprogram = bindings.NativityLLVMFunctionGetSubprogram; + + fn toValue(this: *@This()) *Value { + return @ptrCast(this); + } + + fn toConstant(this: *@This()) *Constant { + return @ptrCast(this); + } + + pub const CallingConvention = enum(c_uint) { + /// The default llvm calling convention, compatible with C. This convention + /// is the only one that supports varargs calls. As with typical C calling + /// conventions, the callee/caller have to tolerate certain amounts of + /// prototype mismatch. + C = 0, + + // Generic LLVM calling conventions. None of these support varargs calls, + // and all assume that the caller and callee prototype exactly match. + + /// Attempts to make calls as fast as possible (e.g. by passing things in + /// registers). + Fast = 8, + + /// Attempts to make code in the caller as efficient as possible under the + /// assumption that the call is not commonly executed. As such, these calls + /// often preserve all registers so that the call does not break any live + /// ranges in the caller side. + Cold = 9, + + /// Used by the Glasgow Haskell Compiler (GHC). + GHC = 10, + + /// Used by the High-Performance Erlang Compiler (HiPE). + HiPE = 11, + + /// Used for stack based JavaScript calls + WebKit_JS = 12, + + /// Used for dynamic register based calls (e.g. stackmap and patchpoint + /// intrinsics). + AnyReg = 13, + + /// Used for runtime calls that preserves most registers. + PreserveMost = 14, + + /// Used for runtime calls that preserves (almost) all registers. + PreserveAll = 15, + + /// Calling convention for Swift. + Swift = 16, + + /// Used for access functions. + CXX_FAST_TLS = 17, + + /// Attemps to make calls as fast as possible while guaranteeing that tail + /// call optimization can always be performed. + Tail = 18, + + /// Special calling convention on Windows for calling the Control Guard + /// Check ICall funtion. The function takes exactly one argument (address of + /// the target function) passed in the first argument register, and has no + /// return value. All register values are preserved. + CFGuard_Check = 19, + + /// This follows the Swift calling convention in how arguments are passed + /// but guarantees tail calls will be made by making the callee clean up + /// their stack. + SwiftTail = 20, + + /// This is the start of the target-specific calling conventions, e.g. + /// fastcall and thiscall on X86. + // FirstTargetCC = 64, + + /// stdcall is mostly used by the Win32 API. It is basically the same as the + /// C convention with the difference in that the callee is responsible for + /// popping the arguments from the stack. + X86_StdCall = 64, + + /// 'fast' analog of X86_StdCall. Passes first two arguments in ECX:EDX + /// registers, others - via stack. Callee is responsible for stack cleaning. + X86_FastCall = 65, + + /// ARM Procedure Calling Standard (obsolete, but still used on some + /// targets). + ARM_APCS = 66, + + /// ARM Architecture Procedure Calling Standard calling convention (aka + /// EABI). Soft float variant. + ARM_AAPCS = 67, + + /// Same as ARM_AAPCS, but uses hard floating point ABI. + ARM_AAPCS_VFP = 68, + + /// Used for MSP430 interrupt routines. + MSP430_INTR = 69, + + /// Similar to X86_StdCall. Passes first argument in ECX, others via stack. + /// Callee is responsible for stack cleaning. MSVC uses this by default for + /// methods in its ABI. + X86_ThisCall = 70, + + /// Call to a PTX kernel. Passes all arguments in parameter space. + PTX_Kernel = 71, + + /// Call to a PTX device function. Passes all arguments in register or + /// parameter space. + PTX_Device = 72, + + /// Used for SPIR non-kernel device functions. No lowering or expansion of + /// arguments. Structures are passed as a pointer to a struct with the + /// byval attribute. Functions can only call SPIR_FUNC and SPIR_KERNEL + /// functions. Functions can only have zero or one return values. Variable + /// arguments are not allowed, except for printf. How arguments/return + /// values are lowered are not specified. Functions are only visible to the + /// devices. + SPIR_FUNC = 75, + + /// Used for SPIR kernel functions. Inherits the restrictions of SPIR_FUNC, + /// except it cannot have non-void return values, it cannot have variable + /// arguments, it can also be called by the host or it is externally + /// visible. + SPIR_KERNEL = 76, + + /// Used for Intel OpenCL built-ins. + Intel_OCL_BI = 77, + + /// The C convention as specified in the x86-64 supplement to the System V + /// ABI, used on most non-Windows systems. + X86_64_SysV = 78, + + /// The C convention as implemented on Windows/x86-64 and AArch64. It + /// differs from the more common \c X86_64_SysV convention in a number of + /// ways, most notably in that XMM registers used to pass arguments are + /// shadowed by GPRs, and vice versa. On AArch64, this is identical to the + /// normal C (AAPCS) calling convention for normal functions, but floats are + /// passed in integer registers to variadic functions. + Win64 = 79, + + /// MSVC calling convention that passes vectors and vector aggregates in SSE + /// registers. + X86_VectorCall = 80, + + /// Used by HipHop Virtual Machine (HHVM) to perform calls to and from + /// translation cache, and for calling PHP functions. HHVM calling + /// convention supports tail/sibling call elimination. + HHVM = 81, + + /// HHVM calling convention for invoking C/C++ helpers. + HHVM_C = 82, + + /// x86 hardware interrupt context. Callee may take one or two parameters, + /// where the 1st represents a pointer to hardware context frame and the 2nd + /// represents hardware error code, the presence of the later depends on the + /// interrupt vector taken. Valid for both 32- and 64-bit subtargets. + X86_INTR = 83, + + /// Used for AVR interrupt routines. + AVR_INTR = 84, + + /// Used for AVR signal routines. + AVR_SIGNAL = 85, + + /// Used for special AVR rtlib functions which have an "optimized" + /// convention to preserve registers. + AVR_BUILTIN = 86, + + /// Used for Mesa vertex shaders, or AMDPAL last shader stage before + /// rasterization (vertex shader if tessellation and geometry are not in + /// use, or otherwise copy shader if one is needed). + AMDGPU_VS = 87, + + /// Used for Mesa/AMDPAL geometry shaders. + AMDGPU_GS = 88, + + /// Used for Mesa/AMDPAL pixel shaders. + AMDGPU_PS = 89, + + /// Used for Mesa/AMDPAL compute shaders. + AMDGPU_CS = 90, + + /// Used for AMDGPU code object kernels. + AMDGPU_KERNEL = 91, + + /// Register calling convention used for parameters transfer optimization + X86_RegCall = 92, + + /// Used for Mesa/AMDPAL hull shaders (= tessellation control shaders). + AMDGPU_HS = 93, + + /// Used for special MSP430 rtlib functions which have an "optimized" + /// convention using additional registers. + MSP430_BUILTIN = 94, + + /// Used for AMDPAL vertex shader if tessellation is in use. + AMDGPU_LS = 95, + + /// Used for AMDPAL shader stage before geometry shader if geometry is in + /// use. So either the domain (= tessellation evaluation) shader if + /// tessellation is in use, or otherwise the vertex shader. + AMDGPU_ES = 96, + + /// Used between AArch64 Advanced SIMD functions + AArch64_VectorCall = 97, + + /// Used between AArch64 SVE functions + AArch64_SVE_VectorCall = 98, + + /// For emscripten __invoke_* functions. The first argument is required to + /// be the function ptr being indirectly called. The remainder matches the + /// regular calling convention. + WASM_EmscriptenInvoke = 99, + + /// Used for AMD graphics targets. + AMDGPU_Gfx = 100, + + /// Used for M68k interrupt routines. + M68k_INTR = 101, + + /// Preserve X0-X13, X19-X29, SP, Z0-Z31, P0-P15. + AArch64_SME_ABI_Support_Routines_PreserveMost_From_X0 = 102, + + /// Preserve X2-X15, X19-X29, SP, Z0-Z31, P0-P15. + AArch64_SME_ABI_Support_Routines_PreserveMost_From_X2 = 103, + + /// The highest possible ID. Must be some 2^k - 1. + MaxID = 1023, + }; + }; + pub const Int = opaque { fn toValue(this: *@This()) *Value { return @ptrCast(this); @@ -910,6 +1111,15 @@ pub const LLVM = struct { } }; + pub const PointerNull = opaque { + fn toValue(this: *@This()) *Value { + return @ptrCast(this); + } + fn toConstant(this: *@This()) *Constant { + return @ptrCast(this); + } + }; + pub const Undefined = opaque { fn toConstant(this: *@This()) *Constant { return @ptrCast(this); @@ -919,7 +1129,7 @@ pub const LLVM = struct { } }; - pub const Poison = opaque{ + pub const Poison = opaque { fn toConstant(this: *@This()) *Constant { return @ptrCast(this); } @@ -970,29 +1180,6 @@ pub const LLVM = struct { switch (unit.types.get(argument_type_index).*) { // TODO: ABI .integer, .pointer, .@"enum", .@"struct", .slice => try parameter_types.append(context.allocator, try llvm.getType(unit, context, argument_type_index)), - // .slice => |slice| { - // const pointer_type = try llvm.getType(llvm.sema.map.pointers.get(.{ - // .many = true, - // .@"const" = slice.@"const", - // .termination = slice.termination, - // .element_type = slice.element_type, - // }).?); - // const usize_type = try llvm.getType(Compilation.Type.usize); - // try parameter_types.append(context.allocator, pointer_type); - // try parameter_types.append(context.allocator, usize_type); - // }, - // .@"struct" => |struct_index| { - // const struct_type = llvm.sema.types.structs.get(struct_index); - // if (!struct_type.backing_type.invalid) { - // unreachable; - // } else { - // for (struct_type.fields.items) |field_index| { - // const field = llvm.sema.types.container_fields.get(field_index); - // const debug_type = try llvm.getType(field.type); - // try parameter_types.append(context.allocator, debug_type); - // } - // } - // }, else => |t| @panic(@tagName(t)), } // arg_types.appendAssumeCapacity(llvm_argument_type); @@ -1045,7 +1232,10 @@ pub const LLVM = struct { .@"struct" => |struct_type_index| blk: { const sema_struct_type = unit.structs.get(struct_type_index); switch (sema_struct_type.backing_type) { - else => @panic(@tagName(.null)), + else => { + const backing_integer_type = try llvm.getType(unit, context, sema_struct_type.backing_type); + break :blk backing_integer_type; + }, .null => { var field_type_list = try ArrayList(*LLVM.Type).initCapacity(context.allocator, sema_struct_type.fields.items.len); for (sema_struct_type.fields.items) |sema_field_index| { @@ -1062,23 +1252,6 @@ pub const LLVM = struct { }, } }, - // .optional => |optional_element_type| blk: { - // switch (unit.types.get(optional_element_type).*) { - // .pointer => |pointer| { - // _ = pointer; - // @panic("super unreachable"); - // }, - // else => { - // const element_type = try llvm.getType(unit, context, optional_element_type); - // const selector_type = try llvm.getType(unit, context, .bool); - // const optional_types = [2]*LLVM.Type{ element_type, selector_type }; - // const name = "optional_type"; - // const is_packed = false; - // const struct_type = llvm.context.createStructType(&optional_types, optional_types.len, name, name.len, is_packed) orelse return Type.Error.@"struct"; - // break :blk struct_type.toType(); - // }, - // } - // }, .array => |array| blk: { const element_type = try llvm.getType(unit, context, array.type); const array_type = LLVM.Type.Array.get(element_type, array.count) orelse return Type.Error.array; @@ -1093,1057 +1266,6 @@ pub const LLVM = struct { } } - fn getDeclaration(llvm: *LLVM, declaration_index: Compilation.Declaration.Index) anyerror!*LLVM.Value { - if (llvm.declaration_map.get(declaration_index)) |declaration_value| { - return declaration_value; - } else { - // This is a global variable - const declaration_value = try llvm.emitDeclaration(declaration_index, null); - return declaration_value; - } - } - - fn emitInteger(llvm: *LLVM, unit: *Compilation.Unit, integer: Compilation.Value.Integer) !*LLVM.Value.Constant.Int { - const t = unit.types.get(integer.type); - const integer_type = t.integer; - const bit_count = integer_type.bit_count; - const signed = switch (integer_type.signedness) { - .signed => true, - .unsigned => false, - }; - const constant_integer = llvm.context.getConstantInt(bit_count, integer.value, signed) orelse return LLVM.Value.Error.constant_int; - return constant_integer; - } - - // fn emitValue(llvm: *LLVM, sema_value_index: Compilation.Value.Index, context: Compilation.ScopeType) anyerror!*LLVM.Value { - // const sema_value = llvm.sema.values.array.get(sema_value_index); - // const sema_type = sema_value.getType(llvm.sema); - // - // switch (sema_value.*) { - // .integer => |integer| { - // const bit_count = llvm.sema.types.array.get(integer.type).integer.bit_count; - // const constant_int = llvm.context.getConstantInt(bit_count, integer.value, switch (integer.signedness) { - // .unsigned => false, - // .signed => true, - // }) orelse return LLVM.Value.Error.constant_int; - // return constant_int.toValue(); - // }, - // .binary_operation => |binary_operation_index| { - // const binary_operation = llvm.sema.values.binary_operations.get(binary_operation_index); - // - // const sema_left_value = llvm.sema.values.array.get(binary_operation.left); - // const sema_left_type_index = sema_left_value.getType(llvm.sema); - // const sema_right_type_index = llvm.sema.values.array.get(binary_operation.right).getType(llvm.sema); - // assert(sema_left_type_index.eq(sema_right_type_index)); - // const sema_left_type = llvm.sema.types.array.get(sema_left_type_index); - // - // const expected_left_type = try llvm.getType(sema_left_type_index); - // - // const left = try llvm.emitValue(binary_operation.left, context); - // assert(left.getType() == expected_left_type); - // const right = try llvm.emitValue(binary_operation.right, context); - // assert(right.getType() == expected_left_type); - // - // switch (binary_operation.id) { - // .compare_less_than, - // .compare_greater_or_equal, - // .compare_equal, - // => { - // switch (sema_left_type.*) { - // .integer => |integer| { - // const integer_comparison: LLVM.Value.Instruction.ICmp.Kind = switch (integer.signedness) { - // .signed => switch (binary_operation.id) { - // .compare_less_than => .slt, - // .compare_greater_or_equal => .sge, - // .compare_equal => .eq, - // else => |t| @panic(@tagName(t)), - // }, - // .unsigned => switch (binary_operation.id) { - // .compare_less_than => .ult, - // .compare_greater_or_equal => .uge, - // .compare_equal => .eq, - // else => |t| @panic(@tagName(t)), - // }, - // }; - // const icmp = llvm.builder.createICmp(integer_comparison, left, right, "cmp", "cmp".len) orelse return LLVM.Value.Instruction.Error.icmp; - // return icmp; - // }, - // else => |t| @panic(@tagName(t)), - // } - // }, - // .add, - // .sub, - // .mul, - // .shift_left, - // => { - // switch (sema_left_type.*) { - // .integer => |integer_type| { - // const result = try llvm.arithmeticIntegerBinaryOperation(left, right, binary_operation.id, integer_type, "binary_operation"); - // return result; - // }, - // else => |t| @panic(@tagName(t)), - // } - // }, - // .div => { - // switch (sema_left_type.*) { - // .integer => |integer_type| { - // const is_exact = false; - // const result = switch (integer_type.signedness) { - // .unsigned => llvm.builder.createUDiv(left, right, "udiv", "udiv".len, is_exact) orelse return LLVM.Value.Instruction.Error.udiv, - // .signed => llvm.builder.createSDiv(left, right, "sdiv", "sdiv".len, is_exact) orelse return LLVM.Value.Instruction.Error.sdiv, - // }; - // - // return result; - // }, - // else => |t| @panic(@tagName(t)), - // } - // }, - // .mod => { - // switch (sema_left_type.*) { - // .integer => |integer_type| { - // const result = switch (integer_type.signedness) { - // .unsigned => llvm.builder.createURem(left, right, "urem", "urem".len) orelse return LLVM.Value.Instruction.Error.udiv, - // .signed => llvm.builder.createSRem(left, right, "srem", "srem".len) orelse return LLVM.Value.Instruction.Error.sdiv, - // }; - // - // return result; - // }, - // else => |t| @panic(@tagName(t)), - // } - // }, - // .shift_right => { - // switch (sema_left_type.*) { - // .integer => |integer_type| { - // const is_exact = false; - // const result = switch (integer_type.signedness) { - // .unsigned => llvm.builder.createLogicalShiftRight(left, right, "logical_shift_right", "logical_shift_right".len, is_exact) orelse return LLVM.Value.Instruction.Error.logical_shift_right, - // .signed => llvm.builder.createArithmeticShiftRight(left, right, "arithmetic_shift_right", "arithmetic_shift_right".len, is_exact) orelse return LLVM.Value.Instruction.Error.arithmetic_shift_right, - // }; - // - // return result; - // }, - // else => |t| @panic(@tagName(t)), - // } - // }, - // .bit_xor => { - // const xor = llvm.builder.createXor(left, right, "xor", "xor".len) orelse return LLVM.Value.Instruction.Error.xor; - // return xor; - // }, - // .bit_and => { - // const result = llvm.builder.createAnd(left, right, "and", "and".len) orelse return LLVM.Value.Instruction.Error.@"and"; - // return result; - // }, - // .bit_or => { - // const result = llvm.builder.createOr(left, right, "or", "or".len) orelse return LLVM.Value.Instruction.Error.@"or"; - // return result; - // }, - // else => |t| @panic(@tagName(t)), - // } - // }, - // .declaration_reference => |declaration_reference| { - // const declaration_alloca = try llvm.getDeclaration(declaration_reference.value); - // const is_volatile = false; - // const load_type = try llvm.getType(declaration_reference.getType(llvm.sema)); - // const load = llvm.builder.createLoad(load_type, declaration_alloca, is_volatile, "declaration_reference", "declaration_reference".len) orelse return LLVM.Value.Instruction.Error.load; - // return load.toValue(); - // }, - // .intrinsic => |intrinsic_index| return try llvm.emitIntrinsic(intrinsic_index, context), - // .enum_field => |enum_field_index| { - // const enum_field = llvm.sema.types.enum_fields.get(enum_field_index); - // switch (llvm.sema.types.array.get(enum_field.parent).*) { - // .@"enum" => |enum_index| { - // const enum_type = llvm.sema.types.enums.get(enum_index); - // const backing_type = llvm.sema.types.array.get(enum_type.backing_type); - // switch (backing_type.*) { - // .integer => |integer| { - // const is_signed = switch (integer.signedness) { - // .signed => true, - // .unsigned => false, - // }; - // assert(!is_signed); - // const enum_value = llvm.context.getConstantInt(integer.bit_count, enum_field.value, is_signed) orelse unreachable; - // return enum_value.toValue(); - // }, - // else => |t| @panic(@tagName(t)), - // } - // }, - // else => |t| @panic(@tagName(t)), - // } - // }, - // .unary_operation => |unary_operation_index| { - // const unary_operation = llvm.sema.values.unary_operations.get(unary_operation_index); - // switch (unary_operation.id) { - // .pointer_dereference => { - // const value = try llvm.emitValue(unary_operation.value, context); - // const is_volatile = false; - // const load = llvm.builder.createLoad(try llvm.getType(unary_operation.type), value, is_volatile, "pointer_dereference", "pointer_dereference".len) orelse return LLVM.Value.Instruction.Error.load; - // return load.toValue(); - // }, - // .address_of => { - // const pointer = try llvm.emitLValue(unary_operation.value, context); - // return pointer; - // }, - // else => |t| @panic(@tagName(t)), - // } - // }, - // .call => |call_index| { - // assert(context == .local); - // return try llvm.emitCall(call_index, context); - // }, - // .function_definition => |function_definition_index| { - // return llvm.function_definition_map.get(function_definition_index).?.toValue(); - // }, - // .container_initialization => |container_initialization_index| { - // const container_initialization = llvm.sema.values.container_initializations.get(container_initialization_index); - // const container_type = llvm.sema.types.array.get(container_initialization.type); - // const llvm_type = try llvm.getType(container_initialization.type); - // - // switch (container_type.*) { - // .@"struct" => |struct_index| { - // const struct_type = llvm.sema.types.structs.get(struct_index); - // switch (struct_type.backing_type.invalid) { - // true => { - // switch (context) { - // .global => { - // var initialization_values = try ArrayList(*LLVM.Value.Constant).initCapacity(context.allocator, container_initialization.field_initializations.items.len); - // - // for (container_initialization.field_initializations.items) |field_value_index| { - // const value = try llvm.emitValue(field_value_index, context); - // initialization_values.appendAssumeCapacity(value.toConstant() orelse unreachable); - // } - // - // // TODO: fix - // const llvm_struct_type = llvm_type.toStruct() orelse unreachable; - // const type_declaration = llvm.sema.values.declarations.get(llvm.sema.map.types.get(container_initialization.type).?); - // const name = llvm.sema.getName(type_declaration.name).?; - // std.debug.print("Type: {s}\n", .{name}); - // const constant_struct = llvm_struct_type.instantiate(initialization_values.items.ptr, initialization_values.items.len) orelse return LLVM.Value.Error.constant_struct; - // return constant_struct.toValue(); - // }, - // .local => { - // const alloca = llvm.builder.createAlloca(llvm_type, address_space, null, "struct_initialization", "struct initialization".len) orelse return LLVM.Value.Instruction.Error.alloca; - // - // const is_signed = false; - // - // for (struct_type.fields.items, container_initialization.field_initializations.items, 0..) |struct_field_index, field_initialization_value_index, index| { - // const struct_field = llvm.sema.types.container_fields.get(struct_field_index); - // const field_initialization = llvm.sema.values.array.get(field_initialization_value_index); - // const field_initialization_type = field_initialization.getType(llvm.sema); - // assert(field_initialization_type.eq(struct_field.type)); - // const llvm_field_type = try llvm.getType(struct_field.type); - // const index_value = llvm.context.getConstantInt(32, index, is_signed) orelse unreachable; - // const indices = [_]*LLVM.Value{index_value.toValue()}; - // const in_bounds = true; - // const gep = llvm.builder.createGEP(llvm_field_type, alloca.toValue(), &indices, indices.len, "gep", "gep".len, in_bounds) orelse return LLVM.Value.Instruction.Error.gep; - // const load = try llvm.emitValue(field_initialization_value_index, context); - // const is_volatile = false; - // const store = llvm.builder.createStore(load, gep, is_volatile) orelse return LLVM.Value.Instruction.Error.store; - // _ = store; - // } - // - // const is_volatile = false; - // const load = llvm.builder.createLoad(llvm_type, alloca.toValue(), is_volatile, "struct_init_load", "struct_init_load".len) orelse return LLVM.Value.Instruction.Error.load; - // return load.toValue(); - // }, - // else => unreachable, - // } - // }, - // false => switch (llvm.sema.types.array.get(struct_type.backing_type).*) { - // else => |t| @panic(@tagName(t)), - // }, - // } - // }, - // else => |t| @panic(@tagName(t)), - // } - // // container_initialization.field_initializations.items - // }, - // .slice_access => |slice_access_index| { - // const slice_access = llvm.sema.values.slice_accesses.get(slice_access_index); - // switch (llvm.sema.types.array.get(llvm.sema.values.array.get(slice_access.value).getType(llvm.sema)).*) { - // .slice => |slice| { - // _ = slice; - // - // const slice_access_value = try llvm.emitValue(slice_access.value, context); - // const index: c_uint = switch (slice_access.field) { - // .ptr => 0, - // .len => 1, - // }; - // const name = switch (slice_access.field) { - // .ptr => "slice_access_ptr", - // .len => "slice_access_len", - // }; - // const indices = [1]c_uint{index}; - // const len_value = llvm.builder.createExtractValue(slice_access_value, &indices, indices.len, name.ptr, name.len) orelse return LLVM.Value.Instruction.Error.extract_value; - // return len_value; - // }, - // else => |t| @panic(@tagName(t)), - // } - // // const llvm_type = try llvm.getType(slice_access.type); - // }, - // .field_access => |field_access_index| { - // const field_access = llvm.sema.values.field_accesses.get(field_access_index); - // const sema_field = llvm.sema.types.container_fields.get(field_access.field); - // const result_type = try llvm.getType(sema_field.type); - // const value = try llvm.emitValue(field_access.declaration_reference, context); - // _ = result_type; - // // extern fn bindings.NativityLLVMBuilderCreateGEP(builder: *LLVM.Builder, type: *LLVM.Type, pointer: *LLVM.Value, index_ptr: [*]const *LLVM.Value, index_count: usize, name_ptr: [*]const u8, name_len: usize, in_bounds: bool) ?*LLVM.Value; - // const indices = [1]u32{sema_field.index}; - // const result = llvm.builder.createExtractValue(value, &indices, indices.len, "field_access", "field_access".len) orelse return LLVM.Value.Instruction.Error.extract_value; - // return result; - // }, - // .optional_check => |optional_check_index| { - // const optional_check = llvm.sema.values.optional_checks.get(optional_check_index); - // const sema_optional_value = llvm.sema.values.array.get(optional_check.value); - // const optional_type_index = sema_optional_value.getType(llvm.sema); - // - // switch (llvm.sema.types.array.get(optional_type_index).*) { - // .optional => |optional| switch (llvm.sema.types.array.get(optional.element_type).*) { - // .pointer => |pointer| { - // _ = pointer; - // - // @panic("TODO: optional check for pointer"); - // }, - // else => { - // const optional_value = try llvm.emitValue(optional_check.value, context); - // const indices = [1]c_uint{1}; - // const result = llvm.builder.createExtractValue(optional_value, &indices, indices.len, "optional_check", "optional_check".len) orelse return LLVM.Value.Instruction.Error.extract_value; - // return result; - // }, - // }, - // else => |t| @panic(@tagName(t)), - // } - // }, - // .optional_unwrap => |optional_unwrap_index| { - // const optional_unwrap = llvm.sema.values.optional_unwraps.get(optional_unwrap_index); - // const sema_optional_value = llvm.sema.values.array.get(optional_unwrap.value); - // const optional_type_index = sema_optional_value.getType(llvm.sema); - // switch (llvm.sema.types.array.get(optional_type_index).*) { - // .optional => |optional| switch (llvm.sema.types.array.get(optional.element_type).*) { - // .pointer => |pointer| { - // _ = pointer; - // - // @panic("TODO: optional check for pointer"); - // }, - // else => { - // const optional_value = try llvm.emitValue(optional_unwrap.value, context); - // const indices = [1]c_uint{0}; - // const result = llvm.builder.createExtractValue(optional_value, &indices, indices.len, "optional_unwrap", "optional_unwrap".len) orelse return LLVM.Value.Instruction.Error.extract_value; - // return result; - // }, - // }, - // else => |t| @panic(@tagName(t)), - // } - // }, - // .optional_null_literal => |optional_type_index| { - // const optional_type = try llvm.getType(optional_type_index); - // const optional_undefined = optional_type.getUndefined() orelse unreachable; - // - // const indices = [1]c_uint{1}; - // const is_signed = false; - // const null_value = llvm.context.getConstantInt(1, 0, is_signed) orelse unreachable; - // const insert_value = llvm.builder.createInsertValue(optional_undefined.toValue(), null_value.toValue(), &indices, indices.len, "optional_null_literal", "optional_null_literal".len) orelse return LLVM.Value.Instruction.Error.insert_value; - // _ = insert_value; - // - // return optional_undefined.toValue(); - // }, - // .slice => |slice_expression_index| { - // const slice_expression = llvm.sema.values.slices.get(slice_expression_index); - // const sliceable = try llvm.emitValue(slice_expression.sliceable, context); - // const slice_expression_type = try llvm.getType(slice_expression.type); - // const sema_sliceable = llvm.sema.values.array.get(slice_expression.sliceable); - // const sema_sliceable_type_index = sema_sliceable.getType(llvm.sema); - // const sema_sliceable_type = llvm.sema.types.array.get(sema_sliceable_type_index); - // const start_value = try llvm.emitValue(slice_expression.range.start, context); - // const result = slice_expression_type.getUndefined() orelse unreachable; - // - // switch (sema_sliceable_type.*) { - // .slice => |slice| { - // const indices = [1]c_uint{0}; - // const sliceable_ptr = llvm.builder.createExtractValue(sliceable, &indices, indices.len, "sliceable_ptr", "sliceable_ptr".len) orelse return LLVM.Value.Instruction.Error.extract_value; - // const element_type = try llvm.getType(slice.element_type); - // const ptr_indices = [1]*LLVM.Value{start_value}; - // const in_bounds = true; - // const offset_ptr = llvm.builder.createGEP(element_type, sliceable_ptr, &ptr_indices, ptr_indices.len, "offset_ptr", "offset_ptr".len, in_bounds) orelse unreachable; - // const insert_slice_ptr = llvm.builder.createInsertValue(result.toValue(), offset_ptr, &indices, indices.len, "insert_slice_ptr", "insert_slice_ptr".len) orelse unreachable; - // _ = insert_slice_ptr; - // - // switch (slice_expression.range.end.invalid) { - // true => { - // const no_unsigned_wrapping = true; - // const no_signed_wrapping = false; - // const len_indices = [1]c_uint{1}; - // const sliceable_len = llvm.builder.createExtractValue(sliceable, &len_indices, len_indices.len, "sliceable_len", "sliceable_len".len) orelse return LLVM.Value.Instruction.Error.extract_value; - // const len_sub = llvm.builder.createSub(sliceable_len, start_value, "slice_len_arithmetic", "slice_len_arithmetic".len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.add; - // const insert_slice_len = llvm.builder.createInsertValue(result.toValue(), len_sub, &len_indices, len_indices.len, "insert_slice_len", "insert_slice_len".len) orelse unreachable; - // _ = insert_slice_len; - // return result.toValue(); - // }, - // false => unreachable, - // } - // }, - // .pointer => |pointer| { - // const offset_indices = [1]*LLVM.Value{start_value}; - // const ptr_indices = [1]c_uint{0}; - // const element_type = try llvm.getType(pointer.element_type); - // const in_bounds = true; - // const offset_ptr = llvm.builder.createGEP(element_type, sliceable, &offset_indices, offset_indices.len, "offset_ptr", "offset_ptr".len, in_bounds) orelse unreachable; - // const insert_slice_ptr = llvm.builder.createInsertValue(result.toValue(), offset_ptr, &ptr_indices, ptr_indices.len, "insert_slice_ptr", "insert_slice_ptr".len) orelse unreachable; - // _ = insert_slice_ptr; - // - // switch (slice_expression.range.end.invalid) { - // true => { - // switch (pointer.many) { - // true => @panic("Only pointer to array"), - // false => { - // switch (llvm.sema.types.array.get(pointer.element_type).*) { - // .array => |array| { - // const len_indices = [1]c_uint{1}; - // const is_signed = false; - // const constant_len = llvm.context.getConstantInt(64, array.element_count, is_signed) orelse unreachable; - // const no_unsigned_wrapping = true; - // const no_signed_wrapping = false; - // const len_sub = llvm.builder.createSub(constant_len.toValue(), start_value, "slice_len_arithmetic", "slice_len_arithmetic".len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.add; - // const insert_slice_len = llvm.builder.createInsertValue(result.toValue(), len_sub, &len_indices, len_indices.len, "insert_slice_len", "insert_slice_len".len) orelse unreachable; - // _ = insert_slice_len; - // return result.toValue(); - // }, - // else => |t| @panic(@tagName(t)), - // } - // }, - // } - // }, - // false => unreachable, - // } - // unreachable; - // }, - // else => |t| @panic(@tagName(t)), - // } - // }, - // .bool => |boolean| { - // const is_signed = false; - // const boolean_constant = llvm.context.getConstantInt(1, @intFromBool(boolean), is_signed) orelse unreachable; - // return boolean_constant.toValue(); - // }, - // .string_literal => |sema_string_literal| { - // const string = llvm.sema.getName(sema_string_literal.hash) orelse unreachable; - // const llvm_const_string = llvm.builder.createGlobalStringPointer(string.ptr, string.len, "string_literal", "string_literal".len, address_space, llvm.module) orelse unreachable; - // return llvm_const_string.toValue(); - // }, - // .indexed_access => |indexed_access_index| { - // const indexed_access = llvm.sema.values.indexed_accesses.get(indexed_access_index); - // const indexed_value = llvm.sema.values.array.get(indexed_access.indexed_expression); - // const indexed_type = indexed_value.getType(llvm.sema); - // const index = try llvm.emitValue(indexed_access.index_expression, context); - // const indexed = try llvm.emitValue(indexed_access.indexed_expression, context); - // - // switch (llvm.sema.types.array.get(indexed_type).*) { - // .pointer => |pointer| { - // switch (pointer.many) { - // true => { - // const element_type = try llvm.getType(pointer.element_type); - // const indices = [_]*LLVM.Value{index}; - // const in_bounds = true; - // const pointer_access = llvm.builder.createGEP(element_type, indexed, &indices, indices.len, "indexed_pointer_access", "indexed_pointer_access".len, in_bounds) orelse unreachable; - // - // const result_type = try llvm.getType(sema_type); - // const is_volatile = false; - // const name = "indexed_pointer_load"; - // const load = llvm.builder.createLoad(result_type, pointer_access, is_volatile, name, name.len) orelse return LLVM.Value.Instruction.Error.load; - // const load_value = load.toValue(); - // return load_value; - // }, - // false => unreachable, - // } - // }, - // .slice => |slice| { - // const slice_indices = [1]c_uint{0}; - // const slice_ptr = llvm.builder.createExtractValue(indexed, &slice_indices, slice_indices.len, "slice_ptr", "slice_ptr".len) orelse return LLVM.Value.Instruction.Error.extract_value; - // const element_type = try llvm.getType(slice.element_type); - // const indices = [_]*LLVM.Value{index}; - // const in_bounds = true; - // const pointer_access = llvm.builder.createGEP(element_type, slice_ptr, &indices, indices.len, "indexed_pointer_access", "indexed_pointer_access".len, in_bounds) orelse unreachable; - // const result_type = try llvm.getType(sema_type); - // const is_volatile = false; - // const name = "indexed_pointer_load"; - // const load = llvm.builder.createLoad(result_type, pointer_access, is_volatile, name, name.len) orelse return LLVM.Value.Instruction.Error.load; - // - // const load_value = load.toValue(); - // assert(load_value.getType() == result_type); - // return load.toValue(); - // }, - // else => |t| @panic(@tagName(t)), - // } - // }, - // .character_literal => |ch| { - // const is_signed = false; - // const constant = llvm.context.getConstantInt(8, ch, is_signed) orelse unreachable; - // return constant.toValue(); - // }, - // .array_initialization => |array_initialization_index| { - // const array_initialization = llvm.sema.values.container_initializations.get(array_initialization_index); - // const initialization_type = try llvm.getType(array_initialization.type); - // const array_type = initialization_type.toArray() orelse unreachable; - // const array_element_type = array_type.getElementType() orelse unreachable; - // const sema_array_element_type = switch (llvm.sema.types.array.get(array_initialization.type).*) { - // .array => |array| array.element_type, - // else => |t| @panic(@tagName(t)), - // }; - // - // if (array_initialization.is_comptime) { - // var list = try ArrayList(*LLVM.Value.Constant).initCapacity(context.allocator, array_initialization.field_initializations.items.len); - // - // for (array_initialization.field_initializations.items) |element_initialization| { - // const value = try llvm.emitValue(element_initialization, context); - // const sema_value_type = llvm.sema.values.array.get(element_initialization).getType(llvm.sema); - // assert(sema_value_type.eq(sema_array_element_type)); - // const value_type = value.getType(); - // if (!value_type.compare(array_element_type)) { - // unreachable; - // } - // const constant = value.toConstant() orelse unreachable; - // list.appendAssumeCapacity(constant); - // } - // - // const constant_array = array_type.getConstant(list.items.ptr, list.items.len) orelse unreachable; - // return constant_array.toValue(); - // } else { - // const array_undefined = initialization_type.getUndefined() orelse unreachable; - // - // for (array_initialization.field_initializations.items, 0..) |element_initialization, index| { - // const value = try llvm.emitValue(element_initialization, context); - // const indices = [1]c_uint{@intCast(index)}; - // const insert_array_element = llvm.builder.createInsertValue(array_undefined.toValue(), value, &indices, indices.len, "insert_array_element", "insert_array_element".len) orelse unreachable; - // _ = insert_array_element; - // } - // - // return array_undefined.toValue(); - // } - // }, - // else => |t| @panic(@tagName(t)), - // } - // } - - // fn emitCall(llvm: *LLVM, call_index: Compilation.Call.Index, context: Compilation.ScopeType) !*LLVM.Value { - // assert(context == .local); - // var argument_buffer: [32]*LLVM.Value = undefined; - // const sema_call = llvm.sema.values.calls.get(call_index); - // const sema_call_arguments = llvm.sema.values.argument_lists.get(sema_call.arguments).array.items; - // const argument_count = sema_call_arguments.len; - // const arguments = argument_buffer[0..argument_count]; - // - // const sema_type = llvm.sema.values.array.get(sema_call.value).getType(llvm.sema); - // - // switch (llvm.sema.values.array.get(sema_call.value).*) { - // .function_definition => |function_definition_index| { - // const function_definition = llvm.sema.types.function_definitions.get(function_definition_index); - // assert(function_definition.prototype.eq(sema_type)); - // - // const function_prototype_type = llvm.sema.types.array.get(function_definition.prototype); - // const function_prototype = llvm.sema.types.function_prototypes.get(function_prototype_type.function); - // const declaration_index = llvm.sema.map.function_definitions.get(function_definition_index).?; - // const declaration = llvm.sema.values.declarations.get(declaration_index); - // const declaration_name = llvm.sema.getName(declaration.name).?; - // std.debug.print("Call to {s}\n", .{declaration_name}); - // if (equal(u8, declaration_name, "count_slice_byte_count")) { - // //@breakpoint(); - // } - // - // const callee = try llvm.emitValue(sema_call.value, context); - // - // for (function_prototype.arguments.items, sema_call_arguments, arguments) |argument_declaration_index, sema_call_value_index, *argument| { - // const argument_declaration = llvm.sema.values.declarations.get(argument_declaration_index); - // const argument_type = argument_declaration.getType(); - // switch (llvm.sema.types.array.get(argument_type).*) { - // .integer => |_| { - // argument.* = try llvm.emitValue(sema_call_value_index, context); - // }, - // .@"struct" => |struct_index| { - // const struct_type = llvm.sema.types.structs.get(struct_index); - // if (!struct_type.backing_type.invalid) { - // unreachable; - // } else { - // unreachable; - // } - // }, - // else => |t| @panic(@tagName(t)), - // } - // - // unreachable; - // // _ = argument_declaration_index; - // // const call_argument = llvm.sema.values.array.get(sema_call_value_index); - // // const call_argument_type = call_argument.getType(llvm.sema); - // // const cat = llvm.sema.types.array.get(call_argument_type); - // // const argument_declaration = llvm.sema.values.declarations.get(argument_declaration_index); - // // const argument_declaration_type = argument_declaration.getType(); - // // const argument_type = try llvm.getType(argument_declaration_type); - // - // // if (!call_argument_type.eq(argument_declaration_type)) { - // // switch (llvm.sema.types.array.get(argument_declaration_type).*) { - // // .slice => |slice| { - // // _ = slice; - // // - // // const result = argument_type.getUndefined() orelse unreachable; - // // - // // const ptr_indices = [1]c_uint{0}; - // // const extract_slice_ptr = llvm.builder.createExtractValue(argument.*, &ptr_indices, ptr_indices.len, "extract_slice_ptr", "extract_slice_ptr".len) orelse unreachable; - // // const insert_slice_ptr = llvm.builder.createInsertValue(result.toValue(), extract_slice_ptr, &ptr_indices, ptr_indices.len, "insert_slice_ptr", "insert_slice_ptr".len) orelse unreachable; - // // _ = insert_slice_ptr; - // // const len_indices = [1]c_uint{1}; - // // const extract_slice_len = llvm.builder.createExtractValue(argument.*, &len_indices, len_indices.len, "extract_slice_ptr", "extract_slice_ptr".len) orelse unreachable; - // // const insert_slice_len = llvm.builder.createInsertValue(result.toValue(), extract_slice_len, &len_indices, len_indices.len, "insert_slice_len", "insert_slice_len".len) orelse unreachable; - // // _ = insert_slice_len; - // // argument.* = result.toValue(); - // // }, - // // else => |t| @panic(@tagName(t)), - // // } - // // // argument.* = llvm.builder.createCast(.bitcast, argument.*, argument_type, "arg_bitcast", "arg_bitcast".len) orelse unreachable; - // // } - // } - // - // // const function = callee.toFunction() orelse unreachable; - // // const llvm_calling_convention = function.getCallingConvention(); - // // const name = if (sema_call.type.eq(Compilation.Type.void) or sema_call.type.eq(Compilation.Type.noreturn)) "" else "call"; - // // const callee_type = try llvm.getType(sema_type); - // // const function_type = callee_type.toFunction() orelse unreachable; - // // const call = llvm.builder.createCall(function_type, callee, arguments.ptr, arguments.len, name.ptr, name.len, null) orelse return LLVM.Value.Instruction.Error.call; - // // call.setCallingConvention(llvm_calling_convention); - // // - // // return call.toValue(); - // // - // }, - // else => |t| @panic(@tagName(t)), - // } - // } - - fn arithmeticIntegerBinaryOperation(llvm: *LLVM, left: *LLVM.Value, right: *LLVM.Value, binary_operation: Compilation.BinaryOperation.Id, sema_integer_type: Compilation.Type.Integer, name: []const u8) !*LLVM.Value { - var no_signed_wrapping = false; - var no_unsigned_wrapping = false; - - switch (sema_integer_type.signedness) { - .signed => no_signed_wrapping = true, - .unsigned => no_unsigned_wrapping = true, - } - - assert(left.getType().isInteger()); - assert(right.getType().isInteger()); - - return switch (binary_operation) { - .add => llvm.builder.createAdd(left, right, name.ptr, name.len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.add, - .sub => llvm.builder.createSub(left, right, name.ptr, name.len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.add, - .mul => llvm.builder.createMultiply(left, right, name.ptr, name.len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.multiply, - .shift_left => llvm.builder.createShiftLeft(left, right, name.ptr, name.len, no_unsigned_wrapping, no_signed_wrapping) orelse return LLVM.Value.Instruction.Error.shift_left, - else => |t| @panic(@tagName(t)), - }; - } - - fn emitStatement(llvm: *LLVM, sema_statement_index: Compilation.Statement.Index, context: Compilation.ScopeType) anyerror!void { - const sema_statement = llvm.sema.values.statements.get(sema_statement_index); - const sema_statement_value = llvm.sema.values.array.get(sema_statement.value); - - llvm.builder.setCurrentDebugLocation(llvm.context, sema_statement.line + 1, sema_statement.column + 1, llvm.scope, llvm.function); - - switch (sema_statement_value.*) { - .branch => |branch_index| { - const branch = llvm.sema.values.branches.get(branch_index); - const branch_type = llvm.sema.values.array.get(branch.expression).getType(llvm.sema); - assert(branch_type.eq(Compilation.Type.boolean)); - const condition_value = try llvm.emitValue(branch.expression, context); - const previous_inside_branch = llvm.inside_branch; - llvm.inside_branch = true; - const taken_basic_block = llvm.context.createBasicBlock("branch_taken", "branch_taken".len, llvm.function, null) orelse return Error.basic_block; - var not_taken_basic_block: ?*LLVM.Value.BasicBlock = null; - var fuse_basic_block: ?*LLVM.Value.BasicBlock = null; - const acting_fuse_block = switch (branch.not_taken_expression.invalid) { - true => b: { - const block = llvm.context.createBasicBlock("branch_fuse", "branch_fuse".len, llvm.function, null) orelse return Error.basic_block; - fuse_basic_block = block; - break :b block; - }, - false => b: { - const block = llvm.context.createBasicBlock("branch_not_taken", "branch_fuse".len, llvm.function, null) orelse return Error.basic_block; - not_taken_basic_block = block; - break :b block; - }, - }; - - const branch_weights = null; - const unpredictable = null; - const conditional_branch = llvm.builder.createConditionalBranch(condition_value, taken_basic_block, acting_fuse_block, branch_weights, unpredictable) orelse return LLVM.Value.Instruction.Error.conditional_branch; - _ = conditional_branch; - - var taken_reaches_end = true; - { - llvm.builder.setInsertPoint(taken_basic_block); - - switch (llvm.sema.values.array.get(branch.taken_expression).*) { - .block => |block_index| { - taken_reaches_end = llvm.sema.values.blocks.get(block_index).reaches_end; - const emit_arguments = false; - try llvm.emitBlock(block_index, context, emit_arguments); - }, - else => |t| @panic(@tagName(t)), - } - } - - if (taken_reaches_end) { - assert(!llvm.builder.isCurrentBlockTerminated()); - if (fuse_basic_block == null) { - const fuse_block = llvm.context.createBasicBlock("branch_fuse", "branch_fuse".len, llvm.function, null) orelse return Error.basic_block; - fuse_basic_block = fuse_block; - } - const merge_br = llvm.builder.createBranch(fuse_basic_block orelse unreachable); - _ = merge_br; - } - - var not_taken_reaches_end = true; - if (!branch.not_taken_expression.invalid) { - llvm.builder.setInsertPoint(not_taken_basic_block orelse unreachable); - - switch (llvm.sema.values.array.get(branch.not_taken_expression).*) { - .block => |block_index| { - const emit_arguments = false; - try llvm.emitBlock(block_index, context, emit_arguments); - not_taken_reaches_end = llvm.sema.values.blocks.get(block_index).reaches_end; - }, - else => |t| @panic(@tagName(t)), - } - - if (not_taken_reaches_end) { - assert(!llvm.builder.isCurrentBlockTerminated()); - if (fuse_basic_block == null) { - const fuse_block = llvm.context.createBasicBlock("branch_fuse", "branch_fuse".len, llvm.function, null) orelse return Error.basic_block; - fuse_basic_block = fuse_block; - } - - const merge_br = llvm.builder.createBranch(fuse_basic_block orelse unreachable); - _ = merge_br; - } - } - - if (fuse_basic_block) |end_block| { - llvm.builder.setInsertPoint(end_block); - } - - llvm.inside_branch = previous_inside_branch; - }, - .loop => |loop_index| { - const loop = llvm.sema.values.loops.get(loop_index); - assert(context == .local); - const previous_inside_branch = llvm.inside_branch; - llvm.inside_branch = true; - for (loop.pre.items) |pre_statement_value_index| { - try llvm.emitStatement(pre_statement_value_index, context); - } - const header_basic_block = llvm.context.createBasicBlock("loop_header", "loop_header".len, llvm.function, null) orelse return Error.basic_block; - const jump_to_loop = llvm.builder.createBranch(header_basic_block) orelse unreachable; - _ = jump_to_loop; - const body_basic_block = llvm.context.createBasicBlock("loop_body", "loop_body".len, llvm.function, null) orelse return Error.basic_block; - const previous_exit_block = llvm.exit_block; - const exit_basic_block = llvm.context.createBasicBlock("loop_exit", "loop_exit".len, llvm.function, null) orelse return Error.basic_block; - llvm.exit_block = exit_basic_block; - - llvm.builder.setInsertPoint(header_basic_block); - - const condition = try llvm.emitValue(loop.condition, context); - const branch_weights = null; - const unpredictable = null; - const conditional_branch = llvm.builder.createConditionalBranch(condition, body_basic_block, exit_basic_block, branch_weights, unpredictable) orelse unreachable; - _ = conditional_branch; - llvm.builder.setInsertPoint(body_basic_block); - try llvm.emitStatement(loop.body, context); - - for (loop.post.items) |post_statement_value_index| { - try llvm.emitStatement(post_statement_value_index, context); - } - - // if (!llvm.builder.isCurrentBlockTerminated()) { - const jump_to_header = llvm.builder.createBranch(header_basic_block) orelse unreachable; - _ = jump_to_header; - // } - - llvm.builder.setInsertPoint(exit_basic_block); - llvm.exit_block = previous_exit_block; - llvm.inside_branch = previous_inside_branch; - - // if (llvm.inside_branch and !llvm.builder.isCurrentBlockTerminated()) { - // unreachable; - // } - }, - .@"break" => { - const jump_to_exit_block = llvm.builder.createBranch(llvm.exit_block) orelse unreachable; - _ = jump_to_exit_block; - }, - else => |t| @panic(@tagName(t)), - } - } - - fn emitIntrinsic(llvm: *LLVM, intrinsic_index: Compilation.Intrinsic.Index, context: Compilation.ScopeType) !*LLVM.Value { - const intrinsic = llvm.sema.values.intrinsics.get(intrinsic_index); - switch (intrinsic.kind) { - .cast => |sema_cast_value| { - const value = try llvm.emitValue(sema_cast_value, context); - const destination_type = llvm.sema.types.array.get(intrinsic.type); - const source_value = llvm.sema.values.array.get(sema_cast_value); - const source_type = llvm.sema.types.array.get(source_value.getType(llvm.sema)); - - switch (destination_type.*) { - .integer => |destination_integer| { - switch (source_type.*) { - .@"enum" => return value, - .integer => |source_integer| { - if (source_integer.bit_count == destination_integer.bit_count) { - return value; - } else if (source_integer.bit_count < destination_integer.bit_count) { - assert(source_integer.signedness != destination_integer.signedness); - const cast_type: LLVM.Value.Instruction.Cast.Type = switch (destination_integer.signedness) { - .signed => .sign_extend, - .unsigned => .zero_extend, - }; - const name = @tagName(cast_type); - const cast = llvm.builder.createCast(cast_type, value, try llvm.getType(intrinsic.type), name.ptr, name.len) orelse return LLVM.Value.Instruction.Error.cast; - return cast; - } else if (source_integer.bit_count > destination_integer.bit_count) { - const cast = llvm.builder.createCast(.truncate, value, try llvm.getType(intrinsic.type), "truncate", "truncate".len) orelse return LLVM.Value.Instruction.Error.cast; - return cast; - } else unreachable; - }, - .pointer => |pointer| { - _ = pointer; - assert(destination_integer.bit_count == 64); - const cast = llvm.builder.createCast(.pointer_to_int, value, try llvm.getType(intrinsic.type), "pointer_to_int", "pointer_to_int".len) orelse return LLVM.Value.Instruction.Error.cast; - return cast; - }, - else => |t| @panic(@tagName(t)), - } - }, - .pointer => |pointer| { - _ = pointer; - - switch (source_type.*) { - .integer => { - const cast = llvm.builder.createCast(.int_to_pointer, value, try llvm.getType(intrinsic.type), "int_to_pointer", "int_to_pointer".len) orelse return LLVM.Value.Instruction.Error.cast; - return cast; - }, - else => |t| @panic(@tagName(t)), - } - }, - else => |t| @panic(@tagName(t)), - } - }, - .sign_extend => |sema_value| { - const value = try llvm.emitValue(sema_value, context); - - const sign_extend = llvm.builder.createCast(.sign_extend, value, try llvm.getType(intrinsic.type), "sign_extend", "sign_extend".len) orelse return LLVM.Value.Instruction.Error.cast; - return sign_extend; - }, - .zero_extend => |sema_value| { - const value = try llvm.emitValue(sema_value, context); - - const zero_extend = llvm.builder.createCast(.zero_extend, value, try llvm.getType(intrinsic.type), "zero_extend", "zero_extend".len) orelse return LLVM.Value.Instruction.Error.cast; - return zero_extend; - }, - .optional_wrap => |sema_value| { - const optional_type = try llvm.getType(intrinsic.type); - switch (llvm.sema.types.array.get(intrinsic.type).*) { - .optional => |optional| switch (llvm.sema.types.array.get(optional.element_type).*) { - .integer => { - const alloca = llvm.builder.createAlloca(optional_type, address_space, null, "optional_wrap_alloca", "optional_wrap_alloca".len) orelse return LLVM.Value.Instruction.Error.alloca; - const is_signed = false; - const index_zero = llvm.context.getConstantInt(32, 0, is_signed) orelse unreachable; - const index_one = llvm.context.getConstantInt(32, 1, is_signed) orelse unreachable; - - const optional_element_type = try llvm.getType(optional.element_type); - const boolean_type = try llvm.getType(Compilation.Type.boolean); - - const indices0 = [_]*LLVM.Value{index_zero.toValue()}; - const in_bounds = true; - const gep0 = llvm.builder.createGEP(optional_element_type, alloca.toValue(), &indices0, indices0.len, "gep", "gep".len, in_bounds) orelse return LLVM.Value.Instruction.Error.gep; - const load0 = try llvm.emitValue(sema_value, context); - const is_volatile = false; - const store0 = llvm.builder.createStore(load0, gep0, is_volatile) orelse return LLVM.Value.Instruction.Error.store; - _ = store0; - - const indices1 = [_]*LLVM.Value{index_one.toValue()}; - const gep1 = llvm.builder.createGEP(boolean_type, alloca.toValue(), &indices1, indices1.len, "gep", "gep".len, in_bounds) orelse return LLVM.Value.Instruction.Error.gep; - const load1 = llvm.context.getConstantInt(1, 1, is_signed) orelse unreachable; - const store1 = llvm.builder.createStore(load1.toValue(), gep1, is_volatile) orelse return LLVM.Value.Instruction.Error.store; - _ = store1; - - const load = llvm.builder.createLoad(optional_type, alloca.toValue(), is_volatile, "struct_init_load", "struct_init_load".len) orelse return LLVM.Value.Instruction.Error.load; - return load.toValue(); - }, - else => |t| @panic(@tagName(t)), - }, - else => |t| @panic(@tagName(t)), - } - }, - .min => |sema_values| { - switch (llvm.sema.types.array.get(intrinsic.type).*) { - .integer => |integer_type| { - const intrinsic_name = switch (integer_type.signedness) { - .unsigned => "llvm.umin", - .signed => "llvm.smin", - }; - const intrinsic_id = LLVM.lookupIntrinsic(intrinsic_name.ptr, intrinsic_name.len); - assert(intrinsic_id != .none); - - const left_type = llvm.sema.values.array.get(sema_values.left).getType(llvm.sema); - const right_type = llvm.sema.values.array.get(sema_values.right).getType(llvm.sema); - assert(left_type.eq(right_type)); - assert(left_type.eq(intrinsic.type)); - const intrinsic_return_type = try llvm.getType(intrinsic.type); - const types = [_]*LLVM.Type{intrinsic_return_type}; - const intrinsic_function = llvm.module.getIntrinsicDeclaration(intrinsic_id, &types, types.len) orelse return LLVM.Value.Error.intrinsic; - const intrinsic_function_type = llvm.context.getIntrinsicType(intrinsic_id, &types, types.len) orelse return LLVM.Type.Error.intrinsic; - - const left = try llvm.emitValue(sema_values.left, context); - const right = try llvm.emitValue(sema_values.right, context); - const arguments = [_]*LLVM.Value{ left, right }; - - const call = llvm.builder.createCall(intrinsic_function_type, intrinsic_function.toValue(), &arguments, arguments.len, "min".ptr, "min".len, null) orelse return LLVM.Value.Instruction.Error.call; - return call.toValue(); - }, - else => |t| @panic(@tagName(t)), - } - }, - .array_coerce_to_slice => |sema_value| { - const result_type = try llvm.getType(intrinsic.type); - const slice_type = result_type.toStruct() orelse unreachable; - const appointee_value = try llvm.emitValue(sema_value, context); - switch (llvm.sema.values.array.get(sema_value).*) { - .array_initialization => |array_initialization_index| { - const array_initialization = llvm.sema.values.container_initializations.get(array_initialization_index); - if (array_initialization.is_comptime) { - const constant_array = appointee_value.toConstant() orelse unreachable; - const array_type = try llvm.getType(array_initialization.type); - const is_constant = true; - const linkage = LLVM.Linkage.@"extern"; - const thread_local_mode = LLVM.ThreadLocalMode.not_thread_local; - const externally_initialized = false; - const global_variable = llvm.module.addGlobalVariable(array_type, is_constant, linkage, constant_array, "", "".len, null, thread_local_mode, address_space, externally_initialized) orelse return LLVM.Value.Error.constant_array; - const is_signed = false; - const len_constant = llvm.context.getConstantInt(@bitSizeOf(usize), array_initialization.field_initializations.items.len, is_signed) orelse unreachable; - const slice_values = [2]*LLVM.Value.Constant{ global_variable.toConstant(), len_constant.toConstant() }; - const constant = slice_type.instantiateConstant(&slice_values, slice_values.len) orelse unreachable; - return constant.toValue(); - } else { - unreachable; - } - // const ptr_indices = [1]c_uint{0}; - // const is_signed = false; - // const len_indices = [1]c_uint{1}; - // const insert_slice_len = llvm.builder.createInsertValue(result.toValue(), len_constant.toValue(), &len_indices, len_indices.len, "insert_slice_len", "insert_slice_len".len) orelse unreachable; - // _ = insert_slice_len; - // _ = insert_slice_ptr; - // return result.toValue(); - // } else { - // unreachable; - // } - }, - else => |t| @panic(@tagName(t)), - } - - // switch (llvm.sema.values.array.get(sema_value).*) { - // .string_literal => |string_literal| { - // const name = llvm.sema.getName(string_literal.hash).?; - // const expected_type = try llvm.getType(string_literal.type); - // assert(expected_type.compare(appointee_value.getType() orelse unreachable)); - // const ptr_indices = [1]c_uint{0}; - // const insert_slice_ptr = llvm.builder.createInsertValue(result.toValue(), appointee_value, &ptr_indices, ptr_indices.len, "insert_slice_ptr", "insert_slice_ptr".len) orelse unreachable; - // const is_signed = false; - // const len_constant = llvm.context.getConstantInt(@bitSizeOf(usize), name.len, is_signed) orelse unreachable; - // const len_indices = [1]c_uint{1}; - // const insert_slice_len = llvm.builder.createInsertValue(result.toValue(), len_constant.toValue(), &len_indices, len_indices.len, "insert_slice_len", "insert_slice_len".len) orelse unreachable; - // _ = insert_slice_len; - // _ = insert_slice_ptr; - // return result.toValue(); - // }, - // else => |t| @panic(@tagName(t)), - // } - unreachable; - }, - else => |t| @panic(@tagName(t)), - } - } - - fn renderDeclarationName(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, declaration_index: Compilation.Declaration.Index, mangle: bool) anyerror![]const u8 { - if (llvm.declaration_names.get(declaration_index)) |name| { - return name; - } else { - const declaration = unit.declarations.get(declaration_index); - const base_declaration_name = unit.getIdentifier(declaration.name); - var list = ArrayList(u8){}; - - try list.insertSlice(context.allocator, 0, base_declaration_name); - - if (mangle) { - switch (declaration.scope.kind) { - .compilation_unit, .file, .file_container, .container => { - var scope_it: ?*Compilation.Scope = declaration.scope; - while (scope_it) |scope| : (scope_it = scope.parent) { - const type_index = switch (scope.kind) { - .compilation_unit => break, - .file => b: { - const file = @fieldParentPtr(Compilation.File, "scope", scope); - break :b file.type; - }, - else => break, - }; - - if (unit.type_declaration_map.get(type_index)) |scope_declaration_index| { - const scope_declaration = unit.declarations.get(scope_declaration_index); - const declaration_name = unit.getIdentifier( scope_declaration.name); - try list.insert(context.allocator, 0, '.'); - try list.insertSlice(context.allocator, 0, declaration_name); - } - } - }, - .function, .block => {}, - } - } - - // TODO: enhance declaration name rendering with file scope name - // const scope = declaration.scope; - try llvm.declaration_names.putNoClobber(context.allocator, declaration_index, list.items); - - return list.items; - } - } - - fn emitBlock(llvm: *LLVM, block_index: Compilation.Block.Index, context: Compilation.ScopeType, emit_arguments: bool) !void { - const block = llvm.sema.values.blocks.get(block_index); - const previous_scope = llvm.scope; - const lexical_block = llvm.debug_info_builder.createLexicalBlock(previous_scope, llvm.file, block.line + 1, block.column + 1) orelse unreachable; - llvm.scope = lexical_block.toScope(); - llvm.builder.setCurrentDebugLocation(llvm.context, block.line + 1, block.column + 1, llvm.scope, llvm.function); - - if (emit_arguments) { - const sema_function = llvm.sema.types.function_definitions.get(llvm.sema_function); - const function_prototype = llvm.sema.types.function_prototypes.get(llvm.sema.types.array.get(sema_function.prototype).function); - _ = function_prototype; - // TODO: rewrite - var argument_buffer: [16]*LLVM.Value.Argument = undefined; - var argument_count: usize = argument_buffer.len; - llvm.function.getArguments(&argument_buffer, &argument_count); - const arguments = argument_buffer[0..argument_count]; - - for (arguments) |arg| { - const argument_value = arg.toValue(); - const alloca_array_size = null; - const declaration_alloca = llvm.builder.createAlloca(argument_value.getType(), address_space, alloca_array_size, "", "".len) orelse return LLVM.Value.Instruction.Error.alloca; - const is_volatile = false; - const store = llvm.builder.createStore(argument_value, declaration_alloca.toValue(), is_volatile) orelse return LLVM.Value.Instruction.Error.store; - _ = store; - } - } - - for (block.statements.items) |sema_statement_value_index| { - try llvm.emitStatement(sema_statement_value_index, context); - } - - llvm.scope = previous_scope; - } - fn getDebugInfoFile(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, sema_file_index: Compilation.Debug.File.Index) !*DebugInfo.File { if (llvm.debug_info_file_map.get(sema_file_index)) |file| { return file; @@ -2159,11 +1281,10 @@ pub const LLVM = struct { } fn renderTypeName(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, sema_type_index: Compilation.Type.Index) ![]const u8 { - if (llvm.type_name_map.get(sema_type_index)) |typename| { - return typename; - } else { + const gop = try llvm.type_name_map.getOrPut(context.allocator, sema_type_index); + if (!gop.found_existing) { if (unit.type_declarations.get(sema_type_index)) |global_declaration| { - return unit.getIdentifier(global_declaration.declaration.name); + gop.value_ptr.* = unit.getIdentifier(global_declaration.declaration.name); } else { const sema_type = unit.types.get(sema_type_index); const result: []const u8 = switch (sema_type.*) { @@ -2192,8 +1313,8 @@ pub const LLVM = struct { if (struct_type.optional) { var name = ArrayList(u8){}; try name.append(context.allocator, '?'); - - const element_type_name = try llvm.renderTypeName(unit, context, unit.struct_fields.get( struct_type.fields.items[0]).type); + + const element_type_name = try llvm.renderTypeName(unit, context, unit.struct_fields.get(struct_type.fields.items[0]).type); try name.appendSlice(context.allocator, element_type_name); break :b name.items; @@ -2201,29 +1322,6 @@ pub const LLVM = struct { unreachable; } }, - // .@"enum", - // .@"struct", - // => b: { - // if (unit.type_declaration_map.get(sema_type_index)) |type_declaration_index| { - // const declaration = unit.declarations.get(type_declaration_index); - // const name = unit.getIdentifier(declaration.name); - // break :b name; - // } else { - // unreachable; - // } - // }, - // .optional => |optional| b: { - // var name = ArrayList(u8){}; - // const element_type_name = try llvm.renderTypeName(optional.element_type); - // try name.writer(context.allocator).print("?{s}", .{element_type_name}); - // break :b name.items; - // }, - // .array => |array| b: { - // var name = ArrayList(u8){}; - // const element_type_name = try llvm.renderTypeName(array.element_type); - // try name.writer(context.allocator).print("[{}]{s}", .{ array.element_count, element_type_name }); - // break :b name.items; - // }, // TODO: termination .slice => |slice| b: { var name = ArrayList(u8){}; @@ -2245,13 +1343,16 @@ pub const LLVM = struct { break :b name.items; }, + // TODO + .function => "fn_type", else => |t| @panic(@tagName(t)), }; - try llvm.type_name_map.putNoClobber(context.allocator, sema_type_index, result); - return result; + gop.value_ptr.* = result; } } + + return gop.value_ptr.*; } fn createDebugStructType(llvm: *LLVM, arguments: struct { @@ -2262,6 +1363,7 @@ pub const LLVM = struct { bitsize: u64, alignment: u32, field_types: []const *LLVM.DebugInfo.Type, + forward_declaration: ?*LLVM.DebugInfo.Type.Composite, }) *LLVM.DebugInfo.Type.Composite { const flags = LLVM.DebugInfo.Node.Flags{ .visibility = .none, @@ -2293,171 +1395,608 @@ pub const LLVM = struct { .all_calls_described = false, }; - const struct_type = llvm.debug_info_builder.createStructType(arguments.scope, arguments.name.ptr, arguments.name.len, arguments.file, arguments.line, arguments.bitsize, arguments.alignment, flags, null, arguments.field_types.ptr, arguments.field_types.len) orelse unreachable; + const struct_type = llvm.debug_info_builder.createStructType(arguments.scope, arguments.name.ptr, arguments.name.len, arguments.file, arguments.line, arguments.bitsize, arguments.alignment, flags, null, arguments.field_types.ptr, arguments.field_types.len, arguments.forward_declaration) orelse unreachable; return struct_type; } fn getDebugType(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, sema_type_index: Compilation.Type.Index) !*LLVM.DebugInfo.Type { - if (llvm.debug_type_map.get(sema_type_index)) |t| { - return t; + if (false) { + const gop = try llvm.debug_type_map.getOrPut(context.allocator, sema_type_index); + if (gop.found_existing) { + const result = gop.value_ptr.*; + assert(@intFromPtr(result) != 0xaaaa_aaaa_aaaa_aaaa); + return result; + } else { + const name = try llvm.renderTypeName(unit, context, sema_type_index); + const sema_type = unit.types.get(sema_type_index); + const result = switch (sema_type.*) { + .integer => |integer| b: { + const dwarf_encoding: LLVM.DebugInfo.AttributeType = switch (integer.signedness) { + .unsigned => .unsigned, + .signed => .signed, + }; + const flags = LLVM.DebugInfo.Node.Flags{ + .visibility = .none, + .forward_declaration = false, + .apple_block = false, + .block_by_ref_struct = false, + .virtual = false, + .artificial = false, + .explicit = false, + .prototyped = false, + .objective_c_class_complete = false, + .object_pointer = false, + .vector = false, + .static_member = false, + .lvalue_reference = false, + .rvalue_reference = false, + .reserved = false, + .inheritance = .none, + .introduced_virtual = false, + .bit_field = false, + .no_return = false, + .type_pass_by_value = false, + .type_pass_by_reference = false, + .enum_class = false, + .thunk = false, + .non_trivial = false, + .big_endian = false, + .little_endian = false, + .all_calls_described = false, + }; + const integer_type = llvm.debug_info_builder.createBasicType(name.ptr, name.len, integer.bit_count, dwarf_encoding, flags) orelse unreachable; + break :b integer_type; + }, + .pointer => |pointer| b: { + const element_type = try llvm.getDebugType(unit, context, pointer.type); + const pointer_width = @bitSizeOf(usize); + const alignment = 3; + const pointer_type = llvm.debug_info_builder.createPointerType(element_type, pointer_width, alignment, name.ptr, name.len) orelse unreachable; + break :b pointer_type.toType(); + }, + .bool => b: { + const flags = LLVM.DebugInfo.Node.Flags{ + .visibility = .none, + .forward_declaration = false, + .apple_block = false, + .block_by_ref_struct = false, + .virtual = false, + .artificial = false, + .explicit = false, + .prototyped = false, + .objective_c_class_complete = false, + .object_pointer = false, + .vector = false, + .static_member = false, + .lvalue_reference = false, + .rvalue_reference = false, + .reserved = false, + .inheritance = .none, + .introduced_virtual = false, + .bit_field = false, + .no_return = false, + .type_pass_by_value = false, + .type_pass_by_reference = false, + .enum_class = false, + .thunk = false, + .non_trivial = false, + .big_endian = false, + .little_endian = false, + .all_calls_described = false, + }; + const boolean_type = llvm.debug_info_builder.createBasicType("bool", "bool".len, 1, .boolean, flags) orelse unreachable; + break :b boolean_type; + }, + .@"struct" => |struct_index| b: { + const sema_struct_type = unit.structs.get(struct_index); + const file = try llvm.getDebugInfoFile(unit, context, sema_struct_type.scope.scope.file); + const line = 0; + + const flags = LLVM.DebugInfo.Node.Flags{ + .visibility = .none, + .forward_declaration = false, + .apple_block = false, + .block_by_ref_struct = false, + .virtual = false, + .artificial = false, + .explicit = false, + .prototyped = false, + .objective_c_class_complete = false, + .object_pointer = false, + .vector = false, + .static_member = false, + .lvalue_reference = false, + .rvalue_reference = false, + .reserved = false, + .inheritance = .none, + .introduced_virtual = false, + .bit_field = false, + .no_return = false, + .type_pass_by_value = false, + .type_pass_by_reference = false, + .enum_class = false, + .thunk = false, + .non_trivial = false, + .big_endian = false, + .little_endian = false, + .all_calls_described = false, + }; + + var bit_size: u32 = 0; + for (sema_struct_type.fields.items) |struct_field_index| { + const struct_field = unit.struct_fields.get(struct_field_index); + const struct_field_type = unit.types.get(struct_field.type); + const struct_field_bit_size = struct_field_type.getBitSize(unit); + bit_size += struct_field_bit_size; + } + + const struct_type = llvm.createDebugStructType(.{ + .scope = null, + .name = name, + .file = file, + .line = line, + .bitsize = bit_size, + .alignment = 0, + .field_types = &.{}, + .forward_declaration = null, + }); + gop.value_ptr.* = struct_type.toType(); + var field_types = try ArrayList(*LLVM.DebugInfo.Type).initCapacity(context.allocator, sema_struct_type.fields.items.len); + bit_size = 0; + for (sema_struct_type.fields.items) |struct_field_index| { + const struct_field = unit.struct_fields.get(struct_field_index); + const struct_field_type = unit.types.get(struct_field.type); + const struct_field_bit_size = struct_field_type.getBitSize(unit); + const field_type = try llvm.getDebugType(unit, context, struct_field.type); + //TODO: fix + const alignment = struct_field_bit_size; + const member_type = llvm.debug_info_builder.createMemberType(null, "", "".len, file, 0, struct_field_bit_size, alignment, bit_size, flags, field_type).toType(); + field_types.appendAssumeCapacity(member_type); + bit_size += struct_field_bit_size; + } + + llvm.debug_info_builder.replaceCompositeTypes(struct_type, field_types.items.ptr, field_types.items.len); + break :b struct_type.toType(); + }, + .@"enum" => |enum_index| b: { + const enum_type = unit.enums.get(enum_index); + var enumerators = try ArrayList(*LLVM.DebugInfo.Type.Enumerator).initCapacity(context.allocator, enum_type.fields.items.len); + for (enum_type.fields.items) |enum_field_index| { + const enum_field = unit.enum_fields.get(enum_field_index); + const enum_field_name = unit.getIdentifier(enum_field.name); + + const is_unsigned = true; + const enumerator = llvm.debug_info_builder.createEnumerator(enum_field_name.ptr, enum_field_name.len, enum_field.value, is_unsigned) orelse unreachable; + enumerators.appendAssumeCapacity(enumerator); + } + + const type_declaration = unit.type_declarations.get(sema_type_index).?; + const file = try llvm.getDebugInfoFile(unit, context, type_declaration.declaration.scope.file); + const bit_size = unit.types.get(enum_type.backing_type).integer.bit_count; + const backing_type = try llvm.getDebugType(unit, context, enum_type.backing_type); + const alignment = 0; + const line = type_declaration.declaration.line + 1; + const scope = try llvm.getScope(unit, context, enum_type.scope.scope.parent.?); + const enumeration_type = llvm.debug_info_builder.createEnumerationType(scope, name.ptr, name.len, file, line, bit_size, alignment, enumerators.items.ptr, enumerators.items.len, backing_type) orelse unreachable; + break :b enumeration_type.toType(); + }, + .slice => |slice| b: { + const pointer_type = try llvm.getDebugType(unit, context, slice.child_pointer_type); + const len_type = try llvm.getDebugType(unit, context, .usize); + const scope = null; + const file = null; + const line = 1; + const flags = LLVM.DebugInfo.Node.Flags{ + .visibility = .none, + .forward_declaration = false, + .apple_block = false, + .block_by_ref_struct = false, + .virtual = false, + .artificial = false, + .explicit = false, + .prototyped = false, + .objective_c_class_complete = false, + .object_pointer = false, + .vector = false, + .static_member = false, + .lvalue_reference = false, + .rvalue_reference = false, + .reserved = false, + .inheritance = .none, + .introduced_virtual = false, + .bit_field = false, + .no_return = false, + .type_pass_by_value = false, + .type_pass_by_reference = false, + .enum_class = false, + .thunk = false, + .non_trivial = false, + .big_endian = false, + .little_endian = false, + .all_calls_described = false, + }; + + const types = [2]*LLVM.DebugInfo.Type{ pointer_type, len_type }; + const member_types = [2]*LLVM.DebugInfo.Type{ + llvm.debug_info_builder.createMemberType(null, "", "".len, null, 0, 64, 3, 0, flags, types[0]).toType(), + llvm.debug_info_builder.createMemberType(null, "", "".len, null, 0, 64, 3, 64, flags, types[1]).toType(), + }; + const struct_type = llvm.createDebugStructType(.{ + .scope = scope, + .name = name, + .file = file, + .line = line, + .bitsize = 2 * @bitSizeOf(usize), + .alignment = @alignOf(usize), + .field_types = &member_types, + .forward_declaration = null, + }); + break :b struct_type.toType(); + }, + .array => |array| b: { + // TODO: compute + const byte_size = 1; // array.count * unit.types.get(array.element_type).getSize(); + const bit_size = byte_size * 8; + const element_type = try llvm.getDebugType(unit, context, array.type); + const array_type = llvm.debug_info_builder.createArrayType(bit_size, 1, element_type, array.count) orelse unreachable; + break :b array_type.toType(); + }, + + .function => |function_prototype_index| b: { + const function_prototype = unit.function_prototypes.get(function_prototype_index); + var parameter_types = try ArrayList(*LLVM.DebugInfo.Type).initCapacity(context.allocator, function_prototype.argument_types.len); + for (function_prototype.argument_types) |argument_type_index| { + const argument_type = try llvm.getDebugType(unit, context, argument_type_index); + parameter_types.appendAssumeCapacity(argument_type); + } + const subroutine_type_flags = LLVM.DebugInfo.Node.Flags{ + .visibility = .none, + .forward_declaration = false, + .apple_block = false, + .block_by_ref_struct = false, + .virtual = false, + .artificial = false, + .explicit = false, + .prototyped = false, + .objective_c_class_complete = false, + .object_pointer = false, + .vector = false, + .static_member = false, + .lvalue_reference = false, + .rvalue_reference = false, + .reserved = false, + .inheritance = .none, + .introduced_virtual = false, + .bit_field = false, + .no_return = false, + .type_pass_by_value = false, + .type_pass_by_reference = false, + .enum_class = false, + .thunk = false, + .non_trivial = false, + .big_endian = false, + .little_endian = false, + .all_calls_described = false, + }; + const subroutine_type_calling_convention = LLVM.DebugInfo.CallingConvention.none; + const subroutine_type = llvm.debug_info_builder.createSubroutineType(parameter_types.items.ptr, parameter_types.items.len, subroutine_type_flags, subroutine_type_calling_convention) orelse unreachable; + break :b subroutine_type.toType(); + }, + else => |t| @panic(@tagName(t)), + }; + + try llvm.debug_type_map.put(context.allocator, sema_type_index, result); + + assert(@intFromPtr(result) != 0xaaaa_aaaa_aaaa_aaaa); + return result; + } } else { - const name = try llvm.renderTypeName(unit, context, sema_type_index); - const sema_type = unit.types.get(sema_type_index); - const result: *LLVM.DebugInfo.Type = switch (sema_type.*) { - .integer => |integer| b: { - const dwarf_encoding: LLVM.DebugInfo.AttributeType = switch (integer.signedness) { - .unsigned => .unsigned, - .signed => .signed, - }; - const flags = LLVM.DebugInfo.Node.Flags{ - .visibility = .none, - .forward_declaration = false, - .apple_block = false, - .block_by_ref_struct = false, - .virtual = false, - .artificial = false, - .explicit = false, - .prototyped = false, - .objective_c_class_complete = false, - .object_pointer = false, - .vector = false, - .static_member = false, - .lvalue_reference = false, - .rvalue_reference = false, - .reserved = false, - .inheritance = .none, - .introduced_virtual = false, - .bit_field = false, - .no_return = false, - .type_pass_by_value = false, - .type_pass_by_reference = false, - .enum_class = false, - .thunk = false, - .non_trivial = false, - .big_endian = false, - .little_endian = false, - .all_calls_described = false, - }; - const integer_type = llvm.debug_info_builder.createBasicType(name.ptr, name.len, integer.bit_count, dwarf_encoding, flags) orelse unreachable; - break :b integer_type; - }, - .pointer => |pointer| b: { - const element_type = try llvm.getDebugType(unit, context, pointer.type); - const pointer_width = @bitSizeOf(usize); - const alignment = 0; - const pointer_type = llvm.debug_info_builder.createPointerType(element_type, pointer_width, alignment, name.ptr, name.len) orelse unreachable; - break :b pointer_type.toType(); - }, - .bool => { - const flags = LLVM.DebugInfo.Node.Flags{ - .visibility = .none, - .forward_declaration = false, - .apple_block = false, - .block_by_ref_struct = false, - .virtual = false, - .artificial = false, - .explicit = false, - .prototyped = false, - .objective_c_class_complete = false, - .object_pointer = false, - .vector = false, - .static_member = false, - .lvalue_reference = false, - .rvalue_reference = false, - .reserved = false, - .inheritance = .none, - .introduced_virtual = false, - .bit_field = false, - .no_return = false, - .type_pass_by_value = false, - .type_pass_by_reference = false, - .enum_class = false, - .thunk = false, - .non_trivial = false, - .big_endian = false, - .little_endian = false, - .all_calls_described = false, - }; - const boolean_type = llvm.debug_info_builder.createBasicType("bool", "bool".len, 1, .boolean, flags) orelse unreachable; - return boolean_type; - }, - .@"struct" => |struct_index| b: { - const sema_struct_type = unit.structs.get(struct_index); + if (llvm.debug_type_map.get(sema_type_index)) |result| { + return result; + } else { + const name = try llvm.renderTypeName(unit, context, sema_type_index); + const sema_type = unit.types.get(sema_type_index); + const result = switch (sema_type.*) { + .integer => |integer| b: { + const dwarf_encoding: LLVM.DebugInfo.AttributeType = switch (integer.signedness) { + .unsigned => .unsigned, + .signed => .signed, + }; + const flags = LLVM.DebugInfo.Node.Flags{ + .visibility = .none, + .forward_declaration = false, + .apple_block = false, + .block_by_ref_struct = false, + .virtual = false, + .artificial = false, + .explicit = false, + .prototyped = false, + .objective_c_class_complete = false, + .object_pointer = false, + .vector = false, + .static_member = false, + .lvalue_reference = false, + .rvalue_reference = false, + .reserved = false, + .inheritance = .none, + .introduced_virtual = false, + .bit_field = false, + .no_return = false, + .type_pass_by_value = false, + .type_pass_by_reference = false, + .enum_class = false, + .thunk = false, + .non_trivial = false, + .big_endian = false, + .little_endian = false, + .all_calls_described = false, + }; + const integer_type = llvm.debug_info_builder.createBasicType(name.ptr, name.len, integer.bit_count, dwarf_encoding, flags) orelse unreachable; + break :b integer_type; + }, + .pointer => |pointer| b: { + const element_type = try llvm.getDebugType(unit, context, pointer.type); + const pointer_width = @bitSizeOf(usize); + const alignment = 3; + const pointer_type = llvm.debug_info_builder.createPointerType(element_type, pointer_width, alignment, name.ptr, name.len) orelse unreachable; + break :b pointer_type.toType(); + }, + .bool => b: { + const flags = LLVM.DebugInfo.Node.Flags{ + .visibility = .none, + .forward_declaration = false, + .apple_block = false, + .block_by_ref_struct = false, + .virtual = false, + .artificial = false, + .explicit = false, + .prototyped = false, + .objective_c_class_complete = false, + .object_pointer = false, + .vector = false, + .static_member = false, + .lvalue_reference = false, + .rvalue_reference = false, + .reserved = false, + .inheritance = .none, + .introduced_virtual = false, + .bit_field = false, + .no_return = false, + .type_pass_by_value = false, + .type_pass_by_reference = false, + .enum_class = false, + .thunk = false, + .non_trivial = false, + .big_endian = false, + .little_endian = false, + .all_calls_described = false, + }; + const boolean_type = llvm.debug_info_builder.createBasicType("bool", "bool".len, 1, .boolean, flags) orelse unreachable; + break :b boolean_type; + }, + .@"struct" => |struct_index| b: { + const sema_struct_type = unit.structs.get(struct_index); + const file = try llvm.getDebugInfoFile(unit, context, sema_struct_type.scope.scope.file); + const line = 0; - var field_types = try ArrayList(*LLVM.DebugInfo.Type).initCapacity(context.allocator, sema_struct_type.fields.items.len); - for (sema_struct_type.fields.items) |struct_field_index| { - const struct_field = unit.struct_fields.get(struct_field_index); - const field_type = try llvm.getDebugType(unit, context, struct_field.type); - field_types.appendAssumeCapacity(field_type); - } + const flags = LLVM.DebugInfo.Node.Flags{ + .visibility = .none, + .forward_declaration = false, + .apple_block = false, + .block_by_ref_struct = false, + .virtual = false, + .artificial = false, + .explicit = false, + .prototyped = false, + .objective_c_class_complete = false, + .object_pointer = false, + .vector = false, + .static_member = false, + .lvalue_reference = false, + .rvalue_reference = false, + .reserved = false, + .inheritance = .none, + .introduced_virtual = false, + .bit_field = false, + .no_return = false, + .type_pass_by_value = false, + .type_pass_by_reference = false, + .enum_class = false, + .thunk = false, + .non_trivial = false, + .big_endian = false, + .little_endian = false, + .all_calls_described = false, + }; - const fields = &.{}; - // const sema_declaration_index = llvm.sema.map.types.get(sema_type_index) orelse unreachable; - // const sema_declaration = llvm.sema.values.declarations.get(sema_declaration_index); - const file = try llvm.getDebugInfoFile(unit, context, sema_struct_type.scope.scope.file); - // const line = sema_declaration.line + 1; - const line = 0; - const struct_type = llvm.createDebugStructType(.{ .scope = null, .name = name, .file = file, .line = line, .bitsize = 0, .alignment = 0, .field_types = fields }); - break :b struct_type.toType(); - }, - .@"enum" => |enum_index| b: { - const enum_type = unit.enums.get(enum_index); - var enumerators = try ArrayList(*LLVM.DebugInfo.Type.Enumerator).initCapacity(context.allocator, enum_type.fields.items.len); - for (enum_type.fields.items) |enum_field_index| { - const enum_field = unit.enum_fields.get(enum_field_index); - const enum_field_name = unit.getIdentifier(enum_field.name); + var bit_size: u32 = 0; + for (sema_struct_type.fields.items) |struct_field_index| { + const struct_field = unit.struct_fields.get(struct_field_index); + const struct_field_type = unit.types.get(struct_field.type); + const struct_field_bit_size = struct_field_type.getBitSize(unit); + bit_size += struct_field_bit_size; + } - const is_unsigned = true; - const enumerator = llvm.debug_info_builder.createEnumerator(enum_field_name.ptr, enum_field_name.len, enum_field.value, is_unsigned) orelse unreachable; - enumerators.appendAssumeCapacity(enumerator); - } + const struct_type = llvm.createDebugStructType(.{ + .scope = null, + .name = name, + .file = file, + .line = line, + .bitsize = bit_size, + .alignment = 0, + .field_types = &.{}, + .forward_declaration = null, + }); + try llvm.debug_type_map.putNoClobber(context.allocator, sema_type_index, struct_type.toType()); + var field_types = try ArrayList(*LLVM.DebugInfo.Type).initCapacity(context.allocator, sema_struct_type.fields.items.len); + bit_size = 0; + for (sema_struct_type.fields.items) |struct_field_index| { + const struct_field = unit.struct_fields.get(struct_field_index); + const struct_field_type = unit.types.get(struct_field.type); + const struct_field_bit_size = struct_field_type.getBitSize(unit); + const field_type = try llvm.getDebugType(unit, context, struct_field.type); + //TODO: fix + const alignment = struct_field_bit_size; + const member_type = llvm.debug_info_builder.createMemberType(null, "", "".len, file, 0, struct_field_bit_size, alignment, bit_size, flags, field_type).toType(); + field_types.appendAssumeCapacity(member_type); + bit_size += struct_field_bit_size; + } - const type_declaration = unit.type_declarations.get(sema_type_index).?; - const file = try llvm.getDebugInfoFile(unit, context, type_declaration.declaration.scope.file); - const bit_size = unit.types.get(enum_type.backing_type).integer.bit_count; - const backing_type = try llvm.getDebugType(unit, context, enum_type.backing_type); - const alignment = 0; - const line = type_declaration.declaration.line + 1; - const enumeration_type = llvm.debug_info_builder.createEnumerationType(llvm.scope, name.ptr, name.len, file, line, bit_size, alignment, enumerators.items.ptr, enumerators.items.len, backing_type) orelse unreachable; - break :b enumeration_type.toType(); - }, - .slice => |slice| b: { - const pointer_type = try llvm.getDebugType(unit, context, slice.child_pointer_type); - const len_type = try llvm.getDebugType(unit, context, .usize); - const scope = null; - const file = null; - const line = 1; - // const forward_declared_type = llvm.debug_info_builder.createReplaceableCompositeType(tag_count, name.ptr, name.len, scope, file, line) orelse unreachable; - // tag_count += 1; + llvm.debug_info_builder.replaceCompositeTypes(struct_type, field_types.items.ptr, field_types.items.len); + break :b struct_type.toType(); + }, + .@"enum" => |enum_index| b: { + const enum_type = unit.enums.get(enum_index); + var enumerators = try ArrayList(*LLVM.DebugInfo.Type.Enumerator).initCapacity(context.allocator, enum_type.fields.items.len); + for (enum_type.fields.items) |enum_field_index| { + const enum_field = unit.enum_fields.get(enum_field_index); + const enum_field_name = unit.getIdentifier(enum_field.name); - const field_types = [2]*LLVM.DebugInfo.Type{ pointer_type, len_type }; - const struct_type = llvm.createDebugStructType(.{ - .scope = scope, - .name = name, - .file = file, - .line = line, - .bitsize = 2 * @bitSizeOf(usize), - .alignment = @alignOf(usize), - .field_types = &field_types, - }); - break :b struct_type.toType(); - }, - .array => |array| b: { - // TODO: compute - const byte_size = 1; // array.count * unit.types.get(array.element_type).getSize(); - const bit_size = byte_size * 8; - const element_type = try llvm.getDebugType(unit, context, array.type); - const array_type = llvm.debug_info_builder.createArrayType(bit_size, 1, element_type, array.count) orelse unreachable; - break :b array_type.toType(); - }, - else => |t| @panic(@tagName(t)), - }; + const is_unsigned = true; + const enumerator = llvm.debug_info_builder.createEnumerator(enum_field_name.ptr, enum_field_name.len, enum_field.value, is_unsigned) orelse unreachable; + enumerators.appendAssumeCapacity(enumerator); + } - try llvm.debug_type_map.putNoClobber(context.allocator, sema_type_index, result); - return result; + const type_declaration = unit.type_declarations.get(sema_type_index).?; + const file = try llvm.getDebugInfoFile(unit, context, type_declaration.declaration.scope.file); + const bit_size = unit.types.get(enum_type.backing_type).integer.bit_count; + const backing_type = try llvm.getDebugType(unit, context, enum_type.backing_type); + const alignment = 0; + const line = type_declaration.declaration.line + 1; + const scope = try llvm.getScope(unit, context, enum_type.scope.scope.parent.?); + const enumeration_type = llvm.debug_info_builder.createEnumerationType(scope, name.ptr, name.len, file, line, bit_size, alignment, enumerators.items.ptr, enumerators.items.len, backing_type) orelse unreachable; + break :b enumeration_type.toType(); + }, + .slice => |slice| b: { + const pointer_type = try llvm.getDebugType(unit, context, slice.child_pointer_type); + const len_type = try llvm.getDebugType(unit, context, .usize); + const scope = null; + const file = null; + const line = 1; + const flags = LLVM.DebugInfo.Node.Flags{ + .visibility = .none, + .forward_declaration = false, + .apple_block = false, + .block_by_ref_struct = false, + .virtual = false, + .artificial = false, + .explicit = false, + .prototyped = false, + .objective_c_class_complete = false, + .object_pointer = false, + .vector = false, + .static_member = false, + .lvalue_reference = false, + .rvalue_reference = false, + .reserved = false, + .inheritance = .none, + .introduced_virtual = false, + .bit_field = false, + .no_return = false, + .type_pass_by_value = false, + .type_pass_by_reference = false, + .enum_class = false, + .thunk = false, + .non_trivial = false, + .big_endian = false, + .little_endian = false, + .all_calls_described = false, + }; + + const types = [2]*LLVM.DebugInfo.Type{ pointer_type, len_type }; + const member_types = [2]*LLVM.DebugInfo.Type{ + llvm.debug_info_builder.createMemberType(null, "", "".len, null, 0, 64, 3, 0, flags, types[0]).toType(), + llvm.debug_info_builder.createMemberType(null, "", "".len, null, 0, 64, 3, 64, flags, types[1]).toType(), + }; + const struct_type = llvm.createDebugStructType(.{ + .scope = scope, + .name = name, + .file = file, + .line = line, + .bitsize = 2 * @bitSizeOf(usize), + .alignment = @alignOf(usize), + .field_types = &member_types, + .forward_declaration = null, + }); + break :b struct_type.toType(); + }, + .array => |array| b: { + // TODO: compute + const byte_size = 1; // array.count * unit.types.get(array.element_type).getSize(); + const bit_size = byte_size * 8; + const element_type = try llvm.getDebugType(unit, context, array.type); + const array_type = llvm.debug_info_builder.createArrayType(bit_size, 1, element_type, array.count) orelse unreachable; + break :b array_type.toType(); + }, + + .function => |function_prototype_index| b: { + const function_prototype = unit.function_prototypes.get(function_prototype_index); + var parameter_types = try ArrayList(*LLVM.DebugInfo.Type).initCapacity(context.allocator, function_prototype.argument_types.len); + for (function_prototype.argument_types) |argument_type_index| { + const argument_type = try llvm.getDebugType(unit, context, argument_type_index); + parameter_types.appendAssumeCapacity(argument_type); + } + const subroutine_type_flags = LLVM.DebugInfo.Node.Flags{ + .visibility = .none, + .forward_declaration = false, + .apple_block = false, + .block_by_ref_struct = false, + .virtual = false, + .artificial = false, + .explicit = false, + .prototyped = false, + .objective_c_class_complete = false, + .object_pointer = false, + .vector = false, + .static_member = false, + .lvalue_reference = false, + .rvalue_reference = false, + .reserved = false, + .inheritance = .none, + .introduced_virtual = false, + .bit_field = false, + .no_return = false, + .type_pass_by_value = false, + .type_pass_by_reference = false, + .enum_class = false, + .thunk = false, + .non_trivial = false, + .big_endian = false, + .little_endian = false, + .all_calls_described = false, + }; + const subroutine_type_calling_convention = LLVM.DebugInfo.CallingConvention.none; + const subroutine_type = llvm.debug_info_builder.createSubroutineType(parameter_types.items.ptr, parameter_types.items.len, subroutine_type_flags, subroutine_type_calling_convention) orelse unreachable; + break :b subroutine_type.toType(); + }, + else => |t| @panic(@tagName(t)), + }; + + try llvm.debug_type_map.put(context.allocator, sema_type_index, result); + + assert(@intFromPtr(result) != 0xaaaa_aaaa_aaaa_aaaa); + return result; + } } } + fn createGEP(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, instruction_index: Compilation.Instruction.Index) !*LLVM.Value { + const instruction = unit.instructions.get(instruction_index); + const gep = instruction.get_element_pointer; + const pointer = llvm.llvm_instruction_map.get(gep.pointer).?; + const index = try llvm.emitRightValue(unit, context, gep.index); + const struct_index = llvm.context.getConstantInt(@bitSizeOf(u32), 0, false) orelse unreachable; + const index_buffer = [2]*LLVM.Value{ struct_index.toValue(), index }; + const indices = index_buffer[@intFromBool(!gep.is_struct)..]; + if (gep.is_struct) assert(indices.len == 2) else assert(indices.len == 1); + const base_type = try llvm.getType(unit, context, gep.base_type); + const in_bounds = true; + if (gep.is_struct and gep.index.type != .u32) unreachable; + const get_element_pointer = llvm.builder.createGEP(base_type, pointer, indices.ptr, indices.len, "gep", "gep".len, in_bounds) orelse unreachable; + try llvm.llvm_instruction_map.putNoClobber(context.allocator, instruction_index, get_element_pointer); + return get_element_pointer; + } + fn emitLeftValue(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, v: Compilation.V) !*LLVM.Value { switch (v.value) { .runtime => |instruction_index| { @@ -2466,127 +2005,137 @@ pub const LLVM = struct { } else { const instruction = unit.instructions.get(instruction_index); switch (instruction.*) { - .global => |global_declaration| { - const global_variable = llvm.global_variable_map.get(global_declaration).?; - return global_variable.toValue(); - }, - .get_element_pointer => |gep| { - const pointer = llvm.llvm_instruction_map.get(gep.pointer).?; - const index = try llvm.emitRightValue(unit, context, gep.index); - const indices = [1]*LLVM.Value{ index }; - const base_type = try llvm.getType(unit, context, gep.base_type); - const in_bounds = true; - const get_element_pointer = llvm.builder.createGEP(base_type, pointer, &indices, indices.len, "gep", "gep".len, in_bounds) orelse unreachable; - try llvm.llvm_value_map.putNoClobber(context.allocator, v, get_element_pointer); - try llvm.llvm_instruction_map.putNoClobber(context.allocator, instruction_index, get_element_pointer); - return get_element_pointer; + .get_element_pointer => { + return try llvm.createGEP(unit, context, instruction_index); }, else => |t| @panic(@tagName(t)), } } }, - .function_reference => |function_declaration| { - const function = llvm.function_definition_map.get(function_declaration).?; - return function.toValue(); + .@"comptime" => |ct| switch (ct) { + .global => |global| switch (global.initial_value) { + .function_definition => return llvm.function_definition_map.get(global).?.toValue(), + else => return llvm.global_variable_map.get(global).?.toValue(), + }, + else => |t| @panic(@tagName(t)), }, else => |t| @panic(@tagName(t)), } } - - fn emitRightValue(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, v: Compilation.V) !*LLVM.Value { - switch (v.value) { - .@"comptime" => |ct| { - switch (ct) { - .constant_int => |integer| { - const integer_type = unit.types.get(v.type); - switch (integer_type.*) { - .integer => |integer_t| { - const signed = switch (integer_t.signedness) { - .signed => true, - .unsigned => false, - }; - const constant_int = llvm.context.getConstantInt(integer_t.bit_count, integer.value, signed) orelse unreachable; - return constant_int.toValue(); - }, - else => |t| @panic(@tagName(t)), - } - }, - .comptime_int => |integer| { - const integer_type = unit.types.get(v.type); - switch (integer_type.*) { - .integer => |integer_t| { - const signed = switch (integer_t.signedness) { - .signed => true, - .unsigned => false, - }; - const constant_int = llvm.context.getConstantInt(integer_t.bit_count, integer.value, signed) orelse unreachable; - return constant_int.toValue(); - }, - else => |t| @panic(@tagName(t)), - } - }, - .enum_value => |enum_field_index| { - const enum_field = unit.enum_fields.get(enum_field_index); - const enum_type = unit.enums.get(unit.types.get( enum_field.parent).@"enum"); - const backing_integer_type = unit.types.get(enum_type.backing_type).integer; - const signed = switch (backing_integer_type.signedness) { + + fn emitComptimeRightValue(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, ct: Compilation.V.Comptime, type_index: Compilation.Type.Index) !*LLVM.Value.Constant { + switch (ct) { + .constant_int => |integer| { + const integer_type = unit.types.get(type_index); + switch (integer_type.*) { + .integer => |integer_t| { + const signed = switch (integer_t.signedness) { .signed => true, .unsigned => false, }; - const constant_int = llvm.context.getConstantInt(backing_integer_type.bit_count, enum_field.value, signed) orelse unreachable; - return constant_int.toValue(); - }, - .constant_struct => |constant_struct_index| { - const constant_struct = unit.constant_structs.get(constant_struct_index); - const field_values = try ArrayList(*LLVM.Value.Constant).initCapacity(context.allocator, constant_struct.fields.len); - for (constant_struct.fields) |field_value| { - _ = field_value; // autofix - unreachable; - // const value = try llvm.emitRightValue(unit, context, field_value); - // const constant = value.toConstant() orelse unreachable; - // field_values.appendAssumeCapacity(constant); - } - - const llvm_type = try llvm.getType(unit, context, constant_struct.type); - const struct_type = llvm_type.toStruct() orelse unreachable; - const const_struct = struct_type.getConstant(field_values.items.ptr, field_values.items.len) orelse unreachable; - return const_struct.toValue(); - }, - .undefined => { - const undefined_type = try llvm.getType(unit, context, v.type); - const poison = undefined_type.getPoison() orelse unreachable; - return poison.toValue(); - }, - .bool => |boolean| { - const bit_count = 1; - const signed = false; - const constant_bool = llvm.context.getConstantInt(bit_count, @intFromBool(boolean), signed) orelse unreachable; - return constant_bool.toValue(); - }, - .constant_slice => |constant_slice_index| { - const constant_slice = try llvm.getConstantSlice(unit, context, constant_slice_index); - return constant_slice.toValue(); - }, - .constant_array => |constant_array_index| { - const constant_array = try llvm.getConstantArray(unit, context, constant_array_index); - return constant_array.toValue(); + const constant_int = llvm.context.getConstantInt(integer_t.bit_count, integer.value, signed) orelse unreachable; + return constant_int.toConstant(); }, else => |t| @panic(@tagName(t)), } }, + .comptime_int => |integer| { + const integer_type = unit.types.get(type_index); + switch (integer_type.*) { + .integer => |integer_t| { + const signed = switch (integer_t.signedness) { + .signed => true, + .unsigned => false, + }; + const constant_int = llvm.context.getConstantInt(integer_t.bit_count, integer.value, signed) orelse unreachable; + return constant_int.toConstant(); + }, + else => |t| @panic(@tagName(t)), + } + }, + .enum_value => |enum_field_index| { + const enum_field = unit.enum_fields.get(enum_field_index); + const enum_type = unit.enums.get(unit.types.get(enum_field.parent).@"enum"); + const backing_integer_type = unit.types.get(enum_type.backing_type).integer; + const signed = switch (backing_integer_type.signedness) { + .signed => true, + .unsigned => false, + }; + const constant_int = llvm.context.getConstantInt(backing_integer_type.bit_count, enum_field.value, signed) orelse unreachable; + return constant_int.toConstant(); + }, + .constant_backed_struct => |value| { + const struct_index = unit.types.get(type_index).@"struct"; + const struct_type = unit.structs.get(struct_index); + const backing_integer_type = unit.types.get(struct_type.backing_type).integer; + const signed = switch (backing_integer_type.signedness) { + .signed => true, + .unsigned => false, + }; + const constant_int = llvm.context.getConstantInt(backing_integer_type.bit_count, value, signed) orelse unreachable; + return constant_int.toConstant(); + }, + .constant_struct => |constant_struct_index| { + const constant_struct = unit.constant_structs.get(constant_struct_index); + var field_values = try ArrayList(*LLVM.Value.Constant).initCapacity(context.allocator, constant_struct.fields.len); + const sema_struct_type = unit.structs.get(unit.types.get(constant_struct.type).@"struct"); + for (constant_struct.fields, sema_struct_type.fields.items) |field_value, field_index| { + const field = unit.struct_fields.get(field_index); + const constant = try llvm.emitComptimeRightValue(unit, context, field_value, field.type); + field_values.appendAssumeCapacity(constant); + } + + const llvm_type = try llvm.getType(unit, context, constant_struct.type); + const struct_type = llvm_type.toStruct() orelse unreachable; + const const_struct = struct_type.getConstant(field_values.items.ptr, field_values.items.len) orelse unreachable; + return const_struct; + }, + .undefined => { + const undefined_type = try llvm.getType(unit, context, type_index); + const poison = undefined_type.getPoison() orelse unreachable; + return poison.toConstant(); + }, + .bool => |boolean| { + const bit_count = 1; + const signed = false; + const constant_bool = llvm.context.getConstantInt(bit_count, @intFromBool(boolean), signed) orelse unreachable; + return constant_bool.toConstant(); + }, + .constant_slice => |constant_slice_index| { + const constant_slice = try llvm.getConstantSlice(unit, context, constant_slice_index); + return constant_slice; + }, + .constant_array => |constant_array_index| { + const constant_array = try llvm.getConstantArray(unit, context, constant_array_index); + return constant_array; + }, + .global => |global| { + const constant = switch (global.initial_value) { + .function_definition => llvm.function_definition_map.get(global).?.toConstant(), + else => llvm.global_variable_map.get(global).?.toConstant(), + }; + return constant; + }, + .null_pointer => { + const value_type = try llvm.getType(unit, context, type_index); + const pointer_type = value_type.toPointer() orelse unreachable; + const constant_null_pointer = pointer_type.getNull(); + return constant_null_pointer.toConstant(); + }, + else => |t| @panic(@tagName(t)), + } + } + + fn emitRightValue(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, v: Compilation.V) !*LLVM.Value { + switch (v.value) { + .@"comptime" => |ct| { + const constant_value = try llvm.emitComptimeRightValue(unit, context, ct, v.type); + return constant_value.toValue(); + }, .runtime => |instruction_index| { if (llvm.llvm_instruction_map.get(instruction_index)) |instruction| { return instruction; } else { - const instruction = unit.instructions.get(instruction_index); - switch (instruction.*) { - .global => |global| { - const global_variable = llvm.global_variable_map.get(global).?; - return global_variable.toValue(); - }, - else => |t| @panic(@tagName(t)), - } - unreachable; } }, @@ -2594,9 +2143,12 @@ pub const LLVM = struct { } } - fn getScope(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, sema_scope: *Compilation.Debug.Scope) !*LLVM.DebugInfo.Scope { + fn getScope(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, sema_scope: *Compilation.Debug.Scope) anyerror!*LLVM.DebugInfo.Scope { switch (sema_scope.kind) { - .function, .block, .compilation_unit, => { + .function, + .block, + .compilation_unit, + => { return llvm.scope_map.get(sema_scope).?; }, .file => { @@ -2628,7 +2180,7 @@ pub const LLVM = struct { return basic_block_node; } - fn getConstantSlice(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, constant_slice_index: Compilation.V.Comptime.ConstantSlice.Index) !*LLVM.Value.Constant{ + fn getConstantSlice(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, constant_slice_index: Compilation.V.Comptime.ConstantSlice.Index) !*LLVM.Value.Constant { const const_slice = unit.constant_slices.get(constant_slice_index); const const_slice_type = try llvm.getType(unit, context, const_slice.type); const slice_struct_type = const_slice_type.toStruct() orelse unreachable; @@ -2643,14 +2195,14 @@ pub const LLVM = struct { const constant_slice = slice_struct_type.getConstant(&slice_fields, slice_fields.len) orelse unreachable; return constant_slice; } - - fn getConstantArray(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, constant_array_index: Compilation.V.Comptime.ConstantArray.Index) !*LLVM.Value.Constant{ + + fn getConstantArray(llvm: *LLVM, unit: *Compilation.Unit, context: *const Compilation.Context, constant_array_index: Compilation.V.Comptime.ConstantArray.Index) !*LLVM.Value.Constant { const constant_array = unit.constant_arrays.get(constant_array_index); const sema_array_type = unit.types.get(constant_array.type).array; const constant_type = try llvm.getType(unit, context, constant_array.type); const array_type = constant_type.toArray() orelse unreachable; var list = try ArrayList(*LLVM.Value.Constant).initCapacity(context.allocator, constant_array.values.len); - for (constant_array.values) |sema_value| { + for (constant_array.values) |sema_value| { const value = switch (sema_value) { .constant_int => |const_int| b: { const integer_type = unit.types.get(sema_array_type.type).integer; @@ -2670,11 +2222,32 @@ pub const LLVM = struct { const result = array_type.getConstant(list.items.ptr, list.items.len) orelse unreachable; return result; } + + fn callIntrinsic(llvm: *LLVM, intrinsic_name: []const u8, intrinsic_parameter_types: []const *LLVM.Type, intrinsic_arguments: []const *LLVM.Value) !*LLVM.Value { + const intrinsic_id = LLVM.lookupIntrinsic(intrinsic_name.ptr, intrinsic_name.len); + assert(intrinsic_id != .none); + + const intrinsic_function = llvm.module.getIntrinsicDeclaration(intrinsic_id, intrinsic_parameter_types.ptr, intrinsic_parameter_types.len) orelse return LLVM.Value.Error.intrinsic; + const intrinsic_type = intrinsic_function.getType(); + const void_name: []const u8 = ""; + const name = switch (intrinsic_type.getReturnType().isVoid()) { + true => void_name, + false => intrinsic_name, + }; + + const call = llvm.builder.createCall(intrinsic_type, intrinsic_function.toValue(), intrinsic_arguments.ptr, intrinsic_arguments.len, name.ptr, name.len, null) orelse return LLVM.Value.Instruction.Error.call; + return call.toValue(); + } }; -const BasicBlockList = std.DoublyLinkedList(Compilation.BasicBlock.Index); +fn getCallingConvention(calling_convention: Compilation.Function.CallingConvention) LLVM.Value.Constant.Function.CallingConvention { + return switch (calling_convention) { + .auto => .Fast, + .c => .C, + }; +} -var tag_count: c_uint = 0; +const BasicBlockList = std.DoublyLinkedList(Compilation.BasicBlock.Index); const Error = error{ context, @@ -2693,7 +2266,6 @@ pub const Format = enum(c_uint) { coff = 2, }; - pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !void { const llvm_context = LLVM.Context.create() orelse return Error.context; const module = LLVM.Module.create(@ptrCast(unit.descriptor.name.ptr), unit.descriptor.name.len, llvm_context) orelse return Error.module; @@ -2730,6 +2302,39 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo try llvm.scope_map.putNoClobber(context.allocator, &unit.scope.scope, llvm.scope); } + for (unit.code_to_emit.values()) |function_declaration| { + const function_definition_index = function_declaration.getFunctionDefinitionIndex(); + const function_definition = unit.function_definitions.get(function_definition_index); + const function_type = try llvm.getType(unit, context, function_definition.type); + const is_export = function_declaration.attributes.contains(.@"export"); + const linkage: LLVM.Linkage = switch (is_export) { + true => .@"extern", + false => .internal, + }; + // TODO: Check name collision + const mangle_name = !is_export; + _ = mangle_name; // autofix + const name = unit.getIdentifier(function_declaration.declaration.name); + const function = llvm.module.createFunction(function_type.toFunction() orelse unreachable, linkage, address_space, name.ptr, name.len) orelse return Error.function; + + const function_prototype = unit.function_prototypes.get(unit.types.get(function_definition.type).function); + switch (unit.types.get(function_prototype.return_type).*) { + .noreturn => { + function.addAttributeKey(.NoReturn); + }, + else => {}, + } + + if (function_prototype.attributes.naked) { + function.addAttributeKey(.Naked); + } + + const calling_convention = getCallingConvention(function_prototype.calling_convention); + function.setCallingConvention(calling_convention); + + try llvm.function_definition_map.putNoClobber(context.allocator, function_declaration, function); + } + // First, cache all the global variables for (unit.data_to_emit.items) |global_declaration| { const name = unit.getIdentifier(global_declaration.declaration.name); @@ -2776,69 +2381,11 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo } for (llvm.global_variable_map.keys(), llvm.global_variable_map.values()) |global_declaration, global_variable| { - const constant_initializer = switch (global_declaration.initial_value) { - .comptime_int => |ct_int| blk: { - const integer_type = unit.types.get( global_declaration.declaration.type).integer; - const signed = switch (integer_type.signedness) { - .signed => true, - .unsigned => false, - }; - assert(ct_int.signedness == .unsigned); - assert(!signed); - const constant_int = llvm.context.getConstantInt(integer_type.bit_count, ct_int.value, signed) orelse unreachable; - break :blk constant_int.toConstant(); - }, - .undefined => b: { - const global_type = try llvm.getType(unit, context, global_declaration.declaration.type); - const poison = global_type.getPoison() orelse unreachable; - break :b poison.toConstant(); - }, - // String literal global variables are already initialized - .string_literal => continue, - .constant_array => |constant_array_index| try llvm.getConstantArray(unit, context, constant_array_index), - else =>|t| @panic(@tagName(t)), - }; - + if (global_declaration.initial_value == .string_literal) continue; + const constant_initializer = try llvm.emitComptimeRightValue(unit, context, global_declaration.initial_value, global_declaration.declaration.type); global_variable.setInitializer(constant_initializer); } - for (unit.code_to_emit.items) |function_declaration| { - const function_definition_index = function_declaration.getFunctionDefinitionIndex(); - const function_definition = unit.function_definitions.get(function_definition_index); - const function_type = try llvm.getType(unit, context, function_definition.type); - const is_export = function_declaration.attributes.contains(.@"export"); - const linkage: LLVM.Linkage = switch (is_export) { - true => .@"extern", - false => .@"internal", - }; - // TODO: Check name collision - const mangle_name = !is_export; - _ = mangle_name; // autofix - const name = unit.getIdentifier(function_declaration.declaration.name); - const function = llvm.module.createFunction(function_type.toFunction() orelse unreachable, linkage, address_space, name.ptr, name.len) orelse return Error.function; - - const function_prototype = unit.function_prototypes.get( unit.types.get( function_definition.type).function); - switch (unit.types.get(function_prototype.return_type).*) { - .noreturn => { - function.addAttributeKey(.NoReturn); - }, - else => {}, - } - - if (function_prototype.attributes.naked) { - function.addAttributeKey(.Naked); - } - - switch (function_prototype.calling_convention) { - .c => {}, - .auto => { - function.setCallingConvention(.Fast); - }, - } - - try llvm.function_definition_map.putNoClobber(context.allocator, function_declaration, function); - } - for (llvm.function_definition_map.keys(), llvm.function_definition_map.values()) |function_declaration, function| { const function_definition_index = function_declaration.getFunctionDefinitionIndex(); const function_definition = unit.function_definitions.get(function_definition_index); @@ -2847,6 +2394,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo llvm.inside_branch = false; const function_prototype = unit.function_prototypes.get(unit.types.get(function_definition.type).function); const function_name = unit.getIdentifier(function_declaration.declaration.name); + if (unit.descriptor.generate_debug_information) { const debug_file = try llvm.getDebugInfoFile(unit, context, function_declaration.declaration.scope.file); var parameter_types = try ArrayList(*LLVM.DebugInfo.Type).initCapacity(context.allocator, function_prototype.argument_types.len); @@ -2936,8 +2484,6 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo llvm.scope = lexical_block.toScope(); }, .pop_scope => |pop_scope| { - // const old = try llvm.getScope(unit, context, pop_scope.old); - // assert(llvm.scope == old); const new = try llvm.getScope(unit, context, pop_scope.new); if (pop_scope.new.kind == .function) { assert(new.toSubprogram() orelse unreachable == llvm.subprogram); @@ -2974,7 +2520,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo .mov => "movq", .@"and" => "andq", .call => "callq", - }); + }); try assembly_statements.append(context.allocator, ' '); if (instruction.operands.len > 0) { @@ -3058,7 +2604,7 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo const store_instruction = llvm.builder.createStore(right, left, is_volatile) orelse return LLVM.Value.Instruction.Error.store; _ = store_instruction; // autofix }, - .cast => |cast|{ + .cast => |cast| { const value = try llvm.emitRightValue(unit, context, cast.value); const dest_type = try llvm.getType(unit, context, cast.type); switch (cast.id) { @@ -3068,7 +2614,16 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo const cast_instruction = llvm.builder.createCast(cast_type, value, value.getType(), cast_name.ptr, cast_name.len) orelse return LLVM.Value.Instruction.Error.cast; try llvm.llvm_instruction_map.putNoClobber(context.allocator, instruction_index, cast_instruction); }, - .pointer_var_to_const, .slice_var_to_const, .enum_to_int => { + // TODO: Poke metadata + .pointer_var_to_const, + .slice_var_to_const, + .enum_to_int, + .slice_to_nullable, + .slice_to_not_null, + .pointer_to_nullable, + .pointer_const_to_var, + .pointer_to_array_to_pointer_to_many, + => { try llvm.llvm_instruction_map.putNoClobber(context.allocator, instruction_index, value); }, .sign_extend => { @@ -3097,19 +2652,10 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo const value = if (llvm.llvm_value_map.get(load.value)) |v| v else blk: { const value = switch (load.value.value) { .runtime => |instr_index| llvm.llvm_instruction_map.get(instr_index) orelse switch (unit.instructions.get(instr_index).*) { - .global => |global| b: { - const global_variable = llvm.global_variable_map.get(global).?; - break :b global_variable.toValue(); - }, - .get_element_pointer => |gep| b: { - const index = try llvm.emitRightValue(unit, context, gep.index); - const pointer = llvm.llvm_instruction_map.get(gep.pointer).?; - const t = try llvm.getType(unit, context, gep.base_type); - const indices = [1]*LLVM.Value{index}; - const in_bounds = true; - const get_element_pointer = llvm.builder.createGEP(t, pointer, &indices, indices.len, "gep", "gep".len, in_bounds) orelse unreachable; - break :b get_element_pointer; - }, + else => |t| @panic(@tagName(t)), + }, + .@"comptime" => |ct| switch (ct) { + .global => |global| llvm.global_variable_map.get(global).?.toValue(), else => |t| @panic(@tagName(t)), }, else => |t| @panic(@tagName(t)), @@ -3119,29 +2665,17 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo break :blk value; }; - const sema_value_type = switch (load.value.value) { - .runtime => |ii| switch (unit.instructions.get(ii).*) { - .stack_slot => |stack_slot| stack_slot.type, - .get_element_pointer => |gep| gep.base_type, - .argument_declaration => |arg| arg.declaration.type, - .cast => |cast| switch (unit.types.get( cast.value.type).*) { - .pointer => |pointer| pointer.type, - else => |t| @panic(@tagName(t)), - }, - .global => |global| global.declaration.type, - else => |t| @panic(@tagName(t)), - }, - else => |t| @panic(@tagName(t)), - }; - const value_type = try llvm.getType(unit, context, sema_value_type); + const value_type = try llvm.getType(unit, context, load.type); // const value_type = try llvm.getType(unit, context, load.value.type); const is_volatile = false; const load_i = llvm.builder.createLoad(value_type, value, is_volatile, "", "".len) orelse return LLVM.Value.Instruction.Error.load; try llvm.llvm_instruction_map.putNoClobber(context.allocator, instruction_index, load_i.toValue()); }, .integer_binary_operation => |binary_operation| { + assert(binary_operation.left.type == binary_operation.right.type); const left = try llvm.emitRightValue(unit, context, binary_operation.left); const right = try llvm.emitRightValue(unit, context, binary_operation.right); + assert(left.getType() == right.getType()); const no_signed_wrapping = binary_operation.signedness == .signed; const no_unsigned_wrapping = binary_operation.signedness == .unsigned; const name = @tagName(binary_operation.id); @@ -3175,26 +2709,56 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo const argument_count = call.arguments.len; const arguments = argument_buffer[0..argument_count]; - switch (call.callable) { - .function_definition => |call_function_declaration| { - const call_function_definition_index = call_function_declaration.getFunctionDefinitionIndex(); - const callee = llvm.function_definition_map.get(call_function_declaration).?; - const call_function_definition = unit.function_definitions.get(call_function_definition_index); - assert(call_function_definition.type == call.function_type); + switch (call.callable.value) { + .@"comptime" => |ct| switch (ct) { + .global => |call_function_declaration| { + const call_function_definition_index = call_function_declaration.getFunctionDefinitionIndex(); + const callee = llvm.function_definition_map.get(call_function_declaration).?; + const call_function_definition = unit.function_definitions.get(call_function_definition_index); + const call_function_prototype = unit.function_prototypes.get(unit.types.get(call_function_definition.type).function); + assert(call_function_definition.type == call.function_type); + for (call.arguments, arguments) |argument_value, *argument| { + argument.* = try llvm.emitRightValue(unit, context, argument_value); + } + + const llvm_calling_convention = callee.getCallingConvention(); + const name = ""; + const call_type = try llvm.getType(unit, context, call.function_type); + const function_type = call_type.toFunction() orelse unreachable; + for (call.arguments, arguments, call_function_prototype.argument_types, 0..) |sema_argument, argument, sema_argument_type, i| { + assert(sema_argument.type == sema_argument_type); + const argument_type = function_type.getArgumentType(@intCast(i)); + assert(argument_type == argument.getType()); + } + const call_instruction = llvm.builder.createCall(function_type, callee.toValue(), arguments.ptr, arguments.len, name.ptr, name.len, null) orelse return LLVM.Value.Instruction.Error.call; + call_instruction.setCallingConvention(llvm_calling_convention); + + try llvm.llvm_instruction_map.putNoClobber(context.allocator, instruction_index, call_instruction.toValue()); + }, + else => |t| @panic(@tagName(t)), + }, + .runtime => |ii| { + const callee = llvm.llvm_instruction_map.get(ii).?; + const callable_type = unit.types.get(call.function_type); + const sema_calling_convention = switch (callable_type.*) { + .function => |function_prototype_index| unit.function_prototypes.get(function_prototype_index).calling_convention, + else => |t| @panic(@tagName(t)), + }; + const calling_convention = getCallingConvention(sema_calling_convention); for (call.arguments, arguments) |argument_value, *argument| { argument.* = try llvm.emitRightValue(unit, context, argument_value); } - const llvm_calling_convention = callee.getCallingConvention(); const name = ""; const call_type = try llvm.getType(unit, context, call.function_type); const function_type = call_type.toFunction() orelse unreachable; - const call_instruction = llvm.builder.createCall(function_type, callee.toValue(), arguments.ptr, arguments.len, name.ptr, name.len, null) orelse return LLVM.Value.Instruction.Error.call; - call_instruction.setCallingConvention(llvm_calling_convention); + const call_instruction = llvm.builder.createCall(function_type, callee, arguments.ptr, arguments.len, name.ptr, name.len, null) orelse return LLVM.Value.Instruction.Error.call; + call_instruction.setCallingConvention(calling_convention); try llvm.llvm_instruction_map.putNoClobber(context.allocator, instruction_index, call_instruction.toValue()); }, + else => |t| @panic(@tagName(t)), } }, .ret => |return_value| { @@ -3202,10 +2766,8 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo .void => null, else => try llvm.emitRightValue(unit, context, return_value), }; - // const value = llvm.llvm_value_map.get(return_value).?; const ret = llvm.builder.createRet(value) orelse return LLVM.Value.Instruction.Error.ret; _ = ret; // autofix - // _ = ret; // autofix }, .syscall => |syscall| { var syscall_argument_buffer: [7]*LLVM.Value = undefined; @@ -3383,6 +2945,10 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo try llvm.llvm_instruction_map.putNoClobber(context.allocator, instruction_index, instruction); }, .extract_value => |extract_value| { + switch (unit.types.get(extract_value.expression.type).*) { + .pointer => unreachable, + else => {}, + } const aggregate = try llvm.emitRightValue(unit, context, extract_value.expression); const aggregate_type = try llvm.getType(unit, context, extract_value.expression.type); assert(aggregate_type == aggregate.getType()); @@ -3392,8 +2958,10 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo try llvm.llvm_instruction_map.putNoClobber(context.allocator, instruction_index, instruction); }, .integer_compare => |integer_compare| { + assert(integer_compare.left.type == integer_compare.right.type); const left = try llvm.emitRightValue(unit, context, integer_compare.left); const right = try llvm.emitRightValue(unit, context, integer_compare.right); + assert(left.getType() == right.getType()); const comparison_id: LLVM.Value.Instruction.ICmp.Kind = switch (integer_compare.id) { .equal => .eq, .not_equal => .ne, @@ -3452,30 +3020,22 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo try llvm.llvm_instruction_map.putNoClobber(context.allocator, instruction_index, phi_node.toValue()); }, .umin => |umin| { - const name = "llvm.umin"; - const intrinsic_id = LLVM.lookupIntrinsic(name.ptr, name.len); - assert(intrinsic_id != .none); - - const intrinsic_return_type = try llvm.getType(unit, context, umin.type); - const types = [_]*LLVM.Type{intrinsic_return_type}; - const intrinsic_function = llvm.module.getIntrinsicDeclaration(intrinsic_id, &types, types.len) orelse return LLVM.Value.Error.intrinsic; - const intrinsic_function_type = llvm.context.getIntrinsicType(intrinsic_id, &types, types.len) orelse return LLVM.Type.Error.intrinsic; - + const intrinsic_type = try llvm.getType(unit, context, umin.type); + const parameter_types = [_]*LLVM.Type{intrinsic_type}; const left = try llvm.emitRightValue(unit, context, umin.left); const right = try llvm.emitRightValue(unit, context, umin.right); const arguments = [_]*LLVM.Value{ left, right }; - - const call = llvm.builder.createCall(intrinsic_function_type, intrinsic_function.toValue(), &arguments, arguments.len, "min".ptr, "min".len, null) orelse return LLVM.Value.Instruction.Error.call; - try llvm.llvm_instruction_map.putNoClobber(context.allocator, instruction_index, call.toValue()); + const intrinsic_call = try llvm.callIntrinsic("llvm.umin", ¶meter_types, &arguments); + try llvm.llvm_instruction_map.putNoClobber(context.allocator, instruction_index, intrinsic_call); }, - .get_element_pointer => |gep| { - const index = try llvm.emitRightValue(unit, context, gep.index); - const pointer = llvm.llvm_instruction_map.get(gep.pointer).?; - const t = try llvm.getType(unit, context, gep.base_type); - const indices = [1]*LLVM.Value{index}; - const in_bounds = true; - const get_element_pointer = llvm.builder.createGEP(t, pointer, &indices, indices.len, "gep", "gep".len, in_bounds) orelse unreachable; - try llvm.llvm_instruction_map.putNoClobber(context.allocator, instruction_index, get_element_pointer); + .get_element_pointer => { + _ = try llvm.createGEP(unit, context, instruction_index); + }, + .trap => { + const parameter_types: []const *LLVM.Type = &.{}; + const parameter_values: []const *LLVM.Value = &.{}; + const intrinsic_call = try llvm.callIntrinsic("llvm.trap", parameter_types, parameter_values); + try llvm.llvm_instruction_map.putNoClobber(context.allocator, instruction_index, intrinsic_call); }, else => |t| @panic(@tagName(t)), } @@ -3491,18 +3051,24 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo std.debug.panic("Function block with no termination:\n{s}\n", .{function_dump}); } + if (unit.descriptor.generate_debug_information) { + llvm.debug_info_builder.finalizeSubprogram(llvm.subprogram, llvm.function); + } + const verify_function = true; + if (verify_function) { + var function_len: usize = 0; + const function_ptr = llvm.function.toString(&function_len); + const function_ir = function_ptr[0..function_len]; + var message_ptr: [*]const u8 = undefined; var message_len: usize = 0; const result = llvm.function.verify(&message_ptr, &message_len); if (!result) { - var function_len: usize = 0; - const function_ptr = llvm.function.toString(&function_len); - const function_ir = function_ptr[0..function_len]; const error_message = message_ptr[0..message_len]; - std.debug.print("PANIC: Failed to verify function:\n{s}\n\n{s}\n", .{error_message, function_ir}); + std.debug.print("PANIC: Failed to verify function:\n{s}\n", .{error_message}); var module_len: usize = 0; const module_ptr = llvm.module.toString(&module_len); @@ -3532,12 +3098,31 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo } } + // TODO: initialize only the target we are going to use + bindings.NativityLLVMInitializeCodeGeneration(); + const target_triple = "x86_64-linux-none"; + const cpu = "generic"; + const features = ""; + const target = blk: { + var error_message: [*]const u8 = undefined; + var error_message_len: usize = 0; + const target = bindings.NativityLLVMGetTarget(target_triple, target_triple.len, &error_message, &error_message_len) orelse unreachable; + break :blk target; + }; + + const jit = false; + const code_model: LLVM.CodeModel = undefined; + const is_code_model_present = false; + const target_machine = target.createTargetMachine(target_triple, target_triple.len, cpu, cpu.len, features, features.len, LLVM.RelocationModel.static, code_model, is_code_model_present, LLVM.OptimizationLevel.none, jit) orelse unreachable; + llvm.module.setTargetMachineDataLayout(target_machine); + llvm.module.setTargetTriple(target_triple, target_triple.len); const file_path = unit.descriptor.executable_path; const object_file_path = try std.mem.joinZ(context.allocator, "", &.{ file_path, ".o" }); const destination_file_path = try std.mem.joinZ(context.allocator, "", &.{file_path}); - const r = llvm.module.generateMachineCode(object_file_path.ptr, object_file_path.len, destination_file_path.ptr, destination_file_path.len); - if (!r) { - @panic("Compilation failed!"); + const disable_verify = false; + const result = llvm.module.addPassesToEmitFile(target_machine, object_file_path.ptr, object_file_path.len, LLVM.CodeGenFileType.object, disable_verify); + if (!result) { + @panic("can't generate machine code"); } const format: Format = switch (unit.descriptor.target.os.tag) { @@ -3560,12 +3145,12 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo try arguments.append(context.allocator, destination_file_path.ptr); if (format == .macho) { - try arguments.append(context.allocator, "-platform_version"); - try arguments.append(context.allocator, "macos"); - try arguments.append(context.allocator, "11"); - try arguments.append(context.allocator, "14"); - try arguments.append(context.allocator, "-arch"); - try arguments.append(context.allocator, "arm64"); + try arguments.append(context.allocator, "-dynamic"); + try arguments.appendSlice(context.allocator, &.{ "-platform_version", "macos", "13.4.1", "13.3" }); + try arguments.appendSlice(context.allocator, &.{ "-arch", "arm64" }); + try arguments.appendSlice(context.allocator, &.{ "-syslibroot", "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" }); + try arguments.appendSlice(context.allocator, &.{ "-e", "_main" }); + try arguments.append(context.allocator, "-lSystem"); } var stdout_ptr: [*]const u8 = undefined; diff --git a/bootstrap/backend/llvm_bindings.zig b/bootstrap/backend/llvm_bindings.zig index c496883..d2bce64 100644 --- a/bootstrap/backend/llvm_bindings.zig +++ b/bootstrap/backend/llvm_bindings.zig @@ -4,18 +4,20 @@ pub extern fn NativityLLVMCreateContext() ?*LLVM.Context; pub extern fn NativityLLVMCreateModule(module_name_ptr: [*:0]const u8, module_name_len: usize, context: *LLVM.Context) ?*LLVM.Module; pub extern fn NativityLLVMCreateBuilder(context: *LLVM.Context) ?*LLVM.Builder; pub extern fn NativityLLVMGetFunctionType(return_type: *LLVM.Type, argument_type_ptr: [*]const *LLVM.Type, argument_type_len: usize, is_var_args: bool) ?*LLVM.Type.Function; +pub extern fn NativityLLVMFunctionTypeGetArgumentType(function_type: *LLVM.Type.Function, argument_index: c_uint) *LLVM.Type; pub extern fn NativityLLVMGetIntegerType(context: *LLVM.Context, bit_count: u32) ?*LLVM.Type.Integer; pub extern fn NativityLLVMGetPointerType(context: *LLVM.Context, address_space: u32) ?*LLVM.Type.Pointer; +pub extern fn NativityLLVMPointerTypeGetNull(pointer_type: *LLVM.Type.Pointer) *LLVM.Value.Constant.PointerNull; pub extern fn NativityLLVMGetArrayType(element_type: *LLVM.Type, element_count: u64) ?*LLVM.Type.Array; pub extern fn NativityLLVMGetStructType(context: *LLVM.Context, type_ptr: [*]const *LLVM.Type, type_count: usize, is_packed: bool) ?*LLVM.Type.Struct; pub extern fn NativityLLVMConstantStruct(struct_type: *LLVM.Type.Struct, constant_ptr: [*]const *LLVM.Value.Constant, constant_count: usize) ?*LLVM.Value.Constant; -pub extern fn NativityLLVMModuleGetFunction(module: *LLVM.Module, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value.Function; -pub extern fn NativityLLVModuleCreateFunction(module: *LLVM.Module, function_type: *LLVM.Type.Function, linkage: LLVM.Linkage, address_space: c_uint, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value.Function; +pub extern fn NativityLLVMModuleGetFunction(module: *LLVM.Module, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value.Constant.Function; +pub extern fn NativityLLVModuleCreateFunction(module: *LLVM.Module, function_type: *LLVM.Type.Function, linkage: LLVM.Linkage, address_space: c_uint, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value.Constant.Function; pub extern fn NativityLLVMModuleCreateDebugInfoBuilder(module: *LLVM.Module) ?*LLVM.DebugInfo.Builder; pub extern fn NativityLLVMDebugInfoBuilderCreateFile(builder: *LLVM.DebugInfo.Builder, filename_ptr: [*]const u8, filename_len: usize, directory_ptr: [*]const u8, directory_len: usize) ?*LLVM.DebugInfo.File; pub extern fn NativityLLVMDebugInfoBuilderCreateCompileUnit(builder: *LLVM.DebugInfo.Builder, language: LLVM.DebugInfo.Language, file: *LLVM.DebugInfo.File, producer_ptr: [*]const u8, producer_len: usize, is_optimized: bool, flags_ptr: [*]const u8, flags_len: usize, runtime_version: c_uint, split_name_ptr: [*]const u8, split_name_len: usize, debug_info_emission_kind: LLVM.DebugInfo.CompileUnit.EmissionKind, DWOId: u64, split_debug_inlining: bool, debug_info_for_profiling: bool, debug_info_name_table_kind: LLVM.DebugInfo.CompileUnit.NameTableKind, ranges_base_address: bool, sysroot_ptr: [*]const u8, sysroot_len: usize, sdk_ptr: [*]const u8, sdk_len: usize) ?*LLVM.DebugInfo.CompileUnit; -pub extern fn NativityLLVMDebugInfoBuilderCreateFunction(builder: *LLVM.DebugInfo.Builder, scope: *LLVM.DebugInfo.Scope, name_ptr: [*]const u8, name_len: usize, linkage_name_ptr: [*]const u8, linkage_name_len: usize, file: *LLVM.DebugInfo.File, line_number: c_uint, type: *LLVM.DebugInfo.SubroutineType, scope_line: c_uint, flags: LLVM.DebugInfo.Node.Flags, subprogram_flags: LLVM.DebugInfo.Subprogram.Flags, declaration: ?*LLVM.DebugInfo.Subprogram) ?*LLVM.DebugInfo.Subprogram; -pub extern fn NativityLLVMDebugInfoBuilderCreateSubroutineType(builder: *LLVM.DebugInfo.Builder, parameter_types_ptr: [*]const *LLVM.DebugInfo.Type, parameter_type_count: usize, flags: LLVM.DebugInfo.Node.Flags, calling_convention: LLVM.DebugInfo.CallingConvention) ?*LLVM.DebugInfo.SubroutineType; +pub extern fn NativityLLVMDebugInfoBuilderCreateFunction(builder: *LLVM.DebugInfo.Builder, scope: *LLVM.DebugInfo.Scope, name_ptr: [*]const u8, name_len: usize, linkage_name_ptr: [*]const u8, linkage_name_len: usize, file: *LLVM.DebugInfo.File, line_number: c_uint, type: *LLVM.DebugInfo.Type.Subroutine, scope_line: c_uint, flags: LLVM.DebugInfo.Node.Flags, subprogram_flags: LLVM.DebugInfo.Subprogram.Flags, declaration: ?*LLVM.DebugInfo.Subprogram) ?*LLVM.DebugInfo.Subprogram; +pub extern fn NativityLLVMDebugInfoBuilderCreateSubroutineType(builder: *LLVM.DebugInfo.Builder, parameter_types_ptr: [*]const *LLVM.DebugInfo.Type, parameter_type_count: usize, flags: LLVM.DebugInfo.Node.Flags, calling_convention: LLVM.DebugInfo.CallingConvention) ?*LLVM.DebugInfo.Type.Subroutine; pub extern fn NativityLLVMDebugInfoBuilderCreateLexicalBlock(builder: *LLVM.DebugInfo.Builder, parent_scope: *LLVM.DebugInfo.Scope, parent_file: *LLVM.DebugInfo.File, line: c_uint, column: c_uint) ?*LLVM.DebugInfo.LexicalBlock; pub extern fn NativityLLVMDebugInfoBuilderCreateExpression(builder: *LLVM.DebugInfo.Builder, address: [*]const u64, length: usize) *LLVM.DebugInfo.Expression; @@ -26,26 +28,31 @@ pub extern fn NativityLLVMDebugInfoBuilderInsertDeclare(builder: *LLVM.DebugInfo pub extern fn NativityLLVMDebugInfoBuilderCreateBasicType(builder: *LLVM.DebugInfo.Builder, name_ptr: [*]const u8, name_len: usize, bit_count: u64, dwarf_encoding: LLVM.DebugInfo.AttributeType, flags: LLVM.DebugInfo.Node.Flags) ?*LLVM.DebugInfo.Type; pub extern fn NativityLLVMDebugInfoBuilderCreatePointerType(builder: *LLVM.DebugInfo.Builder, element_type: *LLVM.DebugInfo.Type, pointer_bit_count: u64, alignment: u32, name_ptr: [*]const u8, name_len: usize) ?*LLVM.DebugInfo.Type.Derived; -pub extern fn NativityLLVMDebugInfoBuilderCreateStructType(builder: *LLVM.DebugInfo.Builder, scope: ?*LLVM.DebugInfo.Scope, name_ptr: [*]const u8, name_len: usize, file: ?*LLVM.DebugInfo.File, line_number: c_uint, bit_count: u64, alignment: u32, flags: LLVM.DebugInfo.Node.Flags, derived_from: ?*LLVM.DebugInfo.Type, element_type_ptr: [*]const *LLVM.DebugInfo.Type, element_type_count: usize) ?*LLVM.DebugInfo.Type.Composite; +pub extern fn NativityLLVMDebugInfoBuilderCreateStructType(builder: *LLVM.DebugInfo.Builder, scope: ?*LLVM.DebugInfo.Scope, name_ptr: [*]const u8, name_len: usize, file: ?*LLVM.DebugInfo.File, line_number: c_uint, bit_count: u64, alignment: u32, flags: LLVM.DebugInfo.Node.Flags, derived_from: ?*LLVM.DebugInfo.Type, element_type_ptr: [*]const *LLVM.DebugInfo.Type, element_type_count: usize, forward_declaration: ?*LLVM.DebugInfo.Type.Composite) ?*LLVM.DebugInfo.Type.Composite; pub extern fn NativityLLVMDebugInfoBuilderCreateArrayType(builder: *LLVM.DebugInfo.Builder, bit_size: u64, alignment: u32, type: *LLVM.DebugInfo.Type, element_count: usize) ?*LLVM.DebugInfo.Type.Composite; pub extern fn NativityLLVMDebugInfoBuilderCreateEnumerationType(builder: *LLVM.DebugInfo.Builder, scope: ?*LLVM.DebugInfo.Scope, name_ptr: [*]const u8, name_len: usize, file: *LLVM.DebugInfo.File, line: c_uint, bit_size: u64, alignment: u32, enumerator_ptr: [*]const *LLVM.DebugInfo.Type.Enumerator, enumerator_count: usize, underlying_type: *LLVM.DebugInfo.Type) ?*LLVM.DebugInfo.Type.Composite; pub extern fn NativityLLVMDebugInfoBuilderCreateEnumerator(builder: *LLVM.DebugInfo.Builder, name_ptr: [*]const u8, name_len: usize, value: u64, is_unsigned: bool) ?*LLVM.DebugInfo.Type.Enumerator; pub extern fn NativityLLVMDebugInfoBuilderCreateReplaceableCompositeType(builder: *LLVM.DebugInfo.Builder, tag: c_uint, name_ptr: [*]const u8, name_len: usize, scope: ?*LLVM.DebugInfo.Scope, file: ?*LLVM.DebugInfo.File, line: c_uint) ?*LLVM.DebugInfo.Type.Composite; -pub extern fn NativityLLVMDebugInfoBuilderFinalizeSubprogram(builder: *LLVM.DebugInfo.Builder, subprogram: *LLVM.DebugInfo.Subprogram, function: *LLVM.Value.Function) void; +pub extern fn NativityLLVMDebugInfoBuilderCreateMemberType(builder: *LLVM.DebugInfo.Builder, scope: ?*LLVM.DebugInfo.Scope, name_ptr: [*]const u8, name_len: usize, file: ?*LLVM.DebugInfo.File, line_number: c_uint, bit_size: u64, alignment: u32, bit_offset: u64, flags: LLVM.DebugInfo.Node.Flags, type: *LLVM.DebugInfo.Type) *LLVM.DebugInfo.Type.Derived; +pub extern fn NativityLLVMDebugInfoBuilderCompositeTypeReplaceTypes(builder: *LLVM.DebugInfo.Builder, type: *LLVM.DebugInfo.Type.Composite, element_type_ptr: [*]const *LLVM.DebugInfo.Type, element_type_count: usize) void; +pub extern fn NativityLLLVMDITypeIsResolved(type: *LLVM.DebugInfo.Type) bool; +pub extern fn NativityLLVMDebugInfoBuilderFinalizeSubprogram(builder: *LLVM.DebugInfo.Builder, subprogram: *LLVM.DebugInfo.Subprogram, function: *LLVM.Value.Constant.Function) void; pub extern fn NativityLLVMDebugInfoBuilderFinalize(builder: *LLVM.DebugInfo.Builder) void; pub extern fn NativityLLVMDebugInfoSubprogramGetFile(subprogram: *LLVM.DebugInfo.Subprogram) ?*LLVM.DebugInfo.File; pub extern fn NativityLLVMDebugInfoSubprogramGetArgumentType(subprogram: *LLVM.DebugInfo.Subprogram, argument_index: usize) ?*LLVM.DebugInfo.Type; pub extern fn NativityLLVMDebugInfoScopeToSubprogram(scope: *LLVM.DebugInfo.Scope) ?*LLVM.DebugInfo.Subprogram; -pub extern fn NativityLLVMCreateBasicBlock(context: *LLVM.Context, name_ptr: [*]const u8, name_len: usize, parent_function: ?*LLVM.Value.Function, insert_before: ?*LLVM.Value.BasicBlock) ?*LLVM.Value.BasicBlock; +pub extern fn NativityLLVMCreateBasicBlock(context: *LLVM.Context, name_ptr: [*]const u8, name_len: usize, parent_function: ?*LLVM.Value.Constant.Function, insert_before: ?*LLVM.Value.BasicBlock) ?*LLVM.Value.BasicBlock; pub extern fn NativityLLVMBasicBlockRemoveFromParent(basic_block: *LLVM.Value.BasicBlock) void; pub extern fn NativityLLVMBuilderSetInsertPoint(builder: *LLVM.Builder, basic_block: *LLVM.Value.BasicBlock) void; pub extern fn NativityLLVMBuilderGetInsertBlock(builder: *LLVM.Builder) ?*LLVM.Value.BasicBlock; -pub extern fn NativityLLVMBuilderSetCurrentDebugLocation(builder: *LLVM.Builder, context: *LLVM.Context, line: c_uint, column: c_uint, scope: *LLVM.DebugInfo.Scope, function: *LLVM.Value.Function) void; +pub extern fn NativityLLVMBuilderSetCurrentDebugLocation(builder: *LLVM.Builder, context: *LLVM.Context, line: c_uint, column: c_uint, scope: *LLVM.DebugInfo.Scope, function: *LLVM.Value.Constant.Function) void; pub extern fn NativityLLVMValueSetName(value: *LLVM.Value, name_ptr: [*]const u8, name_len: usize) void; pub extern fn NativityLLVMValueGetType(value: *LLVM.Value) *LLVM.Type; pub extern fn NativityLLVMArgumentGetIndex(argument: *LLVM.Value.Argument) c_uint; -pub extern fn NativityLLVMFunctionGetArguments(function: *LLVM.Value.Function, argument_ptr: [*]*LLVM.Value.Argument, argument_len: *usize) void; -pub extern fn NativityLLVMFunctionGetReturnType(function: *LLVM.Value.Function) ?*LLVM.Type; +pub extern fn NativityLLVMFunctionGetArguments(function: *LLVM.Value.Constant.Function, argument_ptr: [*]*LLVM.Value.Argument, argument_len: *usize) void; +pub extern fn NativityLLVMFunctionGetType(function: *LLVM.Value.Constant.Function) *LLVM.Type.Function; +pub extern fn NativityLLVMFunctionTypeGetReturnType(function_type: *LLVM.Type.Function) *LLVM.Type; +pub extern fn NativityLLVMTypeIsVoid(type: *LLVM.Type) bool; pub extern fn NativityLLVMBuilderCreateAlloca(builder: *LLVM.Builder, type: *LLVM.Type, address_space: c_uint, array_size: ?*LLVM.Value, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value.Instruction.Alloca; pub extern fn NativityLLVMBuilderCreateStore(builder: *LLVM.Builder, value: *LLVM.Value, pointer: *LLVM.Value, is_volatile: bool) ?*LLVM.Value.Instruction.Store; pub extern fn NativityLLVMContextGetConstantInt(context: *LLVM.Context, bit_count: c_uint, value: u64, is_signed: bool) ?*LLVM.Value.Constant.Int; @@ -56,52 +63,53 @@ pub extern fn NativityLLVMBuilderCreateICmp(builder: *LLVM.Builder, integer_comp pub extern fn NativityLLVMBuilderCreateLoad(builder: *LLVM.Builder, type: *LLVM.Type, value: *LLVM.Value, is_volatile: bool, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value.Instruction.Load; pub extern fn NativityLLVMBuilderCreateRet(builder: *LLVM.Builder, value: ?*LLVM.Value) ?*LLVM.Value.Instruction.Ret; pub extern fn NativityLLVMBuilderCreateCast(builder: *LLVM.Builder, cast_type: LLVM.Value.Instruction.Cast.Type, value: *LLVM.Value, type: *LLVM.Type, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value; -pub extern fn NativityLLVMFunctionAddAttributeKey(builder: *LLVM.Value.Function, attribute_key: LLVM.Attribute) void; +pub extern fn NativityLLVMFunctionAddAttributeKey(builder: *LLVM.Value.Constant.Function, attribute_key: LLVM.Attribute) void; pub extern fn NativityLLVMGetVoidType(context: *LLVM.Context) ?*LLVM.Type; pub extern fn NativityLLVMGetInlineAssembly(function_type: *LLVM.Type.Function, assembly_ptr: [*]const u8, assembly_len: usize, constraints_ptr: [*]const u8, constrains_len: usize, has_side_effects: bool, is_align_stack: bool, dialect: LLVM.Value.InlineAssembly.Dialect, can_throw: bool) ?*LLVM.Value.InlineAssembly; pub extern fn NativityLLVMBuilderCreateCall(builder: *LLVM.Builder, function_type: *LLVM.Type.Function, callee: *LLVM.Value, argument_ptr: [*]const *LLVM.Value, argument_count: usize, name_ptr: [*]const u8, name_len: usize, fp_math_tag: ?*LLVM.Metadata.Node) ?*LLVM.Value.Instruction.Call; pub extern fn NativityLLVMBuilderCreateUnreachable(builder: *LLVM.Builder) ?*LLVM.Value.Instruction.Unreachable; pub extern fn NativityLLVMModuleAddGlobalVariable(module: *LLVM.Module, type: *LLVM.Type, is_constant: bool, linkage: LLVM.Linkage, initializer: ?*LLVM.Value.Constant, name_ptr: [*]const u8, name_len: usize, insert_before: ?*LLVM.Value.Constant.GlobalVariable, thread_local_mode: LLVM.ThreadLocalMode, address_space: c_uint, externally_initialized: bool) ?*LLVM.Value.Constant.GlobalVariable; - + pub extern fn NativityLLVMBuilderCreateAdd(builder: *LLVM.Builder, left: *LLVM.Value, right: *LLVM.Value, name_ptr: [*]const u8, name_len: usize, no_unsigned_wrapping: bool, no_signed_wrapping: bool) ?*LLVM.Value; pub extern fn NativityLLVMBuilderCreateSub(builder: *LLVM.Builder, left: *LLVM.Value, right: *LLVM.Value, name_ptr: [*]const u8, name_len: usize, no_unsigned_wrapping: bool, no_signed_wrapping: bool) ?*LLVM.Value; pub extern fn NativityLLVMBuilderCreateMultiply(builder: *LLVM.Builder, left: *LLVM.Value, right: *LLVM.Value, name_ptr: [*]const u8, name_len: usize, no_unsigned_wrapping: bool, no_signed_wrapping: bool) ?*LLVM.Value; pub extern fn NativityLLVMBuilderCreateShiftLeft(builder: *LLVM.Builder, left: *LLVM.Value, right: *LLVM.Value, name_ptr: [*]const u8, name_len: usize, no_unsigned_wrapping: bool, no_signed_wrapping: bool) ?*LLVM.Value; - + pub extern fn NativityLLVMBuilderCreateUDiv(builder: *LLVM.Builder, left: *LLVM.Value, right: *LLVM.Value, name_ptr: [*]const u8, name_len: usize, is_exact: bool) ?*LLVM.Value; pub extern fn NativityLLVMBuilderCreateSDiv(builder: *LLVM.Builder, left: *LLVM.Value, right: *LLVM.Value, name_ptr: [*]const u8, name_len: usize, is_exact: bool) ?*LLVM.Value; pub extern fn NativityLLVMBuilderCreateURem(builder: *LLVM.Builder, left: *LLVM.Value, right: *LLVM.Value, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value; pub extern fn NativityLLVMBuilderCreateSRem(builder: *LLVM.Builder, left: *LLVM.Value, right: *LLVM.Value, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value; pub extern fn NativityLLVMBuilderCreateLogicalShiftRight(builder: *LLVM.Builder, left: *LLVM.Value, right: *LLVM.Value, name_ptr: [*]const u8, name_len: usize, is_exact: bool) ?*LLVM.Value; pub extern fn NativityLLVMBuilderCreateArithmeticShiftRight(builder: *LLVM.Builder, left: *LLVM.Value, right: *LLVM.Value, name_ptr: [*]const u8, name_len: usize, is_exact: bool) ?*LLVM.Value; - + pub extern fn NativityLLVMBuilderCreateXor(builder: *LLVM.Builder, left: *LLVM.Value, right: *LLVM.Value, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value; pub extern fn NativityLLVMBuilderCreateAnd(builder: *LLVM.Builder, left: *LLVM.Value, right: *LLVM.Value, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value; pub extern fn NativityLLVMBuilderCreateOr(builder: *LLVM.Builder, left: *LLVM.Value, right: *LLVM.Value, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value; pub extern fn NativityLLVMBuilderCreateGEP(builder: *LLVM.Builder, type: *LLVM.Type, pointer: *LLVM.Value, index_ptr: [*]const *LLVM.Value, index_count: usize, name_ptr: [*]const u8, name_len: usize, in_bounds: bool) ?*LLVM.Value; +pub extern fn NativityLLVMBuilderCreateStructGEP(builder: *LLVM.Builder, type: *LLVM.Type, pointer: *LLVM.Value, index: c_uint, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value; pub extern fn NativityLLVMBuilderCreateBranch(builder: *LLVM.Builder, basic_block: *LLVM.Value.BasicBlock) ?*LLVM.Value.Instruction.Branch; pub extern fn NativityLLVMBuilderCreateConditionalBranch(builder: *LLVM.Builder, condition: *LLVM.Value, true_block: *LLVM.Value.BasicBlock, false_block: *LLVM.Value.BasicBlock, branch_weights: ?*LLVM.Metadata.Node, unpredictable: ?*LLVM.Metadata.Node) ?*LLVM.Value.Instruction.Branch; -pub extern fn NativityLLVMVerifyFunction(function: *LLVM.Value.Function, message_ptr: *[*]const u8, message_len: *usize) bool; +pub extern fn NativityLLVMVerifyFunction(function: *LLVM.Value.Constant.Function, message_ptr: *[*]const u8, message_len: *usize) bool; pub extern fn NativityLLVMVerifyModule(module: *LLVM.Module, message_ptr: *[*]const u8, message_len: *usize) bool; pub extern fn NativityLLVMModuleToString(module: *LLVM.Module, message_len: *usize) [*]const u8; -pub extern fn NativityLLVMFunctionToString(function: *LLVM.Value.Function, message_len: *usize) [*]const u8; - +pub extern fn NativityLLVMFunctionToString(function: *LLVM.Value.Constant.Function, message_len: *usize) [*]const u8; +pub extern fn NativityLLVMValueToString(value: *LLVM.Value, message_len: *usize) [*]const u8; pub extern fn NativityLLVMBuilderIsCurrentBlockTerminated(builder: *LLVM.Builder) bool; pub extern fn NativityLLVMGetUndefined(type: *LLVM.Type) ?*LLVM.Value.Constant.Undefined; pub extern fn NativityLLVMGetPoisonValue(type: *LLVM.Type) ?*LLVM.Value.Constant.Poison; -pub extern fn NativityLLVMFunctionSetCallingConvention(function: *LLVM.Value.Function, calling_convention: LLVM.Value.Function.CallingConvention) void; -pub extern fn NativityLLVMFunctionGetCallingConvention(function: *LLVM.Value.Function) LLVM.Value.Function.CallingConvention; -pub extern fn NativityLLVMFunctionSetSubprogram(function: *LLVM.Value.Function, subprogram: *LLVM.DebugInfo.Subprogram) void; -pub extern fn NativityLLVMFunctionGetSubprogram(function: *LLVM.Value.Function) ?*LLVM.DebugInfo.Subprogram; +pub extern fn NativityLLVMFunctionSetCallingConvention(function: *LLVM.Value.Constant.Function, calling_convention: LLVM.Value.Constant.Function.CallingConvention) void; +pub extern fn NativityLLVMFunctionGetCallingConvention(function: *LLVM.Value.Constant.Function) LLVM.Value.Constant.Function.CallingConvention; +pub extern fn NativityLLVMFunctionSetSubprogram(function: *LLVM.Value.Constant.Function, subprogram: *LLVM.DebugInfo.Subprogram) void; +pub extern fn NativityLLVMFunctionGetSubprogram(function: *LLVM.Value.Constant.Function) ?*LLVM.DebugInfo.Subprogram; -pub extern fn NativityLLVMCallSetCallingConvention(instruction: *LLVM.Value.Instruction.Call, calling_convention: LLVM.Value.Function.CallingConvention) void; +pub extern fn NativityLLVMCallSetCallingConvention(instruction: *LLVM.Value.Instruction.Call, calling_convention: LLVM.Value.Constant.Function.CallingConvention) void; pub extern fn NativityLLVMGetStruct(struct_type: *LLVM.Type.Struct, constant_ptr: [*]const *LLVM.Value.Constant, constant_len: usize) ?*LLVM.Value.Constant; pub extern fn NativityLLVMValueToConstant(value: *LLVM.Value) ?*LLVM.Value.Constant; -pub extern fn NativityLLVMValueToFunction(value: *LLVM.Value) ?*LLVM.Value.Function; +pub extern fn NativityLLVMValueToFunction(value: *LLVM.Value) ?*LLVM.Value.Constant.Function; pub extern fn NativityLLVMTypeIsPointer(type: *LLVM.Type) bool; pub extern fn NativityLLVMTypeIsInteger(type: *LLVM.Type) bool; @@ -113,7 +121,7 @@ pub extern fn NativityLLVMTypeToPointer(Type: *LLVM.Type) ?*LLVM.Type.Pointer; pub extern fn NativityLLVMArrayTypeGetElementType(array_type: *LLVM.Type.Array) ?*LLVM.Type; pub extern fn NativityLLVMLookupIntrinsic(name_ptr: [*]const u8, name_len: usize) LLVM.Value.IntrinsicID; -pub extern fn NativityLLVMModuleGetIntrinsicDeclaration(module: *LLVM.Module, intrinsic_id: LLVM.Value.IntrinsicID, parameter_types_ptr: [*]const *LLVM.Type, parameter_type_count: usize) ?*LLVM.Value.Function; +pub extern fn NativityLLVMModuleGetIntrinsicDeclaration(module: *LLVM.Module, intrinsic_id: LLVM.Value.IntrinsicID, parameter_types_ptr: [*]const *LLVM.Type, parameter_type_count: usize) ?*LLVM.Value.Constant.Function; pub extern fn NativityLLVMContextGetIntrinsicType(context: *LLVM.Context, intrinsic_id: LLVM.Value.IntrinsicID, parameter_type_ptr: [*]const *LLVM.Type, parameter_type_count: usize) ?*LLVM.Type.Function; pub extern fn NativityLLVMBuilderCreateExtractValue(builder: *LLVM.Builder, aggregate: *LLVM.Value, indices_ptr: [*]const c_uint, indices_len: usize, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value; pub extern fn NativityLLVMBuilderCreateInsertValue(builder: *LLVM.Builder, aggregate: *LLVM.Value, value: *LLVM.Value, indices_ptr: [*]const c_uint, indices_len: usize, name_ptr: [*]const u8, name_len: usize) ?*LLVM.Value; @@ -127,5 +135,10 @@ pub extern fn NativityLLVMAllocatGetAllocatedType(alloca: *LLVM.Value.Instructio pub extern fn NativityLLVMValueToAlloca(value: *LLVM.Value) ?*LLVM.Value.Instruction.Alloca; pub extern fn NativityLLVMGlobalVariableSetInitializer(global_variable: *LLVM.Value.Constant.GlobalVariable, constant_initializer: *LLVM.Value.Constant) void; -pub extern fn NativityLLVMGenerateMachineCode(module: *LLVM.Module, object_file_path_ptr: [*]const u8, object_file_path_len: usize, file_path_ptr: [*]const u8, file_path_len: usize) bool; +pub extern fn NativityLLVMInitializeCodeGeneration() void; +pub extern fn NativityLLVMGetTarget(target_triple_ptr: [*]const u8, target_triple_len: usize, message_ptr: *[*]const u8, message_len: *usize) ?*LLVM.Target; +pub extern fn NativityLLVMTargetCreateTargetMachine(target: *LLVM.Target, target_triple_ptr: [*]const u8, target_triple_len: usize, cpu_ptr: [*]const u8, cpu_len: usize, features_ptr: [*]const u8, features_len: usize, relocation_model: LLVM.RelocationModel, maybe_code_model: LLVM.CodeModel, is_code_model_present: bool, optimization_level: LLVM.OptimizationLevel, jit: bool) ?*LLVM.Target.Machine; +pub extern fn NativityLLVMModuleSetTargetMachineDataLayout(module: *LLVM.Module, target_machine: *LLVM.Target.Machine) void; +pub extern fn NativityLLVMModuleSetTargetTriple(module: *LLVM.Module, target_triple_ptr: [*]const u8, target_triple_len: usize) void; +pub extern fn NativityLLVMModuleAddPassesToEmitFile(module: *LLVM.Module, target_machine: *LLVM.Target.Machine, object_file_path_ptr: [*]const u8, object_file_path_len: usize, codegen_file_type: LLVM.CodeGenFileType, disable_verify: bool) bool; pub extern fn NativityLLDLink(format: llvm.Format, argument_ptr: [*]const [*:0]const u8, argument_count: usize, stdout_ptr: *[*]const u8, stdout_len: *usize, stderr_ptr: *[*]const u8, stderr_len: *usize) bool; diff --git a/bootstrap/frontend/parser.zig b/bootstrap/frontend/parser.zig index 87b6e01..63f14e8 100644 --- a/bootstrap/frontend/parser.zig +++ b/bootstrap/frontend/parser.zig @@ -1433,6 +1433,7 @@ const Analyzer = struct { } logln(.parser, .suffix, "[DEPTH={}] Ending suffix call-like expression", .{analyzer.suffix_depth}); + logln(.parser, .suffix, "Callee node: {s}", .{@tagName(analyzer.nodes.get(result).id)}); _ = try analyzer.expectToken(.operator_right_parenthesis); // const is_comma = analyzer.tokens[analyzer.token_i].id == .comma; diff --git a/build.zig b/build.zig index f3267b5..c629991 100644 --- a/build.zig +++ b/build.zig @@ -5,6 +5,7 @@ pub fn build(b: *std.Build) !void { const self_hosted_ci = b.option(bool, "self_hosted_ci", "This option enables the self-hosted CI behavior") orelse false; const third_party_ci = b.option(bool, "third_party_ci", "This option enables the third-party CI behavior") orelse false; const is_ci = self_hosted_ci or third_party_ci; + const print_stack_trace = b.option(bool, "print_stack_trace", "This option enables printing stack traces inside the compiler") orelse is_ci; const native_target = b.resolveTargetQuery(.{}); const optimization = b.standardOptimizeOption(.{}); var target_query = b.standardTargetOptionsQueryOnly(.{}); @@ -69,7 +70,7 @@ pub fn build(b: *std.Build) !void { } }; const compiler_options = b.addOptions(); - compiler_options.addOption(bool, "print_stack_trace", is_ci); + compiler_options.addOption(bool, "print_stack_trace", print_stack_trace); const compiler = b.addExecutable(.{ .name = "nat", @@ -78,8 +79,8 @@ pub fn build(b: *std.Build) !void { .optimize = optimization, }); compiler.root_module.addOptions("configuration", compiler_options); - compiler.formatted_panics = is_ci; - compiler.root_module.unwind_tables = is_ci; + compiler.formatted_panics = print_stack_trace; + compiler.root_module.unwind_tables = print_stack_trace; compiler.root_module.omit_frame_pointer = false; compiler.want_lto = false; diff --git a/build/test_runner.zig b/build/test_runner.zig index aabc8d6..dfca186 100644 --- a/build/test_runner.zig +++ b/build/test_runner.zig @@ -100,7 +100,7 @@ pub fn main() !void { } std.debug.print("\nTOTAL COMPILATIONS: {}. FAILED: {}\n", .{total_compilation_count, failed_compilation_count}); - std.debug.print("\nTOTAL TESTS: {}. RAN: {}. FAILED: {}\n", .{total_test_count, ran_test_count, failed_test_count}); + std.debug.print("TOTAL TESTS: {}. RAN: {}. FAILED: {}\n", .{total_test_count, ran_test_count, failed_test_count}); if (failed_compilation_count > 0 or failed_test_count > 0) { return error.fail; diff --git a/lib/std/os.nat b/lib/std/os.nat index b7021d3..c904db1 100644 --- a/lib/std/os.nat +++ b/lib/std/os.nat @@ -160,7 +160,9 @@ const duplicate_process = fn () ?Process.Id { switch (current) { .linux => { if (linux.unwrap_syscall(syscall_result = linux.fork())) |fork_result| { - return #cast(fork_result); + const unsigned: u32 = #cast(fork_result); + const signed: s32 = #cast(unsigned); + return signed; } else { return null; } diff --git a/lib/std/os/linux.nat b/lib/std/os/linux.nat index 7bc0045..f883d73 100644 --- a/lib/std/os/linux.nat +++ b/lib/std/os/linux.nat @@ -411,7 +411,9 @@ const get_map_flags = fn(flags: std.os.MapFlags) MapFlags{ } const mmap = fn(address: ?[&]u8, length: usize, protection_flags: ProtectionFlags, map_flags: MapFlags, fd: s32, offset: u64) usize { - const result = #syscall(#cast(Syscall.mmap), #cast(address), length, #cast(protection_flags), #cast(map_flags), #cast(fd), offset); + const flat_protection_flags: u32 = #cast(protection_flags); + const flat_map_flags: u32 = #cast(map_flags); + const result = #syscall(#cast(Syscall.mmap), #cast(address), length, flat_protection_flags, flat_map_flags, #cast(fd), offset); return result; } diff --git a/lib/std/std.nat b/lib/std/std.nat index d6bb3d2..85960b9 100644 --- a/lib/std/std.nat +++ b/lib/std/std.nat @@ -105,12 +105,12 @@ const PageAllocator = struct{ if (new_size > 0) { const general_protection_flags = os.ProtectionFlags{ .read = true, - .write = true, - .execute = false, + .write = true, + .execute = false, }; const general_map_flags = os.MapFlags{ .reserve = true, - .commit = true, + .commit = true, }; maybe_new_ptr = os.allocate_virtual_memory(address = null, length = new_size, general_protection_flags, general_map_flags); diff --git a/test/standalone/bit_struct/main.nat b/test/standalone/bit_struct/main.nat new file mode 100644 index 0000000..6b8e9da --- /dev/null +++ b/test/standalone/bit_struct/main.nat @@ -0,0 +1,27 @@ +const BitStruct = struct(u8) { + a: bool, + b: bool, + c: bool, + d: u5, +}; + +const main = fn () s32 { + var bs = BitStruct{ + .a = false, + .b = true, + .c = true, + .d = 0, + }; + const bitcast_bs: u8 = #cast(bs); + #assert(bitcast_bs == 6); + + const const_bs = BitStruct{ + .a = true, + .b = false, + .c = true, + .d = 0, + }; + const bitcast_const_bs: u8 = #cast(const_bs); + #assert(bitcast_const_bs == 5); + return 0; +} diff --git a/test/standalone/bit_struct_call/main.nat b/test/standalone/bit_struct_call/main.nat new file mode 100644 index 0000000..2c99df8 --- /dev/null +++ b/test/standalone/bit_struct_call/main.nat @@ -0,0 +1,28 @@ +const A = struct(u8) { + a: u4, + b: u4, +}; +const B = struct(u8) { + b: u4, + a: u4, +}; + +const transform = fn (a: A) B { + return B{ + .a = a.a, + .b = a.b, + }; +} + +const main = fn () s32 { + var a = A{ + .a = 3, + .b = 8, + }; + + const b = transform(a); + #assert(a.a == b.a); + #assert(a.b == b.b); + + return 0; +} diff --git a/todo_test/standalone/fork/main.nat b/test/standalone/fork/main.nat similarity index 100% rename from todo_test/standalone/fork/main.nat rename to test/standalone/fork/main.nat diff --git a/todo_test/standalone/fork_exec/main.nat b/test/standalone/fork_exec/main.nat similarity index 81% rename from todo_test/standalone/fork_exec/main.nat rename to test/standalone/fork_exec/main.nat index df8bd18..8f52b08 100644 --- a/todo_test/standalone/fork_exec/main.nat +++ b/test/standalone/fork_exec/main.nat @@ -5,7 +5,7 @@ const main = fn() s32 { if (pid == 0) { std.print(bytes = "Hello from child\n"); const argv = [_:null] ?[&:0]const u8{"/usr/bin/ls"}; - std.os.execute(path = "/usr/bin/ls", argv = argv.&, env = std.start.environment_values); + _ = std.os.execute(path = "/usr/bin/ls", argv = argv.&, env = std.start.environment_values); return 1; } else { std.print(bytes = "Hello from parent\n"); diff --git a/test/standalone/function_pointer/main.nat b/test/standalone/function_pointer/main.nat new file mode 100644 index 0000000..43f0eb8 --- /dev/null +++ b/test/standalone/function_pointer/main.nat @@ -0,0 +1,11 @@ +const expected_number = 123; + +const foo = fn () s32 { + return expected_number; +} + +const main = fn () s32 { + var function_pointer = foo.&; + const result = function_pointer(); + return result - expected_number; +} diff --git a/test/standalone/function_pointer_struct/main.nat b/test/standalone/function_pointer_struct/main.nat new file mode 100644 index 0000000..af2ce36 --- /dev/null +++ b/test/standalone/function_pointer_struct/main.nat @@ -0,0 +1,23 @@ +const expected_number = 123; + +const foo = fn () s32 { + return expected_number; +} + +const Struct = struct{ + a: s32, + handler: &const fn(s: &Struct) s32, + + const handler_function = fn (s: &Struct) s32 { + return s.a; + } +}; + +const main = fn () s32 { + var s = Struct{ + .a = expected_number, + .handler = Struct.handler_function.&, + }; + + return s.handler(s.&) - expected_number; +} diff --git a/todo_test/standalone/self_exe_path/main.nat b/test/standalone/self_exe_path/main.nat similarity index 100% rename from todo_test/standalone/self_exe_path/main.nat rename to test/standalone/self_exe_path/main.nat diff --git a/todo_test/standalone/virtual_memory/main.nat b/test/standalone/virtual_memory/main.nat similarity index 100% rename from todo_test/standalone/virtual_memory/main.nat rename to test/standalone/virtual_memory/main.nat