authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-11-02 17:18:54+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-02 13:05:01-04:00
log5ebdc8c46c594580a95acbc7252e77ccac8d0680
treec9c058d2c28c3e4d274659f096d78341bac10f28
parent674932e503e5b3a929f5a17eb4aa2fd2866219c3

stage2 RISCV64: move codegen to separate file


2 files changed, 2166 insertions(+), 117 deletions(-)

src/arch/riscv64/CodeGen.zig created+2165
...@@ -0,0 +1,2165 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const mem = std.mem;
4const math = std.math;
5const assert = std.debug.assert;
6const Air = @import("../../Air.zig");
7const Zir = @import("../../Zir.zig");
8const Liveness = @import("../../Liveness.zig");
9const Type = @import("../../type.zig").Type;
10const Value = @import("../../value.zig").Value;
11const TypedValue = @import("../../TypedValue.zig");
12const link = @import("../../link.zig");
13const Module = @import("../../Module.zig");
14const Compilation = @import("../../Compilation.zig");
15const ErrorMsg = Module.ErrorMsg;
16const Target = std.Target;
17const Allocator = mem.Allocator;
18const trace = @import("../../tracy.zig").trace;
19const DW = std.dwarf;
20const leb128 = std.leb;
21const log = std.log.scoped(.codegen);
22const build_options = @import("build_options");
23const RegisterManager = @import("../../register_manager.zig").RegisterManager;
24
25const FnResult = @import("../../codegen.zig").FnResult;
26const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
27const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
28
29const InnerError = error{
30 OutOfMemory,
31 CodegenFail,
32};
33
34gpa: *Allocator,
35air: Air,
36liveness: Liveness,
37bin_file: *link.File,
38target: *const std.Target,
39mod_fn: *const Module.Fn,
40code: *std.ArrayList(u8),
41debug_output: DebugInfoOutput,
42err_msg: ?*ErrorMsg,
43args: []MCValue,
44ret_mcv: MCValue,
45fn_type: Type,
46arg_index: usize,
47src_loc: Module.SrcLoc,
48stack_align: u32,
49
50prev_di_line: u32,
51prev_di_column: u32,
52/// Byte offset within the source file of the ending curly.
53end_di_line: u32,
54end_di_column: u32,
55/// Relative to the beginning of `code`.
56prev_di_pc: usize,
57
58/// The value is an offset into the `Function` `code` from the beginning.
59/// To perform the reloc, write 32-bit signed little-endian integer
60/// which is a relative jump, based on the address following the reloc.
61exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
62
63/// Whenever there is a runtime branch, we push a Branch onto this stack,
64/// and pop it off when the runtime branch joins. This provides an "overlay"
65/// of the table of mappings from instructions to `MCValue` from within the branch.
66/// This way we can modify the `MCValue` for an instruction in different ways
67/// within different branches. Special consideration is needed when a branch
68/// joins with its parent, to make sure all instructions have the same MCValue
69/// across each runtime branch upon joining.
70branch_stack: *std.ArrayList(Branch),
71
72// Key is the block instruction
73blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
74
75register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{},
76/// Maps offset to what is stored there.
77stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
78
79/// Offset from the stack base, representing the end of the stack frame.
80max_end_stack: u32 = 0,
81/// Represents the current end stack offset. If there is no existing slot
82/// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
83next_stack_offset: u32 = 0,
84
85/// Debug field, used to find bugs in the compiler.
86air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
87
88const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
89
90const MCValue = union(enum) {
91 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
92 /// TODO Look into deleting this tag and using `dead` instead, since every use
93 /// of MCValue.none should be instead looking at the type and noticing it is 0 bits.
94 none,
95 /// Control flow will not allow this value to be observed.
96 unreach,
97 /// No more references to this value remain.
98 dead,
99 /// The value is undefined.
100 undef,
101 /// A pointer-sized integer that fits in a register.
102 /// If the type is a pointer, this is the pointer address in virtual address space.
103 immediate: u64,
104 /// The constant was emitted into the code, at this offset.
105 /// If the type is a pointer, it means the pointer address is embedded in the code.
106 embedded_in_code: usize,
107 /// The value is a pointer to a constant which was emitted into the code, at this offset.
108 ptr_embedded_in_code: usize,
109 /// The value is in a target-specific register.
110 register: Register,
111 /// The value is in memory at a hard-coded address.
112 /// If the type is a pointer, it means the pointer address is at this memory location.
113 memory: u64,
114 /// The value is one of the stack variables.
115 /// If the type is a pointer, it means the pointer address is in the stack at this offset.
116 stack_offset: u32,
117 /// The value is a pointer to one of the stack variables (payload is stack offset).
118 ptr_stack_offset: u32,
119 /// The value is in the compare flags assuming an unsigned operation,
120 /// with this operator applied on top of it.
121 compare_flags_unsigned: math.CompareOperator,
122 /// The value is in the compare flags assuming a signed operation,
123 /// with this operator applied on top of it.
124 compare_flags_signed: math.CompareOperator,
125
126 fn isMemory(mcv: MCValue) bool {
127 return switch (mcv) {
128 .embedded_in_code, .memory, .stack_offset => true,
129 else => false,
130 };
131 }
132
133 fn isImmediate(mcv: MCValue) bool {
134 return switch (mcv) {
135 .immediate => true,
136 else => false,
137 };
138 }
139
140 fn isMutable(mcv: MCValue) bool {
141 return switch (mcv) {
142 .none => unreachable,
143 .unreach => unreachable,
144 .dead => unreachable,
145
146 .immediate,
147 .embedded_in_code,
148 .memory,
149 .compare_flags_unsigned,
150 .compare_flags_signed,
151 .ptr_stack_offset,
152 .ptr_embedded_in_code,
153 .undef,
154 => false,
155
156 .register,
157 .stack_offset,
158 => true,
159 };
160 }
161};
162
163const Branch = struct {
164 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},
165
166 fn deinit(self: *Branch, gpa: *Allocator) void {
167 self.inst_table.deinit(gpa);
168 self.* = undefined;
169 }
170};
171
172const StackAllocation = struct {
173 inst: Air.Inst.Index,
174 /// TODO do we need size? should be determined by inst.ty.abiSize()
175 size: u32,
176};
177
178const BlockData = struct {
179 relocs: std.ArrayListUnmanaged(Reloc),
180 /// The first break instruction encounters `null` here and chooses a
181 /// machine code value for the block result, populating this field.
182 /// Following break instructions encounter that value and use it for
183 /// the location to store their block results.
184 mcv: MCValue,
185};
186
187const Reloc = union(enum) {
188 /// The value is an offset into the `Function` `code` from the beginning.
189 /// To perform the reloc, write 32-bit signed little-endian integer
190 /// which is a relative jump, based on the address following the reloc.
191 rel32: usize,
192 /// A branch in the ARM instruction set
193 arm_branch: struct {
194 pos: usize,
195 cond: @import("../arm/bits.zig").Condition,
196 },
197};
198
199const BigTomb = struct {
200 function: *Self,
201 inst: Air.Inst.Index,
202 tomb_bits: Liveness.Bpi,
203 big_tomb_bits: u32,
204 bit_index: usize,
205
206 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
207 const this_bit_index = bt.bit_index;
208 bt.bit_index += 1;
209
210 const op_int = @enumToInt(op_ref);
211 if (op_int < Air.Inst.Ref.typed_value_map.len) return;
212 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
213
214 if (this_bit_index < Liveness.bpi - 1) {
215 const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0;
216 if (!dies) return;
217 } else {
218 const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1));
219 const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0;
220 if (!dies) return;
221 }
222 bt.function.processDeath(op_index);
223 }
224
225 fn finishAir(bt: *BigTomb, result: MCValue) void {
226 const is_used = !bt.function.liveness.isUnused(bt.inst);
227 if (is_used) {
228 log.debug("%{d} => {}", .{ bt.inst, result });
229 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
230 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
231 }
232 bt.function.finishAirBookkeeping();
233 }
234};
235
236const Self = @This();
237
238pub fn generate(
239 bin_file: *link.File,
240 src_loc: Module.SrcLoc,
241 module_fn: *Module.Fn,
242 air: Air,
243 liveness: Liveness,
244 code: *std.ArrayList(u8),
245 debug_output: DebugInfoOutput,
246) GenerateSymbolError!FnResult {
247 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) {
248 @panic("Attempted to compile for architecture that was disabled by build configuration");
249 }
250
251 assert(module_fn.owner_decl.has_tv);
252 const fn_type = module_fn.owner_decl.ty;
253
254 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
255 defer {
256 assert(branch_stack.items.len == 1);
257 branch_stack.items[0].deinit(bin_file.allocator);
258 branch_stack.deinit();
259 }
260 try branch_stack.append(.{});
261
262 var function = Self{
263 .gpa = bin_file.allocator,
264 .air = air,
265 .liveness = liveness,
266 .target = &bin_file.options.target,
267 .bin_file = bin_file,
268 .mod_fn = module_fn,
269 .code = code,
270 .debug_output = debug_output,
271 .err_msg = null,
272 .args = undefined, // populated after `resolveCallingConventionValues`
273 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
274 .fn_type = fn_type,
275 .arg_index = 0,
276 .branch_stack = &branch_stack,
277 .src_loc = src_loc,
278 .stack_align = undefined,
279 .prev_di_pc = 0,
280 .prev_di_line = module_fn.lbrace_line,
281 .prev_di_column = module_fn.lbrace_column,
282 .end_di_line = module_fn.rbrace_line,
283 .end_di_column = module_fn.rbrace_column,
284 };
285 defer function.stack.deinit(bin_file.allocator);
286 defer function.blocks.deinit(bin_file.allocator);
287 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
288
289 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
290 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
291 else => |e| return e,
292 };
293 defer call_info.deinit(&function);
294
295 function.args = call_info.args;
296 function.ret_mcv = call_info.return_value;
297 function.stack_align = call_info.stack_align;
298 function.max_end_stack = call_info.stack_byte_count;
299
300 function.gen() catch |err| switch (err) {
301 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
302 else => |e| return e,
303 };
304
305 if (function.err_msg) |em| {
306 return FnResult{ .fail = em };
307 } else {
308 return FnResult{ .appended = {} };
309 }
310}
311
312fn gen(self: *Self) !void {
313 try self.dbgSetPrologueEnd();
314 try self.genBody(self.air.getMainBody());
315 try self.dbgSetEpilogueBegin();
316
317 // Drop them off at the rbrace.
318 try self.dbgAdvancePCAndLine(self.end_di_line, self.end_di_column);
319}
320
321fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
322 const air_tags = self.air.instructions.items(.tag);
323
324 for (body) |inst| {
325 const old_air_bookkeeping = self.air_bookkeeping;
326 try self.ensureProcessDeathCapacity(Liveness.bpi);
327
328 switch (air_tags[inst]) {
329 // zig fmt: off
330 .add, .ptr_add => try self.airAdd(inst),
331 .addwrap => try self.airAddWrap(inst),
332 .add_sat => try self.airAddSat(inst),
333 .sub, .ptr_sub => try self.airSub(inst),
334 .subwrap => try self.airSubWrap(inst),
335 .sub_sat => try self.airSubSat(inst),
336 .mul => try self.airMul(inst),
337 .mulwrap => try self.airMulWrap(inst),
338 .mul_sat => try self.airMulSat(inst),
339 .rem => try self.airRem(inst),
340 .mod => try self.airMod(inst),
341 .shl, .shl_exact => try self.airShl(inst),
342 .shl_sat => try self.airShlSat(inst),
343 .min => try self.airMin(inst),
344 .max => try self.airMax(inst),
345 .slice => try self.airSlice(inst),
346
347 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
348
349 .cmp_lt => try self.airCmp(inst, .lt),
350 .cmp_lte => try self.airCmp(inst, .lte),
351 .cmp_eq => try self.airCmp(inst, .eq),
352 .cmp_gte => try self.airCmp(inst, .gte),
353 .cmp_gt => try self.airCmp(inst, .gt),
354 .cmp_neq => try self.airCmp(inst, .neq),
355
356 .bool_and => try self.airBoolOp(inst),
357 .bool_or => try self.airBoolOp(inst),
358 .bit_and => try self.airBitAnd(inst),
359 .bit_or => try self.airBitOr(inst),
360 .xor => try self.airXor(inst),
361 .shr => try self.airShr(inst),
362
363 .alloc => try self.airAlloc(inst),
364 .ret_ptr => try self.airRetPtr(inst),
365 .arg => try self.airArg(inst),
366 .assembly => try self.airAsm(inst),
367 .bitcast => try self.airBitCast(inst),
368 .block => try self.airBlock(inst),
369 .br => try self.airBr(inst),
370 .breakpoint => try self.airBreakpoint(),
371 .fence => try self.airFence(),
372 .call => try self.airCall(inst),
373 .cond_br => try self.airCondBr(inst),
374 .dbg_stmt => try self.airDbgStmt(inst),
375 .fptrunc => try self.airFptrunc(inst),
376 .fpext => try self.airFpext(inst),
377 .intcast => try self.airIntCast(inst),
378 .trunc => try self.airTrunc(inst),
379 .bool_to_int => try self.airBoolToInt(inst),
380 .is_non_null => try self.airIsNonNull(inst),
381 .is_non_null_ptr => try self.airIsNonNullPtr(inst),
382 .is_null => try self.airIsNull(inst),
383 .is_null_ptr => try self.airIsNullPtr(inst),
384 .is_non_err => try self.airIsNonErr(inst),
385 .is_non_err_ptr => try self.airIsNonErrPtr(inst),
386 .is_err => try self.airIsErr(inst),
387 .is_err_ptr => try self.airIsErrPtr(inst),
388 .load => try self.airLoad(inst),
389 .loop => try self.airLoop(inst),
390 .not => try self.airNot(inst),
391 .ptrtoint => try self.airPtrToInt(inst),
392 .ret => try self.airRet(inst),
393 .ret_load => try self.airRetLoad(inst),
394 .store => try self.airStore(inst),
395 .struct_field_ptr=> try self.airStructFieldPtr(inst),
396 .struct_field_val=> try self.airStructFieldVal(inst),
397 .array_to_slice => try self.airArrayToSlice(inst),
398 .int_to_float => try self.airIntToFloat(inst),
399 .float_to_int => try self.airFloatToInt(inst),
400 .cmpxchg_strong => try self.airCmpxchg(inst),
401 .cmpxchg_weak => try self.airCmpxchg(inst),
402 .atomic_rmw => try self.airAtomicRmw(inst),
403 .atomic_load => try self.airAtomicLoad(inst),
404 .memcpy => try self.airMemcpy(inst),
405 .memset => try self.airMemset(inst),
406 .set_union_tag => try self.airSetUnionTag(inst),
407 .get_union_tag => try self.airGetUnionTag(inst),
408 .clz => try self.airClz(inst),
409 .ctz => try self.airCtz(inst),
410 .popcount => try self.airPopcount(inst),
411
412 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
413 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
414 .atomic_store_release => try self.airAtomicStore(inst, .Release),
415 .atomic_store_seq_cst => try self.airAtomicStore(inst, .SeqCst),
416
417 .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
418 .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1),
419 .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),
420 .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),
421
422 .switch_br => try self.airSwitch(inst),
423 .slice_ptr => try self.airSlicePtr(inst),
424 .slice_len => try self.airSliceLen(inst),
425
426 .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst),
427 .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst),
428
429 .array_elem_val => try self.airArrayElemVal(inst),
430 .slice_elem_val => try self.airSliceElemVal(inst),
431 .slice_elem_ptr => try self.airSliceElemPtr(inst),
432 .ptr_elem_val => try self.airPtrElemVal(inst),
433 .ptr_elem_ptr => try self.airPtrElemPtr(inst),
434
435 .constant => unreachable, // excluded from function bodies
436 .const_ty => unreachable, // excluded from function bodies
437 .unreach => self.finishAirBookkeeping(),
438
439 .optional_payload => try self.airOptionalPayload(inst),
440 .optional_payload_ptr => try self.airOptionalPayloadPtr(inst),
441 .unwrap_errunion_err => try self.airUnwrapErrErr(inst),
442 .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst),
443 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
444 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
445
446 .wrap_optional => try self.airWrapOptional(inst),
447 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
448 .wrap_errunion_err => try self.airWrapErrUnionErr(inst),
449 // zig fmt: on
450 }
451 if (std.debug.runtime_safety) {
452 if (self.air_bookkeeping < old_air_bookkeeping + 1) {
453 std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[inst] });
454 }
455 }
456 }
457}
458
459fn dbgSetPrologueEnd(self: *Self) InnerError!void {
460 switch (self.debug_output) {
461 .dwarf => |dbg_out| {
462 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
463 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
464 },
465 .plan9 => {},
466 .none => {},
467 }
468}
469
470fn dbgSetEpilogueBegin(self: *Self) InnerError!void {
471 switch (self.debug_output) {
472 .dwarf => |dbg_out| {
473 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
474 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
475 },
476 .plan9 => {},
477 .none => {},
478 }
479}
480
481fn dbgAdvancePCAndLine(self: *Self, line: u32, column: u32) InnerError!void {
482 const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);
483 const delta_pc: usize = self.code.items.len - self.prev_di_pc;
484 switch (self.debug_output) {
485 .dwarf => |dbg_out| {
486 // TODO Look into using the DWARF special opcodes to compress this data.
487 // It lets you emit single-byte opcodes that add different numbers to
488 // both the PC and the line number at the same time.
489 try dbg_out.dbg_line.ensureUnusedCapacity(11);
490 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc);
491 leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
492 if (delta_line != 0) {
493 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line);
494 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
495 }
496 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
497 self.prev_di_pc = self.code.items.len;
498 self.prev_di_line = line;
499 self.prev_di_column = column;
500 self.prev_di_pc = self.code.items.len;
501 },
502 .plan9 => |dbg_out| {
503 if (delta_pc <= 0) return; // only do this when the pc changes
504 // we have already checked the target in the linker to make sure it is compatable
505 const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;
506
507 // increasing the line number
508 try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
509 // increasing the pc
510 const d_pc_p9 = @intCast(i64, delta_pc) - quant;
511 if (d_pc_p9 > 0) {
512 // minus one because if its the last one, we want to leave space to change the line which is one quanta
513 try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant);
514 if (dbg_out.pcop_change_index.*) |pci|
515 dbg_out.dbg_line.items[pci] += 1;
516 dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1);
517 } else if (d_pc_p9 == 0) {
518 // we don't need to do anything, because adding the quant does it for us
519 } else unreachable;
520 if (dbg_out.start_line.* == null)
521 dbg_out.start_line.* = self.prev_di_line;
522 dbg_out.end_line.* = line;
523 // only do this if the pc changed
524 self.prev_di_line = line;
525 self.prev_di_column = column;
526 self.prev_di_pc = self.code.items.len;
527 },
528 .none => {},
529 }
530}
531
532/// Asserts there is already capacity to insert into top branch inst_table.
533fn processDeath(self: *Self, inst: Air.Inst.Index) void {
534 const air_tags = self.air.instructions.items(.tag);
535 if (air_tags[inst] == .constant) return; // Constants are immortal.
536 // When editing this function, note that the logic must synchronize with `reuseOperand`.
537 const prev_value = self.getResolvedInstValue(inst);
538 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
539 branch.inst_table.putAssumeCapacity(inst, .dead);
540 switch (prev_value) {
541 .register => |reg| {
542 self.register_manager.freeReg(reg);
543 },
544 else => {}, // TODO process stack allocation death
545 }
546}
547
548/// Called when there are no operands, and the instruction is always unreferenced.
549fn finishAirBookkeeping(self: *Self) void {
550 if (std.debug.runtime_safety) {
551 self.air_bookkeeping += 1;
552 }
553}
554
555fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
556 var tomb_bits = self.liveness.getTombBits(inst);
557 for (operands) |op| {
558 const dies = @truncate(u1, tomb_bits) != 0;
559 tomb_bits >>= 1;
560 if (!dies) continue;
561 const op_int = @enumToInt(op);
562 if (op_int < Air.Inst.Ref.typed_value_map.len) continue;
563 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
564 self.processDeath(op_index);
565 }
566 const is_used = @truncate(u1, tomb_bits) == 0;
567 if (is_used) {
568 log.debug("%{d} => {}", .{ inst, result });
569 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
570 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
571
572 switch (result) {
573 .register => |reg| {
574 // In some cases (such as bitcast), an operand
575 // may be the same MCValue as the result. If
576 // that operand died and was a register, it
577 // was freed by processDeath. We have to
578 // "re-allocate" the register.
579 if (self.register_manager.isRegFree(reg)) {
580 self.register_manager.getRegAssumeFree(reg, inst);
581 }
582 },
583 else => {},
584 }
585 }
586 self.finishAirBookkeeping();
587}
588
589fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
590 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
591 try table.ensureUnusedCapacity(self.gpa, additional_count);
592}
593
594/// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
595/// after codegen for this symbol is done.
596fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
597 switch (self.debug_output) {
598 .dwarf => |dbg_out| {
599 assert(ty.hasCodeGenBits());
600 const index = dbg_out.dbg_info.items.len;
601 try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4
602
603 const gop = try dbg_out.dbg_info_type_relocs.getOrPut(self.gpa, ty);
604 if (!gop.found_existing) {
605 gop.value_ptr.* = .{
606 .off = undefined,
607 .relocs = .{},
608 };
609 }
610 try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index));
611 },
612 .plan9 => {},
613 .none => {},
614 }
615}
616
617fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {
618 if (abi_align > self.stack_align)
619 self.stack_align = abi_align;
620 // TODO find a free slot instead of always appending
621 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align);
622 self.next_stack_offset = offset + abi_size;
623 if (self.next_stack_offset > self.max_end_stack)
624 self.max_end_stack = self.next_stack_offset;
625 try self.stack.putNoClobber(self.gpa, offset, .{
626 .inst = inst,
627 .size = abi_size,
628 });
629 return offset;
630}
631
632/// Use a pointer instruction as the basis for allocating stack memory.
633fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
634 const elem_ty = self.air.typeOfIndex(inst).elemType();
635 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
636 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
637 };
638 // TODO swap this for inst.ty.ptrAlign
639 const abi_align = elem_ty.abiAlignment(self.target.*);
640 return self.allocMem(inst, abi_size, abi_align);
641}
642
643fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
644 const elem_ty = self.air.typeOfIndex(inst);
645 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
646 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
647 };
648 const abi_align = elem_ty.abiAlignment(self.target.*);
649 if (abi_align > self.stack_align)
650 self.stack_align = abi_align;
651
652 if (reg_ok) {
653 // Make sure the type can fit in a register before we try to allocate one.
654 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
655 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
656 if (abi_size <= ptr_bytes) {
657 if (self.register_manager.tryAllocReg(inst, &.{})) |reg| {
658 return MCValue{ .register = reg };
659 }
660 }
661 }
662 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
663 return MCValue{ .stack_offset = stack_offset };
664}
665
666pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
667 const stack_mcv = try self.allocRegOrMem(inst, false);
668 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
669 const reg_mcv = self.getResolvedInstValue(inst);
670 assert(reg == reg_mcv.register);
671 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
672 try branch.inst_table.put(self.gpa, inst, stack_mcv);
673 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
674}
675
676/// Copies a value to a register without tracking the register. The register is not considered
677/// allocated. A second call to `copyToTmpRegister` may return the same register.
678/// This can have a side effect of spilling instructions to the stack to free up a register.
679fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
680 const reg = try self.register_manager.allocReg(null, &.{});
681 try self.genSetReg(ty, reg, mcv);
682 return reg;
683}
684
685/// Allocates a new register and copies `mcv` into it.
686/// `reg_owner` is the instruction that gets associated with the register in the register table.
687/// This can have a side effect of spilling instructions to the stack to free up a register.
688fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
689 const reg = try self.register_manager.allocReg(reg_owner, &.{});
690 try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv);
691 return MCValue{ .register = reg };
692}
693
694fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
695 const stack_offset = try self.allocMemPtr(inst);
696 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
697}
698
699fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
700 const stack_offset = try self.allocMemPtr(inst);
701 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
702}
703
704fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {
705 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
706 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch});
707 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
708}
709
710fn airFpext(self: *Self, inst: Air.Inst.Index) !void {
711 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
712 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch});
713 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
714}
715
716fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
717 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
718 if (self.liveness.isUnused(inst))
719 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
720
721 const operand_ty = self.air.typeOf(ty_op.operand);
722 const operand = try self.resolveInst(ty_op.operand);
723 const info_a = operand_ty.intInfo(self.target.*);
724 const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*);
725 if (info_a.signedness != info_b.signedness)
726 return self.fail("TODO gen intcast sign safety in semantic analysis", .{});
727
728 if (info_a.bits == info_b.bits)
729 return self.finishAir(inst, operand, .{ ty_op.operand, .none, .none });
730
731 return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch});
732 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
733}
734
735fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
736 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
737 if (self.liveness.isUnused(inst))
738 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
739
740 const operand = try self.resolveInst(ty_op.operand);
741 _ = operand;
742 return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch});
743 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
744}
745
746fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
747 const un_op = self.air.instructions.items(.data)[inst].un_op;
748 const operand = try self.resolveInst(un_op);
749 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand;
750 return self.finishAir(inst, result, .{ un_op, .none, .none });
751}
752
753fn airNot(self: *Self, inst: Air.Inst.Index) !void {
754 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
755 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
756 const operand = try self.resolveInst(ty_op.operand);
757 switch (operand) {
758 .dead => unreachable,
759 .unreach => unreachable,
760 .compare_flags_unsigned => |op| {
761 const r = MCValue{
762 .compare_flags_unsigned = switch (op) {
763 .gte => .lt,
764 .gt => .lte,
765 .neq => .eq,
766 .lt => .gte,
767 .lte => .gt,
768 .eq => .neq,
769 },
770 };
771 break :result r;
772 },
773 .compare_flags_signed => |op| {
774 const r = MCValue{
775 .compare_flags_signed = switch (op) {
776 .gte => .lt,
777 .gt => .lte,
778 .neq => .eq,
779 .lt => .gte,
780 .lte => .gt,
781 .eq => .neq,
782 },
783 };
784 break :result r;
785 },
786 else => {},
787 }
788
789 return self.fail("TODO implement NOT for {}", .{self.target.cpu.arch});
790 };
791
792 _ = result;
793 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
794}
795
796fn airMin(self: *Self, inst: Air.Inst.Index) !void {
797 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
798 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement min for {}", .{self.target.cpu.arch});
799 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
800}
801
802fn airMax(self: *Self, inst: Air.Inst.Index) !void {
803 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
804 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement max for {}", .{self.target.cpu.arch});
805 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
806}
807
808fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
809 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
810 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
811 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice for {}", .{self.target.cpu.arch});
812 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
813}
814
815fn airAdd(self: *Self, inst: Air.Inst.Index) !void {
816 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
817 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add for {}", .{self.target.cpu.arch});
818 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
819}
820
821fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void {
822 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
823 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch});
824 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
825}
826
827fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
828 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
829 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});
830 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
831}
832
833fn airSub(self: *Self, inst: Air.Inst.Index) !void {
834 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
835 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub for {}", .{self.target.cpu.arch});
836 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
837}
838
839fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {
840 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
841 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch});
842 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
843}
844
845fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
846 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
847 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});
848 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
849}
850
851fn airMul(self: *Self, inst: Air.Inst.Index) !void {
852 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
853 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul for {}", .{self.target.cpu.arch});
854 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
855}
856
857fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void {
858 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
859 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch});
860 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
861}
862
863fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
864 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
865 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});
866 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
867}
868
869fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
870 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
871 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch});
872 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
873}
874
875fn airRem(self: *Self, inst: Air.Inst.Index) !void {
876 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
877 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement rem for {}", .{self.target.cpu.arch});
878 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
879}
880
881fn airMod(self: *Self, inst: Air.Inst.Index) !void {
882 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
883 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mod for {}", .{self.target.cpu.arch});
884 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
885}
886
887fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void {
888 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
889 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement bitwise and for {}", .{self.target.cpu.arch});
890 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
891}
892
893fn airBitOr(self: *Self, inst: Air.Inst.Index) !void {
894 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
895 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement bitwise or for {}", .{self.target.cpu.arch});
896 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
897}
898
899fn airXor(self: *Self, inst: Air.Inst.Index) !void {
900 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
901 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement xor for {}", .{self.target.cpu.arch});
902 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
903}
904
905fn airShl(self: *Self, inst: Air.Inst.Index) !void {
906 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
907 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl for {}", .{self.target.cpu.arch});
908 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
909}
910
911fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
912 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
913 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
914 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
915}
916
917fn airShr(self: *Self, inst: Air.Inst.Index) !void {
918 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
919 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shr for {}", .{self.target.cpu.arch});
920 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
921}
922
923fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
924 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
925 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch});
926 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
927}
928
929fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
930 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
931 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});
932 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
933}
934
935fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
936 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
937 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union error for {}", .{self.target.cpu.arch});
938 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
939}
940
941fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
942 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
943 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch});
944 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
945}
946
947// *(E!T) -> E
948fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void {
949 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
950 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union error ptr for {}", .{self.target.cpu.arch});
951 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
952}
953
954// *(E!T) -> *T
955fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
956 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
957 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload ptr for {}", .{self.target.cpu.arch});
958 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
959}
960
961fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
962 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
963 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
964 const optional_ty = self.air.typeOfIndex(inst);
965
966 // Optional with a zero-bit payload type is just a boolean true
967 if (optional_ty.abiSize(self.target.*) == 1)
968 break :result MCValue{ .immediate = 1 };
969
970 return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch});
971 };
972 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
973}
974
975/// T to E!T
976fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
977 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
978 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch});
979 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
980}
981
982/// E to E!T
983fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
984 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
985 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement wrap errunion error for {}", .{self.target.cpu.arch});
986 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
987}
988
989fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
990 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
991 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_ptr for {}", .{self.target.cpu.arch});
992 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
993}
994
995fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
996 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
997 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_len for {}", .{self.target.cpu.arch});
998 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
999}
1000
1001fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
1002 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1003 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_slice_len_ptr for {}", .{self.target.cpu.arch});
1004 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1005}
1006
1007fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
1008 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1009 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_slice_ptr_ptr for {}", .{self.target.cpu.arch});
1010 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1011}
1012
1013fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
1014 const is_volatile = false; // TODO
1015 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1016 const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_elem_val for {}", .{self.target.cpu.arch});
1017 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1018}
1019
1020fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
1021 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1022 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
1023 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_elem_ptr for {}", .{self.target.cpu.arch});
1024 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
1025}
1026
1027fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
1028 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1029 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch});
1030 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1031}
1032
1033fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
1034 const is_volatile = false; // TODO
1035 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1036 const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_val for {}", .{self.target.cpu.arch});
1037 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1038}
1039
1040fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
1041 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1042 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
1043 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_ptr for {}", .{self.target.cpu.arch});
1044 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
1045}
1046
1047fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
1048 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1049 _ = bin_op;
1050 return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch});
1051 // return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1052}
1053
1054fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
1055 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1056 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch});
1057 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1058}
1059
1060fn airClz(self: *Self, inst: Air.Inst.Index) !void {
1061 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1062 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch});
1063 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1064}
1065
1066fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
1067 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1068 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch});
1069 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1070}
1071
1072fn airPopcount(self: *Self, inst: Air.Inst.Index) !void {
1073 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1074 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch});
1075 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1076}
1077
1078fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
1079 if (!self.liveness.operandDies(inst, op_index))
1080 return false;
1081
1082 switch (mcv) {
1083 .register => |reg| {
1084 // If it's in the registers table, need to associate the register with the
1085 // new instruction.
1086 if (reg.allocIndex()) |index| {
1087 if (!self.register_manager.isRegFree(reg)) {
1088 self.register_manager.registers[index] = inst;
1089 }
1090 }
1091 log.debug("%{d} => {} (reused)", .{ inst, reg });
1092 },
1093 .stack_offset => |off| {
1094 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
1095 },
1096 else => return false,
1097 }
1098
1099 // Prevent the operand deaths processing code from deallocating it.
1100 self.liveness.clearOperandDeath(inst, op_index);
1101
1102 // That makes us responsible for doing the rest of the stuff that processDeath would have done.
1103 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1104 branch.inst_table.putAssumeCapacity(Air.refToIndex(operand).?, .dead);
1105
1106 return true;
1107}
1108
1109fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void {
1110 const elem_ty = ptr_ty.elemType();
1111 switch (ptr) {
1112 .none => unreachable,
1113 .undef => unreachable,
1114 .unreach => unreachable,
1115 .dead => unreachable,
1116 .compare_flags_unsigned => unreachable,
1117 .compare_flags_signed => unreachable,
1118 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
1119 .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),
1120 .ptr_embedded_in_code => |off| {
1121 try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off });
1122 },
1123 .embedded_in_code => {
1124 return self.fail("TODO implement loading from MCValue.embedded_in_code", .{});
1125 },
1126 .register => {
1127 return self.fail("TODO implement loading from MCValue.register", .{});
1128 },
1129 .memory => |addr| {
1130 const reg = try self.register_manager.allocReg(null, &.{});
1131 try self.genSetReg(ptr_ty, reg, .{ .memory = addr });
1132 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
1133 },
1134 .stack_offset => {
1135 return self.fail("TODO implement loading from MCValue.stack_offset", .{});
1136 },
1137 }
1138}
1139
1140fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
1141 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1142 const elem_ty = self.air.typeOfIndex(inst);
1143 const result: MCValue = result: {
1144 if (!elem_ty.hasCodeGenBits())
1145 break :result MCValue.none;
1146
1147 const ptr = try self.resolveInst(ty_op.operand);
1148 const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr();
1149 if (self.liveness.isUnused(inst) and !is_volatile)
1150 break :result MCValue.dead;
1151
1152 const dst_mcv: MCValue = blk: {
1153 if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
1154 // The MCValue that holds the pointer can be re-used as the value.
1155 break :blk ptr;
1156 } else {
1157 break :blk try self.allocRegOrMem(inst, true);
1158 }
1159 };
1160 try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand));
1161 break :result dst_mcv;
1162 };
1163 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1164}
1165
1166fn airStore(self: *Self, inst: Air.Inst.Index) !void {
1167 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1168 const ptr = try self.resolveInst(bin_op.lhs);
1169 const value = try self.resolveInst(bin_op.rhs);
1170 const elem_ty = self.air.typeOf(bin_op.rhs);
1171 switch (ptr) {
1172 .none => unreachable,
1173 .undef => unreachable,
1174 .unreach => unreachable,
1175 .dead => unreachable,
1176 .compare_flags_unsigned => unreachable,
1177 .compare_flags_signed => unreachable,
1178 .immediate => |imm| {
1179 try self.setRegOrMem(elem_ty, .{ .memory = imm }, value);
1180 },
1181 .ptr_stack_offset => |off| {
1182 try self.genSetStack(elem_ty, off, value);
1183 },
1184 .ptr_embedded_in_code => |off| {
1185 try self.setRegOrMem(elem_ty, .{ .embedded_in_code = off }, value);
1186 },
1187 .embedded_in_code => {
1188 return self.fail("TODO implement storing to MCValue.embedded_in_code", .{});
1189 },
1190 .register => {
1191 return self.fail("TODO implement storing to MCValue.register", .{});
1192 },
1193 .memory => {
1194 return self.fail("TODO implement storing to MCValue.memory", .{});
1195 },
1196 .stack_offset => {
1197 return self.fail("TODO implement storing to MCValue.stack_offset", .{});
1198 },
1199 }
1200 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
1201}
1202
1203fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {
1204 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1205 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
1206 return self.structFieldPtr(extra.struct_operand, ty_pl.ty, extra.field_index);
1207}
1208
1209fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {
1210 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1211 return self.structFieldPtr(ty_op.operand, ty_op.ty, index);
1212}
1213fn structFieldPtr(self: *Self, operand: Air.Inst.Ref, ty: Air.Inst.Ref, index: u32) !void {
1214 _ = self;
1215 _ = operand;
1216 _ = ty;
1217 _ = index;
1218 return self.fail("TODO implement codegen struct_field_ptr", .{});
1219 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
1220}
1221
1222fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
1223 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1224 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
1225 _ = extra;
1226 return self.fail("TODO implement codegen struct_field_val", .{});
1227 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
1228}
1229
1230fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
1231 const ty_str = self.air.instructions.items(.data)[inst].ty_str;
1232 const zir = &self.mod_fn.owner_decl.getFileScope().zir;
1233 const name = zir.nullTerminatedString(ty_str.str);
1234 const name_with_null = name.ptr[0 .. name.len + 1];
1235 const ty = self.air.getRefType(ty_str.ty);
1236
1237 switch (mcv) {
1238 .register => |reg| {
1239 switch (self.debug_output) {
1240 .dwarf => |dbg_out| {
1241 try dbg_out.dbg_info.ensureUnusedCapacity(3);
1242 dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
1243 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
1244 1, // ULEB128 dwarf expression length
1245 reg.dwarfLocOp(),
1246 });
1247 try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
1248 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
1249 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
1250 },
1251 .plan9 => {},
1252 .none => {},
1253 }
1254 },
1255 .stack_offset => |offset| {
1256 _ = offset;
1257 switch (self.debug_output) {
1258 .dwarf => {},
1259 .plan9 => {},
1260 .none => {},
1261 }
1262 },
1263 else => {},
1264 }
1265}
1266
1267fn airArg(self: *Self, inst: Air.Inst.Index) !void {
1268 const arg_index = self.arg_index;
1269 self.arg_index += 1;
1270
1271 const ty = self.air.typeOfIndex(inst);
1272 _ = ty;
1273
1274 const result = self.args[arg_index];
1275 // TODO support stack-only arguments
1276 // TODO Copy registers to the stack
1277 const mcv = result;
1278 try self.genArgDbgInfo(inst, mcv);
1279
1280 if (self.liveness.isUnused(inst))
1281 return self.finishAirBookkeeping();
1282
1283 switch (mcv) {
1284 .register => |reg| {
1285 self.register_manager.getRegAssumeFree(reg, inst);
1286 },
1287 else => {},
1288 }
1289
1290 return self.finishAir(inst, mcv, .{ .none, .none, .none });
1291}
1292
1293fn airBreakpoint(self: *Self) !void {
1294 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ebreak.toU32());
1295 return self.finishAirBookkeeping();
1296}
1297
1298fn airFence(self: *Self) !void {
1299 return self.fail("TODO implement fence() for {}", .{self.target.cpu.arch});
1300 //return self.finishAirBookkeeping();
1301}
1302
1303fn airCall(self: *Self, inst: Air.Inst.Index) !void {
1304 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1305 const fn_ty = self.air.typeOf(pl_op.operand);
1306 const callee = pl_op.operand;
1307 const extra = self.air.extraData(Air.Call, pl_op.payload);
1308 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]);
1309
1310 var info = try self.resolveCallingConventionValues(fn_ty);
1311 defer info.deinit(self);
1312
1313 // Due to incremental compilation, how function calls are generated depends
1314 // on linking.
1315 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
1316 if (info.args.len > 0) return self.fail("TODO implement fn args for {}", .{self.target.cpu.arch});
1317
1318 if (self.air.value(callee)) |func_value| {
1319 if (func_value.castTag(.function)) |func_payload| {
1320 const func = func_payload.data;
1321
1322 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1323 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1324 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1325 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1326 break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1327 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
1328 coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
1329 else
1330 unreachable;
1331
1332 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });
1333 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());
1334 } else if (func_value.castTag(.extern_fn)) |_| {
1335 return self.fail("TODO implement calling extern functions", .{});
1336 } else {
1337 return self.fail("TODO implement calling bitcasted functions", .{});
1338 }
1339 } else {
1340 return self.fail("TODO implement calling runtime known function pointer", .{});
1341 }
1342 } else if (self.bin_file.cast(link.File.MachO)) |_| {
1343 unreachable; // unsupported architecture for MachO
1344 } else if (self.bin_file.cast(link.File.Plan9)) |_| {
1345 return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch});
1346 } else unreachable;
1347
1348 const result: MCValue = result: {
1349 switch (info.return_value) {
1350 .register => |reg| {
1351 if (Register.allocIndex(reg) == null) {
1352 // Save function return value in a callee saved register
1353 break :result try self.copyToNewRegister(inst, info.return_value);
1354 }
1355 },
1356 else => {},
1357 }
1358 break :result info.return_value;
1359 };
1360
1361 if (args.len <= Liveness.bpi - 2) {
1362 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
1363 buf[0] = callee;
1364 std.mem.copy(Air.Inst.Ref, buf[1..], args);
1365 return self.finishAir(inst, result, buf);
1366 }
1367 var bt = try self.iterateBigTomb(inst, 1 + args.len);
1368 bt.feed(callee);
1369 for (args) |arg| {
1370 bt.feed(arg);
1371 }
1372 return bt.finishAir(result);
1373}
1374
1375fn ret(self: *Self, mcv: MCValue) !void {
1376 const ret_ty = self.fn_type.fnReturnType();
1377 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
1378 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());
1379}
1380
1381fn airRet(self: *Self, inst: Air.Inst.Index) !void {
1382 const un_op = self.air.instructions.items(.data)[inst].un_op;
1383 const operand = try self.resolveInst(un_op);
1384 try self.ret(operand);
1385 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
1386}
1387
1388fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
1389 const un_op = self.air.instructions.items(.data)[inst].un_op;
1390 const ptr = try self.resolveInst(un_op);
1391 _ = ptr;
1392 return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch});
1393 //return self.finishAir(inst, .dead, .{ un_op, .none, .none });
1394}
1395
1396fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
1397 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1398 if (self.liveness.isUnused(inst))
1399 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
1400 const ty = self.air.typeOf(bin_op.lhs);
1401 assert(ty.eql(self.air.typeOf(bin_op.rhs)));
1402 if (ty.zigTypeTag() == .ErrorSet)
1403 return self.fail("TODO implement cmp for errors", .{});
1404
1405 const lhs = try self.resolveInst(bin_op.lhs);
1406 const rhs = try self.resolveInst(bin_op.rhs);
1407 _ = op;
1408 _ = lhs;
1409 _ = rhs;
1410
1411 return self.fail("TODO implement cmp for {}", .{self.target.cpu.arch});
1412 // return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1413}
1414
1415fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
1416 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
1417 try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column);
1418 return self.finishAirBookkeeping();
1419}
1420
1421fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
1422 _ = inst;
1423
1424 return self.fail("TODO implement condbr {}", .{self.target.cpu.arch});
1425 // return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });
1426}
1427
1428fn isNull(self: *Self, operand: MCValue) !MCValue {
1429 _ = operand;
1430 // Here you can specialize this instruction if it makes sense to, otherwise the default
1431 // will call isNonNull and invert the result.
1432 return self.fail("TODO call isNonNull and invert the result", .{});
1433}
1434
1435fn isNonNull(self: *Self, operand: MCValue) !MCValue {
1436 _ = operand;
1437 // Here you can specialize this instruction if it makes sense to, otherwise the default
1438 // will call isNull and invert the result.
1439 return self.fail("TODO call isNull and invert the result", .{});
1440}
1441
1442fn isErr(self: *Self, operand: MCValue) !MCValue {
1443 _ = operand;
1444 // Here you can specialize this instruction if it makes sense to, otherwise the default
1445 // will call isNonNull and invert the result.
1446 return self.fail("TODO call isNonErr and invert the result", .{});
1447}
1448
1449fn isNonErr(self: *Self, operand: MCValue) !MCValue {
1450 _ = operand;
1451 // Here you can specialize this instruction if it makes sense to, otherwise the default
1452 // will call isNull and invert the result.
1453 return self.fail("TODO call isErr and invert the result", .{});
1454}
1455
1456fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
1457 const un_op = self.air.instructions.items(.data)[inst].un_op;
1458 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1459 const operand = try self.resolveInst(un_op);
1460 break :result try self.isNull(operand);
1461 };
1462 return self.finishAir(inst, result, .{ un_op, .none, .none });
1463}
1464
1465fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
1466 const un_op = self.air.instructions.items(.data)[inst].un_op;
1467 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1468 const operand_ptr = try self.resolveInst(un_op);
1469 const operand: MCValue = blk: {
1470 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
1471 // The MCValue that holds the pointer can be re-used as the value.
1472 break :blk operand_ptr;
1473 } else {
1474 break :blk try self.allocRegOrMem(inst, true);
1475 }
1476 };
1477 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
1478 break :result try self.isNull(operand);
1479 };
1480 return self.finishAir(inst, result, .{ un_op, .none, .none });
1481}
1482
1483fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
1484 const un_op = self.air.instructions.items(.data)[inst].un_op;
1485 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1486 const operand = try self.resolveInst(un_op);
1487 break :result try self.isNonNull(operand);
1488 };
1489 return self.finishAir(inst, result, .{ un_op, .none, .none });
1490}
1491
1492fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
1493 const un_op = self.air.instructions.items(.data)[inst].un_op;
1494 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1495 const operand_ptr = try self.resolveInst(un_op);
1496 const operand: MCValue = blk: {
1497 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
1498 // The MCValue that holds the pointer can be re-used as the value.
1499 break :blk operand_ptr;
1500 } else {
1501 break :blk try self.allocRegOrMem(inst, true);
1502 }
1503 };
1504 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
1505 break :result try self.isNonNull(operand);
1506 };
1507 return self.finishAir(inst, result, .{ un_op, .none, .none });
1508}
1509
1510fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
1511 const un_op = self.air.instructions.items(.data)[inst].un_op;
1512 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1513 const operand = try self.resolveInst(un_op);
1514 break :result try self.isErr(operand);
1515 };
1516 return self.finishAir(inst, result, .{ un_op, .none, .none });
1517}
1518
1519fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
1520 const un_op = self.air.instructions.items(.data)[inst].un_op;
1521 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1522 const operand_ptr = try self.resolveInst(un_op);
1523 const operand: MCValue = blk: {
1524 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
1525 // The MCValue that holds the pointer can be re-used as the value.
1526 break :blk operand_ptr;
1527 } else {
1528 break :blk try self.allocRegOrMem(inst, true);
1529 }
1530 };
1531 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
1532 break :result try self.isErr(operand);
1533 };
1534 return self.finishAir(inst, result, .{ un_op, .none, .none });
1535}
1536
1537fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
1538 const un_op = self.air.instructions.items(.data)[inst].un_op;
1539 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1540 const operand = try self.resolveInst(un_op);
1541 break :result try self.isNonErr(operand);
1542 };
1543 return self.finishAir(inst, result, .{ un_op, .none, .none });
1544}
1545
1546fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
1547 const un_op = self.air.instructions.items(.data)[inst].un_op;
1548 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1549 const operand_ptr = try self.resolveInst(un_op);
1550 const operand: MCValue = blk: {
1551 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
1552 // The MCValue that holds the pointer can be re-used as the value.
1553 break :blk operand_ptr;
1554 } else {
1555 break :blk try self.allocRegOrMem(inst, true);
1556 }
1557 };
1558 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
1559 break :result try self.isNonErr(operand);
1560 };
1561 return self.finishAir(inst, result, .{ un_op, .none, .none });
1562}
1563
1564fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
1565 // A loop is a setup to be able to jump back to the beginning.
1566 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1567 const loop = self.air.extraData(Air.Block, ty_pl.payload);
1568 const body = self.air.extra[loop.end..][0..loop.data.body_len];
1569 const start_index = self.code.items.len;
1570 try self.genBody(body);
1571 try self.jump(start_index);
1572 return self.finishAirBookkeeping();
1573}
1574
1575/// Send control flow to the `index` of `self.code`.
1576fn jump(self: *Self, index: usize) !void {
1577 _ = index;
1578 return self.fail("TODO implement jump for {}", .{self.target.cpu.arch});
1579}
1580
1581fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
1582 try self.blocks.putNoClobber(self.gpa, inst, .{
1583 // A block is a setup to be able to jump to the end.
1584 .relocs = .{},
1585 // It also acts as a receptacle for break operands.
1586 // Here we use `MCValue.none` to represent a null value so that the first
1587 // break instruction will choose a MCValue for the block result and overwrite
1588 // this field. Following break instructions will use that MCValue to put their
1589 // block results.
1590 .mcv = MCValue{ .none = {} },
1591 });
1592 const block_data = self.blocks.getPtr(inst).?;
1593 defer block_data.relocs.deinit(self.gpa);
1594
1595 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1596 const extra = self.air.extraData(Air.Block, ty_pl.payload);
1597 const body = self.air.extra[extra.end..][0..extra.data.body_len];
1598 try self.genBody(body);
1599
1600 for (block_data.relocs.items) |reloc| try self.performReloc(reloc);
1601
1602 const result = @bitCast(MCValue, block_data.mcv);
1603 return self.finishAir(inst, result, .{ .none, .none, .none });
1604}
1605
1606fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
1607 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1608 const condition = pl_op.operand;
1609 _ = condition;
1610 return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch});
1611 // return self.finishAir(inst, .dead, .{ condition, .none, .none });
1612}
1613
1614fn performReloc(self: *Self, reloc: Reloc) !void {
1615 _ = self;
1616 switch (reloc) {
1617 .rel32 => unreachable,
1618 .arm_branch => unreachable,
1619 }
1620}
1621
1622fn airBr(self: *Self, inst: Air.Inst.Index) !void {
1623 const branch = self.air.instructions.items(.data)[inst].br;
1624 try self.br(branch.block_inst, branch.operand);
1625 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
1626}
1627
1628fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void {
1629 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1630 const air_tags = self.air.instructions.items(.tag);
1631 _ = air_tags;
1632 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement boolean operations for {}", .{self.target.cpu.arch});
1633 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1634}
1635
1636fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
1637 const block_data = self.blocks.getPtr(block).?;
1638
1639 if (self.air.typeOf(operand).hasCodeGenBits()) {
1640 const operand_mcv = try self.resolveInst(operand);
1641 const block_mcv = block_data.mcv;
1642 if (block_mcv == .none) {
1643 block_data.mcv = operand_mcv;
1644 } else {
1645 try self.setRegOrMem(self.air.typeOfIndex(block), block_mcv, operand_mcv);
1646 }
1647 }
1648 return self.brVoid(block);
1649}
1650
1651fn brVoid(self: *Self, block: Air.Inst.Index) !void {
1652 const block_data = self.blocks.getPtr(block).?;
1653
1654 // Emit a jump with a relocation. It will be patched up after the block ends.
1655 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1);
1656
1657 return self.fail("TODO implement brvoid for {}", .{self.target.cpu.arch});
1658}
1659
1660fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
1661 const air_datas = self.air.instructions.items(.data);
1662 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
1663 const zir = self.mod_fn.owner_decl.getFileScope().zir;
1664 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
1665 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
1666 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
1667 const outputs_len = @truncate(u5, extended.small);
1668 const args_len = @truncate(u5, extended.small >> 5);
1669 const clobbers_len = @truncate(u5, extended.small >> 10);
1670 _ = clobbers_len; // TODO honor these
1671 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
1672 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]);
1673 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]);
1674
1675 if (outputs_len > 1) {
1676 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
1677 }
1678 var extra_i: usize = zir_extra.end;
1679 const output_constraint: ?[]const u8 = out: {
1680 var i: usize = 0;
1681 while (i < outputs_len) : (i += 1) {
1682 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
1683 extra_i = output.end;
1684 break :out zir.nullTerminatedString(output.data.constraint);
1685 }
1686 break :out null;
1687 };
1688
1689 const dead = !is_volatile and self.liveness.isUnused(inst);
1690 const result: MCValue = if (dead) .dead else result: {
1691 for (args) |arg| {
1692 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
1693 extra_i = input.end;
1694 const constraint = zir.nullTerminatedString(input.data.constraint);
1695
1696 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
1697 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
1698 }
1699 const reg_name = constraint[1 .. constraint.len - 1];
1700 const reg = parseRegName(reg_name) orelse
1701 return self.fail("unrecognized register: '{s}'", .{reg_name});
1702
1703 const arg_mcv = try self.resolveInst(arg);
1704 try self.register_manager.getReg(reg, null);
1705 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
1706 }
1707
1708 if (mem.eql(u8, asm_source, "ecall")) {
1709 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ecall.toU32());
1710 } else {
1711 return self.fail("TODO implement support for more riscv64 assembly instructions", .{});
1712 }
1713
1714 if (output_constraint) |output| {
1715 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
1716 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
1717 }
1718 const reg_name = output[2 .. output.len - 1];
1719 const reg = parseRegName(reg_name) orelse
1720 return self.fail("unrecognized register: '{s}'", .{reg_name});
1721 break :result MCValue{ .register = reg };
1722 } else {
1723 break :result MCValue{ .none = {} };
1724 }
1725 };
1726 if (outputs.len + args.len <= Liveness.bpi - 1) {
1727 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
1728 std.mem.copy(Air.Inst.Ref, &buf, outputs);
1729 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
1730 return self.finishAir(inst, result, buf);
1731 }
1732 var bt = try self.iterateBigTomb(inst, outputs.len + args.len);
1733 for (outputs) |output| {
1734 bt.feed(output);
1735 }
1736 for (args) |arg| {
1737 bt.feed(arg);
1738 }
1739 return bt.finishAir(result);
1740}
1741
1742fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
1743 try self.ensureProcessDeathCapacity(operand_count + 1);
1744 return BigTomb{
1745 .function = self,
1746 .inst = inst,
1747 .tomb_bits = self.liveness.getTombBits(inst),
1748 .big_tomb_bits = self.liveness.special.get(inst) orelse 0,
1749 .bit_index = 0,
1750 };
1751}
1752
1753/// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
1754fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
1755 switch (loc) {
1756 .none => return,
1757 .register => |reg| return self.genSetReg(ty, reg, val),
1758 .stack_offset => |off| return self.genSetStack(ty, off, val),
1759 .memory => {
1760 return self.fail("TODO implement setRegOrMem for memory", .{});
1761 },
1762 else => unreachable,
1763 }
1764}
1765
1766fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
1767 _ = ty;
1768 _ = stack_offset;
1769 _ = mcv;
1770 return self.fail("TODO implement getSetStack for {}", .{self.target.cpu.arch});
1771}
1772
1773fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
1774 switch (mcv) {
1775 .dead => unreachable,
1776 .ptr_stack_offset => unreachable,
1777 .ptr_embedded_in_code => unreachable,
1778 .unreach, .none => return, // Nothing to do.
1779 .undef => {
1780 if (!self.wantSafety())
1781 return; // The already existing value will do just fine.
1782 // Write the debug undefined value.
1783 return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
1784 },
1785 .immediate => |unsigned_x| {
1786 const x = @bitCast(i64, unsigned_x);
1787 if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {
1788 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, .zero, @truncate(i12, x)).toU32());
1789 return;
1790 }
1791 if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {
1792 const lo12 = @truncate(i12, x);
1793 const carry: i32 = if (lo12 < 0) 1 else 0;
1794 const hi20 = @truncate(i20, (x >> 12) +% carry);
1795
1796 // TODO: add test case for 32-bit immediate
1797 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.lui(reg, hi20).toU32());
1798 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, reg, lo12).toU32());
1799 return;
1800 }
1801 // li rd, immediate
1802 // "Myriad sequences"
1803 return self.fail("TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
1804 },
1805 .memory => |addr| {
1806 // The value is in memory at a hard-coded address.
1807 // If the type is a pointer, it means the pointer address is at this memory location.
1808 try self.genSetReg(ty, reg, .{ .immediate = addr });
1809
1810 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32());
1811 // LOAD imm=[i12 offset = 0], rs1 =
1812
1813 // return self.fail("TODO implement genSetReg memory for riscv64");
1814 },
1815 else => return self.fail("TODO implement getSetReg for riscv64 {}", .{mcv}),
1816 }
1817}
1818
1819fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
1820 const un_op = self.air.instructions.items(.data)[inst].un_op;
1821 const result = try self.resolveInst(un_op);
1822 return self.finishAir(inst, result, .{ un_op, .none, .none });
1823}
1824
1825fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
1826 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1827 const result = try self.resolveInst(ty_op.operand);
1828 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1829}
1830
1831fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
1832 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1833 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airArrayToSlice for {}", .{
1834 self.target.cpu.arch,
1835 });
1836 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1837}
1838
1839fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void {
1840 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1841 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntToFloat for {}", .{
1842 self.target.cpu.arch,
1843 });
1844 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1845}
1846
1847fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
1848 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1849 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatToInt for {}", .{
1850 self.target.cpu.arch,
1851 });
1852 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1853}
1854
1855fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
1856 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1857 const extra = self.air.extraData(Air.Block, ty_pl.payload);
1858 _ = extra;
1859 return self.fail("TODO implement airCmpxchg for {}", .{
1860 self.target.cpu.arch,
1861 });
1862 // return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value });
1863}
1864
1865fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
1866 _ = inst;
1867 return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch});
1868}
1869
1870fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void {
1871 _ = inst;
1872 return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch});
1873}
1874
1875fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void {
1876 _ = inst;
1877 _ = order;
1878 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
1879}
1880
1881fn airMemset(self: *Self, inst: Air.Inst.Index) !void {
1882 _ = inst;
1883 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
1884}
1885
1886fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
1887 _ = inst;
1888 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});
1889}
1890
1891fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
1892 // First section of indexes correspond to a set number of constant values.
1893 const ref_int = @enumToInt(inst);
1894 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
1895 const tv = Air.Inst.Ref.typed_value_map[ref_int];
1896 if (!tv.ty.hasCodeGenBits()) {
1897 return MCValue{ .none = {} };
1898 }
1899 return self.genTypedValue(tv);
1900 }
1901
1902 // If the type has no codegen bits, no need to store it.
1903 const inst_ty = self.air.typeOf(inst);
1904 if (!inst_ty.hasCodeGenBits())
1905 return MCValue{ .none = {} };
1906
1907 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
1908 switch (self.air.instructions.items(.tag)[inst_index]) {
1909 .constant => {
1910 // Constants have static lifetimes, so they are always memoized in the outer most table.
1911 const branch = &self.branch_stack.items[0];
1912 const gop = try branch.inst_table.getOrPut(self.gpa, inst_index);
1913 if (!gop.found_existing) {
1914 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;
1915 gop.value_ptr.* = try self.genTypedValue(.{
1916 .ty = inst_ty,
1917 .val = self.air.values[ty_pl.payload],
1918 });
1919 }
1920 return gop.value_ptr.*;
1921 },
1922 .const_ty => unreachable,
1923 else => return self.getResolvedInstValue(inst_index),
1924 }
1925}
1926
1927fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
1928 // Treat each stack item as a "layer" on top of the previous one.
1929 var i: usize = self.branch_stack.items.len;
1930 while (true) {
1931 i -= 1;
1932 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
1933 assert(mcv != .dead);
1934 return mcv;
1935 }
1936 }
1937}
1938
1939/// If the MCValue is an immediate, and it does not fit within this type,
1940/// we put it in a register.
1941/// A potential opportunity for future optimization here would be keeping track
1942/// of the fact that the instruction is available both as an immediate
1943/// and as a register.
1944fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCValue {
1945 const mcv = try self.resolveInst(operand);
1946 const ti = @typeInfo(T).Int;
1947 switch (mcv) {
1948 .immediate => |imm| {
1949 // This immediate is unsigned.
1950 const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed));
1951 if (imm >= math.maxInt(U)) {
1952 return MCValue{ .register = try self.copyToTmpRegister(Type.initTag(.usize), mcv) };
1953 }
1954 },
1955 else => {},
1956 }
1957 return mcv;
1958}
1959
1960fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
1961 if (typed_value.val.isUndef())
1962 return MCValue{ .undef = {} };
1963 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1964 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1965 switch (typed_value.ty.zigTypeTag()) {
1966 .Pointer => switch (typed_value.ty.ptrSize()) {
1967 .Slice => {
1968 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
1969 const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
1970 const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val });
1971 const slice_len = typed_value.val.sliceLen();
1972 // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean
1973 // the Sema code needs to use anonymous Decls or alloca instructions to store data.
1974 const ptr_imm = ptr_mcv.memory;
1975 _ = slice_len;
1976 _ = ptr_imm;
1977 // We need more general support for const data being stored in memory to make this work.
1978 return self.fail("TODO codegen for const slices", .{});
1979 },
1980 else => {
1981 if (typed_value.val.castTag(.decl_ref)) |payload| {
1982 const decl = payload.data;
1983 decl.alive = true;
1984 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
1985 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1986 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
1987 return MCValue{ .memory = got_addr };
1988 } else if (self.bin_file.cast(link.File.MachO)) |_| {
1989 // TODO I'm hacking my way through here by repurposing .memory for storing
1990 // index to the GOT target symbol index.
1991 return MCValue{ .memory = decl.link.macho.local_sym_index };
1992 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
1993 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
1994 return MCValue{ .memory = got_addr };
1995 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
1996 try p9.seeDecl(decl);
1997 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
1998 return MCValue{ .memory = got_addr };
1999 } else {
2000 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
2001 }
2002 }
2003 if (typed_value.val.tag() == .int_u64) {
2004 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
2005 }
2006 return self.fail("TODO codegen more kinds of const pointers", .{});
2007 },
2008 },
2009 .Int => {
2010 const info = typed_value.ty.intInfo(self.target.*);
2011 if (info.bits > ptr_bits or info.signedness == .signed) {
2012 return self.fail("TODO const int bigger than ptr and signed int", .{});
2013 }
2014 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
2015 },
2016 .Bool => {
2017 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
2018 },
2019 .ComptimeInt => unreachable, // semantic analysis prevents this
2020 .ComptimeFloat => unreachable, // semantic analysis prevents this
2021 .Optional => {
2022 if (typed_value.ty.isPtrLikeOptional()) {
2023 if (typed_value.val.isNull())
2024 return MCValue{ .immediate = 0 };
2025
2026 var buf: Type.Payload.ElemType = undefined;
2027 return self.genTypedValue(.{
2028 .ty = typed_value.ty.optionalChild(&buf),
2029 .val = typed_value.val,
2030 });
2031 } else if (typed_value.ty.abiSize(self.target.*) == 1) {
2032 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
2033 }
2034 return self.fail("TODO non pointer optionals", .{});
2035 },
2036 .Enum => {
2037 if (typed_value.val.castTag(.enum_field_index)) |field_index| {
2038 switch (typed_value.ty.tag()) {
2039 .enum_simple => {
2040 return MCValue{ .immediate = field_index.data };
2041 },
2042 .enum_full, .enum_nonexhaustive => {
2043 const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
2044 if (enum_full.values.count() != 0) {
2045 const tag_val = enum_full.values.keys()[field_index.data];
2046 return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val });
2047 } else {
2048 return MCValue{ .immediate = field_index.data };
2049 }
2050 },
2051 else => unreachable,
2052 }
2053 } else {
2054 var int_tag_buffer: Type.Payload.Bits = undefined;
2055 const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
2056 return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val });
2057 }
2058 },
2059 .ErrorSet => {
2060 switch (typed_value.val.tag()) {
2061 .@"error" => {
2062 const err_name = typed_value.val.castTag(.@"error").?.data.name;
2063 const module = self.bin_file.options.module.?;
2064 const global_error_set = module.global_error_set;
2065 const error_index = global_error_set.get(err_name).?;
2066 return MCValue{ .immediate = error_index };
2067 },
2068 else => {
2069 // In this case we are rendering an error union which has a 0 bits payload.
2070 return MCValue{ .immediate = 0 };
2071 },
2072 }
2073 },
2074 .ErrorUnion => {
2075 const error_type = typed_value.ty.errorUnionSet();
2076 const payload_type = typed_value.ty.errorUnionPayload();
2077 const sub_val = typed_value.val.castTag(.eu_payload).?.data;
2078
2079 if (!payload_type.hasCodeGenBits()) {
2080 // We use the error type directly as the type.
2081 return self.genTypedValue(.{ .ty = error_type, .val = sub_val });
2082 }
2083
2084 return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty});
2085 },
2086 else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}),
2087 }
2088}
2089
2090const CallMCValues = struct {
2091 args: []MCValue,
2092 return_value: MCValue,
2093 stack_byte_count: u32,
2094 stack_align: u32,
2095
2096 fn deinit(self: *CallMCValues, func: *Self) void {
2097 func.gpa.free(self.args);
2098 self.* = undefined;
2099 }
2100};
2101
2102/// Caller must call `CallMCValues.deinit`.
2103fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
2104 const cc = fn_ty.fnCallingConvention();
2105 _ = cc;
2106 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
2107 defer self.gpa.free(param_types);
2108 fn_ty.fnParamTypes(param_types);
2109 var result: CallMCValues = .{
2110 .args = try self.gpa.alloc(MCValue, param_types.len),
2111 // These undefined values must be populated before returning from this function.
2112 .return_value = undefined,
2113 .stack_byte_count = undefined,
2114 .stack_align = undefined,
2115 };
2116 errdefer self.gpa.free(result.args);
2117
2118 const ret_ty = fn_ty.fnReturnType();
2119
2120 if (param_types.len != 0) {
2121 return self.fail("TODO implement codegen parameters for {}", .{self.target.cpu.arch});
2122 }
2123
2124 if (ret_ty.zigTypeTag() == .NoReturn) {
2125 result.return_value = .{ .unreach = {} };
2126 } else if (!ret_ty.hasCodeGenBits()) {
2127 result.return_value = .{ .none = {} };
2128 } else return self.fail("TODO implement codegen return values for {}", .{self.target.cpu.arch});
2129 return result;
2130}
2131
2132/// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`.
2133fn wantSafety(self: *Self) bool {
2134 return switch (self.bin_file.options.optimize_mode) {
2135 .Debug => true,
2136 .ReleaseSafe => true,
2137 .ReleaseFast => false,
2138 .ReleaseSmall => false,
2139 };
2140}
2141
2142fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {
2143 @setCold(true);
2144 assert(self.err_msg == null);
2145 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args);
2146 return error.CodegenFail;
2147}
2148
2149fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError {
2150 @setCold(true);
2151 assert(self.err_msg == null);
2152 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args);
2153 return error.CodegenFail;
2154}
2155
2156const Register = @import("bits.zig").Register;
2157const Instruction = @import("bits.zig").Instruction;
2158const callee_preserved_regs = @import("bits.zig").callee_preserved_regs;
2159
2160fn parseRegName(name: []const u8) ?Register {
2161 if (@hasDecl(Register, "parseRegName")) {
2162 return Register.parseRegName(name);
2163 }
2164 return std.meta.stringToEnum(Register, name);
2165}
src/codegen.zig+1-117
...@@ -106,7 +106,7 @@ pub fn generateFunction(...@@ -106,7 +106,7 @@ pub fn generateFunction(
106 //.r600 => return Function(.r600).generate(bin_file, src_loc, func, air, liveness, code, debug_output),106 //.r600 => return Function(.r600).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
107 //.amdgcn => return Function(.amdgcn).generate(bin_file, src_loc, func, air, liveness, code, debug_output),107 //.amdgcn => return Function(.amdgcn).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
108 //.riscv32 => return Function(.riscv32).generate(bin_file, src_loc, func, air, liveness, code, debug_output),108 //.riscv32 => return Function(.riscv32).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
109 .riscv64 => return Function(.riscv64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),109 .riscv64 => return @import("arch/riscv64/CodeGen.zig").generate(bin_file, src_loc, func, air, liveness, code, debug_output),
110 //.sparc => return Function(.sparc).generate(bin_file, src_loc, func, air, liveness, code, debug_output),110 //.sparc => return Function(.sparc).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
111 //.sparcv9 => return Function(.sparcv9).generate(bin_file, src_loc, func, air, liveness, code, debug_output),111 //.sparcv9 => return Function(.sparcv9).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
112 //.sparcel => return Function(.sparcel).generate(bin_file, src_loc, func, air, liveness, code, debug_output),112 //.sparcel => return Function(.sparcel).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
...@@ -2123,9 +2123,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2123,9 +2123,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2123 .i386 => {2123 .i386 => {
2124 try self.code.append(0xcc); // int32124 try self.code.append(0xcc); // int3
2125 },2125 },
2126 .riscv64 => {
2127 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ebreak.toU32());
2128 },
2129 .arm, .armeb => {2126 .arm, .armeb => {
2130 writeInt(u32, try self.code.addManyAsArray(4), Instruction.bkpt(0).toU32());2127 writeInt(u32, try self.code.addManyAsArray(4), Instruction.bkpt(0).toU32());
2131 },2128 },
...@@ -2153,34 +2150,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2153,34 +2150,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2153 // on linking.2150 // on linking.
2154 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {2151 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
2155 switch (arch) {2152 switch (arch) {
2156 .riscv64 => {
2157 if (info.args.len > 0) return self.fail("TODO implement fn args for {}", .{self.target.cpu.arch});
2158
2159 if (self.air.value(callee)) |func_value| {
2160 if (func_value.castTag(.function)) |func_payload| {
2161 const func = func_payload.data;
2162
2163 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
2164 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
2165 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
2166 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
2167 break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
2168 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
2169 coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
2170 else
2171 unreachable;
2172
2173 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr });
2174 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.ra, 0, .ra).toU32());
2175 } else if (func_value.castTag(.extern_fn)) |_| {
2176 return self.fail("TODO implement calling extern functions", .{});
2177 } else {
2178 return self.fail("TODO implement calling bitcasted functions", .{});
2179 }
2180 } else {
2181 return self.fail("TODO implement calling runtime known function pointer", .{});
2182 }
2183 },
2184 .arm, .armeb => {2153 .arm, .armeb => {
2185 for (info.args) |mc_arg, arg_i| {2154 for (info.args) |mc_arg, arg_i| {
2186 const arg = args[arg_i];2155 const arg = args[arg_i];
...@@ -2287,9 +2256,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2287,9 +2256,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2287 .i386 => {2256 .i386 => {
2288 try self.code.append(0xc3); // ret2257 try self.code.append(0xc3); // ret
2289 },2258 },
2290 .riscv64 => {
2291 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.jalr(.zero, 0, .ra).toU32());
2292 },
2293 .arm, .armeb => {2259 .arm, .armeb => {
2294 // Just add space for an instruction, patch this later2260 // Just add space for an instruction, patch this later
2295 try self.code.resize(self.code.items.len + 4);2261 try self.code.resize(self.code.items.len + 4);
...@@ -2969,42 +2935,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2969,42 +2935,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2969 break :result MCValue{ .none = {} };2935 break :result MCValue{ .none = {} };
2970 }2936 }
2971 },2937 },
2972 .riscv64 => result: {
2973 for (args) |arg| {
2974 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
2975 extra_i = input.end;
2976 const constraint = zir.nullTerminatedString(input.data.constraint);
2977
2978 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
2979 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
2980 }
2981 const reg_name = constraint[1 .. constraint.len - 1];
2982 const reg = parseRegName(reg_name) orelse
2983 return self.fail("unrecognized register: '{s}'", .{reg_name});
2984
2985 const arg_mcv = try self.resolveInst(arg);
2986 try self.register_manager.getReg(reg, null);
2987 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
2988 }
2989
2990 if (mem.eql(u8, asm_source, "ecall")) {
2991 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ecall.toU32());
2992 } else {
2993 return self.fail("TODO implement support for more riscv64 assembly instructions", .{});
2994 }
2995
2996 if (output_constraint) |output| {
2997 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
2998 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
2999 }
3000 const reg_name = output[2 .. output.len - 1];
3001 const reg = parseRegName(reg_name) orelse
3002 return self.fail("unrecognized register: '{s}'", .{reg_name});
3003 break :result MCValue{ .register = reg };
3004 } else {
3005 break :result MCValue{ .none = {} };
3006 }
3007 },
3008 .i386 => result: {2938 .i386 => result: {
3009 for (args) |arg| {2939 for (args) |arg| {
3010 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);2940 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
...@@ -3311,49 +3241,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3311,49 +3241,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3311 },3241 },
3312 else => return self.fail("TODO implement getSetReg for arm {}", .{mcv}),3242 else => return self.fail("TODO implement getSetReg for arm {}", .{mcv}),
3313 },3243 },
3314 .riscv64 => switch (mcv) {
3315 .dead => unreachable,
3316 .ptr_stack_offset => unreachable,
3317 .ptr_embedded_in_code => unreachable,
3318 .unreach, .none => return, // Nothing to do.
3319 .undef => {
3320 if (!self.wantSafety())
3321 return; // The already existing value will do just fine.
3322 // Write the debug undefined value.
3323 return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa });
3324 },
3325 .immediate => |unsigned_x| {
3326 const x = @bitCast(i64, unsigned_x);
3327 if (math.minInt(i12) <= x and x <= math.maxInt(i12)) {
3328 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, .zero, @truncate(i12, x)).toU32());
3329 return;
3330 }
3331 if (math.minInt(i32) <= x and x <= math.maxInt(i32)) {
3332 const lo12 = @truncate(i12, x);
3333 const carry: i32 = if (lo12 < 0) 1 else 0;
3334 const hi20 = @truncate(i20, (x >> 12) +% carry);
3335
3336 // TODO: add test case for 32-bit immediate
3337 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.lui(reg, hi20).toU32());
3338 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.addi(reg, reg, lo12).toU32());
3339 return;
3340 }
3341 // li rd, immediate
3342 // "Myriad sequences"
3343 return self.fail("TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf
3344 },
3345 .memory => |addr| {
3346 // The value is in memory at a hard-coded address.
3347 // If the type is a pointer, it means the pointer address is at this memory location.
3348 try self.genSetReg(ty, reg, .{ .immediate = addr });
3349
3350 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ld(reg, 0, reg).toU32());
3351 // LOAD imm=[i12 offset = 0], rs1 =
3352
3353 // return self.fail("TODO implement genSetReg memory for riscv64");
3354 },
3355 else => return self.fail("TODO implement getSetReg for riscv64 {}", .{mcv}),
3356 },
3357 else => return self.fail("TODO implement getSetReg for {}", .{self.target.cpu.arch}),3244 else => return self.fail("TODO implement getSetReg for {}", .{self.target.cpu.arch}),
3358 }3245 }
3359 }3246 }
...@@ -3762,7 +3649,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3762,7 +3649,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
37623649
3763 const Register = switch (arch) {3650 const Register = switch (arch) {
3764 .i386 => @import("arch/x86/bits.zig").Register,3651 .i386 => @import("arch/x86/bits.zig").Register,
3765 .riscv64 => @import("arch/riscv64/bits.zig").Register,
3766 .arm, .armeb => @import("arch/arm/bits.zig").Register,3652 .arm, .armeb => @import("arch/arm/bits.zig").Register,
3767 else => enum {3653 else => enum {
3768 dummy,3654 dummy,
...@@ -3775,7 +3661,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3775,7 +3661,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
3775 };3661 };
37763662
3777 const Instruction = switch (arch) {3663 const Instruction = switch (arch) {
3778 .riscv64 => @import("arch/riscv64/bits.zig").Instruction,
3779 .arm, .armeb => @import("arch/arm/bits.zig").Instruction,3664 .arm, .armeb => @import("arch/arm/bits.zig").Instruction,
3780 else => void,3665 else => void,
3781 };3666 };
...@@ -3787,7 +3672,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -3787,7 +3672,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
37873672
3788 const callee_preserved_regs = switch (arch) {3673 const callee_preserved_regs = switch (arch) {
3789 .i386 => @import("arch/x86/bits.zig").callee_preserved_regs,3674 .i386 => @import("arch/x86/bits.zig").callee_preserved_regs,
3790 .riscv64 => @import("arch/riscv64/bits.zig").callee_preserved_regs,
3791 .arm, .armeb => @import("arch/arm/bits.zig").callee_preserved_regs,3675 .arm, .armeb => @import("arch/arm/bits.zig").callee_preserved_regs,
3792 else => [_]Register{},3676 else => [_]Register{},
3793 };3677 };