authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-17 09:33:56-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-20 13:12:20-07:00
logef9aeb6ac415348e16f04913839002929064c91e
treefffbaed7bab9523a61b31796003d1961b6ff55f2
parent596ca6cf70cf43c27e31bbcfc36bcdc70b13897a

stage2: codegen: refactor to always have comptime arch


2 files changed, 1050 insertions(+), 1043 deletions(-)

src-self-hosted/codegen.zig+1046-1042
...@@ -11,6 +11,8 @@ const ErrorMsg = Module.ErrorMsg;...@@ -11,6 +11,8 @@ const ErrorMsg = Module.ErrorMsg;
11const Target = std.Target;11const Target = std.Target;
12const Allocator = mem.Allocator;12const Allocator = mem.Allocator;
13const trace = @import("tracy.zig").trace;13const trace = @import("tracy.zig").trace;
14const x86_64 = @import("codegen/x86_64.zig");
15const x86 = @import("codegen/x86.zig");
1416
15/// The codegen-related data that is stored in `ir.Inst.Block` instructions.17/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
16pub const BlockData = struct {18pub const BlockData = struct {
...@@ -32,67 +34,75 @@ pub const Result = union(enum) {...@@ -32,67 +34,75 @@ pub const Result = union(enum) {
32 fail: *Module.ErrorMsg,34 fail: *Module.ErrorMsg,
33};35};
3436
37pub const GenerateSymbolError = error{
38 OutOfMemory,
39 /// A Decl that this symbol depends on had a semantic analysis failure.
40 AnalysisFail,
41};
42
35pub fn generateSymbol(43pub fn generateSymbol(
36 bin_file: *link.File.Elf,44 bin_file: *link.File.Elf,
37 src: usize,45 src: usize,
38 typed_value: TypedValue,46 typed_value: TypedValue,
39 code: *std.ArrayList(u8),47 code: *std.ArrayList(u8),
40) error{48) GenerateSymbolError!Result {
41 OutOfMemory,
42 /// A Decl that this symbol depends on had a semantic analysis failure.
43 AnalysisFail,
44}!Result {
45 const tracy = trace(@src());49 const tracy = trace(@src());
46 defer tracy.end();50 defer tracy.end();
4751
48 switch (typed_value.ty.zigTypeTag()) {52 switch (typed_value.ty.zigTypeTag()) {
49 .Fn => {53 .Fn => {
50 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;54 switch (bin_file.options.target.cpu.arch) {
5155 .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code),
52 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;56 .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code),
53 const param_types = try bin_file.allocator.alloc(Type, fn_type.fnParamLen());57 .aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code),
54 defer bin_file.allocator.free(param_types);58 .aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code),
55 fn_type.fnParamTypes(param_types);59 .aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code),
56 var mc_args = try bin_file.allocator.alloc(MCValue, param_types.len);60 .arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code),
57 defer bin_file.allocator.free(mc_args);61 .avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code),
5862 .bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code),
59 var branch_stack = std.ArrayList(Function.Branch).init(bin_file.allocator);63 .bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code),
60 defer {64 .hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code),
61 assert(branch_stack.items.len == 1);65 .mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code),
62 branch_stack.items[0].deinit(bin_file.allocator);66 .mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code),
63 branch_stack.deinit();67 .mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code),
64 }68 .mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code),
65 const branch = try branch_stack.addOne();69 .msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code),
66 branch.* = .{};70 .powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code),
6771 .powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code),
68 var function = Function{72 .powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code),
69 .gpa = bin_file.allocator,73 .r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code),
70 .target = &bin_file.options.target,74 .amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code),
71 .bin_file = bin_file,75 .riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code),
72 .mod_fn = module_fn,76 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code),
73 .code = code,77 .sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code),
74 .err_msg = null,78 .sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code),
75 .args = mc_args,79 .sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code),
76 .arg_index = 0,80 .s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code),
77 .branch_stack = &branch_stack,81 .tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code),
78 .src = src,82 .tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code),
79 };83 .thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code),
8084 .thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code),
81 const cc = fn_type.fnCallingConvention();85 .i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code),
82 branch.max_end_stack = function.resolveParameters(src, cc, param_types, mc_args) catch |err| switch (err) {86 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code),
83 error.CodegenFail => return Result{ .fail = function.err_msg.? },87 .xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code),
84 else => |e| return e,88 .nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code),
85 };89 .nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code),
8690 .le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code),
87 function.gen() catch |err| switch (err) {91 .le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code),
88 error.CodegenFail => return Result{ .fail = function.err_msg.? },92 .amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code),
89 else => |e| return e,93 .amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code),
90 };94 .hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code),
9195 .hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code),
92 if (function.err_msg) |em| {96 .spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code),
93 return Result{ .fail = em };97 .spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code),
94 } else {98 .kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code),
95 return Result{ .appended = {} };99 .shave => return Function(.shave).generateSymbol(bin_file, src, typed_value, code),
100 .lanai => return Function(.lanai).generateSymbol(bin_file, src, typed_value, code),
101 .wasm32 => return Function(.wasm32).generateSymbol(bin_file, src, typed_value, code),
102 .wasm64 => return Function(.wasm64).generateSymbol(bin_file, src, typed_value, code),
103 .renderscript32 => return Function(.renderscript32).generateSymbol(bin_file, src, typed_value, code),
104 .renderscript64 => return Function(.renderscript64).generateSymbol(bin_file, src, typed_value, code),
105 .ve => return Function(.ve).generateSymbol(bin_file, src, typed_value, code),
96 }106 }
97 },107 },
98 .Array => {108 .Array => {
...@@ -189,1101 +199,1095 @@ const InnerError = error{...@@ -189,1101 +199,1095 @@ const InnerError = error{
189 CodegenFail,199 CodegenFail,
190};200};
191201
192const MCValue = union(enum) {202fn Function(comptime arch: std.Target.Cpu.Arch) type {
193 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.203 return struct {
194 none,204 gpa: *Allocator,
195 /// Control flow will not allow this value to be observed.205 bin_file: *link.File.Elf,
196 unreach,206 target: *const std.Target,
197 /// No more references to this value remain.207 mod_fn: *const Module.Fn,
198 dead,208 code: *std.ArrayList(u8),
199 /// A pointer-sized integer that fits in a register.209 err_msg: ?*ErrorMsg,
200 immediate: u64,210 args: []MCValue,
201 /// The constant was emitted into the code, at this offset.211 arg_index: usize,
202 embedded_in_code: usize,212 src: usize,
203 /// The value is in a target-specific register. The value can213
204 /// be @intToEnum casted to the respective Reg enum.214 /// Whenever there is a runtime branch, we push a Branch onto this stack,
205 register: usize,215 /// and pop it off when the runtime branch joins. This provides an "overlay"
206 /// The value is in memory at a hard-coded address.216 /// of the table of mappings from instructions to `MCValue` from within the branch.
207 memory: u64,217 /// This way we can modify the `MCValue` for an instruction in different ways
208 /// The value is one of the stack variables.218 /// within different branches. Special consideration is needed when a branch
209 stack_offset: u64,219 /// joins with its parent, to make sure all instructions have the same MCValue
210 /// The value is in the compare flags assuming an unsigned operation,220 /// across each runtime branch upon joining.
211 /// with this operator applied on top of it.221 branch_stack: *std.ArrayList(Branch),
212 compare_flags_unsigned: std.math.CompareOperator,222
213 /// The value is in the compare flags assuming a signed operation,223 const MCValue = union(enum) {
214 /// with this operator applied on top of it.224 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
215 compare_flags_signed: std.math.CompareOperator,225 none,
216226 /// Control flow will not allow this value to be observed.
217 fn isMemory(mcv: MCValue) bool {227 unreach,
218 return switch (mcv) {228 /// No more references to this value remain.
219 .embedded_in_code, .memory, .stack_offset => true,229 dead,
220 else => false,230 /// A pointer-sized integer that fits in a register.
231 immediate: u64,
232 /// The constant was emitted into the code, at this offset.
233 embedded_in_code: usize,
234 /// The value is in a target-specific register.
235 register: Reg,
236 /// The value is in memory at a hard-coded address.
237 memory: u64,
238 /// The value is one of the stack variables.
239 stack_offset: u64,
240 /// The value is in the compare flags assuming an unsigned operation,
241 /// with this operator applied on top of it.
242 compare_flags_unsigned: std.math.CompareOperator,
243 /// The value is in the compare flags assuming a signed operation,
244 /// with this operator applied on top of it.
245 compare_flags_signed: std.math.CompareOperator,
246
247 fn isMemory(mcv: MCValue) bool {
248 return switch (mcv) {
249 .embedded_in_code, .memory, .stack_offset => true,
250 else => false,
251 };
252 }
253
254 fn isImmediate(mcv: MCValue) bool {
255 return switch (mcv) {
256 .immediate => true,
257 else => false,
258 };
259 }
260
261 fn isMutable(mcv: MCValue) bool {
262 return switch (mcv) {
263 .none => unreachable,
264 .unreach => unreachable,
265 .dead => unreachable,
266
267 .immediate,
268 .embedded_in_code,
269 .memory,
270 .compare_flags_unsigned,
271 .compare_flags_signed,
272 => false,
273
274 .register,
275 .stack_offset,
276 => true,
277 };
278 }
221 };279 };
222 }
223280
224 fn isImmediate(mcv: MCValue) bool {281 const Branch = struct {
225 return switch (mcv) {282 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},
226 .immediate => true,283
227 else => false,284 /// The key is an enum value of an arch-specific register.
285 registers: std.AutoHashMapUnmanaged(usize, RegisterAllocation) = .{},
286
287 /// Maps offset to what is stored there.
288 stack: std.AutoHashMapUnmanaged(usize, StackAllocation) = .{},
289 /// Offset from the stack base, representing the end of the stack frame.
290 max_end_stack: u32 = 0,
291 /// Represents the current end stack offset. If there is no existing slot
292 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
293 next_stack_offset: u32 = 0,
294
295 fn deinit(self: *Branch, gpa: *Allocator) void {
296 self.inst_table.deinit(gpa);
297 self.registers.deinit(gpa);
298 self.stack.deinit(gpa);
299 self.* = undefined;
300 }
228 };301 };
229 }
230302
231 fn isMutable(mcv: MCValue) bool {303 const RegisterAllocation = struct {
232 return switch (mcv) {304 inst: *ir.Inst,
233 .none => unreachable,
234 .unreach => unreachable,
235 .dead => unreachable,
236
237 .immediate,
238 .embedded_in_code,
239 .memory,
240 .compare_flags_unsigned,
241 .compare_flags_signed,
242 => false,
243
244 .register,
245 .stack_offset,
246 => true,
247 };305 };
248 }
249};
250306
251const Function = struct {307 const StackAllocation = struct {
252 gpa: *Allocator,308 inst: *ir.Inst,
253 bin_file: *link.File.Elf,309 size: u32,
254 target: *const std.Target,310 };
255 mod_fn: *const Module.Fn,
256 code: *std.ArrayList(u8),
257 err_msg: ?*ErrorMsg,
258 args: []MCValue,
259 arg_index: usize,
260 src: usize,
261311
262 /// Whenever there is a runtime branch, we push a Branch onto this stack,312 const Self = @This();
263 /// and pop it off when the runtime branch joins. This provides an "overlay"
264 /// of the table of mappings from instructions to `MCValue` from within the branch.
265 /// This way we can modify the `MCValue` for an instruction in different ways
266 /// within different branches. Special consideration is needed when a branch
267 /// joins with its parent, to make sure all instructions have the same MCValue
268 /// across each runtime branch upon joining.
269 branch_stack: *std.ArrayList(Branch),
270
271 const Branch = struct {
272 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},
273
274 /// The key is an enum value of an arch-specific register.
275 registers: std.AutoHashMapUnmanaged(usize, RegisterAllocation) = .{},
276
277 /// Maps offset to what is stored there.
278 stack: std.AutoHashMapUnmanaged(usize, StackAllocation) = .{},
279 /// Offset from the stack base, representing the end of the stack frame.
280 max_end_stack: u32 = 0,
281 /// Represents the current end stack offset. If there is no existing slot
282 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
283 next_stack_offset: u32 = 0,
284
285 fn deinit(self: *Branch, gpa: *Allocator) void {
286 self.inst_table.deinit(gpa);
287 self.registers.deinit(gpa);
288 self.stack.deinit(gpa);
289 self.* = undefined;
290 }
291 };
292313
293 const RegisterAllocation = struct {314 fn generateSymbol(
294 inst: *ir.Inst,315 bin_file: *link.File.Elf,
295 };316 src: usize,
317 typed_value: TypedValue,
318 code: *std.ArrayList(u8),
319 ) GenerateSymbolError!Result {
320 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
296321
297 const StackAllocation = struct {322 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
298 inst: *ir.Inst,323 const param_types = try bin_file.allocator.alloc(Type, fn_type.fnParamLen());
299 size: u32,324 defer bin_file.allocator.free(param_types);
300 };325 fn_type.fnParamTypes(param_types);
326 var mc_args = try bin_file.allocator.alloc(MCValue, param_types.len);
327 defer bin_file.allocator.free(mc_args);
301328
302 fn gen(self: *Function) !void {329 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
303 switch (self.target.cpu.arch) {330 defer {
304 .arm => return self.genArch(.arm),331 assert(branch_stack.items.len == 1);
305 .armeb => return self.genArch(.armeb),332 branch_stack.items[0].deinit(bin_file.allocator);
306 .aarch64 => return self.genArch(.aarch64),333 branch_stack.deinit();
307 .aarch64_be => return self.genArch(.aarch64_be),334 }
308 .aarch64_32 => return self.genArch(.aarch64_32),335 const branch = try branch_stack.addOne();
309 .arc => return self.genArch(.arc),336 branch.* = .{};
310 .avr => return self.genArch(.avr),
311 .bpfel => return self.genArch(.bpfel),
312 .bpfeb => return self.genArch(.bpfeb),
313 .hexagon => return self.genArch(.hexagon),
314 .mips => return self.genArch(.mips),
315 .mipsel => return self.genArch(.mipsel),
316 .mips64 => return self.genArch(.mips64),
317 .mips64el => return self.genArch(.mips64el),
318 .msp430 => return self.genArch(.msp430),
319 .powerpc => return self.genArch(.powerpc),
320 .powerpc64 => return self.genArch(.powerpc64),
321 .powerpc64le => return self.genArch(.powerpc64le),
322 .r600 => return self.genArch(.r600),
323 .amdgcn => return self.genArch(.amdgcn),
324 .riscv32 => return self.genArch(.riscv32),
325 .riscv64 => return self.genArch(.riscv64),
326 .sparc => return self.genArch(.sparc),
327 .sparcv9 => return self.genArch(.sparcv9),
328 .sparcel => return self.genArch(.sparcel),
329 .s390x => return self.genArch(.s390x),
330 .tce => return self.genArch(.tce),
331 .tcele => return self.genArch(.tcele),
332 .thumb => return self.genArch(.thumb),
333 .thumbeb => return self.genArch(.thumbeb),
334 .i386 => return self.genArch(.i386),
335 .x86_64 => return self.genArch(.x86_64),
336 .xcore => return self.genArch(.xcore),
337 .nvptx => return self.genArch(.nvptx),
338 .nvptx64 => return self.genArch(.nvptx64),
339 .le32 => return self.genArch(.le32),
340 .le64 => return self.genArch(.le64),
341 .amdil => return self.genArch(.amdil),
342 .amdil64 => return self.genArch(.amdil64),
343 .hsail => return self.genArch(.hsail),
344 .hsail64 => return self.genArch(.hsail64),
345 .spir => return self.genArch(.spir),
346 .spir64 => return self.genArch(.spir64),
347 .kalimba => return self.genArch(.kalimba),
348 .shave => return self.genArch(.shave),
349 .lanai => return self.genArch(.lanai),
350 .wasm32 => return self.genArch(.wasm32),
351 .wasm64 => return self.genArch(.wasm64),
352 .renderscript32 => return self.genArch(.renderscript32),
353 .renderscript64 => return self.genArch(.renderscript64),
354 .ve => return self.genArch(.ve),
355 }
356 }
357337
358 fn genArch(self: *Function, comptime arch: std.Target.Cpu.Arch) !void {338 var function = Self{
359 try self.code.ensureCapacity(self.code.items.len + 11);339 .gpa = bin_file.allocator,
360340 .target = &bin_file.options.target,
361 // push rbp341 .bin_file = bin_file,
362 // mov rbp, rsp342 .mod_fn = module_fn,
363 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x55, 0x48, 0x89, 0xe5 });343 .code = code,
364344 .err_msg = null,
365 // sub rsp, x345 .args = mc_args,
366 const stack_end = self.branch_stack.items[0].max_end_stack;346 .arg_index = 0,
367 if (stack_end > std.math.maxInt(i32)) {347 .branch_stack = &branch_stack,
368 return self.fail(self.src, "too much stack used in call parameters", .{});348 .src = src,
369 } else if (stack_end > std.math.maxInt(i8)) {349 };
370 // 48 83 ec xx sub rsp,0x10350
371 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x81, 0xec });351 const cc = fn_type.fnCallingConvention();
372 const x = @intCast(u32, stack_end);352 branch.max_end_stack = function.resolveParameters(src, cc, param_types, mc_args) catch |err| switch (err) {
373 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);353 error.CodegenFail => return Result{ .fail = function.err_msg.? },
374 } else if (stack_end != 0) {354 else => |e| return e,
375 // 48 81 ec xx xx xx xx sub rsp,0x80355 };
376 const x = @intCast(u8, stack_end);356
377 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x83, 0xec, x });357 function.gen() catch |err| switch (err) {
358 error.CodegenFail => return Result{ .fail = function.err_msg.? },
359 else => |e| return e,
360 };
361
362 if (function.err_msg) |em| {
363 return Result{ .fail = em };
364 } else {
365 return Result{ .appended = {} };
366 }
378 }367 }
379368
380 try self.genBody(self.mod_fn.analysis.success, arch);369 fn gen(self: *Self) !void {
381 }370 try self.code.ensureCapacity(self.code.items.len + 11);
371
372 // push rbp
373 // mov rbp, rsp
374 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x55, 0x48, 0x89, 0xe5 });
375
376 // sub rsp, x
377 const stack_end = self.branch_stack.items[0].max_end_stack;
378 if (stack_end > std.math.maxInt(i32)) {
379 return self.fail(self.src, "too much stack used in call parameters", .{});
380 } else if (stack_end > std.math.maxInt(i8)) {
381 // 48 83 ec xx sub rsp,0x10
382 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x81, 0xec });
383 const x = @intCast(u32, stack_end);
384 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);
385 } else if (stack_end != 0) {
386 // 48 81 ec xx xx xx xx sub rsp,0x80
387 const x = @intCast(u8, stack_end);
388 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x83, 0xec, x });
389 }
382390
383 fn genBody(self: *Function, body: ir.Body, comptime arch: std.Target.Cpu.Arch) InnerError!void {391 try self.genBody(self.mod_fn.analysis.success);
384 const inst_table = &self.branch_stack.items[0].inst_table;
385 for (body.instructions) |inst| {
386 const new_inst = try self.genFuncInst(inst, arch);
387 try inst_table.putNoClobber(self.gpa, inst, new_inst);
388 }392 }
389 }
390393
391 fn genFuncInst(self: *Function, inst: *ir.Inst, comptime arch: std.Target.Cpu.Arch) !MCValue {394 fn genBody(self: *Self, body: ir.Body) InnerError!void {
392 switch (inst.tag) {395 const inst_table = &self.branch_stack.items[0].inst_table;
393 .add => return self.genAdd(inst.cast(ir.Inst.Add).?, arch),396 for (body.instructions) |inst| {
394 .arg => return self.genArg(inst.cast(ir.Inst.Arg).?),397 const new_inst = try self.genFuncInst(inst);
395 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?, arch),398 try inst_table.putNoClobber(self.gpa, inst, new_inst);
396 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),399 }
397 .block => return self.genBlock(inst.cast(ir.Inst.Block).?, arch),
398 .br => return self.genBr(inst.cast(ir.Inst.Br).?, arch),
399 .breakpoint => return self.genBreakpoint(inst.src, arch),
400 .brvoid => return self.genBrVoid(inst.cast(ir.Inst.BrVoid).?, arch),
401 .call => return self.genCall(inst.cast(ir.Inst.Call).?, arch),
402 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?, arch),
403 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?, arch),
404 .constant => unreachable, // excluded from function bodies
405 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?, arch),
406 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?, arch),
407 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
408 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?, arch),
409 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?, arch),
410 .sub => return self.genSub(inst.cast(ir.Inst.Sub).?, arch),
411 .unreach => return MCValue{ .unreach = {} },
412 .not => return self.genNot(inst.cast(ir.Inst.Not).?, arch),
413 }400 }
414 }
415401
416 fn genNot(self: *Function, inst: *ir.Inst.Not, comptime arch: std.Target.Cpu.Arch) !MCValue {402 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {
417 // No side effects, so if it's unreferenced, do nothing.403 switch (inst.tag) {
418 if (inst.base.isUnused())404 .add => return self.genAdd(inst.cast(ir.Inst.Add).?),
419 return MCValue.dead;405 .arg => return self.genArg(inst.cast(ir.Inst.Arg).?),
420 const operand = try self.resolveInst(inst.args.operand);406 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),
421 switch (operand) {407 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
422 .dead => unreachable,408 .block => return self.genBlock(inst.cast(ir.Inst.Block).?),
423 .unreach => unreachable,409 .br => return self.genBr(inst.cast(ir.Inst.Br).?),
424 .compare_flags_unsigned => |op| return MCValue{410 .breakpoint => return self.genBreakpoint(inst.src),
425 .compare_flags_unsigned = switch (op) {411 .brvoid => return self.genBrVoid(inst.cast(ir.Inst.BrVoid).?),
426 .gte => .lt,412 .call => return self.genCall(inst.cast(ir.Inst.Call).?),
427 .gt => .lte,413 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?),
428 .neq => .eq,414 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?),
429 .lt => .gte,415 .constant => unreachable, // excluded from function bodies
430 .lte => .gt,416 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?),
431 .eq => .neq,417 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?),
432 },418 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
433 },419 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?),
434 .compare_flags_signed => |op| return MCValue{420 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?),
435 .compare_flags_signed = switch (op) {421 .sub => return self.genSub(inst.cast(ir.Inst.Sub).?),
436 .gte => .lt,422 .unreach => return MCValue{ .unreach = {} },
437 .gt => .lte,423 .not => return self.genNot(inst.cast(ir.Inst.Not).?),
438 .neq => .eq,424 }
439 .lt => .gte,
440 .lte => .gt,
441 .eq => .neq,
442 },
443 },
444 else => {},
445 }425 }
446426
447 switch (arch) {427 fn genNot(self: *Self, inst: *ir.Inst.Not) !MCValue {
448 .x86_64 => {428 // No side effects, so if it's unreferenced, do nothing.
449 var imm = ir.Inst.Constant{429 if (inst.base.isUnused())
450 .base = .{430 return MCValue.dead;
451 .tag = .constant,431 const operand = try self.resolveInst(inst.args.operand);
452 .deaths = 0,432 switch (operand) {
453 .ty = inst.args.operand.ty,433 .dead => unreachable,
454 .src = inst.args.operand.src,434 .unreach => unreachable,
435 .compare_flags_unsigned => |op| return MCValue{
436 .compare_flags_unsigned = switch (op) {
437 .gte => .lt,
438 .gt => .lte,
439 .neq => .eq,
440 .lt => .gte,
441 .lte => .gt,
442 .eq => .neq,
455 },443 },
456 .val = Value.initTag(.bool_true),444 },
457 };445 .compare_flags_signed => |op| return MCValue{
458 return try self.genX8664BinMath(&inst.base, inst.args.operand, &imm.base, 6, 0x30);446 .compare_flags_signed = switch (op) {
459 },447 .gte => .lt,
460 else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),448 .gt => .lte,
461 }449 .neq => .eq,
462 }450 .lt => .gte,
451 .lte => .gt,
452 .eq => .neq,
453 },
454 },
455 else => {},
456 }
463457
464 fn genAdd(self: *Function, inst: *ir.Inst.Add, comptime arch: std.Target.Cpu.Arch) !MCValue {458 switch (arch) {
465 // No side effects, so if it's unreferenced, do nothing.459 .x86_64 => {
466 if (inst.base.isUnused())460 var imm = ir.Inst.Constant{
467 return MCValue.dead;461 .base = .{
468 switch (arch) {462 .tag = .constant,
469 .x86_64 => {463 .deaths = 0,
470 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 0, 0x00);464 .ty = inst.args.operand.ty,
471 },465 .src = inst.args.operand.src,
472 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),466 },
467 .val = Value.initTag(.bool_true),
468 };
469 return try self.genX8664BinMath(&inst.base, inst.args.operand, &imm.base, 6, 0x30);
470 },
471 else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),
472 }
473 }473 }
474 }
475474
476 fn genSub(self: *Function, inst: *ir.Inst.Sub, comptime arch: std.Target.Cpu.Arch) !MCValue {475 fn genAdd(self: *Self, inst: *ir.Inst.Add) !MCValue {
477 // No side effects, so if it's unreferenced, do nothing.476 // No side effects, so if it's unreferenced, do nothing.
478 if (inst.base.isUnused())477 if (inst.base.isUnused())
479 return MCValue.dead;478 return MCValue.dead;
480 switch (arch) {479 switch (arch) {
481 .x86_64 => {480 .x86_64 => {
482 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 5, 0x28);481 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 0, 0x00);
483 },482 },
484 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),483 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),
484 }
485 }485 }
486 }
487486
488 /// ADD, SUB, XOR, OR, AND487 fn genSub(self: *Self, inst: *ir.Inst.Sub) !MCValue {
489 fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {488 // No side effects, so if it's unreferenced, do nothing.
490 try self.code.ensureCapacity(self.code.items.len + 8);489 if (inst.base.isUnused())
491490 return MCValue.dead;
492 const lhs = try self.resolveInst(op_lhs);491 switch (arch) {
493 const rhs = try self.resolveInst(op_rhs);492 .x86_64 => {
494493 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 5, 0x28);
495 // There are 2 operands, destination and source.494 },
496 // Either one, but not both, can be a memory operand.495 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),
497 // Source operand can be an immediate, 8 bits or 32 bits.
498 // So, if either one of the operands dies with this instruction, we can use it
499 // as the result MCValue.
500 var dst_mcv: MCValue = undefined;
501 var src_mcv: MCValue = undefined;
502 var src_inst: *ir.Inst = undefined;
503 if (inst.operandDies(0) and lhs.isMutable()) {
504 // LHS dies; use it as the destination.
505 // Both operands cannot be memory.
506 src_inst = op_rhs;
507 if (lhs.isMemory() and rhs.isMemory()) {
508 dst_mcv = try self.copyToNewRegister(op_lhs);
509 src_mcv = rhs;
510 } else {
511 dst_mcv = lhs;
512 src_mcv = rhs;
513 }
514 } else if (inst.operandDies(1) and rhs.isMutable()) {
515 // RHS dies; use it as the destination.
516 // Both operands cannot be memory.
517 src_inst = op_lhs;
518 if (lhs.isMemory() and rhs.isMemory()) {
519 dst_mcv = try self.copyToNewRegister(op_rhs);
520 src_mcv = lhs;
521 } else {
522 dst_mcv = rhs;
523 src_mcv = lhs;
524 }496 }
525 } else {497 }
526 if (lhs.isMemory()) {498
527 dst_mcv = try self.copyToNewRegister(op_lhs);499 /// ADD, SUB, XOR, OR, AND
528 src_mcv = rhs;500 fn genX8664BinMath(self: *Self, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {
501 try self.code.ensureCapacity(self.code.items.len + 8);
502
503 const lhs = try self.resolveInst(op_lhs);
504 const rhs = try self.resolveInst(op_rhs);
505
506 // There are 2 operands, destination and source.
507 // Either one, but not both, can be a memory operand.
508 // Source operand can be an immediate, 8 bits or 32 bits.
509 // So, if either one of the operands dies with this instruction, we can use it
510 // as the result MCValue.
511 var dst_mcv: MCValue = undefined;
512 var src_mcv: MCValue = undefined;
513 var src_inst: *ir.Inst = undefined;
514 if (inst.operandDies(0) and lhs.isMutable()) {
515 // LHS dies; use it as the destination.
516 // Both operands cannot be memory.
529 src_inst = op_rhs;517 src_inst = op_rhs;
530 } else {518 if (lhs.isMemory() and rhs.isMemory()) {
531 dst_mcv = try self.copyToNewRegister(op_rhs);519 dst_mcv = try self.moveToNewRegister(op_lhs);
532 src_mcv = lhs;520 src_mcv = rhs;
521 } else {
522 dst_mcv = lhs;
523 src_mcv = rhs;
524 }
525 } else if (inst.operandDies(1) and rhs.isMutable()) {
526 // RHS dies; use it as the destination.
527 // Both operands cannot be memory.
533 src_inst = op_lhs;528 src_inst = op_lhs;
534 }529 if (lhs.isMemory() and rhs.isMemory()) {
535 }530 dst_mcv = try self.moveToNewRegister(op_rhs);
536 // This instruction supports only signed 32-bit immediates at most. If the immediate531 src_mcv = lhs;
537 // value is larger than this, we put it in a register.532 } else {
538 // A potential opportunity for future optimization here would be keeping track533 dst_mcv = rhs;
539 // of the fact that the instruction is available both as an immediate534 src_mcv = lhs;
540 // and as a register.
541 switch (src_mcv) {
542 .immediate => |imm| {
543 if (imm > std.math.maxInt(u31)) {
544 src_mcv = try self.copyToNewRegister(src_inst);
545 }535 }
546 },536 } else {
547 else => {},537 if (lhs.isMemory()) {
548 }538 dst_mcv = try self.moveToNewRegister(op_lhs);
539 src_mcv = rhs;
540 src_inst = op_rhs;
541 } else {
542 dst_mcv = try self.moveToNewRegister(op_rhs);
543 src_mcv = lhs;
544 src_inst = op_lhs;
545 }
546 }
547 // This instruction supports only signed 32-bit immediates at most. If the immediate
548 // value is larger than this, we put it in a register.
549 // A potential opportunity for future optimization here would be keeping track
550 // of the fact that the instruction is available both as an immediate
551 // and as a register.
552 switch (src_mcv) {
553 .immediate => |imm| {
554 if (imm > std.math.maxInt(u31)) {
555 src_mcv = try self.moveToNewRegister(src_inst);
556 }
557 },
558 else => {},
559 }
549560
550 try self.genX8664BinMathCode(inst.src, dst_mcv, src_mcv, opx, mr);561 try self.genX8664BinMathCode(inst.src, dst_mcv, src_mcv, opx, mr);
551562
552 return dst_mcv;563 return dst_mcv;
553 }564 }
554565
555 fn genX8664BinMathCode(self: *Function, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {566 fn genX8664BinMathCode(self: *Self, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {
556 switch (dst_mcv) {567 switch (dst_mcv) {
557 .none => unreachable,568 .none => unreachable,
558 .dead, .unreach, .immediate => unreachable,569 .dead, .unreach, .immediate => unreachable,
559 .compare_flags_unsigned => unreachable,570 .compare_flags_unsigned => unreachable,
560 .compare_flags_signed => unreachable,571 .compare_flags_signed => unreachable,
561 .register => |dst_reg_usize| {572 .register => |dst_reg| {
562 const dst_reg = @intToEnum(Reg(.x86_64), @intCast(u8, dst_reg_usize));573 switch (src_mcv) {
563 switch (src_mcv) {574 .none => unreachable,
564 .none => unreachable,575 .dead, .unreach => unreachable,
565 .dead, .unreach => unreachable,576 .register => |src_reg| {
566 .register => |src_reg_usize| {577 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });
567 const src_reg = @intToEnum(Reg(.x86_64), @intCast(u8, src_reg_usize));578 self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });
568 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });579 },
569 self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });580 .immediate => |imm| {
570 },581 const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode.
571 .immediate => |imm| {582 // 81 /opx id
572 const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode.583 if (imm32 <= std.math.maxInt(u7)) {
573 // 81 /opx id584 self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
574 if (imm32 <= std.math.maxInt(u7)) {585 self.code.appendSliceAssumeCapacity(&[_]u8{
575 self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 });586 0x83,
576 self.code.appendSliceAssumeCapacity(&[_]u8{587 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
577 0x83,588 @intCast(u8, imm32),
578 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),589 });
579 @intCast(u8, imm32),590 } else {
580 });591 self.rex(.{ .r = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
581 } else {592 self.code.appendSliceAssumeCapacity(&[_]u8{
582 self.rex(.{ .r = dst_reg.isExtended(), .w = dst_reg.size() == 64 });593 0x81,
583 self.code.appendSliceAssumeCapacity(&[_]u8{594 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
584 0x81,595 });
585 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),596 std.mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), imm32);
586 });597 }
587 std.mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), imm32);598 },
588 }599 .embedded_in_code, .memory, .stack_offset => {
589 },600 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});
590 .embedded_in_code, .memory, .stack_offset => {601 },
591 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});602 .compare_flags_unsigned => {
592 },603 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
593 .compare_flags_unsigned => {604 },
594 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});605 .compare_flags_signed => {
595 },606 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
596 .compare_flags_signed => {607 },
597 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});608 }
598 },609 },
599 }610 .embedded_in_code, .memory, .stack_offset => {
600 },611 return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});
601 .embedded_in_code, .memory, .stack_offset => {612 },
602 return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});613 }
603 },
604 }614 }
605 }
606615
607 fn genArg(self: *Function, inst: *ir.Inst.Arg) !MCValue {616 fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {
608 const i = self.arg_index;617 const i = self.arg_index;
609 self.arg_index += 1;618 self.arg_index += 1;
610 return self.args[i];619 return self.args[i];
611 }620 }
612621
613 fn genBreakpoint(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch) !MCValue {622 fn genBreakpoint(self: *Self, src: usize) !MCValue {
614 switch (arch) {623 switch (arch) {
615 .i386, .x86_64 => {624 .i386, .x86_64 => {
616 try self.code.append(0xcc); // int3625 try self.code.append(0xcc); // int3
617 },626 },
618 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),627 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
628 }
629 return .none;
619 }630 }
620 return .none;
621 }
622631
623 fn genCall(self: *Function, inst: *ir.Inst.Call, comptime arch: std.Target.Cpu.Arch) !MCValue {632 fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue {
624 const fn_ty = inst.args.func.ty;633 const fn_ty = inst.args.func.ty;
625 const cc = fn_ty.fnCallingConvention();634 const cc = fn_ty.fnCallingConvention();
626 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());635 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
627 defer self.gpa.free(param_types);636 defer self.gpa.free(param_types);
628 fn_ty.fnParamTypes(param_types);637 fn_ty.fnParamTypes(param_types);
629 var mc_args = try self.gpa.alloc(MCValue, param_types.len);638 var mc_args = try self.gpa.alloc(MCValue, param_types.len);
630 defer self.gpa.free(mc_args);639 defer self.gpa.free(mc_args);
631 const stack_byte_count = try self.resolveParameters(inst.base.src, cc, param_types, mc_args);640 const stack_byte_count = try self.resolveParameters(inst.base.src, cc, param_types, mc_args);
632641
633 switch (arch) {642 switch (arch) {
634 .x86_64 => {643 .x86_64 => {
635 for (mc_args) |mc_arg, arg_i| {644 for (mc_args) |mc_arg, arg_i| {
636 const arg = inst.args.args[arg_i];645 const arg = inst.args.args[arg_i];
637 const arg_mcv = try self.resolveInst(inst.args.args[arg_i]);646 const arg_mcv = try self.resolveInst(inst.args.args[arg_i]);
638 switch (mc_arg) {647 switch (mc_arg) {
639 .none => continue,648 .none => continue,
640 .register => |reg| {649 .register => |reg| {
641 try self.genSetReg(arg.src, arch, @intToEnum(Reg(arch), @intCast(u8, reg)), arg_mcv);650 try self.genSetReg(arg.src, reg, arg_mcv);
642 // TODO interact with the register allocator to mark the instruction as moved.651 // TODO interact with the register allocator to mark the instruction as moved.
643 },652 },
644 .stack_offset => {653 .stack_offset => {
645 // Here we need to emit instructions like this:654 // Here we need to emit instructions like this:
646 // mov qword ptr [rsp + stack_offset], x655 // mov qword ptr [rsp + stack_offset], x
647 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});656 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
648 },657 },
649 .immediate => unreachable,658 .immediate => unreachable,
650 .unreach => unreachable,659 .unreach => unreachable,
651 .dead => unreachable,660 .dead => unreachable,
652 .embedded_in_code => unreachable,661 .embedded_in_code => unreachable,
653 .memory => unreachable,662 .memory => unreachable,
654 .compare_flags_signed => unreachable,663 .compare_flags_signed => unreachable,
655 .compare_flags_unsigned => unreachable,664 .compare_flags_unsigned => unreachable,
665 }
656 }666 }
657 }
658667
659 if (inst.args.func.cast(ir.Inst.Constant)) |func_inst| {668 if (inst.args.func.cast(ir.Inst.Constant)) |func_inst| {
660 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {669 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
661 const func = func_val.func;670 const func = func_val.func;
662 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];671 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
663 const ptr_bits = self.target.cpu.arch.ptrBitWidth();672 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
664 const ptr_bytes: u64 = @divExact(ptr_bits, 8);673 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
665 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes);674 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes);
666 // ff 14 25 xx xx xx xx call [addr]675 // ff 14 25 xx xx xx xx call [addr]
667 try self.code.ensureCapacity(self.code.items.len + 7);676 try self.code.ensureCapacity(self.code.items.len + 7);
668 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });677 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
669 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);678 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
679 } else {
680 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});
681 }
670 } else {682 } else {
671 return self.fail(inst.base.src, "TODO implement calling bitcasted functions", .{});683 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
672 }684 }
673 } else {685 },
674 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});686 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
675 }687 }
676 },
677 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
678 }
679688
680 const return_type = fn_ty.fnReturnType();689 const return_type = fn_ty.fnReturnType();
681 switch (return_type.zigTypeTag()) {690 switch (return_type.zigTypeTag()) {
682 .Void => return MCValue{ .none = {} },691 .Void => return MCValue{ .none = {} },
683 .NoReturn => return MCValue{ .unreach = {} },692 .NoReturn => return MCValue{ .unreach = {} },
684 else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),693 else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),
694 }
685 }695 }
686 }
687696
688 fn ret(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch, mcv: MCValue) !MCValue {697 fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {
689 if (mcv != .none) {698 if (mcv != .none) {
690 return self.fail(src, "TODO implement return with non-void operand", .{});699 return self.fail(src, "TODO implement return with non-void operand", .{});
691 }700 }
692 switch (arch) {701 switch (arch) {
693 .i386 => {702 .i386 => {
694 try self.code.append(0xc3); // ret703 try self.code.append(0xc3); // ret
695 },704 },
696 .x86_64 => {705 .x86_64 => {
697 try self.code.appendSlice(&[_]u8{706 try self.code.appendSlice(&[_]u8{
698 0x5d, // pop rbp707 0x5d, // pop rbp
699 0xc3, // ret708 0xc3, // ret
700 });709 });
701 },710 },
702 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),711 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
712 }
713 return .unreach;
703 }714 }
704 return .unreach;
705 }
706715
707 fn genRet(self: *Function, inst: *ir.Inst.Ret, comptime arch: std.Target.Cpu.Arch) !MCValue {716 fn genRet(self: *Self, inst: *ir.Inst.Ret) !MCValue {
708 const operand = try self.resolveInst(inst.args.operand);717 const operand = try self.resolveInst(inst.args.operand);
709 return self.ret(inst.base.src, arch, operand);718 return self.ret(inst.base.src, operand);
710 }719 }
711
712 fn genRetVoid(self: *Function, inst: *ir.Inst.RetVoid, comptime arch: std.Target.Cpu.Arch) !MCValue {
713 return self.ret(inst.base.src, arch, .none);
714 }
715720
716 fn genCmp(self: *Function, inst: *ir.Inst.Cmp, comptime arch: std.Target.Cpu.Arch) !MCValue {721 fn genRetVoid(self: *Self, inst: *ir.Inst.RetVoid) !MCValue {
717 // No side effects, so if it's unreferenced, do nothing.722 return self.ret(inst.base.src, .none);
718 if (inst.base.isUnused())
719 return MCValue.dead;
720 switch (arch) {
721 .x86_64 => {
722 try self.code.ensureCapacity(self.code.items.len + 8);
723
724 const lhs = try self.resolveInst(inst.args.lhs);
725 const rhs = try self.resolveInst(inst.args.rhs);
726
727 // There are 2 operands, destination and source.
728 // Either one, but not both, can be a memory operand.
729 // Source operand can be an immediate, 8 bits or 32 bits.
730 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
731 try self.copyToNewRegister(inst.args.lhs)
732 else
733 lhs;
734 // This instruction supports only signed 32-bit immediates at most.
735 const src_mcv = try self.limitImmediateType(inst.args.rhs, i32);
736
737 try self.genX8664BinMathCode(inst.base.src, dst_mcv, src_mcv, 7, 0x38);
738 const info = inst.args.lhs.ty.intInfo(self.target.*);
739 if (info.signed) {
740 return MCValue{ .compare_flags_signed = inst.args.op };
741 } else {
742 return MCValue{ .compare_flags_unsigned = inst.args.op };
743 }
744 },
745 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
746 }723 }
747 }
748724
749 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue {725 fn genCmp(self: *Self, inst: *ir.Inst.Cmp) !MCValue {
750 switch (arch) {726 // No side effects, so if it's unreferenced, do nothing.
751 .x86_64 => {727 if (inst.base.isUnused())
752 try self.code.ensureCapacity(self.code.items.len + 6);728 return MCValue.dead;
753729 switch (arch) {
754 const cond = try self.resolveInst(inst.args.condition);730 .x86_64 => {
755 switch (cond) {731 try self.code.ensureCapacity(self.code.items.len + 8);
756 .compare_flags_signed => |cmp_op| {732
757 // Here we map to the opposite opcode because the jump is to the false branch.733 const lhs = try self.resolveInst(inst.args.lhs);
758 const opcode: u8 = switch (cmp_op) {734 const rhs = try self.resolveInst(inst.args.rhs);
759 .gte => 0x8c,735
760 .gt => 0x8e,736 // There are 2 operands, destination and source.
761 .neq => 0x84,737 // Either one, but not both, can be a memory operand.
762 .lt => 0x8d,738 // Source operand can be an immediate, 8 bits or 32 bits.
763 .lte => 0x8f,739 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
764 .eq => 0x85,740 try self.moveToNewRegister(inst.args.lhs)
765 };741 else
766 return self.genX86CondBr(inst, opcode, arch);742 lhs;
767 },743 // This instruction supports only signed 32-bit immediates at most.
768 .compare_flags_unsigned => |cmp_op| {744 const src_mcv = try self.limitImmediateType(inst.args.rhs, i32);
769 // Here we map to the opposite opcode because the jump is to the false branch.745
770 const opcode: u8 = switch (cmp_op) {746 try self.genX8664BinMathCode(inst.base.src, dst_mcv, src_mcv, 7, 0x38);
771 .gte => 0x82,747 const info = inst.args.lhs.ty.intInfo(self.target.*);
772 .gt => 0x86,748 if (info.signed) {
773 .neq => 0x84,749 return MCValue{ .compare_flags_signed = inst.args.op };
774 .lt => 0x83,750 } else {
775 .lte => 0x87,751 return MCValue{ .compare_flags_unsigned = inst.args.op };
776 .eq => 0x85,752 }
777 };753 },
778 return self.genX86CondBr(inst, opcode, arch);754 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
779 },755 }
780 .register => |reg_usize| {
781 const reg = @intToEnum(Reg(arch), @intCast(u8, reg_usize));
782 // test reg, 1
783 // TODO detect al, ax, eax
784 try self.code.ensureCapacity(self.code.items.len + 4);
785 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
786 self.code.appendSliceAssumeCapacity(&[_]u8{
787 0xf6,
788 @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()),
789 0x01,
790 });
791 return self.genX86CondBr(inst, 0x84, arch);
792 },
793 else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }),
794 }
795 },
796 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),
797 }756 }
798 }
799757
800 fn genX86CondBr(self: *Function, inst: *ir.Inst.CondBr, opcode: u8, comptime arch: std.Target.Cpu.Arch) !MCValue {758 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {
801 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });759 switch (arch) {
802 const reloc = Reloc{ .rel32 = self.code.items.len };760 .x86_64 => {
803 self.code.items.len += 4;761 try self.code.ensureCapacity(self.code.items.len + 6);
804 try self.genBody(inst.args.true_body, arch);762
805 try self.performReloc(inst.base.src, reloc);763 const cond = try self.resolveInst(inst.args.condition);
806 try self.genBody(inst.args.false_body, arch);764 switch (cond) {
807 return MCValue.unreach;765 .compare_flags_signed => |cmp_op| {
808 }766 // Here we map to the opposite opcode because the jump is to the false branch.
767 const opcode: u8 = switch (cmp_op) {
768 .gte => 0x8c,
769 .gt => 0x8e,
770 .neq => 0x84,
771 .lt => 0x8d,
772 .lte => 0x8f,
773 .eq => 0x85,
774 };
775 return self.genX86CondBr(inst, opcode);
776 },
777 .compare_flags_unsigned => |cmp_op| {
778 // Here we map to the opposite opcode because the jump is to the false branch.
779 const opcode: u8 = switch (cmp_op) {
780 .gte => 0x82,
781 .gt => 0x86,
782 .neq => 0x84,
783 .lt => 0x83,
784 .lte => 0x87,
785 .eq => 0x85,
786 };
787 return self.genX86CondBr(inst, opcode);
788 },
789 .register => |reg| {
790 // test reg, 1
791 // TODO detect al, ax, eax
792 try self.code.ensureCapacity(self.code.items.len + 4);
793 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
794 self.code.appendSliceAssumeCapacity(&[_]u8{
795 0xf6,
796 @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()),
797 0x01,
798 });
799 return self.genX86CondBr(inst, 0x84);
800 },
801 else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }),
802 }
803 },
804 else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}),
805 }
806 }
809807
810 fn genIsNull(self: *Function, inst: *ir.Inst.IsNull, comptime arch: std.Target.Cpu.Arch) !MCValue {808 fn genX86CondBr(self: *Self, inst: *ir.Inst.CondBr, opcode: u8) !MCValue {
811 switch (arch) {809 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });
812 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),810 const reloc = Reloc{ .rel32 = self.code.items.len };
811 self.code.items.len += 4;
812 try self.genBody(inst.args.true_body);
813 try self.performReloc(inst.base.src, reloc);
814 try self.genBody(inst.args.false_body);
815 return MCValue.unreach;
813 }816 }
814 }
815817
816 fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull, comptime arch: std.Target.Cpu.Arch) !MCValue {818 fn genIsNull(self: *Self, inst: *ir.Inst.IsNull) !MCValue {
817 // Here you can specialize this instruction if it makes sense to, otherwise the default819 switch (arch) {
818 // will call genIsNull and invert the result.820 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),
819 switch (arch) {821 }
820 else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),
821 }822 }
822 }
823823
824 fn genBlock(self: *Function, inst: *ir.Inst.Block, comptime arch: std.Target.Cpu.Arch) !MCValue {824 fn genIsNonNull(self: *Self, inst: *ir.Inst.IsNonNull) !MCValue {
825 if (inst.base.ty.hasCodeGenBits()) {825 // Here you can specialize this instruction if it makes sense to, otherwise the default
826 return self.fail(inst.base.src, "TODO codegen Block with non-void type", .{});826 // will call genIsNull and invert the result.
827 switch (arch) {
828 else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),
829 }
827 }830 }
828 // A block is nothing but a setup to be able to jump to the end.
829 defer inst.codegen.relocs.deinit(self.gpa);
830 try self.genBody(inst.args.body, arch);
831831
832 for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);832 fn genBlock(self: *Self, inst: *ir.Inst.Block) !MCValue {
833 if (inst.base.ty.hasCodeGenBits()) {
834 return self.fail(inst.base.src, "TODO codegen Block with non-void type", .{});
835 }
836 // A block is nothing but a setup to be able to jump to the end.
837 defer inst.codegen.relocs.deinit(self.gpa);
838 try self.genBody(inst.args.body);
833839
834 return MCValue.none;840 for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);
835 }
836841
837 fn performReloc(self: *Function, src: usize, reloc: Reloc) !void {842 return MCValue.none;
838 switch (reloc) {
839 .rel32 => |pos| {
840 const amt = self.code.items.len - (pos + 4);
841 const s32_amt = std.math.cast(i32, amt) catch
842 return self.fail(src, "unable to perform relocation: jump too far", .{});
843 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
844 },
845 }843 }
846 }
847844
848 fn genBr(self: *Function, inst: *ir.Inst.Br, comptime arch: std.Target.Cpu.Arch) !MCValue {845 fn performReloc(self: *Self, src: usize, reloc: Reloc) !void {
849 if (!inst.args.operand.ty.hasCodeGenBits())846 switch (reloc) {
850 return self.brVoid(inst.base.src, inst.args.block, arch);847 .rel32 => |pos| {
851848 const amt = self.code.items.len - (pos + 4);
852 const operand = try self.resolveInst(inst.args.operand);849 const s32_amt = std.math.cast(i32, amt) catch
853 switch (arch) {850 return self.fail(src, "unable to perform relocation: jump too far", .{});
854 else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}),851 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
852 },
853 }
855 }854 }
856 }
857855
858 fn genBrVoid(self: *Function, inst: *ir.Inst.BrVoid, comptime arch: std.Target.Cpu.Arch) !MCValue {856 fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {
859 return self.brVoid(inst.base.src, inst.args.block, arch);857 if (!inst.args.operand.ty.hasCodeGenBits())
860 }858 return self.brVoid(inst.base.src, inst.args.block);
861859
862 fn brVoid(self: *Function, src: usize, block: *ir.Inst.Block, comptime arch: std.Target.Cpu.Arch) !MCValue {860 const operand = try self.resolveInst(inst.args.operand);
863 // Emit a jump with a relocation. It will be patched up after the block ends.861 switch (arch) {
864 try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1);862 else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}),
865863 }
866 switch (arch) {
867 .i386, .x86_64 => {
868 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
869 // which is available if the jump is 127 bytes or less forward.
870 try self.code.resize(self.code.items.len + 5);
871 self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
872 // Leave the jump offset undefined
873 block.codegen.relocs.appendAssumeCapacity(.{ .rel32 = self.code.items.len - 4 });
874 },
875 else => return self.fail(src, "TODO implement brvoid for {}", .{self.target.cpu.arch}),
876 }864 }
877 return .none;
878 }
879865
880 fn genAsm(self: *Function, inst: *ir.Inst.Assembly, comptime arch: Target.Cpu.Arch) !MCValue {866 fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue {
881 if (!inst.args.is_volatile and inst.base.isUnused())867 return self.brVoid(inst.base.src, inst.args.block);
882 return MCValue.dead;
883 if (arch != .x86_64 and arch != .i386) {
884 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
885 }868 }
886 for (inst.args.inputs) |input, i| {869
887 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {870 fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue {
888 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});871 // Emit a jump with a relocation. It will be patched up after the block ends.
872 try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1);
873
874 switch (arch) {
875 .i386, .x86_64 => {
876 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction
877 // which is available if the jump is 127 bytes or less forward.
878 try self.code.resize(self.code.items.len + 5);
879 self.code.items[self.code.items.len - 5] = 0xe9; // jmp rel32
880 // Leave the jump offset undefined
881 block.codegen.relocs.appendAssumeCapacity(.{ .rel32 = self.code.items.len - 4 });
882 },
883 else => return self.fail(src, "TODO implement brvoid for {}", .{self.target.cpu.arch}),
889 }884 }
890 const reg_name = input[1 .. input.len - 1];885 return .none;
891 const reg = parseRegName(arch, reg_name) orelse
892 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
893 const arg = try self.resolveInst(inst.args.args[i]);
894 try self.genSetReg(inst.base.src, arch, reg, arg);
895 }886 }
896887
897 if (mem.eql(u8, inst.args.asm_source, "syscall")) {888 fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {
898 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });889 if (!inst.args.is_volatile and inst.base.isUnused())
899 } else {890 return MCValue.dead;
900 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});891 if (arch != .x86_64 and arch != .i386) {
901 }892 return self.fail(inst.base.src, "TODO implement inline asm support for more architectures", .{});
893 }
894 for (inst.args.inputs) |input, i| {
895 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
896 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
897 }
898 const reg_name = input[1 .. input.len - 1];
899 const reg = parseRegName(reg_name) orelse
900 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
901 const arg = try self.resolveInst(inst.args.args[i]);
902 try self.genSetReg(inst.base.src, reg, arg);
903 }
902904
903 if (inst.args.output) |output| {905 if (mem.eql(u8, inst.args.asm_source, "syscall")) {
904 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {906 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
905 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});907 } else {
908 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
906 }909 }
907 const reg_name = output[2 .. output.len - 1];
908 const reg = parseRegName(arch, reg_name) orelse
909 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
910 return MCValue{ .register = @enumToInt(reg) };
911 } else {
912 return MCValue.none;
913 }
914 }
915910
916 /// Encodes a REX prefix as specified, and appends it to the instruction911 if (inst.args.output) |output| {
917 /// stream. This only modifies the instruction stream if at least one bit912 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
918 /// is set true, which has a few implications:913 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
919 ///914 }
920 /// * The length of the instruction buffer will be modified *if* the915 const reg_name = output[2 .. output.len - 1];
921 /// resulting REX is meaningful, but will remain the same if it is not.916 const reg = parseRegName(reg_name) orelse
922 /// * Deliberately inserting a "meaningless REX" requires explicit usage of917 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
923 /// 0x40, and cannot be done via this function.918 return MCValue{ .register = reg };
924 fn rex(self: *Function, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void {919 } else {
925 // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.920 return MCValue.none;
926 var value: u8 = 0x40;921 }
927 if (arg.b) {
928 value |= 0x1;
929 }
930 if (arg.x) {
931 value |= 0x2;
932 }
933 if (arg.r) {
934 value |= 0x4;
935 }
936 if (arg.w) {
937 value |= 0x8;
938 }922 }
939 if (value != 0x40) {923
940 self.code.appendAssumeCapacity(value);924 /// Encodes a REX prefix as specified, and appends it to the instruction
925 /// stream. This only modifies the instruction stream if at least one bit
926 /// is set true, which has a few implications:
927 ///
928 /// * The length of the instruction buffer will be modified *if* the
929 /// resulting REX is meaningful, but will remain the same if it is not.
930 /// * Deliberately inserting a "meaningless REX" requires explicit usage of
931 /// 0x40, and cannot be done via this function.
932 fn rex(self: *Self, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void {
933 // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.
934 var value: u8 = 0x40;
935 if (arg.b) {
936 value |= 0x1;
937 }
938 if (arg.x) {
939 value |= 0x2;
940 }
941 if (arg.r) {
942 value |= 0x4;
943 }
944 if (arg.w) {
945 value |= 0x8;
946 }
947 if (value != 0x40) {
948 self.code.appendAssumeCapacity(value);
949 }
941 }950 }
942 }
943951
944 fn genSetReg(self: *Function, src: usize, comptime arch: Target.Cpu.Arch, reg: Reg(arch), mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {952 fn genSetReg(self: *Self, src: usize, reg: Reg, mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {
945 switch (arch) {953 switch (arch) {
946 .x86_64 => switch (mcv) {954 .x86_64 => switch (mcv) {
947 .dead => unreachable,955 .dead => unreachable,
948 .none => unreachable,956 .none => unreachable,
949 .unreach => unreachable,957 .unreach => unreachable,
950 .compare_flags_unsigned => |op| {958 .compare_flags_unsigned => |op| {
951 try self.code.ensureCapacity(self.code.items.len + 3);
952 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
953 const opcode: u8 = switch (op) {
954 .gte => 0x93,
955 .gt => 0x97,
956 .neq => 0x95,
957 .lt => 0x92,
958 .lte => 0x96,
959 .eq => 0x94,
960 };
961 const id = @as(u8, reg.id() & 0b111);
962 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode, 0xC0 | id });
963 },
964 .compare_flags_signed => |op| {
965 return self.fail(src, "TODO set register with compare flags value (signed)", .{});
966 },
967 .immediate => |x| {
968 if (reg.size() != 64) {
969 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
970 }
971 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
972 // register is the fastest way to zero a register.
973 if (x == 0) {
974 // The encoding for `xor r32, r32` is `0x31 /r`.
975 // Section 3.1.1.1 of the Intel x64 Manual states that "/r indicates that the
976 // ModR/M byte of the instruction contains a register operand and an r/m operand."
977 //
978 // R/M bytes are composed of two bits for the mode, then three bits for the register,
979 // then three bits for the operand. Since we're zeroing a register, the two three-bit
980 // values will be identical, and the mode is three (the raw register value).
981 //
982 // If we're accessing e.g. r8d, we need to use a REX prefix before the actual operation. Since
983 // this is a 32-bit operation, the W flag is set to zero. X is also zero, as we're not using a SIB.
984 // Both R and B are set, as we're extending, in effect, the register bits *and* the operand.
985 try self.code.ensureCapacity(self.code.items.len + 3);959 try self.code.ensureCapacity(self.code.items.len + 3);
986 self.rex(.{ .r = reg.isExtended(), .b = reg.isExtended() });960 self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 });
961 const opcode: u8 = switch (op) {
962 .gte => 0x93,
963 .gt => 0x97,
964 .neq => 0x95,
965 .lt => 0x92,
966 .lte => 0x96,
967 .eq => 0x94,
968 };
987 const id = @as(u8, reg.id() & 0b111);969 const id = @as(u8, reg.id() & 0b111);
988 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x31, 0xC0 | id << 3 | id });970 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode, 0xC0 | id });
989 return;971 },
990 }972 .compare_flags_signed => |op| {
991 if (x <= std.math.maxInt(u32)) {973 return self.fail(src, "TODO set register with compare flags value (signed)", .{});
992 // Next best case: if we set the lower four bytes, the upper four will be zeroed.974 },
993 //975 .immediate => |x| {
994 // The encoding for `mov IMM32 -> REG` is (0xB8 + R) IMM.976 if (reg.size() != 64) {
995 if (reg.isExtended()) {977 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
996 // Just as with XORing, we need a REX prefix. This time though, we only
997 // need the B bit set, as we're extending the opcode's register field,
998 // and there is no Mod R/M byte.
999 //
1000 // Thus, we need b01000001, or 0x41.
1001 try self.code.resize(self.code.items.len + 6);
1002 self.code.items[self.code.items.len - 6] = 0x41;
1003 } else {
1004 try self.code.resize(self.code.items.len + 5);
1005 }978 }
1006 self.code.items[self.code.items.len - 5] = 0xB8 | @as(u8, reg.id() & 0b111);979 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
1007 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];980 // register is the fastest way to zero a register.
1008 mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));981 if (x == 0) {
1009 return;982 // The encoding for `xor r32, r32` is `0x31 /r`.
1010 }983 // Section 3.1.1.1 of the Intel x64 Manual states that "/r indicates that the
1011 // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls984 // ModR/M byte of the instruction contains a register operand and an r/m operand."
1012 // this `movabs`, though this is officially just a different variant of the plain `mov`985 //
1013 // instruction.986 // R/M bytes are composed of two bits for the mode, then three bits for the register,
1014 //987 // then three bits for the operand. Since we're zeroing a register, the two three-bit
1015 // This encoding is, in fact, the *same* as the one used for 32-bit loads. The only988 // values will be identical, and the mode is three (the raw register value).
1016 // difference is that we set REX.W before the instruction, which extends the load to
1017 // 64-bit and uses the full bit-width of the register.
1018 //
1019 // Since we always need a REX here, let's just check if we also need to set REX.B.
1020 //
1021 // In this case, the encoding of the REX byte is 0b0100100B
1022 try self.code.ensureCapacity(self.code.items.len + 10);
1023 self.rex(.{ .w = true, .b = reg.isExtended() });
1024 self.code.items.len += 9;
1025 self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111);
1026 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
1027 mem.writeIntLittle(u64, imm_ptr, x);
1028 },
1029 .embedded_in_code => |code_offset| {
1030 if (reg.size() != 64) {
1031 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1032 }
1033 // We need the offset from RIP in a signed i32 twos complement.
1034 // The instruction is 7 bytes long and RIP points to the next instruction.
1035 try self.code.ensureCapacity(self.code.items.len + 7);
1036 // 64-bit LEA is encoded as REX.W 8D /r. If the register is extended, the REX byte is modified,
1037 // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three
1038 // bits as five.
1039 // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id.
1040 self.rex(.{ .w = true, .b = reg.isExtended() });
1041 self.code.items.len += 6;
1042 const rip = self.code.items.len;
1043 const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip);
1044 const offset = @intCast(i32, big_offset);
1045 self.code.items[self.code.items.len - 6] = 0x8D;
1046 self.code.items[self.code.items.len - 5] = 0b101 | (@as(u8, reg.id() & 0b111) << 3);
1047 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
1048 mem.writeIntLittle(i32, imm_ptr, offset);
1049 },
1050 .register => |r| {
1051 if (reg.size() != 64) {
1052 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1053 }
1054 const src_reg = @intToEnum(Reg(arch), @intCast(u8, r));
1055 // This is a variant of 8B /r. Since we're using 64-bit moves, we require a REX.
1056 // This is thus three bytes: REX 0x8B R/M.
1057 // If the destination is extended, the R field must be 1.
1058 // If the *source* is extended, the B field must be 1.
1059 // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle
1060 // three bits) contain the destination, and the R/M field (the lower three bits) contain the source.
1061 try self.code.ensureCapacity(self.code.items.len + 3);
1062 self.rex(.{ .w = true, .r = reg.isExtended(), .b = src_reg.isExtended() });
1063 const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @as(u8, src_reg.id() & 0b111);
1064 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R });
1065 },
1066 .memory => |x| {
1067 if (reg.size() != 64) {
1068 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1069 }
1070 if (x <= std.math.maxInt(u32)) {
1071 // Moving from memory to a register is a variant of `8B /r`.
1072 // Since we're using 64-bit moves, we require a REX.
1073 // This variant also requires a SIB, as it would otherwise be RIP-relative.
1074 // We want mode zero with the lower three bits set to four to indicate an SIB with no other displacement.
1075 // The SIB must be 0x25, to indicate a disp32 with no scaled index.
1076 // 0b00RRR100, where RRR is the lower three bits of the register ID.
1077 // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32.
1078 try self.code.ensureCapacity(self.code.items.len + 8);
1079 self.rex(.{ .w = true, .b = reg.isExtended() });
1080 self.code.appendSliceAssumeCapacity(&[_]u8{
1081 0x8B,
1082 0x04 | (@as(u8, reg.id() & 0b111) << 3), // R
1083 0x25,
1084 });
1085 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, x));
1086 } else {
1087 // If this is RAX, we can use a direct load; otherwise, we need to load the address, then indirectly load
1088 // the value.
1089 if (reg.id() == 0) {
1090 // REX.W 0xA1 moffs64*
1091 // moffs64* is a 64-bit offset "relative to segment base", which really just means the
1092 // absolute address for all practical purposes.
1093 try self.code.resize(self.code.items.len + 10);
1094 // REX.W == 0x48
1095 self.code.items[self.code.items.len - 10] = 0x48;
1096 self.code.items[self.code.items.len - 9] = 0xA1;
1097 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
1098 mem.writeIntLittle(u64, imm_ptr, x);
1099 } else {
1100 // This requires two instructions; a move imm as used above, followed by an indirect load using the register
1101 // as the address and the register as the destination.
1102 //989 //
1103 // This cannot be used if the lower three bits of the id are equal to four or five, as there990 // If we're accessing e.g. r8d, we need to use a REX prefix before the actual operation. Since
1104 // is no way to possibly encode it. This means that RSP, RBP, R12, and R13 cannot be used with991 // this is a 32-bit operation, the W flag is set to zero. X is also zero, as we're not using a SIB.
1105 // this instruction.992 // Both R and B are set, as we're extending, in effect, the register bits *and* the operand.
1106 const id3 = @truncate(u3, reg.id());
1107 std.debug.assert(id3 != 4 and id3 != 5);
1108
1109 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
1110 try self.genSetReg(src, arch, reg, MCValue{ .immediate = x });
1111
1112 // Now, the register contains the address of the value to load into it
1113 // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.
1114 // TODO: determine whether to allow other sized registers, and if so, handle them properly.
1115 // This operation requires three bytes: REX 0x8B R/M
1116 try self.code.ensureCapacity(self.code.items.len + 3);993 try self.code.ensureCapacity(self.code.items.len + 3);
1117 // For this operation, we want R/M mode *zero* (use register indirectly), and the two register994 self.rex(.{ .r = reg.isExtended(), .b = reg.isExtended() });
1118 // values must match. Thus, it's 00ABCABC where ABC is the lower three bits of the register ID.995 const id = @as(u8, reg.id() & 0b111);
996 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x31, 0xC0 | id << 3 | id });
997 return;
998 }
999 if (x <= std.math.maxInt(u32)) {
1000 // Next best case: if we set the lower four bytes, the upper four will be zeroed.
1119 //1001 //
1120 // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both*1002 // The encoding for `mov IMM32 -> REG` is (0xB8 + R) IMM.
1121 // register operands need to be marked as extended.1003 if (reg.isExtended()) {
1122 self.rex(.{ .w = true, .b = reg.isExtended(), .r = reg.isExtended() });1004 // Just as with XORing, we need a REX prefix. This time though, we only
1123 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());1005 // need the B bit set, as we're extending the opcode's register field,
1124 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });1006 // and there is no Mod R/M byte.
1007 //
1008 // Thus, we need b01000001, or 0x41.
1009 try self.code.resize(self.code.items.len + 6);
1010 self.code.items[self.code.items.len - 6] = 0x41;
1011 } else {
1012 try self.code.resize(self.code.items.len + 5);
1013 }
1014 self.code.items[self.code.items.len - 5] = 0xB8 | @as(u8, reg.id() & 0b111);
1015 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
1016 mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));
1017 return;
1125 }1018 }
1126 }1019 // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls
1127 },1020 // this `movabs`, though this is officially just a different variant of the plain `mov`
1128 .stack_offset => |off| {1021 // instruction.
1129 return self.fail(src, "TODO implement genSetReg for stack variables", .{});1022 //
1023 // This encoding is, in fact, the *same* as the one used for 32-bit loads. The only
1024 // difference is that we set REX.W before the instruction, which extends the load to
1025 // 64-bit and uses the full bit-width of the register.
1026 //
1027 // Since we always need a REX here, let's just check if we also need to set REX.B.
1028 //
1029 // In this case, the encoding of the REX byte is 0b0100100B
1030 try self.code.ensureCapacity(self.code.items.len + 10);
1031 self.rex(.{ .w = true, .b = reg.isExtended() });
1032 self.code.items.len += 9;
1033 self.code.items[self.code.items.len - 9] = 0xB8 | @as(u8, reg.id() & 0b111);
1034 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
1035 mem.writeIntLittle(u64, imm_ptr, x);
1036 },
1037 .embedded_in_code => |code_offset| {
1038 if (reg.size() != 64) {
1039 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1040 }
1041 // We need the offset from RIP in a signed i32 twos complement.
1042 // The instruction is 7 bytes long and RIP points to the next instruction.
1043 try self.code.ensureCapacity(self.code.items.len + 7);
1044 // 64-bit LEA is encoded as REX.W 8D /r. If the register is extended, the REX byte is modified,
1045 // but the operation size is unchanged. Since we're using a disp32, we want mode 0 and lower three
1046 // bits as five.
1047 // REX 0x8D 0b00RRR101, where RRR is the lower three bits of the id.
1048 self.rex(.{ .w = true, .b = reg.isExtended() });
1049 self.code.items.len += 6;
1050 const rip = self.code.items.len;
1051 const big_offset = @intCast(i64, code_offset) - @intCast(i64, rip);
1052 const offset = @intCast(i32, big_offset);
1053 self.code.items[self.code.items.len - 6] = 0x8D;
1054 self.code.items[self.code.items.len - 5] = 0b101 | (@as(u8, reg.id() & 0b111) << 3);
1055 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
1056 mem.writeIntLittle(i32, imm_ptr, offset);
1057 },
1058 .register => |src_reg| {
1059 if (reg.size() != 64) {
1060 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1061 }
1062 // This is a variant of 8B /r. Since we're using 64-bit moves, we require a REX.
1063 // This is thus three bytes: REX 0x8B R/M.
1064 // If the destination is extended, the R field must be 1.
1065 // If the *source* is extended, the B field must be 1.
1066 // Since the register is being accessed directly, the R/M mode is three. The reg field (the middle
1067 // three bits) contain the destination, and the R/M field (the lower three bits) contain the source.
1068 try self.code.ensureCapacity(self.code.items.len + 3);
1069 self.rex(.{ .w = true, .r = reg.isExtended(), .b = src_reg.isExtended() });
1070 const R = 0xC0 | (@as(u8, reg.id() & 0b111) << 3) | @as(u8, src_reg.id() & 0b111);
1071 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, R });
1072 },
1073 .memory => |x| {
1074 if (reg.size() != 64) {
1075 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1076 }
1077 if (x <= std.math.maxInt(u32)) {
1078 // Moving from memory to a register is a variant of `8B /r`.
1079 // Since we're using 64-bit moves, we require a REX.
1080 // This variant also requires a SIB, as it would otherwise be RIP-relative.
1081 // We want mode zero with the lower three bits set to four to indicate an SIB with no other displacement.
1082 // The SIB must be 0x25, to indicate a disp32 with no scaled index.
1083 // 0b00RRR100, where RRR is the lower three bits of the register ID.
1084 // The instruction is thus eight bytes; REX 0x8B 0b00RRR100 0x25 followed by a four-byte disp32.
1085 try self.code.ensureCapacity(self.code.items.len + 8);
1086 self.rex(.{ .w = true, .b = reg.isExtended() });
1087 self.code.appendSliceAssumeCapacity(&[_]u8{
1088 0x8B,
1089 0x04 | (@as(u8, reg.id() & 0b111) << 3), // R
1090 0x25,
1091 });
1092 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), @intCast(u32, x));
1093 } else {
1094 // If this is RAX, we can use a direct load; otherwise, we need to load the address, then indirectly load
1095 // the value.
1096 if (reg.id() == 0) {
1097 // REX.W 0xA1 moffs64*
1098 // moffs64* is a 64-bit offset "relative to segment base", which really just means the
1099 // absolute address for all practical purposes.
1100 try self.code.resize(self.code.items.len + 10);
1101 // REX.W == 0x48
1102 self.code.items[self.code.items.len - 10] = 0x48;
1103 self.code.items[self.code.items.len - 9] = 0xA1;
1104 const imm_ptr = self.code.items[self.code.items.len - 8 ..][0..8];
1105 mem.writeIntLittle(u64, imm_ptr, x);
1106 } else {
1107 // This requires two instructions; a move imm as used above, followed by an indirect load using the register
1108 // as the address and the register as the destination.
1109 //
1110 // This cannot be used if the lower three bits of the id are equal to four or five, as there
1111 // is no way to possibly encode it. This means that RSP, RBP, R12, and R13 cannot be used with
1112 // this instruction.
1113 const id3 = @truncate(u3, reg.id());
1114 std.debug.assert(id3 != 4 and id3 != 5);
1115
1116 // Rather than duplicate the logic used for the move, we just use a self-call with a new MCValue.
1117 try self.genSetReg(src, reg, MCValue{ .immediate = x });
1118
1119 // Now, the register contains the address of the value to load into it
1120 // Currently, we're only allowing 64-bit registers, so we need the `REX.W 8B /r` variant.
1121 // TODO: determine whether to allow other sized registers, and if so, handle them properly.
1122 // This operation requires three bytes: REX 0x8B R/M
1123 try self.code.ensureCapacity(self.code.items.len + 3);
1124 // For this operation, we want R/M mode *zero* (use register indirectly), and the two register
1125 // values must match. Thus, it's 00ABCABC where ABC is the lower three bits of the register ID.
1126 //
1127 // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both*
1128 // register operands need to be marked as extended.
1129 self.rex(.{ .w = true, .b = reg.isExtended(), .r = reg.isExtended() });
1130 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());
1131 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });
1132 }
1133 }
1134 },
1135 .stack_offset => |off| {
1136 return self.fail(src, "TODO implement genSetReg for stack variables", .{});
1137 },
1130 },1138 },
1131 },1139 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),
1132 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),1140 }
1133 }1141 }
1134 }
11351142
1136 fn genPtrToInt(self: *Function, inst: *ir.Inst.PtrToInt) !MCValue {1143 fn genPtrToInt(self: *Self, inst: *ir.Inst.PtrToInt) !MCValue {
1137 // no-op1144 // no-op
1138 return self.resolveInst(inst.args.ptr);1145 return self.resolveInst(inst.args.ptr);
1139 }1146 }
11401147
1141 fn genBitCast(self: *Function, inst: *ir.Inst.BitCast) !MCValue {1148 fn genBitCast(self: *Self, inst: *ir.Inst.BitCast) !MCValue {
1142 const operand = try self.resolveInst(inst.args.operand);1149 const operand = try self.resolveInst(inst.args.operand);
1143 return operand;1150 return operand;
1144 }1151 }
11451152
1146 fn resolveInst(self: *Function, inst: *ir.Inst) !MCValue {1153 fn resolveInst(self: *Self, inst: *ir.Inst) !MCValue {
1147 // Constants have static lifetimes, so they are always memoized in the outer most table.1154 // Constants have static lifetimes, so they are always memoized in the outer most table.
1148 if (inst.cast(ir.Inst.Constant)) |const_inst| {1155 if (inst.cast(ir.Inst.Constant)) |const_inst| {
1149 const branch = &self.branch_stack.items[0];1156 const branch = &self.branch_stack.items[0];
1150 const gop = try branch.inst_table.getOrPut(self.gpa, inst);1157 const gop = try branch.inst_table.getOrPut(self.gpa, inst);
1151 if (!gop.found_existing) {1158 if (!gop.found_existing) {
1152 gop.entry.value = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });1159 gop.entry.value = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
1160 }
1161 return gop.entry.value;
1153 }1162 }
1154 return gop.entry.value;
1155 }
11561163
1157 // Treat each stack item as a "layer" on top of the previous one.1164 // Treat each stack item as a "layer" on top of the previous one.
1158 var i: usize = self.branch_stack.items.len;1165 var i: usize = self.branch_stack.items.len;
1159 while (true) {1166 while (true) {
1160 i -= 1;1167 i -= 1;
1161 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {1168 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
1162 return mcv;1169 return mcv;
1170 }
1163 }1171 }
1164 }1172 }
1165 }
11661173
1167 fn copyToNewRegister(self: *Function, inst: *ir.Inst) !MCValue {1174 fn moveToNewRegister(self: *Self, inst: *ir.Inst) !MCValue {
1168 return self.fail(inst.src, "TODO implement copyToNewRegister", .{});1175 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1169 }1176 return self.fail(inst.src, "TODO implement moveToNewRegister", .{});
1177 }
11701178
1171 /// If the MCValue is an immediate, and it does not fit within this type,1179 /// If the MCValue is an immediate, and it does not fit within this type,
1172 /// we put it in a register.1180 /// we put it in a register.
1173 /// A potential opportunity for future optimization here would be keeping track1181 /// A potential opportunity for future optimization here would be keeping track
1174 /// of the fact that the instruction is available both as an immediate1182 /// of the fact that the instruction is available both as an immediate
1175 /// and as a register.1183 /// and as a register.
1176 fn limitImmediateType(self: *Function, inst: *ir.Inst, comptime T: type) !MCValue {1184 fn limitImmediateType(self: *Self, inst: *ir.Inst, comptime T: type) !MCValue {
1177 const mcv = try self.resolveInst(inst);1185 const mcv = try self.resolveInst(inst);
1178 const ti = @typeInfo(T).Int;1186 const ti = @typeInfo(T).Int;
1179 switch (mcv) {1187 switch (mcv) {
1180 .immediate => |imm| {1188 .immediate => |imm| {
1181 // This immediate is unsigned.1189 // This immediate is unsigned.
1182 const U = @Type(.{1190 const U = @Type(.{
1183 .Int = .{1191 .Int = .{
1184 .bits = ti.bits - @boolToInt(ti.is_signed),1192 .bits = ti.bits - @boolToInt(ti.is_signed),
1185 .is_signed = false,1193 .is_signed = false,
1186 },1194 },
1187 });1195 });
1188 if (imm >= std.math.maxInt(U)) {1196 if (imm >= std.math.maxInt(U)) {
1189 return self.copyToNewRegister(inst);1197 return self.moveToNewRegister(inst);
1190 }1198 }
1191 },1199 },
1192 else => {},1200 else => {},
1201 }
1202 return mcv;
1193 }1203 }
1194 return mcv;
1195 }
11961204
1197 fn genTypedValue(self: *Function, src: usize, typed_value: TypedValue) !MCValue {1205 fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) !MCValue {
1198 const ptr_bits = self.target.cpu.arch.ptrBitWidth();1206 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1199 const ptr_bytes: u64 = @divExact(ptr_bits, 8);1207 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1200 switch (typed_value.ty.zigTypeTag()) {1208 switch (typed_value.ty.zigTypeTag()) {
1201 .Pointer => {1209 .Pointer => {
1202 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {1210 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
1203 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];1211 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
1204 const decl = payload.decl;1212 const decl = payload.decl;
1205 const got_addr = got.p_vaddr + decl.link.offset_table_index * ptr_bytes;1213 const got_addr = got.p_vaddr + decl.link.offset_table_index * ptr_bytes;
1206 return MCValue{ .memory = got_addr };1214 return MCValue{ .memory = got_addr };
1207 }1215 }
1208 return self.fail(src, "TODO codegen more kinds of const pointers", .{});1216 return self.fail(src, "TODO codegen more kinds of const pointers", .{});
1209 },1217 },
1210 .Int => {1218 .Int => {
1211 const info = typed_value.ty.intInfo(self.target.*);1219 const info = typed_value.ty.intInfo(self.target.*);
1212 if (info.bits > ptr_bits or info.signed) {1220 if (info.bits > ptr_bits or info.signed) {
1213 return self.fail(src, "TODO const int bigger than ptr and signed int", .{});1221 return self.fail(src, "TODO const int bigger than ptr and signed int", .{});
1214 }1222 }
1215 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };1223 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
1216 },1224 },
1217 .Bool => {1225 .Bool => {
1218 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };1226 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
1219 },1227 },
1220 .ComptimeInt => unreachable, // semantic analysis prevents this1228 .ComptimeInt => unreachable, // semantic analysis prevents this
1221 .ComptimeFloat => unreachable, // semantic analysis prevents this1229 .ComptimeFloat => unreachable, // semantic analysis prevents this
1222 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),1230 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
1231 }
1223 }1232 }
1224 }
12251233
1226 fn resolveParameters(1234 fn resolveParameters(
1227 self: *Function,1235 self: *Self,
1228 src: usize,1236 src: usize,
1229 cc: std.builtin.CallingConvention,1237 cc: std.builtin.CallingConvention,
1230 param_types: []const Type,1238 param_types: []const Type,
1231 results: []MCValue,1239 results: []MCValue,
1232 ) !u32 {1240 ) !u32 {
1233 switch (self.target.cpu.arch) {1241 switch (arch) {
1234 .x86_64 => {1242 .x86_64 => {
1235 switch (cc) {1243 switch (cc) {
1236 .Naked => {1244 .Naked => {
1237 assert(results.len == 0);1245 assert(results.len == 0);
1238 return 0;1246 return 0;
1239 },1247 },
1240 .Unspecified, .C => {1248 .Unspecified, .C => {
1241 var next_int_reg: usize = 0;1249 var next_int_reg: usize = 0;
1242 var next_stack_offset: u32 = 0;1250 var next_stack_offset: u32 = 0;
12431251
1244 const integer_registers = [_]Reg(.x86_64){ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };1252 const integer_registers = [_]Reg{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
1245 for (param_types) |ty, i| {1253 for (param_types) |ty, i| {
1246 switch (ty.zigTypeTag()) {1254 switch (ty.zigTypeTag()) {
1247 .Bool, .Int => {1255 .Bool, .Int => {
1248 if (next_int_reg >= integer_registers.len) {1256 if (next_int_reg >= integer_registers.len) {
1249 results[i] = .{ .stack_offset = next_stack_offset };1257 results[i] = .{ .stack_offset = next_stack_offset };
1250 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));1258 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));
1251 } else {1259 } else {
1252 results[i] = .{ .register = @enumToInt(integer_registers[next_int_reg]) };1260 results[i] = .{ .register = integer_registers[next_int_reg] };
1253 next_int_reg += 1;1261 next_int_reg += 1;
1254 }1262 }
1255 },1263 },
1256 else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}),1264 else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}),
1265 }
1257 }1266 }
1258 }1267 return next_stack_offset;
1259 return next_stack_offset;1268 },
1260 },1269 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),
1261 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),1270 }
1262 }1271 },
1263 },1272 else => return self.fail(src, "TODO implement C ABI support for {}", .{self.target.cpu.arch}),
1264 else => return self.fail(src, "TODO implement C ABI support for {}", .{self.target.cpu.arch}),1273 }
1265 }1274 }
1266 }
12671275
1268 fn fail(self: *Function, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {1276 fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {
1269 @setCold(true);1277 @setCold(true);
1270 assert(self.err_msg == null);1278 assert(self.err_msg == null);
1271 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src, format, args);1279 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src, format, args);
1272 return error.CodegenFail;1280 return error.CodegenFail;
1273 }1281 }
1274};
12751282
1276const x86_64 = @import("codegen/x86_64.zig");1283 const Reg = switch (arch) {
1277const x86 = @import("codegen/x86.zig");1284 .i386 => x86.Register,
1285 .x86_64 => x86_64.Register,
1286 else => enum { dummy },
1287 };
12781288
1279fn Reg(comptime arch: Target.Cpu.Arch) type {1289 fn parseRegName(name: []const u8) ?Reg {
1280 return switch (arch) {1290 return std.meta.stringToEnum(Reg, name);
1281 .i386 => x86.Register,1291 }
1282 .x86_64 => x86_64.Register,
1283 else => @compileError("TODO add more register enums"),
1284 };1292 };
1285}1293}
1286
1287fn parseRegName(comptime arch: Target.Cpu.Arch, name: []const u8) ?Reg(arch) {
1288 return std.meta.stringToEnum(Reg(arch), name);
1289}
src-self-hosted/codegen/x86_64.zig+4-1
...@@ -67,4 +67,7 @@ pub const Register = enum(u8) {...@@ -67,4 +67,7 @@ pub const Register = enum(u8) {
67 }67 }
68};68};
6969
70// zig fmt: on
\ No newline at end of file
70// zig fmt: on
71
72/// These registers belong to the called function.
73pub const callee_preserved = [_]Register{ rax, rcx, rdx, rsi, rdi, r8, r9, r10, r11 };