authorgravatar for joachim.schmidt557@outlook.comJoachim Schmidt <joachim.schmidt557@outlook.com> 2021-10-15 15:54:00+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-10-15 13:58:14-04:00
log01b390568872344e53862d61da091071e0ab1f3e
tree1aeaab0e2cf2147affed763d41eb0e4dd9aa328b
parent6a9726e495650602638037c9ec022e264eadb586

stage2 AArch64: move codegen to separate file


2 files changed, 2916 insertions(+), 531 deletions(-)

src/arch/aarch64/CodeGen.zig created+2911
......@@ -0,0 +1,2911 @@
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 GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError;
26const FnResult = @import("../../codegen.zig").FnResult;
27const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput;
28
29const InnerError = error{
30 OutOfMemory,
31 CodegenFail,
32};
33
34arch: std.Target.Cpu.Arch,
35gpa: *Allocator,
36air: Air,
37liveness: Liveness,
38bin_file: *link.File,
39target: *const std.Target,
40mod_fn: *const Module.Fn,
41code: *std.ArrayList(u8),
42debug_output: DebugInfoOutput,
43err_msg: ?*ErrorMsg,
44args: []MCValue,
45ret_mcv: MCValue,
46fn_type: Type,
47arg_index: usize,
48src_loc: Module.SrcLoc,
49stack_align: u32,
50
51prev_di_line: u32,
52prev_di_column: u32,
53/// Byte offset within the source file of the ending curly.
54end_di_line: u32,
55end_di_column: u32,
56/// Relative to the beginning of `code`.
57prev_di_pc: usize,
58
59/// The value is an offset into the `Function` `code` from the beginning.
60/// To perform the reloc, write 32-bit signed little-endian integer
61/// which is a relative jump, based on the address following the reloc.
62exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{},
63
64/// Whenever there is a runtime branch, we push a Branch onto this stack,
65/// and pop it off when the runtime branch joins. This provides an "overlay"
66/// of the table of mappings from instructions to `MCValue` from within the branch.
67/// This way we can modify the `MCValue` for an instruction in different ways
68/// within different branches. Special consideration is needed when a branch
69/// joins with its parent, to make sure all instructions have the same MCValue
70/// across each runtime branch upon joining.
71branch_stack: *std.ArrayList(Branch),
72
73// Key is the block instruction
74blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{},
75
76register_manager: RegisterManager(Self, Register, &callee_preserved_regs) = .{},
77/// Maps offset to what is stored there.
78stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
79
80/// Offset from the stack base, representing the end of the stack frame.
81max_end_stack: u32 = 0,
82/// Represents the current end stack offset. If there is no existing slot
83/// to place a new stack allocation, it goes here, and then bumps `max_end_stack`.
84next_stack_offset: u32 = 0,
85
86/// Debug field, used to find bugs in the compiler.
87air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init,
88
89const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {};
90
91const MCValue = union(enum) {
92 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc.
93 /// TODO Look into deleting this tag and using `dead` instead, since every use
94 /// of MCValue.none should be instead looking at the type and noticing it is 0 bits.
95 none,
96 /// Control flow will not allow this value to be observed.
97 unreach,
98 /// No more references to this value remain.
99 dead,
100 /// The value is undefined.
101 undef,
102 /// A pointer-sized integer that fits in a register.
103 /// If the type is a pointer, this is the pointer address in virtual address space.
104 immediate: u64,
105 /// The constant was emitted into the code, at this offset.
106 /// If the type is a pointer, it means the pointer address is embedded in the code.
107 embedded_in_code: usize,
108 /// The value is a pointer to a constant which was emitted into the code, at this offset.
109 ptr_embedded_in_code: usize,
110 /// The value is in a target-specific register.
111 register: Register,
112 /// The value is in memory at a hard-coded address.
113 /// If the type is a pointer, it means the pointer address is at this memory location.
114 memory: u64,
115 /// The value is one of the stack variables.
116 /// If the type is a pointer, it means the pointer address is in the stack at this offset.
117 stack_offset: u32,
118 /// The value is a pointer to one of the stack variables (payload is stack offset).
119 ptr_stack_offset: u32,
120 /// The value is in the compare flags assuming an unsigned operation,
121 /// with this operator applied on top of it.
122 compare_flags_unsigned: math.CompareOperator,
123 /// The value is in the compare flags assuming a signed operation,
124 /// with this operator applied on top of it.
125 compare_flags_signed: math.CompareOperator,
126
127 fn isMemory(mcv: MCValue) bool {
128 return switch (mcv) {
129 .embedded_in_code, .memory, .stack_offset => true,
130 else => false,
131 };
132 }
133
134 fn isImmediate(mcv: MCValue) bool {
135 return switch (mcv) {
136 .immediate => true,
137 else => false,
138 };
139 }
140
141 fn isMutable(mcv: MCValue) bool {
142 return switch (mcv) {
143 .none => unreachable,
144 .unreach => unreachable,
145 .dead => unreachable,
146
147 .immediate,
148 .embedded_in_code,
149 .memory,
150 .compare_flags_unsigned,
151 .compare_flags_signed,
152 .ptr_stack_offset,
153 .ptr_embedded_in_code,
154 .undef,
155 => false,
156
157 .register,
158 .stack_offset,
159 => true,
160 };
161 }
162};
163
164const Branch = struct {
165 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{},
166
167 fn deinit(self: *Branch, gpa: *Allocator) void {
168 self.inst_table.deinit(gpa);
169 self.* = undefined;
170 }
171};
172
173const StackAllocation = struct {
174 inst: Air.Inst.Index,
175 /// TODO do we need size? should be determined by inst.ty.abiSize()
176 size: u32,
177};
178
179const BlockData = struct {
180 relocs: std.ArrayListUnmanaged(Reloc),
181 /// The first break instruction encounters `null` here and chooses a
182 /// machine code value for the block result, populating this field.
183 /// Following break instructions encounter that value and use it for
184 /// the location to store their block results.
185 mcv: MCValue,
186};
187
188const Reloc = union(enum) {
189 /// The value is an offset into the `Function` `code` from the beginning.
190 /// To perform the reloc, write 32-bit signed little-endian integer
191 /// which is a relative jump, based on the address following the reloc.
192 rel32: usize,
193 /// A branch in the ARM instruction set
194 arm_branch: struct {
195 pos: usize,
196 cond: @import("../arm/bits.zig").Condition,
197 },
198};
199
200const BigTomb = struct {
201 function: *Self,
202 inst: Air.Inst.Index,
203 tomb_bits: Liveness.Bpi,
204 big_tomb_bits: u32,
205 bit_index: usize,
206
207 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
208 const this_bit_index = bt.bit_index;
209 bt.bit_index += 1;
210
211 const op_int = @enumToInt(op_ref);
212 if (op_int < Air.Inst.Ref.typed_value_map.len) return;
213 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
214
215 if (this_bit_index < Liveness.bpi - 1) {
216 const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0;
217 if (!dies) return;
218 } else {
219 const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1));
220 const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0;
221 if (!dies) return;
222 }
223 bt.function.processDeath(op_index);
224 }
225
226 fn finishAir(bt: *BigTomb, result: MCValue) void {
227 const is_used = !bt.function.liveness.isUnused(bt.inst);
228 if (is_used) {
229 log.debug("%{d} => {}", .{ bt.inst, result });
230 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1];
231 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result);
232 }
233 bt.function.finishAirBookkeeping();
234 }
235};
236
237const Self = @This();
238
239pub fn generate(
240 arch: std.Target.Cpu.Arch,
241 bin_file: *link.File,
242 src_loc: Module.SrcLoc,
243 module_fn: *Module.Fn,
244 air: Air,
245 liveness: Liveness,
246 code: *std.ArrayList(u8),
247 debug_output: DebugInfoOutput,
248) GenerateSymbolError!FnResult {
249 if (build_options.skip_non_native and builtin.cpu.arch != arch) {
250 @panic("Attempted to compile for architecture that was disabled by build configuration");
251 }
252
253 assert(module_fn.owner_decl.has_tv);
254 const fn_type = module_fn.owner_decl.ty;
255
256 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator);
257 defer {
258 assert(branch_stack.items.len == 1);
259 branch_stack.items[0].deinit(bin_file.allocator);
260 branch_stack.deinit();
261 }
262 try branch_stack.append(.{});
263
264 var function = Self{
265 .arch = arch,
266 .gpa = bin_file.allocator,
267 .air = air,
268 .liveness = liveness,
269 .target = &bin_file.options.target,
270 .bin_file = bin_file,
271 .mod_fn = module_fn,
272 .code = code,
273 .debug_output = debug_output,
274 .err_msg = null,
275 .args = undefined, // populated after `resolveCallingConventionValues`
276 .ret_mcv = undefined, // populated after `resolveCallingConventionValues`
277 .fn_type = fn_type,
278 .arg_index = 0,
279 .branch_stack = &branch_stack,
280 .src_loc = src_loc,
281 .stack_align = undefined,
282 .prev_di_pc = 0,
283 .prev_di_line = module_fn.lbrace_line,
284 .prev_di_column = module_fn.lbrace_column,
285 .end_di_line = module_fn.rbrace_line,
286 .end_di_column = module_fn.rbrace_column,
287 };
288 defer function.stack.deinit(bin_file.allocator);
289 defer function.blocks.deinit(bin_file.allocator);
290 defer function.exitlude_jump_relocs.deinit(bin_file.allocator);
291
292 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) {
293 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
294 else => |e| return e,
295 };
296 defer call_info.deinit(&function);
297
298 function.args = call_info.args;
299 function.ret_mcv = call_info.return_value;
300 function.stack_align = call_info.stack_align;
301 function.max_end_stack = call_info.stack_byte_count;
302
303 function.gen() catch |err| switch (err) {
304 error.CodegenFail => return FnResult{ .fail = function.err_msg.? },
305 else => |e| return e,
306 };
307
308 if (function.err_msg) |em| {
309 return FnResult{ .fail = em };
310 } else {
311 return FnResult{ .appended = {} };
312 }
313}
314
315fn gen(self: *Self) !void {
316 const cc = self.fn_type.fnCallingConvention();
317 if (cc != .Naked) {
318 // TODO Finish function prologue and epilogue for aarch64.
319
320 // stp fp, lr, [sp, #-16]!
321 // mov fp, sp
322 // sub sp, sp, #reloc
323 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.stp(
324 .x29,
325 .x30,
326 Register.sp,
327 Instruction.LoadStorePairOffset.pre_index(-16),
328 ).toU32());
329 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.add(.x29, .xzr, 0, false).toU32());
330 const backpatch_reloc = self.code.items.len;
331 try self.code.resize(backpatch_reloc + 4);
332
333 try self.dbgSetPrologueEnd();
334
335 try self.genBody(self.air.getMainBody());
336
337 // Backpatch stack offset
338 const stack_end = self.max_end_stack;
339 const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
340 if (math.cast(u12, aligned_stack_end)) |size| {
341 mem.writeIntLittle(u32, self.code.items[backpatch_reloc..][0..4], Instruction.sub(.xzr, .xzr, size, false).toU32());
342 } else |_| {
343 return self.failSymbol("TODO AArch64: allow larger stacks", .{});
344 }
345
346 try self.dbgSetEpilogueBegin();
347
348 // exitlude jumps
349 if (self.exitlude_jump_relocs.items.len == 1) {
350 // There is only one relocation. Hence,
351 // this relocation must be at the end of
352 // the code. Therefore, we can just delete
353 // the space initially reserved for the
354 // jump
355 self.code.items.len -= 4;
356 } else for (self.exitlude_jump_relocs.items) |jmp_reloc| {
357 const amt = @intCast(i32, self.code.items.len) - @intCast(i32, jmp_reloc + 8);
358 if (amt == -4) {
359 // This return is at the end of the
360 // code block. We can't just delete
361 // the space because there may be
362 // other jumps we already relocated to
363 // the address. Instead, insert a nop
364 mem.writeIntLittle(u32, self.code.items[jmp_reloc..][0..4], Instruction.nop().toU32());
365 } else {
366 if (math.cast(i28, amt)) |offset| {
367 mem.writeIntLittle(u32, self.code.items[jmp_reloc..][0..4], Instruction.b(offset).toU32());
368 } else |_| {
369 return self.failSymbol("exitlude jump is too large", .{});
370 }
371 }
372 }
373
374 // ldp fp, lr, [sp], #16
375 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldp(
376 .x29,
377 .x30,
378 Register.sp,
379 Instruction.LoadStorePairOffset.post_index(16),
380 ).toU32());
381 // add sp, sp, #stack_size
382 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.add(.xzr, .xzr, @intCast(u12, aligned_stack_end), false).toU32());
383 // ret lr
384 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ret(null).toU32());
385 } else {
386 try self.dbgSetPrologueEnd();
387 try self.genBody(self.air.getMainBody());
388 try self.dbgSetEpilogueBegin();
389 }
390
391 // Drop them off at the rbrace.
392 try self.dbgAdvancePCAndLine(self.end_di_line, self.end_di_column);
393}
394
395fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
396 const air_tags = self.air.instructions.items(.tag);
397
398 for (body) |inst| {
399 const old_air_bookkeeping = self.air_bookkeeping;
400 try self.ensureProcessDeathCapacity(Liveness.bpi);
401
402 switch (air_tags[inst]) {
403 // zig fmt: off
404 .add, .ptr_add => try self.airAdd(inst),
405 .addwrap => try self.airAddWrap(inst),
406 .add_sat => try self.airAddSat(inst),
407 .sub, .ptr_sub => try self.airSub(inst),
408 .subwrap => try self.airSubWrap(inst),
409 .sub_sat => try self.airSubSat(inst),
410 .mul => try self.airMul(inst),
411 .mulwrap => try self.airMulWrap(inst),
412 .mul_sat => try self.airMulSat(inst),
413 .div => try self.airDiv(inst),
414 .rem => try self.airRem(inst),
415 .mod => try self.airMod(inst),
416 .shl, .shl_exact => try self.airShl(inst),
417 .shl_sat => try self.airShlSat(inst),
418 .min => try self.airMin(inst),
419 .max => try self.airMax(inst),
420
421 .cmp_lt => try self.airCmp(inst, .lt),
422 .cmp_lte => try self.airCmp(inst, .lte),
423 .cmp_eq => try self.airCmp(inst, .eq),
424 .cmp_gte => try self.airCmp(inst, .gte),
425 .cmp_gt => try self.airCmp(inst, .gt),
426 .cmp_neq => try self.airCmp(inst, .neq),
427
428 .bool_and => try self.airBoolOp(inst),
429 .bool_or => try self.airBoolOp(inst),
430 .bit_and => try self.airBitAnd(inst),
431 .bit_or => try self.airBitOr(inst),
432 .xor => try self.airXor(inst),
433 .shr => try self.airShr(inst),
434
435 .alloc => try self.airAlloc(inst),
436 .ret_ptr => try self.airRetPtr(inst),
437 .arg => try self.airArg(inst),
438 .assembly => try self.airAsm(inst),
439 .bitcast => try self.airBitCast(inst),
440 .block => try self.airBlock(inst),
441 .br => try self.airBr(inst),
442 .breakpoint => try self.airBreakpoint(),
443 .fence => try self.airFence(),
444 .call => try self.airCall(inst),
445 .cond_br => try self.airCondBr(inst),
446 .dbg_stmt => try self.airDbgStmt(inst),
447 .fptrunc => try self.airFptrunc(inst),
448 .fpext => try self.airFpext(inst),
449 .intcast => try self.airIntCast(inst),
450 .trunc => try self.airTrunc(inst),
451 .bool_to_int => try self.airBoolToInt(inst),
452 .is_non_null => try self.airIsNonNull(inst),
453 .is_non_null_ptr => try self.airIsNonNullPtr(inst),
454 .is_null => try self.airIsNull(inst),
455 .is_null_ptr => try self.airIsNullPtr(inst),
456 .is_non_err => try self.airIsNonErr(inst),
457 .is_non_err_ptr => try self.airIsNonErrPtr(inst),
458 .is_err => try self.airIsErr(inst),
459 .is_err_ptr => try self.airIsErrPtr(inst),
460 .load => try self.airLoad(inst),
461 .loop => try self.airLoop(inst),
462 .not => try self.airNot(inst),
463 .ptrtoint => try self.airPtrToInt(inst),
464 .ret => try self.airRet(inst),
465 .ret_load => try self.airRetLoad(inst),
466 .store => try self.airStore(inst),
467 .struct_field_ptr=> try self.airStructFieldPtr(inst),
468 .struct_field_val=> try self.airStructFieldVal(inst),
469 .array_to_slice => try self.airArrayToSlice(inst),
470 .int_to_float => try self.airIntToFloat(inst),
471 .float_to_int => try self.airFloatToInt(inst),
472 .cmpxchg_strong => try self.airCmpxchg(inst),
473 .cmpxchg_weak => try self.airCmpxchg(inst),
474 .atomic_rmw => try self.airAtomicRmw(inst),
475 .atomic_load => try self.airAtomicLoad(inst),
476 .memcpy => try self.airMemcpy(inst),
477 .memset => try self.airMemset(inst),
478 .set_union_tag => try self.airSetUnionTag(inst),
479 .get_union_tag => try self.airGetUnionTag(inst),
480 .clz => try self.airClz(inst),
481 .ctz => try self.airCtz(inst),
482
483 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered),
484 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic),
485 .atomic_store_release => try self.airAtomicStore(inst, .Release),
486 .atomic_store_seq_cst => try self.airAtomicStore(inst, .SeqCst),
487
488 .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0),
489 .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1),
490 .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2),
491 .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3),
492
493 .switch_br => try self.airSwitch(inst),
494 .slice_ptr => try self.airSlicePtr(inst),
495 .slice_len => try self.airSliceLen(inst),
496
497 .array_elem_val => try self.airArrayElemVal(inst),
498 .slice_elem_val => try self.airSliceElemVal(inst),
499 .ptr_slice_elem_val => try self.airPtrSliceElemVal(inst),
500 .ptr_elem_val => try self.airPtrElemVal(inst),
501 .ptr_elem_ptr => try self.airPtrElemPtr(inst),
502 .ptr_ptr_elem_val => try self.airPtrPtrElemVal(inst),
503
504 .constant => unreachable, // excluded from function bodies
505 .const_ty => unreachable, // excluded from function bodies
506 .unreach => self.finishAirBookkeeping(),
507
508 .optional_payload => try self.airOptionalPayload(inst),
509 .optional_payload_ptr => try self.airOptionalPayloadPtr(inst),
510 .unwrap_errunion_err => try self.airUnwrapErrErr(inst),
511 .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst),
512 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst),
513 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst),
514
515 .wrap_optional => try self.airWrapOptional(inst),
516 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst),
517 .wrap_errunion_err => try self.airWrapErrUnionErr(inst),
518 // zig fmt: on
519 }
520 if (std.debug.runtime_safety) {
521 if (self.air_bookkeeping < old_air_bookkeeping + 1) {
522 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] });
523 }
524 }
525 }
526}
527
528fn dbgSetPrologueEnd(self: *Self) InnerError!void {
529 switch (self.debug_output) {
530 .dwarf => |dbg_out| {
531 try dbg_out.dbg_line.append(DW.LNS.set_prologue_end);
532 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
533 },
534 .plan9 => {},
535 .none => {},
536 }
537}
538
539fn dbgSetEpilogueBegin(self: *Self) InnerError!void {
540 switch (self.debug_output) {
541 .dwarf => |dbg_out| {
542 try dbg_out.dbg_line.append(DW.LNS.set_epilogue_begin);
543 try self.dbgAdvancePCAndLine(self.prev_di_line, self.prev_di_column);
544 },
545 .plan9 => {},
546 .none => {},
547 }
548}
549
550fn dbgAdvancePCAndLine(self: *Self, line: u32, column: u32) InnerError!void {
551 const delta_line = @intCast(i32, line) - @intCast(i32, self.prev_di_line);
552 const delta_pc: usize = self.code.items.len - self.prev_di_pc;
553 switch (self.debug_output) {
554 .dwarf => |dbg_out| {
555 // TODO Look into using the DWARF special opcodes to compress this data.
556 // It lets you emit single-byte opcodes that add different numbers to
557 // both the PC and the line number at the same time.
558 try dbg_out.dbg_line.ensureUnusedCapacity(11);
559 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_pc);
560 leb128.writeULEB128(dbg_out.dbg_line.writer(), delta_pc) catch unreachable;
561 if (delta_line != 0) {
562 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.advance_line);
563 leb128.writeILEB128(dbg_out.dbg_line.writer(), delta_line) catch unreachable;
564 }
565 dbg_out.dbg_line.appendAssumeCapacity(DW.LNS.copy);
566 self.prev_di_pc = self.code.items.len;
567 self.prev_di_line = line;
568 self.prev_di_column = column;
569 self.prev_di_pc = self.code.items.len;
570 },
571 .plan9 => |dbg_out| {
572 if (delta_pc <= 0) return; // only do this when the pc changes
573 // we have already checked the target in the linker to make sure it is compatable
574 const quant = @import("../../link/Plan9/aout.zig").getPCQuant(self.target.cpu.arch) catch unreachable;
575
576 // increasing the line number
577 try @import("../../link/Plan9.zig").changeLine(dbg_out.dbg_line, delta_line);
578 // increasing the pc
579 const d_pc_p9 = @intCast(i64, delta_pc) - quant;
580 if (d_pc_p9 > 0) {
581 // minus one because if its the last one, we want to leave space to change the line which is one quanta
582 try dbg_out.dbg_line.append(@intCast(u8, @divExact(d_pc_p9, quant) + 128) - quant);
583 if (dbg_out.pcop_change_index.*) |pci|
584 dbg_out.dbg_line.items[pci] += 1;
585 dbg_out.pcop_change_index.* = @intCast(u32, dbg_out.dbg_line.items.len - 1);
586 } else if (d_pc_p9 == 0) {
587 // we don't need to do anything, because adding the quant does it for us
588 } else unreachable;
589 if (dbg_out.start_line.* == null)
590 dbg_out.start_line.* = self.prev_di_line;
591 dbg_out.end_line.* = line;
592 // only do this if the pc changed
593 self.prev_di_line = line;
594 self.prev_di_column = column;
595 self.prev_di_pc = self.code.items.len;
596 },
597 .none => {},
598 }
599}
600
601/// Asserts there is already capacity to insert into top branch inst_table.
602fn processDeath(self: *Self, inst: Air.Inst.Index) void {
603 const air_tags = self.air.instructions.items(.tag);
604 if (air_tags[inst] == .constant) return; // Constants are immortal.
605 // When editing this function, note that the logic must synchronize with `reuseOperand`.
606 const prev_value = self.getResolvedInstValue(inst);
607 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
608 branch.inst_table.putAssumeCapacity(inst, .dead);
609 switch (prev_value) {
610 .register => |reg| {
611 const canon_reg = toCanonicalReg(reg);
612 self.register_manager.freeReg(canon_reg);
613 },
614 else => {}, // TODO process stack allocation death
615 }
616}
617
618/// Called when there are no operands, and the instruction is always unreferenced.
619fn finishAirBookkeeping(self: *Self) void {
620 if (std.debug.runtime_safety) {
621 self.air_bookkeeping += 1;
622 }
623}
624
625fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void {
626 var tomb_bits = self.liveness.getTombBits(inst);
627 for (operands) |op| {
628 const dies = @truncate(u1, tomb_bits) != 0;
629 tomb_bits >>= 1;
630 if (!dies) continue;
631 const op_int = @enumToInt(op);
632 if (op_int < Air.Inst.Ref.typed_value_map.len) continue;
633 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
634 self.processDeath(op_index);
635 }
636 const is_used = @truncate(u1, tomb_bits) == 0;
637 if (is_used) {
638 log.debug("%{d} => {}", .{ inst, result });
639 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
640 branch.inst_table.putAssumeCapacityNoClobber(inst, result);
641
642 switch (result) {
643 .register => |reg| {
644 // In some cases (such as bitcast), an operand
645 // may be the same MCValue as the result. If
646 // that operand died and was a register, it
647 // was freed by processDeath. We have to
648 // "re-allocate" the register.
649 if (self.register_manager.isRegFree(reg)) {
650 self.register_manager.getRegAssumeFree(reg, inst);
651 }
652 },
653 else => {},
654 }
655 }
656 self.finishAirBookkeeping();
657}
658
659fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
660 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table;
661 try table.ensureUnusedCapacity(self.gpa, additional_count);
662}
663
664/// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
665/// after codegen for this symbol is done.
666fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void {
667 switch (self.debug_output) {
668 .dwarf => |dbg_out| {
669 assert(ty.hasCodeGenBits());
670 const index = dbg_out.dbg_info.items.len;
671 try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4
672
673 const gop = try dbg_out.dbg_info_type_relocs.getOrPut(self.gpa, ty);
674 if (!gop.found_existing) {
675 gop.value_ptr.* = .{
676 .off = undefined,
677 .relocs = .{},
678 };
679 }
680 try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index));
681 },
682 .plan9 => {},
683 .none => {},
684 }
685}
686
687fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {
688 if (abi_align > self.stack_align)
689 self.stack_align = abi_align;
690 // TODO find a free slot instead of always appending
691 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align);
692 self.next_stack_offset = offset + abi_size;
693 if (self.next_stack_offset > self.max_end_stack)
694 self.max_end_stack = self.next_stack_offset;
695 try self.stack.putNoClobber(self.gpa, offset, .{
696 .inst = inst,
697 .size = abi_size,
698 });
699 return offset;
700}
701
702/// Use a pointer instruction as the basis for allocating stack memory.
703fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
704 const elem_ty = self.air.typeOfIndex(inst).elemType();
705 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
706 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
707 };
708 // TODO swap this for inst.ty.ptrAlign
709 const abi_align = elem_ty.abiAlignment(self.target.*);
710 return self.allocMem(inst, abi_size, abi_align);
711}
712
713fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
714 const elem_ty = self.air.typeOfIndex(inst);
715 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch {
716 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty});
717 };
718 const abi_align = elem_ty.abiAlignment(self.target.*);
719 if (abi_align > self.stack_align)
720 self.stack_align = abi_align;
721
722 if (reg_ok) {
723 // Make sure the type can fit in a register before we try to allocate one.
724 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
725 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
726 if (abi_size <= ptr_bytes) {
727 if (self.register_manager.tryAllocReg(inst, &.{})) |reg| {
728 return MCValue{ .register = registerAlias(reg, abi_size) };
729 }
730 }
731 }
732 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
733 return MCValue{ .stack_offset = stack_offset };
734}
735
736pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
737 const stack_mcv = try self.allocRegOrMem(inst, false);
738 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv });
739 const reg_mcv = self.getResolvedInstValue(inst);
740 assert(reg == toCanonicalReg(reg_mcv.register));
741 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
742 try branch.inst_table.put(self.gpa, inst, stack_mcv);
743 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv);
744}
745
746/// Copies a value to a register without tracking the register. The register is not considered
747/// allocated. A second call to `copyToTmpRegister` may return the same register.
748/// This can have a side effect of spilling instructions to the stack to free up a register.
749fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
750 const reg = try self.register_manager.allocReg(null, &.{});
751 try self.genSetReg(ty, reg, mcv);
752 return reg;
753}
754
755/// Allocates a new register and copies `mcv` into it.
756/// `reg_owner` is the instruction that gets associated with the register in the register table.
757/// This can have a side effect of spilling instructions to the stack to free up a register.
758fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue {
759 const reg = try self.register_manager.allocReg(reg_owner, &.{});
760 try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv);
761 return MCValue{ .register = reg };
762}
763
764fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
765 const stack_offset = try self.allocMemPtr(inst);
766 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
767}
768
769fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
770 const stack_offset = try self.allocMemPtr(inst);
771 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
772}
773
774fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void {
775 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
776 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch});
777 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
778}
779
780fn airFpext(self: *Self, inst: Air.Inst.Index) !void {
781 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
782 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch});
783 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
784}
785
786fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
787 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
788 if (self.liveness.isUnused(inst))
789 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
790
791 const operand_ty = self.air.typeOf(ty_op.operand);
792 const operand = try self.resolveInst(ty_op.operand);
793 const info_a = operand_ty.intInfo(self.target.*);
794 const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*);
795 if (info_a.signedness != info_b.signedness)
796 return self.fail("TODO gen intcast sign safety in semantic analysis", .{});
797
798 if (info_a.bits == info_b.bits)
799 return self.finishAir(inst, operand, .{ ty_op.operand, .none, .none });
800
801 return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch});
802}
803
804fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
805 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
806 if (self.liveness.isUnused(inst))
807 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
808
809 const operand = try self.resolveInst(ty_op.operand);
810 _ = operand;
811 return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch});
812}
813
814fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
815 const un_op = self.air.instructions.items(.data)[inst].un_op;
816 const operand = try self.resolveInst(un_op);
817 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand;
818 return self.finishAir(inst, result, .{ un_op, .none, .none });
819}
820
821fn airNot(self: *Self, inst: Air.Inst.Index) !void {
822 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
823 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
824 const operand = try self.resolveInst(ty_op.operand);
825 switch (operand) {
826 .dead => unreachable,
827 .unreach => unreachable,
828 .compare_flags_unsigned => |op| {
829 const r = MCValue{
830 .compare_flags_unsigned = switch (op) {
831 .gte => .lt,
832 .gt => .lte,
833 .neq => .eq,
834 .lt => .gte,
835 .lte => .gt,
836 .eq => .neq,
837 },
838 };
839 break :result r;
840 },
841 .compare_flags_signed => |op| {
842 const r = MCValue{
843 .compare_flags_signed = switch (op) {
844 .gte => .lt,
845 .gt => .lte,
846 .neq => .eq,
847 .lt => .gte,
848 .lte => .gt,
849 .eq => .neq,
850 },
851 };
852 break :result r;
853 },
854 else => {},
855 }
856
857 return self.fail("TODO implement NOT for {}", .{self.target.cpu.arch});
858 };
859 _ = result;
860}
861
862fn airMin(self: *Self, inst: Air.Inst.Index) !void {
863 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
864 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement min for {}", .{self.target.cpu.arch});
865 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
866}
867
868fn airMax(self: *Self, inst: Air.Inst.Index) !void {
869 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
870 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement max for {}", .{self.target.cpu.arch});
871 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
872}
873
874fn airAdd(self: *Self, inst: Air.Inst.Index) !void {
875 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
876 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add for {}", .{self.target.cpu.arch});
877 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
878}
879
880fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void {
881 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
882 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch});
883 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
884}
885
886fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
887 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
888 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});
889 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
890}
891
892fn airSub(self: *Self, inst: Air.Inst.Index) !void {
893 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
894 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub for {}", .{self.target.cpu.arch});
895 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
896}
897
898fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void {
899 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
900 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch});
901 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
902}
903
904fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
905 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
906 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});
907 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
908}
909
910fn airMul(self: *Self, inst: Air.Inst.Index) !void {
911 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
912 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul for {}", .{self.target.cpu.arch});
913 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
914}
915
916fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void {
917 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
918 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch});
919 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
920}
921
922fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
923 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
924 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});
925 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
926}
927
928fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
929 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
930 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch});
931 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
932}
933
934fn airRem(self: *Self, inst: Air.Inst.Index) !void {
935 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
936 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement rem for {}", .{self.target.cpu.arch});
937 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
938}
939
940fn airMod(self: *Self, inst: Air.Inst.Index) !void {
941 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
942 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mod for {}", .{self.target.cpu.arch});
943 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
944}
945
946fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void {
947 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
948 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement bitwise and for {}", .{self.target.cpu.arch});
949 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
950}
951
952fn airBitOr(self: *Self, inst: Air.Inst.Index) !void {
953 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
954 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement bitwise or for {}", .{self.target.cpu.arch});
955 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
956}
957
958fn airXor(self: *Self, inst: Air.Inst.Index) !void {
959 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
960 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement xor for {}", .{self.target.cpu.arch});
961 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
962}
963
964fn airShl(self: *Self, inst: Air.Inst.Index) !void {
965 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
966 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl for {}", .{self.target.cpu.arch});
967 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
968}
969
970fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
971 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
972 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
973 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
974}
975
976fn airShr(self: *Self, inst: Air.Inst.Index) !void {
977 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
978 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shr for {}", .{self.target.cpu.arch});
979 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
980}
981
982fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void {
983 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
984 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch});
985 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
986}
987
988fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
989 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
990 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch});
991 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
992}
993
994fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
995 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
996 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union error for {}", .{self.target.cpu.arch});
997 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
998}
999
1000fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
1001 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1002 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch});
1003 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1004}
1005
1006// *(E!T) -> E
1007fn airUnwrapErrErrPtr(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 unwrap error union error ptr for {}", .{self.target.cpu.arch});
1010 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1011}
1012
1013// *(E!T) -> *T
1014fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void {
1015 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1016 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});
1017 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1018}
1019
1020fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
1021 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1022 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1023 const optional_ty = self.air.typeOfIndex(inst);
1024
1025 // Optional with a zero-bit payload type is just a boolean true
1026 if (optional_ty.abiSize(self.target.*) == 1)
1027 break :result MCValue{ .immediate = 1 };
1028
1029 return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch});
1030 };
1031 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1032}
1033
1034/// T to E!T
1035fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
1036 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1037 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch});
1038 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1039}
1040
1041/// E to E!T
1042fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
1043 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1044 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement wrap errunion error for {}", .{self.target.cpu.arch});
1045 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1046}
1047
1048fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void {
1049 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1050 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_ptr for {}", .{self.target.cpu.arch});
1051 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1052}
1053
1054fn airSliceLen(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 slice_len for {}", .{self.target.cpu.arch});
1057 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1058}
1059
1060fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
1061 const is_volatile = false; // TODO
1062 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1063 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});
1064 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1065}
1066
1067fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
1068 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1069 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch});
1070 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1071}
1072
1073fn airPtrSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
1074 const is_volatile = false; // TODO
1075 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1076 const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_slice_elem_val for {}", .{self.target.cpu.arch});
1077 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1078}
1079
1080fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
1081 const is_volatile = false; // TODO
1082 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1083 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});
1084 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1085}
1086
1087fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
1088 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1089 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
1090 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_ptr for {}", .{self.target.cpu.arch});
1091 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
1092}
1093
1094fn airPtrPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
1095 const is_volatile = false; // TODO
1096 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1097 const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_ptr_elem_val for {}", .{self.target.cpu.arch});
1098 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1099}
1100
1101fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
1102 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1103 _ = bin_op;
1104 return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch});
1105}
1106
1107fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void {
1108 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1109 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch});
1110 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1111}
1112
1113fn airClz(self: *Self, inst: Air.Inst.Index) !void {
1114 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1115 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch});
1116 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1117}
1118
1119fn airCtz(self: *Self, inst: Air.Inst.Index) !void {
1120 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1121 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch});
1122 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1123}
1124
1125fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {
1126 if (!self.liveness.operandDies(inst, op_index))
1127 return false;
1128
1129 switch (mcv) {
1130 .register => |reg| {
1131 // If it's in the registers table, need to associate the register with the
1132 // new instruction.
1133 if (reg.allocIndex()) |index| {
1134 if (!self.register_manager.isRegFree(reg)) {
1135 self.register_manager.registers[index] = inst;
1136 }
1137 }
1138 log.debug("%{d} => {} (reused)", .{ inst, reg });
1139 },
1140 .stack_offset => |off| {
1141 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off });
1142 },
1143 else => return false,
1144 }
1145
1146 // Prevent the operand deaths processing code from deallocating it.
1147 self.liveness.clearOperandDeath(inst, op_index);
1148
1149 // That makes us responsible for doing the rest of the stuff that processDeath would have done.
1150 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1151 branch.inst_table.putAssumeCapacity(Air.refToIndex(operand).?, .dead);
1152
1153 return true;
1154}
1155
1156fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void {
1157 const elem_ty = ptr_ty.elemType();
1158 switch (ptr) {
1159 .none => unreachable,
1160 .undef => unreachable,
1161 .unreach => unreachable,
1162 .dead => unreachable,
1163 .compare_flags_unsigned => unreachable,
1164 .compare_flags_signed => unreachable,
1165 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
1166 .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }),
1167 .ptr_embedded_in_code => |off| {
1168 try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off });
1169 },
1170 .embedded_in_code => {
1171 return self.fail("TODO implement loading from MCValue.embedded_in_code", .{});
1172 },
1173 .register => {
1174 return self.fail("TODO implement loading from MCValue.register for {}", .{self.target.cpu.arch});
1175 },
1176 .memory => |addr| {
1177 const reg = try self.register_manager.allocReg(null, &.{});
1178 try self.genSetReg(ptr_ty, reg, .{ .memory = addr });
1179 try self.load(dst_mcv, .{ .register = reg }, ptr_ty);
1180 },
1181 .stack_offset => {
1182 return self.fail("TODO implement loading from MCValue.stack_offset", .{});
1183 },
1184 }
1185}
1186
1187fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
1188 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1189 const elem_ty = self.air.typeOfIndex(inst);
1190 const result: MCValue = result: {
1191 if (!elem_ty.hasCodeGenBits())
1192 break :result MCValue.none;
1193
1194 const ptr = try self.resolveInst(ty_op.operand);
1195 const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr();
1196 if (self.liveness.isUnused(inst) and !is_volatile)
1197 break :result MCValue.dead;
1198
1199 const dst_mcv: MCValue = blk: {
1200 if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
1201 // The MCValue that holds the pointer can be re-used as the value.
1202 break :blk ptr;
1203 } else {
1204 break :blk try self.allocRegOrMem(inst, true);
1205 }
1206 };
1207 try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand));
1208 break :result dst_mcv;
1209 };
1210 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1211}
1212
1213fn airStore(self: *Self, inst: Air.Inst.Index) !void {
1214 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1215 const ptr = try self.resolveInst(bin_op.lhs);
1216 const value = try self.resolveInst(bin_op.rhs);
1217 const elem_ty = self.air.typeOf(bin_op.rhs);
1218 switch (ptr) {
1219 .none => unreachable,
1220 .undef => unreachable,
1221 .unreach => unreachable,
1222 .dead => unreachable,
1223 .compare_flags_unsigned => unreachable,
1224 .compare_flags_signed => unreachable,
1225 .immediate => |imm| {
1226 try self.setRegOrMem(elem_ty, .{ .memory = imm }, value);
1227 },
1228 .ptr_stack_offset => |off| {
1229 try self.genSetStack(elem_ty, off, value);
1230 },
1231 .ptr_embedded_in_code => |off| {
1232 try self.setRegOrMem(elem_ty, .{ .embedded_in_code = off }, value);
1233 },
1234 .embedded_in_code => {
1235 return self.fail("TODO implement storing to MCValue.embedded_in_code", .{});
1236 },
1237 .register => {
1238 return self.fail("TODO implement storing to MCValue.register", .{});
1239 },
1240 .memory => {
1241 return self.fail("TODO implement storing to MCValue.memory", .{});
1242 },
1243 .stack_offset => {
1244 return self.fail("TODO implement storing to MCValue.stack_offset", .{});
1245 },
1246 }
1247 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
1248}
1249
1250fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void {
1251 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1252 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
1253 return self.structFieldPtr(extra.struct_operand, ty_pl.ty, extra.field_index);
1254}
1255
1256fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void {
1257 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1258 return self.structFieldPtr(ty_op.operand, ty_op.ty, index);
1259}
1260fn structFieldPtr(self: *Self, operand: Air.Inst.Ref, ty: Air.Inst.Ref, index: u32) !void {
1261 _ = self;
1262 _ = operand;
1263 _ = ty;
1264 _ = index;
1265 return self.fail("TODO implement codegen struct_field_ptr", .{});
1266 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
1267}
1268
1269fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
1270 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1271 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data;
1272 _ = extra;
1273 return self.fail("TODO implement codegen struct_field_val", .{});
1274 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none });
1275}
1276
1277fn armOperandShouldBeRegister(self: *Self, mcv: MCValue) !bool {
1278 return switch (mcv) {
1279 .none => unreachable,
1280 .undef => unreachable,
1281 .dead, .unreach => unreachable,
1282 .compare_flags_unsigned => unreachable,
1283 .compare_flags_signed => unreachable,
1284 .ptr_stack_offset => unreachable,
1285 .ptr_embedded_in_code => unreachable,
1286 .immediate => |imm| blk: {
1287 if (imm > std.math.maxInt(u32)) return self.fail("TODO ARM binary arithmetic immediate larger than u32", .{});
1288
1289 // Load immediate into register if it doesn't fit
1290 // in an operand
1291 break :blk Instruction.Operand.fromU32(@intCast(u32, imm)) == null;
1292 },
1293 .register => true,
1294 .stack_offset,
1295 .embedded_in_code,
1296 .memory,
1297 => true,
1298 };
1299}
1300
1301fn genArmBinOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref, op: Air.Inst.Tag) !MCValue {
1302 // In the case of bitshifts, the type of rhs is different
1303 // from the resulting type
1304 const ty = self.air.typeOf(op_lhs);
1305
1306 switch (ty.zigTypeTag()) {
1307 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
1308 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
1309 .Bool => {
1310 return self.genArmBinIntOp(inst, op_lhs, op_rhs, op, 1, .unsigned);
1311 },
1312 .Int => {
1313 const int_info = ty.intInfo(self.target.*);
1314 return self.genArmBinIntOp(inst, op_lhs, op_rhs, op, int_info.bits, int_info.signedness);
1315 },
1316 else => unreachable,
1317 }
1318}
1319
1320fn genArmBinIntOp(
1321 self: *Self,
1322 inst: Air.Inst.Index,
1323 op_lhs: Air.Inst.Ref,
1324 op_rhs: Air.Inst.Ref,
1325 op: Air.Inst.Tag,
1326 bits: u16,
1327 signedness: std.builtin.Signedness,
1328) !MCValue {
1329 if (bits > 32) {
1330 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
1331 }
1332
1333 const lhs = try self.resolveInst(op_lhs);
1334 const rhs = try self.resolveInst(op_rhs);
1335
1336 const lhs_is_register = lhs == .register;
1337 const rhs_is_register = rhs == .register;
1338 const lhs_should_be_register = switch (op) {
1339 .shr, .shl => true,
1340 else => try self.armOperandShouldBeRegister(lhs),
1341 };
1342 const rhs_should_be_register = try self.armOperandShouldBeRegister(rhs);
1343 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);
1344 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs);
1345 const can_swap_lhs_and_rhs = switch (op) {
1346 .shr, .shl => false,
1347 else => true,
1348 };
1349
1350 // Destination must be a register
1351 var dst_mcv: MCValue = undefined;
1352 var lhs_mcv = lhs;
1353 var rhs_mcv = rhs;
1354 var swap_lhs_and_rhs = false;
1355
1356 // Allocate registers for operands and/or destination
1357 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1358 if (reuse_lhs) {
1359 // Allocate 0 or 1 registers
1360 if (!rhs_is_register and rhs_should_be_register) {
1361 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?, &.{lhs.register}) };
1362 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
1363 }
1364 dst_mcv = lhs;
1365 } else if (reuse_rhs and can_swap_lhs_and_rhs) {
1366 // Allocate 0 or 1 registers
1367 if (!lhs_is_register and lhs_should_be_register) {
1368 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?, &.{rhs.register}) };
1369 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv);
1370 }
1371 dst_mcv = rhs;
1372
1373 swap_lhs_and_rhs = true;
1374 } else {
1375 // Allocate 1 or 2 registers
1376 if (lhs_should_be_register and rhs_should_be_register) {
1377 if (lhs_is_register and rhs_is_register) {
1378 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{ lhs.register, rhs.register }) };
1379 } else if (lhs_is_register) {
1380 // Move RHS to register
1381 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{lhs.register}) };
1382 rhs_mcv = dst_mcv;
1383 } else if (rhs_is_register) {
1384 // Move LHS to register
1385 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{rhs.register}) };
1386 lhs_mcv = dst_mcv;
1387 } else {
1388 // Move LHS and RHS to register
1389 const regs = try self.register_manager.allocRegs(2, .{ inst, Air.refToIndex(op_rhs).? }, &.{});
1390 lhs_mcv = MCValue{ .register = regs[0] };
1391 rhs_mcv = MCValue{ .register = regs[1] };
1392 dst_mcv = lhs_mcv;
1393
1394 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
1395 }
1396 } else if (lhs_should_be_register) {
1397 // RHS is immediate
1398 if (lhs_is_register) {
1399 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{lhs.register}) };
1400 } else {
1401 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{}) };
1402 lhs_mcv = dst_mcv;
1403 }
1404 } else if (rhs_should_be_register and can_swap_lhs_and_rhs) {
1405 // LHS is immediate
1406 if (rhs_is_register) {
1407 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{rhs.register}) };
1408 } else {
1409 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{}) };
1410 rhs_mcv = dst_mcv;
1411 }
1412
1413 swap_lhs_and_rhs = true;
1414 } else unreachable; // binary operation on two immediates
1415 }
1416
1417 // Move the operands to the newly allocated registers
1418 if (lhs_mcv == .register and !lhs_is_register) {
1419 try self.genSetReg(self.air.typeOf(op_lhs), lhs_mcv.register, lhs);
1420 }
1421 if (rhs_mcv == .register and !rhs_is_register) {
1422 try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs);
1423 }
1424
1425 try self.genArmBinOpCode(
1426 dst_mcv.register,
1427 lhs_mcv,
1428 rhs_mcv,
1429 swap_lhs_and_rhs,
1430 op,
1431 signedness,
1432 );
1433 return dst_mcv;
1434}
1435
1436fn genArmBinOpCode(
1437 self: *Self,
1438 dst_reg: Register,
1439 lhs_mcv: MCValue,
1440 rhs_mcv: MCValue,
1441 swap_lhs_and_rhs: bool,
1442 op: Air.Inst.Tag,
1443 signedness: std.builtin.Signedness,
1444) !void {
1445 assert(lhs_mcv == .register or rhs_mcv == .register);
1446
1447 const op1 = if (swap_lhs_and_rhs) rhs_mcv.register else lhs_mcv.register;
1448 const op2 = if (swap_lhs_and_rhs) lhs_mcv else rhs_mcv;
1449
1450 const operand = switch (op2) {
1451 .none => unreachable,
1452 .undef => unreachable,
1453 .dead, .unreach => unreachable,
1454 .compare_flags_unsigned => unreachable,
1455 .compare_flags_signed => unreachable,
1456 .ptr_stack_offset => unreachable,
1457 .ptr_embedded_in_code => unreachable,
1458 .immediate => |imm| Instruction.Operand.fromU32(@intCast(u32, imm)).?,
1459 .register => |reg| Instruction.Operand.reg(reg, Instruction.Operand.Shift.none),
1460 .stack_offset,
1461 .embedded_in_code,
1462 .memory,
1463 => unreachable,
1464 };
1465
1466 switch (op) {
1467 .add => {
1468 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.add(.al, dst_reg, op1, operand).toU32());
1469 },
1470 .sub => {
1471 if (swap_lhs_and_rhs) {
1472 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.rsb(.al, dst_reg, op1, operand).toU32());
1473 } else {
1474 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.sub(.al, dst_reg, op1, operand).toU32());
1475 }
1476 },
1477 .bool_and, .bit_and => {
1478 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.@"and"(.al, dst_reg, op1, operand).toU32());
1479 },
1480 .bool_or, .bit_or => {
1481 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(.al, dst_reg, op1, operand).toU32());
1482 },
1483 .not, .xor => {
1484 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.eor(.al, dst_reg, op1, operand).toU32());
1485 },
1486 .cmp_eq => {
1487 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.cmp(.al, op1, operand).toU32());
1488 },
1489 .shl => {
1490 assert(!swap_lhs_and_rhs);
1491 const shift_amount = switch (operand) {
1492 .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),
1493 .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),
1494 };
1495 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.lsl(.al, dst_reg, op1, shift_amount).toU32());
1496 },
1497 .shr => {
1498 assert(!swap_lhs_and_rhs);
1499 const shift_amount = switch (operand) {
1500 .Register => |reg_op| Instruction.ShiftAmount.reg(@intToEnum(Register, reg_op.rm)),
1501 .Immediate => |imm_op| Instruction.ShiftAmount.imm(@intCast(u5, imm_op.imm)),
1502 };
1503
1504 const shr = switch (signedness) {
1505 .signed => Instruction.asr,
1506 .unsigned => Instruction.lsr,
1507 };
1508 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), shr(.al, dst_reg, op1, shift_amount).toU32());
1509 },
1510 else => unreachable, // not a binary instruction
1511 }
1512}
1513
1514fn genArmMul(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {
1515 const lhs = try self.resolveInst(op_lhs);
1516 const rhs = try self.resolveInst(op_rhs);
1517
1518 const lhs_is_register = lhs == .register;
1519 const rhs_is_register = rhs == .register;
1520 const reuse_lhs = lhs_is_register and self.reuseOperand(inst, op_lhs, 0, lhs);
1521 const reuse_rhs = !reuse_lhs and rhs_is_register and self.reuseOperand(inst, op_rhs, 1, rhs);
1522
1523 // Destination must be a register
1524 // LHS must be a register
1525 // RHS must be a register
1526 var dst_mcv: MCValue = undefined;
1527 var lhs_mcv: MCValue = lhs;
1528 var rhs_mcv: MCValue = rhs;
1529
1530 // Allocate registers for operands and/or destination
1531 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
1532 if (reuse_lhs) {
1533 // Allocate 0 or 1 registers
1534 if (!rhs_is_register) {
1535 rhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_rhs).?, &.{lhs.register}) };
1536 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
1537 }
1538 dst_mcv = lhs;
1539 } else if (reuse_rhs) {
1540 // Allocate 0 or 1 registers
1541 if (!lhs_is_register) {
1542 lhs_mcv = MCValue{ .register = try self.register_manager.allocReg(Air.refToIndex(op_lhs).?, &.{rhs.register}) };
1543 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_lhs).?, lhs_mcv);
1544 }
1545 dst_mcv = rhs;
1546 } else {
1547 // Allocate 1 or 2 registers
1548 if (lhs_is_register and rhs_is_register) {
1549 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{ lhs.register, rhs.register }) };
1550 } else if (lhs_is_register) {
1551 // Move RHS to register
1552 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{lhs.register}) };
1553 rhs_mcv = dst_mcv;
1554 } else if (rhs_is_register) {
1555 // Move LHS to register
1556 dst_mcv = MCValue{ .register = try self.register_manager.allocReg(inst, &.{rhs.register}) };
1557 lhs_mcv = dst_mcv;
1558 } else {
1559 // Move LHS and RHS to register
1560 const regs = try self.register_manager.allocRegs(2, .{ inst, Air.refToIndex(op_rhs).? }, &.{});
1561 lhs_mcv = MCValue{ .register = regs[0] };
1562 rhs_mcv = MCValue{ .register = regs[1] };
1563 dst_mcv = lhs_mcv;
1564
1565 branch.inst_table.putAssumeCapacity(Air.refToIndex(op_rhs).?, rhs_mcv);
1566 }
1567 }
1568
1569 // Move the operands to the newly allocated registers
1570 if (!lhs_is_register) {
1571 try self.genSetReg(self.air.typeOf(op_lhs), lhs_mcv.register, lhs);
1572 }
1573 if (!rhs_is_register) {
1574 try self.genSetReg(self.air.typeOf(op_rhs), rhs_mcv.register, rhs);
1575 }
1576
1577 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.mul(.al, dst_mcv.register, lhs_mcv.register, rhs_mcv.register).toU32());
1578 return dst_mcv;
1579}
1580
1581fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue) !void {
1582 const ty_str = self.air.instructions.items(.data)[inst].ty_str;
1583 const zir = &self.mod_fn.owner_decl.getFileScope().zir;
1584 const name = zir.nullTerminatedString(ty_str.str);
1585 const name_with_null = name.ptr[0 .. name.len + 1];
1586 const ty = self.air.getRefType(ty_str.ty);
1587
1588 switch (mcv) {
1589 .register => |reg| {
1590 switch (self.debug_output) {
1591 .dwarf => |dbg_out| {
1592 try dbg_out.dbg_info.ensureUnusedCapacity(3);
1593 dbg_out.dbg_info.appendAssumeCapacity(link.File.Elf.abbrev_parameter);
1594 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc
1595 1, // ULEB128 dwarf expression length
1596 reg.dwarfLocOp(),
1597 });
1598 try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len);
1599 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4
1600 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string
1601 },
1602 .plan9 => {},
1603 .none => {},
1604 }
1605 },
1606 .stack_offset => {},
1607 else => {},
1608 }
1609}
1610
1611fn airArg(self: *Self, inst: Air.Inst.Index) !void {
1612 const arg_index = self.arg_index;
1613 self.arg_index += 1;
1614
1615 const ty = self.air.typeOfIndex(inst);
1616
1617 const result = self.args[arg_index];
1618 const mcv = switch (result) {
1619 // Copy registers to the stack
1620 .register => |reg| blk: {
1621 const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch {
1622 return self.fail("type '{}' too big to fit into stack frame", .{ty});
1623 };
1624 const abi_align = ty.abiAlignment(self.target.*);
1625 const stack_offset = try self.allocMem(inst, abi_size, abi_align);
1626 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
1627
1628 break :blk MCValue{ .stack_offset = stack_offset };
1629 },
1630 else => result,
1631 };
1632 try self.genArgDbgInfo(inst, mcv);
1633
1634 if (self.liveness.isUnused(inst))
1635 return self.finishAirBookkeeping();
1636
1637 switch (mcv) {
1638 .register => |reg| {
1639 self.register_manager.getRegAssumeFree(toCanonicalReg(reg), inst);
1640 },
1641 else => {},
1642 }
1643
1644 return self.finishAir(inst, mcv, .{ .none, .none, .none });
1645}
1646
1647fn airBreakpoint(self: *Self) !void {
1648 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.brk(1).toU32());
1649 return self.finishAirBookkeeping();
1650}
1651
1652fn airFence(self: *Self) !void {
1653 return self.fail("TODO implement fence() for {}", .{self.target.cpu.arch});
1654 //return self.finishAirBookkeeping();
1655}
1656
1657fn airCall(self: *Self, inst: Air.Inst.Index) !void {
1658 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1659 const fn_ty = self.air.typeOf(pl_op.operand);
1660 const callee = pl_op.operand;
1661 const extra = self.air.extraData(Air.Call, pl_op.payload);
1662 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]);
1663
1664 var info = try self.resolveCallingConventionValues(fn_ty);
1665 defer info.deinit(self);
1666
1667 // Due to incremental compilation, how function calls are generated depends
1668 // on linking.
1669 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) {
1670 for (info.args) |mc_arg, arg_i| {
1671 const arg = args[arg_i];
1672 const arg_ty = self.air.typeOf(arg);
1673 const arg_mcv = try self.resolveInst(args[arg_i]);
1674
1675 switch (mc_arg) {
1676 .none => continue,
1677 .undef => unreachable,
1678 .immediate => unreachable,
1679 .unreach => unreachable,
1680 .dead => unreachable,
1681 .embedded_in_code => unreachable,
1682 .memory => unreachable,
1683 .compare_flags_signed => unreachable,
1684 .compare_flags_unsigned => unreachable,
1685 .register => |reg| {
1686 try self.register_manager.getReg(reg, null);
1687 try self.genSetReg(arg_ty, reg, arg_mcv);
1688 },
1689 .stack_offset => {
1690 return self.fail("TODO implement calling with parameters in memory", .{});
1691 },
1692 .ptr_stack_offset => {
1693 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
1694 },
1695 .ptr_embedded_in_code => {
1696 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
1697 },
1698 }
1699 }
1700
1701 if (self.air.value(callee)) |func_value| {
1702 if (func_value.castTag(.function)) |func_payload| {
1703 const func = func_payload.data;
1704 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1705 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1706 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
1707 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
1708 break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
1709 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
1710 coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
1711 else
1712 unreachable;
1713
1714 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });
1715
1716 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
1717 } else if (func_value.castTag(.extern_fn)) |_| {
1718 return self.fail("TODO implement calling extern functions", .{});
1719 } else {
1720 return self.fail("TODO implement calling bitcasted functions", .{});
1721 }
1722 } else {
1723 return self.fail("TODO implement calling runtime known function pointer", .{});
1724 }
1725 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
1726 for (info.args) |mc_arg, arg_i| {
1727 const arg = args[arg_i];
1728 const arg_ty = self.air.typeOf(arg);
1729 const arg_mcv = try self.resolveInst(args[arg_i]);
1730 // Here we do not use setRegOrMem even though the logic is similar, because
1731 // the function call will move the stack pointer, so the offsets are different.
1732 switch (mc_arg) {
1733 .none => continue,
1734 .register => |reg| {
1735 try self.register_manager.getReg(reg, null);
1736 try self.genSetReg(arg_ty, reg, arg_mcv);
1737 },
1738 .stack_offset => {
1739 // Here we need to emit instructions like this:
1740 // mov qword ptr [rsp + stack_offset], x
1741 return self.fail("TODO implement calling with parameters in memory", .{});
1742 },
1743 .ptr_stack_offset => {
1744 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
1745 },
1746 .ptr_embedded_in_code => {
1747 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
1748 },
1749 .undef => unreachable,
1750 .immediate => unreachable,
1751 .unreach => unreachable,
1752 .dead => unreachable,
1753 .embedded_in_code => unreachable,
1754 .memory => unreachable,
1755 .compare_flags_signed => unreachable,
1756 .compare_flags_unsigned => unreachable,
1757 }
1758 }
1759
1760 if (self.air.value(callee)) |func_value| {
1761 if (func_value.castTag(.function)) |func_payload| {
1762 const func = func_payload.data;
1763 // TODO I'm hacking my way through here by repurposing .memory for storing
1764 // index to the GOT target symbol index.
1765 try self.genSetReg(Type.initTag(.u64), .x30, .{
1766 .memory = func.owner_decl.link.macho.local_sym_index,
1767 });
1768 // blr x30
1769 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
1770 } else if (func_value.castTag(.extern_fn)) |func_payload| {
1771 const decl = func_payload.data;
1772 const n_strx = try macho_file.addExternFn(mem.spanZ(decl.name));
1773 const offset = blk: {
1774 const offset = @intCast(u32, self.code.items.len);
1775 // bl
1776 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32());
1777 break :blk offset;
1778 };
1779 // Add relocation to the decl.
1780 try macho_file.active_decl.?.link.macho.relocs.append(self.bin_file.allocator, .{
1781 .offset = offset,
1782 .target = .{ .global = n_strx },
1783 .addend = 0,
1784 .subtractor = null,
1785 .pcrel = true,
1786 .length = 2,
1787 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
1788 });
1789 } else {
1790 return self.fail("TODO implement calling bitcasted functions", .{});
1791 }
1792 } else {
1793 return self.fail("TODO implement calling runtime known function pointer", .{});
1794 }
1795 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
1796 for (info.args) |mc_arg, arg_i| {
1797 const arg = args[arg_i];
1798 const arg_ty = self.air.typeOf(arg);
1799 const arg_mcv = try self.resolveInst(args[arg_i]);
1800
1801 switch (mc_arg) {
1802 .none => continue,
1803 .undef => unreachable,
1804 .immediate => unreachable,
1805 .unreach => unreachable,
1806 .dead => unreachable,
1807 .embedded_in_code => unreachable,
1808 .memory => unreachable,
1809 .compare_flags_signed => unreachable,
1810 .compare_flags_unsigned => unreachable,
1811 .register => |reg| {
1812 try self.register_manager.getReg(reg, null);
1813 try self.genSetReg(arg_ty, reg, arg_mcv);
1814 },
1815 .stack_offset => {
1816 return self.fail("TODO implement calling with parameters in memory", .{});
1817 },
1818 .ptr_stack_offset => {
1819 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
1820 },
1821 .ptr_embedded_in_code => {
1822 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
1823 },
1824 }
1825 }
1826 if (self.air.value(callee)) |func_value| {
1827 if (func_value.castTag(.function)) |func_payload| {
1828 try p9.seeDecl(func_payload.data.owner_decl);
1829 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
1830 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
1831 const got_addr = p9.bases.data;
1832 const got_index = func_payload.data.owner_decl.link.plan9.got_index.?;
1833 const fn_got_addr = got_addr + got_index * ptr_bytes;
1834
1835 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });
1836
1837 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
1838 } else if (func_value.castTag(.extern_fn)) |_| {
1839 return self.fail("TODO implement calling extern functions", .{});
1840 } else {
1841 return self.fail("TODO implement calling bitcasted functions", .{});
1842 }
1843 } else {
1844 return self.fail("TODO implement calling runtime known function pointer", .{});
1845 }
1846 } else unreachable;
1847
1848 const result: MCValue = result: {
1849 switch (info.return_value) {
1850 .register => |reg| {
1851 if (Register.allocIndex(reg) == null) {
1852 // Save function return value in a callee saved register
1853 break :result try self.copyToNewRegister(inst, info.return_value);
1854 }
1855 },
1856 else => {},
1857 }
1858 break :result info.return_value;
1859 };
1860
1861 if (args.len <= Liveness.bpi - 2) {
1862 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
1863 buf[0] = callee;
1864 std.mem.copy(Air.Inst.Ref, buf[1..], args);
1865 return self.finishAir(inst, result, buf);
1866 }
1867 var bt = try self.iterateBigTomb(inst, 1 + args.len);
1868 bt.feed(callee);
1869 for (args) |arg| {
1870 bt.feed(arg);
1871 }
1872 return bt.finishAir(result);
1873}
1874
1875fn ret(self: *Self, mcv: MCValue) !void {
1876 const ret_ty = self.fn_type.fnReturnType();
1877 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv);
1878 // Just add space for an instruction, patch this later
1879 try self.code.resize(self.code.items.len + 4);
1880 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
1881}
1882
1883fn airRet(self: *Self, inst: Air.Inst.Index) !void {
1884 const un_op = self.air.instructions.items(.data)[inst].un_op;
1885 const operand = try self.resolveInst(un_op);
1886 try self.ret(operand);
1887 return self.finishAir(inst, .dead, .{ un_op, .none, .none });
1888}
1889
1890fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
1891 const un_op = self.air.instructions.items(.data)[inst].un_op;
1892 const ptr = try self.resolveInst(un_op);
1893 _ = ptr;
1894 return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch});
1895 //return self.finishAir(inst, .dead, .{ un_op, .none, .none });
1896}
1897
1898fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
1899 _ = op;
1900
1901 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1902 if (self.liveness.isUnused(inst))
1903 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
1904 const ty = self.air.typeOf(bin_op.lhs);
1905 assert(ty.eql(self.air.typeOf(bin_op.rhs)));
1906 if (ty.zigTypeTag() == .ErrorSet)
1907 return self.fail("TODO implement cmp for errors", .{});
1908
1909 const lhs = try self.resolveInst(bin_op.lhs);
1910 const rhs = try self.resolveInst(bin_op.rhs);
1911 _ = lhs;
1912 _ = rhs;
1913
1914 return self.fail("TODO implement cmp for {}", .{self.target.cpu.arch});
1915}
1916
1917fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void {
1918 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
1919 try self.dbgAdvancePCAndLine(dbg_stmt.line, dbg_stmt.column);
1920 return self.finishAirBookkeeping();
1921}
1922
1923fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
1924 _ = inst;
1925
1926 return self.fail("TODO implement condbr {}", .{self.target.cpu.arch});
1927}
1928
1929fn isNull(self: *Self, operand: MCValue) !MCValue {
1930 _ = operand;
1931 // Here you can specialize this instruction if it makes sense to, otherwise the default
1932 // will call isNonNull and invert the result.
1933 return self.fail("TODO call isNonNull and invert the result", .{});
1934}
1935
1936fn isNonNull(self: *Self, operand: MCValue) !MCValue {
1937 _ = operand;
1938 // Here you can specialize this instruction if it makes sense to, otherwise the default
1939 // will call isNull and invert the result.
1940 return self.fail("TODO call isNull and invert the result", .{});
1941}
1942
1943fn isErr(self: *Self, operand: MCValue) !MCValue {
1944 _ = operand;
1945 // Here you can specialize this instruction if it makes sense to, otherwise the default
1946 // will call isNonNull and invert the result.
1947 return self.fail("TODO call isNonErr and invert the result", .{});
1948}
1949
1950fn isNonErr(self: *Self, operand: MCValue) !MCValue {
1951 _ = operand;
1952 // Here you can specialize this instruction if it makes sense to, otherwise the default
1953 // will call isNull and invert the result.
1954 return self.fail("TODO call isErr and invert the result", .{});
1955}
1956
1957fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
1958 const un_op = self.air.instructions.items(.data)[inst].un_op;
1959 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1960 const operand = try self.resolveInst(un_op);
1961 break :result try self.isNull(operand);
1962 };
1963 return self.finishAir(inst, result, .{ un_op, .none, .none });
1964}
1965
1966fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
1967 const un_op = self.air.instructions.items(.data)[inst].un_op;
1968 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1969 const operand_ptr = try self.resolveInst(un_op);
1970 const operand: MCValue = blk: {
1971 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
1972 // The MCValue that holds the pointer can be re-used as the value.
1973 break :blk operand_ptr;
1974 } else {
1975 break :blk try self.allocRegOrMem(inst, true);
1976 }
1977 };
1978 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
1979 break :result try self.isNull(operand);
1980 };
1981 return self.finishAir(inst, result, .{ un_op, .none, .none });
1982}
1983
1984fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
1985 const un_op = self.air.instructions.items(.data)[inst].un_op;
1986 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1987 const operand = try self.resolveInst(un_op);
1988 break :result try self.isNonNull(operand);
1989 };
1990 return self.finishAir(inst, result, .{ un_op, .none, .none });
1991}
1992
1993fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
1994 const un_op = self.air.instructions.items(.data)[inst].un_op;
1995 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1996 const operand_ptr = try self.resolveInst(un_op);
1997 const operand: MCValue = blk: {
1998 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
1999 // The MCValue that holds the pointer can be re-used as the value.
2000 break :blk operand_ptr;
2001 } else {
2002 break :blk try self.allocRegOrMem(inst, true);
2003 }
2004 };
2005 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
2006 break :result try self.isNonNull(operand);
2007 };
2008 return self.finishAir(inst, result, .{ un_op, .none, .none });
2009}
2010
2011fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
2012 const un_op = self.air.instructions.items(.data)[inst].un_op;
2013 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2014 const operand = try self.resolveInst(un_op);
2015 break :result try self.isErr(operand);
2016 };
2017 return self.finishAir(inst, result, .{ un_op, .none, .none });
2018}
2019
2020fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
2021 const un_op = self.air.instructions.items(.data)[inst].un_op;
2022 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2023 const operand_ptr = try self.resolveInst(un_op);
2024 const operand: MCValue = blk: {
2025 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
2026 // The MCValue that holds the pointer can be re-used as the value.
2027 break :blk operand_ptr;
2028 } else {
2029 break :blk try self.allocRegOrMem(inst, true);
2030 }
2031 };
2032 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
2033 break :result try self.isErr(operand);
2034 };
2035 return self.finishAir(inst, result, .{ un_op, .none, .none });
2036}
2037
2038fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
2039 const un_op = self.air.instructions.items(.data)[inst].un_op;
2040 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2041 const operand = try self.resolveInst(un_op);
2042 break :result try self.isNonErr(operand);
2043 };
2044 return self.finishAir(inst, result, .{ un_op, .none, .none });
2045}
2046
2047fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
2048 const un_op = self.air.instructions.items(.data)[inst].un_op;
2049 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2050 const operand_ptr = try self.resolveInst(un_op);
2051 const operand: MCValue = blk: {
2052 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
2053 // The MCValue that holds the pointer can be re-used as the value.
2054 break :blk operand_ptr;
2055 } else {
2056 break :blk try self.allocRegOrMem(inst, true);
2057 }
2058 };
2059 try self.load(operand, operand_ptr, self.air.typeOf(un_op));
2060 break :result try self.isNonErr(operand);
2061 };
2062 return self.finishAir(inst, result, .{ un_op, .none, .none });
2063}
2064
2065fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
2066 // A loop is a setup to be able to jump back to the beginning.
2067 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2068 const loop = self.air.extraData(Air.Block, ty_pl.payload);
2069 const body = self.air.extra[loop.end..][0..loop.data.body_len];
2070 const start_index = self.code.items.len;
2071 try self.genBody(body);
2072 try self.jump(start_index);
2073 return self.finishAirBookkeeping();
2074}
2075
2076/// Send control flow to the `index` of `self.code`.
2077fn jump(self: *Self, index: usize) !void {
2078 if (math.cast(i28, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| {
2079 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.b(delta).toU32());
2080 } else |_| {
2081 return self.fail("TODO: enable larger branch offset", .{});
2082 }
2083}
2084
2085fn airBlock(self: *Self, inst: Air.Inst.Index) !void {
2086 try self.blocks.putNoClobber(self.gpa, inst, .{
2087 // A block is a setup to be able to jump to the end.
2088 .relocs = .{},
2089 // It also acts as a receptacle for break operands.
2090 // Here we use `MCValue.none` to represent a null value so that the first
2091 // break instruction will choose a MCValue for the block result and overwrite
2092 // this field. Following break instructions will use that MCValue to put their
2093 // block results.
2094 .mcv = MCValue{ .none = {} },
2095 });
2096 const block_data = self.blocks.getPtr(inst).?;
2097 defer block_data.relocs.deinit(self.gpa);
2098
2099 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2100 const extra = self.air.extraData(Air.Block, ty_pl.payload);
2101 const body = self.air.extra[extra.end..][0..extra.data.body_len];
2102 try self.genBody(body);
2103
2104 for (block_data.relocs.items) |reloc| try self.performReloc(reloc);
2105
2106 const result = @bitCast(MCValue, block_data.mcv);
2107 return self.finishAir(inst, result, .{ .none, .none, .none });
2108}
2109
2110fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
2111 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2112 const condition = pl_op.operand;
2113 _ = condition;
2114
2115 return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch});
2116}
2117
2118fn performReloc(self: *Self, reloc: Reloc) !void {
2119 switch (reloc) {
2120 .rel32 => |pos| {
2121 const amt = self.code.items.len - (pos + 4);
2122 // Here it would be tempting to implement testing for amt == 0 and then elide the
2123 // jump. However, that will cause a problem because other jumps may assume that they
2124 // can jump to this code. Or maybe I didn't understand something when I was debugging.
2125 // It could be worth another look. Anyway, that's why that isn't done here. Probably the
2126 // best place to elide jumps will be in semantic analysis, by inlining blocks that only
2127 // only have 1 break instruction.
2128 const s32_amt = math.cast(i32, amt) catch
2129 return self.fail("unable to perform relocation: jump too far", .{});
2130 mem.writeIntLittle(i32, self.code.items[pos..][0..4], s32_amt);
2131 },
2132 .arm_branch => unreachable,
2133 }
2134}
2135
2136fn airBr(self: *Self, inst: Air.Inst.Index) !void {
2137 const branch = self.air.instructions.items(.data)[inst].br;
2138 try self.br(branch.block_inst, branch.operand);
2139 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none });
2140}
2141
2142fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void {
2143 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2144 const air_tags = self.air.instructions.items(.tag);
2145 _ = air_tags;
2146
2147 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement boolean operations for {}", .{self.target.cpu.arch});
2148 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2149}
2150
2151fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
2152 const block_data = self.blocks.getPtr(block).?;
2153
2154 if (self.air.typeOf(operand).hasCodeGenBits()) {
2155 const operand_mcv = try self.resolveInst(operand);
2156 const block_mcv = block_data.mcv;
2157 if (block_mcv == .none) {
2158 block_data.mcv = operand_mcv;
2159 } else {
2160 try self.setRegOrMem(self.air.typeOfIndex(block), block_mcv, operand_mcv);
2161 }
2162 }
2163 return self.brVoid(block);
2164}
2165
2166fn brVoid(self: *Self, block: Air.Inst.Index) !void {
2167 const block_data = self.blocks.getPtr(block).?;
2168
2169 // Emit a jump with a relocation. It will be patched up after the block ends.
2170 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1);
2171
2172 return self.fail("TODO implement brvoid for {}", .{self.target.cpu.arch});
2173}
2174
2175fn airAsm(self: *Self, inst: Air.Inst.Index) !void {
2176 const air_datas = self.air.instructions.items(.data);
2177 const air_extra = self.air.extraData(Air.Asm, air_datas[inst].ty_pl.payload);
2178 const zir = self.mod_fn.owner_decl.getFileScope().zir;
2179 const extended = zir.instructions.items(.data)[air_extra.data.zir_index].extended;
2180 const zir_extra = zir.extraData(Zir.Inst.Asm, extended.operand);
2181 const asm_source = zir.nullTerminatedString(zir_extra.data.asm_source);
2182 const outputs_len = @truncate(u5, extended.small);
2183 const args_len = @truncate(u5, extended.small >> 5);
2184 const clobbers_len = @truncate(u5, extended.small >> 10);
2185 _ = clobbers_len; // TODO honor these
2186 const is_volatile = @truncate(u1, extended.small >> 15) != 0;
2187 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end..][0..outputs_len]);
2188 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[air_extra.end + outputs.len ..][0..args_len]);
2189
2190 if (outputs_len > 1) {
2191 return self.fail("TODO implement codegen for asm with more than 1 output", .{});
2192 }
2193 var extra_i: usize = zir_extra.end;
2194 const output_constraint: ?[]const u8 = out: {
2195 var i: usize = 0;
2196 while (i < outputs_len) : (i += 1) {
2197 const output = zir.extraData(Zir.Inst.Asm.Output, extra_i);
2198 extra_i = output.end;
2199 break :out zir.nullTerminatedString(output.data.constraint);
2200 }
2201 break :out null;
2202 };
2203
2204 const dead = !is_volatile and self.liveness.isUnused(inst);
2205 const result: MCValue = if (dead) .dead else result: {
2206 for (args) |arg| {
2207 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
2208 extra_i = input.end;
2209 const constraint = zir.nullTerminatedString(input.data.constraint);
2210
2211 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
2212 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
2213 }
2214 const reg_name = constraint[1 .. constraint.len - 1];
2215 const reg = parseRegName(reg_name) orelse
2216 return self.fail("unrecognized register: '{s}'", .{reg_name});
2217
2218 const arg_mcv = try self.resolveInst(arg);
2219 try self.register_manager.getReg(reg, null);
2220 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
2221 }
2222
2223 if (mem.eql(u8, asm_source, "svc #0")) {
2224 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x0).toU32());
2225 } else if (mem.eql(u8, asm_source, "svc #0x80")) {
2226 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x80).toU32());
2227 } else {
2228 return self.fail("TODO implement support for more aarch64 assembly instructions", .{});
2229 }
2230
2231 if (output_constraint) |output| {
2232 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
2233 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
2234 }
2235 const reg_name = output[2 .. output.len - 1];
2236 const reg = parseRegName(reg_name) orelse
2237 return self.fail("unrecognized register: '{s}'", .{reg_name});
2238 break :result MCValue{ .register = reg };
2239 } else {
2240 break :result MCValue{ .none = {} };
2241 }
2242 };
2243 if (outputs.len + args.len <= Liveness.bpi - 1) {
2244 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
2245 std.mem.copy(Air.Inst.Ref, &buf, outputs);
2246 std.mem.copy(Air.Inst.Ref, buf[outputs.len..], args);
2247 return self.finishAir(inst, result, buf);
2248 }
2249 var bt = try self.iterateBigTomb(inst, outputs.len + args.len);
2250 for (outputs) |output| {
2251 bt.feed(output);
2252 }
2253 for (args) |arg| {
2254 bt.feed(arg);
2255 }
2256 return bt.finishAir(result);
2257}
2258
2259fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb {
2260 try self.ensureProcessDeathCapacity(operand_count + 1);
2261 return BigTomb{
2262 .function = self,
2263 .inst = inst,
2264 .tomb_bits = self.liveness.getTombBits(inst),
2265 .big_tomb_bits = self.liveness.special.get(inst) orelse 0,
2266 .bit_index = 0,
2267 };
2268}
2269
2270/// Sets the value without any modifications to register allocation metadata or stack allocation metadata.
2271fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
2272 switch (loc) {
2273 .none => return,
2274 .register => |reg| return self.genSetReg(ty, reg, val),
2275 .stack_offset => |off| return self.genSetStack(ty, off, val),
2276 .memory => {
2277 return self.fail("TODO implement setRegOrMem for memory", .{});
2278 },
2279 else => unreachable,
2280 }
2281}
2282
2283fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void {
2284 switch (mcv) {
2285 .dead => unreachable,
2286 .ptr_stack_offset => unreachable,
2287 .ptr_embedded_in_code => unreachable,
2288 .unreach, .none => return, // Nothing to do.
2289 .undef => {
2290 if (!self.wantSafety())
2291 return; // The already existing value will do just fine.
2292 // TODO Upgrade this to a memset call when we have that available.
2293 switch (ty.abiSize(self.target.*)) {
2294 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
2295 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
2296 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
2297 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
2298 else => return self.fail("TODO implement memset", .{}),
2299 }
2300 },
2301 .compare_flags_unsigned,
2302 .compare_flags_signed,
2303 .immediate,
2304 => {
2305 const reg = try self.copyToTmpRegister(ty, mcv);
2306 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
2307 },
2308 .embedded_in_code => |code_offset| {
2309 _ = code_offset;
2310 return self.fail("TODO implement set stack variable from embedded_in_code", .{});
2311 },
2312 .register => |reg| {
2313 const abi_size = ty.abiSize(self.target.*);
2314 const adj_off = stack_offset + abi_size;
2315
2316 switch (abi_size) {
2317 1, 2, 4, 8 => {
2318 const offset = if (math.cast(i9, adj_off)) |imm|
2319 Instruction.LoadStoreOffset.imm_post_index(-imm)
2320 else |_|
2321 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u64), MCValue{ .immediate = adj_off }));
2322 const rn: Register = switch (self.target.cpu.arch) {
2323 .aarch64, .aarch64_be => .x29,
2324 .aarch64_32 => .w29,
2325 else => unreachable,
2326 };
2327 const str = switch (abi_size) {
2328 1 => Instruction.strb,
2329 2 => Instruction.strh,
2330 4, 8 => Instruction.str,
2331 else => unreachable, // unexpected abi size
2332 };
2333
2334 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), str(reg, rn, .{
2335 .offset = offset,
2336 }).toU32());
2337 },
2338 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
2339 }
2340 },
2341 .memory => |vaddr| {
2342 _ = vaddr;
2343 return self.fail("TODO implement set stack variable from memory vaddr", .{});
2344 },
2345 .stack_offset => |off| {
2346 if (stack_offset == off)
2347 return; // Copy stack variable to itself; nothing to do.
2348
2349 const reg = try self.copyToTmpRegister(ty, mcv);
2350 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
2351 },
2352 }
2353}
2354
2355fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
2356 switch (mcv) {
2357 .dead => unreachable,
2358 .ptr_stack_offset => unreachable,
2359 .ptr_embedded_in_code => unreachable,
2360 .unreach, .none => return, // Nothing to do.
2361 .undef => {
2362 if (!self.wantSafety())
2363 return; // The already existing value will do just fine.
2364 // Write the debug undefined value.
2365 switch (reg.size()) {
2366 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }),
2367 64 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
2368 else => unreachable, // unexpected register size
2369 }
2370 },
2371 .immediate => |x| {
2372 if (x <= math.maxInt(u16)) {
2373 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @intCast(u16, x), 0).toU32());
2374 } else if (x <= math.maxInt(u32)) {
2375 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32());
2376 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 16), 16).toU32());
2377 } else if (x <= math.maxInt(u32)) {
2378 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32());
2379 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 16), 16).toU32());
2380 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 32), 32).toU32());
2381 } else {
2382 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32());
2383 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 16), 16).toU32());
2384 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 32), 32).toU32());
2385 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 48), 48).toU32());
2386 }
2387 },
2388 .register => |src_reg| {
2389 // If the registers are the same, nothing to do.
2390 if (src_reg.id() == reg.id())
2391 return;
2392
2393 // mov reg, src_reg
2394 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.orr(
2395 reg,
2396 .xzr,
2397 src_reg,
2398 Instruction.Shift.none,
2399 ).toU32());
2400 },
2401 .memory => |addr| {
2402 if (self.bin_file.options.pie) {
2403 // PC-relative displacement to the entry in the GOT table.
2404 // adrp
2405 const offset = @intCast(u32, self.code.items.len);
2406 mem.writeIntLittle(
2407 u32,
2408 try self.code.addManyAsArray(4),
2409 Instruction.adrp(reg, 0).toU32(),
2410 );
2411 // ldr reg, reg, offset
2412 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{
2413 .register = .{
2414 .rn = reg,
2415 .offset = Instruction.LoadStoreOffset.imm(0),
2416 },
2417 }).toU32());
2418
2419 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
2420 // TODO I think the reloc might be in the wrong place.
2421 const decl = macho_file.active_decl.?;
2422 // Page reloc for adrp instruction.
2423 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
2424 .offset = offset,
2425 .target = .{ .local = @intCast(u32, addr) },
2426 .addend = 0,
2427 .subtractor = null,
2428 .pcrel = true,
2429 .length = 2,
2430 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
2431 });
2432 // Pageoff reloc for adrp instruction.
2433 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
2434 .offset = offset + 4,
2435 .target = .{ .local = @intCast(u32, addr) },
2436 .addend = 0,
2437 .subtractor = null,
2438 .pcrel = false,
2439 .length = 2,
2440 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
2441 });
2442 } else {
2443 return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{});
2444 }
2445 } else {
2446 // The value is in memory at a hard-coded address.
2447 // If the type is a pointer, it means the pointer address is at this memory location.
2448 try self.genSetReg(Type.initTag(.usize), reg, .{ .immediate = addr });
2449 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ .rn = reg } }).toU32());
2450 }
2451 },
2452 .stack_offset => |unadjusted_off| {
2453 // TODO: maybe addressing from sp instead of fp
2454 const abi_size = ty.abiSize(self.target.*);
2455 const adj_off = unadjusted_off + abi_size;
2456
2457 const rn: Register = switch (self.target.cpu.arch) {
2458 .aarch64, .aarch64_be => .x29,
2459 .aarch64_32 => .w29,
2460 else => unreachable,
2461 };
2462
2463 const offset = if (math.cast(i9, adj_off)) |imm|
2464 Instruction.LoadStoreOffset.imm_post_index(-imm)
2465 else |_|
2466 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u64), MCValue{ .immediate = adj_off }));
2467
2468 switch (abi_size) {
2469 1, 2 => {
2470 const ldr = switch (abi_size) {
2471 1 => Instruction.ldrb,
2472 2 => Instruction.ldrh,
2473 else => unreachable, // unexpected abi size
2474 };
2475
2476 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), ldr(reg, rn, .{
2477 .offset = offset,
2478 }).toU32());
2479 },
2480 4, 8 => {
2481 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{
2482 .rn = rn,
2483 .offset = offset,
2484 } }).toU32());
2485 },
2486 else => return self.fail("TODO implement genSetReg other types abi_size={}", .{abi_size}),
2487 }
2488 },
2489 else => return self.fail("TODO implement genSetReg for aarch64 {}", .{mcv}),
2490 }
2491}
2492
2493fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
2494 const un_op = self.air.instructions.items(.data)[inst].un_op;
2495 const result = try self.resolveInst(un_op);
2496 return self.finishAir(inst, result, .{ un_op, .none, .none });
2497}
2498
2499fn airBitCast(self: *Self, inst: Air.Inst.Index) !void {
2500 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2501 const result = try self.resolveInst(ty_op.operand);
2502 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2503}
2504
2505fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
2506 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2507 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airArrayToSlice for {}", .{
2508 self.target.cpu.arch,
2509 });
2510 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2511}
2512
2513fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void {
2514 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2515 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntToFloat for {}", .{
2516 self.target.cpu.arch,
2517 });
2518 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2519}
2520
2521fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void {
2522 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
2523 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatToInt for {}", .{
2524 self.target.cpu.arch,
2525 });
2526 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2527}
2528
2529fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void {
2530 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2531 const extra = self.air.extraData(Air.Block, ty_pl.payload);
2532 _ = extra;
2533
2534 return self.fail("TODO implement airCmpxchg for {}", .{
2535 self.target.cpu.arch,
2536 });
2537}
2538
2539fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void {
2540 _ = inst;
2541 return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch});
2542}
2543
2544fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void {
2545 _ = inst;
2546 return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch});
2547}
2548
2549fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void {
2550 _ = inst;
2551 _ = order;
2552 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch});
2553}
2554
2555fn airMemset(self: *Self, inst: Air.Inst.Index) !void {
2556 _ = inst;
2557 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
2558}
2559
2560fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void {
2561 _ = inst;
2562 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch});
2563}
2564
2565fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue {
2566 // First section of indexes correspond to a set number of constant values.
2567 const ref_int = @enumToInt(inst);
2568 if (ref_int < Air.Inst.Ref.typed_value_map.len) {
2569 const tv = Air.Inst.Ref.typed_value_map[ref_int];
2570 if (!tv.ty.hasCodeGenBits()) {
2571 return MCValue{ .none = {} };
2572 }
2573 return self.genTypedValue(tv);
2574 }
2575
2576 // If the type has no codegen bits, no need to store it.
2577 const inst_ty = self.air.typeOf(inst);
2578 if (!inst_ty.hasCodeGenBits())
2579 return MCValue{ .none = {} };
2580
2581 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len);
2582 switch (self.air.instructions.items(.tag)[inst_index]) {
2583 .constant => {
2584 // Constants have static lifetimes, so they are always memoized in the outer most table.
2585 const branch = &self.branch_stack.items[0];
2586 const gop = try branch.inst_table.getOrPut(self.gpa, inst_index);
2587 if (!gop.found_existing) {
2588 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl;
2589 gop.value_ptr.* = try self.genTypedValue(.{
2590 .ty = inst_ty,
2591 .val = self.air.values[ty_pl.payload],
2592 });
2593 }
2594 return gop.value_ptr.*;
2595 },
2596 .const_ty => unreachable,
2597 else => return self.getResolvedInstValue(inst_index),
2598 }
2599}
2600
2601fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue {
2602 // Treat each stack item as a "layer" on top of the previous one.
2603 var i: usize = self.branch_stack.items.len;
2604 while (true) {
2605 i -= 1;
2606 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| {
2607 assert(mcv != .dead);
2608 return mcv;
2609 }
2610 }
2611}
2612
2613/// If the MCValue is an immediate, and it does not fit within this type,
2614/// we put it in a register.
2615/// A potential opportunity for future optimization here would be keeping track
2616/// of the fact that the instruction is available both as an immediate
2617/// and as a register.
2618fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCValue {
2619 const mcv = try self.resolveInst(operand);
2620 const ti = @typeInfo(T).Int;
2621 switch (mcv) {
2622 .immediate => |imm| {
2623 // This immediate is unsigned.
2624 const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed));
2625 if (imm >= math.maxInt(U)) {
2626 return MCValue{ .register = try self.copyToTmpRegister(Type.initTag(.usize), mcv) };
2627 }
2628 },
2629 else => {},
2630 }
2631 return mcv;
2632}
2633
2634fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue {
2635 if (typed_value.val.isUndef())
2636 return MCValue{ .undef = {} };
2637 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
2638 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
2639 switch (typed_value.ty.zigTypeTag()) {
2640 .Pointer => switch (typed_value.ty.ptrSize()) {
2641 .Slice => {
2642 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
2643 const ptr_type = typed_value.ty.slicePtrFieldType(&buf);
2644 const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val });
2645 const slice_len = typed_value.val.sliceLen();
2646 // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean
2647 // the Sema code needs to use anonymous Decls or alloca instructions to store data.
2648 const ptr_imm = ptr_mcv.memory;
2649 _ = slice_len;
2650 _ = ptr_imm;
2651 // We need more general support for const data being stored in memory to make this work.
2652 return self.fail("TODO codegen for const slices", .{});
2653 },
2654 else => {
2655 if (typed_value.val.castTag(.decl_ref)) |payload| {
2656 const decl = payload.data;
2657 decl.alive = true;
2658 if (self.bin_file.cast(link.File.Elf)) |elf_file| {
2659 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
2660 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes;
2661 return MCValue{ .memory = got_addr };
2662 } else if (self.bin_file.cast(link.File.MachO)) |_| {
2663 // TODO I'm hacking my way through here by repurposing .memory for storing
2664 // index to the GOT target symbol index.
2665 return MCValue{ .memory = decl.link.macho.local_sym_index };
2666 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| {
2667 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes;
2668 return MCValue{ .memory = got_addr };
2669 } else if (self.bin_file.cast(link.File.Plan9)) |p9| {
2670 try p9.seeDecl(decl);
2671 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes;
2672 return MCValue{ .memory = got_addr };
2673 } else {
2674 return self.fail("TODO codegen non-ELF const Decl pointer", .{});
2675 }
2676 }
2677 if (typed_value.val.tag() == .int_u64) {
2678 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
2679 }
2680 return self.fail("TODO codegen more kinds of const pointers", .{});
2681 },
2682 },
2683 .Int => {
2684 const info = typed_value.ty.intInfo(self.target.*);
2685 if (info.bits > ptr_bits or info.signedness == .signed) {
2686 return self.fail("TODO const int bigger than ptr and signed int", .{});
2687 }
2688 return MCValue{ .immediate = typed_value.val.toUnsignedInt() };
2689 },
2690 .Bool => {
2691 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) };
2692 },
2693 .ComptimeInt => unreachable, // semantic analysis prevents this
2694 .ComptimeFloat => unreachable, // semantic analysis prevents this
2695 .Optional => {
2696 if (typed_value.ty.isPtrLikeOptional()) {
2697 if (typed_value.val.isNull())
2698 return MCValue{ .immediate = 0 };
2699
2700 var buf: Type.Payload.ElemType = undefined;
2701 return self.genTypedValue(.{
2702 .ty = typed_value.ty.optionalChild(&buf),
2703 .val = typed_value.val,
2704 });
2705 } else if (typed_value.ty.abiSize(self.target.*) == 1) {
2706 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) };
2707 }
2708 return self.fail("TODO non pointer optionals", .{});
2709 },
2710 .Enum => {
2711 if (typed_value.val.castTag(.enum_field_index)) |field_index| {
2712 switch (typed_value.ty.tag()) {
2713 .enum_simple => {
2714 return MCValue{ .immediate = field_index.data };
2715 },
2716 .enum_full, .enum_nonexhaustive => {
2717 const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data;
2718 if (enum_full.values.count() != 0) {
2719 const tag_val = enum_full.values.keys()[field_index.data];
2720 return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val });
2721 } else {
2722 return MCValue{ .immediate = field_index.data };
2723 }
2724 },
2725 else => unreachable,
2726 }
2727 } else {
2728 var int_tag_buffer: Type.Payload.Bits = undefined;
2729 const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer);
2730 return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val });
2731 }
2732 },
2733 .ErrorSet => {
2734 switch (typed_value.val.tag()) {
2735 .@"error" => {
2736 const err_name = typed_value.val.castTag(.@"error").?.data.name;
2737 const module = self.bin_file.options.module.?;
2738 const global_error_set = module.global_error_set;
2739 const error_index = global_error_set.get(err_name).?;
2740 return MCValue{ .immediate = error_index };
2741 },
2742 else => {
2743 // In this case we are rendering an error union which has a 0 bits payload.
2744 return MCValue{ .immediate = 0 };
2745 },
2746 }
2747 },
2748 .ErrorUnion => {
2749 const error_type = typed_value.ty.errorUnionSet();
2750 const payload_type = typed_value.ty.errorUnionPayload();
2751 const sub_val = typed_value.val.castTag(.eu_payload).?.data;
2752
2753 if (!payload_type.hasCodeGenBits()) {
2754 // We use the error type directly as the type.
2755 return self.genTypedValue(.{ .ty = error_type, .val = sub_val });
2756 }
2757
2758 return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty});
2759 },
2760 else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}),
2761 }
2762}
2763
2764const CallMCValues = struct {
2765 args: []MCValue,
2766 return_value: MCValue,
2767 stack_byte_count: u32,
2768 stack_align: u32,
2769
2770 fn deinit(self: *CallMCValues, func: *Self) void {
2771 func.gpa.free(self.args);
2772 self.* = undefined;
2773 }
2774};
2775
2776/// Caller must call `CallMCValues.deinit`.
2777fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues {
2778 const cc = fn_ty.fnCallingConvention();
2779 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen());
2780 defer self.gpa.free(param_types);
2781 fn_ty.fnParamTypes(param_types);
2782 var result: CallMCValues = .{
2783 .args = try self.gpa.alloc(MCValue, param_types.len),
2784 // These undefined values must be populated before returning from this function.
2785 .return_value = undefined,
2786 .stack_byte_count = undefined,
2787 .stack_align = undefined,
2788 };
2789 errdefer self.gpa.free(result.args);
2790
2791 const ret_ty = fn_ty.fnReturnType();
2792
2793 switch (cc) {
2794 .Naked => {
2795 assert(result.args.len == 0);
2796 result.return_value = .{ .unreach = {} };
2797 result.stack_byte_count = 0;
2798 result.stack_align = 1;
2799 return result;
2800 },
2801 .Unspecified, .C => {
2802 // ARM64 Procedure Call Standard
2803 var ncrn: usize = 0; // Next Core Register Number
2804 var nsaa: u32 = 0; // Next stacked argument address
2805
2806 for (param_types) |ty, i| {
2807 // We round up NCRN only for non-Apple platforms which allow the 16-byte aligned
2808 // values to spread across odd-numbered registers.
2809 if (ty.abiAlignment(self.target.*) == 16 and !self.target.isDarwin()) {
2810 // Round up NCRN to the next even number
2811 ncrn += ncrn % 2;
2812 }
2813
2814 const param_size = @intCast(u32, ty.abiSize(self.target.*));
2815 if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) {
2816 if (param_size <= 8) {
2817 result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };
2818 ncrn += 1;
2819 } else {
2820 return self.fail("TODO MCValues with multiple registers", .{});
2821 }
2822 } else if (ncrn < 8 and nsaa == 0) {
2823 return self.fail("TODO MCValues split between registers and stack", .{});
2824 } else {
2825 ncrn = 8;
2826 // TODO Apple allows the arguments on the stack to be non-8-byte aligned provided
2827 // that the entire stack space consumed by the arguments is 8-byte aligned.
2828 if (ty.abiAlignment(self.target.*) == 8) {
2829 if (nsaa % 8 != 0) {
2830 nsaa += 8 - (nsaa % 8);
2831 }
2832 }
2833
2834 result.args[i] = .{ .stack_offset = nsaa };
2835 nsaa += param_size;
2836 }
2837 }
2838
2839 result.stack_byte_count = nsaa;
2840 result.stack_align = 16;
2841 },
2842 else => return self.fail("TODO implement function parameters for {} on aarch64", .{cc}),
2843 }
2844
2845 if (ret_ty.zigTypeTag() == .NoReturn) {
2846 result.return_value = .{ .unreach = {} };
2847 } else if (!ret_ty.hasCodeGenBits()) {
2848 result.return_value = .{ .none = {} };
2849 } else switch (cc) {
2850 .Naked => unreachable,
2851 .Unspecified, .C => {
2852 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
2853 if (ret_ty_size <= 8) {
2854 result.return_value = .{ .register = c_abi_int_return_regs[0] };
2855 } else {
2856 return self.fail("TODO support more return types for ARM backend", .{});
2857 }
2858 },
2859 else => return self.fail("TODO implement function return values for {}", .{cc}),
2860 }
2861 return result;
2862}
2863
2864/// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`.
2865fn wantSafety(self: *Self) bool {
2866 return switch (self.bin_file.options.optimize_mode) {
2867 .Debug => true,
2868 .ReleaseSafe => true,
2869 .ReleaseFast => false,
2870 .ReleaseSmall => false,
2871 };
2872}
2873
2874fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {
2875 @setCold(true);
2876 assert(self.err_msg == null);
2877 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args);
2878 return error.CodegenFail;
2879}
2880
2881fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError {
2882 @setCold(true);
2883 assert(self.err_msg == null);
2884 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args);
2885 return error.CodegenFail;
2886}
2887
2888const Register = @import("bits.zig").Register;
2889const Instruction = @import("bits.zig").Instruction;
2890const callee_preserved_regs = @import("bits.zig").callee_preserved_regs;
2891const c_abi_int_param_regs = @import("bits.zig").c_abi_int_param_regs;
2892const c_abi_int_return_regs = @import("bits.zig").c_abi_int_return_regs;
2893
2894fn parseRegName(name: []const u8) ?Register {
2895 if (@hasDecl(Register, "parseRegName")) {
2896 return Register.parseRegName(name);
2897 }
2898 return std.meta.stringToEnum(Register, name);
2899}
2900
2901fn registerAlias(reg: Register, size_bytes: u32) Register {
2902 _ = size_bytes;
2903
2904 return reg;
2905}
2906
2907/// For most architectures this does nothing. For x86_64 it resolves any aliased registers
2908/// to the 64-bit wide ones.
2909fn toCanonicalReg(reg: Register) Register {
2910 return reg;
2911}
src/codegen.zig+5-531
......@@ -88,9 +88,9 @@ pub fn generateFunction(
8888 .wasm64 => unreachable, // has its own code path
8989 .arm => return Function(.arm).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
9090 .armeb => return Function(.armeb).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
91 .aarch64 => return Function(.aarch64).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
92 .aarch64_be => return Function(.aarch64_be).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
93 .aarch64_32 => return Function(.aarch64_32).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
91 .aarch64 => return @import("arch/aarch64/CodeGen.zig").generate(.aarch64, bin_file, src_loc, func, air, liveness, code, debug_output),
92 .aarch64_be => return @import("arch/aarch64/CodeGen.zig").generate(.aarch64_be, bin_file, src_loc, func, air, liveness, code, debug_output),
93 .aarch64_32 => return @import("arch/aarch64/CodeGen.zig").generate(.aarch64_32, bin_file, src_loc, func, air, liveness, code, debug_output),
9494 //.arc => return Function(.arc).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
9595 //.avr => return Function(.avr).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
9696 //.bpfel => return Function(.bpfel).generate(bin_file, src_loc, func, air, liveness, code, debug_output),
......@@ -730,82 +730,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
730730 try self.dbgSetEpilogueBegin();
731731 }
732732 },
733 .aarch64, .aarch64_be, .aarch64_32 => {
734 const cc = self.fn_type.fnCallingConvention();
735 if (cc != .Naked) {
736 // TODO Finish function prologue and epilogue for aarch64.
737
738 // stp fp, lr, [sp, #-16]!
739 // mov fp, sp
740 // sub sp, sp, #reloc
741 writeInt(u32, try self.code.addManyAsArray(4), Instruction.stp(
742 .x29,
743 .x30,
744 Register.sp,
745 Instruction.LoadStorePairOffset.pre_index(-16),
746 ).toU32());
747 writeInt(u32, try self.code.addManyAsArray(4), Instruction.add(.x29, .xzr, 0, false).toU32());
748 const backpatch_reloc = self.code.items.len;
749 try self.code.resize(backpatch_reloc + 4);
750
751 try self.dbgSetPrologueEnd();
752
753 try self.genBody(self.air.getMainBody());
754
755 // Backpatch stack offset
756 const stack_end = self.max_end_stack;
757 const aligned_stack_end = mem.alignForward(stack_end, self.stack_align);
758 if (math.cast(u12, aligned_stack_end)) |size| {
759 writeInt(u32, self.code.items[backpatch_reloc..][0..4], Instruction.sub(.xzr, .xzr, size, false).toU32());
760 } else |_| {
761 return self.failSymbol("TODO AArch64: allow larger stacks", .{});
762 }
763
764 try self.dbgSetEpilogueBegin();
765
766 // exitlude jumps
767 if (self.exitlude_jump_relocs.items.len == 1) {
768 // There is only one relocation. Hence,
769 // this relocation must be at the end of
770 // the code. Therefore, we can just delete
771 // the space initially reserved for the
772 // jump
773 self.code.items.len -= 4;
774 } else for (self.exitlude_jump_relocs.items) |jmp_reloc| {
775 const amt = @intCast(i32, self.code.items.len) - @intCast(i32, jmp_reloc + 8);
776 if (amt == -4) {
777 // This return is at the end of the
778 // code block. We can't just delete
779 // the space because there may be
780 // other jumps we already relocated to
781 // the address. Instead, insert a nop
782 writeInt(u32, self.code.items[jmp_reloc..][0..4], Instruction.nop().toU32());
783 } else {
784 if (math.cast(i28, amt)) |offset| {
785 writeInt(u32, self.code.items[jmp_reloc..][0..4], Instruction.b(offset).toU32());
786 } else |_| {
787 return self.failSymbol("exitlude jump is too large", .{});
788 }
789 }
790 }
791
792 // ldp fp, lr, [sp], #16
793 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldp(
794 .x29,
795 .x30,
796 Register.sp,
797 Instruction.LoadStorePairOffset.post_index(16),
798 ).toU32());
799 // add sp, sp, #stack_size
800 writeInt(u32, try self.code.addManyAsArray(4), Instruction.add(.xzr, .xzr, @intCast(u12, aligned_stack_end), false).toU32());
801 // ret lr
802 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ret(null).toU32());
803 } else {
804 try self.dbgSetPrologueEnd();
805 try self.genBody(self.air.getMainBody());
806 try self.dbgSetEpilogueBegin();
807 }
808 },
809733 else => {
810734 try self.dbgSetPrologueEnd();
811735 try self.genBody(self.air.getMainBody());
......@@ -2690,7 +2614,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
26902614 const result = self.args[arg_index];
26912615 const mcv = switch (arch) {
26922616 // TODO support stack-only arguments on all target architectures
2693 .arm, .armeb, .aarch64, .aarch64_32, .aarch64_be => switch (result) {
2617 .arm, .armeb => switch (result) {
26942618 // Copy registers to the stack
26952619 .register => |reg| blk: {
26962620 const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch {
......@@ -2732,9 +2656,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
27322656 .arm, .armeb => {
27332657 writeInt(u32, try self.code.addManyAsArray(4), Instruction.bkpt(0).toU32());
27342658 },
2735 .aarch64 => {
2736 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.brk(1).toU32());
2737 },
27382659 else => return self.fail("TODO implement @breakpoint() for {}", .{self.target.cpu.arch}),
27392660 }
27402661 return self.finishAirBookkeeping();
......@@ -2913,63 +2834,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
29132834 return self.fail("TODO implement calling runtime known function pointer", .{});
29142835 }
29152836 },
2916 .aarch64 => {
2917 for (info.args) |mc_arg, arg_i| {
2918 const arg = args[arg_i];
2919 const arg_ty = self.air.typeOf(arg);
2920 const arg_mcv = try self.resolveInst(args[arg_i]);
2921
2922 switch (mc_arg) {
2923 .none => continue,
2924 .undef => unreachable,
2925 .immediate => unreachable,
2926 .unreach => unreachable,
2927 .dead => unreachable,
2928 .embedded_in_code => unreachable,
2929 .memory => unreachable,
2930 .compare_flags_signed => unreachable,
2931 .compare_flags_unsigned => unreachable,
2932 .register => |reg| {
2933 try self.register_manager.getReg(reg, null);
2934 try self.genSetReg(arg_ty, reg, arg_mcv);
2935 },
2936 .stack_offset => {
2937 return self.fail("TODO implement calling with parameters in memory", .{});
2938 },
2939 .ptr_stack_offset => {
2940 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
2941 },
2942 .ptr_embedded_in_code => {
2943 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
2944 },
2945 }
2946 }
2947
2948 if (self.air.value(callee)) |func_value| {
2949 if (func_value.castTag(.function)) |func_payload| {
2950 const func = func_payload.data;
2951 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
2952 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
2953 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: {
2954 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?];
2955 break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes);
2956 } else if (self.bin_file.cast(link.File.Coff)) |coff_file|
2957 coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes
2958 else
2959 unreachable;
2960
2961 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr });
2962
2963 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
2964 } else if (func_value.castTag(.extern_fn)) |_| {
2965 return self.fail("TODO implement calling extern functions", .{});
2966 } else {
2967 return self.fail("TODO implement calling bitcasted functions", .{});
2968 }
2969 } else {
2970 return self.fail("TODO implement calling runtime known function pointer", .{});
2971 }
2972 },
29732837 else => return self.fail("TODO implement call for {}", .{self.target.cpu.arch}),
29742838 }
29752839 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| {
......@@ -2984,7 +2848,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
29842848 .register => |reg| {
29852849 // TODO prevent this macho if block to be generated for all archs
29862850 switch (arch) {
2987 .x86_64, .aarch64 => try self.register_manager.getReg(reg, null),
2851 .x86_64 => try self.register_manager.getReg(reg, null),
29882852 else => unreachable,
29892853 }
29902854 try self.genSetReg(arg_ty, reg, arg_mcv);
......@@ -3025,13 +2889,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
30252889 try self.code.ensureUnusedCapacity(2);
30262890 self.code.appendSliceAssumeCapacity(&[2]u8{ 0xff, 0xd0 });
30272891 },
3028 .aarch64 => {
3029 try self.genSetReg(Type.initTag(.u64), .x30, .{
3030 .memory = func.owner_decl.link.macho.local_sym_index,
3031 });
3032 // blr x30
3033 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
3034 },
30352892 else => unreachable, // unsupported architecture on MachO
30362893 }
30372894 } else if (func_value.castTag(.extern_fn)) |func_payload| {
......@@ -3045,12 +2902,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
30452902 self.code.appendSliceAssumeCapacity(&[5]u8{ 0xe8, 0x0, 0x0, 0x0, 0x0 });
30462903 break :blk @intCast(u32, self.code.items.len) - 4;
30472904 },
3048 .aarch64 => {
3049 const offset = @intCast(u32, self.code.items.len);
3050 // bl
3051 writeInt(u32, try self.code.addManyAsArray(4), Instruction.bl(0).toU32());
3052 break :blk offset;
3053 },
30542905 else => unreachable, // unsupported architecture on MachO
30552906 }
30562907 };
......@@ -3063,7 +2914,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
30632914 .pcrel = true,
30642915 .length = 2,
30652916 .@"type" = switch (arch) {
3066 .aarch64 => @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_BRANCH26),
30672917 .x86_64 => @enumToInt(std.macho.reloc_type_x86_64.X86_64_RELOC_BRANCH),
30682918 else => unreachable,
30692919 },
......@@ -3127,58 +2977,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
31272977 return self.fail("TODO implement calling runtime known function pointer", .{});
31282978 }
31292979 },
3130 .aarch64 => {
3131 for (info.args) |mc_arg, arg_i| {
3132 const arg = args[arg_i];
3133 const arg_ty = self.air.typeOf(arg);
3134 const arg_mcv = try self.resolveInst(args[arg_i]);
3135
3136 switch (mc_arg) {
3137 .none => continue,
3138 .undef => unreachable,
3139 .immediate => unreachable,
3140 .unreach => unreachable,
3141 .dead => unreachable,
3142 .embedded_in_code => unreachable,
3143 .memory => unreachable,
3144 .compare_flags_signed => unreachable,
3145 .compare_flags_unsigned => unreachable,
3146 .register => |reg| {
3147 try self.register_manager.getReg(reg, null);
3148 try self.genSetReg(arg_ty, reg, arg_mcv);
3149 },
3150 .stack_offset => {
3151 return self.fail("TODO implement calling with parameters in memory", .{});
3152 },
3153 .ptr_stack_offset => {
3154 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{});
3155 },
3156 .ptr_embedded_in_code => {
3157 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{});
3158 },
3159 }
3160 }
3161 if (self.air.value(callee)) |func_value| {
3162 if (func_value.castTag(.function)) |func_payload| {
3163 try p9.seeDecl(func_payload.data.owner_decl);
3164 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
3165 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
3166 const got_addr = p9.bases.data;
3167 const got_index = func_payload.data.owner_decl.link.plan9.got_index.?;
3168 const fn_got_addr = got_addr + got_index * ptr_bytes;
3169
3170 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr });
3171
3172 writeInt(u32, try self.code.addManyAsArray(4), Instruction.blr(.x30).toU32());
3173 } else if (func_value.castTag(.extern_fn)) |_| {
3174 return self.fail("TODO implement calling extern functions", .{});
3175 } else {
3176 return self.fail("TODO implement calling bitcasted functions", .{});
3177 }
3178 } else {
3179 return self.fail("TODO implement calling runtime known function pointer", .{});
3180 }
3181 },
31822980 else => return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}),
31832981 }
31842982 } else unreachable;
......@@ -3233,11 +3031,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
32333031 try self.code.resize(self.code.items.len + 4);
32343032 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
32353033 },
3236 .aarch64 => {
3237 // Just add space for an instruction, patch this later
3238 try self.code.resize(self.code.items.len + 4);
3239 try self.exitlude_jump_relocs.append(self.gpa, self.code.items.len - 4);
3240 },
32413034 else => return self.fail("TODO implement return for {}", .{self.target.cpu.arch}),
32423035 }
32433036 }
......@@ -3731,13 +3524,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
37313524 return self.fail("TODO: enable larger branch offset", .{});
37323525 }
37333526 },
3734 .aarch64, .aarch64_be, .aarch64_32 => {
3735 if (math.cast(i28, @intCast(i32, index) - @intCast(i32, self.code.items.len + 8))) |delta| {
3736 writeInt(u32, try self.code.addManyAsArray(4), Instruction.b(delta).toU32());
3737 } else |_| {
3738 return self.fail("TODO: enable larger branch offset", .{});
3739 }
3740 },
37413527 else => return self.fail("TODO implement jump for {}", .{self.target.cpu.arch}),
37423528 }
37433529 }
......@@ -3944,44 +3730,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
39443730 break :result MCValue{ .none = {} };
39453731 }
39463732 },
3947 .aarch64 => result: {
3948 for (args) |arg| {
3949 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
3950 extra_i = input.end;
3951 const constraint = zir.nullTerminatedString(input.data.constraint);
3952
3953 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') {
3954 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint});
3955 }
3956 const reg_name = constraint[1 .. constraint.len - 1];
3957 const reg = parseRegName(reg_name) orelse
3958 return self.fail("unrecognized register: '{s}'", .{reg_name});
3959
3960 const arg_mcv = try self.resolveInst(arg);
3961 try self.register_manager.getReg(reg, null);
3962 try self.genSetReg(self.air.typeOf(arg), reg, arg_mcv);
3963 }
3964
3965 if (mem.eql(u8, asm_source, "svc #0")) {
3966 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x0).toU32());
3967 } else if (mem.eql(u8, asm_source, "svc #0x80")) {
3968 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.svc(0x80).toU32());
3969 } else {
3970 return self.fail("TODO implement support for more aarch64 assembly instructions", .{});
3971 }
3972
3973 if (output_constraint) |output| {
3974 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') {
3975 return self.fail("unrecognized asm output constraint: '{s}'", .{output});
3976 }
3977 const reg_name = output[2 .. output.len - 1];
3978 const reg = parseRegName(reg_name) orelse
3979 return self.fail("unrecognized register: '{s}'", .{reg_name});
3980 break :result MCValue{ .register = reg };
3981 } else {
3982 break :result MCValue{ .none = {} };
3983 }
3984 },
39853733 .riscv64 => result: {
39863734 for (args) |arg| {
39873735 const input = zir.extraData(Zir.Inst.Asm.Input, extra_i);
......@@ -4303,75 +4051,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
43034051 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
43044052 },
43054053 },
4306 .aarch64, .aarch64_be, .aarch64_32 => switch (mcv) {
4307 .dead => unreachable,
4308 .ptr_stack_offset => unreachable,
4309 .ptr_embedded_in_code => unreachable,
4310 .unreach, .none => return, // Nothing to do.
4311 .undef => {
4312 if (!self.wantSafety())
4313 return; // The already existing value will do just fine.
4314 // TODO Upgrade this to a memset call when we have that available.
4315 switch (ty.abiSize(self.target.*)) {
4316 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }),
4317 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }),
4318 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }),
4319 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
4320 else => return self.fail("TODO implement memset", .{}),
4321 }
4322 },
4323 .compare_flags_unsigned,
4324 .compare_flags_signed,
4325 .immediate,
4326 => {
4327 const reg = try self.copyToTmpRegister(ty, mcv);
4328 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
4329 },
4330 .embedded_in_code => |code_offset| {
4331 _ = code_offset;
4332 return self.fail("TODO implement set stack variable from embedded_in_code", .{});
4333 },
4334 .register => |reg| {
4335 const abi_size = ty.abiSize(self.target.*);
4336 const adj_off = stack_offset + abi_size;
4337
4338 switch (abi_size) {
4339 1, 2, 4, 8 => {
4340 const offset = if (math.cast(i9, adj_off)) |imm|
4341 Instruction.LoadStoreOffset.imm_post_index(-imm)
4342 else |_|
4343 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u64), MCValue{ .immediate = adj_off }));
4344 const rn: Register = switch (arch) {
4345 .aarch64, .aarch64_be => .x29,
4346 .aarch64_32 => .w29,
4347 else => unreachable,
4348 };
4349 const str = switch (abi_size) {
4350 1 => Instruction.strb,
4351 2 => Instruction.strh,
4352 4, 8 => Instruction.str,
4353 else => unreachable, // unexpected abi size
4354 };
4355
4356 writeInt(u32, try self.code.addManyAsArray(4), str(reg, rn, .{
4357 .offset = offset,
4358 }).toU32());
4359 },
4360 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}),
4361 }
4362 },
4363 .memory => |vaddr| {
4364 _ = vaddr;
4365 return self.fail("TODO implement set stack variable from memory vaddr", .{});
4366 },
4367 .stack_offset => |off| {
4368 if (stack_offset == off)
4369 return; // Copy stack variable to itself; nothing to do.
4370
4371 const reg = try self.copyToTmpRegister(ty, mcv);
4372 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
4373 },
4374 },
43754054 else => return self.fail("TODO implement getSetStack for {}", .{self.target.cpu.arch}),
43764055 }
43774056 }
......@@ -4491,141 +4170,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
44914170 },
44924171 else => return self.fail("TODO implement getSetReg for arm {}", .{mcv}),
44934172 },
4494 .aarch64 => switch (mcv) {
4495 .dead => unreachable,
4496 .ptr_stack_offset => unreachable,
4497 .ptr_embedded_in_code => unreachable,
4498 .unreach, .none => return, // Nothing to do.
4499 .undef => {
4500 if (!self.wantSafety())
4501 return; // The already existing value will do just fine.
4502 // Write the debug undefined value.
4503 switch (reg.size()) {
4504 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }),
4505 64 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }),
4506 else => unreachable, // unexpected register size
4507 }
4508 },
4509 .immediate => |x| {
4510 if (x <= math.maxInt(u16)) {
4511 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @intCast(u16, x), 0).toU32());
4512 } else if (x <= math.maxInt(u32)) {
4513 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32());
4514 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 16), 16).toU32());
4515 } else if (x <= math.maxInt(u32)) {
4516 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32());
4517 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 16), 16).toU32());
4518 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 32), 32).toU32());
4519 } else {
4520 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movz(reg, @truncate(u16, x), 0).toU32());
4521 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 16), 16).toU32());
4522 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @truncate(u16, x >> 32), 32).toU32());
4523 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.movk(reg, @intCast(u16, x >> 48), 48).toU32());
4524 }
4525 },
4526 .register => |src_reg| {
4527 // If the registers are the same, nothing to do.
4528 if (src_reg.id() == reg.id())
4529 return;
4530
4531 // mov reg, src_reg
4532 writeInt(u32, try self.code.addManyAsArray(4), Instruction.orr(
4533 reg,
4534 .xzr,
4535 src_reg,
4536 Instruction.Shift.none,
4537 ).toU32());
4538 },
4539 .memory => |addr| {
4540 if (self.bin_file.options.pie) {
4541 // PC-relative displacement to the entry in the GOT table.
4542 // adrp
4543 const offset = @intCast(u32, self.code.items.len);
4544 mem.writeIntLittle(
4545 u32,
4546 try self.code.addManyAsArray(4),
4547 Instruction.adrp(reg, 0).toU32(),
4548 );
4549 // ldr reg, reg, offset
4550 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{
4551 .register = .{
4552 .rn = reg,
4553 .offset = Instruction.LoadStoreOffset.imm(0),
4554 },
4555 }).toU32());
4556
4557 if (self.bin_file.cast(link.File.MachO)) |macho_file| {
4558 // TODO I think the reloc might be in the wrong place.
4559 const decl = macho_file.active_decl.?;
4560 // Page reloc for adrp instruction.
4561 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
4562 .offset = offset,
4563 .target = .{ .local = @intCast(u32, addr) },
4564 .addend = 0,
4565 .subtractor = null,
4566 .pcrel = true,
4567 .length = 2,
4568 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21),
4569 });
4570 // Pageoff reloc for adrp instruction.
4571 try decl.link.macho.relocs.append(self.bin_file.allocator, .{
4572 .offset = offset + 4,
4573 .target = .{ .local = @intCast(u32, addr) },
4574 .addend = 0,
4575 .subtractor = null,
4576 .pcrel = false,
4577 .length = 2,
4578 .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12),
4579 });
4580 } else {
4581 return self.fail("TODO implement genSetReg for PIE GOT indirection on this platform", .{});
4582 }
4583 } else {
4584 // The value is in memory at a hard-coded address.
4585 // If the type is a pointer, it means the pointer address is at this memory location.
4586 try self.genSetReg(Type.initTag(.usize), reg, .{ .immediate = addr });
4587 mem.writeIntLittle(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{ .rn = reg } }).toU32());
4588 }
4589 },
4590 .stack_offset => |unadjusted_off| {
4591 // TODO: maybe addressing from sp instead of fp
4592 const abi_size = ty.abiSize(self.target.*);
4593 const adj_off = unadjusted_off + abi_size;
4594
4595 const rn: Register = switch (arch) {
4596 .aarch64, .aarch64_be => .x29,
4597 .aarch64_32 => .w29,
4598 else => unreachable,
4599 };
4600
4601 const offset = if (math.cast(i9, adj_off)) |imm|
4602 Instruction.LoadStoreOffset.imm_post_index(-imm)
4603 else |_|
4604 Instruction.LoadStoreOffset.reg(try self.copyToTmpRegister(Type.initTag(.u64), MCValue{ .immediate = adj_off }));
4605
4606 switch (abi_size) {
4607 1, 2 => {
4608 const ldr = switch (abi_size) {
4609 1 => Instruction.ldrb,
4610 2 => Instruction.ldrh,
4611 else => unreachable, // unexpected abi size
4612 };
4613
4614 writeInt(u32, try self.code.addManyAsArray(4), ldr(reg, rn, .{
4615 .offset = offset,
4616 }).toU32());
4617 },
4618 4, 8 => {
4619 writeInt(u32, try self.code.addManyAsArray(4), Instruction.ldr(reg, .{ .register = .{
4620 .rn = rn,
4621 .offset = offset,
4622 } }).toU32());
4623 },
4624 else => return self.fail("TODO implement genSetReg other types abi_size={}", .{abi_size}),
4625 }
4626 },
4627 else => return self.fail("TODO implement genSetReg for aarch64 {}", .{mcv}),
4628 },
46294173 .riscv64 => switch (mcv) {
46304174 .dead => unreachable,
46314175 .ptr_stack_offset => unreachable,
......@@ -5358,59 +4902,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
53584902 else => return self.fail("TODO implement function parameters for {} on arm", .{cc}),
53594903 }
53604904 },
5361 .aarch64 => {
5362 switch (cc) {
5363 .Naked => {
5364 assert(result.args.len == 0);
5365 result.return_value = .{ .unreach = {} };
5366 result.stack_byte_count = 0;
5367 result.stack_align = 1;
5368 return result;
5369 },
5370 .Unspecified, .C => {
5371 // ARM64 Procedure Call Standard
5372 var ncrn: usize = 0; // Next Core Register Number
5373 var nsaa: u32 = 0; // Next stacked argument address
5374
5375 for (param_types) |ty, i| {
5376 // We round up NCRN only for non-Apple platforms which allow the 16-byte aligned
5377 // values to spread across odd-numbered registers.
5378 if (ty.abiAlignment(self.target.*) == 16 and !self.target.isDarwin()) {
5379 // Round up NCRN to the next even number
5380 ncrn += ncrn % 2;
5381 }
5382
5383 const param_size = @intCast(u32, ty.abiSize(self.target.*));
5384 if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) {
5385 if (param_size <= 8) {
5386 result.args[i] = .{ .register = c_abi_int_param_regs[ncrn] };
5387 ncrn += 1;
5388 } else {
5389 return self.fail("TODO MCValues with multiple registers", .{});
5390 }
5391 } else if (ncrn < 8 and nsaa == 0) {
5392 return self.fail("TODO MCValues split between registers and stack", .{});
5393 } else {
5394 ncrn = 8;
5395 // TODO Apple allows the arguments on the stack to be non-8-byte aligned provided
5396 // that the entire stack space consumed by the arguments is 8-byte aligned.
5397 if (ty.abiAlignment(self.target.*) == 8) {
5398 if (nsaa % 8 != 0) {
5399 nsaa += 8 - (nsaa % 8);
5400 }
5401 }
5402
5403 result.args[i] = .{ .stack_offset = nsaa };
5404 nsaa += param_size;
5405 }
5406 }
5407
5408 result.stack_byte_count = nsaa;
5409 result.stack_align = 16;
5410 },
5411 else => return self.fail("TODO implement function parameters for {} on aarch64", .{cc}),
5412 }
5413 },
54144905 else => if (param_types.len != 0)
54154906 return self.fail("TODO implement codegen parameters for {}", .{self.target.cpu.arch}),
54164907 }
......@@ -5441,18 +4932,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
54414932 },
54424933 else => return self.fail("TODO implement function return values for {}", .{cc}),
54434934 },
5444 .aarch64 => switch (cc) {
5445 .Naked => unreachable,
5446 .Unspecified, .C => {
5447 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*));
5448 if (ret_ty_size <= 8) {
5449 result.return_value = .{ .register = c_abi_int_return_regs[0] };
5450 } else {
5451 return self.fail("TODO support more return types for ARM backend", .{});
5452 }
5453 },
5454 else => return self.fail("TODO implement function return values for {}", .{cc}),
5455 },
54564935 else => return self.fail("TODO implement codegen return values for {}", .{self.target.cpu.arch}),
54574936 }
54584937 return result;
......@@ -5487,7 +4966,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
54874966 .x86_64 => @import("arch/x86_64/bits.zig").Register,
54884967 .riscv64 => @import("arch/riscv64/bits.zig").Register,
54894968 .arm, .armeb => @import("arch/arm/bits.zig").Register,
5490 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").Register,
54914969 else => enum {
54924970 dummy,
54934971
......@@ -5501,7 +4979,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
55014979 const Instruction = switch (arch) {
55024980 .riscv64 => @import("arch/riscv64/bits.zig").Instruction,
55034981 .arm, .armeb => @import("arch/arm/bits.zig").Instruction,
5504 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").Instruction,
55054982 else => void,
55064983 };
55074984
......@@ -5515,7 +4992,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
55154992 .x86_64 => @import("arch/x86_64/bits.zig").callee_preserved_regs,
55164993 .riscv64 => @import("arch/riscv64/bits.zig").callee_preserved_regs,
55174994 .arm, .armeb => @import("arch/arm/bits.zig").callee_preserved_regs,
5518 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").callee_preserved_regs,
55194995 else => [_]Register{},
55204996 };
55214997
......@@ -5523,7 +4999,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
55234999 .i386 => @import("arch/x86/bits.zig").c_abi_int_param_regs,
55245000 .x86_64 => @import("arch/x86_64/bits.zig").c_abi_int_param_regs,
55255001 .arm, .armeb => @import("arch/arm/bits.zig").c_abi_int_param_regs,
5526 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").c_abi_int_param_regs,
55275002 else => [_]Register{},
55285003 };
55295004
......@@ -5531,7 +5006,6 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
55315006 .i386 => @import("arch/x86/bits.zig").c_abi_int_return_regs,
55325007 .x86_64 => @import("arch/x86_64/bits.zig").c_abi_int_return_regs,
55335008 .arm, .armeb => @import("arch/arm/bits.zig").c_abi_int_return_regs,
5534 .aarch64, .aarch64_be, .aarch64_32 => @import("arch/aarch64/bits.zig").c_abi_int_return_regs,
55355009 else => [_]Register{},
55365010 };
55375011