blob 5ed7b63d (173616B) - Raw
1 const std = @import("std"); 2 const builtin = @import("builtin"); 3 const mem = std.mem; 4 const math = std.math; 5 const assert = std.debug.assert; 6 const Air = @import("../../Air.zig"); 7 const Mir = @import("Mir.zig"); 8 const Emit = @import("Emit.zig"); 9 const Liveness = @import("../../Liveness.zig"); 10 const Type = @import("../../type.zig").Type; 11 const Value = @import("../../value.zig").Value; 12 const TypedValue = @import("../../TypedValue.zig"); 13 const link = @import("../../link.zig"); 14 const Module = @import("../../Module.zig"); 15 const Compilation = @import("../../Compilation.zig"); 16 const ErrorMsg = Module.ErrorMsg; 17 const Target = std.Target; 18 const Allocator = mem.Allocator; 19 const trace = @import("../../tracy.zig").trace; 20 const DW = std.dwarf; 21 const leb128 = std.leb; 22 const log = std.log.scoped(.codegen); 23 const build_options = @import("build_options"); 24 const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; 25 const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs); 26 27 const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; 28 const FnResult = @import("../../codegen.zig").FnResult; 29 const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; 30 31 const bits = @import("bits.zig"); 32 const abi = @import("abi.zig"); 33 const Register = bits.Register; 34 const Instruction = bits.Instruction; 35 const callee_preserved_regs = abi.callee_preserved_regs; 36 const c_abi_int_param_regs = abi.c_abi_int_param_regs; 37 const c_abi_int_return_regs = abi.c_abi_int_return_regs; 38 39 const InnerError = error{ 40 OutOfMemory, 41 CodegenFail, 42 OutOfRegisters, 43 }; 44 45 gpa: Allocator, 46 air: Air, 47 liveness: Liveness, 48 bin_file: *link.File, 49 target: *const std.Target, 50 mod_fn: *const Module.Fn, 51 err_msg: ?*ErrorMsg, 52 args: []MCValue, 53 ret_mcv: MCValue, 54 fn_type: Type, 55 arg_index: usize, 56 src_loc: Module.SrcLoc, 57 stack_align: u32, 58 59 /// MIR Instructions 60 mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, 61 /// MIR extra data 62 mir_extra: std.ArrayListUnmanaged(u32) = .{}, 63 64 /// Byte offset within the source file of the ending curly. 65 end_di_line: u32, 66 end_di_column: u32, 67 68 /// The value is an offset into the `Function` `code` from the beginning. 69 /// To perform the reloc, write 32-bit signed little-endian integer 70 /// which is a relative jump, based on the address following the reloc. 71 exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{}, 72 73 /// Whenever there is a runtime branch, we push a Branch onto this stack, 74 /// and pop it off when the runtime branch joins. This provides an "overlay" 75 /// of the table of mappings from instructions to `MCValue` from within the branch. 76 /// This way we can modify the `MCValue` for an instruction in different ways 77 /// within different branches. Special consideration is needed when a branch 78 /// joins with its parent, to make sure all instructions have the same MCValue 79 /// across each runtime branch upon joining. 80 branch_stack: *std.ArrayList(Branch), 81 82 // Key is the block instruction 83 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, 84 85 register_manager: RegisterManager = .{}, 86 /// Maps offset to what is stored there. 87 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, 88 /// Tracks the current instruction allocated to the compare flags 89 compare_flags_inst: ?Air.Inst.Index = null, 90 91 /// Offset from the stack base, representing the end of the stack frame. 92 max_end_stack: u32 = 0, 93 /// Represents the current end stack offset. If there is no existing slot 94 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`. 95 next_stack_offset: u32 = 0, 96 97 saved_regs_stack_space: u32 = 0, 98 99 /// Debug field, used to find bugs in the compiler. 100 air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, 101 102 const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; 103 104 const MCValue = union(enum) { 105 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. 106 /// TODO Look into deleting this tag and using `dead` instead, since every use 107 /// of MCValue.none should be instead looking at the type and noticing it is 0 bits. 108 none, 109 /// Control flow will not allow this value to be observed. 110 unreach, 111 /// No more references to this value remain. 112 dead, 113 /// The value is undefined. 114 undef, 115 /// A pointer-sized integer that fits in a register. 116 /// If the type is a pointer, this is the pointer address in virtual address space. 117 immediate: u64, 118 /// The value is in a target-specific register. 119 register: Register, 120 /// The value is in memory at a hard-coded address. 121 /// If the type is a pointer, it means the pointer address is at this memory location. 122 memory: u64, 123 /// The value is in memory referenced indirectly via a GOT entry index. 124 /// If the type is a pointer, it means the pointer is referenced indirectly via GOT. 125 /// When lowered, linker will emit relocations of type ARM64_RELOC_GOT_LOAD_PAGE21 and ARM64_RELOC_GOT_LOAD_PAGEOFF12. 126 got_load: u32, 127 /// The value is in memory referenced directly via symbol index. 128 /// If the type is a pointer, it means the pointer is referenced directly via symbol index. 129 /// When lowered, linker will emit a relocation of type ARM64_RELOC_PAGE21 and ARM64_RELOC_PAGEOFF12. 130 direct_load: u32, 131 /// The value is one of the stack variables. 132 /// If the type is a pointer, it means the pointer address is in the stack at this offset. 133 stack_offset: u32, 134 /// The value is a pointer to one of the stack variables (payload is stack offset). 135 ptr_stack_offset: u32, 136 /// The value is in the compare flags assuming an unsigned operation, 137 /// with this operator applied on top of it. 138 compare_flags_unsigned: math.CompareOperator, 139 /// The value is in the compare flags assuming a signed operation, 140 /// with this operator applied on top of it. 141 compare_flags_signed: math.CompareOperator, 142 143 fn isMemory(mcv: MCValue) bool { 144 return switch (mcv) { 145 .memory, .stack_offset => true, 146 else => false, 147 }; 148 } 149 150 fn isImmediate(mcv: MCValue) bool { 151 return switch (mcv) { 152 .immediate => true, 153 else => false, 154 }; 155 } 156 157 fn isMutable(mcv: MCValue) bool { 158 return switch (mcv) { 159 .none => unreachable, 160 .unreach => unreachable, 161 .dead => unreachable, 162 163 .immediate, 164 .memory, 165 .compare_flags_unsigned, 166 .compare_flags_signed, 167 .ptr_stack_offset, 168 .undef, 169 => false, 170 171 .register, 172 .stack_offset, 173 => true, 174 }; 175 } 176 }; 177 178 const Branch = struct { 179 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{}, 180 181 fn deinit(self: *Branch, gpa: Allocator) void { 182 self.inst_table.deinit(gpa); 183 self.* = undefined; 184 } 185 }; 186 187 const StackAllocation = struct { 188 inst: Air.Inst.Index, 189 /// TODO do we need size? should be determined by inst.ty.abiSize() 190 size: u32, 191 }; 192 193 const BlockData = struct { 194 relocs: std.ArrayListUnmanaged(Mir.Inst.Index), 195 /// The first break instruction encounters `null` here and chooses a 196 /// machine code value for the block result, populating this field. 197 /// Following break instructions encounter that value and use it for 198 /// the location to store their block results. 199 mcv: MCValue, 200 }; 201 202 const BigTomb = struct { 203 function: *Self, 204 inst: Air.Inst.Index, 205 lbt: Liveness.BigTomb, 206 207 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void { 208 const dies = bt.lbt.feed(); 209 const op_index = Air.refToIndex(op_ref) orelse return; 210 if (!dies) return; 211 bt.function.processDeath(op_index); 212 } 213 214 fn finishAir(bt: *BigTomb, result: MCValue) void { 215 const is_used = !bt.function.liveness.isUnused(bt.inst); 216 if (is_used) { 217 log.debug("%{d} => {}", .{ bt.inst, result }); 218 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1]; 219 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result); 220 } 221 bt.function.finishAirBookkeeping(); 222 } 223 }; 224 225 const Self = @This(); 226 227 pub fn generate( 228 bin_file: *link.File, 229 src_loc: Module.SrcLoc, 230 module_fn: *Module.Fn, 231 air: Air, 232 liveness: Liveness, 233 code: *std.ArrayList(u8), 234 debug_output: DebugInfoOutput, 235 ) GenerateSymbolError!FnResult { 236 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { 237 @panic("Attempted to compile for architecture that was disabled by build configuration"); 238 } 239 240 const mod = bin_file.options.module.?; 241 const fn_owner_decl = mod.declPtr(module_fn.owner_decl); 242 assert(fn_owner_decl.has_tv); 243 const fn_type = fn_owner_decl.ty; 244 245 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator); 246 defer { 247 assert(branch_stack.items.len == 1); 248 branch_stack.items[0].deinit(bin_file.allocator); 249 branch_stack.deinit(); 250 } 251 try branch_stack.append(.{}); 252 253 var function = Self{ 254 .gpa = bin_file.allocator, 255 .air = air, 256 .liveness = liveness, 257 .target = &bin_file.options.target, 258 .bin_file = bin_file, 259 .mod_fn = module_fn, 260 .err_msg = null, 261 .args = undefined, // populated after `resolveCallingConventionValues` 262 .ret_mcv = undefined, // populated after `resolveCallingConventionValues` 263 .fn_type = fn_type, 264 .arg_index = 0, 265 .branch_stack = &branch_stack, 266 .src_loc = src_loc, 267 .stack_align = undefined, 268 .end_di_line = module_fn.rbrace_line, 269 .end_di_column = module_fn.rbrace_column, 270 }; 271 defer function.stack.deinit(bin_file.allocator); 272 defer function.blocks.deinit(bin_file.allocator); 273 defer function.exitlude_jump_relocs.deinit(bin_file.allocator); 274 275 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { 276 error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, 277 error.OutOfRegisters => return FnResult{ 278 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), 279 }, 280 else => |e| return e, 281 }; 282 defer call_info.deinit(&function); 283 284 function.args = call_info.args; 285 function.ret_mcv = call_info.return_value; 286 function.stack_align = call_info.stack_align; 287 function.max_end_stack = call_info.stack_byte_count; 288 289 function.gen() catch |err| switch (err) { 290 error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, 291 error.OutOfRegisters => return FnResult{ 292 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), 293 }, 294 else => |e| return e, 295 }; 296 297 var mir = Mir{ 298 .instructions = function.mir_instructions.toOwnedSlice(), 299 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator), 300 }; 301 defer mir.deinit(bin_file.allocator); 302 303 var emit = Emit{ 304 .mir = mir, 305 .bin_file = bin_file, 306 .debug_output = debug_output, 307 .target = &bin_file.options.target, 308 .src_loc = src_loc, 309 .code = code, 310 .prev_di_pc = 0, 311 .prev_di_line = module_fn.lbrace_line, 312 .prev_di_column = module_fn.lbrace_column, 313 .stack_size = mem.alignForwardGeneric(u32, function.max_end_stack, function.stack_align), 314 }; 315 defer emit.deinit(); 316 317 emit.emitMir() catch |err| switch (err) { 318 error.EmitFail => return FnResult{ .fail = emit.err_msg.? }, 319 else => |e| return e, 320 }; 321 322 if (function.err_msg) |em| { 323 return FnResult{ .fail = em }; 324 } else { 325 return FnResult{ .appended = {} }; 326 } 327 } 328 329 fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { 330 const gpa = self.gpa; 331 332 try self.mir_instructions.ensureUnusedCapacity(gpa, 1); 333 334 const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len); 335 self.mir_instructions.appendAssumeCapacity(inst); 336 return result_index; 337 } 338 339 pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 { 340 const fields = std.meta.fields(@TypeOf(extra)); 341 try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len); 342 return self.addExtraAssumeCapacity(extra); 343 } 344 345 pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { 346 const fields = std.meta.fields(@TypeOf(extra)); 347 const result = @intCast(u32, self.mir_extra.items.len); 348 inline for (fields) |field| { 349 self.mir_extra.appendAssumeCapacity(switch (field.field_type) { 350 u32 => @field(extra, field.name), 351 i32 => @bitCast(u32, @field(extra, field.name)), 352 else => @compileError("bad field type"), 353 }); 354 } 355 return result; 356 } 357 358 fn gen(self: *Self) !void { 359 const cc = self.fn_type.fnCallingConvention(); 360 if (cc != .Naked) { 361 // stp fp, lr, [sp, #-16]! 362 _ = try self.addInst(.{ 363 .tag = .stp, 364 .data = .{ .load_store_register_pair = .{ 365 .rt = .x29, 366 .rt2 = .x30, 367 .rn = .sp, 368 .offset = Instruction.LoadStorePairOffset.pre_index(-16), 369 } }, 370 }); 371 372 // <store other registers> 373 const backpatch_save_registers = try self.addInst(.{ 374 .tag = .nop, 375 .data = .{ .nop = {} }, 376 }); 377 378 // mov fp, sp 379 _ = try self.addInst(.{ 380 .tag = .mov_to_from_sp, 381 .data = .{ .rr = .{ .rd = .x29, .rn = .sp } }, 382 }); 383 384 // sub sp, sp, #reloc 385 const backpatch_reloc = try self.addInst(.{ 386 .tag = .nop, 387 .data = .{ .nop = {} }, 388 }); 389 390 _ = try self.addInst(.{ 391 .tag = .dbg_prologue_end, 392 .data = .{ .nop = {} }, 393 }); 394 395 try self.genBody(self.air.getMainBody()); 396 397 // Backpatch push callee saved regs 398 var saved_regs: u32 = 0; 399 self.saved_regs_stack_space = 16; 400 inline for (callee_preserved_regs) |reg| { 401 if (self.register_manager.isRegAllocated(reg)) { 402 saved_regs |= @as(u32, 1) << @intCast(u5, reg.id()); 403 self.saved_regs_stack_space += 8; 404 } 405 } 406 407 // Emit.mirPopPushRegs automatically adds extra empty space so 408 // that sp is always aligned to 16 409 if (!std.mem.isAlignedGeneric(u32, self.saved_regs_stack_space, 16)) { 410 self.saved_regs_stack_space += 8; 411 } 412 assert(std.mem.isAlignedGeneric(u32, self.saved_regs_stack_space, 16)); 413 414 self.mir_instructions.set(backpatch_save_registers, .{ 415 .tag = .push_regs, 416 .data = .{ .reg_list = saved_regs }, 417 }); 418 419 // Backpatch stack offset 420 const total_stack_size = self.max_end_stack + self.saved_regs_stack_space; 421 const aligned_total_stack_end = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align); 422 const stack_size = aligned_total_stack_end - self.saved_regs_stack_space; 423 if (math.cast(u12, stack_size)) |size| { 424 self.mir_instructions.set(backpatch_reloc, .{ 425 .tag = .sub_immediate, 426 .data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = size } }, 427 }); 428 } else |_| { 429 return self.failSymbol("TODO AArch64: allow larger stacks", .{}); 430 } 431 432 _ = try self.addInst(.{ 433 .tag = .dbg_epilogue_begin, 434 .data = .{ .nop = {} }, 435 }); 436 437 // exitlude jumps 438 if (self.exitlude_jump_relocs.items.len > 0 and 439 self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2) 440 { 441 // If the last Mir instruction (apart from the 442 // dbg_epilogue_begin) is the last exitlude jump 443 // relocation (which would just jump one instruction 444 // further), it can be safely removed 445 self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop()); 446 } 447 448 for (self.exitlude_jump_relocs.items) |jmp_reloc| { 449 self.mir_instructions.set(jmp_reloc, .{ 450 .tag = .b, 451 .data = .{ .inst = @intCast(u32, self.mir_instructions.len) }, 452 }); 453 } 454 455 // add sp, sp, #stack_size 456 _ = try self.addInst(.{ 457 .tag = .add_immediate, 458 .data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = @intCast(u12, stack_size) } }, 459 }); 460 461 // <load other registers> 462 _ = try self.addInst(.{ 463 .tag = .pop_regs, 464 .data = .{ .reg_list = saved_regs }, 465 }); 466 467 // ldp fp, lr, [sp], #16 468 _ = try self.addInst(.{ 469 .tag = .ldp, 470 .data = .{ .load_store_register_pair = .{ 471 .rt = .x29, 472 .rt2 = .x30, 473 .rn = .sp, 474 .offset = Instruction.LoadStorePairOffset.post_index(16), 475 } }, 476 }); 477 478 // ret lr 479 _ = try self.addInst(.{ 480 .tag = .ret, 481 .data = .{ .reg = .x30 }, 482 }); 483 } else { 484 _ = try self.addInst(.{ 485 .tag = .dbg_prologue_end, 486 .data = .{ .nop = {} }, 487 }); 488 489 try self.genBody(self.air.getMainBody()); 490 491 _ = try self.addInst(.{ 492 .tag = .dbg_epilogue_begin, 493 .data = .{ .nop = {} }, 494 }); 495 } 496 497 // Drop them off at the rbrace. 498 _ = try self.addInst(.{ 499 .tag = .dbg_line, 500 .data = .{ .dbg_line_column = .{ 501 .line = self.end_di_line, 502 .column = self.end_di_column, 503 } }, 504 }); 505 } 506 507 fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { 508 const air_tags = self.air.instructions.items(.tag); 509 510 for (body) |inst| { 511 const old_air_bookkeeping = self.air_bookkeeping; 512 try self.ensureProcessDeathCapacity(Liveness.bpi); 513 514 switch (air_tags[inst]) { 515 // zig fmt: off 516 .add, .ptr_add => try self.airBinOp(inst), 517 .addwrap => try self.airBinOp(inst), 518 .add_sat => try self.airAddSat(inst), 519 .sub, .ptr_sub => try self.airBinOp(inst), 520 .subwrap => try self.airBinOp(inst), 521 .sub_sat => try self.airSubSat(inst), 522 .mul => try self.airBinOp(inst), 523 .mulwrap => try self.airBinOp(inst), 524 .mul_sat => try self.airMulSat(inst), 525 .rem => try self.airRem(inst), 526 .mod => try self.airMod(inst), 527 .shl, .shl_exact => try self.airBinOp(inst), 528 .shl_sat => try self.airShlSat(inst), 529 .min => try self.airMin(inst), 530 .max => try self.airMax(inst), 531 .slice => try self.airSlice(inst), 532 533 .sqrt, 534 .sin, 535 .cos, 536 .tan, 537 .exp, 538 .exp2, 539 .log, 540 .log2, 541 .log10, 542 .fabs, 543 .floor, 544 .ceil, 545 .round, 546 .trunc_float 547 => try self.airUnaryMath(inst), 548 549 .add_with_overflow => try self.airAddWithOverflow(inst), 550 .sub_with_overflow => try self.airSubWithOverflow(inst), 551 .mul_with_overflow => try self.airMulWithOverflow(inst), 552 .shl_with_overflow => try self.airShlWithOverflow(inst), 553 554 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), 555 556 .cmp_lt => try self.airCmp(inst, .lt), 557 .cmp_lte => try self.airCmp(inst, .lte), 558 .cmp_eq => try self.airCmp(inst, .eq), 559 .cmp_gte => try self.airCmp(inst, .gte), 560 .cmp_gt => try self.airCmp(inst, .gt), 561 .cmp_neq => try self.airCmp(inst, .neq), 562 563 .cmp_vector => try self.airCmpVector(inst), 564 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), 565 566 .bool_and => try self.airBinOp(inst), 567 .bool_or => try self.airBinOp(inst), 568 .bit_and => try self.airBinOp(inst), 569 .bit_or => try self.airBinOp(inst), 570 .xor => try self.airBinOp(inst), 571 .shr, .shr_exact => try self.airBinOp(inst), 572 573 .alloc => try self.airAlloc(inst), 574 .ret_ptr => try self.airRetPtr(inst), 575 .arg => try self.airArg(inst), 576 .assembly => try self.airAsm(inst), 577 .bitcast => try self.airBitCast(inst), 578 .block => try self.airBlock(inst), 579 .br => try self.airBr(inst), 580 .breakpoint => try self.airBreakpoint(), 581 .ret_addr => try self.airRetAddr(inst), 582 .frame_addr => try self.airFrameAddress(inst), 583 .fence => try self.airFence(), 584 .cond_br => try self.airCondBr(inst), 585 .dbg_stmt => try self.airDbgStmt(inst), 586 .fptrunc => try self.airFptrunc(inst), 587 .fpext => try self.airFpext(inst), 588 .intcast => try self.airIntCast(inst), 589 .trunc => try self.airTrunc(inst), 590 .bool_to_int => try self.airBoolToInt(inst), 591 .is_non_null => try self.airIsNonNull(inst), 592 .is_non_null_ptr => try self.airIsNonNullPtr(inst), 593 .is_null => try self.airIsNull(inst), 594 .is_null_ptr => try self.airIsNullPtr(inst), 595 .is_non_err => try self.airIsNonErr(inst), 596 .is_non_err_ptr => try self.airIsNonErrPtr(inst), 597 .is_err => try self.airIsErr(inst), 598 .is_err_ptr => try self.airIsErrPtr(inst), 599 .load => try self.airLoad(inst), 600 .loop => try self.airLoop(inst), 601 .not => try self.airNot(inst), 602 .ptrtoint => try self.airPtrToInt(inst), 603 .ret => try self.airRet(inst), 604 .ret_load => try self.airRetLoad(inst), 605 .store => try self.airStore(inst), 606 .struct_field_ptr=> try self.airStructFieldPtr(inst), 607 .struct_field_val=> try self.airStructFieldVal(inst), 608 .array_to_slice => try self.airArrayToSlice(inst), 609 .int_to_float => try self.airIntToFloat(inst), 610 .float_to_int => try self.airFloatToInt(inst), 611 .cmpxchg_strong => try self.airCmpxchg(inst), 612 .cmpxchg_weak => try self.airCmpxchg(inst), 613 .atomic_rmw => try self.airAtomicRmw(inst), 614 .atomic_load => try self.airAtomicLoad(inst), 615 .memcpy => try self.airMemcpy(inst), 616 .memset => try self.airMemset(inst), 617 .set_union_tag => try self.airSetUnionTag(inst), 618 .get_union_tag => try self.airGetUnionTag(inst), 619 .clz => try self.airClz(inst), 620 .ctz => try self.airCtz(inst), 621 .popcount => try self.airPopcount(inst), 622 .byte_swap => try self.airByteSwap(inst), 623 .bit_reverse => try self.airBitReverse(inst), 624 .tag_name => try self.airTagName(inst), 625 .error_name => try self.airErrorName(inst), 626 .splat => try self.airSplat(inst), 627 .select => try self.airSelect(inst), 628 .shuffle => try self.airShuffle(inst), 629 .reduce => try self.airReduce(inst), 630 .aggregate_init => try self.airAggregateInit(inst), 631 .union_init => try self.airUnionInit(inst), 632 .prefetch => try self.airPrefetch(inst), 633 .mul_add => try self.airMulAdd(inst), 634 635 .dbg_var_ptr, 636 .dbg_var_val, 637 => try self.airDbgVar(inst), 638 639 .dbg_inline_begin, 640 .dbg_inline_end, 641 => try self.airDbgInline(inst), 642 643 .dbg_block_begin, 644 .dbg_block_end, 645 => try self.airDbgBlock(inst), 646 647 .call => try self.airCall(inst, .auto), 648 .call_always_tail => try self.airCall(inst, .always_tail), 649 .call_never_tail => try self.airCall(inst, .never_tail), 650 .call_never_inline => try self.airCall(inst, .never_inline), 651 652 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), 653 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), 654 .atomic_store_release => try self.airAtomicStore(inst, .Release), 655 .atomic_store_seq_cst => try self.airAtomicStore(inst, .SeqCst), 656 657 .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), 658 .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), 659 .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), 660 .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), 661 662 .field_parent_ptr => try self.airFieldParentPtr(inst), 663 664 .switch_br => try self.airSwitch(inst), 665 .slice_ptr => try self.airSlicePtr(inst), 666 .slice_len => try self.airSliceLen(inst), 667 668 .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst), 669 .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst), 670 671 .array_elem_val => try self.airArrayElemVal(inst), 672 .slice_elem_val => try self.airSliceElemVal(inst), 673 .slice_elem_ptr => try self.airSliceElemPtr(inst), 674 .ptr_elem_val => try self.airPtrElemVal(inst), 675 .ptr_elem_ptr => try self.airPtrElemPtr(inst), 676 677 .constant => unreachable, // excluded from function bodies 678 .const_ty => unreachable, // excluded from function bodies 679 .unreach => self.finishAirBookkeeping(), 680 681 .optional_payload => try self.airOptionalPayload(inst), 682 .optional_payload_ptr => try self.airOptionalPayloadPtr(inst), 683 .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst), 684 .unwrap_errunion_err => try self.airUnwrapErrErr(inst), 685 .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), 686 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), 687 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), 688 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst), 689 690 .wrap_optional => try self.airWrapOptional(inst), 691 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), 692 .wrap_errunion_err => try self.airWrapErrUnionErr(inst), 693 694 .wasm_memory_size => unreachable, 695 .wasm_memory_grow => unreachable, 696 // zig fmt: on 697 } 698 699 assert(!self.register_manager.frozenRegsExist()); 700 701 if (std.debug.runtime_safety) { 702 if (self.air_bookkeeping < old_air_bookkeeping + 1) { 703 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] }); 704 } 705 } 706 } 707 } 708 709 /// Asserts there is already capacity to insert into top branch inst_table. 710 fn processDeath(self: *Self, inst: Air.Inst.Index) void { 711 const air_tags = self.air.instructions.items(.tag); 712 if (air_tags[inst] == .constant) return; // Constants are immortal. 713 // When editing this function, note that the logic must synchronize with `reuseOperand`. 714 const prev_value = self.getResolvedInstValue(inst); 715 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 716 branch.inst_table.putAssumeCapacity(inst, .dead); 717 switch (prev_value) { 718 .register => |reg| { 719 const canon_reg = toCanonicalReg(reg); 720 self.register_manager.freeReg(canon_reg); 721 }, 722 .compare_flags_signed, .compare_flags_unsigned => { 723 self.compare_flags_inst = null; 724 }, 725 else => {}, // TODO process stack allocation death 726 } 727 } 728 729 /// Called when there are no operands, and the instruction is always unreferenced. 730 fn finishAirBookkeeping(self: *Self) void { 731 if (std.debug.runtime_safety) { 732 self.air_bookkeeping += 1; 733 } 734 } 735 736 fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void { 737 var tomb_bits = self.liveness.getTombBits(inst); 738 for (operands) |op| { 739 const dies = @truncate(u1, tomb_bits) != 0; 740 tomb_bits >>= 1; 741 if (!dies) continue; 742 const op_int = @enumToInt(op); 743 if (op_int < Air.Inst.Ref.typed_value_map.len) continue; 744 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); 745 self.processDeath(op_index); 746 } 747 const is_used = @truncate(u1, tomb_bits) == 0; 748 if (is_used) { 749 log.debug("%{d} => {}", .{ inst, result }); 750 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 751 branch.inst_table.putAssumeCapacityNoClobber(inst, result); 752 753 switch (result) { 754 .register => |reg| { 755 // In some cases (such as bitcast), an operand 756 // may be the same MCValue as the result. If 757 // that operand died and was a register, it 758 // was freed by processDeath. We have to 759 // "re-allocate" the register. 760 if (self.register_manager.isRegFree(reg)) { 761 self.register_manager.getRegAssumeFree(reg, inst); 762 } 763 }, 764 else => {}, 765 } 766 } 767 self.finishAirBookkeeping(); 768 } 769 770 fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { 771 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table; 772 try table.ensureUnusedCapacity(self.gpa, additional_count); 773 } 774 775 /// Adds a Type to the .debug_info at the current position. The bytes will be populated later, 776 /// after codegen for this symbol is done. 777 fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { 778 switch (self.debug_output) { 779 .dwarf => |dbg_out| { 780 assert(ty.hasRuntimeBits()); 781 const index = dbg_out.dbg_info.items.len; 782 try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 783 784 const gop = try dbg_out.dbg_info_type_relocs.getOrPutContext(self.gpa, ty, .{ 785 .target = self.target.*, 786 }); 787 if (!gop.found_existing) { 788 gop.value_ptr.* = .{ 789 .off = undefined, 790 .relocs = .{}, 791 }; 792 } 793 try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index)); 794 }, 795 .plan9 => {}, 796 .none => {}, 797 } 798 } 799 800 fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 { 801 if (abi_align > self.stack_align) 802 self.stack_align = abi_align; 803 // TODO find a free slot instead of always appending 804 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size; 805 self.next_stack_offset = offset; 806 self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset); 807 try self.stack.putNoClobber(self.gpa, offset, .{ 808 .inst = inst, 809 .size = abi_size, 810 }); 811 return offset; 812 } 813 814 /// Use a pointer instruction as the basis for allocating stack memory. 815 fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { 816 const elem_ty = self.air.typeOfIndex(inst).elemType(); 817 818 if (!elem_ty.hasRuntimeBits()) { 819 // return the stack offset 0. Stack offset 0 will be where all 820 // zero-sized stack allocations live as non-zero-sized 821 // allocations will always have an offset > 0. 822 return @as(u32, 0); 823 } 824 825 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { 826 const mod = self.bin_file.options.module.?; 827 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); 828 }; 829 // TODO swap this for inst.ty.ptrAlign 830 const abi_align = elem_ty.abiAlignment(self.target.*); 831 return self.allocMem(inst, abi_size, abi_align); 832 } 833 834 fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { 835 const elem_ty = self.air.typeOfIndex(inst); 836 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { 837 const mod = self.bin_file.options.module.?; 838 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); 839 }; 840 const abi_align = elem_ty.abiAlignment(self.target.*); 841 if (abi_align > self.stack_align) 842 self.stack_align = abi_align; 843 844 if (reg_ok) { 845 // Make sure the type can fit in a register before we try to allocate one. 846 if (abi_size <= 8) { 847 if (self.register_manager.tryAllocReg(inst)) |reg| { 848 return MCValue{ .register = registerAlias(reg, abi_size) }; 849 } 850 } 851 } 852 const stack_offset = try self.allocMem(inst, abi_size, abi_align); 853 return MCValue{ .stack_offset = stack_offset }; 854 } 855 856 pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { 857 const stack_mcv = try self.allocRegOrMem(inst, false); 858 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); 859 const reg_mcv = self.getResolvedInstValue(inst); 860 assert(reg == toCanonicalReg(reg_mcv.register)); 861 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 862 try branch.inst_table.put(self.gpa, inst, stack_mcv); 863 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); 864 } 865 866 /// Save the current instruction stored in the compare flags if 867 /// occupied 868 fn spillCompareFlagsIfOccupied(self: *Self) !void { 869 if (self.compare_flags_inst) |inst_to_save| { 870 const mcv = self.getResolvedInstValue(inst_to_save); 871 assert(mcv == .compare_flags_signed or mcv == .compare_flags_unsigned); 872 873 const new_mcv = try self.allocRegOrMem(inst_to_save, true); 874 try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); 875 log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); 876 877 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 878 try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); 879 880 self.compare_flags_inst = null; 881 } 882 } 883 884 /// Copies a value to a register without tracking the register. The register is not considered 885 /// allocated. A second call to `copyToTmpRegister` may return the same register. 886 /// This can have a side effect of spilling instructions to the stack to free up a register. 887 fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { 888 const raw_reg = try self.register_manager.allocReg(null); 889 const reg = registerAlias(raw_reg, ty.abiSize(self.target.*)); 890 try self.genSetReg(ty, reg, mcv); 891 return reg; 892 } 893 894 /// Allocates a new register and copies `mcv` into it. 895 /// `reg_owner` is the instruction that gets associated with the register in the register table. 896 /// This can have a side effect of spilling instructions to the stack to free up a register. 897 fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { 898 const raw_reg = try self.register_manager.allocReg(reg_owner); 899 const ty = self.air.typeOfIndex(reg_owner); 900 const reg = registerAlias(raw_reg, ty.abiSize(self.target.*)); 901 try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); 902 return MCValue{ .register = reg }; 903 } 904 905 fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { 906 const stack_offset = try self.allocMemPtr(inst); 907 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); 908 } 909 910 fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { 911 const stack_offset = try self.allocMemPtr(inst); 912 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); 913 } 914 915 fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { 916 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 917 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch}); 918 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 919 } 920 921 fn airFpext(self: *Self, inst: Air.Inst.Index) !void { 922 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 923 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch}); 924 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 925 } 926 927 fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { 928 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 929 if (self.liveness.isUnused(inst)) 930 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); 931 932 const operand_ty = self.air.typeOf(ty_op.operand); 933 const operand = try self.resolveInst(ty_op.operand); 934 const info_a = operand_ty.intInfo(self.target.*); 935 const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*); 936 if (info_a.signedness != info_b.signedness) 937 return self.fail("TODO gen intcast sign safety in semantic analysis", .{}); 938 939 if (info_a.bits == info_b.bits) 940 return self.finishAir(inst, operand, .{ ty_op.operand, .none, .none }); 941 942 return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch}); 943 } 944 945 fn truncRegister( 946 self: *Self, 947 operand_reg: Register, 948 dest_reg: Register, 949 int_signedness: std.builtin.Signedness, 950 int_bits: u16, 951 ) !void { 952 switch (int_bits) { 953 1...31, 33...63 => { 954 _ = try self.addInst(.{ 955 .tag = switch (int_signedness) { 956 .signed => .sbfx, 957 .unsigned => .ubfx, 958 }, 959 .data = .{ .rr_lsb_width = .{ 960 .rd = dest_reg, 961 .rn = operand_reg, 962 .lsb = 0, 963 .width = @intCast(u6, int_bits), 964 } }, 965 }); 966 }, 967 32, 64 => { 968 _ = try self.addInst(.{ 969 .tag = .mov_register, 970 .data = .{ .rr = .{ 971 .rd = dest_reg, 972 .rn = operand_reg, 973 } }, 974 }); 975 }, 976 else => unreachable, 977 } 978 } 979 980 fn trunc( 981 self: *Self, 982 maybe_inst: ?Air.Inst.Index, 983 operand: MCValue, 984 operand_ty: Type, 985 dest_ty: Type, 986 ) !MCValue { 987 const info_a = operand_ty.intInfo(self.target.*); 988 const info_b = dest_ty.intInfo(self.target.*); 989 990 if (info_b.bits <= 64) { 991 const operand_reg = switch (operand) { 992 .register => |r| r, 993 else => operand_reg: { 994 if (info_a.bits <= 64) { 995 const raw_reg = try self.copyToTmpRegister(operand_ty, operand); 996 break :operand_reg registerAlias(raw_reg, operand_ty.abiSize(self.target.*)); 997 } else { 998 return self.fail("TODO load least significant word into register", .{}); 999 } 1000 }, 1001 }; 1002 self.register_manager.freezeRegs(&.{operand_reg}); 1003 defer self.register_manager.unfreezeRegs(&.{operand_reg}); 1004 1005 const dest_reg = if (maybe_inst) |inst| blk: { 1006 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1007 1008 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { 1009 break :blk registerAlias(operand_reg, dest_ty.abiSize(self.target.*)); 1010 } else { 1011 const raw_reg = try self.register_manager.allocReg(inst); 1012 break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); 1013 } 1014 } else blk: { 1015 const raw_reg = try self.register_manager.allocReg(null); 1016 break :blk registerAlias(raw_reg, dest_ty.abiSize(self.target.*)); 1017 }; 1018 1019 try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits); 1020 1021 return MCValue{ .register = dest_reg }; 1022 } else { 1023 return self.fail("TODO: truncate to ints > 32 bits", .{}); 1024 } 1025 } 1026 1027 fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { 1028 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1029 const operand = try self.resolveInst(ty_op.operand); 1030 const operand_ty = self.air.typeOf(ty_op.operand); 1031 const dest_ty = self.air.typeOfIndex(inst); 1032 1033 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { 1034 break :blk try self.trunc(inst, operand, operand_ty, dest_ty); 1035 }; 1036 1037 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1038 } 1039 1040 fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void { 1041 const un_op = self.air.instructions.items(.data)[inst].un_op; 1042 const operand = try self.resolveInst(un_op); 1043 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand; 1044 return self.finishAir(inst, result, .{ un_op, .none, .none }); 1045 } 1046 1047 fn airNot(self: *Self, inst: Air.Inst.Index) !void { 1048 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1049 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1050 const operand = try self.resolveInst(ty_op.operand); 1051 const operand_ty = self.air.typeOf(ty_op.operand); 1052 switch (operand) { 1053 .dead => unreachable, 1054 .unreach => unreachable, 1055 .compare_flags_unsigned => |op| { 1056 const r = MCValue{ 1057 .compare_flags_unsigned = switch (op) { 1058 .gte => .lt, 1059 .gt => .lte, 1060 .neq => .eq, 1061 .lt => .gte, 1062 .lte => .gt, 1063 .eq => .neq, 1064 }, 1065 }; 1066 break :result r; 1067 }, 1068 .compare_flags_signed => |op| { 1069 const r = MCValue{ 1070 .compare_flags_signed = switch (op) { 1071 .gte => .lt, 1072 .gt => .lte, 1073 .neq => .eq, 1074 .lt => .gte, 1075 .lte => .gt, 1076 .eq => .neq, 1077 }, 1078 }; 1079 break :result r; 1080 }, 1081 else => { 1082 switch (operand_ty.zigTypeTag()) { 1083 .Bool => { 1084 // TODO convert this to mvn + and 1085 const op_reg = switch (operand) { 1086 .register => |r| r, 1087 else => try self.copyToTmpRegister(operand_ty, operand), 1088 }; 1089 self.register_manager.freezeRegs(&.{op_reg}); 1090 defer self.register_manager.unfreezeRegs(&.{op_reg}); 1091 1092 const dest_reg = blk: { 1093 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { 1094 break :blk op_reg; 1095 } 1096 1097 const raw_reg = try self.register_manager.allocReg(null); 1098 break :blk raw_reg.to32(); 1099 }; 1100 1101 _ = try self.addInst(.{ 1102 .tag = .eor_immediate, 1103 .data = .{ .rr_bitmask = .{ 1104 .rd = dest_reg, 1105 .rn = op_reg, 1106 .imms = 0b000000, 1107 .immr = 0b000000, 1108 .n = 0b0, 1109 } }, 1110 }); 1111 1112 break :result MCValue{ .register = dest_reg }; 1113 }, 1114 .Vector => return self.fail("TODO bitwise not for vectors", .{}), 1115 .Int => { 1116 const int_info = operand_ty.intInfo(self.target.*); 1117 if (int_info.bits <= 64) { 1118 const op_reg = switch (operand) { 1119 .register => |r| r, 1120 else => try self.copyToTmpRegister(operand_ty, operand), 1121 }; 1122 self.register_manager.freezeRegs(&.{op_reg}); 1123 defer self.register_manager.unfreezeRegs(&.{op_reg}); 1124 1125 const dest_reg = blk: { 1126 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { 1127 break :blk op_reg; 1128 } 1129 1130 const raw_reg = try self.register_manager.allocReg(null); 1131 break :blk registerAlias(raw_reg, operand_ty.abiSize(self.target.*)); 1132 }; 1133 1134 _ = try self.addInst(.{ 1135 .tag = .mvn, 1136 .data = .{ .rr_imm6_logical_shift = .{ 1137 .rd = dest_reg, 1138 .rm = op_reg, 1139 .imm6 = 0, 1140 .shift = .lsl, 1141 } }, 1142 }); 1143 1144 try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); 1145 1146 break :result MCValue{ .register = dest_reg }; 1147 } else { 1148 return self.fail("TODO AArch64 not on integers > u64/i64", .{}); 1149 } 1150 }, 1151 else => unreachable, 1152 } 1153 }, 1154 } 1155 }; 1156 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1157 } 1158 1159 fn airMin(self: *Self, inst: Air.Inst.Index) !void { 1160 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1161 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement min for {}", .{self.target.cpu.arch}); 1162 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1163 } 1164 1165 fn airMax(self: *Self, inst: Air.Inst.Index) !void { 1166 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1167 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement max for {}", .{self.target.cpu.arch}); 1168 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1169 } 1170 1171 fn airSlice(self: *Self, inst: Air.Inst.Index) !void { 1172 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 1173 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 1174 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1175 const ptr = try self.resolveInst(bin_op.lhs); 1176 const ptr_ty = self.air.typeOf(bin_op.lhs); 1177 const len = try self.resolveInst(bin_op.rhs); 1178 const len_ty = self.air.typeOf(bin_op.rhs); 1179 1180 const ptr_bits = self.target.cpu.arch.ptrBitWidth(); 1181 const ptr_bytes = @divExact(ptr_bits, 8); 1182 1183 const stack_offset = try self.allocMem(inst, ptr_bytes * 2, ptr_bytes * 2); 1184 try self.genSetStack(ptr_ty, stack_offset, ptr); 1185 try self.genSetStack(len_ty, stack_offset - ptr_bytes, len); 1186 break :result MCValue{ .stack_offset = stack_offset }; 1187 }; 1188 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1189 } 1190 1191 /// Don't call this function directly. Use binOp instead. 1192 /// 1193 /// Calling this function signals an intention to generate a Mir 1194 /// instruction of the form 1195 /// 1196 /// op dest, lhs, rhs 1197 /// 1198 /// Asserts that generating an instruction of that form is possible. 1199 fn binOpRegister( 1200 self: *Self, 1201 mir_tag: Mir.Inst.Tag, 1202 maybe_inst: ?Air.Inst.Index, 1203 lhs: MCValue, 1204 rhs: MCValue, 1205 lhs_ty: Type, 1206 rhs_ty: Type, 1207 ) !MCValue { 1208 const lhs_is_register = lhs == .register; 1209 const rhs_is_register = rhs == .register; 1210 1211 if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); 1212 if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register}); 1213 1214 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 1215 1216 const lhs_reg = if (lhs_is_register) lhs.register else blk: { 1217 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { 1218 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1219 break :inst Air.refToIndex(bin_op.lhs).?; 1220 } else null; 1221 1222 const raw_reg = try self.register_manager.allocReg(track_inst); 1223 const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); 1224 self.register_manager.freezeRegs(&.{reg}); 1225 1226 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); 1227 1228 break :blk reg; 1229 }; 1230 defer self.register_manager.unfreezeRegs(&.{lhs_reg}); 1231 1232 const rhs_reg = if (rhs_is_register) rhs.register else blk: { 1233 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { 1234 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1235 break :inst Air.refToIndex(bin_op.rhs).?; 1236 } else null; 1237 1238 const raw_reg = try self.register_manager.allocReg(track_inst); 1239 const reg = registerAlias(raw_reg, rhs_ty.abiAlignment(self.target.*)); 1240 self.register_manager.freezeRegs(&.{reg}); 1241 1242 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); 1243 1244 break :blk reg; 1245 }; 1246 defer self.register_manager.unfreezeRegs(&.{rhs_reg}); 1247 1248 const dest_reg = if (maybe_inst) |inst| blk: { 1249 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1250 1251 if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { 1252 break :blk lhs_reg; 1253 } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { 1254 break :blk rhs_reg; 1255 } else { 1256 const raw_reg = try self.register_manager.allocReg(inst); 1257 break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); 1258 } 1259 } else try self.register_manager.allocReg(null); 1260 1261 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); 1262 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); 1263 1264 const mir_data: Mir.Inst.Data = switch (mir_tag) { 1265 .add_shifted_register, 1266 .sub_shifted_register, 1267 => .{ .rrr_imm6_shift = .{ 1268 .rd = dest_reg, 1269 .rn = lhs_reg, 1270 .rm = rhs_reg, 1271 .imm6 = 0, 1272 .shift = .lsl, 1273 } }, 1274 .cmp_shifted_register => .{ .rr_imm6_shift = .{ 1275 .rn = lhs_reg, 1276 .rm = rhs_reg, 1277 .imm6 = 0, 1278 .shift = .lsl, 1279 } }, 1280 .mul, 1281 .lsl_register, 1282 .asr_register, 1283 .lsr_register, 1284 => .{ .rrr = .{ 1285 .rd = dest_reg, 1286 .rn = lhs_reg, 1287 .rm = rhs_reg, 1288 } }, 1289 .and_shifted_register, 1290 .orr_shifted_register, 1291 .eor_shifted_register, 1292 => .{ .rrr_imm6_logical_shift = .{ 1293 .rd = dest_reg, 1294 .rn = lhs_reg, 1295 .rm = rhs_reg, 1296 .imm6 = 0, 1297 .shift = .lsl, 1298 } }, 1299 else => unreachable, 1300 }; 1301 1302 _ = try self.addInst(.{ 1303 .tag = mir_tag, 1304 .data = mir_data, 1305 }); 1306 1307 return MCValue{ .register = dest_reg }; 1308 } 1309 1310 /// Don't call this function directly. Use binOp instead. 1311 /// 1312 /// Calling this function signals an intention to generate a Mir 1313 /// instruction of the form 1314 /// 1315 /// op dest, lhs, #rhs_imm 1316 /// 1317 /// Set lhs_and_rhs_swapped to true iff inst.bin_op.lhs corresponds to 1318 /// rhs and vice versa. This parameter is only used when maybe_inst != 1319 /// null. 1320 /// 1321 /// Asserts that generating an instruction of that form is possible. 1322 fn binOpImmediate( 1323 self: *Self, 1324 mir_tag: Mir.Inst.Tag, 1325 maybe_inst: ?Air.Inst.Index, 1326 lhs: MCValue, 1327 rhs: MCValue, 1328 lhs_ty: Type, 1329 lhs_and_rhs_swapped: bool, 1330 ) !MCValue { 1331 const lhs_is_register = lhs == .register; 1332 1333 if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register}); 1334 1335 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 1336 1337 const lhs_reg = if (lhs_is_register) lhs.register else blk: { 1338 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { 1339 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1340 break :inst Air.refToIndex( 1341 if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, 1342 ).?; 1343 } else null; 1344 1345 const raw_reg = try self.register_manager.allocReg(track_inst); 1346 const reg = registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); 1347 self.register_manager.freezeRegs(&.{reg}); 1348 1349 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); 1350 1351 break :blk reg; 1352 }; 1353 defer self.register_manager.unfreezeRegs(&.{lhs_reg}); 1354 1355 const dest_reg = switch (mir_tag) { 1356 .cmp_immediate => undefined, // cmp has no destination register 1357 else => if (maybe_inst) |inst| blk: { 1358 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1359 1360 if (lhs_is_register and self.reuseOperand( 1361 inst, 1362 if (lhs_and_rhs_swapped) bin_op.rhs else bin_op.lhs, 1363 if (lhs_and_rhs_swapped) 1 else 0, 1364 lhs, 1365 )) { 1366 break :blk lhs_reg; 1367 } else { 1368 const raw_reg = try self.register_manager.allocReg(inst); 1369 break :blk registerAlias(raw_reg, lhs_ty.abiSize(self.target.*)); 1370 } 1371 } else try self.register_manager.allocReg(null), 1372 }; 1373 1374 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); 1375 1376 const mir_data: Mir.Inst.Data = switch (mir_tag) { 1377 .add_immediate, 1378 .sub_immediate, 1379 => .{ .rr_imm12_sh = .{ 1380 .rd = dest_reg, 1381 .rn = lhs_reg, 1382 .imm12 = @intCast(u12, rhs.immediate), 1383 } }, 1384 .lsl_immediate, 1385 .asr_immediate, 1386 .lsr_immediate, 1387 => .{ .rr_shift = .{ 1388 .rd = dest_reg, 1389 .rn = lhs_reg, 1390 .shift = @intCast(u6, rhs.immediate), 1391 } }, 1392 .cmp_immediate => .{ .r_imm12_sh = .{ 1393 .rn = lhs_reg, 1394 .imm12 = @intCast(u12, rhs.immediate), 1395 } }, 1396 else => unreachable, 1397 }; 1398 1399 _ = try self.addInst(.{ 1400 .tag = mir_tag, 1401 .data = mir_data, 1402 }); 1403 1404 return MCValue{ .register = dest_reg }; 1405 } 1406 1407 /// For all your binary operation needs, this function will generate 1408 /// the corresponding Mir instruction(s). Returns the location of the 1409 /// result. 1410 /// 1411 /// If the binary operation itself happens to be an Air instruction, 1412 /// pass the corresponding index in the inst parameter. That helps 1413 /// this function do stuff like reusing operands. 1414 /// 1415 /// This function does not do any lowering to Mir itself, but instead 1416 /// looks at the lhs and rhs and determines which kind of lowering 1417 /// would be best suitable and then delegates the lowering to other 1418 /// functions. 1419 fn binOp( 1420 self: *Self, 1421 tag: Air.Inst.Tag, 1422 maybe_inst: ?Air.Inst.Index, 1423 lhs: MCValue, 1424 rhs: MCValue, 1425 lhs_ty: Type, 1426 rhs_ty: Type, 1427 ) InnerError!MCValue { 1428 const mod = self.bin_file.options.module.?; 1429 switch (tag) { 1430 .add, 1431 .sub, 1432 .cmp_eq, 1433 => { 1434 switch (lhs_ty.zigTypeTag()) { 1435 .Float => return self.fail("TODO binary operations on floats", .{}), 1436 .Vector => return self.fail("TODO binary operations on vectors", .{}), 1437 .Int => { 1438 assert(lhs_ty.eql(rhs_ty, mod)); 1439 const int_info = lhs_ty.intInfo(self.target.*); 1440 if (int_info.bits <= 64) { 1441 // Only say yes if the operation is 1442 // commutative, i.e. we can swap both of the 1443 // operands 1444 const lhs_immediate_ok = switch (tag) { 1445 .add => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12), 1446 .sub, .cmp_eq => false, 1447 else => unreachable, 1448 }; 1449 const rhs_immediate_ok = switch (tag) { 1450 .add, 1451 .sub, 1452 .cmp_eq, 1453 => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12), 1454 else => unreachable, 1455 }; 1456 1457 const mir_tag_register: Mir.Inst.Tag = switch (tag) { 1458 .add => .add_shifted_register, 1459 .sub => .sub_shifted_register, 1460 .cmp_eq => .cmp_shifted_register, 1461 else => unreachable, 1462 }; 1463 const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { 1464 .add => .add_immediate, 1465 .sub => .sub_immediate, 1466 .cmp_eq => .cmp_immediate, 1467 else => unreachable, 1468 }; 1469 1470 if (rhs_immediate_ok) { 1471 return try self.binOpImmediate(mir_tag_immediate, maybe_inst, lhs, rhs, lhs_ty, false); 1472 } else if (lhs_immediate_ok) { 1473 // swap lhs and rhs 1474 return try self.binOpImmediate(mir_tag_immediate, maybe_inst, rhs, lhs, rhs_ty, true); 1475 } else { 1476 return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); 1477 } 1478 } else { 1479 return self.fail("TODO binary operations on int with bits > 64", .{}); 1480 } 1481 }, 1482 else => unreachable, 1483 } 1484 }, 1485 .mul => { 1486 switch (lhs_ty.zigTypeTag()) { 1487 .Vector => return self.fail("TODO binary operations on vectors", .{}), 1488 .Int => { 1489 assert(lhs_ty.eql(rhs_ty, mod)); 1490 const int_info = lhs_ty.intInfo(self.target.*); 1491 if (int_info.bits <= 64) { 1492 // TODO add optimisations for multiplication 1493 // with immediates, for example a * 2 can be 1494 // lowered to a << 1 1495 return try self.binOpRegister(.mul, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); 1496 } else { 1497 return self.fail("TODO binary operations on int with bits > 64", .{}); 1498 } 1499 }, 1500 else => unreachable, 1501 } 1502 }, 1503 .addwrap, 1504 .subwrap, 1505 .mulwrap, 1506 => { 1507 const base_tag: Air.Inst.Tag = switch (tag) { 1508 .addwrap => .add, 1509 .subwrap => .sub, 1510 .mulwrap => .mul, 1511 else => unreachable, 1512 }; 1513 1514 // Generate an add/sub/mul 1515 const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); 1516 1517 // Truncate if necessary 1518 switch (lhs_ty.zigTypeTag()) { 1519 .Vector => return self.fail("TODO binary operations on vectors", .{}), 1520 .Int => { 1521 const int_info = lhs_ty.intInfo(self.target.*); 1522 if (int_info.bits <= 64) { 1523 const result_reg = result.register; 1524 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); 1525 return result; 1526 } else { 1527 return self.fail("TODO binary operations on integers > u64/i64", .{}); 1528 } 1529 }, 1530 else => unreachable, 1531 } 1532 }, 1533 .bit_and, 1534 .bit_or, 1535 .xor, 1536 => { 1537 switch (lhs_ty.zigTypeTag()) { 1538 .Vector => return self.fail("TODO binary operations on vectors", .{}), 1539 .Int => { 1540 assert(lhs_ty.eql(rhs_ty, mod)); 1541 const int_info = lhs_ty.intInfo(self.target.*); 1542 if (int_info.bits <= 64) { 1543 // TODO implement bitwise operations with immediates 1544 const mir_tag: Mir.Inst.Tag = switch (tag) { 1545 .bit_and => .and_shifted_register, 1546 .bit_or => .orr_shifted_register, 1547 .xor => .eor_shifted_register, 1548 else => unreachable, 1549 }; 1550 1551 return try self.binOpRegister(mir_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); 1552 } else { 1553 return self.fail("TODO binary operations on int with bits > 64", .{}); 1554 } 1555 }, 1556 else => unreachable, 1557 } 1558 }, 1559 .shl_exact, 1560 .shr_exact, 1561 => { 1562 switch (lhs_ty.zigTypeTag()) { 1563 .Vector => return self.fail("TODO binary operations on vectors", .{}), 1564 .Int => { 1565 const int_info = lhs_ty.intInfo(self.target.*); 1566 if (int_info.bits <= 64) { 1567 const rhs_immediate_ok = rhs == .immediate; 1568 1569 const mir_tag_register: Mir.Inst.Tag = switch (tag) { 1570 .shl_exact => .lsl_register, 1571 .shr_exact => switch (int_info.signedness) { 1572 .signed => Mir.Inst.Tag.asr_register, 1573 .unsigned => Mir.Inst.Tag.lsr_register, 1574 }, 1575 else => unreachable, 1576 }; 1577 const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { 1578 .shl_exact => .lsl_immediate, 1579 .shr_exact => switch (int_info.signedness) { 1580 .signed => Mir.Inst.Tag.asr_immediate, 1581 .unsigned => Mir.Inst.Tag.lsr_immediate, 1582 }, 1583 else => unreachable, 1584 }; 1585 1586 if (rhs_immediate_ok) { 1587 return try self.binOpImmediate(mir_tag_immediate, maybe_inst, lhs, rhs, lhs_ty, false); 1588 } else { 1589 return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); 1590 } 1591 } else { 1592 return self.fail("TODO binary operations on int with bits > 64", .{}); 1593 } 1594 }, 1595 else => unreachable, 1596 } 1597 }, 1598 .shl, 1599 .shr, 1600 => { 1601 const base_tag: Air.Inst.Tag = switch (tag) { 1602 .shl => .shl_exact, 1603 .shr => .shr_exact, 1604 else => unreachable, 1605 }; 1606 1607 // Generate a shl_exact/shr_exact 1608 const result = try self.binOp(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); 1609 1610 // Truncate if necessary 1611 switch (tag) { 1612 .shr => return result, 1613 .shl => switch (lhs_ty.zigTypeTag()) { 1614 .Vector => return self.fail("TODO binary operations on vectors", .{}), 1615 .Int => { 1616 const int_info = lhs_ty.intInfo(self.target.*); 1617 if (int_info.bits <= 64) { 1618 const result_reg = result.register; 1619 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); 1620 return result; 1621 } else { 1622 return self.fail("TODO binary operations on integers > u64/i64", .{}); 1623 } 1624 }, 1625 else => unreachable, 1626 }, 1627 else => unreachable, 1628 } 1629 }, 1630 .bool_and, 1631 .bool_or, 1632 => { 1633 switch (lhs_ty.zigTypeTag()) { 1634 .Bool => { 1635 assert(lhs != .immediate); // should have been handled by Sema 1636 assert(rhs != .immediate); // should have been handled by Sema 1637 1638 const mir_tag_register: Mir.Inst.Tag = switch (tag) { 1639 .bool_and => .and_shifted_register, 1640 .bool_or => .orr_shifted_register, 1641 else => unreachable, 1642 }; 1643 1644 return try self.binOpRegister(mir_tag_register, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); 1645 }, 1646 else => unreachable, 1647 } 1648 }, 1649 .ptr_add, 1650 .ptr_sub, 1651 => { 1652 switch (lhs_ty.zigTypeTag()) { 1653 .Pointer => { 1654 const ptr_ty = lhs_ty; 1655 const elem_ty = switch (ptr_ty.ptrSize()) { 1656 .One => ptr_ty.childType().childType(), // ptr to array, so get array element type 1657 else => ptr_ty.childType(), 1658 }; 1659 const elem_size = elem_ty.abiSize(self.target.*); 1660 1661 if (elem_size == 1) { 1662 const base_tag: Mir.Inst.Tag = switch (tag) { 1663 .ptr_add => .add_shifted_register, 1664 .ptr_sub => .sub_shifted_register, 1665 else => unreachable, 1666 }; 1667 1668 return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); 1669 } else { 1670 // convert the offset into a byte offset by 1671 // multiplying it with elem_size 1672 const offset = try self.binOp(.mul, null, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize); 1673 const addr = try self.binOp(tag, null, lhs, offset, Type.initTag(.manyptr_u8), Type.usize); 1674 return addr; 1675 } 1676 }, 1677 else => unreachable, 1678 } 1679 }, 1680 else => unreachable, 1681 } 1682 } 1683 1684 fn airBinOp(self: *Self, inst: Air.Inst.Index) !void { 1685 const tag = self.air.instructions.items(.tag)[inst]; 1686 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1687 const lhs = try self.resolveInst(bin_op.lhs); 1688 const rhs = try self.resolveInst(bin_op.rhs); 1689 const lhs_ty = self.air.typeOf(bin_op.lhs); 1690 const rhs_ty = self.air.typeOf(bin_op.rhs); 1691 1692 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); 1693 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1694 } 1695 1696 fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { 1697 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1698 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch}); 1699 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1700 } 1701 1702 fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { 1703 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1704 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch}); 1705 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1706 } 1707 1708 fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { 1709 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1710 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch}); 1711 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1712 } 1713 1714 fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { 1715 _ = inst; 1716 return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch}); 1717 } 1718 1719 fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { 1720 _ = inst; 1721 return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch}); 1722 } 1723 1724 fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { 1725 _ = inst; 1726 return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch}); 1727 } 1728 1729 fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { 1730 _ = inst; 1731 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch}); 1732 } 1733 1734 fn airDiv(self: *Self, inst: Air.Inst.Index) !void { 1735 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1736 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch}); 1737 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1738 } 1739 1740 fn airRem(self: *Self, inst: Air.Inst.Index) !void { 1741 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1742 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement rem for {}", .{self.target.cpu.arch}); 1743 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1744 } 1745 1746 fn airMod(self: *Self, inst: Air.Inst.Index) !void { 1747 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1748 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mod for {}", .{self.target.cpu.arch}); 1749 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1750 } 1751 1752 fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { 1753 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1754 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}); 1755 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1756 } 1757 1758 fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { 1759 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1760 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch}); 1761 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1762 } 1763 1764 fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { 1765 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1766 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}); 1767 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1768 } 1769 1770 fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { 1771 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1772 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch}); 1773 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1774 } 1775 1776 fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { 1777 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1778 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1779 const error_union_ty = self.air.typeOf(ty_op.operand); 1780 const payload_ty = error_union_ty.errorUnionPayload(); 1781 const mcv = try self.resolveInst(ty_op.operand); 1782 if (!payload_ty.hasRuntimeBits()) break :result mcv; 1783 1784 return self.fail("TODO implement unwrap error union error for non-empty payloads", .{}); 1785 }; 1786 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1787 } 1788 1789 fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { 1790 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1791 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1792 const error_union_ty = self.air.typeOf(ty_op.operand); 1793 const payload_ty = error_union_ty.errorUnionPayload(); 1794 if (!payload_ty.hasRuntimeBits()) break :result MCValue.none; 1795 1796 return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{}); 1797 }; 1798 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1799 } 1800 1801 // *(E!T) -> E 1802 fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void { 1803 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1804 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}); 1805 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1806 } 1807 1808 // *(E!T) -> *T 1809 fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { 1810 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1811 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}); 1812 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1813 } 1814 1815 fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { 1816 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1817 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch}); 1818 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1819 } 1820 1821 fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { 1822 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1823 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1824 const optional_ty = self.air.typeOfIndex(inst); 1825 1826 // Optional with a zero-bit payload type is just a boolean true 1827 if (optional_ty.abiSize(self.target.*) == 1) 1828 break :result MCValue{ .immediate = 1 }; 1829 1830 return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}); 1831 }; 1832 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1833 } 1834 1835 /// T to E!T 1836 fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { 1837 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1838 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch}); 1839 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1840 } 1841 1842 /// E to E!T 1843 fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { 1844 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1845 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1846 const error_union_ty = self.air.getRefType(ty_op.ty); 1847 const payload_ty = error_union_ty.errorUnionPayload(); 1848 const mcv = try self.resolveInst(ty_op.operand); 1849 if (!payload_ty.hasRuntimeBits()) break :result mcv; 1850 1851 return self.fail("TODO implement wrap errunion error for non-empty payloads", .{}); 1852 }; 1853 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1854 } 1855 1856 fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { 1857 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1858 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1859 const mcv = try self.resolveInst(ty_op.operand); 1860 switch (mcv) { 1861 .dead, .unreach, .none => unreachable, 1862 .register => unreachable, // a slice doesn't fit in one register 1863 .stack_offset => |off| { 1864 break :result MCValue{ .stack_offset = off }; 1865 }, 1866 .memory => |addr| { 1867 break :result MCValue{ .memory = addr }; 1868 }, 1869 else => return self.fail("TODO implement slice_len for {}", .{mcv}), 1870 } 1871 }; 1872 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1873 } 1874 1875 fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { 1876 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1877 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1878 const ptr_bits = self.target.cpu.arch.ptrBitWidth(); 1879 const ptr_bytes = @divExact(ptr_bits, 8); 1880 const mcv = try self.resolveInst(ty_op.operand); 1881 switch (mcv) { 1882 .dead, .unreach, .none => unreachable, 1883 .register => unreachable, // a slice doesn't fit in one register 1884 .stack_offset => |off| { 1885 break :result MCValue{ .stack_offset = off - ptr_bytes }; 1886 }, 1887 .memory => |addr| { 1888 break :result MCValue{ .memory = addr + ptr_bytes }; 1889 }, 1890 else => return self.fail("TODO implement slice_len for {}", .{mcv}), 1891 } 1892 }; 1893 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1894 } 1895 1896 fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void { 1897 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1898 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1899 const ptr_bits = self.target.cpu.arch.ptrBitWidth(); 1900 const ptr_bytes = @divExact(ptr_bits, 8); 1901 const mcv = try self.resolveInst(ty_op.operand); 1902 switch (mcv) { 1903 .dead, .unreach, .none => unreachable, 1904 .ptr_stack_offset => |off| { 1905 break :result MCValue{ .ptr_stack_offset = off - ptr_bytes }; 1906 }, 1907 else => return self.fail("TODO implement ptr_slice_len_ptr for {}", .{mcv}), 1908 } 1909 }; 1910 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1911 } 1912 1913 fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { 1914 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1915 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1916 const mcv = try self.resolveInst(ty_op.operand); 1917 switch (mcv) { 1918 .dead, .unreach, .none => unreachable, 1919 .ptr_stack_offset => |off| { 1920 break :result MCValue{ .ptr_stack_offset = off }; 1921 }, 1922 else => return self.fail("TODO implement ptr_slice_len_ptr for {}", .{mcv}), 1923 } 1924 }; 1925 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1926 } 1927 1928 fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { 1929 const is_volatile = false; // TODO 1930 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1931 1932 if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); 1933 const result: MCValue = result: { 1934 const slice_mcv = try self.resolveInst(bin_op.lhs); 1935 1936 // TODO optimize for the case where the index is a constant, 1937 // i.e. index_mcv == .immediate 1938 const index_mcv = try self.resolveInst(bin_op.rhs); 1939 const index_is_register = index_mcv == .register; 1940 1941 const slice_ty = self.air.typeOf(bin_op.lhs); 1942 const elem_ty = slice_ty.childType(); 1943 const elem_size = elem_ty.abiSize(self.target.*); 1944 1945 var buf: Type.SlicePtrFieldTypeBuffer = undefined; 1946 const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); 1947 1948 if (index_is_register) self.register_manager.freezeRegs(&.{index_mcv.register}); 1949 defer if (index_is_register) self.register_manager.unfreezeRegs(&.{index_mcv.register}); 1950 1951 const base_mcv: MCValue = switch (slice_mcv) { 1952 .stack_offset => |off| .{ .register = try self.copyToTmpRegister(slice_ptr_field_type, .{ .stack_offset = off }) }, 1953 else => return self.fail("TODO slice_elem_val when slice is {}", .{slice_mcv}), 1954 }; 1955 self.register_manager.freezeRegs(&.{base_mcv.register}); 1956 1957 switch (elem_size) { 1958 else => { 1959 const dest = try self.allocRegOrMem(inst, true); 1960 const addr = try self.binOp(.ptr_add, null, base_mcv, index_mcv, slice_ty, Type.usize); 1961 try self.load(dest, addr, slice_ptr_field_type); 1962 1963 break :result dest; 1964 }, 1965 } 1966 }; 1967 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1968 } 1969 1970 fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { 1971 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 1972 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 1973 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_elem_ptr for {}", .{self.target.cpu.arch}); 1974 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); 1975 } 1976 1977 fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { 1978 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1979 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch}); 1980 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1981 } 1982 1983 fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { 1984 const is_volatile = false; // TODO 1985 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1986 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}); 1987 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1988 } 1989 1990 fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { 1991 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 1992 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 1993 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_ptr for {}", .{self.target.cpu.arch}); 1994 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); 1995 } 1996 1997 fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { 1998 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1999 _ = bin_op; 2000 return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch}); 2001 } 2002 2003 fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { 2004 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2005 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch}); 2006 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2007 } 2008 2009 fn airClz(self: *Self, inst: Air.Inst.Index) !void { 2010 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2011 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch}); 2012 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2013 } 2014 2015 fn airCtz(self: *Self, inst: Air.Inst.Index) !void { 2016 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2017 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch}); 2018 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2019 } 2020 2021 fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { 2022 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2023 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch}); 2024 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2025 } 2026 2027 fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { 2028 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2029 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); 2030 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2031 } 2032 2033 fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { 2034 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2035 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch}); 2036 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2037 } 2038 2039 fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { 2040 const un_op = self.air.instructions.items(.data)[inst].un_op; 2041 const result: MCValue = if (self.liveness.isUnused(inst)) 2042 .dead 2043 else 2044 return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch}); 2045 return self.finishAir(inst, result, .{ un_op, .none, .none }); 2046 } 2047 2048 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { 2049 if (!self.liveness.operandDies(inst, op_index)) 2050 return false; 2051 2052 switch (mcv) { 2053 .register => |reg| { 2054 // If it's in the registers table, need to associate the register with the 2055 // new instruction. 2056 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { 2057 if (!self.register_manager.isRegFree(reg)) { 2058 self.register_manager.registers[index] = inst; 2059 } 2060 } 2061 log.debug("%{d} => {} (reused)", .{ inst, reg }); 2062 }, 2063 .stack_offset => |off| { 2064 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off }); 2065 }, 2066 else => return false, 2067 } 2068 2069 // Prevent the operand deaths processing code from deallocating it. 2070 self.liveness.clearOperandDeath(inst, op_index); 2071 2072 // That makes us responsible for doing the rest of the stuff that processDeath would have done. 2073 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 2074 branch.inst_table.putAssumeCapacity(Air.refToIndex(operand).?, .dead); 2075 2076 return true; 2077 } 2078 2079 fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { 2080 const elem_ty = ptr_ty.elemType(); 2081 const elem_size = elem_ty.abiSize(self.target.*); 2082 2083 switch (ptr) { 2084 .none => unreachable, 2085 .undef => unreachable, 2086 .unreach => unreachable, 2087 .dead => unreachable, 2088 .compare_flags_unsigned => unreachable, 2089 .compare_flags_signed => unreachable, 2090 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), 2091 .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), 2092 .register => |addr_reg| { 2093 self.register_manager.freezeRegs(&.{addr_reg}); 2094 defer self.register_manager.unfreezeRegs(&.{addr_reg}); 2095 2096 switch (dst_mcv) { 2097 .dead => unreachable, 2098 .undef => unreachable, 2099 .compare_flags_signed, .compare_flags_unsigned => unreachable, 2100 .register => |dst_reg| { 2101 try self.genLdrRegister(dst_reg, addr_reg, elem_ty); 2102 }, 2103 .stack_offset => |off| { 2104 if (elem_size <= 8) { 2105 const raw_tmp_reg = try self.register_manager.allocReg(null); 2106 const tmp_reg = registerAlias(raw_tmp_reg, elem_size); 2107 self.register_manager.freezeRegs(&.{tmp_reg}); 2108 defer self.register_manager.unfreezeRegs(&.{tmp_reg}); 2109 2110 try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); 2111 try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); 2112 } else { 2113 // TODO optimize the register allocation 2114 const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }); 2115 self.register_manager.freezeRegs(®s); 2116 defer self.register_manager.unfreezeRegs(®s); 2117 2118 const src_reg = addr_reg; 2119 const dst_reg = regs[0]; 2120 const len_reg = regs[1]; 2121 const count_reg = regs[2]; 2122 const tmp_reg = regs[3]; 2123 2124 // sub dst_reg, fp, #off 2125 try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = off }); 2126 2127 // mov len, #elem_size 2128 try self.genSetReg(Type.usize, len_reg, .{ .immediate = elem_size }); 2129 2130 // memcpy(src, dst, len) 2131 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); 2132 } 2133 }, 2134 else => return self.fail("TODO load from register into {}", .{dst_mcv}), 2135 } 2136 }, 2137 .memory, 2138 .stack_offset, 2139 .got_load, 2140 .direct_load, 2141 => { 2142 const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr); 2143 try self.load(dst_mcv, .{ .register = addr_reg }, ptr_ty); 2144 }, 2145 } 2146 } 2147 2148 fn genInlineMemcpy( 2149 self: *Self, 2150 src: Register, 2151 dst: Register, 2152 len: Register, 2153 count: Register, 2154 tmp: Register, 2155 ) !void { 2156 // movz count, #0 2157 _ = try self.addInst(.{ 2158 .tag = .movz, 2159 .data = .{ .r_imm16_sh = .{ 2160 .rd = count, 2161 .imm16 = 0, 2162 } }, 2163 }); 2164 2165 // loop: 2166 // cmp count, len 2167 _ = try self.addInst(.{ 2168 .tag = .cmp_shifted_register, 2169 .data = .{ .rr_imm6_shift = .{ 2170 .rn = count, 2171 .rm = len, 2172 .imm6 = 0, 2173 .shift = .lsl, 2174 } }, 2175 }); 2176 2177 // bge end 2178 _ = try self.addInst(.{ 2179 .tag = .b_cond, 2180 .data = .{ .inst_cond = .{ 2181 .inst = @intCast(u32, self.mir_instructions.len + 5), 2182 .cond = .ge, 2183 } }, 2184 }); 2185 2186 // ldrb tmp, [src, count] 2187 _ = try self.addInst(.{ 2188 .tag = .ldrb_register, 2189 .data = .{ .load_store_register_register = .{ 2190 .rt = tmp, 2191 .rn = src, 2192 .offset = Instruction.LoadStoreOffset.reg(count).register, 2193 } }, 2194 }); 2195 2196 // strb tmp, [dest, count] 2197 _ = try self.addInst(.{ 2198 .tag = .strb_register, 2199 .data = .{ .load_store_register_register = .{ 2200 .rt = tmp, 2201 .rn = dst, 2202 .offset = Instruction.LoadStoreOffset.reg(count).register, 2203 } }, 2204 }); 2205 2206 // add count, count, #1 2207 _ = try self.addInst(.{ 2208 .tag = .add_immediate, 2209 .data = .{ .rr_imm12_sh = .{ 2210 .rd = count, 2211 .rn = count, 2212 .imm12 = 1, 2213 } }, 2214 }); 2215 2216 // b loop 2217 _ = try self.addInst(.{ 2218 .tag = .b, 2219 .data = .{ .inst = @intCast(u32, self.mir_instructions.len - 5) }, 2220 }); 2221 2222 // end: 2223 } 2224 2225 fn airLoad(self: *Self, inst: Air.Inst.Index) !void { 2226 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2227 const elem_ty = self.air.typeOfIndex(inst); 2228 const elem_size = elem_ty.abiSize(self.target.*); 2229 const result: MCValue = result: { 2230 if (!elem_ty.hasRuntimeBits()) 2231 break :result MCValue.none; 2232 2233 const ptr = try self.resolveInst(ty_op.operand); 2234 const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr(); 2235 if (self.liveness.isUnused(inst) and !is_volatile) 2236 break :result MCValue.dead; 2237 2238 const dst_mcv: MCValue = blk: { 2239 if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) { 2240 // The MCValue that holds the pointer can be re-used as the value. 2241 break :blk switch (ptr) { 2242 .register => |r| MCValue{ .register = registerAlias(r, elem_size) }, 2243 else => ptr, 2244 }; 2245 } else { 2246 break :blk try self.allocRegOrMem(inst, true); 2247 } 2248 }; 2249 try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand)); 2250 break :result dst_mcv; 2251 }; 2252 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2253 } 2254 2255 fn genLdrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void { 2256 const abi_size = ty.abiSize(self.target.*); 2257 2258 const tag: Mir.Inst.Tag = switch (abi_size) { 2259 1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb_immediate else .ldrb_immediate, 2260 2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh_immediate else .ldrh_immediate, 2261 4 => .ldr_immediate, 2262 8 => .ldr_immediate, 2263 3, 5, 6, 7 => return self.fail("TODO: genLdrRegister for more abi_sizes", .{}), 2264 else => unreachable, 2265 }; 2266 2267 _ = try self.addInst(.{ 2268 .tag = tag, 2269 .data = .{ .load_store_register_immediate = .{ 2270 .rt = value_reg, 2271 .rn = addr_reg, 2272 .offset = Instruction.LoadStoreOffset.none.immediate, 2273 } }, 2274 }); 2275 } 2276 2277 fn genStrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void { 2278 const abi_size = ty.abiSize(self.target.*); 2279 2280 const tag: Mir.Inst.Tag = switch (abi_size) { 2281 1 => .strb_immediate, 2282 2 => .strh_immediate, 2283 4, 8 => .str_immediate, 2284 3, 5, 6, 7 => return self.fail("TODO: genStrRegister for more abi_sizes", .{}), 2285 else => unreachable, 2286 }; 2287 2288 _ = try self.addInst(.{ 2289 .tag = tag, 2290 .data = .{ .load_store_register_immediate = .{ 2291 .rt = value_reg, 2292 .rn = addr_reg, 2293 .offset = Instruction.LoadStoreOffset.none.immediate, 2294 } }, 2295 }); 2296 } 2297 2298 fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { 2299 const abi_size = value_ty.abiSize(self.target.*); 2300 2301 switch (ptr) { 2302 .none => unreachable, 2303 .undef => unreachable, 2304 .unreach => unreachable, 2305 .dead => unreachable, 2306 .compare_flags_unsigned => unreachable, 2307 .compare_flags_signed => unreachable, 2308 .immediate => |imm| { 2309 try self.setRegOrMem(value_ty, .{ .memory = imm }, value); 2310 }, 2311 .ptr_stack_offset => |off| { 2312 try self.genSetStack(value_ty, off, value); 2313 }, 2314 .register => |addr_reg| { 2315 self.register_manager.freezeRegs(&.{addr_reg}); 2316 defer self.register_manager.unfreezeRegs(&.{addr_reg}); 2317 2318 switch (value) { 2319 .register => |value_reg| { 2320 try self.genStrRegister(value_reg, addr_reg, value_ty); 2321 }, 2322 else => { 2323 if (abi_size <= 8) { 2324 const raw_tmp_reg = try self.register_manager.allocReg(null); 2325 const tmp_reg = registerAlias(raw_tmp_reg, abi_size); 2326 self.register_manager.freezeRegs(&.{tmp_reg}); 2327 defer self.register_manager.unfreezeRegs(&.{tmp_reg}); 2328 2329 try self.genSetReg(value_ty, tmp_reg, value); 2330 try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); 2331 } else { 2332 return self.fail("TODO implement memcpy", .{}); 2333 } 2334 }, 2335 } 2336 }, 2337 .memory, 2338 .stack_offset, 2339 .got_load, 2340 .direct_load, 2341 => { 2342 const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr); 2343 try self.store(.{ .register = addr_reg }, value, ptr_ty, value_ty); 2344 }, 2345 } 2346 } 2347 2348 fn airStore(self: *Self, inst: Air.Inst.Index) !void { 2349 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 2350 const ptr = try self.resolveInst(bin_op.lhs); 2351 const value = try self.resolveInst(bin_op.rhs); 2352 const ptr_ty = self.air.typeOf(bin_op.lhs); 2353 const value_ty = self.air.typeOf(bin_op.rhs); 2354 2355 try self.store(ptr, value, ptr_ty, value_ty); 2356 2357 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); 2358 } 2359 2360 fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void { 2361 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 2362 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; 2363 const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index); 2364 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); 2365 } 2366 2367 fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { 2368 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2369 const result = try self.structFieldPtr(inst, ty_op.operand, index); 2370 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2371 } 2372 2373 fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue { 2374 return if (self.liveness.isUnused(inst)) .dead else result: { 2375 const mcv = try self.resolveInst(operand); 2376 const ptr_ty = self.air.typeOf(operand); 2377 const struct_ty = ptr_ty.childType(); 2378 const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); 2379 switch (mcv) { 2380 .ptr_stack_offset => |off| { 2381 break :result MCValue{ .ptr_stack_offset = off - struct_field_offset }; 2382 }, 2383 else => { 2384 const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ 2385 .immediate = struct_field_offset, 2386 }); 2387 self.register_manager.freezeRegs(&.{offset_reg}); 2388 defer self.register_manager.unfreezeRegs(&.{offset_reg}); 2389 2390 const addr_reg = try self.copyToTmpRegister(ptr_ty, mcv); 2391 self.register_manager.freezeRegs(&.{addr_reg}); 2392 defer self.register_manager.unfreezeRegs(&.{addr_reg}); 2393 2394 const dest = try self.binOp( 2395 .add, 2396 null, 2397 .{ .register = addr_reg }, 2398 .{ .register = offset_reg }, 2399 Type.usize, 2400 Type.usize, 2401 ); 2402 2403 break :result dest; 2404 }, 2405 } 2406 }; 2407 } 2408 2409 fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { 2410 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 2411 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; 2412 _ = extra; 2413 return self.fail("TODO implement codegen struct_field_val", .{}); 2414 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); 2415 } 2416 2417 fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { 2418 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 2419 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; 2420 _ = extra; 2421 return self.fail("TODO implement codegen airFieldParentPtr", .{}); 2422 } 2423 2424 fn airArg(self: *Self, inst: Air.Inst.Index) !void { 2425 const arg_index = self.arg_index; 2426 self.arg_index += 1; 2427 2428 const ty = self.air.typeOfIndex(inst); 2429 2430 const result = self.args[arg_index]; 2431 const mcv = switch (result) { 2432 // Copy registers to the stack 2433 .register => |reg| blk: { 2434 const mod = self.bin_file.options.module.?; 2435 const abi_size = math.cast(u32, ty.abiSize(self.target.*)) catch { 2436 return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(mod)}); 2437 }; 2438 const abi_align = ty.abiAlignment(self.target.*); 2439 const stack_offset = try self.allocMem(inst, abi_size, abi_align); 2440 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); 2441 2442 break :blk MCValue{ .stack_offset = stack_offset }; 2443 }, 2444 else => result, 2445 }; 2446 // TODO generate debug info 2447 // try self.genArgDbgInfo(inst, mcv); 2448 2449 if (self.liveness.isUnused(inst)) 2450 return self.finishAirBookkeeping(); 2451 2452 switch (mcv) { 2453 .register => |reg| { 2454 self.register_manager.getRegAssumeFree(toCanonicalReg(reg), inst); 2455 }, 2456 else => {}, 2457 } 2458 2459 return self.finishAir(inst, mcv, .{ .none, .none, .none }); 2460 } 2461 2462 fn airBreakpoint(self: *Self) !void { 2463 _ = try self.addInst(.{ 2464 .tag = .brk, 2465 .data = .{ .imm16 = 1 }, 2466 }); 2467 return self.finishAirBookkeeping(); 2468 } 2469 2470 fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void { 2471 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for aarch64", .{}); 2472 return self.finishAir(inst, result, .{ .none, .none, .none }); 2473 } 2474 2475 fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void { 2476 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for aarch64", .{}); 2477 return self.finishAir(inst, result, .{ .none, .none, .none }); 2478 } 2479 2480 fn airFence(self: *Self) !void { 2481 return self.fail("TODO implement fence() for {}", .{self.target.cpu.arch}); 2482 //return self.finishAirBookkeeping(); 2483 } 2484 2485 fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) !void { 2486 if (modifier == .always_tail) return self.fail("TODO implement tail calls for aarch64", .{}); 2487 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 2488 const callee = pl_op.operand; 2489 const extra = self.air.extraData(Air.Call, pl_op.payload); 2490 const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); 2491 const ty = self.air.typeOf(callee); 2492 2493 const fn_ty = switch (ty.zigTypeTag()) { 2494 .Fn => ty, 2495 .Pointer => ty.childType(), 2496 else => unreachable, 2497 }; 2498 2499 var info = try self.resolveCallingConventionValues(fn_ty); 2500 defer info.deinit(self); 2501 2502 // According to the Procedure Call Standard for the ARM 2503 // Architecture, compare flags are not preserved across 2504 // calls. Therefore, if some value is currently stored there, we 2505 // need to save it. 2506 // 2507 // TODO once caller-saved registers are implemented, save them 2508 // here too, but crucially *after* we save the compare flags as 2509 // saving compare flags may require a new caller-saved register 2510 try self.spillCompareFlagsIfOccupied(); 2511 2512 for (info.args) |mc_arg, arg_i| { 2513 const arg = args[arg_i]; 2514 const arg_ty = self.air.typeOf(arg); 2515 const arg_mcv = try self.resolveInst(args[arg_i]); 2516 2517 switch (mc_arg) { 2518 .none => continue, 2519 .undef => unreachable, 2520 .immediate => unreachable, 2521 .unreach => unreachable, 2522 .dead => unreachable, 2523 .memory => unreachable, 2524 .compare_flags_signed => unreachable, 2525 .compare_flags_unsigned => unreachable, 2526 .got_load => unreachable, 2527 .direct_load => unreachable, 2528 .register => |reg| { 2529 try self.register_manager.getReg(reg, null); 2530 try self.genSetReg(arg_ty, reg, arg_mcv); 2531 }, 2532 .stack_offset => { 2533 return self.fail("TODO implement calling with parameters in memory", .{}); 2534 }, 2535 .ptr_stack_offset => { 2536 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); 2537 }, 2538 } 2539 } 2540 2541 // Due to incremental compilation, how function calls are generated depends 2542 // on linking. 2543 const mod = self.bin_file.options.module.?; 2544 if (self.air.value(callee)) |func_value| { 2545 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) { 2546 if (func_value.castTag(.function)) |func_payload| { 2547 const func = func_payload.data; 2548 const ptr_bits = self.target.cpu.arch.ptrBitWidth(); 2549 const ptr_bytes: u64 = @divExact(ptr_bits, 8); 2550 const fn_owner_decl = mod.declPtr(func.owner_decl); 2551 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { 2552 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; 2553 break :blk @intCast(u32, got.p_vaddr + fn_owner_decl.link.elf.offset_table_index * ptr_bytes); 2554 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| 2555 coff_file.offset_table_virtual_address + fn_owner_decl.link.coff.offset_table_index * ptr_bytes 2556 else 2557 unreachable; 2558 2559 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr }); 2560 2561 _ = try self.addInst(.{ 2562 .tag = .blr, 2563 .data = .{ .reg = .x30 }, 2564 }); 2565 } else if (func_value.castTag(.extern_fn)) |_| { 2566 return self.fail("TODO implement calling extern functions", .{}); 2567 } else { 2568 return self.fail("TODO implement calling bitcasted functions", .{}); 2569 } 2570 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { 2571 if (func_value.castTag(.function)) |func_payload| { 2572 const func = func_payload.data; 2573 const fn_owner_decl = mod.declPtr(func.owner_decl); 2574 try self.genSetReg(Type.initTag(.u64), .x30, .{ 2575 .got_load = fn_owner_decl.link.macho.local_sym_index, 2576 }); 2577 // blr x30 2578 _ = try self.addInst(.{ 2579 .tag = .blr, 2580 .data = .{ .reg = .x30 }, 2581 }); 2582 } else if (func_value.castTag(.extern_fn)) |func_payload| { 2583 const extern_fn = func_payload.data; 2584 const decl_name = mod.declPtr(extern_fn.owner_decl).name; 2585 if (extern_fn.lib_name) |lib_name| { 2586 log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{ 2587 decl_name, 2588 lib_name, 2589 }); 2590 } 2591 const n_strx = try macho_file.addExternFn(mem.sliceTo(decl_name, 0)); 2592 2593 _ = try self.addInst(.{ 2594 .tag = .call_extern, 2595 .data = .{ 2596 .extern_fn = .{ 2597 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.local_sym_index, 2598 .sym_name = n_strx, 2599 }, 2600 }, 2601 }); 2602 } else { 2603 return self.fail("TODO implement calling bitcasted functions", .{}); 2604 } 2605 } else if (self.bin_file.cast(link.File.Plan9)) |p9| { 2606 if (func_value.castTag(.function)) |func_payload| { 2607 try p9.seeDecl(func_payload.data.owner_decl); 2608 const ptr_bits = self.target.cpu.arch.ptrBitWidth(); 2609 const ptr_bytes: u64 = @divExact(ptr_bits, 8); 2610 const got_addr = p9.bases.data; 2611 const got_index = mod.declPtr(func_payload.data.owner_decl).link.plan9.got_index.?; 2612 const fn_got_addr = got_addr + got_index * ptr_bytes; 2613 2614 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr }); 2615 2616 _ = try self.addInst(.{ 2617 .tag = .blr, 2618 .data = .{ .reg = .x30 }, 2619 }); 2620 } else if (func_value.castTag(.extern_fn)) |_| { 2621 return self.fail("TODO implement calling extern functions", .{}); 2622 } else { 2623 return self.fail("TODO implement calling bitcasted functions", .{}); 2624 } 2625 } else unreachable; 2626 } else { 2627 assert(ty.zigTypeTag() == .Pointer); 2628 const mcv = try self.resolveInst(callee); 2629 try self.genSetReg(ty, .x30, mcv); 2630 2631 _ = try self.addInst(.{ 2632 .tag = .blr, 2633 .data = .{ .reg = .x30 }, 2634 }); 2635 } 2636 2637 const result: MCValue = result: { 2638 switch (info.return_value) { 2639 .register => |reg| { 2640 if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) { 2641 // Save function return value in a callee saved register 2642 break :result try self.copyToNewRegister(inst, info.return_value); 2643 } 2644 }, 2645 else => {}, 2646 } 2647 break :result info.return_value; 2648 }; 2649 2650 if (args.len + 1 <= Liveness.bpi - 1) { 2651 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); 2652 buf[0] = callee; 2653 std.mem.copy(Air.Inst.Ref, buf[1..], args); 2654 return self.finishAir(inst, result, buf); 2655 } 2656 var bt = try self.iterateBigTomb(inst, 1 + args.len); 2657 bt.feed(callee); 2658 for (args) |arg| { 2659 bt.feed(arg); 2660 } 2661 return bt.finishAir(result); 2662 } 2663 2664 fn ret(self: *Self, mcv: MCValue) !void { 2665 const ret_ty = self.fn_type.fnReturnType(); 2666 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); 2667 // Just add space for an instruction, patch this later 2668 const index = try self.addInst(.{ 2669 .tag = .nop, 2670 .data = .{ .nop = {} }, 2671 }); 2672 try self.exitlude_jump_relocs.append(self.gpa, index); 2673 } 2674 2675 fn airRet(self: *Self, inst: Air.Inst.Index) !void { 2676 const un_op = self.air.instructions.items(.data)[inst].un_op; 2677 const operand = try self.resolveInst(un_op); 2678 try self.ret(operand); 2679 return self.finishAir(inst, .dead, .{ un_op, .none, .none }); 2680 } 2681 2682 fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { 2683 const un_op = self.air.instructions.items(.data)[inst].un_op; 2684 const ptr = try self.resolveInst(un_op); 2685 _ = ptr; 2686 return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch}); 2687 //return self.finishAir(inst, .dead, .{ un_op, .none, .none }); 2688 } 2689 2690 fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { 2691 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 2692 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 2693 const lhs = try self.resolveInst(bin_op.lhs); 2694 const rhs = try self.resolveInst(bin_op.rhs); 2695 const lhs_ty = self.air.typeOf(bin_op.lhs); 2696 2697 var int_buffer: Type.Payload.Bits = undefined; 2698 const int_ty = switch (lhs_ty.zigTypeTag()) { 2699 .Vector => return self.fail("TODO AArch64 cmp vectors", .{}), 2700 .Enum => lhs_ty.intTagType(&int_buffer), 2701 .Int => lhs_ty, 2702 .Bool => Type.initTag(.u1), 2703 .Pointer => Type.usize, 2704 .ErrorSet => Type.initTag(.u16), 2705 .Optional => blk: { 2706 var opt_buffer: Type.Payload.ElemType = undefined; 2707 const payload_ty = lhs_ty.optionalChild(&opt_buffer); 2708 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { 2709 break :blk Type.initTag(.u1); 2710 } else if (lhs_ty.isPtrLikeOptional()) { 2711 break :blk Type.usize; 2712 } else { 2713 return self.fail("TODO AArch64 cmp non-pointer optionals", .{}); 2714 } 2715 }, 2716 .Float => return self.fail("TODO AArch64 cmp floats", .{}), 2717 else => unreachable, 2718 }; 2719 2720 const int_info = int_ty.intInfo(self.target.*); 2721 if (int_info.bits <= 64) { 2722 _ = try self.binOp(.cmp_eq, inst, lhs, rhs, int_ty, int_ty); 2723 2724 try self.spillCompareFlagsIfOccupied(); 2725 self.compare_flags_inst = inst; 2726 2727 break :result switch (int_info.signedness) { 2728 .signed => MCValue{ .compare_flags_signed = op }, 2729 .unsigned => MCValue{ .compare_flags_unsigned = op }, 2730 }; 2731 } else { 2732 return self.fail("TODO AArch64 cmp for ints > 64 bits", .{}); 2733 } 2734 }; 2735 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 2736 } 2737 2738 fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { 2739 _ = inst; 2740 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); 2741 } 2742 2743 fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { 2744 const un_op = self.air.instructions.items(.data)[inst].un_op; 2745 const operand = try self.resolveInst(un_op); 2746 _ = operand; 2747 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch}); 2748 return self.finishAir(inst, result, .{ un_op, .none, .none }); 2749 } 2750 2751 fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { 2752 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; 2753 2754 _ = try self.addInst(.{ 2755 .tag = .dbg_line, 2756 .data = .{ .dbg_line_column = .{ 2757 .line = dbg_stmt.line, 2758 .column = dbg_stmt.column, 2759 } }, 2760 }); 2761 2762 return self.finishAirBookkeeping(); 2763 } 2764 2765 fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { 2766 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 2767 const function = self.air.values[ty_pl.payload].castTag(.function).?.data; 2768 // TODO emit debug info for function change 2769 _ = function; 2770 return self.finishAir(inst, .dead, .{ .none, .none, .none }); 2771 } 2772 2773 fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { 2774 // TODO emit debug info lexical block 2775 return self.finishAir(inst, .dead, .{ .none, .none, .none }); 2776 } 2777 2778 fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { 2779 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 2780 const name = self.air.nullTerminatedString(pl_op.payload); 2781 const operand = pl_op.operand; 2782 // TODO emit debug info for this variable 2783 _ = name; 2784 return self.finishAir(inst, .dead, .{ operand, .none, .none }); 2785 } 2786 2787 fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { 2788 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 2789 const cond = try self.resolveInst(pl_op.operand); 2790 const extra = self.air.extraData(Air.CondBr, pl_op.payload); 2791 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len]; 2792 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; 2793 const liveness_condbr = self.liveness.getCondBr(inst); 2794 2795 const reloc: Mir.Inst.Index = switch (cond) { 2796 .compare_flags_signed, 2797 .compare_flags_unsigned, 2798 => try self.addInst(.{ 2799 .tag = .b_cond, 2800 .data = .{ 2801 .inst_cond = .{ 2802 .inst = undefined, // populated later through performReloc 2803 .cond = switch (cond) { 2804 .compare_flags_signed => |cmp_op| blk: { 2805 // Here we map to the opposite condition because the jump is to the false branch. 2806 const condition = Instruction.Condition.fromCompareOperatorSigned(cmp_op); 2807 break :blk condition.negate(); 2808 }, 2809 .compare_flags_unsigned => |cmp_op| blk: { 2810 // Here we map to the opposite condition because the jump is to the false branch. 2811 const condition = Instruction.Condition.fromCompareOperatorUnsigned(cmp_op); 2812 break :blk condition.negate(); 2813 }, 2814 else => unreachable, 2815 }, 2816 }, 2817 }, 2818 }), 2819 else => blk: { 2820 const reg = switch (cond) { 2821 .register => |r| r, 2822 else => try self.copyToTmpRegister(Type.bool, cond), 2823 }; 2824 2825 break :blk try self.addInst(.{ 2826 .tag = .cbz, 2827 .data = .{ 2828 .r_inst = .{ 2829 .rt = reg, 2830 .inst = undefined, // populated later through performReloc 2831 }, 2832 }, 2833 }); 2834 }, 2835 }; 2836 2837 // If the condition dies here in this condbr instruction, process 2838 // that death now instead of later as this has an effect on 2839 // whether it needs to be spilled in the branches 2840 if (self.liveness.operandDies(inst, 0)) { 2841 const op_int = @enumToInt(pl_op.operand); 2842 if (op_int >= Air.Inst.Ref.typed_value_map.len) { 2843 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); 2844 self.processDeath(op_index); 2845 } 2846 } 2847 2848 // Capture the state of register and stack allocation state so that we can revert to it. 2849 const parent_next_stack_offset = self.next_stack_offset; 2850 const parent_free_registers = self.register_manager.free_registers; 2851 var parent_stack = try self.stack.clone(self.gpa); 2852 defer parent_stack.deinit(self.gpa); 2853 const parent_registers = self.register_manager.registers; 2854 const parent_compare_flags_inst = self.compare_flags_inst; 2855 2856 try self.branch_stack.append(.{}); 2857 2858 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len); 2859 for (liveness_condbr.then_deaths) |operand| { 2860 self.processDeath(operand); 2861 } 2862 try self.genBody(then_body); 2863 2864 // Revert to the previous register and stack allocation state. 2865 2866 var saved_then_branch = self.branch_stack.pop(); 2867 defer saved_then_branch.deinit(self.gpa); 2868 2869 self.register_manager.registers = parent_registers; 2870 self.compare_flags_inst = parent_compare_flags_inst; 2871 2872 self.stack.deinit(self.gpa); 2873 self.stack = parent_stack; 2874 parent_stack = .{}; 2875 2876 self.next_stack_offset = parent_next_stack_offset; 2877 self.register_manager.free_registers = parent_free_registers; 2878 2879 try self.performReloc(reloc); 2880 const else_branch = self.branch_stack.addOneAssumeCapacity(); 2881 else_branch.* = .{}; 2882 2883 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len); 2884 for (liveness_condbr.else_deaths) |operand| { 2885 self.processDeath(operand); 2886 } 2887 try self.genBody(else_body); 2888 2889 // At this point, each branch will possibly have conflicting values for where 2890 // each instruction is stored. They agree, however, on which instructions are alive/dead. 2891 // We use the first ("then") branch as canonical, and here emit 2892 // instructions into the second ("else") branch to make it conform. 2893 // We continue respect the data structure semantic guarantees of the else_branch so 2894 // that we can use all the code emitting abstractions. This is why at the bottom we 2895 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers 2896 // rather than assigning it. 2897 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2]; 2898 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count()); 2899 2900 const else_slice = else_branch.inst_table.entries.slice(); 2901 const else_keys = else_slice.items(.key); 2902 const else_values = else_slice.items(.value); 2903 for (else_keys) |else_key, else_idx| { 2904 const else_value = else_values[else_idx]; 2905 const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: { 2906 // The instruction's MCValue is overridden in both branches. 2907 parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value); 2908 if (else_value == .dead) { 2909 assert(then_entry.value == .dead); 2910 continue; 2911 } 2912 break :blk then_entry.value; 2913 } else blk: { 2914 if (else_value == .dead) 2915 continue; 2916 // The instruction is only overridden in the else branch. 2917 var i: usize = self.branch_stack.items.len - 2; 2918 while (true) { 2919 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead? 2920 if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| { 2921 assert(mcv != .dead); 2922 break :blk mcv; 2923 } 2924 } 2925 }; 2926 log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv }); 2927 // TODO make sure the destination stack offset / register does not already have something 2928 // going on there. 2929 try self.setRegOrMem(self.air.typeOfIndex(else_key), canon_mcv, else_value); 2930 // TODO track the new register / stack allocation 2931 } 2932 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count()); 2933 const then_slice = saved_then_branch.inst_table.entries.slice(); 2934 const then_keys = then_slice.items(.key); 2935 const then_values = then_slice.items(.value); 2936 for (then_keys) |then_key, then_idx| { 2937 const then_value = then_values[then_idx]; 2938 // We already deleted the items from this table that matched the else_branch. 2939 // So these are all instructions that are only overridden in the then branch. 2940 parent_branch.inst_table.putAssumeCapacity(then_key, then_value); 2941 if (then_value == .dead) 2942 continue; 2943 const parent_mcv = blk: { 2944 var i: usize = self.branch_stack.items.len - 2; 2945 while (true) { 2946 i -= 1; 2947 if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| { 2948 assert(mcv != .dead); 2949 break :blk mcv; 2950 } 2951 } 2952 }; 2953 log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value }); 2954 // TODO make sure the destination stack offset / register does not already have something 2955 // going on there. 2956 try self.setRegOrMem(self.air.typeOfIndex(then_key), parent_mcv, then_value); 2957 // TODO track the new register / stack allocation 2958 } 2959 2960 { 2961 var item = self.branch_stack.pop(); 2962 item.deinit(self.gpa); 2963 } 2964 2965 // We already took care of pl_op.operand earlier, so we're going 2966 // to pass .none here 2967 return self.finishAir(inst, .unreach, .{ .none, .none, .none }); 2968 } 2969 2970 fn isNull(self: *Self, operand: MCValue) !MCValue { 2971 _ = operand; 2972 // Here you can specialize this instruction if it makes sense to, otherwise the default 2973 // will call isNonNull and invert the result. 2974 return self.fail("TODO call isNonNull and invert the result", .{}); 2975 } 2976 2977 fn isNonNull(self: *Self, operand: MCValue) !MCValue { 2978 _ = operand; 2979 // Here you can specialize this instruction if it makes sense to, otherwise the default 2980 // will call isNull and invert the result. 2981 return self.fail("TODO call isNull and invert the result", .{}); 2982 } 2983 2984 fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { 2985 const error_type = ty.errorUnionSet(); 2986 const payload_type = ty.errorUnionPayload(); 2987 2988 if (!error_type.hasRuntimeBits()) { 2989 return MCValue{ .immediate = 0 }; // always false 2990 } else if (!payload_type.hasRuntimeBits()) { 2991 if (error_type.abiSize(self.target.*) <= 8) { 2992 const reg_mcv: MCValue = switch (operand) { 2993 .register => operand, 2994 else => .{ .register = try self.copyToTmpRegister(error_type, operand) }, 2995 }; 2996 2997 _ = try self.addInst(.{ 2998 .tag = .cmp_immediate, 2999 .data = .{ .r_imm12_sh = .{ 3000 .rn = reg_mcv.register, 3001 .imm12 = 0, 3002 } }, 3003 }); 3004 3005 return MCValue{ .compare_flags_unsigned = .gt }; 3006 } else { 3007 return self.fail("TODO isErr for errors with size > 8", .{}); 3008 } 3009 } else { 3010 return self.fail("TODO isErr for non-empty payloads", .{}); 3011 } 3012 } 3013 3014 fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { 3015 const is_err_result = try self.isErr(ty, operand); 3016 switch (is_err_result) { 3017 .compare_flags_unsigned => |op| { 3018 assert(op == .gt); 3019 return MCValue{ .compare_flags_unsigned = .lte }; 3020 }, 3021 .immediate => |imm| { 3022 assert(imm == 0); 3023 return MCValue{ .immediate = 1 }; 3024 }, 3025 else => unreachable, 3026 } 3027 } 3028 3029 fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { 3030 const un_op = self.air.instructions.items(.data)[inst].un_op; 3031 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3032 const operand = try self.resolveInst(un_op); 3033 break :result try self.isNull(operand); 3034 }; 3035 return self.finishAir(inst, result, .{ un_op, .none, .none }); 3036 } 3037 3038 fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { 3039 const un_op = self.air.instructions.items(.data)[inst].un_op; 3040 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3041 const operand_ptr = try self.resolveInst(un_op); 3042 const operand: MCValue = blk: { 3043 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { 3044 // The MCValue that holds the pointer can be re-used as the value. 3045 break :blk operand_ptr; 3046 } else { 3047 break :blk try self.allocRegOrMem(inst, true); 3048 } 3049 }; 3050 try self.load(operand, operand_ptr, self.air.typeOf(un_op)); 3051 break :result try self.isNull(operand); 3052 }; 3053 return self.finishAir(inst, result, .{ un_op, .none, .none }); 3054 } 3055 3056 fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { 3057 const un_op = self.air.instructions.items(.data)[inst].un_op; 3058 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3059 const operand = try self.resolveInst(un_op); 3060 break :result try self.isNonNull(operand); 3061 }; 3062 return self.finishAir(inst, result, .{ un_op, .none, .none }); 3063 } 3064 3065 fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { 3066 const un_op = self.air.instructions.items(.data)[inst].un_op; 3067 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3068 const operand_ptr = try self.resolveInst(un_op); 3069 const operand: MCValue = blk: { 3070 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { 3071 // The MCValue that holds the pointer can be re-used as the value. 3072 break :blk operand_ptr; 3073 } else { 3074 break :blk try self.allocRegOrMem(inst, true); 3075 } 3076 }; 3077 try self.load(operand, operand_ptr, self.air.typeOf(un_op)); 3078 break :result try self.isNonNull(operand); 3079 }; 3080 return self.finishAir(inst, result, .{ un_op, .none, .none }); 3081 } 3082 3083 fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { 3084 const un_op = self.air.instructions.items(.data)[inst].un_op; 3085 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3086 const operand = try self.resolveInst(un_op); 3087 const ty = self.air.typeOf(un_op); 3088 break :result try self.isErr(ty, operand); 3089 }; 3090 return self.finishAir(inst, result, .{ un_op, .none, .none }); 3091 } 3092 3093 fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { 3094 const un_op = self.air.instructions.items(.data)[inst].un_op; 3095 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3096 const operand_ptr = try self.resolveInst(un_op); 3097 const ptr_ty = self.air.typeOf(un_op); 3098 const operand: MCValue = blk: { 3099 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { 3100 // The MCValue that holds the pointer can be re-used as the value. 3101 break :blk operand_ptr; 3102 } else { 3103 break :blk try self.allocRegOrMem(inst, true); 3104 } 3105 }; 3106 try self.load(operand, operand_ptr, self.air.typeOf(un_op)); 3107 break :result try self.isErr(ptr_ty.elemType(), operand); 3108 }; 3109 return self.finishAir(inst, result, .{ un_op, .none, .none }); 3110 } 3111 3112 fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { 3113 const un_op = self.air.instructions.items(.data)[inst].un_op; 3114 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3115 const operand = try self.resolveInst(un_op); 3116 const ty = self.air.typeOf(un_op); 3117 break :result try self.isNonErr(ty, operand); 3118 }; 3119 return self.finishAir(inst, result, .{ un_op, .none, .none }); 3120 } 3121 3122 fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { 3123 const un_op = self.air.instructions.items(.data)[inst].un_op; 3124 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3125 const operand_ptr = try self.resolveInst(un_op); 3126 const ptr_ty = self.air.typeOf(un_op); 3127 const operand: MCValue = blk: { 3128 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { 3129 // The MCValue that holds the pointer can be re-used as the value. 3130 break :blk operand_ptr; 3131 } else { 3132 break :blk try self.allocRegOrMem(inst, true); 3133 } 3134 }; 3135 try self.load(operand, operand_ptr, self.air.typeOf(un_op)); 3136 break :result try self.isNonErr(ptr_ty.elemType(), operand); 3137 }; 3138 return self.finishAir(inst, result, .{ un_op, .none, .none }); 3139 } 3140 3141 fn airLoop(self: *Self, inst: Air.Inst.Index) !void { 3142 // A loop is a setup to be able to jump back to the beginning. 3143 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 3144 const loop = self.air.extraData(Air.Block, ty_pl.payload); 3145 const body = self.air.extra[loop.end..][0..loop.data.body_len]; 3146 const start_index = @intCast(u32, self.mir_instructions.len); 3147 try self.genBody(body); 3148 try self.jump(start_index); 3149 return self.finishAirBookkeeping(); 3150 } 3151 3152 /// Send control flow to `inst`. 3153 fn jump(self: *Self, inst: Mir.Inst.Index) !void { 3154 _ = try self.addInst(.{ 3155 .tag = .b, 3156 .data = .{ .inst = inst }, 3157 }); 3158 } 3159 3160 fn airBlock(self: *Self, inst: Air.Inst.Index) !void { 3161 try self.blocks.putNoClobber(self.gpa, inst, .{ 3162 // A block is a setup to be able to jump to the end. 3163 .relocs = .{}, 3164 // It also acts as a receptacle for break operands. 3165 // Here we use `MCValue.none` to represent a null value so that the first 3166 // break instruction will choose a MCValue for the block result and overwrite 3167 // this field. Following break instructions will use that MCValue to put their 3168 // block results. 3169 .mcv = MCValue{ .none = {} }, 3170 }); 3171 defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); 3172 3173 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 3174 const extra = self.air.extraData(Air.Block, ty_pl.payload); 3175 const body = self.air.extra[extra.end..][0..extra.data.body_len]; 3176 try self.genBody(body); 3177 3178 // relocations for `br` instructions 3179 const relocs = &self.blocks.getPtr(inst).?.relocs; 3180 if (relocs.items.len > 0 and relocs.items[relocs.items.len - 1] == self.mir_instructions.len - 1) { 3181 // If the last Mir instruction is the last relocation (which 3182 // would just jump one instruction further), it can be safely 3183 // removed 3184 self.mir_instructions.orderedRemove(relocs.pop()); 3185 } 3186 for (relocs.items) |reloc| { 3187 try self.performReloc(reloc); 3188 } 3189 3190 const result = self.blocks.getPtr(inst).?.mcv; 3191 return self.finishAir(inst, result, .{ .none, .none, .none }); 3192 } 3193 3194 fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { 3195 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 3196 const condition = pl_op.operand; 3197 _ = condition; 3198 3199 return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch}); 3200 } 3201 3202 fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { 3203 const tag = self.mir_instructions.items(.tag)[inst]; 3204 switch (tag) { 3205 .cbz => self.mir_instructions.items(.data)[inst].r_inst.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len), 3206 .b_cond => self.mir_instructions.items(.data)[inst].inst_cond.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len), 3207 .b => self.mir_instructions.items(.data)[inst].inst = @intCast(Mir.Inst.Index, self.mir_instructions.len), 3208 else => unreachable, 3209 } 3210 } 3211 3212 fn airBr(self: *Self, inst: Air.Inst.Index) !void { 3213 const branch = self.air.instructions.items(.data)[inst].br; 3214 try self.br(branch.block_inst, branch.operand); 3215 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); 3216 } 3217 3218 fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { 3219 const block_data = self.blocks.getPtr(block).?; 3220 3221 if (self.air.typeOf(operand).hasRuntimeBits()) { 3222 const operand_mcv = try self.resolveInst(operand); 3223 const block_mcv = block_data.mcv; 3224 if (block_mcv == .none) { 3225 block_data.mcv = switch (operand_mcv) { 3226 .none, .dead, .unreach => unreachable, 3227 .register, .stack_offset, .memory => operand_mcv, 3228 .immediate => blk: { 3229 const new_mcv = try self.allocRegOrMem(block, true); 3230 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); 3231 break :blk new_mcv; 3232 }, 3233 else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}), 3234 }; 3235 } else { 3236 try self.setRegOrMem(self.air.typeOfIndex(block), block_mcv, operand_mcv); 3237 } 3238 } 3239 return self.brVoid(block); 3240 } 3241 3242 fn brVoid(self: *Self, block: Air.Inst.Index) !void { 3243 const block_data = self.blocks.getPtr(block).?; 3244 3245 // Emit a jump with a relocation. It will be patched up after the block ends. 3246 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1); 3247 3248 block_data.relocs.appendAssumeCapacity(try self.addInst(.{ 3249 .tag = .b, 3250 .data = .{ .inst = undefined }, // populated later through performReloc 3251 })); 3252 } 3253 3254 fn airAsm(self: *Self, inst: Air.Inst.Index) !void { 3255 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 3256 const extra = self.air.extraData(Air.Asm, ty_pl.payload); 3257 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; 3258 const clobbers_len = @truncate(u31, extra.data.flags); 3259 var extra_i: usize = extra.end; 3260 const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); 3261 extra_i += outputs.len; 3262 const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); 3263 extra_i += inputs.len; 3264 3265 const dead = !is_volatile and self.liveness.isUnused(inst); 3266 const result: MCValue = if (dead) .dead else result: { 3267 if (outputs.len > 1) { 3268 return self.fail("TODO implement codegen for asm with more than 1 output", .{}); 3269 } 3270 3271 const output_constraint: ?[]const u8 = for (outputs) |output| { 3272 if (output != .none) { 3273 return self.fail("TODO implement codegen for non-expr asm", .{}); 3274 } 3275 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); 3276 // This equation accounts for the fact that even if we have exactly 4 bytes 3277 // for the string, we still use the next u32 for the null terminator. 3278 extra_i += constraint.len / 4 + 1; 3279 3280 break constraint; 3281 } else null; 3282 3283 for (inputs) |input| { 3284 const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); 3285 const constraint = std.mem.sliceTo(input_bytes, 0); 3286 const input_name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); 3287 // This equation accounts for the fact that even if we have exactly 4 bytes 3288 // for the string, we still use the next u32 for the null terminator. 3289 extra_i += (constraint.len + input_name.len + 1) / 4 + 1; 3290 3291 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { 3292 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); 3293 } 3294 const reg_name = constraint[1 .. constraint.len - 1]; 3295 const reg = parseRegName(reg_name) orelse 3296 return self.fail("unrecognized register: '{s}'", .{reg_name}); 3297 3298 const arg_mcv = try self.resolveInst(input); 3299 try self.register_manager.getReg(reg, null); 3300 try self.genSetReg(self.air.typeOf(input), reg, arg_mcv); 3301 } 3302 3303 { 3304 var clobber_i: u32 = 0; 3305 while (clobber_i < clobbers_len) : (clobber_i += 1) { 3306 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); 3307 // This equation accounts for the fact that even if we have exactly 4 bytes 3308 // for the string, we still use the next u32 for the null terminator. 3309 extra_i += clobber.len / 4 + 1; 3310 3311 // TODO honor these 3312 } 3313 } 3314 3315 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; 3316 3317 if (mem.eql(u8, asm_source, "svc #0")) { 3318 _ = try self.addInst(.{ 3319 .tag = .svc, 3320 .data = .{ .imm16 = 0x0 }, 3321 }); 3322 } else if (mem.eql(u8, asm_source, "svc #0x80")) { 3323 _ = try self.addInst(.{ 3324 .tag = .svc, 3325 .data = .{ .imm16 = 0x80 }, 3326 }); 3327 } else { 3328 return self.fail("TODO implement support for more aarch64 assembly instructions", .{}); 3329 } 3330 3331 if (output_constraint) |output| { 3332 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { 3333 return self.fail("unrecognized asm output constraint: '{s}'", .{output}); 3334 } 3335 const reg_name = output[2 .. output.len - 1]; 3336 const reg = parseRegName(reg_name) orelse 3337 return self.fail("unrecognized register: '{s}'", .{reg_name}); 3338 break :result MCValue{ .register = reg }; 3339 } else { 3340 break :result MCValue{ .none = {} }; 3341 } 3342 }; 3343 3344 simple: { 3345 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); 3346 var buf_index: usize = 0; 3347 for (outputs) |output| { 3348 if (output == .none) continue; 3349 3350 if (buf_index >= buf.len) break :simple; 3351 buf[buf_index] = output; 3352 buf_index += 1; 3353 } 3354 if (buf_index + inputs.len > buf.len) break :simple; 3355 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); 3356 return self.finishAir(inst, result, buf); 3357 } 3358 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); 3359 for (outputs) |output| { 3360 if (output == .none) continue; 3361 3362 bt.feed(output); 3363 } 3364 for (inputs) |input| { 3365 bt.feed(input); 3366 } 3367 return bt.finishAir(result); 3368 } 3369 3370 fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb { 3371 try self.ensureProcessDeathCapacity(operand_count + 1); 3372 return BigTomb{ 3373 .function = self, 3374 .inst = inst, 3375 .lbt = self.liveness.iterateBigTomb(inst), 3376 }; 3377 } 3378 3379 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata. 3380 fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { 3381 switch (loc) { 3382 .none => return, 3383 .register => |reg| return self.genSetReg(ty, reg, val), 3384 .stack_offset => |off| return self.genSetStack(ty, off, val), 3385 .memory => { 3386 return self.fail("TODO implement setRegOrMem for memory", .{}); 3387 }, 3388 else => unreachable, 3389 } 3390 } 3391 3392 fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { 3393 const abi_size = ty.abiSize(self.target.*); 3394 switch (mcv) { 3395 .dead => unreachable, 3396 .unreach, .none => return, // Nothing to do. 3397 .undef => { 3398 if (!self.wantSafety()) 3399 return; // The already existing value will do just fine. 3400 // TODO Upgrade this to a memset call when we have that available. 3401 switch (ty.abiSize(self.target.*)) { 3402 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }), 3403 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }), 3404 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), 3405 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), 3406 else => return self.fail("TODO implement memset", .{}), 3407 } 3408 }, 3409 .compare_flags_unsigned, 3410 .compare_flags_signed, 3411 .immediate, 3412 .ptr_stack_offset, 3413 => { 3414 const reg = try self.copyToTmpRegister(ty, mcv); 3415 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); 3416 }, 3417 .register => |reg| { 3418 switch (abi_size) { 3419 1, 2, 4, 8 => { 3420 const tag: Mir.Inst.Tag = switch (abi_size) { 3421 1 => .strb_stack, 3422 2 => .strh_stack, 3423 4, 8 => .str_stack, 3424 else => unreachable, // unexpected abi size 3425 }; 3426 const rt = registerAlias(reg, abi_size); 3427 3428 _ = try self.addInst(.{ 3429 .tag = tag, 3430 .data = .{ .load_store_stack = .{ 3431 .rt = rt, 3432 .offset = @intCast(u32, stack_offset), 3433 } }, 3434 }); 3435 }, 3436 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), 3437 } 3438 }, 3439 .got_load, 3440 .direct_load, 3441 .memory, 3442 .stack_offset, 3443 => { 3444 switch (mcv) { 3445 .stack_offset => |off| { 3446 if (stack_offset == off) 3447 return; // Copy stack variable to itself; nothing to do. 3448 }, 3449 else => {}, 3450 } 3451 3452 if (abi_size <= 8) { 3453 const reg = try self.copyToTmpRegister(ty, mcv); 3454 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); 3455 } else { 3456 var ptr_ty_payload: Type.Payload.ElemType = .{ 3457 .base = .{ .tag = .single_mut_pointer }, 3458 .data = ty, 3459 }; 3460 const ptr_ty = Type.initPayload(&ptr_ty_payload.base); 3461 3462 // TODO call extern memcpy 3463 const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }); 3464 self.register_manager.freezeRegs(®s); 3465 defer self.register_manager.unfreezeRegs(®s); 3466 3467 const src_reg = regs[0]; 3468 const dst_reg = regs[1]; 3469 const len_reg = regs[2]; 3470 const count_reg = regs[3]; 3471 const tmp_reg = regs[4]; 3472 3473 switch (mcv) { 3474 .stack_offset => |off| { 3475 // sub src_reg, fp, #off 3476 try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); 3477 }, 3478 .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = addr }), 3479 .got_load, 3480 .direct_load, 3481 => |sym_index| { 3482 const tag: Mir.Inst.Tag = switch (mcv) { 3483 .got_load => .load_memory_ptr_got, 3484 .direct_load => .load_memory_ptr_direct, 3485 else => unreachable, 3486 }; 3487 const mod = self.bin_file.options.module.?; 3488 _ = try self.addInst(.{ 3489 .tag = tag, 3490 .data = .{ 3491 .payload = try self.addExtra(Mir.LoadMemoryPie{ 3492 .register = @enumToInt(src_reg), 3493 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.local_sym_index, 3494 .sym_index = sym_index, 3495 }), 3496 }, 3497 }); 3498 }, 3499 else => unreachable, 3500 } 3501 3502 // sub dst_reg, fp, #stack_offset 3503 try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset }); 3504 3505 // mov len, #abi_size 3506 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size }); 3507 3508 // memcpy(src, dst, len) 3509 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); 3510 } 3511 }, 3512 } 3513 } 3514 3515 fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { 3516 switch (mcv) { 3517 .dead => unreachable, 3518 .unreach, .none => return, // Nothing to do. 3519 .undef => { 3520 if (!self.wantSafety()) 3521 return; // The already existing value will do just fine. 3522 // Write the debug undefined value. 3523 switch (reg.size()) { 3524 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }), 3525 64 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), 3526 else => unreachable, // unexpected register size 3527 } 3528 }, 3529 .ptr_stack_offset => |off| { 3530 // TODO: maybe addressing from sp instead of fp 3531 const imm12 = math.cast(u12, off) catch 3532 return self.fail("TODO larger stack offsets", .{}); 3533 3534 _ = try self.addInst(.{ 3535 .tag = .sub_immediate, 3536 .data = .{ .rr_imm12_sh = .{ 3537 .rd = reg, 3538 .rn = .x29, 3539 .imm12 = imm12, 3540 } }, 3541 }); 3542 }, 3543 .compare_flags_unsigned, 3544 .compare_flags_signed, 3545 => |op| { 3546 const condition = switch (mcv) { 3547 .compare_flags_unsigned => Instruction.Condition.fromCompareOperatorUnsigned(op), 3548 .compare_flags_signed => Instruction.Condition.fromCompareOperatorSigned(op), 3549 else => unreachable, 3550 }; 3551 3552 _ = try self.addInst(.{ 3553 .tag = .cset, 3554 .data = .{ .r_cond = .{ 3555 .rd = reg, 3556 .cond = condition.negate(), 3557 } }, 3558 }); 3559 }, 3560 .immediate => |x| { 3561 _ = try self.addInst(.{ 3562 .tag = .movz, 3563 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x) } }, 3564 }); 3565 3566 if (x & 0x0000_0000_ffff_0000 != 0) { 3567 _ = try self.addInst(.{ 3568 .tag = .movk, 3569 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } }, 3570 }); 3571 } 3572 3573 if (reg.size() == 64) { 3574 if (x & 0x0000_ffff_0000_0000 != 0) { 3575 _ = try self.addInst(.{ 3576 .tag = .movk, 3577 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } }, 3578 }); 3579 } 3580 if (x & 0xffff_0000_0000_0000 != 0) { 3581 _ = try self.addInst(.{ 3582 .tag = .movk, 3583 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } }, 3584 }); 3585 } 3586 } 3587 }, 3588 .register => |src_reg| { 3589 // If the registers are the same, nothing to do. 3590 if (src_reg.id() == reg.id()) 3591 return; 3592 3593 // mov reg, src_reg 3594 _ = try self.addInst(.{ 3595 .tag = .mov_register, 3596 .data = .{ .rr = .{ .rd = reg, .rn = src_reg } }, 3597 }); 3598 }, 3599 .got_load, 3600 .direct_load, 3601 => |sym_index| { 3602 const tag: Mir.Inst.Tag = switch (mcv) { 3603 .got_load => .load_memory_got, 3604 .direct_load => .load_memory_direct, 3605 else => unreachable, 3606 }; 3607 const mod = self.bin_file.options.module.?; 3608 _ = try self.addInst(.{ 3609 .tag = tag, 3610 .data = .{ 3611 .payload = try self.addExtra(Mir.LoadMemoryPie{ 3612 .register = @enumToInt(reg), 3613 .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.local_sym_index, 3614 .sym_index = sym_index, 3615 }), 3616 }, 3617 }); 3618 }, 3619 .memory => |addr| { 3620 // The value is in memory at a hard-coded address. 3621 // If the type is a pointer, it means the pointer address is at this memory location. 3622 try self.genSetReg(ty, reg.to64(), .{ .immediate = addr }); 3623 try self.genLdrRegister(reg, reg.to64(), ty); 3624 }, 3625 .stack_offset => |off| { 3626 const abi_size = ty.abiSize(self.target.*); 3627 3628 switch (abi_size) { 3629 1, 2, 4, 8 => { 3630 const tag: Mir.Inst.Tag = switch (abi_size) { 3631 1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb_stack else .ldrb_stack, 3632 2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh_stack else .ldrh_stack, 3633 4, 8 => .ldr_stack, 3634 else => unreachable, // unexpected abi size 3635 }; 3636 3637 _ = try self.addInst(.{ 3638 .tag = tag, 3639 .data = .{ .load_store_stack = .{ 3640 .rt = reg, 3641 .offset = @intCast(u32, off), 3642 } }, 3643 }); 3644 }, 3645 3, 5, 6, 7 => return self.fail("TODO implement genSetReg types size {}", .{abi_size}), 3646 else => unreachable, 3647 } 3648 }, 3649 } 3650 } 3651 3652 fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { 3653 const un_op = self.air.instructions.items(.data)[inst].un_op; 3654 const result = try self.resolveInst(un_op); 3655 return self.finishAir(inst, result, .{ un_op, .none, .none }); 3656 } 3657 3658 fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { 3659 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3660 const result = try self.resolveInst(ty_op.operand); 3661 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3662 } 3663 3664 fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { 3665 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3666 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3667 const ptr_ty = self.air.typeOf(ty_op.operand); 3668 const ptr = try self.resolveInst(ty_op.operand); 3669 const array_ty = ptr_ty.childType(); 3670 const array_len = @intCast(u32, array_ty.arrayLen()); 3671 3672 const ptr_bits = self.target.cpu.arch.ptrBitWidth(); 3673 const ptr_bytes = @divExact(ptr_bits, 8); 3674 3675 const stack_offset = try self.allocMem(inst, ptr_bytes * 2, ptr_bytes * 2); 3676 try self.genSetStack(ptr_ty, stack_offset, ptr); 3677 try self.genSetStack(Type.initTag(.usize), stack_offset - ptr_bytes, .{ .immediate = array_len }); 3678 break :result MCValue{ .stack_offset = stack_offset }; 3679 }; 3680 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3681 } 3682 3683 fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void { 3684 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3685 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntToFloat for {}", .{ 3686 self.target.cpu.arch, 3687 }); 3688 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3689 } 3690 3691 fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { 3692 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3693 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatToInt for {}", .{ 3694 self.target.cpu.arch, 3695 }); 3696 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3697 } 3698 3699 fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { 3700 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 3701 const extra = self.air.extraData(Air.Block, ty_pl.payload); 3702 _ = extra; 3703 3704 return self.fail("TODO implement airCmpxchg for {}", .{ 3705 self.target.cpu.arch, 3706 }); 3707 } 3708 3709 fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void { 3710 _ = inst; 3711 return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch}); 3712 } 3713 3714 fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void { 3715 _ = inst; 3716 return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch}); 3717 } 3718 3719 fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void { 3720 _ = inst; 3721 _ = order; 3722 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); 3723 } 3724 3725 fn airMemset(self: *Self, inst: Air.Inst.Index) !void { 3726 _ = inst; 3727 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); 3728 } 3729 3730 fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { 3731 _ = inst; 3732 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); 3733 } 3734 3735 fn airTagName(self: *Self, inst: Air.Inst.Index) !void { 3736 const un_op = self.air.instructions.items(.data)[inst].un_op; 3737 const operand = try self.resolveInst(un_op); 3738 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { 3739 _ = operand; 3740 return self.fail("TODO implement airTagName for aarch64", .{}); 3741 }; 3742 return self.finishAir(inst, result, .{ un_op, .none, .none }); 3743 } 3744 3745 fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { 3746 const un_op = self.air.instructions.items(.data)[inst].un_op; 3747 const operand = try self.resolveInst(un_op); 3748 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { 3749 _ = operand; 3750 return self.fail("TODO implement airErrorName for aarch64", .{}); 3751 }; 3752 return self.finishAir(inst, result, .{ un_op, .none, .none }); 3753 } 3754 3755 fn airSplat(self: *Self, inst: Air.Inst.Index) !void { 3756 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3757 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for {}", .{self.target.cpu.arch}); 3758 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3759 } 3760 3761 fn airSelect(self: *Self, inst: Air.Inst.Index) !void { 3762 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 3763 const extra = self.air.extraData(Air.Bin, pl_op.payload).data; 3764 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for {}", .{self.target.cpu.arch}); 3765 return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs }); 3766 } 3767 3768 fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { 3769 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3770 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for {}", .{self.target.cpu.arch}); 3771 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3772 } 3773 3774 fn airReduce(self: *Self, inst: Air.Inst.Index) !void { 3775 const reduce = self.air.instructions.items(.data)[inst].reduce; 3776 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for aarch64", .{}); 3777 return self.finishAir(inst, result, .{ reduce.operand, .none, .none }); 3778 } 3779 3780 fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { 3781 const vector_ty = self.air.typeOfIndex(inst); 3782 const len = vector_ty.vectorLen(); 3783 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 3784 const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); 3785 const result: MCValue = res: { 3786 if (self.liveness.isUnused(inst)) break :res MCValue.dead; 3787 return self.fail("TODO implement airAggregateInit for {}", .{self.target.cpu.arch}); 3788 }; 3789 3790 if (elements.len <= Liveness.bpi - 1) { 3791 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); 3792 std.mem.copy(Air.Inst.Ref, &buf, elements); 3793 return self.finishAir(inst, result, buf); 3794 } 3795 var bt = try self.iterateBigTomb(inst, elements.len); 3796 for (elements) |elem| { 3797 bt.feed(elem); 3798 } 3799 return bt.finishAir(result); 3800 } 3801 3802 fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void { 3803 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 3804 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; 3805 _ = extra; 3806 return self.fail("TODO implement airUnionInit for aarch64", .{}); 3807 } 3808 3809 fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void { 3810 const prefetch = self.air.instructions.items(.data)[inst].prefetch; 3811 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none }); 3812 } 3813 3814 fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { 3815 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 3816 const extra = self.air.extraData(Air.Bin, pl_op.payload).data; 3817 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { 3818 return self.fail("TODO implement airMulAdd for aarch64", .{}); 3819 }; 3820 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); 3821 } 3822 3823 fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { 3824 // First section of indexes correspond to a set number of constant values. 3825 const ref_int = @enumToInt(inst); 3826 if (ref_int < Air.Inst.Ref.typed_value_map.len) { 3827 const tv = Air.Inst.Ref.typed_value_map[ref_int]; 3828 if (!tv.ty.hasRuntimeBits()) { 3829 return MCValue{ .none = {} }; 3830 } 3831 return self.genTypedValue(tv); 3832 } 3833 3834 // If the type has no codegen bits, no need to store it. 3835 const inst_ty = self.air.typeOf(inst); 3836 if (!inst_ty.hasRuntimeBits()) 3837 return MCValue{ .none = {} }; 3838 3839 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len); 3840 switch (self.air.instructions.items(.tag)[inst_index]) { 3841 .constant => { 3842 // Constants have static lifetimes, so they are always memoized in the outer most table. 3843 const branch = &self.branch_stack.items[0]; 3844 const gop = try branch.inst_table.getOrPut(self.gpa, inst_index); 3845 if (!gop.found_existing) { 3846 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl; 3847 gop.value_ptr.* = try self.genTypedValue(.{ 3848 .ty = inst_ty, 3849 .val = self.air.values[ty_pl.payload], 3850 }); 3851 } 3852 return gop.value_ptr.*; 3853 }, 3854 .const_ty => unreachable, 3855 else => return self.getResolvedInstValue(inst_index), 3856 } 3857 } 3858 3859 fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { 3860 // Treat each stack item as a "layer" on top of the previous one. 3861 var i: usize = self.branch_stack.items.len; 3862 while (true) { 3863 i -= 1; 3864 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| { 3865 assert(mcv != .dead); 3866 return mcv; 3867 } 3868 } 3869 } 3870 3871 fn lowerDeclRef(self: *Self, tv: TypedValue, decl_index: Module.Decl.Index) InnerError!MCValue { 3872 const ptr_bits = self.target.cpu.arch.ptrBitWidth(); 3873 const ptr_bytes: u64 = @divExact(ptr_bits, 8); 3874 3875 // TODO this feels clunky. Perhaps we should check for it in `genTypedValue`? 3876 if (tv.ty.zigTypeTag() == .Pointer) blk: { 3877 if (tv.ty.castPtrToFn()) |_| break :blk; 3878 if (!tv.ty.elemType2().hasRuntimeBits()) { 3879 return MCValue.none; 3880 } 3881 } 3882 3883 const mod = self.bin_file.options.module.?; 3884 const decl = mod.declPtr(decl_index); 3885 mod.markDeclAlive(decl); 3886 3887 if (self.bin_file.cast(link.File.Elf)) |elf_file| { 3888 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; 3889 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; 3890 return MCValue{ .memory = got_addr }; 3891 } else if (self.bin_file.cast(link.File.MachO)) |_| { 3892 // Because MachO is PIE-always-on, we defer memory address resolution until 3893 // the linker has enough info to perform relocations. 3894 assert(decl.link.macho.local_sym_index != 0); 3895 return MCValue{ .got_load = decl.link.macho.local_sym_index }; 3896 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { 3897 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; 3898 return MCValue{ .memory = got_addr }; 3899 } else if (self.bin_file.cast(link.File.Plan9)) |p9| { 3900 try p9.seeDecl(decl_index); 3901 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; 3902 return MCValue{ .memory = got_addr }; 3903 } else { 3904 return self.fail("TODO codegen non-ELF const Decl pointer", .{}); 3905 } 3906 _ = tv; 3907 } 3908 3909 fn lowerUnnamedConst(self: *Self, tv: TypedValue) InnerError!MCValue { 3910 log.debug("lowerUnnamedConst: ty = {}, val = {}", .{ tv.ty.fmtDebug(), tv.val.fmtDebug() }); 3911 const local_sym_index = self.bin_file.lowerUnnamedConst(tv, self.mod_fn.owner_decl) catch |err| { 3912 return self.fail("lowering unnamed constant failed: {s}", .{@errorName(err)}); 3913 }; 3914 if (self.bin_file.cast(link.File.Elf)) |elf_file| { 3915 const vaddr = elf_file.local_symbols.items[local_sym_index].st_value; 3916 return MCValue{ .memory = vaddr }; 3917 } else if (self.bin_file.cast(link.File.MachO)) |_| { 3918 return MCValue{ .direct_load = local_sym_index }; 3919 } else if (self.bin_file.cast(link.File.Coff)) |_| { 3920 return self.fail("TODO lower unnamed const in COFF", .{}); 3921 } else if (self.bin_file.cast(link.File.Plan9)) |_| { 3922 return self.fail("TODO lower unnamed const in Plan9", .{}); 3923 } else { 3924 return self.fail("TODO lower unnamed const", .{}); 3925 } 3926 } 3927 3928 fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { 3929 if (typed_value.val.isUndef()) 3930 return MCValue{ .undef = {} }; 3931 3932 if (typed_value.val.castTag(.decl_ref)) |payload| { 3933 return self.lowerDeclRef(typed_value, payload.data); 3934 } 3935 if (typed_value.val.castTag(.decl_ref_mut)) |payload| { 3936 return self.lowerDeclRef(typed_value, payload.data.decl_index); 3937 } 3938 const target = self.target.*; 3939 3940 switch (typed_value.ty.zigTypeTag()) { 3941 .Pointer => switch (typed_value.ty.ptrSize()) { 3942 .Slice => { 3943 return self.lowerUnnamedConst(typed_value); 3944 }, 3945 else => { 3946 switch (typed_value.val.tag()) { 3947 .int_u64 => { 3948 return MCValue{ .immediate = typed_value.val.toUnsignedInt(target) }; 3949 }, 3950 .slice => { 3951 return self.lowerUnnamedConst(typed_value); 3952 }, 3953 else => { 3954 return self.fail("TODO codegen more kinds of const pointers: {}", .{typed_value.val.tag()}); 3955 }, 3956 } 3957 }, 3958 }, 3959 .Int => { 3960 const info = typed_value.ty.intInfo(self.target.*); 3961 if (info.bits <= 64) { 3962 const unsigned = switch (info.signedness) { 3963 .signed => blk: { 3964 const signed = typed_value.val.toSignedInt(); 3965 break :blk @bitCast(u64, signed); 3966 }, 3967 .unsigned => typed_value.val.toUnsignedInt(target), 3968 }; 3969 3970 return MCValue{ .immediate = unsigned }; 3971 } else { 3972 return self.lowerUnnamedConst(typed_value); 3973 } 3974 }, 3975 .Bool => { 3976 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; 3977 }, 3978 .ComptimeInt => unreachable, // semantic analysis prevents this 3979 .ComptimeFloat => unreachable, // semantic analysis prevents this 3980 .Optional => { 3981 if (typed_value.ty.isPtrLikeOptional()) { 3982 if (typed_value.val.isNull()) 3983 return MCValue{ .immediate = 0 }; 3984 3985 var buf: Type.Payload.ElemType = undefined; 3986 return self.genTypedValue(.{ 3987 .ty = typed_value.ty.optionalChild(&buf), 3988 .val = typed_value.val, 3989 }); 3990 } else if (typed_value.ty.abiSize(self.target.*) == 1) { 3991 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; 3992 } 3993 return self.fail("TODO non pointer optionals", .{}); 3994 }, 3995 .Enum => { 3996 if (typed_value.val.castTag(.enum_field_index)) |field_index| { 3997 switch (typed_value.ty.tag()) { 3998 .enum_simple => { 3999 return MCValue{ .immediate = field_index.data }; 4000 }, 4001 .enum_full, .enum_nonexhaustive => { 4002 const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data; 4003 if (enum_full.values.count() != 0) { 4004 const tag_val = enum_full.values.keys()[field_index.data]; 4005 return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val }); 4006 } else { 4007 return MCValue{ .immediate = field_index.data }; 4008 } 4009 }, 4010 else => unreachable, 4011 } 4012 } else { 4013 var int_tag_buffer: Type.Payload.Bits = undefined; 4014 const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer); 4015 return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val }); 4016 } 4017 }, 4018 .ErrorSet => { 4019 const err_name = typed_value.val.castTag(.@"error").?.data.name; 4020 const module = self.bin_file.options.module.?; 4021 const global_error_set = module.global_error_set; 4022 const error_index = global_error_set.get(err_name).?; 4023 return MCValue{ .immediate = error_index }; 4024 }, 4025 .ErrorUnion => { 4026 const error_type = typed_value.ty.errorUnionSet(); 4027 const payload_type = typed_value.ty.errorUnionPayload(); 4028 4029 if (typed_value.val.castTag(.eu_payload)) |pl| { 4030 if (!payload_type.hasRuntimeBits()) { 4031 // We use the error type directly as the type. 4032 return MCValue{ .immediate = 0 }; 4033 } 4034 4035 _ = pl; 4036 return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty.fmtDebug()}); 4037 } else { 4038 if (!payload_type.hasRuntimeBits()) { 4039 // We use the error type directly as the type. 4040 return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); 4041 } 4042 4043 return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty.fmtDebug()}); 4044 } 4045 }, 4046 .Struct => { 4047 return self.lowerUnnamedConst(typed_value); 4048 }, 4049 else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty.fmtDebug()}), 4050 } 4051 } 4052 4053 const CallMCValues = struct { 4054 args: []MCValue, 4055 return_value: MCValue, 4056 stack_byte_count: u32, 4057 stack_align: u32, 4058 4059 fn deinit(self: *CallMCValues, func: *Self) void { 4060 func.gpa.free(self.args); 4061 self.* = undefined; 4062 } 4063 }; 4064 4065 /// Caller must call `CallMCValues.deinit`. 4066 fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { 4067 const cc = fn_ty.fnCallingConvention(); 4068 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen()); 4069 defer self.gpa.free(param_types); 4070 fn_ty.fnParamTypes(param_types); 4071 var result: CallMCValues = .{ 4072 .args = try self.gpa.alloc(MCValue, param_types.len), 4073 // These undefined values must be populated before returning from this function. 4074 .return_value = undefined, 4075 .stack_byte_count = undefined, 4076 .stack_align = undefined, 4077 }; 4078 errdefer self.gpa.free(result.args); 4079 4080 const ret_ty = fn_ty.fnReturnType(); 4081 4082 switch (cc) { 4083 .Naked => { 4084 assert(result.args.len == 0); 4085 result.return_value = .{ .unreach = {} }; 4086 result.stack_byte_count = 0; 4087 result.stack_align = 1; 4088 return result; 4089 }, 4090 .Unspecified, .C => { 4091 // ARM64 Procedure Call Standard 4092 var ncrn: usize = 0; // Next Core Register Number 4093 var nsaa: u32 = 0; // Next stacked argument address 4094 4095 for (param_types) |ty, i| { 4096 const param_size = @intCast(u32, ty.abiSize(self.target.*)); 4097 if (param_size == 0) { 4098 result.args[i] = .{ .none = {} }; 4099 continue; 4100 } 4101 4102 // We round up NCRN only for non-Apple platforms which allow the 16-byte aligned 4103 // values to spread across odd-numbered registers. 4104 if (ty.abiAlignment(self.target.*) == 16 and !self.target.isDarwin()) { 4105 // Round up NCRN to the next even number 4106 ncrn += ncrn % 2; 4107 } 4108 4109 if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) { 4110 if (param_size <= 8) { 4111 result.args[i] = .{ .register = registerAlias(c_abi_int_param_regs[ncrn], param_size) }; 4112 ncrn += 1; 4113 } else { 4114 return self.fail("TODO MCValues with multiple registers", .{}); 4115 } 4116 } else if (ncrn < 8 and nsaa == 0) { 4117 return self.fail("TODO MCValues split between registers and stack", .{}); 4118 } else { 4119 ncrn = 8; 4120 // TODO Apple allows the arguments on the stack to be non-8-byte aligned provided 4121 // that the entire stack space consumed by the arguments is 8-byte aligned. 4122 if (ty.abiAlignment(self.target.*) == 8) { 4123 if (nsaa % 8 != 0) { 4124 nsaa += 8 - (nsaa % 8); 4125 } 4126 } 4127 4128 result.args[i] = .{ .stack_offset = nsaa }; 4129 nsaa += param_size; 4130 } 4131 } 4132 4133 result.stack_byte_count = nsaa; 4134 result.stack_align = 16; 4135 }, 4136 else => return self.fail("TODO implement function parameters for {} on aarch64", .{cc}), 4137 } 4138 4139 if (ret_ty.zigTypeTag() == .NoReturn) { 4140 result.return_value = .{ .unreach = {} }; 4141 } else if (!ret_ty.hasRuntimeBits()) { 4142 result.return_value = .{ .none = {} }; 4143 } else switch (cc) { 4144 .Naked => unreachable, 4145 .Unspecified, .C => { 4146 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); 4147 if (ret_ty_size <= 8) { 4148 result.return_value = .{ .register = registerAlias(c_abi_int_return_regs[0], ret_ty_size) }; 4149 } else { 4150 return self.fail("TODO support more return types for ARM backend", .{}); 4151 } 4152 }, 4153 else => return self.fail("TODO implement function return values for {}", .{cc}), 4154 } 4155 return result; 4156 } 4157 4158 /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`. 4159 fn wantSafety(self: *Self) bool { 4160 return switch (self.bin_file.options.optimize_mode) { 4161 .Debug => true, 4162 .ReleaseSafe => true, 4163 .ReleaseFast => false, 4164 .ReleaseSmall => false, 4165 }; 4166 } 4167 4168 fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { 4169 @setCold(true); 4170 assert(self.err_msg == null); 4171 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); 4172 return error.CodegenFail; 4173 } 4174 4175 fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError { 4176 @setCold(true); 4177 assert(self.err_msg == null); 4178 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); 4179 return error.CodegenFail; 4180 } 4181 4182 fn parseRegName(name: []const u8) ?Register { 4183 if (@hasDecl(Register, "parseRegName")) { 4184 return Register.parseRegName(name); 4185 } 4186 return std.meta.stringToEnum(Register, name); 4187 } 4188 4189 fn registerAlias(reg: Register, size_bytes: u64) Register { 4190 if (size_bytes == 0) { 4191 unreachable; // should be comptime known 4192 } else if (size_bytes <= 4) { 4193 return reg.to32(); 4194 } else if (size_bytes <= 8) { 4195 return reg.to64(); 4196 } else { 4197 unreachable; // TODO handle floating-point registers 4198 } 4199 } 4200 4201 /// Resolves any aliased registers to the 64-bit wide ones. 4202 fn toCanonicalReg(reg: Register) Register { 4203 return reg.to64(); 4204 }