From bca2f024cd2232a089be5b741ac44e80cba07979 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Sat, 30 Sep 2023 12:51:58 -0600 Subject: [PATCH] ir for main function --- src/Compilation.zig | 32 +++- src/backend/emit.zig | 7 +- src/backend/intermediate_representation.zig | 161 ++++++++++++++++++-- src/backend/x86_64.zig | 69 +++------ src/frontend/semantic_analyzer.zig | 5 + 5 files changed, 209 insertions(+), 65 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 6a7e357..0c56802 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -76,6 +76,20 @@ pub const Type = union(enum) { pub const List = BlockList(@This()); pub const Index = List.Index; pub const Allocation = List.Allocation; + + pub fn getSize(type_info: Type) u64 { + return switch (type_info) { + .integer => |integer| integer.getSize(), + else => |t| @panic(@tagName(t)), + }; + } + + pub fn getAlignment(type_info: Type) u64 { + return switch (type_info) { + .integer => |integer| @min(16, integer.getSize()), + else => |t| @panic(@tagName(t)), + }; + } }; pub const Integer = struct { @@ -85,6 +99,10 @@ pub const Integer = struct { unsigned = 0, signed = 1, }; + + pub fn getSize(integer: Integer) u64 { + return integer.bit_count / @bitSizeOf(u8) + @intFromBool(integer.bit_count % @bitSizeOf(u8) != 0); + } }; /// A scope contains a bunch of declarations @@ -201,6 +219,7 @@ pub const Syscall = struct { pub const Call = struct { value: Value.Index, arguments: ArgumentList.Index, + type: Type.Index, pub const List = BlockList(@This()); pub const Index = List.Index; pub const Allocation = List.Allocation; @@ -251,11 +270,11 @@ pub const Value = union(enum) { }; } - pub fn getType(value: *Value) !void { - switch (value.*) { + pub fn getType(value: *Value, module: *Module) Type.Index { + return switch (value.*) { + .call => |call_index| module.calls.get(call_index).type, else => |t| @panic(@tagName(t)), - } - unreachable; + }; } }; @@ -490,10 +509,7 @@ pub fn compileModule(compilation: *Compilation, descriptor: Module.Descriptor) ! var ir = try intermediate_representation.initialize(compilation, module, packages[0], main_declaration); - switch (@import("builtin").cpu.arch) { - .x86_64 => |arch| try emit.get(arch).initialize(compilation.base_allocator, &ir), - else => {}, - } + try emit.get(.x86_64).initialize(compilation.base_allocator, &ir); } fn generateAST() !void {} diff --git a/src/backend/emit.zig b/src/backend/emit.zig index ddc073c..5299d57 100644 --- a/src/backend/emit.zig +++ b/src/backend/emit.zig @@ -46,13 +46,18 @@ pub const Result = struct { break :blk @as([*]align(0x1000) u8, @ptrCast(@alignCast(try windows.VirtualAlloc(null, size, windows.MEM_COMMIT | windows.MEM_RESERVE, windows.PAGE_EXECUTE_READWRITE))))[0..size]; }, .linux, .macos => |os_tag| blk: { + const jit = switch (os_tag) { + .macos => 0x800, + .linux => 0, + else => unreachable, + }; const execute_flag: switch (os_tag) { .linux => u32, .macos => c_int, else => unreachable, } = if (flags.executable) std.os.PROT.EXEC else 0; const protection_flags: u32 = @intCast(std.os.PROT.READ | std.os.PROT.WRITE | execute_flag); - const mmap_flags = std.os.MAP.ANONYMOUS | std.os.MAP.PRIVATE; + const mmap_flags = std.os.MAP.ANONYMOUS | std.os.MAP.PRIVATE | jit; break :blk std.os.mmap(null, size, protection_flags, mmap_flags, -1, 0); }, diff --git a/src/backend/intermediate_representation.zig b/src/backend/intermediate_representation.zig index e817d4a..643609f 100644 --- a/src/backend/intermediate_representation.zig +++ b/src/backend/intermediate_representation.zig @@ -10,16 +10,20 @@ const Package = Compilation.Package; const data_structures = @import("../data_structures.zig"); const ArrayList = data_structures.ArrayList; const BlockList = data_structures.BlockList; +const AutoHashMap = data_structures.AutoHashMap; pub const Result = struct { - functions: BlockList(Function) = .{}, blocks: BlockList(BasicBlock) = .{}, + calls: BlockList(Call) = .{}, + functions: BlockList(Function) = .{}, instructions: BlockList(Instruction) = .{}, jumps: BlockList(Jump) = .{}, - values: BlockList(Value) = .{}, - syscalls: BlockList(Syscall) = .{}, loads: BlockList(Load) = .{}, phis: BlockList(Phi) = .{}, + stores: BlockList(Store) = .{}, + syscalls: BlockList(Syscall) = .{}, + values: BlockList(Value) = .{}, + stack_references: BlockList(StackReference) = .{}, }; pub fn initialize(compilation: *Compilation, module: *Module, package: *Package, main_file: Compilation.Type.Index) !Result { @@ -62,10 +66,12 @@ pub const BasicBlock = struct { }; pub const Instruction = union(enum) { + call: Call.Index, jump: Jump.Index, load: Load.Index, phi: Phi.Index, ret: Ret, + store: Store.Index, syscall: Syscall.Index, @"unreachable", @@ -106,9 +112,34 @@ const Load = struct { pub const Index = List.Index; }; +const Store = struct { + source: Value.Index, + destination: StackReference.Index, + pub const List = BlockList(@This()); + pub const Index = List.Index; +}; + +const StackReference = struct { + size: u64, + alignment: u64, + offset: u64, + pub const List = BlockList(@This()); + pub const Index = List.Index; +}; + +const Call = struct { + function: Function.Index, + + pub const List = BlockList(@This()); + pub const Index = List.Index; + pub const Allocation = List.Allocation; +}; + pub const Value = union(enum) { integer: Integer, load: Load.Index, + call: Call.Index, + stack_reference: StackReference.Index, pub const List = BlockList(@This()); pub const Index = List.Index; @@ -116,6 +147,8 @@ pub const Value = union(enum) { return switch (value) { .integer => false, .load => true, + .call => true, + .stack_reference => true, }; } }; @@ -138,11 +171,15 @@ pub const Builder = struct { current_basic_block: BasicBlock.Index = BasicBlock.Index.invalid, current_function_index: Function.Index = Function.Index.invalid, return_phi_node: Instruction.Index = Instruction.Index.invalid, + current_stack_offset: usize = 0, + stack_map: AutoHashMap(Compilation.Declaration.Index, StackReference.Index) = .{}, fn function(builder: *Builder, sema_function: Compilation.Function) !void { builder.current_function_index = (try builder.ir.functions.append(builder.allocator, .{})).index; // TODO: arguments builder.current_basic_block = try builder.newBlock(); + builder.current_stack_offset = 0; + builder.stack_map = .{}; const return_type = builder.module.types.get(builder.module.function_prototypes.get(sema_function.prototype).return_type); const is_noreturn = return_type.* == .noreturn; @@ -301,28 +338,77 @@ pub const Builder = struct { }); }, .declaration => |sema_declaration_index| { - _ = sema_declaration_index; - unreachable; + const sema_declaration = builder.module.declarations.get(sema_declaration_index); + assert(sema_declaration.scope_type == .local); + const sema_init_value = builder.module.values.get(sema_declaration.init_value); + const declaration_type = builder.module.types.get(sema_init_value.getType(builder.module)); + const size = declaration_type.getSize(); + const alignment = declaration_type.getAlignment(); + const stack_offset = switch (size > 0) { + true => builder.allocateStack(size, alignment), + false => 0, + }; + var value_index = try builder.emitValue(sema_declaration.init_value); + const value = builder.ir.values.get(value_index); + print("Value: {}\n", .{value.*}); + value_index = switch (value.isInMemory()) { + false => try builder.load(value_index), + true => value_index, + }; + + if (stack_offset > 0) { + _ = try builder.store(.{ + .source = value_index, + .destination = try builder.stackReference(stack_offset, declaration_type.*, sema_declaration_index), + }); + } }, else => |t| @panic(@tagName(t)), } } } + fn stackReference(builder: *Builder, stack_offset: u64, t: Compilation.Type, value: Compilation.Declaration.Index) !StackReference.Index { + const stack_reference_allocation = try builder.ir.stack_references.append(builder.allocator, .{ + .offset = stack_offset, + .size = t.getSize(), + .alignment = t.getAlignment(), + }); + + const index = stack_reference_allocation.index; + + try builder.stack_map.put(builder.allocator, value, index); + + return index; + } + + fn store(builder: *Builder, descriptor: Store) !void { + const store_allocation = try builder.ir.stores.append(builder.allocator, descriptor); + _ = try builder.append(.{ + .store = store_allocation.index, + }); + } + + fn allocateStack(builder: *Builder, size: u64, alignment: u64) u64 { + builder.current_stack_offset = std.mem.alignForward(u64, builder.current_stack_offset, alignment); + builder.current_stack_offset += size; + return builder.current_stack_offset; + } + fn load(builder: *Builder, value_index: Value.Index) !Value.Index { print("Doing load!\n", .{}); - const load_index = try builder.ir.loads.append(builder.allocator, .{ + const load_allocation = try builder.ir.loads.append(builder.allocator, .{ .value = value_index, }); const instruction_index = try builder.append(.{ - .load = load_index, + .load = load_allocation.index, }); _ = instruction_index; const result = try builder.ir.values.append(builder.allocator, .{ - .load = load_index, + .load = load_allocation.index, }); - return result; + return result.index; } fn emitValue(builder: *Builder, sema_value_index: Compilation.Value.Index) !Value.Index { @@ -335,14 +421,65 @@ pub const Builder = struct { .sign = false, }, })).index, + .call => |sema_call_index| { + const sema_call = builder.module.calls.get(sema_call_index); + const argument_list_index = sema_call.arguments; + if (argument_list_index.valid) { + unreachable; + } + + const call_index = try builder.call(.{ + .function = switch (builder.module.values.get(sema_call.value).*) { + .function => |function_index| .{ + .index = function_index.index, + .block = function_index.block, + }, + else => |t| @panic(@tagName(t)), + }, + }); + + _ = try builder.append(.{ + .call = call_index, + }); + + const value_allocation = try builder.ir.values.append(builder.allocator, .{ + .call = call_index, + }); + + return value_allocation.index; + }, + .declaration_reference => |sema_declaration_index| { + const sema_declaration = builder.module.declarations.get(sema_declaration_index); + const sema_init_value = builder.module.values.get(sema_declaration.init_value); + const init_type = sema_init_value.getType(builder.module); + _ = init_type; + switch (sema_declaration.scope_type) { + .local => { + const stack_reference = builder.stack_map.get(sema_declaration_index).?; + const value = try builder.ir.values.append(builder.allocator, .{ + .stack_reference = stack_reference, + }); + return value.index; + }, + .global => unreachable, + } + // switch (sema_declaration.*) { + // else => |t| @panic(@tagName(t)), + // } + }, else => |t| @panic(@tagName(t)), }; } - fn jump(builder: *Builder, jump_descriptor: Jump) !Jump.Index { - const destination_block = builder.ir.blocks.get(jump_descriptor.destination); + fn call(builder: *Builder, descriptor: Call) !Call.Index { + const call_allocation = try builder.ir.calls.append(builder.allocator, descriptor); + return call_allocation.index; + } + + fn jump(builder: *Builder, descriptor: Jump) !Jump.Index { + const destination_block = builder.ir.blocks.get(descriptor.destination); assert(!destination_block.sealed); - const jump_allocation = try builder.ir.jumps.append(builder.allocator, jump_descriptor); + const jump_allocation = try builder.ir.jumps.append(builder.allocator, descriptor); return jump_allocation.index; } diff --git a/src/backend/x86_64.zig b/src/backend/x86_64.zig index 9f473ec..1963e19 100644 --- a/src/backend/x86_64.zig +++ b/src/backend/x86_64.zig @@ -54,23 +54,6 @@ pub fn selectInstruction(instruction_selector: *InstructionSelector, function: * }); // TODO } else unreachable; - // if (integer.value == 0) { - // try function.instructions.append(instruction_selector.allocator, .{ - // .xor_reg32_reg32 = .{ - // .destination = syscall_register, - // .source = syscall_register, - // }, - // }); - // } else if (integer.value < std.math.maxInt(u32)) { - // try function.instructions.append(instruction_selector.allocator, .{ - // .mov_reg_imm32 = .{ - // .destination = syscall_register, - // .source = @intCast(integer.value), - // }, - // }); - // } else { - // unreachable; - // } }, else => |t| @panic(@tagName(t)), } @@ -83,14 +66,14 @@ pub fn selectInstruction(instruction_selector: *InstructionSelector, function: * .phi => unreachable, .ret => unreachable, .jump => |jump_index| { - _ = jump_index; - // const jump = intermediate.jumps.get(jump_index); - // const relocation = LocalRelative{ - // .instruction = .jmp_rel_8, - // .source = @intCast(function.block_map.get(jump.source) orelse unreachable), - // .destination = @intCast(function.block_map.get(jump.destination) orelse unreachable), - // .offset_in_block = function.block_byte_count, - // }; + const jump = intermediate.jumps.get(jump_index); + const relocation = Displacement{ + .size = .one, + .source = @intCast(function.block_map.get(jump.source) orelse unreachable), + .destination = @intCast(function.block_map.get(jump.destination) orelse unreachable), + .offset_in_block = function.block_byte_count, + }; + _ = relocation; // const index = function.instructions.items.len; // try function.relocations.append(instruction_selector.allocator, @intCast(index)); // try function.instructions.append(instruction_selector.allocator, .{ @@ -98,6 +81,8 @@ pub fn selectInstruction(instruction_selector: *InstructionSelector, function: * // }); unreachable; }, + .call => unreachable, + .store => unreachable, } } @@ -115,6 +100,13 @@ const RegisterMemoryRegister = struct { direct: bool, }; +const Displacement = struct { + size: Size, + source: u16, + destination: u16, + offset_in_block: u16, +}; + const RmResult = struct { rex: Rex, mod_rm: ModRm, @@ -215,24 +207,7 @@ pub fn emitInstruction(result: *emit.Result, instruction: Instruction, intermedi result.appendCodeByte(opcode_byte); emitImmediate(result, intermediate, register_immediate.immediate, register_immediate.immediate_size); }, - // .jmp_rel_8 => unreachable, //result.appendOnlyOpcodeSkipInstructionBytes(instruction), - // inline .mov_reg_imm32 => |content, tag| { - // _ = tag; - // _ = content; - // // const descriptor = instruction_descriptors.get(tag); - // // result.writeOpcode(descriptor.opcode); - // // result.appendCodeByte(descriptor.getOpcode()[0] | @intFromEnum(content.destination)); - // // result.appendCode(std.mem.asBytes(&content.source)); - // unreachable; - // }, - // inline .xor_reg32_reg32 => |content, tag| { - // _ = tag; - // _ = content; - // // const descriptor = instruction_descriptors.get(tag); - // // result.appendCodeByte(descriptor.getOpcode()[0]); - // // result.appendCodeByte(0xc0 | @as(u8, @intFromEnum(content.source)) << 4 | @intFromEnum(content.destination)); - // unreachable; - // }, + .jmp_rel => unreachable, inline .syscall, .ud2 => |_, tag| { const opcode = tag.getOpcode(&.{}); result.appendCode(opcode); @@ -244,6 +219,7 @@ pub fn emitInstruction(result: *emit.Result, instruction: Instruction, intermedi 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, @@ -259,7 +235,7 @@ pub const Instruction = union(Id) { const Id = enum { xor_rm_r, mov_r_imm, - // jmp_rel_8, + jmp_rel, // mov_reg_imm32, // xor_reg32_reg32, syscall, @@ -277,6 +253,11 @@ pub const Instruction = union(Id) { .one => &.{0x30}, .two, .four, .eight => &.{0x31}, }, + .jmp_rel => switch (operands[0].displacement.size) { + .one => unreachable, + .four => unreachable, + else => unreachable, + }, }; } }; diff --git a/src/frontend/semantic_analyzer.zig b/src/frontend/semantic_analyzer.zig index 438cd95..289862b 100644 --- a/src/frontend/semantic_analyzer.zig +++ b/src/frontend/semantic_analyzer.zig @@ -396,10 +396,15 @@ const Analyzer = struct { .call_one => blk: { const this_value_node_index = node.left; const this_value_allocation = try analyzer.unresolvedAllocate(scope_index, ExpectType.none, this_value_node_index); + const value_type = switch (this_value_allocation.ptr.*) { + .function => |function_index| analyzer.module.function_prototypes.get(analyzer.module.functions.get(function_index).prototype).return_type, + else => |t| @panic(@tagName(t)), + }; const call_allocation = try analyzer.module.calls.append(analyzer.allocator, .{ .value = this_value_allocation.index, .arguments = ArgumentList.Index.invalid, + .type = value_type, }); break :blk .{ .call = call_allocation.index,