| ... | @@ -34,7 +34,6 @@ pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std. | ... | @@ -34,7 +34,6 @@ pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std. |
| 34 | .code = code, | 34 | .code = code, |
| 35 | .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(code.allocator), | 35 | .inst_table = std.AutoHashMap(*ir.Inst, Function.MCValue).init(code.allocator), |
| 36 | .errors = std.ArrayList(ErrorMsg).init(code.allocator), | 36 | .errors = std.ArrayList(ErrorMsg).init(code.allocator), |
| 37 | .constants = std.ArrayList(ir.TypedValue).init(code.allocator), | | |
| 38 | }; | 37 | }; |
| 39 | defer function.inst_table.deinit(); | 38 | defer function.inst_table.deinit(); |
| 40 | defer function.errors.deinit(); | 39 | defer function.errors.deinit(); |
| ... | @@ -49,6 +48,7 @@ pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std. | ... | @@ -49,6 +48,7 @@ pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std. |
| 49 | }; | 48 | }; |
| 50 | try function.inst_table.putNoClobber(inst, new_inst); | 49 | try function.inst_table.putNoClobber(inst, new_inst); |
| 51 | } | 50 | } |
| | 51 | |
| 52 | return Symbol{ .errors = function.errors.toOwnedSlice() }; | 52 | return Symbol{ .errors = function.errors.toOwnedSlice() }; |
| 53 | }, | 53 | }, |
| 54 | else => @panic("TODO implement generateSymbol for non-function types"), | 54 | else => @panic("TODO implement generateSymbol for non-function types"), |
| ... | @@ -60,10 +60,6 @@ const Function = struct { | ... | @@ -60,10 +60,6 @@ const Function = struct { |
| 60 | mod_fn: *const ir.Module.Fn, | 60 | mod_fn: *const ir.Module.Fn, |
| 61 | code: *std.ArrayList(u8), | 61 | code: *std.ArrayList(u8), |
| 62 | inst_table: std.AutoHashMap(*ir.Inst, MCValue), | 62 | inst_table: std.AutoHashMap(*ir.Inst, MCValue), |
| 63 | /// Constants are embedded within functions (at the end, after `ret`) | | |
| 64 | /// so that they are independently updateable. | | |
| 65 | /// This is a list of constants that must be appended to the symbol after `ret`. | | |
| 66 | constants: std.ArrayList(ir.TypedValue), | | |
| 67 | errors: std.ArrayList(ErrorMsg), | 63 | errors: std.ArrayList(ErrorMsg), |
| 68 | | 64 | |
| 69 | const MCValue = union(enum) { | 65 | const MCValue = union(enum) { |
| ... | @@ -71,8 +67,8 @@ const Function = struct { | ... | @@ -71,8 +67,8 @@ const Function = struct { |
| 71 | unreach, | 67 | unreach, |
| 72 | /// A pointer-sized integer that fits in a register. | 68 | /// A pointer-sized integer that fits in a register. |
| 73 | immediate: u64, | 69 | immediate: u64, |
| 74 | /// Refers to the index into `constants` field of `Function`. | 70 | /// The constant was emitted into the code, at this offset. |
| 75 | local_const_ptr: usize, | 71 | embedded_in_code: usize, |
| 76 | }; | 72 | }; |
| 77 | | 73 | |
| 78 | fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue { | 74 | fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue { |
| ... | @@ -88,13 +84,46 @@ const Function = struct { | ... | @@ -88,13 +84,46 @@ const Function = struct { |
| 88 | // TODO change this to call the panic function | 84 | // TODO change this to call the panic function |
| 89 | switch (self.module.target.cpu.arch) { | 85 | switch (self.module.target.cpu.arch) { |
| 90 | .i386, .x86_64 => { | 86 | .i386, .x86_64 => { |
| 91 | try self.code.append(0xcc); // x86 int3 | 87 | try self.code.append(0xcc); // int3 |
| 92 | }, | 88 | }, |
| 93 | else => return self.fail(src, "TODO implement panic for {}", .{self.module.target.cpu.arch}), | 89 | else => return self.fail(src, "TODO implement panic for {}", .{self.module.target.cpu.arch}), |
| 94 | } | 90 | } |
| 95 | return .unreach; | 91 | return .unreach; |
| 96 | } | 92 | } |
| 97 | | 93 | |
| | 94 | fn genRet(self: *Function, src: usize) !void { |
| | 95 | // TODO change this to call the panic function |
| | 96 | switch (self.module.target.cpu.arch) { |
| | 97 | .i386, .x86_64 => { |
| | 98 | try self.code.append(0xc3); // ret |
| | 99 | }, |
| | 100 | else => return self.fail(src, "TODO implement ret for {}", .{self.module.target.cpu.arch}), |
| | 101 | } |
| | 102 | } |
| | 103 | |
| | 104 | fn genRelativeFwdJump(self: *Function, src: usize, amount: u32) !void { |
| | 105 | switch (self.module.target.cpu.arch) { |
| | 106 | .i386, .x86_64 => { |
| | 107 | if (amount <= std.math.maxInt(u8)) { |
| | 108 | try self.code.resize(self.code.items.len + 2); |
| | 109 | self.code.items[self.code.items.len - 2] = 0xeb; |
| | 110 | self.code.items[self.code.items.len - 1] = @intCast(u8, amount); |
| | 111 | } else if (amount <= std.math.maxInt(u16)) { |
| | 112 | try self.code.resize(self.code.items.len + 3); |
| | 113 | self.code.items[self.code.items.len - 3] = 0xe9; // jmp rel16 |
| | 114 | const imm_ptr = self.code.items[self.code.items.len - 2 ..][0..2]; |
| | 115 | mem.writeIntLittle(u16, imm_ptr, @intCast(u16, amount)); |
| | 116 | } else { |
| | 117 | try self.code.resize(self.code.items.len + 5); |
| | 118 | self.code.items[self.code.items.len - 5] = 0xea; // jmp rel32 |
| | 119 | const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4]; |
| | 120 | mem.writeIntLittle(u32, imm_ptr, amount); |
| | 121 | } |
| | 122 | }, |
| | 123 | else => return self.fail(src, "TODO implement relative forward jump for {}", .{self.module.target.cpu.arch}), |
| | 124 | } |
| | 125 | } |
| | 126 | |
| 98 | fn genAsm(self: *Function, inst: *ir.Inst.Assembly) !MCValue { | 127 | fn genAsm(self: *Function, inst: *ir.Inst.Assembly) !MCValue { |
| 99 | return self.fail(inst.base.src, "TODO machine code gen assembly", .{}); | 128 | return self.fail(inst.base.src, "TODO machine code gen assembly", .{}); |
| 100 | } | 129 | } |
| ... | @@ -105,23 +134,51 @@ const Function = struct { | ... | @@ -105,23 +134,51 @@ const Function = struct { |
| 105 | } | 134 | } |
| 106 | | 135 | |
| 107 | fn resolveInst(self: *Function, inst: *ir.Inst) !MCValue { | 136 | fn resolveInst(self: *Function, inst: *ir.Inst) !MCValue { |
| | 137 | if (self.inst_table.getValue(inst)) |mcv| { |
| | 138 | return mcv; |
| | 139 | } |
| 108 | if (inst.cast(ir.Inst.Constant)) |const_inst| { | 140 | if (inst.cast(ir.Inst.Constant)) |const_inst| { |
| 109 | switch (inst.ty.zigTypeTag()) { | 141 | const mcvalue = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val }); |
| 110 | .Int => { | 142 | try self.inst_table.putNoClobber(inst, mcvalue); |
| 111 | const info = inst.ty.intInfo(self.module.target); | 143 | return mcvalue; |
| 112 | const ptr_bits = self.module.target.cpu.arch.ptrBitWidth(); | | |
| 113 | if (info.bits > ptr_bits or info.signed) { | | |
| 114 | return self.fail(inst.src, "TODO const int bigger than ptr and signed int", .{}); | | |
| 115 | } | | |
| 116 | return MCValue{ .immediate = const_inst.val.toUnsignedInt() }; | | |
| 117 | }, | | |
| 118 | else => return self.fail(inst.src, "TODO implement const of type '{}'", .{inst.ty}), | | |
| 119 | } | | |
| 120 | } else { | 144 | } else { |
| 121 | return self.inst_table.getValue(inst).?; | 145 | return self.inst_table.getValue(inst).?; |
| 122 | } | 146 | } |
| 123 | } | 147 | } |
| 124 | | 148 | |
| | 149 | fn genTypedValue(self: *Function, src: usize, typed_value: ir.TypedValue) !MCValue { |
| | 150 | switch (typed_value.ty.zigTypeTag()) { |
| | 151 | .Pointer => { |
| | 152 | const ptr_elem_type = typed_value.ty.elemType(); |
| | 153 | switch (ptr_elem_type.zigTypeTag()) { |
| | 154 | .Array => { |
| | 155 | // TODO more checks to make sure this can be emitted as a string literal |
| | 156 | const bytes = try typed_value.val.toAllocatedBytes(self.code.allocator); |
| | 157 | defer self.code.allocator.free(bytes); |
| | 158 | const smaller_len = std.math.cast(u32, bytes.len) catch |
| | 159 | return self.fail(src, "TODO handle a larger string constant", .{}); |
| | 160 | |
| | 161 | // Emit the string literal directly into the code; jump over it. |
| | 162 | const offset = self.code.items.len; |
| | 163 | try self.genRelativeFwdJump(src, smaller_len); |
| | 164 | try self.code.appendSlice(bytes); |
| | 165 | return MCValue{ .embedded_in_code = offset }; |
| | 166 | }, |
| | 167 | else => |t| return self.fail(src, "TODO implement emitTypedValue for pointer to '{}'", .{@tagName(t)}), |
| | 168 | } |
| | 169 | }, |
| | 170 | .Int => { |
| | 171 | const info = typed_value.ty.intInfo(self.module.target); |
| | 172 | const ptr_bits = self.module.target.cpu.arch.ptrBitWidth(); |
| | 173 | if (info.bits > ptr_bits or info.signed) { |
| | 174 | return self.fail(src, "TODO const int bigger than ptr and signed int", .{}); |
| | 175 | } |
| | 176 | return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; |
| | 177 | }, |
| | 178 | else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}), |
| | 179 | } |
| | 180 | } |
| | 181 | |
| 125 | fn fail(self: *Function, src: usize, comptime format: []const u8, args: var) error{ CodegenFail, OutOfMemory } { | 182 | fn fail(self: *Function, src: usize, comptime format: []const u8, args: var) error{ CodegenFail, OutOfMemory } { |
| 126 | @setCold(true); | 183 | @setCold(true); |
| 127 | const msg = try std.fmt.allocPrint(self.errors.allocator, format, args); | 184 | const msg = try std.fmt.allocPrint(self.errors.allocator, format, args); |