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;
1111const Target = std.Target;
1212const Allocator = mem.Allocator;
1313const trace = @import("tracy.zig").trace;
14const x86_64 = @import("codegen/x86_64.zig");
15const x86 = @import("codegen/x86.zig");
1416
1517/// The codegen-related data that is stored in `ir.Inst.Block` instructions.
1618pub const BlockData = struct {
......@@ -32,67 +34,75 @@ pub const Result = union(enum) {
3234 fail: *Module.ErrorMsg,
3335};
3436
37pub const GenerateSymbolError = error{
38 OutOfMemory,
39 /// A Decl that this symbol depends on had a semantic analysis failure.
40 AnalysisFail,
41};
42
3543pub fn generateSymbol(
3644 bin_file: *link.File.Elf,
3745 src: usize,
3846 typed_value: TypedValue,
3947 code: *std.ArrayList(u8),
40) error{
41 OutOfMemory,
42 /// A Decl that this symbol depends on had a semantic analysis failure.
43 AnalysisFail,
44}!Result {
48) GenerateSymbolError!Result {
4549 const tracy = trace(@src());
4650 defer tracy.end();
4751
4852 switch (typed_value.ty.zigTypeTag()) {
4953 .Fn => {
50 const module_fn = typed_value.val.cast(Value.Payload.Function).?.func;
51
52 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
53 const param_types = try bin_file.allocator.alloc(Type, fn_type.fnParamLen());
54 defer bin_file.allocator.free(param_types);
55 fn_type.fnParamTypes(param_types);
56 var mc_args = try bin_file.allocator.alloc(MCValue, param_types.len);
57 defer bin_file.allocator.free(mc_args);
58
59 var branch_stack = std.ArrayList(Function.Branch).init(bin_file.allocator);
60 defer {
61 assert(branch_stack.items.len == 1);
62 branch_stack.items[0].deinit(bin_file.allocator);
63 branch_stack.deinit();
64 }
65 const branch = try branch_stack.addOne();
66 branch.* = .{};
67
68 var function = Function{
69 .gpa = bin_file.allocator,
70 .target = &bin_file.options.target,
71 .bin_file = bin_file,
72 .mod_fn = module_fn,
73 .code = code,
74 .err_msg = null,
75 .args = mc_args,
76 .arg_index = 0,
77 .branch_stack = &branch_stack,
78 .src = src,
79 };
80
81 const cc = fn_type.fnCallingConvention();
82 branch.max_end_stack = function.resolveParameters(src, cc, param_types, mc_args) catch |err| switch (err) {
83 error.CodegenFail => return Result{ .fail = function.err_msg.? },
84 else => |e| return e,
85 };
86
87 function.gen() catch |err| switch (err) {
88 error.CodegenFail => return Result{ .fail = function.err_msg.? },
89 else => |e| return e,
90 };
91
92 if (function.err_msg) |em| {
93 return Result{ .fail = em };
94 } else {
95 return Result{ .appended = {} };
54 switch (bin_file.options.target.cpu.arch) {
55 .arm => return Function(.arm).generateSymbol(bin_file, src, typed_value, code),
56 .armeb => return Function(.armeb).generateSymbol(bin_file, src, typed_value, code),
57 .aarch64 => return Function(.aarch64).generateSymbol(bin_file, src, typed_value, code),
58 .aarch64_be => return Function(.aarch64_be).generateSymbol(bin_file, src, typed_value, code),
59 .aarch64_32 => return Function(.aarch64_32).generateSymbol(bin_file, src, typed_value, code),
60 .arc => return Function(.arc).generateSymbol(bin_file, src, typed_value, code),
61 .avr => return Function(.avr).generateSymbol(bin_file, src, typed_value, code),
62 .bpfel => return Function(.bpfel).generateSymbol(bin_file, src, typed_value, code),
63 .bpfeb => return Function(.bpfeb).generateSymbol(bin_file, src, typed_value, code),
64 .hexagon => return Function(.hexagon).generateSymbol(bin_file, src, typed_value, code),
65 .mips => return Function(.mips).generateSymbol(bin_file, src, typed_value, code),
66 .mipsel => return Function(.mipsel).generateSymbol(bin_file, src, typed_value, code),
67 .mips64 => return Function(.mips64).generateSymbol(bin_file, src, typed_value, code),
68 .mips64el => return Function(.mips64el).generateSymbol(bin_file, src, typed_value, code),
69 .msp430 => return Function(.msp430).generateSymbol(bin_file, src, typed_value, code),
70 .powerpc => return Function(.powerpc).generateSymbol(bin_file, src, typed_value, code),
71 .powerpc64 => return Function(.powerpc64).generateSymbol(bin_file, src, typed_value, code),
72 .powerpc64le => return Function(.powerpc64le).generateSymbol(bin_file, src, typed_value, code),
73 .r600 => return Function(.r600).generateSymbol(bin_file, src, typed_value, code),
74 .amdgcn => return Function(.amdgcn).generateSymbol(bin_file, src, typed_value, code),
75 .riscv32 => return Function(.riscv32).generateSymbol(bin_file, src, typed_value, code),
76 .riscv64 => return Function(.riscv64).generateSymbol(bin_file, src, typed_value, code),
77 .sparc => return Function(.sparc).generateSymbol(bin_file, src, typed_value, code),
78 .sparcv9 => return Function(.sparcv9).generateSymbol(bin_file, src, typed_value, code),
79 .sparcel => return Function(.sparcel).generateSymbol(bin_file, src, typed_value, code),
80 .s390x => return Function(.s390x).generateSymbol(bin_file, src, typed_value, code),
81 .tce => return Function(.tce).generateSymbol(bin_file, src, typed_value, code),
82 .tcele => return Function(.tcele).generateSymbol(bin_file, src, typed_value, code),
83 .thumb => return Function(.thumb).generateSymbol(bin_file, src, typed_value, code),
84 .thumbeb => return Function(.thumbeb).generateSymbol(bin_file, src, typed_value, code),
85 .i386 => return Function(.i386).generateSymbol(bin_file, src, typed_value, code),
86 .x86_64 => return Function(.x86_64).generateSymbol(bin_file, src, typed_value, code),
87 .xcore => return Function(.xcore).generateSymbol(bin_file, src, typed_value, code),
88 .nvptx => return Function(.nvptx).generateSymbol(bin_file, src, typed_value, code),
89 .nvptx64 => return Function(.nvptx64).generateSymbol(bin_file, src, typed_value, code),
90 .le32 => return Function(.le32).generateSymbol(bin_file, src, typed_value, code),
91 .le64 => return Function(.le64).generateSymbol(bin_file, src, typed_value, code),
92 .amdil => return Function(.amdil).generateSymbol(bin_file, src, typed_value, code),
93 .amdil64 => return Function(.amdil64).generateSymbol(bin_file, src, typed_value, code),
94 .hsail => return Function(.hsail).generateSymbol(bin_file, src, typed_value, code),
95 .hsail64 => return Function(.hsail64).generateSymbol(bin_file, src, typed_value, code),
96 .spir => return Function(.spir).generateSymbol(bin_file, src, typed_value, code),
97 .spir64 => return Function(.spir64).generateSymbol(bin_file, src, typed_value, code),
98 .kalimba => return Function(.kalimba).generateSymbol(bin_file, src, typed_value, code),
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),
96106 }
97107 },
98108 .Array => {
......@@ -189,1101 +199,1095 @@ const InnerError = error{
189199 CodegenFail,
190200};
191201
192const MCValue = union(enum) {
193 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
194 none,
195 /// Control flow will not allow this value to be observed.
196 unreach,
197 /// No more references to this value remain.
198 dead,
199 /// A pointer-sized integer that fits in a register.
200 immediate: u64,
201 /// The constant was emitted into the code, at this offset.
202 embedded_in_code: usize,
203 /// The value is in a target-specific register. The value can
204 /// be @intToEnum casted to the respective Reg enum.
205 register: usize,
206 /// The value is in memory at a hard-coded address.
207 memory: u64,
208 /// The value is one of the stack variables.
209 stack_offset: u64,
210 /// The value is in the compare flags assuming an unsigned operation,
211 /// with this operator applied on top of it.
212 compare_flags_unsigned: std.math.CompareOperator,
213 /// The value is in the compare flags assuming a signed operation,
214 /// with this operator applied on top of it.
215 compare_flags_signed: std.math.CompareOperator,
216
217 fn isMemory(mcv: MCValue) bool {
218 return switch (mcv) {
219 .embedded_in_code, .memory, .stack_offset => true,
220 else => false,
202fn Function(comptime arch: std.Target.Cpu.Arch) type {
203 return struct {
204 gpa: *Allocator,
205 bin_file: *link.File.Elf,
206 target: *const std.Target,
207 mod_fn: *const Module.Fn,
208 code: *std.ArrayList(u8),
209 err_msg: ?*ErrorMsg,
210 args: []MCValue,
211 arg_index: usize,
212 src: usize,
213
214 /// Whenever there is a runtime branch, we push a Branch onto this stack,
215 /// and pop it off when the runtime branch joins. This provides an "overlay"
216 /// of the table of mappings from instructions to `MCValue` from within the branch.
217 /// This way we can modify the `MCValue` for an instruction in different ways
218 /// within different branches. Special consideration is needed when a branch
219 /// joins with its parent, to make sure all instructions have the same MCValue
220 /// across each runtime branch upon joining.
221 branch_stack: *std.ArrayList(Branch),
222
223 const MCValue = union(enum) {
224 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
225 none,
226 /// Control flow will not allow this value to be observed.
227 unreach,
228 /// No more references to this value remain.
229 dead,
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 }
221279 };
222 }
223280
224 fn isImmediate(mcv: MCValue) bool {
225 return switch (mcv) {
226 .immediate => true,
227 else => false,
281 const Branch = struct {
282 inst_table: std.AutoHashMapUnmanaged(*ir.Inst, MCValue) = .{},
283
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 }
228301 };
229 }
230302
231 fn isMutable(mcv: MCValue) bool {
232 return switch (mcv) {
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,
303 const RegisterAllocation = struct {
304 inst: *ir.Inst,
247305 };
248 }
249};
250306
251const Function = struct {
252 gpa: *Allocator,
253 bin_file: *link.File.Elf,
254 target: *const std.Target,
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,
307 const StackAllocation = struct {
308 inst: *ir.Inst,
309 size: u32,
310 };
261311
262 /// Whenever there is a runtime branch, we push a Branch onto this stack,
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 };
312 const Self = @This();
292313
293 const RegisterAllocation = struct {
294 inst: *ir.Inst,
295 };
314 fn generateSymbol(
315 bin_file: *link.File.Elf,
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 {
298 inst: *ir.Inst,
299 size: u32,
300 };
322 const fn_type = module_fn.owner_decl.typed_value.most_recent.typed_value.ty;
323 const param_types = try bin_file.allocator.alloc(Type, fn_type.fnParamLen());
324 defer bin_file.allocator.free(param_types);
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 {
303 switch (self.target.cpu.arch) {
304 .arm => return self.genArch(.arm),
305 .armeb => return self.genArch(.armeb),
306 .aarch64 => return self.genArch(.aarch64),
307 .aarch64_be => return self.genArch(.aarch64_be),
308 .aarch64_32 => return self.genArch(.aarch64_32),
309 .arc => return self.genArch(.arc),
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 }
329 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
330 defer {
331 assert(branch_stack.items.len == 1);
332 branch_stack.items[0].deinit(bin_file.allocator);
333 branch_stack.deinit();
334 }
335 const branch = try branch_stack.addOne();
336 branch.* = .{};
357337
358 fn genArch(self: *Function, comptime arch: std.Target.Cpu.Arch) !void {
359 try self.code.ensureCapacity(self.code.items.len + 11);
360
361 // push rbp
362 // mov rbp, rsp
363 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x55, 0x48, 0x89, 0xe5 });
364
365 // sub rsp, x
366 const stack_end = self.branch_stack.items[0].max_end_stack;
367 if (stack_end > std.math.maxInt(i32)) {
368 return self.fail(self.src, "too much stack used in call parameters", .{});
369 } else if (stack_end > std.math.maxInt(i8)) {
370 // 48 83 ec xx sub rsp,0x10
371 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x81, 0xec });
372 const x = @intCast(u32, stack_end);
373 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), x);
374 } else if (stack_end != 0) {
375 // 48 81 ec xx xx xx xx sub rsp,0x80
376 const x = @intCast(u8, stack_end);
377 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x48, 0x83, 0xec, x });
338 var function = Self{
339 .gpa = bin_file.allocator,
340 .target = &bin_file.options.target,
341 .bin_file = bin_file,
342 .mod_fn = module_fn,
343 .code = code,
344 .err_msg = null,
345 .args = mc_args,
346 .arg_index = 0,
347 .branch_stack = &branch_stack,
348 .src = src,
349 };
350
351 const cc = fn_type.fnCallingConvention();
352 branch.max_end_stack = function.resolveParameters(src, cc, param_types, mc_args) catch |err| switch (err) {
353 error.CodegenFail => return Result{ .fail = function.err_msg.? },
354 else => |e| return e,
355 };
356
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 }
378367 }
379368
380 try self.genBody(self.mod_fn.analysis.success, arch);
381 }
369 fn gen(self: *Self) !void {
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 {
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);
391 try self.genBody(self.mod_fn.analysis.success);
388392 }
389 }
390393
391 fn genFuncInst(self: *Function, inst: *ir.Inst, comptime arch: std.Target.Cpu.Arch) !MCValue {
392 switch (inst.tag) {
393 .add => return self.genAdd(inst.cast(ir.Inst.Add).?, arch),
394 .arg => return self.genArg(inst.cast(ir.Inst.Arg).?),
395 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?, arch),
396 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
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),
394 fn genBody(self: *Self, body: ir.Body) InnerError!void {
395 const inst_table = &self.branch_stack.items[0].inst_table;
396 for (body.instructions) |inst| {
397 const new_inst = try self.genFuncInst(inst);
398 try inst_table.putNoClobber(self.gpa, inst, new_inst);
399 }
413400 }
414 }
415401
416 fn genNot(self: *Function, inst: *ir.Inst.Not, comptime arch: std.Target.Cpu.Arch) !MCValue {
417 // No side effects, so if it's unreferenced, do nothing.
418 if (inst.base.isUnused())
419 return MCValue.dead;
420 const operand = try self.resolveInst(inst.args.operand);
421 switch (operand) {
422 .dead => unreachable,
423 .unreach => unreachable,
424 .compare_flags_unsigned => |op| return MCValue{
425 .compare_flags_unsigned = switch (op) {
426 .gte => .lt,
427 .gt => .lte,
428 .neq => .eq,
429 .lt => .gte,
430 .lte => .gt,
431 .eq => .neq,
432 },
433 },
434 .compare_flags_signed => |op| return MCValue{
435 .compare_flags_signed = switch (op) {
436 .gte => .lt,
437 .gt => .lte,
438 .neq => .eq,
439 .lt => .gte,
440 .lte => .gt,
441 .eq => .neq,
442 },
443 },
444 else => {},
402 fn genFuncInst(self: *Self, inst: *ir.Inst) !MCValue {
403 switch (inst.tag) {
404 .add => return self.genAdd(inst.cast(ir.Inst.Add).?),
405 .arg => return self.genArg(inst.cast(ir.Inst.Arg).?),
406 .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?),
407 .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?),
408 .block => return self.genBlock(inst.cast(ir.Inst.Block).?),
409 .br => return self.genBr(inst.cast(ir.Inst.Br).?),
410 .breakpoint => return self.genBreakpoint(inst.src),
411 .brvoid => return self.genBrVoid(inst.cast(ir.Inst.BrVoid).?),
412 .call => return self.genCall(inst.cast(ir.Inst.Call).?),
413 .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?),
414 .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?),
415 .constant => unreachable, // excluded from function bodies
416 .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?),
417 .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?),
418 .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?),
419 .ret => return self.genRet(inst.cast(ir.Inst.Ret).?),
420 .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?),
421 .sub => return self.genSub(inst.cast(ir.Inst.Sub).?),
422 .unreach => return MCValue{ .unreach = {} },
423 .not => return self.genNot(inst.cast(ir.Inst.Not).?),
424 }
445425 }
446426
447 switch (arch) {
448 .x86_64 => {
449 var imm = ir.Inst.Constant{
450 .base = .{
451 .tag = .constant,
452 .deaths = 0,
453 .ty = inst.args.operand.ty,
454 .src = inst.args.operand.src,
427 fn genNot(self: *Self, inst: *ir.Inst.Not) !MCValue {
428 // No side effects, so if it's unreferenced, do nothing.
429 if (inst.base.isUnused())
430 return MCValue.dead;
431 const operand = try self.resolveInst(inst.args.operand);
432 switch (operand) {
433 .dead => unreachable,
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,
455443 },
456 .val = Value.initTag(.bool_true),
457 };
458 return try self.genX8664BinMath(&inst.base, inst.args.operand, &imm.base, 6, 0x30);
459 },
460 else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}),
461 }
462 }
444 },
445 .compare_flags_signed => |op| return MCValue{
446 .compare_flags_signed = switch (op) {
447 .gte => .lt,
448 .gt => .lte,
449 .neq => .eq,
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 {
465 // No side effects, so if it's unreferenced, do nothing.
466 if (inst.base.isUnused())
467 return MCValue.dead;
468 switch (arch) {
469 .x86_64 => {
470 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 0, 0x00);
471 },
472 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),
458 switch (arch) {
459 .x86_64 => {
460 var imm = ir.Inst.Constant{
461 .base = .{
462 .tag = .constant,
463 .deaths = 0,
464 .ty = inst.args.operand.ty,
465 .src = inst.args.operand.src,
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 }
473473 }
474 }
475474
476 fn genSub(self: *Function, inst: *ir.Inst.Sub, comptime arch: std.Target.Cpu.Arch) !MCValue {
477 // No side effects, so if it's unreferenced, do nothing.
478 if (inst.base.isUnused())
479 return MCValue.dead;
480 switch (arch) {
481 .x86_64 => {
482 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 5, 0x28);
483 },
484 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),
475 fn genAdd(self: *Self, inst: *ir.Inst.Add) !MCValue {
476 // No side effects, so if it's unreferenced, do nothing.
477 if (inst.base.isUnused())
478 return MCValue.dead;
479 switch (arch) {
480 .x86_64 => {
481 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 0, 0x00);
482 },
483 else => return self.fail(inst.base.src, "TODO implement add for {}", .{self.target.cpu.arch}),
484 }
485485 }
486 }
487486
488 /// ADD, SUB, XOR, OR, AND
489 fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue {
490 try self.code.ensureCapacity(self.code.items.len + 8);
491
492 const lhs = try self.resolveInst(op_lhs);
493 const rhs = try self.resolveInst(op_rhs);
494
495 // There are 2 operands, destination and source.
496 // Either one, but not both, can be a memory operand.
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;
487 fn genSub(self: *Self, inst: *ir.Inst.Sub) !MCValue {
488 // No side effects, so if it's unreferenced, do nothing.
489 if (inst.base.isUnused())
490 return MCValue.dead;
491 switch (arch) {
492 .x86_64 => {
493 return try self.genX8664BinMath(&inst.base, inst.args.lhs, inst.args.rhs, 5, 0x28);
494 },
495 else => return self.fail(inst.base.src, "TODO implement sub for {}", .{self.target.cpu.arch}),
524496 }
525 } else {
526 if (lhs.isMemory()) {
527 dst_mcv = try self.copyToNewRegister(op_lhs);
528 src_mcv = rhs;
497 }
498
499 /// ADD, SUB, XOR, OR, AND
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.
529517 src_inst = op_rhs;
530 } else {
531 dst_mcv = try self.copyToNewRegister(op_rhs);
532 src_mcv = lhs;
518 if (lhs.isMemory() and rhs.isMemory()) {
519 dst_mcv = try self.moveToNewRegister(op_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.
533528 src_inst = op_lhs;
534 }
535 }
536 // This instruction supports only signed 32-bit immediates at most. If the immediate
537 // value is larger than this, we put it in a register.
538 // A potential opportunity for future optimization here would be keeping track
539 // of the fact that the instruction is available both as an immediate
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);
529 if (lhs.isMemory() and rhs.isMemory()) {
530 dst_mcv = try self.moveToNewRegister(op_rhs);
531 src_mcv = lhs;
532 } else {
533 dst_mcv = rhs;
534 src_mcv = lhs;
545535 }
546 },
547 else => {},
548 }
536 } else {
537 if (lhs.isMemory()) {
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;
553 }
563 return dst_mcv;
564 }
554565
555 fn genX8664BinMathCode(self: *Function, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {
556 switch (dst_mcv) {
557 .none => unreachable,
558 .dead, .unreach, .immediate => unreachable,
559 .compare_flags_unsigned => unreachable,
560 .compare_flags_signed => unreachable,
561 .register => |dst_reg_usize| {
562 const dst_reg = @intToEnum(Reg(.x86_64), @intCast(u8, dst_reg_usize));
563 switch (src_mcv) {
564 .none => unreachable,
565 .dead, .unreach => unreachable,
566 .register => |src_reg_usize| {
567 const src_reg = @intToEnum(Reg(.x86_64), @intCast(u8, src_reg_usize));
568 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });
569 self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });
570 },
571 .immediate => |imm| {
572 const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode.
573 // 81 /opx id
574 if (imm32 <= std.math.maxInt(u7)) {
575 self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
576 self.code.appendSliceAssumeCapacity(&[_]u8{
577 0x83,
578 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
579 @intCast(u8, imm32),
580 });
581 } else {
582 self.rex(.{ .r = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
583 self.code.appendSliceAssumeCapacity(&[_]u8{
584 0x81,
585 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
586 });
587 std.mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), imm32);
588 }
589 },
590 .embedded_in_code, .memory, .stack_offset => {
591 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});
592 },
593 .compare_flags_unsigned => {
594 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
595 },
596 .compare_flags_signed => {
597 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
598 },
599 }
600 },
601 .embedded_in_code, .memory, .stack_offset => {
602 return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});
603 },
566 fn genX8664BinMathCode(self: *Self, src: usize, dst_mcv: MCValue, src_mcv: MCValue, opx: u8, mr: u8) !void {
567 switch (dst_mcv) {
568 .none => unreachable,
569 .dead, .unreach, .immediate => unreachable,
570 .compare_flags_unsigned => unreachable,
571 .compare_flags_signed => unreachable,
572 .register => |dst_reg| {
573 switch (src_mcv) {
574 .none => unreachable,
575 .dead, .unreach => unreachable,
576 .register => |src_reg| {
577 self.rex(.{ .b = dst_reg.isExtended(), .r = src_reg.isExtended(), .w = dst_reg.size() == 64 });
578 self.code.appendSliceAssumeCapacity(&[_]u8{ mr + 0x1, 0xC0 | (@as(u8, src_reg.id() & 0b111) << 3) | @as(u8, dst_reg.id() & 0b111) });
579 },
580 .immediate => |imm| {
581 const imm32 = @intCast(u31, imm); // This case must be handled before calling genX8664BinMathCode.
582 // 81 /opx id
583 if (imm32 <= std.math.maxInt(u7)) {
584 self.rex(.{ .b = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
585 self.code.appendSliceAssumeCapacity(&[_]u8{
586 0x83,
587 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
588 @intCast(u8, imm32),
589 });
590 } else {
591 self.rex(.{ .r = dst_reg.isExtended(), .w = dst_reg.size() == 64 });
592 self.code.appendSliceAssumeCapacity(&[_]u8{
593 0x81,
594 0xC0 | (opx << 3) | @truncate(u3, dst_reg.id()),
595 });
596 std.mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), imm32);
597 }
598 },
599 .embedded_in_code, .memory, .stack_offset => {
600 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source memory", .{});
601 },
602 .compare_flags_unsigned => {
603 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (unsigned)", .{});
604 },
605 .compare_flags_signed => {
606 return self.fail(src, "TODO implement x86 ADD/SUB/CMP source compare flag (signed)", .{});
607 },
608 }
609 },
610 .embedded_in_code, .memory, .stack_offset => {
611 return self.fail(src, "TODO implement x86 ADD/SUB/CMP destination memory", .{});
612 },
613 }
604614 }
605 }
606615
607 fn genArg(self: *Function, inst: *ir.Inst.Arg) !MCValue {
608 const i = self.arg_index;
609 self.arg_index += 1;
610 return self.args[i];
611 }
616 fn genArg(self: *Self, inst: *ir.Inst.Arg) !MCValue {
617 const i = self.arg_index;
618 self.arg_index += 1;
619 return self.args[i];
620 }
612621
613 fn genBreakpoint(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch) !MCValue {
614 switch (arch) {
615 .i386, .x86_64 => {
616 try self.code.append(0xcc); // int3
617 },
618 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
622 fn genBreakpoint(self: *Self, src: usize) !MCValue {
623 switch (arch) {
624 .i386, .x86_64 => {
625 try self.code.append(0xcc); // int3
626 },
627 else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
628 }
629 return .none;
619630 }
620 return .none;
621 }
622631
623 fn genCall(self: *Function, inst: *ir.Inst.Call, comptime arch: std.Target.Cpu.Arch) !MCValue {
624 const fn_ty = inst.args.func.ty;
625 const cc = fn_ty.fnCallingConvention();
626 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
627 defer self.gpa.free(param_types);
628 fn_ty.fnParamTypes(param_types);
629 var mc_args = try self.gpa.alloc(MCValue, param_types.len);
630 defer self.gpa.free(mc_args);
631 const stack_byte_count = try self.resolveParameters(inst.base.src, cc, param_types, mc_args);
632
633 switch (arch) {
634 .x86_64 => {
635 for (mc_args) |mc_arg, arg_i| {
636 const arg = inst.args.args[arg_i];
637 const arg_mcv = try self.resolveInst(inst.args.args[arg_i]);
638 switch (mc_arg) {
639 .none => continue,
640 .register => |reg| {
641 try self.genSetReg(arg.src, arch, @intToEnum(Reg(arch), @intCast(u8, reg)), arg_mcv);
642 // TODO interact with the register allocator to mark the instruction as moved.
643 },
644 .stack_offset => {
645 // Here we need to emit instructions like this:
646 // mov qword ptr [rsp + stack_offset], x
647 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
648 },
649 .immediate => unreachable,
650 .unreach => unreachable,
651 .dead => unreachable,
652 .embedded_in_code => unreachable,
653 .memory => unreachable,
654 .compare_flags_signed => unreachable,
655 .compare_flags_unsigned => unreachable,
632 fn genCall(self: *Self, inst: *ir.Inst.Call) !MCValue {
633 const fn_ty = inst.args.func.ty;
634 const cc = fn_ty.fnCallingConvention();
635 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
636 defer self.gpa.free(param_types);
637 fn_ty.fnParamTypes(param_types);
638 var mc_args = try self.gpa.alloc(MCValue, param_types.len);
639 defer self.gpa.free(mc_args);
640 const stack_byte_count = try self.resolveParameters(inst.base.src, cc, param_types, mc_args);
641
642 switch (arch) {
643 .x86_64 => {
644 for (mc_args) |mc_arg, arg_i| {
645 const arg = inst.args.args[arg_i];
646 const arg_mcv = try self.resolveInst(inst.args.args[arg_i]);
647 switch (mc_arg) {
648 .none => continue,
649 .register => |reg| {
650 try self.genSetReg(arg.src, reg, arg_mcv);
651 // TODO interact with the register allocator to mark the instruction as moved.
652 },
653 .stack_offset => {
654 // Here we need to emit instructions like this:
655 // mov qword ptr [rsp + stack_offset], x
656 return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{});
657 },
658 .immediate => unreachable,
659 .unreach => unreachable,
660 .dead => unreachable,
661 .embedded_in_code => unreachable,
662 .memory => unreachable,
663 .compare_flags_signed => unreachable,
664 .compare_flags_unsigned => unreachable,
665 }
656666 }
657 }
658667
659 if (inst.args.func.cast(ir.Inst.Constant)) |func_inst| {
660 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
661 const func = func_val.func;
662 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
663 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
664 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);
666 // ff 14 25 xx xx xx xx call [addr]
667 try self.code.ensureCapacity(self.code.items.len + 7);
668 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
669 mem.writeIntLittle(u32, self.code.addManyAsArrayAssumeCapacity(4), got_addr);
668 if (inst.args.func.cast(ir.Inst.Constant)) |func_inst| {
669 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
670 const func = func_val.func;
671 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
672 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
673 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
674 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes);
675 // ff 14 25 xx xx xx xx call [addr]
676 try self.code.ensureCapacity(self.code.items.len + 7);
677 self.code.appendSliceAssumeCapacity(&[3]u8{ 0xff, 0x14, 0x25 });
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 }
670682 } 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", .{});
672684 }
673 } else {
674 return self.fail(inst.base.src, "TODO implement calling runtime known function pointer", .{});
675 }
676 },
677 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
678 }
685 },
686 else => return self.fail(inst.base.src, "TODO implement call for {}", .{self.target.cpu.arch}),
687 }
679688
680 const return_type = fn_ty.fnReturnType();
681 switch (return_type.zigTypeTag()) {
682 .Void => return MCValue{ .none = {} },
683 .NoReturn => return MCValue{ .unreach = {} },
684 else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),
689 const return_type = fn_ty.fnReturnType();
690 switch (return_type.zigTypeTag()) {
691 .Void => return MCValue{ .none = {} },
692 .NoReturn => return MCValue{ .unreach = {} },
693 else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),
694 }
685695 }
686 }
687696
688 fn ret(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch, mcv: MCValue) !MCValue {
689 if (mcv != .none) {
690 return self.fail(src, "TODO implement return with non-void operand", .{});
691 }
692 switch (arch) {
693 .i386 => {
694 try self.code.append(0xc3); // ret
695 },
696 .x86_64 => {
697 try self.code.appendSlice(&[_]u8{
698 0x5d, // pop rbp
699 0xc3, // ret
700 });
701 },
702 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
697 fn ret(self: *Self, src: usize, mcv: MCValue) !MCValue {
698 if (mcv != .none) {
699 return self.fail(src, "TODO implement return with non-void operand", .{});
700 }
701 switch (arch) {
702 .i386 => {
703 try self.code.append(0xc3); // ret
704 },
705 .x86_64 => {
706 try self.code.appendSlice(&[_]u8{
707 0x5d, // pop rbp
708 0xc3, // ret
709 });
710 },
711 else => return self.fail(src, "TODO implement return for {}", .{self.target.cpu.arch}),
712 }
713 return .unreach;
703714 }
704 return .unreach;
705 }
706715
707 fn genRet(self: *Function, inst: *ir.Inst.Ret, comptime arch: std.Target.Cpu.Arch) !MCValue {
708 const operand = try self.resolveInst(inst.args.operand);
709 return self.ret(inst.base.src, arch, operand);
710 }
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 }
716 fn genRet(self: *Self, inst: *ir.Inst.Ret) !MCValue {
717 const operand = try self.resolveInst(inst.args.operand);
718 return self.ret(inst.base.src, operand);
719 }
715720
716 fn genCmp(self: *Function, inst: *ir.Inst.Cmp, comptime arch: std.Target.Cpu.Arch) !MCValue {
717 // No side effects, so if it's unreferenced, do nothing.
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}),
721 fn genRetVoid(self: *Self, inst: *ir.Inst.RetVoid) !MCValue {
722 return self.ret(inst.base.src, .none);
746723 }
747 }
748724
749 fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue {
750 switch (arch) {
751 .x86_64 => {
752 try self.code.ensureCapacity(self.code.items.len + 6);
753
754 const cond = try self.resolveInst(inst.args.condition);
755 switch (cond) {
756 .compare_flags_signed => |cmp_op| {
757 // Here we map to the opposite opcode because the jump is to the false branch.
758 const opcode: u8 = switch (cmp_op) {
759 .gte => 0x8c,
760 .gt => 0x8e,
761 .neq => 0x84,
762 .lt => 0x8d,
763 .lte => 0x8f,
764 .eq => 0x85,
765 };
766 return self.genX86CondBr(inst, opcode, arch);
767 },
768 .compare_flags_unsigned => |cmp_op| {
769 // Here we map to the opposite opcode because the jump is to the false branch.
770 const opcode: u8 = switch (cmp_op) {
771 .gte => 0x82,
772 .gt => 0x86,
773 .neq => 0x84,
774 .lt => 0x83,
775 .lte => 0x87,
776 .eq => 0x85,
777 };
778 return self.genX86CondBr(inst, opcode, arch);
779 },
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}),
725 fn genCmp(self: *Self, inst: *ir.Inst.Cmp) !MCValue {
726 // No side effects, so if it's unreferenced, do nothing.
727 if (inst.base.isUnused())
728 return MCValue.dead;
729 switch (arch) {
730 .x86_64 => {
731 try self.code.ensureCapacity(self.code.items.len + 8);
732
733 const lhs = try self.resolveInst(inst.args.lhs);
734 const rhs = try self.resolveInst(inst.args.rhs);
735
736 // There are 2 operands, destination and source.
737 // Either one, but not both, can be a memory operand.
738 // Source operand can be an immediate, 8 bits or 32 bits.
739 const dst_mcv = if (lhs.isImmediate() or (lhs.isMemory() and rhs.isMemory()))
740 try self.moveToNewRegister(inst.args.lhs)
741 else
742 lhs;
743 // This instruction supports only signed 32-bit immediates at most.
744 const src_mcv = try self.limitImmediateType(inst.args.rhs, i32);
745
746 try self.genX8664BinMathCode(inst.base.src, dst_mcv, src_mcv, 7, 0x38);
747 const info = inst.args.lhs.ty.intInfo(self.target.*);
748 if (info.signed) {
749 return MCValue{ .compare_flags_signed = inst.args.op };
750 } else {
751 return MCValue{ .compare_flags_unsigned = inst.args.op };
752 }
753 },
754 else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.target.cpu.arch}),
755 }
797756 }
798 }
799757
800 fn genX86CondBr(self: *Function, inst: *ir.Inst.CondBr, opcode: u8, comptime arch: std.Target.Cpu.Arch) !MCValue {
801 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });
802 const reloc = Reloc{ .rel32 = self.code.items.len };
803 self.code.items.len += 4;
804 try self.genBody(inst.args.true_body, arch);
805 try self.performReloc(inst.base.src, reloc);
806 try self.genBody(inst.args.false_body, arch);
807 return MCValue.unreach;
808 }
758 fn genCondBr(self: *Self, inst: *ir.Inst.CondBr) !MCValue {
759 switch (arch) {
760 .x86_64 => {
761 try self.code.ensureCapacity(self.code.items.len + 6);
762
763 const cond = try self.resolveInst(inst.args.condition);
764 switch (cond) {
765 .compare_flags_signed => |cmp_op| {
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 {
811 switch (arch) {
812 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),
808 fn genX86CondBr(self: *Self, inst: *ir.Inst.CondBr, opcode: u8) !MCValue {
809 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode });
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;
813816 }
814 }
815817
816 fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull, comptime arch: std.Target.Cpu.Arch) !MCValue {
817 // Here you can specialize this instruction if it makes sense to, otherwise the default
818 // will call genIsNull and invert the result.
819 switch (arch) {
820 else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}),
818 fn genIsNull(self: *Self, inst: *ir.Inst.IsNull) !MCValue {
819 switch (arch) {
820 else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.target.cpu.arch}),
821 }
821822 }
822 }
823823
824 fn genBlock(self: *Function, inst: *ir.Inst.Block, comptime arch: std.Target.Cpu.Arch) !MCValue {
825 if (inst.base.ty.hasCodeGenBits()) {
826 return self.fail(inst.base.src, "TODO codegen Block with non-void type", .{});
824 fn genIsNonNull(self: *Self, inst: *ir.Inst.IsNonNull) !MCValue {
825 // Here you can specialize this instruction if it makes sense to, otherwise the default
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 }
827830 }
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;
835 }
840 for (inst.codegen.relocs.items) |reloc| try self.performReloc(inst.base.src, reloc);
836841
837 fn performReloc(self: *Function, src: usize, reloc: Reloc) !void {
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 },
842 return MCValue.none;
845843 }
846 }
847844
848 fn genBr(self: *Function, inst: *ir.Inst.Br, comptime arch: std.Target.Cpu.Arch) !MCValue {
849 if (!inst.args.operand.ty.hasCodeGenBits())
850 return self.brVoid(inst.base.src, inst.args.block, arch);
851
852 const operand = try self.resolveInst(inst.args.operand);
853 switch (arch) {
854 else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}),
845 fn performReloc(self: *Self, src: usize, reloc: Reloc) !void {
846 switch (reloc) {
847 .rel32 => |pos| {
848 const amt = self.code.items.len - (pos + 4);
849 const s32_amt = std.math.cast(i32, amt) catch
850 return self.fail(src, "unable to perform relocation: jump too far", .{});
851 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
852 },
853 }
855854 }
856 }
857855
858 fn genBrVoid(self: *Function, inst: *ir.Inst.BrVoid, comptime arch: std.Target.Cpu.Arch) !MCValue {
859 return self.brVoid(inst.base.src, inst.args.block, arch);
860 }
856 fn genBr(self: *Self, inst: *ir.Inst.Br) !MCValue {
857 if (!inst.args.operand.ty.hasCodeGenBits())
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 {
863 // Emit a jump with a relocation. It will be patched up after the block ends.
864 try block.codegen.relocs.ensureCapacity(self.gpa, block.codegen.relocs.items.len + 1);
865
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}),
860 const operand = try self.resolveInst(inst.args.operand);
861 switch (arch) {
862 else => return self.fail(inst.base.src, "TODO implement br for {}", .{self.target.cpu.arch}),
863 }
876864 }
877 return .none;
878 }
879865
880 fn genAsm(self: *Function, inst: *ir.Inst.Assembly, comptime arch: Target.Cpu.Arch) !MCValue {
881 if (!inst.args.is_volatile and inst.base.isUnused())
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", .{});
866 fn genBrVoid(self: *Self, inst: *ir.Inst.BrVoid) !MCValue {
867 return self.brVoid(inst.base.src, inst.args.block);
885868 }
886 for (inst.args.inputs) |input, i| {
887 if (input.len < 3 or input[0] != '{' or input[input.len - 1] != '}') {
888 return self.fail(inst.base.src, "unrecognized asm input constraint: '{}'", .{input});
869
870 fn brVoid(self: *Self, src: usize, block: *ir.Inst.Block) !MCValue {
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}),
889884 }
890 const reg_name = input[1 .. input.len - 1];
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);
885 return .none;
895886 }
896887
897 if (mem.eql(u8, inst.args.asm_source, "syscall")) {
898 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
899 } else {
900 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
901 }
888 fn genAsm(self: *Self, inst: *ir.Inst.Assembly) !MCValue {
889 if (!inst.args.is_volatile and inst.base.isUnused())
890 return MCValue.dead;
891 if (arch != .x86_64 and arch != .i386) {
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| {
904 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
905 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
905 if (mem.eql(u8, inst.args.asm_source, "syscall")) {
906 try self.code.appendSlice(&[_]u8{ 0x0f, 0x05 });
907 } else {
908 return self.fail(inst.base.src, "TODO implement support for more x86 assembly instructions", .{});
906909 }
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 instruction
917 /// stream. This only modifies the instruction stream if at least one bit
918 /// is set true, which has a few implications:
919 ///
920 /// * The length of the instruction buffer will be modified *if* the
921 /// resulting REX is meaningful, but will remain the same if it is not.
922 /// * Deliberately inserting a "meaningless REX" requires explicit usage of
923 /// 0x40, and cannot be done via this function.
924 fn rex(self: *Function, arg: struct { b: bool = false, w: bool = false, x: bool = false, r: bool = false }) void {
925 // From section 2.2.1.2 of the manual, REX is encoded as b0100WRXB.
926 var value: u8 = 0x40;
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;
911 if (inst.args.output) |output| {
912 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
913 return self.fail(inst.base.src, "unrecognized asm output constraint: '{}'", .{output});
914 }
915 const reg_name = output[2 .. output.len - 1];
916 const reg = parseRegName(reg_name) orelse
917 return self.fail(inst.base.src, "unrecognized register: '{}'", .{reg_name});
918 return MCValue{ .register = reg };
919 } else {
920 return MCValue.none;
921 }
938922 }
939 if (value != 0x40) {
940 self.code.appendAssumeCapacity(value);
923
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 }
941950 }
942 }
943951
944 fn genSetReg(self: *Function, src: usize, comptime arch: Target.Cpu.Arch, reg: Reg(arch), mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {
945 switch (arch) {
946 .x86_64 => switch (mcv) {
947 .dead => unreachable,
948 .none => unreachable,
949 .unreach => unreachable,
950 .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.
952 fn genSetReg(self: *Self, src: usize, reg: Reg, mcv: MCValue) error{ CodegenFail, OutOfMemory }!void {
953 switch (arch) {
954 .x86_64 => switch (mcv) {
955 .dead => unreachable,
956 .none => unreachable,
957 .unreach => unreachable,
958 .compare_flags_unsigned => |op| {
985959 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 };
987969 const id = @as(u8, reg.id() & 0b111);
988 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x31, 0xC0 | id << 3 | id });
989 return;
990 }
991 if (x <= std.math.maxInt(u32)) {
992 // Next best case: if we set the lower four bytes, the upper four will be zeroed.
993 //
994 // The encoding for `mov IMM32 -> REG` is (0xB8 + R) IMM.
995 if (reg.isExtended()) {
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);
970 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode, 0xC0 | id });
971 },
972 .compare_flags_signed => |op| {
973 return self.fail(src, "TODO set register with compare flags value (signed)", .{});
974 },
975 .immediate => |x| {
976 if (reg.size() != 64) {
977 return self.fail(src, "TODO decide whether to implement non-64-bit loads", .{});
1005978 }
1006 self.code.items[self.code.items.len - 5] = 0xB8 | @as(u8, reg.id() & 0b111);
1007 const imm_ptr = self.code.items[self.code.items.len - 4 ..][0..4];
1008 mem.writeIntLittle(u32, imm_ptr, @intCast(u32, x));
1009 return;
1010 }
1011 // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls
1012 // this `movabs`, though this is officially just a different variant of the plain `mov`
1013 // instruction.
1014 //
1015 // This encoding is, in fact, the *same* as the one used for 32-bit loads. The only
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.
979 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit
980 // register is the fastest way to zero a register.
981 if (x == 0) {
982 // The encoding for `xor r32, r32` is `0x31 /r`.
983 // Section 3.1.1.1 of the Intel x64 Manual states that "/r indicates that the
984 // ModR/M byte of the instruction contains a register operand and an r/m operand."
985 //
986 // R/M bytes are composed of two bits for the mode, then three bits for the register,
987 // then three bits for the operand. Since we're zeroing a register, the two three-bit
988 // values will be identical, and the mode is three (the raw register value).
1102989 //
1103 // This cannot be used if the lower three bits of the id are equal to four or five, as there
1104 // is no way to possibly encode it. This means that RSP, RBP, R12, and R13 cannot be used with
1105 // this instruction.
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
990 // If we're accessing e.g. r8d, we need to use a REX prefix before the actual operation. Since
991 // this is a 32-bit operation, the W flag is set to zero. X is also zero, as we're not using a SIB.
992 // Both R and B are set, as we're extending, in effect, the register bits *and* the operand.
1116993 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 register
1118 // values must match. Thus, it's 00ABCABC where ABC is the lower three bits of the register ID.
994 self.rex(.{ .r = reg.isExtended(), .b = reg.isExtended() });
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.
11191001 //
1120 // Furthermore, if this is an extended register, both B and R must be set in the REX byte, as *both*
1121 // register operands need to be marked as extended.
1122 self.rex(.{ .w = true, .b = reg.isExtended(), .r = reg.isExtended() });
1123 const RM = (@as(u8, reg.id() & 0b111) << 3) | @truncate(u3, reg.id());
1124 self.code.appendSliceAssumeCapacity(&[_]u8{ 0x8B, RM });
1002 // The encoding for `mov IMM32 -> REG` is (0xB8 + R) IMM.
1003 if (reg.isExtended()) {
1004 // Just as with XORing, we need a REX prefix. This time though, we only
1005 // need the B bit set, as we're extending the opcode's register field,
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;
11251018 }
1126 }
1127 },
1128 .stack_offset => |off| {
1129 return self.fail(src, "TODO implement genSetReg for stack variables", .{});
1019 // Worst case: we need to load the 64-bit register with the IMM. GNU's assemblers calls
1020 // this `movabs`, though this is officially just a different variant of the plain `mov`
1021 // instruction.
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 },
11301138 },
1131 },
1132 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),
1139 else => return self.fail(src, "TODO implement genSetReg for more architectures", .{}),
1140 }
11331141 }
1134 }
11351142
1136 fn genPtrToInt(self: *Function, inst: *ir.Inst.PtrToInt) !MCValue {
1137 // no-op
1138 return self.resolveInst(inst.args.ptr);
1139 }
1143 fn genPtrToInt(self: *Self, inst: *ir.Inst.PtrToInt) !MCValue {
1144 // no-op
1145 return self.resolveInst(inst.args.ptr);
1146 }
11401147
1141 fn genBitCast(self: *Function, inst: *ir.Inst.BitCast) !MCValue {
1142 const operand = try self.resolveInst(inst.args.operand);
1143 return operand;
1144 }
1148 fn genBitCast(self: *Self, inst: *ir.Inst.BitCast) !MCValue {
1149 const operand = try self.resolveInst(inst.args.operand);
1150 return operand;
1151 }
11451152
1146 fn resolveInst(self: *Function, inst: *ir.Inst) !MCValue {
1147 // Constants have static lifetimes, so they are always memoized in the outer most table.
1148 if (inst.cast(ir.Inst.Constant)) |const_inst| {
1149 const branch = &self.branch_stack.items[0];
1150 const gop = try branch.inst_table.getOrPut(self.gpa, inst);
1151 if (!gop.found_existing) {
1152 gop.entry.value = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
1153 fn resolveInst(self: *Self, inst: *ir.Inst) !MCValue {
1154 // Constants have static lifetimes, so they are always memoized in the outer most table.
1155 if (inst.cast(ir.Inst.Constant)) |const_inst| {
1156 const branch = &self.branch_stack.items[0];
1157 const gop = try branch.inst_table.getOrPut(self.gpa, inst);
1158 if (!gop.found_existing) {
1159 gop.entry.value = try self.genTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val });
1160 }
1161 return gop.entry.value;
11531162 }
1154 return gop.entry.value;
1155 }
11561163
1157 // Treat each stack item as a "layer" on top of the previous one.
1158 var i: usize = self.branch_stack.items.len;
1159 while (true) {
1160 i -= 1;
1161 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
1162 return mcv;
1164 // Treat each stack item as a "layer" on top of the previous one.
1165 var i: usize = self.branch_stack.items.len;
1166 while (true) {
1167 i -= 1;
1168 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
1169 return mcv;
1170 }
11631171 }
11641172 }
1165 }
11661173
1167 fn copyToNewRegister(self: *Function, inst: *ir.Inst) !MCValue {
1168 return self.fail(inst.src, "TODO implement copyToNewRegister", .{});
1169 }
1174 fn moveToNewRegister(self: *Self, inst: *ir.Inst) !MCValue {
1175 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
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,
1172 /// we put it in a register.
1173 /// A potential opportunity for future optimization here would be keeping track
1174 /// of the fact that the instruction is available both as an immediate
1175 /// and as a register.
1176 fn limitImmediateType(self: *Function, inst: *ir.Inst, comptime T: type) !MCValue {
1177 const mcv = try self.resolveInst(inst);
1178 const ti = @typeInfo(T).Int;
1179 switch (mcv) {
1180 .immediate => |imm| {
1181 // This immediate is unsigned.
1182 const U = @Type(.{
1183 .Int = .{
1184 .bits = ti.bits - @boolToInt(ti.is_signed),
1185 .is_signed = false,
1186 },
1187 });
1188 if (imm >= std.math.maxInt(U)) {
1189 return self.copyToNewRegister(inst);
1190 }
1191 },
1192 else => {},
1179 /// If the MCValue is an immediate, and it does not fit within this type,
1180 /// we put it in a register.
1181 /// A potential opportunity for future optimization here would be keeping track
1182 /// of the fact that the instruction is available both as an immediate
1183 /// and as a register.
1184 fn limitImmediateType(self: *Self, inst: *ir.Inst, comptime T: type) !MCValue {
1185 const mcv = try self.resolveInst(inst);
1186 const ti = @typeInfo(T).Int;
1187 switch (mcv) {
1188 .immediate => |imm| {
1189 // This immediate is unsigned.
1190 const U = @Type(.{
1191 .Int = .{
1192 .bits = ti.bits - @boolToInt(ti.is_signed),
1193 .is_signed = false,
1194 },
1195 });
1196 if (imm >= std.math.maxInt(U)) {
1197 return self.moveToNewRegister(inst);
1198 }
1199 },
1200 else => {},
1201 }
1202 return mcv;
11931203 }
1194 return mcv;
1195 }
11961204
1197 fn genTypedValue(self: *Function, src: usize, typed_value: TypedValue) !MCValue {
1198 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1199 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1200 switch (typed_value.ty.zigTypeTag()) {
1201 .Pointer => {
1202 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
1203 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
1204 const decl = payload.decl;
1205 const got_addr = got.p_vaddr + decl.link.offset_table_index * ptr_bytes;
1206 return MCValue{ .memory = got_addr };
1207 }
1208 return self.fail(src, "TODO codegen more kinds of const pointers", .{});
1209 },
1210 .Int => {
1211 const info = typed_value.ty.intInfo(self.target.*);
1212 if (info.bits > ptr_bits or info.signed) {
1213 return self.fail(src, "TODO const int bigger than ptr and signed int", .{});
1214 }
1215 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
1216 },
1217 .Bool => {
1218 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
1219 },
1220 .ComptimeInt => unreachable, // semantic analysis prevents this
1221 .ComptimeFloat => unreachable, // semantic analysis prevents this
1222 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
1205 fn genTypedValue(self: *Self, src: usize, typed_value: TypedValue) !MCValue {
1206 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1207 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1208 switch (typed_value.ty.zigTypeTag()) {
1209 .Pointer => {
1210 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
1211 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
1212 const decl = payload.decl;
1213 const got_addr = got.p_vaddr + decl.link.offset_table_index * ptr_bytes;
1214 return MCValue{ .memory = got_addr };
1215 }
1216 return self.fail(src, "TODO codegen more kinds of const pointers", .{});
1217 },
1218 .Int => {
1219 const info = typed_value.ty.intInfo(self.target.*);
1220 if (info.bits > ptr_bits or info.signed) {
1221 return self.fail(src, "TODO const int bigger than ptr and signed int", .{});
1222 }
1223 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
1224 },
1225 .Bool => {
1226 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
1227 },
1228 .ComptimeInt => unreachable, // semantic analysis prevents this
1229 .ComptimeFloat => unreachable, // semantic analysis prevents this
1230 else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}),
1231 }
12231232 }
1224 }
12251233
1226 fn resolveParameters(
1227 self: *Function,
1228 src: usize,
1229 cc: std.builtin.CallingConvention,
1230 param_types: []const Type,
1231 results: []MCValue,
1232 ) !u32 {
1233 switch (self.target.cpu.arch) {
1234 .x86_64 => {
1235 switch (cc) {
1236 .Naked => {
1237 assert(results.len == 0);
1238 return 0;
1239 },
1240 .Unspecified, .C => {
1241 var next_int_reg: usize = 0;
1242 var next_stack_offset: u32 = 0;
1243
1244 const integer_registers = [_]Reg(.x86_64){ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
1245 for (param_types) |ty, i| {
1246 switch (ty.zigTypeTag()) {
1247 .Bool, .Int => {
1248 if (next_int_reg >= integer_registers.len) {
1249 results[i] = .{ .stack_offset = next_stack_offset };
1250 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));
1251 } else {
1252 results[i] = .{ .register = @enumToInt(integer_registers[next_int_reg]) };
1253 next_int_reg += 1;
1254 }
1255 },
1256 else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}),
1234 fn resolveParameters(
1235 self: *Self,
1236 src: usize,
1237 cc: std.builtin.CallingConvention,
1238 param_types: []const Type,
1239 results: []MCValue,
1240 ) !u32 {
1241 switch (arch) {
1242 .x86_64 => {
1243 switch (cc) {
1244 .Naked => {
1245 assert(results.len == 0);
1246 return 0;
1247 },
1248 .Unspecified, .C => {
1249 var next_int_reg: usize = 0;
1250 var next_stack_offset: u32 = 0;
1251
1252 const integer_registers = [_]Reg{ .rdi, .rsi, .rdx, .rcx, .r8, .r9 };
1253 for (param_types) |ty, i| {
1254 switch (ty.zigTypeTag()) {
1255 .Bool, .Int => {
1256 if (next_int_reg >= integer_registers.len) {
1257 results[i] = .{ .stack_offset = next_stack_offset };
1258 next_stack_offset += @intCast(u32, ty.abiSize(self.target.*));
1259 } else {
1260 results[i] = .{ .register = integer_registers[next_int_reg] };
1261 next_int_reg += 1;
1262 }
1263 },
1264 else => return self.fail(src, "TODO implement function parameters of type {}", .{@tagName(ty.zigTypeTag())}),
1265 }
12571266 }
1258 }
1259 return next_stack_offset;
1260 },
1261 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),
1262 }
1263 },
1264 else => return self.fail(src, "TODO implement C ABI support for {}", .{self.target.cpu.arch}),
1267 return next_stack_offset;
1268 },
1269 else => return self.fail(src, "TODO implement function parameters for {}", .{cc}),
1270 }
1271 },
1272 else => return self.fail(src, "TODO implement C ABI support for {}", .{self.target.cpu.arch}),
1273 }
12651274 }
1266 }
12671275
1268 fn fail(self: *Function, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {
1269 @setCold(true);
1270 assert(self.err_msg == null);
1271 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src, format, args);
1272 return error.CodegenFail;
1273 }
1274};
1276 fn fail(self: *Self, src: usize, comptime format: []const u8, args: anytype) error{ CodegenFail, OutOfMemory } {
1277 @setCold(true);
1278 assert(self.err_msg == null);
1279 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, src, format, args);
1280 return error.CodegenFail;
1281 }
12751282
1276const x86_64 = @import("codegen/x86_64.zig");
1277const x86 = @import("codegen/x86.zig");
1283 const Reg = switch (arch) {
1284 .i386 => x86.Register,
1285 .x86_64 => x86_64.Register,
1286 else => enum { dummy },
1287 };
12781288
1279fn Reg(comptime arch: Target.Cpu.Arch) type {
1280 return switch (arch) {
1281 .i386 => x86.Register,
1282 .x86_64 => x86_64.Register,
1283 else => @compileError("TODO add more register enums"),
1289 fn parseRegName(name: []const u8) ?Reg {
1290 return std.meta.stringToEnum(Reg, name);
1291 }
12841292 };
12851293}
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) {
6767 }
6868};
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 };