diff --git a/src/Compilation.zig b/src/Compilation.zig index 54f89fa..d2268d4 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -303,6 +303,7 @@ pub const Module = struct { calls: BlockList(Call) = .{}, argument_list: BlockList(ArgumentList) = .{}, returns: BlockList(Return) = .{}, + entry_point: ?u32 = null, pub const Descriptor = struct { main_package_path: []const u8, diff --git a/src/backend/emit.zig b/src/backend/emit.zig index 97c4c2e..c35c0f3 100644 --- a/src/backend/emit.zig +++ b/src/backend/emit.zig @@ -27,7 +27,7 @@ pub const Result = struct { }, entry_point: u32 = 0, - fn create() !Result { + pub fn create() !Result { return Result{ .sections = .{ .text = .{ .content = try mmap(page_size, .{ .executable = true }) }, @@ -82,14 +82,6 @@ pub const Result = struct { image.sections.text.index += 1; } - // fn appendOnlyOpcodeSkipInstructionBytes(image: *Result, instruction: Instruction) void { - // const instruction_descriptor = instruction_descriptors.get(instruction); - // assert(instruction_descriptor.opcode_byte_count == instruction_descriptor.operand_offset); - // image.appendCode(instruction_descriptor.getOpcode()); - // - // image.sections.text.index += instruction_descriptor.size - instruction_descriptor.opcode_byte_count; - // } - fn getEntryPoint(image: *const Result, comptime FunctionType: type) *const FunctionType { comptime { assert(@typeInfo(FunctionType) == .Fn); @@ -127,79 +119,13 @@ pub fn get(comptime arch: std.Target.Cpu.Arch) type { .x86_64 => @import("x86_64.zig"), else => @compileError("Architecture not supported"), }; - const Instruction = backend.Instruction; - _ = Instruction; return struct { pub fn initialize(allocator: Allocator, intermediate: *ir.Result) !void { - var result = try Result.create(); + std.debug.print("Entry point: {}\n", .{intermediate.entry_point}); var mir = try backend.MIR.generate(allocator, intermediate); try mir.allocateRegisters(allocator, intermediate); - // var function_iterator = intermediate.functions.iterator(); - // const IS = InstructionSelector(Instruction); - // var instruction_selector = IS{ - // .functions = try ArrayList(IS.Function).initCapacity(allocator, intermediate.functions.len), - // .allocator = allocator, - // }; - // - // while (function_iterator.next()) |ir_function| { - // const function = instruction_selector.functions.addOneAssumeCapacity(); - // function.* = .{}; - // try function.block_map.ensureTotalCapacity(allocator, @intCast(ir_function.blocks.items.len)); - // for (ir_function.blocks.items, 0..) |block_index, index| { - // function.block_map.putAssumeCapacity(block_index, @intCast(index)); - // } - // - // for (ir_function.blocks.items) |block_index| { - // const block = intermediate.blocks.get(block_index); - // for (block.instructions.items) |instruction_index| { - // const instruction = intermediate.instructions.get(instruction_index).*; - // try backend.selectInstruction(&instruction_selector, function, intermediate, instruction); - // } - // - // // function.block_byte_counts.appendAssumeCapacity(function.block_byte_count); - // // function.byte_count += function.block_byte_count; - // } - // } - // - // for (instruction_selector.functions.items) |function| { - // for (function.instructions.items) |instruction| backend.emitInstruction(&result, instruction, intermediate); - // } - - // for (instruction_selector.functions.items) |function| { - // var fix_size: bool = false; - // _ = fix_size; - // for (function.relocations.items) |instruction_index| { - // const instruction = function.instructions.items[instruction_index]; - // const relative = instruction.jmp_rel_8; - // const source_block = relative.source; - // const destination_block = relative.destination; - // const source_offset = function.block_offsets.items[source_block]; - // const destination_offset = function.block_offsets.items[destination_block]; - // std.debug.print("Source offset: {}. Destination: {}\n", .{ source_offset, destination_offset }); - // const instruction_descriptor = instruction_descriptors.get(relative.instruction); - // const instruction_offset = source_offset + relative.block_offset; - // const really_source_offset = instruction_offset + instruction_descriptor.size; - // const displacement = @as(i64, destination_offset) - @as(i64, really_source_offset); - // - // const operands = instruction_descriptor.getOperands(); - // switch (operands.len) { - // 1 => switch (operands[0].size) { - // @sizeOf(u8) => { - // if (displacement >= std.math.minInt(i8) and displacement <= std.math.maxInt(i8)) { - // const writer_index = instruction_offset + instruction_descriptor.operand_offset; - // std.debug.print("Instruction offset: {}. Operand offset: {}. Writer index: {}. displacement: {}\n", .{ instruction_offset, instruction_descriptor.operand_offset, writer_index, displacement }); - // result.sections.text.content[writer_index] = @bitCast(@as(i8, @intCast(displacement))); - // } else { - // unreachable; - // } - // }, - // else => unreachable, - // }, - // else => unreachable, - // } - // } - // } + const result = try mir.encode(intermediate); const text_section = result.sections.text.content[0..result.sections.text.index]; for (text_section) |byte| { diff --git a/src/backend/intermediate_representation.zig b/src/backend/intermediate_representation.zig index 0ff9b3e..b396aa7 100644 --- a/src/backend/intermediate_representation.zig +++ b/src/backend/intermediate_representation.zig @@ -25,6 +25,7 @@ pub const Result = struct { syscalls: BlockList(Syscall) = .{}, values: BlockList(Value) = .{}, stack_references: BlockList(StackReference) = .{}, + entry_point: u32 = 0, }; pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_file: Compilation.Type.Index) !Result { @@ -38,6 +39,8 @@ pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, .module = module, }; + builder.ir.entry_point = module.entry_point orelse unreachable; + while (function_iterator.next()) |sema_function| { const function_index = try builder.buildFunction(sema_function); try builder.optimizeFunction(function_index); @@ -284,10 +287,29 @@ pub const Builder = struct { for (basic_block.instructions.items) |instruction_index| { did_something = did_something or try builder.removeUnreachablePhis(reachable_blocks, instruction_index); did_something = did_something or try builder.removeTrivialPhis(instruction_index); - did_something = did_something or try builder.removeCopies(instruction_index); + const copy = try builder.removeCopyReferences(instruction_index); + did_something = did_something or copy; } } } + + var instructions_to_delete = ArrayList(u32){}; + for (reachable_blocks) |basic_block_index| { + instructions_to_delete.clearRetainingCapacity(); + const basic_block = builder.ir.blocks.get(basic_block_index); + for (basic_block.instructions.items, 0..) |instruction_index, index| { + const instruction = builder.ir.instructions.get(instruction_index); + switch (instruction.*) { + .copy => try instructions_to_delete.append(builder.allocator, @intCast(index)), + else => {}, + } + } + + var deleted_instruction_count: usize = 0; + for (instructions_to_delete.items) |instruction_to_delete| { + _ = basic_block.instructions.orderedRemove(instruction_to_delete - deleted_instruction_count); + } + } } fn removeUnreachablePhis(builder: *Builder, reachable_blocks: []const BasicBlock.Index, instruction_index: Instruction.Index) !bool { @@ -367,7 +389,7 @@ pub const Builder = struct { }; } - fn removeCopies(builder: *Builder, instruction_index: Instruction.Index) !bool { + fn removeCopyReferences(builder: *Builder, instruction_index: Instruction.Index) !bool { const instruction = builder.ir.instructions.get(instruction_index); return switch (instruction.*) { .copy => false, diff --git a/src/backend/x86_64.zig b/src/backend/x86_64.zig index 7e316ab..f56900e 100644 --- a/src/backend/x86_64.zig +++ b/src/backend/x86_64.zig @@ -11,7 +11,6 @@ const data_structures = @import("../data_structures.zig"); const ArrayList = data_structures.ArrayList; const AutoArrayHashMap = data_structures.AutoArrayHashMap; -const InstructionSelector = emit.InstructionSelector(Instruction); const x86_64 = @This(); const Size = enum(u2) { @@ -19,12 +18,57 @@ const Size = enum(u2) { two = 1, four = 2, eight = 3, + + fn fromByteCount(byte_count: u8) Size { + return @enumFromInt(@as(u2, @intCast(std.math.log2(byte_count)))); + } + + fn fromBitCount(bit_count: u16) Size { + assert(bit_count % @bitSizeOf(u8) == 0); + const byte_count: u8 = @intCast(bit_count >> 3); + return fromByteCount(byte_count); + } + + fn toInteger(comptime size: Size) type { + return switch (size) { + .one => u8, + .two => u16, + .four => u32, + .eight => u64, + }; + } +}; + +fn Relocation(comptime Target: type) type { + return struct { + target: Target, + instruction_byte_offset: u32, + instruction_length: u8, + source_address_writer_offset: u8, + size: Size, + }; +} + +const LocalRelocation = Relocation(ir.BasicBlock.Index); +const GlobalRelocation = Relocation(u32); + +fn RelocationIndex(comptime relocation_type: RelocationType) type { + return switch (relocation_type) { + .local => ir.BasicBlock.Index, + .global => u32, + }; +} +const RelocationType = enum { + local, + global, }; pub const MIR = struct { functions: ArrayList(Function) = .{}, + allocator: Allocator, const GPRegister = struct { value: ?x86_64.GPRegister = null, + size: Size, can_omit_if_present: bool = true, }; const Stack = struct { @@ -33,6 +77,7 @@ pub const MIR = struct { const Function = struct { instructions: ArrayList(MIR.Instruction) = .{}, blocks: AutoArrayHashMap(ir.BasicBlock.Index, u32) = .{}, + instruction_byte_offset: u32 = 0, }; const Instruction = struct { operands: [4]Operand, @@ -53,32 +98,22 @@ pub const MIR = struct { sub, syscall, ud2, + xor, }; - - fn new(id: Id, reference: ir.Instruction.Index, operands: []const Operand) MIR.Instruction { - var out_operands: [4]Operand = undefined; - @memset(std.mem.asBytes(&out_operands), 0); - @memcpy(out_operands[0..operands.len], operands); - - return .{ - .operands = out_operands, - .ir = reference, - .id = id, - .operand_count = @intCast(operands.len), - }; - } - - const Operand = union(enum) { - gp_register: MIR.GPRegister, - fp_register, - memory, - relative: union(enum) { + }; + const Operand = union(enum) { + gp_register: MIR.GPRegister, + fp_register, + memory, + displacement: struct { + source: ir.BasicBlock.Index, + destination: union(enum) { block: ir.BasicBlock.Index, function: ir.Function.Index, }, - immediate: Compilation.Integer, - stack: Stack, - }; + }, + immediate: Compilation.Integer, + stack: Stack, }; const RegisterUse = union(enum) { @@ -88,41 +123,78 @@ pub const MIR = struct { syscall_param: x86_64.GPRegister, }; - fn movRegImm(function: *Function, allocator: Allocator, integer: Compilation.Integer, instruction_index: ir.Instruction.Index, use: RegisterUse) !void { + fn addInstruction(mir: *MIR, function: *Function, instruction_id: Instruction.Id, ir_instruction: ir.Instruction.Index, operands: []const Operand) !void { + var out_operands: [4]Operand = undefined; + @memset(std.mem.asBytes(&out_operands), 0); + @memcpy(out_operands[0..operands.len], operands); + + const instruction = MIR.Instruction{ + .operands = out_operands, + .ir = ir_instruction, + .id = instruction_id, + .operand_count = @intCast(operands.len), + }; + print("Adding instruction {s}\n", .{@tagName(instruction_id)}); + try function.instructions.append(mir.allocator, instruction); + } + + fn emitMovRegImm(mir: *MIR, function: *Function, integer: Compilation.Integer, instruction_index: ir.Instruction.Index, use: RegisterUse, register_size: Size) !void { if (integer.type.bit_count <= @bitSizeOf(u64)) { - switch (integer.type.signedness) { - .signed, .unsigned => { - if (integer.value <= std.math.maxInt(u32)) { - try function.instructions.append(allocator, MIR.Instruction.new(.mov, instruction_index, &.{ - .{ - .gp_register = .{ - .value = switch (use) { - .general => null, - .ret => .a, - .param => unreachable, - .syscall_param => |register| register, - }, - }, + if (integer.value == 0) { + const operand = .{ + .gp_register = .{ + .value = switch (use) { + .general => null, + .ret => .a, + .param => unreachable, + .syscall_param => |register| register, + }, + .size = register_size, + }, + }; + + try mir.addInstruction(function, .xor, instruction_index, &.{ + operand, + operand, + }); + } else if (integer.value <= std.math.maxInt(u32)) { + try mir.addInstruction(function, .mov, instruction_index, &.{ + .{ + .gp_register = .{ + .value = switch (use) { + .general => null, + .ret => .a, + .param => unreachable, + .syscall_param => |register| register, }, - .{ .immediate = integer }, - })); - } else { - unreachable; - } - }, + .size = .four, + }, + }, + .{ + .immediate = .{ + .value = integer.value, + .type = .{ + .signedness = integer.type.signedness, + .bit_count = 32, + }, + }, + }, + }); + } else { + unreachable; } } else { unreachable; } } - fn movRegStack(function: *Function, allocator: Allocator, use: RegisterUse, stack_reference: ir.StackReference, instruction_index: ir.Instruction.Index) !void { + fn emitMovRegStack(mir: *MIR, function: *Function, use: RegisterUse, stack_reference: ir.StackReference, instruction_index: ir.Instruction.Index) !void { if (stack_reference.size <= @sizeOf(u64)) { switch (stack_reference.size) { @sizeOf(u8) => unreachable, @sizeOf(u16) => unreachable, @sizeOf(u32) => { - try function.instructions.append(allocator, MIR.Instruction.new(.mov, instruction_index, &.{ + try mir.addInstruction(function, .mov, instruction_index, &.{ .{ .gp_register = .{ .value = switch (use) { @@ -131,6 +203,7 @@ pub const MIR = struct { .param => unreachable, .syscall_param => |syscall_register| syscall_register, }, + .size = Size.fromByteCount(@intCast(stack_reference.size)), }, }, .{ @@ -138,7 +211,7 @@ pub const MIR = struct { .offset = stack_reference.offset, }, }, - })); + }); }, @sizeOf(u64) => unreachable, else => unreachable, @@ -149,7 +222,9 @@ pub const MIR = struct { } pub fn generate(allocator: Allocator, intermediate: *ir.Result) !MIR { - var mir = MIR{}; + var mir = MIR{ + .allocator = allocator, + }; try mir.functions.ensureTotalCapacity(allocator, intermediate.functions.len); var ir_function_it = intermediate.functions.iterator(); @@ -163,17 +238,15 @@ pub const MIR = struct { if (ir_function.current_stack_offset > 0) { // TODO: switch on ABI - try function.instructions.append(allocator, MIR.Instruction.new(.push, ir.Instruction.Index.invalid, &.{ - .{ .gp_register = .{ .value = .bp } }, - })); + try mir.addInstruction(function, .push, ir.Instruction.Index.invalid, &.{.{ .gp_register = .{ .value = .bp, .size = .eight } }}); - try function.instructions.append(allocator, MIR.Instruction.new(.mov, ir.Instruction.Index.invalid, &.{ - .{ .gp_register = .{ .value = .bp } }, - .{ .gp_register = .{ .value = .sp } }, - })); + try mir.addInstruction(function, .mov, ir.Instruction.Index.invalid, &.{ + .{ .gp_register = .{ .value = .bp, .size = .eight } }, + .{ .gp_register = .{ .value = .sp, .size = .eight } }, + }); - try function.instructions.append(allocator, MIR.Instruction.new(.sub, ir.Instruction.Index.invalid, &.{ - .{ .gp_register = .{ .value = .sp } }, + try mir.addInstruction(function, .sub, ir.Instruction.Index.invalid, &.{ + .{ .gp_register = .{ .value = .sp, .size = .eight } }, .{ .immediate = Compilation.Integer{ .value = ir_function.current_stack_offset, @@ -183,7 +256,7 @@ pub const MIR = struct { }, }, }, - })); + }); } for (basic_block.instructions.items) |instruction_index| { @@ -191,21 +264,24 @@ pub const MIR = struct { switch (instruction.*) { .jump => |jump_index| { const jump = intermediate.jumps.get(jump_index); - try function.instructions.append(allocator, MIR.Instruction.new(.jmp, instruction_index, &.{ - .{ .relative = .{ .block = jump.destination } }, - })); + try mir.addInstruction(function, .jmp, instruction_index, &.{ + .{ .displacement = .{ + .source = jump.source, + .destination = .{ .block = jump.destination }, + } }, + }); }, .copy => |copy_value_index| { const copy_value = intermediate.values.get(copy_value_index); switch (copy_value.*) { - .integer => |integer| try movRegImm(function, allocator, integer, instruction_index, .general), + .integer => |integer| try mir.emitMovRegImm(function, integer, instruction_index, .general, Size.fromBitCount(integer.type.bit_count)), else => |t| @panic(@tagName(t)), } }, .ret => |ret_value_index| { const ret_value = intermediate.values.get(ret_value_index); switch (ret_value.*) { - .integer => |integer| try movRegImm(function, allocator, integer, instruction_index, .ret), + .integer => |integer| try mir.emitMovRegImm(function, integer, instruction_index, .ret, Size.fromBitCount(integer.type.bit_count)), else => |t| @panic(@tagName(t)), } @@ -213,14 +289,21 @@ pub const MIR = struct { unreachable; } - try function.instructions.append(allocator, MIR.Instruction.new(.ret, instruction_index, &.{})); + try mir.addInstruction(function, .ret, instruction_index, &.{}); }, .call => |call_value_index| { // TODO: args const call = intermediate.calls.get(call_value_index); - try function.instructions.append(allocator, MIR.Instruction.new(.call, instruction_index, &.{ - .{ .relative = .{ .function = call.function } }, - })); + try mir.addInstruction(function, .call, instruction_index, &.{ + .{ + .displacement = .{ + .source = block_index, + .destination = .{ + .function = call.function, + }, + }, + }, + }); }, .store => |store_index| { const store = intermediate.stores.get(store_index); @@ -231,9 +314,7 @@ pub const MIR = struct { const stack_reference = intermediate.stack_references.get(stack_reference_index); print("stack ref: {}\n", .{stack_reference}); switch (source_value.*) { - .call => |call_index| { - try storeFunctionCallResult(allocator, function, intermediate, instruction_index, stack_reference.*, call_index); - }, + .call => |call_index| try mir.emitStoreForFunctionCallResult(function, intermediate, instruction_index, stack_reference.*, call_index), else => |t| @panic(@tagName(t)), } }, @@ -246,18 +327,18 @@ pub const MIR = struct { for (syscall.arguments.items, syscall_registers[0..syscall.arguments.items.len]) |argument_index, syscall_register| { const argument = intermediate.values.get(argument_index).*; switch (argument) { - .integer => |integer| try movRegImm(function, allocator, integer, instruction_index, .{ .syscall_param = syscall_register }), + .integer => |integer| try mir.emitMovRegImm(function, integer, instruction_index, .{ .syscall_param = syscall_register }, Size.eight), .stack_reference => |stack_reference_index| { const stack_reference = intermediate.stack_references.get(stack_reference_index); - try movRegStack(function, allocator, .{ .syscall_param = syscall_register }, stack_reference.*, instruction_index); + try mir.emitMovRegStack(function, .{ .syscall_param = syscall_register }, stack_reference.*, instruction_index); }, else => |t| @panic(@tagName(t)), } } - try function.instructions.append(allocator, MIR.Instruction.new(.syscall, instruction_index, &.{})); + try mir.addInstruction(function, .syscall, instruction_index, &.{}); }, - .@"unreachable" => try function.instructions.append(allocator, MIR.Instruction.new(.ud2, instruction_index, &.{})), + .@"unreachable" => try mir.addInstruction(function, .ud2, instruction_index, &.{}), else => |t| @panic(@tagName(t)), } } @@ -267,6 +348,85 @@ pub const MIR = struct { return mir; } + pub fn allocateRegisters(mir: *MIR, allocator: Allocator, intermediate: *ir.Result) !void { + for (mir.functions.items) |*function| { + var register_allocator = try RegisterAllocator.init(allocator); + var instructions_to_delete = AutoArrayHashMap(u32, void){}; + for (function.instructions.items, 0..) |*instruction, instruction_index| { + print("#{} {s}\n", .{ instruction_index, @tagName(instruction.id) }); + var allocated_gp_register: ?x86_64.GPRegister = null; + for (instruction.getOperands()) |*operand| { + switch (operand.*) { + .displacement, .immediate, .stack => {}, + .gp_register => |gp_register| switch (instruction.ir.valid) { + true => operand.gp_register.value = blk: { + const value_index = getValueFromInstruction(intermediate, instruction.ir); + + if (gp_register.value) |expected_register| { + if (register_allocator.gp_registers.used.get(expected_register)) |allocated_value| { + switch (value_index.eq(allocated_value)) { + // TODO delete the instruction + true => if (allocated_gp_register == null) unreachable else { + assert(allocated_gp_register.? == expected_register); + }, + // _ = try instructions_to_delete.getOrPut(allocator, @intCast(instruction_index)), //.append(allocator, @intCast(instruction_index)), + false => unreachable, + } + } else { + if (register_allocator.gp_registers.free.get(expected_register)) |_| { + try register_allocator.gp_registers.allocate(allocator, expected_register, intermediate, instruction.*, value_index); + allocated_gp_register = expected_register; + } else { + unreachable; + } + } + + break :blk expected_register; + } else { + for (register_allocator.gp_registers.free.keys()) |register| { + try register_allocator.gp_registers.allocate(allocator, register, intermediate, instruction.*, value_index); + break :blk register; + } else { + unreachable; + } + } + }, + false => {}, + }, + else => |t| @panic(@tagName(t)), + } + } + } + + if (instructions_to_delete.keys().len > 0) { + var next_instruction_to_delete_index: usize = 0; + print("Instructions to delete: ", .{}); + for (instructions_to_delete.keys()) |instruction| { + print("#{}, ", .{instruction}); + } + print("\n", .{}); + for (function.blocks.keys(), function.blocks.values()) |*block_index, *instruction_offset| { + _ = block_index; + while (instructions_to_delete.keys()[next_instruction_to_delete_index] <= instruction_offset.*) : (next_instruction_to_delete_index += 1) { + unreachable; + } + } + + var removed_instruction_count: usize = 0; + for (instructions_to_delete.keys()) |instruction_to_delete_index| { + _ = function.instructions.orderedRemove(instruction_to_delete_index - removed_instruction_count); + removed_instruction_count += 1; + } + + print("Instructions after deletion\n", .{}); + for (function.instructions.items, 0..) |instruction, index| { + print("#{} {s}\n", .{ index, @tagName(instruction.id) }); + } + print("\n", .{}); + } + } + } + const RegisterAllocator = struct { gp_registers: RegisterSet(x86_64.GPRegister) = .{}, @@ -314,63 +474,16 @@ pub const MIR = struct { return value_index; } - pub fn allocateRegisters(mir: *MIR, allocator: Allocator, intermediate: *ir.Result) !void { - for (mir.functions.items) |*function| { - var register_allocator = try RegisterAllocator.init(allocator); - for (function.instructions.items) |*instruction| { - for (instruction.getOperands()) |*operand| { - switch (operand.*) { - .relative, .immediate, .stack => {}, - .gp_register => |gp_register| switch (instruction.ir.valid) { - true => operand.gp_register.value = blk: { - const value_index = getValueFromInstruction(intermediate, instruction.ir); - - if (gp_register.value) |expected_register| { - if (register_allocator.gp_registers.used.get(expected_register)) |allocated_value| { - const allocated = intermediate.values.get(allocated_value); - const value = intermediate.values.get(value_index); - print("\nAllocated: {}.\nValue: {}\n", .{ allocated.*, value.* }); - switch (value_index.eq(allocated_value)) { - true => {}, - false => unreachable, - } - } else { - if (register_allocator.gp_registers.free.get(expected_register)) |_| { - try register_allocator.gp_registers.allocate(allocator, expected_register, intermediate, instruction.*, value_index); - } else { - unreachable; - } - } - - break :blk expected_register; - } else { - for (register_allocator.gp_registers.free.keys()) |register| { - try register_allocator.gp_registers.allocate(allocator, register, intermediate, instruction.*, value_index); - break :blk register; - } else { - unreachable; - } - } - }, - false => {}, - }, - else => |t| @panic(@tagName(t)), - } - } - } - } - } - - fn storeFunctionCallResult(allocator: Allocator, function: *MIR.Function, intermediate: *ir.Result, instruction: ir.Instruction.Index, stack_reference: ir.StackReference, call_index: ir.Call.Index) !void { + fn emitStoreForFunctionCallResult(mir: *MIR, function: *MIR.Function, intermediate: *ir.Result, instruction: ir.Instruction.Index, stack_reference: ir.StackReference, call_index: ir.Call.Index) !void { _ = call_index; _ = intermediate; if (stack_reference.size <= @sizeOf(u64)) { switch (stack_reference.size) { @sizeOf(u8) => unreachable, @sizeOf(u16) => unreachable, - @sizeOf(u32) => try function.instructions.append(allocator, MIR.Instruction.new(.mov, instruction, &.{ - .{ .stack = .{ .offset = stack_reference.offset } }, .{ .gp_register = .{ .value = .a } }, - })), + @sizeOf(u32) => try mir.addInstruction(function, .mov, instruction, &.{ + .{ .stack = .{ .offset = stack_reference.offset } }, .{ .gp_register = .{ .value = .a, .size = Size.fromByteCount(@intCast(stack_reference.size)) } }, + }), @sizeOf(u64) => unreachable, else => unreachable, } @@ -378,80 +491,339 @@ pub const MIR = struct { unreachable; } } -}; -pub fn selectInstruction(instruction_selector: *InstructionSelector, function: *InstructionSelector.Function, intermediate: *ir.Result, instruction: ir.Instruction) !void { - switch (instruction) { - .copy => |copy_value| { - _ = copy_value; - unreachable; - }, - .@"unreachable" => _ = try function.addInstruction(instruction_selector.allocator, .{ .ud2 = {} }), - .load => |load_index| { - const load = intermediate.loads.get(load_index).*; - const load_value = intermediate.values.get(load.value).*; - switch (load_value) { - .integer => |integer| { - _ = integer; - unreachable; - }, - else => |t| @panic(@tagName(t)), - } - unreachable; - }, - .syscall => |syscall_index| { - const syscall = intermediate.syscalls.get(syscall_index); - for (syscall.arguments.items, syscall_registers[0..syscall.arguments.items.len]) |argument_index, syscall_register| { - const argument = intermediate.values.get(argument_index).*; - switch (argument) { - .integer => |integer| { - if (integer.value == 0) { - _ = try function.addInstruction(instruction_selector.allocator, .{ - .xor_rm_r = .{ - .destination = @enumFromInt(@intFromEnum(syscall_register)), - .source = @enumFromInt(@intFromEnum(syscall_register)), - .size = .four, - .direct = true, - }, - }); - } else if (integer.value <= std.math.maxInt(u32)) { - _ = try function.addInstruction(instruction_selector.allocator, .{ - .mov_r_imm = .{ - .register_size = .four, - .register = @enumFromInt(@intFromEnum(syscall_register)), - .immediate = argument_index, - .immediate_size = .four, - }, - }); - // TODO - } else unreachable; + pub fn encode(mir: *const MIR, intermediate: *const ir.Result) !emit.Result { + var local_relocations = ArrayList(LocalRelocation){}; + var global_relocations = ArrayList(GlobalRelocation){}; + var block_index: usize = 0; + + var image = try emit.Result.create(); + + for (mir.functions.items) |*function| { + local_relocations.clearRetainingCapacity(); + function.instruction_byte_offset = @intCast(image.sections.text.index); + for (function.instructions.items, 0..) |*instruction, instruction_index| { + if (block_index < function.blocks.values().len) { + if (instruction_index == function.blocks.values()[block_index]) { + function.blocks.values()[block_index] = @intCast(image.sections.text.index); + block_index += 1; + } + } + + const operands = instruction.getOperands(); + switch (operands.len) { + 0 => switch (instruction.id) { + .ret => image.appendCodeByte(0xc3), + .syscall => image.appendCode(&.{ 0x0f, 0x05 }), + .ud2 => image.appendCode(&.{ 0x0f, 0x0b }), + else => |t| @panic(@tagName(t)), }, - else => |t| @panic(@tagName(t)), + 1 => switch (instruction.id) { + .call => { + const operand = operands[0]; + assert(operand == .displacement); + switch (operand.displacement.destination) { + .function => |ir_function_index| { + const function_index = ir_function_index.uniqueInteger(); + const current_function_index = @divExact(@intFromPtr(function) - @intFromPtr(mir.functions.items.ptr), @sizeOf(MIR.Function)); + + if (current_function_index < function_index) { + try mir.encodeRel32InstructionWithRelocation(&image, RelocationType.global, .{ + .relocations = &global_relocations, + .target = function_index, + .opcode = 0xe8, + }); + } else { + try encodeRel32Instruction(&image, .{ + .target = mir.functions.items[function_index].instruction_byte_offset, + .opcode = 0xe8, + }); + } + }, + else => |t| @panic(@tagName(t)), + } + }, + .jmp => { + const operand = operands[0]; + assert(operand == .displacement); + if (operand.displacement.source.uniqueInteger() < operand.displacement.destination.block.uniqueInteger()) { + try mir.encodeRel32InstructionWithRelocation(&image, RelocationType.local, .{ + .relocations = &local_relocations, + .target = operand.displacement.destination.block, + .opcode = 0xe9, + }); + } else if (operand.displacement.source.uniqueInteger() == operand.displacement.destination.block.uniqueInteger()) { + unreachable; + } else { + unreachable; + } + }, + .push => { + const operand = operands[0]; + switch (operand) { + .gp_register => |gp_register| { + assert(gp_register.size == .eight); + if (Rex.create(.{ .rm = gp_register.value.? })) |rex_byte| { + image.appendCodeByte(@bitCast(rex_byte)); + } + const opcode = @as(u8, 0x50) | @as(u3, @truncate(@intFromEnum(gp_register.value.?))); + image.appendCodeByte(opcode); + }, + else => |t| @panic(@tagName(t)), + } + }, + else => |t| @panic(@tagName(t)), + }, + 2 => switch (operands[0]) { + .gp_register => |dst_gp_register| switch (operands[1]) { + .gp_register => |src_gp_register| { + assert(dst_gp_register.size == src_gp_register.size); + const direct = true; + const rm = dst_gp_register.value.?; + const reg = src_gp_register.value.?; + + if (Rex.create(.{ + .rm = rm, + .reg = reg, + .rm_size = dst_gp_register.size, + })) |rex_byte| { + image.appendCodeByte(@bitCast(rex_byte)); + } + + const opcode_option: [2]u8 = switch (instruction.id) { + .mov => .{ 0x88, 0x89 }, + .xor => .{ 0x30, 0x31 }, + else => |t| @panic(@tagName(t)), + }; + + image.appendCodeByte(switch (dst_gp_register.size) { + .one => opcode_option[0], + else => opcode_option[1], + }); + + const modrm = ModRm{ + .rm = @truncate(@intFromEnum(rm)), + .reg = @truncate(@intFromEnum(reg)), + .mod = @as(u2, @intFromBool(direct)) << 1 | @intFromBool(direct), + }; + image.appendCodeByte(@bitCast(modrm)); + }, + .immediate => |src_immediate| { + assert(src_immediate.type.bit_count % @bitSizeOf(u8) == 0); + print("DST GP register: {}. SRC immediate: {}\n", .{ dst_gp_register, src_immediate }); + switch (instruction.id) { + .mov => switch (@intFromEnum(dst_gp_register.value.?) > std.math.maxInt(u3)) { + true => unreachable, // Use RM encoding + false => { + const opcode: u8 = switch (dst_gp_register.size) { + .one => 0xb0, + else => 0xb8, + }; + const opcode_byte = opcode | @intFromEnum(dst_gp_register.value.?); + image.appendCodeByte(opcode_byte); + const immediate_byte_count = @as(usize, 1) << @intFromEnum(dst_gp_register.size); + print("Immediate byte count: {}\n", .{immediate_byte_count}); + for (std.mem.asBytes(&src_immediate.value)[0..immediate_byte_count]) |immediate_byte| { + image.appendCodeByte(immediate_byte); + } + }, + }, + else => { + const immediate8_different_than_register = src_immediate.type.bit_count == 8 and dst_gp_register.size != .one; + switch (dst_gp_register.value.? == .a and !immediate8_different_than_register) { + true => unreachable, + false => { + const reg: x86_64.GPRegister = @enumFromInt(@as(u3, switch (instruction.id) { + .sub => 5, + else => |t| @panic(@tagName(t)), + })); + if (Rex.create(.{ .reg = reg, .rm = dst_gp_register.value.?, .rm_size = dst_gp_register.size })) |rex_byte| { + image.appendCodeByte(@bitCast(rex_byte)); + } + const opcode: u8 = switch (immediate8_different_than_register) { + true => switch (instruction.id) { + .sub => 0x83, + else => |t| @panic(@tagName(t)), + }, + false => unreachable, + }; + image.appendCodeByte(opcode); + + const rm = dst_gp_register.value.?; + const direct = true; + const modrm = ModRm{ + .rm = @truncate(@intFromEnum(rm)), + .reg = @truncate(@intFromEnum(reg)), + .mod = @as(u2, @intFromBool(direct)) << 1 | @intFromBool(direct), + }; + image.appendCodeByte(@bitCast(modrm)); + + switch (Size.fromBitCount(src_immediate.type.bit_count)) { + inline else => |size| image.appendCode(std.mem.asBytes(&@as(size.toInteger(), @intCast(src_immediate.value)))), + } + }, + } + }, + } + }, + .stack => |src_stack| { + const stack_offset = -@as(i64, @intCast(src_stack.offset)); + for (std.mem.asBytes(&stack_offset)) |stack_byte| { + print("0x{x} ", .{stack_byte}); + } + print("\n", .{}); + const displacement_bytes: u3 = if (std.math.cast(i8, stack_offset)) |_| @sizeOf(i8) else if (std.math.cast(i32, stack_offset)) |_| @sizeOf(i32) else unreachable; + + const reg = dst_gp_register.value.?; + if (Rex.create(.{ .reg = reg, .rm_size = dst_gp_register.size })) |rex_byte| { + image.appendCodeByte(@bitCast(rex_byte)); + } + const opcode_option: [2]u8 = switch (instruction.id) { + .mov => .{ 0x8a, 0x8b }, + else => |t| @panic(@tagName(t)), + }; + + image.appendCodeByte(switch (dst_gp_register.size) { + .one => opcode_option[0], + else => opcode_option[1], + }); + + const rm = x86_64.GPRegister.bp; + const modrm = ModRm{ + .rm = @truncate(@intFromEnum(rm)), + .reg = @truncate(@intFromEnum(reg)), + .mod = 0b01, + }; + image.appendCodeByte(@bitCast(modrm)); + + image.appendCode(std.mem.asBytes(&stack_offset)[0..displacement_bytes]); + }, + else => |t| @panic(@tagName(t)), + }, + .stack => |dst_stack| switch (operands[1]) { + .gp_register => |src_gp_register| switch (instruction.id) { + .mov => { + const stack_offset = -@as(i64, @intCast(dst_stack.offset)); + for (std.mem.asBytes(&stack_offset)) |stack_byte| { + print("0x{x} ", .{stack_byte}); + } + print("\n", .{}); + const displacement_bytes: u3 = if (std.math.cast(i8, stack_offset)) |_| @sizeOf(i8) else if (std.math.cast(i32, stack_offset)) |_| @sizeOf(i32) else unreachable; + + const reg = src_gp_register.value.?; + if (Rex.create(.{ .reg = reg, .rm_size = src_gp_register.size })) |rex_byte| { + image.appendCodeByte(@bitCast(rex_byte)); + } + const opcode_option: [2]u8 = switch (instruction.id) { + .mov => .{ 0x88, 0x89 }, + else => |t| @panic(@tagName(t)), + }; + + image.appendCodeByte(switch (src_gp_register.size) { + .one => opcode_option[0], + else => opcode_option[1], + }); + + const rm = x86_64.GPRegister.bp; + const modrm = ModRm{ + .rm = @truncate(@intFromEnum(rm)), + .reg = @truncate(@intFromEnum(reg)), + .mod = 0b01, + }; + image.appendCodeByte(@bitCast(modrm)); + + image.appendCode(std.mem.asBytes(&stack_offset)[0..displacement_bytes]); + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }, + else => |t| @panic(@tagName(t)), + }, + 3 => switch (instruction.id) { + else => |t| @panic(@tagName(t)), + }, + 4 => switch (instruction.id) { + else => |t| @panic(@tagName(t)), + }, + else => unreachable, } } - _ = try function.addInstruction(instruction_selector.allocator, .{ - .syscall = {}, - }); - }, - .phi => unreachable, - .ret => unreachable, - .jump => |jump_index| { - const jump = intermediate.jumps.get(jump_index); - const instruction_index = try function.addInstruction(instruction_selector.allocator, .{ - .jmp_rel = Displacement{ - .size = .one, - .source = @intCast(function.block_map.get(jump.source) orelse unreachable), - .destination = @intCast(function.block_map.get(jump.destination) orelse unreachable), - .instruction_index = @intCast(function.instructions.items.len), - }, - }); - try function.relocations.append(instruction_selector.allocator, instruction_index); - }, - .call => unreachable, - .store => unreachable, + for (local_relocations.items) |relocation| { + const source_offset: i64 = relocation.instruction_byte_offset + relocation.instruction_length; + const destination_offset: i64 = function.blocks.get(relocation.target).?; + print("Source: {}. Destination: {}\n", .{ source_offset, destination_offset }); + const displacement_offset = destination_offset - source_offset; + const address_to_address = @intFromPtr(&image.sections.text.content[relocation.instruction_byte_offset + relocation.source_address_writer_offset]); + switch (relocation.size) { + inline .one, .four => |relocation_size| { + const RelocationInteger = switch (relocation_size) { + .one => i8, + .four => i32, + else => @compileError("Unreachable"), + }; + const ptr: *align(1) RelocationInteger = @ptrFromInt(address_to_address); + ptr.* = @intCast(displacement_offset); + }, + else => unreachable, + } + } + + print("Function code:\n", .{}); + for (image.sections.text.content[function.instruction_byte_offset..][0..image.sections.text.index]) |code_byte| { + print("0x{x:0>2} ", .{code_byte}); + } + print("\n", .{}); + } + + for (global_relocations.items) |global_relocation| { + _ = global_relocation; + unreachable; + } + + image.entry_point = mir.functions.items[intermediate.entry_point].instruction_byte_offset; + + return image; } -} + + fn encodeRel32Instruction(image: *emit.Result, arguments: struct { + target: u32, + opcode: u8, + }) !void { + const instruction_byte_offset: u32 = @intCast(image.sections.text.index); + const instruction_length = 5; + + const source_offset: i64 = instruction_byte_offset + instruction_length; + const destination_offset: i64 = arguments.target; + const offset: i32 = @intCast(destination_offset - source_offset); + + image.appendCodeByte(arguments.opcode); + image.appendCode(std.mem.asBytes(&offset)); + } + + fn encodeRel32InstructionWithRelocation(mir: *const MIR, image: *emit.Result, comptime relocation_type: RelocationType, arguments: struct { + relocations: *ArrayList(Relocation(RelocationIndex(relocation_type))), + target: RelocationIndex(relocation_type), + opcode: u8, + }) !void { + const instruction_byte_offset = image.sections.text.index; + const source_address_writer_offset = 1; + const instruction_length = 5; + const size = .four; + + image.appendCodeByte(arguments.opcode); + image.appendCode(&(.{0} ** 4)); + + try arguments.relocations.append(mir.allocator, .{ + .instruction_byte_offset = @intCast(instruction_byte_offset), + .source_address_writer_offset = source_address_writer_offset, + .instruction_length = instruction_length, + .target = arguments.target, + .size = size, + }); + } +}; const RegisterImmediate = struct { immediate: ir.Value.Index, @@ -486,247 +858,38 @@ const RmAndRexArguments = packed struct { bit64: bool, sib: bool, }; - -// fn computeRmAndRex(args: RmAndRexArguments) RmResult { -// _ = register_memory_register; -// const rex_byte = Rex{ -// .b = @intFromEnum(args.rm) > std.math.maxInt(u3), -// .x = args.sib, -// .r = @intFromEnum(args.reg) > std.math.maxInt(u3), -// .w = args.bit64, -// }; -// var rex_byte = std.mem.zeroes(Rex); -// if (@intFromEnum(rm) > std.math.maxInt(u3)) -// } -fn emitImmediate(result: *emit.Result, intermediate: *ir.Result, value_index: ir.Value.Index, size: Size) void { - const value = intermediate.values.get(value_index); - const integer = value.integer.value; - const integer_bytes = switch (size) { - .one => std.mem.asBytes(&@as(u8, @intCast(integer))), - .two => std.mem.asBytes(&@as(u16, @intCast(integer))), - .four => std.mem.asBytes(&@as(u32, @intCast(integer))), - .eight => std.mem.asBytes(&@as(u64, @intCast(integer))), - }; - result.appendCode(integer_bytes); -} - const ModRm = packed struct(u8) { rm: u3, reg: u3, mod: u2, }; -pub fn emitInstruction(result: *emit.Result, instruction: Instruction, intermediate: *ir.Result) void { - switch (instruction) { - inline .xor_rm_r => |register_memory_register, tag| { - const rm = register_memory_register.destination; - const reg = register_memory_register.source; - const rex_byte = Rex{ - .b = @intFromEnum(rm) > std.math.maxInt(u3), - .x = false, //args.sib, - .r = @intFromEnum(reg) > std.math.maxInt(u3), - .w = register_memory_register.size == .eight, - }; - - if (@as(u4, @truncate(@as(u8, @bitCast(rex_byte)))) != 0) { - result.appendCodeByte(@bitCast(rex_byte)); - } - - const modrm = ModRm{ - .rm = @truncate(@intFromEnum(rm)), - .reg = @truncate(@intFromEnum(reg)), - .mod = @as(u2, @intFromBool(register_memory_register.direct)) << 1 | @intFromBool(register_memory_register.direct), - }; - // _ = modrm; - const opcode = tag.getOpcode(&.{ - .{ - .register_memory = .{ - .value = register_memory_register.destination, - .size = register_memory_register.size, - .direct = register_memory_register.direct, - }, - }, - .{ - .register = .{ - .value = register_memory_register.source, - .size = register_memory_register.size, - }, - }, - }); - - result.appendCode(opcode); - result.appendCodeByte(@bitCast(modrm)); - }, - inline .mov_r_imm => |register_immediate, tag| { - const opcode = tag.getOpcode(&.{ - .{ - .register = .{ - .value = register_immediate.register, - .size = register_immediate.register_size, - }, - }, - .{ - .immediate = register_immediate.immediate_size, - }, - }); - assert(opcode.len == 1); - const opcode_byte = opcode[0] | @intFromEnum(register_immediate.register); - result.appendCodeByte(opcode_byte); - emitImmediate(result, intermediate, register_immediate.immediate, register_immediate.immediate_size); - }, - .jmp_rel => unreachable, - inline .syscall, .ud2 => |_, tag| { - const opcode = tag.getOpcode(&.{}); - result.appendCode(opcode); - }, - // else => unreachable, - } -} - -pub const Instruction = union(Id) { - xor_rm_r: RegisterMemoryRegister, - mov_r_imm: RegisterImmediate, - jmp_rel: Displacement, - // jmp_rel_8: LocalRelative, - // mov_reg_imm32: struct { - // destination: GPRegister, - // source: u32, - // }, - // xor_reg32_reg32: struct { - // destination: GPRegister, - // source: GPRegister, - // }, - syscall, - ud2, - - const Id = enum { - xor_rm_r, - mov_r_imm, - jmp_rel, - // mov_reg_imm32, - // xor_reg32_reg32, - syscall, - ud2, - - fn getOpcode(comptime instruction: Instruction.Id, operands: []const Operand) []const u8 { - return switch (instruction) { - .mov_r_imm => switch (operands[0].register.size) { - .one => &.{0xb0}, - .two, .four, .eight => &.{0xb8}, - }, - .syscall => &.{ 0x0f, 0x05 }, - .ud2 => &.{ 0x0f, 0x0b }, - .xor_rm_r => switch (operands[0].register_memory.size) { - .one => &.{0x30}, - .two, .four, .eight => &.{0x31}, - }, - .jmp_rel => switch (operands[0].displacement.size) { - .one => unreachable, - .four => unreachable, - else => unreachable, - }, - }; - } - }; - - const Operand = union(enum) { - displacement, - register: struct { - value: GPRegister, - size: Size, - }, - // TODO - register_memory: struct { - value: GPRegister, - size: Size, - direct: bool, - }, - immediate: Size, - - const Id = enum { - displacement, - register, - register_memory, - immediate, - }; - }; - - pub const descriptors = blk: { - var result = std.EnumArray(Instruction.Id, Instruction.Descriptor).initUndefined(); - result.getPtr(.jmp_rel_8).* = Instruction.Descriptor.new(&.{0xeb}, &[_]Instruction.Operand{rel8}); - result.getPtr(.mov_reg_imm32).* = Instruction.Descriptor.new(&.{0xb8}, &[_]Instruction.Operand{ reg32, imm32 }); - result.getPtr(.xor_reg_reg).* = Instruction.Descriptor.new(&.{0x31}, &[_]Instruction.Operand{ reg32, reg32 }); - result.getPtr(.syscall).* = Instruction.Descriptor.new(&.{ 0x0f, 0x05 }, &.{}); - result.getPtr(.ud2).* = Instruction.Descriptor.new(&.{ 0x0f, 0x0b }, &.{}); - break :blk result; - }; - - const Descriptor = struct { - operands: [4]Operand, - operand_count: u3, - operand_offset: u5, - size: u8, - opcode: [3]u8, - opcode_byte_count: u8, - - fn getOperands(descriptor: Descriptor) []const Operand { - return descriptor.operands[0..descriptor.operand_count]; - } - - fn new(opcode_bytes: []const u8, operands: []const Operand) Descriptor { - // TODO: prefixes - var result = Descriptor{ - .operands = undefined, - .operand_count = @intCast(operands.len), - .operand_offset = opcode_bytes.len, - .size = opcode_bytes.len, - .opcode = .{ 0, 0 }, - .opcode_byte_count = opcode_bytes.len, - }; - - if (opcode_bytes.len == 1) { - result.opcode[1] = opcode_bytes[0]; - } else for (opcode_bytes, result.opcode[0..opcode_bytes.len]) |opcode_byte, *out_opcode| { - out_opcode.* = opcode_byte; - } - - for (operands, result.operands[0..operands.len]) |operand, *out_operand| { - out_operand.* = operand; - result.size += operand.size; - } - - return result; - } - }; -}; -const LocalRelative = struct { - instruction: Instruction.Id, - source: u16, - destination: u16, - offset_in_block: u16, -}; - -const rel8 = Instruction.Operand{ - .type = .relative, - .size = @sizeOf(u8), -}; - -const reg32 = Instruction.Operand{ - .type = .register, - .size = @sizeOf(u32), -}; - -const imm32 = Instruction.Operand{ - .type = .immediate, - .size = @sizeOf(u32), -}; - const Rex = packed struct(u8) { b: bool, x: bool, r: bool, w: bool, fixed: u4 = 0b0100, + + fn create(args: struct { + rm: ?GPRegister = null, + reg: ?GPRegister = null, + sib: bool = false, + rm_size: ?Size = null, + }) ?Rex { + const rex_byte = Rex{ + .b = if (args.rm) |rm| @intFromEnum(rm) > std.math.maxInt(u3) else false, + .x = args.sib, + .r = if (args.reg) |reg| @intFromEnum(reg) > std.math.maxInt(u3) else false, + .w = if (args.rm_size) |rm_size| rm_size == .eight else false, + }; + + if (@as(u4, @truncate(@as(u8, @bitCast(rex_byte)))) != 0) { + return rex_byte; + } else { + return null; + } + } }; const GPRegister = enum(u4) { @@ -748,15 +911,4 @@ const GPRegister = enum(u4) { r15 = 15, }; -// pub const BasicGPRegister = enum(u3) { -// a = 0, -// c = 1, -// d = 2, -// b = 3, -// sp = 4, -// bp = 5, -// si = 6, -// di = 7, -// }; - const syscall_registers = [7]GPRegister{ .a, .di, .si, .d, .r10, .r8, .r9 }; diff --git a/src/frontend/semantic_analyzer.zig b/src/frontend/semantic_analyzer.zig index 1ad2c3d..ce75292 100644 --- a/src/frontend/semantic_analyzer.zig +++ b/src/frontend/semantic_analyzer.zig @@ -518,37 +518,6 @@ const Analyzer = struct { } } - fn analyzeDeclaration(analyzer: *Analyzer, scope_index: Scope.Index, declaration: *Declaration) !Value.Index { - _ = scope_index; - _ = declaration; - _ = analyzer; - // switch (declaration.*) { - // .unresolved => |node_index| { - // const declaration_node = analyzer.nodes[node_index.unwrap()]; - // return switch (declaration_node.id) { - // .simple_variable_declaration => blk: { - // const expect_type = switch (declaration_node.left.valid) { - // true => unreachable, - // false => @unionInit(ExpectType, "none", {}), - // }; - // - // const initialization_expression = try analyzer.expression(scope, expect_type, declaration_node.right); - // const value = analyzer.module.values.get(initialization_expression); - // if (value.is_comptime and value.is_const) { - // break :blk initialization_expression; - // } - // - // unreachable; - // }, - // else => |t| @panic(@tagName(t)), - // }; - // }, - // .struct_type => unreachable, - // } - - @panic("TODO: analyzeDeclaration"); - } - fn structType(analyzer: *Analyzer, value: *Value, parent_scope_index: Scope.Index, index: Node.Index, file_index: File.Index) !Type.Index { var node_buffer: [2]Node.Index = undefined; // We have the file because this might be the first file @@ -892,7 +861,23 @@ pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, }, }); - return analyzeExistingPackage(value_allocation.ptr, compilation, module, package); + const result = analyzeExistingPackage(value_allocation.ptr, compilation, module, package); + + var decl_iterator = module.declarations.iterator(); + while (decl_iterator.nextPointer()) |decl| { + if (equal(u8, decl.name, "_start")) { + const value = module.values.get(decl.init_value); + module.entry_point = switch (value.*) { + .function => |function_index| function_index.uniqueInteger(), + else => |t| @panic(@tagName(t)), + }; + break; + } + } else { + @panic("Entry point not found"); + } + + return result; } pub fn analyzeExistingPackage(value: *Value, compilation: *Compilation, module: *Module, package: *Package) !Type.Index {