blob e2e2ce9e (262129B) - 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 codegen = @import("../../codegen.zig"); 7 const Air = @import("../../Air.zig"); 8 const Mir = @import("Mir.zig"); 9 const Emit = @import("Emit.zig"); 10 const Liveness = @import("../../Liveness.zig"); 11 const Type = @import("../../type.zig").Type; 12 const Value = @import("../../value.zig").Value; 13 const TypedValue = @import("../../TypedValue.zig"); 14 const link = @import("../../link.zig"); 15 const Module = @import("../../Module.zig"); 16 const Compilation = @import("../../Compilation.zig"); 17 const ErrorMsg = Module.ErrorMsg; 18 const Target = std.Target; 19 const Allocator = mem.Allocator; 20 const trace = @import("../../tracy.zig").trace; 21 const DW = std.dwarf; 22 const leb128 = std.leb; 23 const log = std.log.scoped(.codegen); 24 const build_options = @import("build_options"); 25 26 const CodeGenError = codegen.CodeGenError; 27 const Result = codegen.Result; 28 const DebugInfoOutput = codegen.DebugInfoOutput; 29 30 const bits = @import("bits.zig"); 31 const abi = @import("abi.zig"); 32 const errUnionPayloadOffset = codegen.errUnionPayloadOffset; 33 const errUnionErrorOffset = codegen.errUnionErrorOffset; 34 const RegisterManager = abi.RegisterManager; 35 const RegisterLock = RegisterManager.RegisterLock; 36 const Register = bits.Register; 37 const Instruction = bits.Instruction; 38 const Condition = bits.Instruction.Condition; 39 const callee_preserved_regs = abi.callee_preserved_regs; 40 const c_abi_int_param_regs = abi.c_abi_int_param_regs; 41 const c_abi_int_return_regs = abi.c_abi_int_return_regs; 42 const gp = abi.RegisterClass.gp; 43 44 const InnerError = CodeGenError || error{OutOfRegisters}; 45 46 gpa: Allocator, 47 air: Air, 48 liveness: Liveness, 49 bin_file: *link.File, 50 debug_output: DebugInfoOutput, 51 target: *const std.Target, 52 mod_fn: *const Module.Fn, 53 err_msg: ?*ErrorMsg, 54 args: []MCValue, 55 ret_mcv: MCValue, 56 fn_type: Type, 57 arg_index: u32, 58 src_loc: Module.SrcLoc, 59 stack_align: u32, 60 61 /// MIR Instructions 62 mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, 63 /// MIR extra data 64 mir_extra: std.ArrayListUnmanaged(u32) = .{}, 65 66 /// Byte offset within the source file of the ending curly. 67 end_di_line: u32, 68 end_di_column: u32, 69 70 /// The value is an offset into the `Function` `code` from the beginning. 71 /// To perform the reloc, write 32-bit signed little-endian integer 72 /// which is a relative jump, based on the address following the reloc. 73 exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{}, 74 75 /// We postpone the creation of debug info for function args and locals 76 /// until after all Mir instructions have been generated. Only then we 77 /// will know saved_regs_stack_space which is necessary in order to 78 /// calculate the right stack offsest with respect to the `.fp` register. 79 dbg_info_relocs: std.ArrayListUnmanaged(DbgInfoReloc) = .{}, 80 81 /// Whenever there is a runtime branch, we push a Branch onto this stack, 82 /// and pop it off when the runtime branch joins. This provides an "overlay" 83 /// of the table of mappings from instructions to `MCValue` from within the branch. 84 /// This way we can modify the `MCValue` for an instruction in different ways 85 /// within different branches. Special consideration is needed when a branch 86 /// joins with its parent, to make sure all instructions have the same MCValue 87 /// across each runtime branch upon joining. 88 branch_stack: *std.ArrayList(Branch), 89 90 // Key is the block instruction 91 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, 92 93 register_manager: RegisterManager = .{}, 94 /// Maps offset to what is stored there. 95 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, 96 /// Tracks the current instruction allocated to the compare flags 97 compare_flags_inst: ?Air.Inst.Index = null, 98 99 /// Offset from the stack base, representing the end of the stack frame. 100 max_end_stack: u32 = 0, 101 /// Represents the current end stack offset. If there is no existing slot 102 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`. 103 next_stack_offset: u32 = 0, 104 105 saved_regs_stack_space: u32 = 0, 106 107 /// Debug field, used to find bugs in the compiler. 108 air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, 109 110 const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; 111 112 const MCValue = union(enum) { 113 /// No runtime bits. `void` types, empty structs, u0, enums with 1 114 /// tag, etc. 115 /// 116 /// TODO Look into deleting this tag and using `dead` instead, 117 /// since every use of MCValue.none should be instead looking at 118 /// the type and noticing it is 0 bits. 119 none, 120 /// Control flow will not allow this value to be observed. 121 unreach, 122 /// No more references to this value remain. 123 dead, 124 /// The value is undefined. 125 undef, 126 /// A pointer-sized integer that fits in a register. 127 /// 128 /// If the type is a pointer, this is the pointer address in 129 /// virtual address space. 130 immediate: u64, 131 /// The value is in a target-specific register. 132 register: Register, 133 /// The value is a tuple { wrapped: u32, overflow: u1 } where 134 /// wrapped is stored in the register and the overflow bit is 135 /// stored in the C (signed) or V (unsigned) flag of the CPSR. 136 /// 137 /// This MCValue is only generated by a add_with_overflow or 138 /// sub_with_overflow instruction operating on 32- or 64-bit values. 139 register_with_overflow: struct { reg: Register, flag: bits.Instruction.Condition }, 140 /// The value is in memory at a hard-coded address. 141 /// 142 /// If the type is a pointer, it means the pointer address is at 143 /// this memory location. 144 memory: u64, 145 /// The value is in memory but requires a linker relocation fixup. 146 linker_load: codegen.LinkerLoad, 147 /// The value is one of the stack variables. 148 /// 149 /// If the type is a pointer, it means the pointer address is in 150 /// the stack at this offset. 151 stack_offset: u32, 152 /// The value is a pointer to one of the stack variables (payload 153 /// is stack offset). 154 ptr_stack_offset: u32, 155 /// The value resides in the N, Z, C, V flags. The value is 1 (if 156 /// the type is u1) or true (if the type in bool) iff the 157 /// specified condition is true. 158 compare_flags: Condition, 159 /// The value is a function argument passed via the stack. 160 stack_argument_offset: u32, 161 }; 162 163 const DbgInfoReloc = struct { 164 tag: Air.Inst.Tag, 165 ty: Type, 166 name: [:0]const u8, 167 mcv: MCValue, 168 169 fn genDbgInfo(reloc: DbgInfoReloc, function: Self) !void { 170 switch (reloc.tag) { 171 .arg => try reloc.genArgDbgInfo(function), 172 173 .dbg_var_ptr, 174 .dbg_var_val, 175 => try reloc.genVarDbgInfo(function), 176 177 else => unreachable, 178 } 179 } 180 181 fn genArgDbgInfo(reloc: DbgInfoReloc, function: Self) error{OutOfMemory}!void { 182 switch (function.debug_output) { 183 .dwarf => |dw| { 184 const loc: link.File.Dwarf.DeclState.DbgInfoLoc = switch (reloc.mcv) { 185 .register => |reg| .{ .register = reg.dwarfLocOp() }, 186 .stack_offset, 187 .stack_argument_offset, 188 => |offset| blk: { 189 const adjusted_offset = switch (reloc.mcv) { 190 .stack_offset => -@intCast(i32, offset), 191 .stack_argument_offset => @intCast(i32, function.saved_regs_stack_space + offset), 192 else => unreachable, 193 }; 194 break :blk .{ .stack = .{ 195 .fp_register = Register.x29.dwarfLocOpDeref(), 196 .offset = adjusted_offset, 197 } }; 198 }, 199 else => unreachable, // not a possible argument 200 201 }; 202 try dw.genArgDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, loc); 203 }, 204 .plan9 => {}, 205 .none => {}, 206 } 207 } 208 209 fn genVarDbgInfo(reloc: DbgInfoReloc, function: Self) !void { 210 const is_ptr = switch (reloc.tag) { 211 .dbg_var_ptr => true, 212 .dbg_var_val => false, 213 else => unreachable, 214 }; 215 216 switch (function.debug_output) { 217 .dwarf => |dw| { 218 const loc: link.File.Dwarf.DeclState.DbgInfoLoc = switch (reloc.mcv) { 219 .register => |reg| .{ .register = reg.dwarfLocOp() }, 220 .ptr_stack_offset, 221 .stack_offset, 222 .stack_argument_offset, 223 => |offset| blk: { 224 const adjusted_offset = switch (reloc.mcv) { 225 .ptr_stack_offset, 226 .stack_offset, 227 => -@intCast(i32, offset), 228 .stack_argument_offset => @intCast(i32, function.saved_regs_stack_space + offset), 229 else => unreachable, 230 }; 231 break :blk .{ 232 .stack = .{ 233 .fp_register = Register.x29.dwarfLocOpDeref(), 234 .offset = adjusted_offset, 235 }, 236 }; 237 }, 238 .memory => |address| .{ .memory = address }, 239 .linker_load => |linker_load| .{ .linker_load = linker_load }, 240 .immediate => |x| .{ .immediate = x }, 241 .undef => .undef, 242 .none => .none, 243 else => blk: { 244 log.debug("TODO generate debug info for {}", .{reloc.mcv}); 245 break :blk .nop; 246 }, 247 }; 248 try dw.genVarDbgInfo(reloc.name, reloc.ty, function.mod_fn.owner_decl, is_ptr, loc); 249 }, 250 .plan9 => {}, 251 .none => {}, 252 } 253 } 254 }; 255 256 const Branch = struct { 257 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{}, 258 259 fn deinit(self: *Branch, gpa: Allocator) void { 260 self.inst_table.deinit(gpa); 261 self.* = undefined; 262 } 263 }; 264 265 const StackAllocation = struct { 266 inst: Air.Inst.Index, 267 /// TODO do we need size? should be determined by inst.ty.abiSize() 268 size: u32, 269 }; 270 271 const BlockData = struct { 272 relocs: std.ArrayListUnmanaged(Mir.Inst.Index), 273 /// The first break instruction encounters `null` here and chooses a 274 /// machine code value for the block result, populating this field. 275 /// Following break instructions encounter that value and use it for 276 /// the location to store their block results. 277 mcv: MCValue, 278 }; 279 280 const BigTomb = struct { 281 function: *Self, 282 inst: Air.Inst.Index, 283 lbt: Liveness.BigTomb, 284 285 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void { 286 const dies = bt.lbt.feed(); 287 const op_index = Air.refToIndex(op_ref) orelse return; 288 if (!dies) return; 289 bt.function.processDeath(op_index); 290 } 291 292 fn finishAir(bt: *BigTomb, result: MCValue) void { 293 const is_used = !bt.function.liveness.isUnused(bt.inst); 294 if (is_used) { 295 log.debug("%{d} => {}", .{ bt.inst, result }); 296 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1]; 297 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result); 298 299 switch (result) { 300 .register => |reg| { 301 // In some cases (such as bitcast), an operand 302 // may be the same MCValue as the result. If 303 // that operand died and was a register, it 304 // was freed by processDeath. We have to 305 // "re-allocate" the register. 306 if (bt.function.register_manager.isRegFree(reg)) { 307 bt.function.register_manager.getRegAssumeFree(reg, bt.inst); 308 } 309 }, 310 .register_with_overflow => |rwo| { 311 if (bt.function.register_manager.isRegFree(rwo.reg)) { 312 bt.function.register_manager.getRegAssumeFree(rwo.reg, bt.inst); 313 } 314 bt.function.compare_flags_inst = bt.inst; 315 }, 316 .compare_flags => |_| { 317 bt.function.compare_flags_inst = bt.inst; 318 }, 319 else => {}, 320 } 321 } 322 bt.function.finishAirBookkeeping(); 323 } 324 }; 325 326 const Self = @This(); 327 328 pub fn generate( 329 bin_file: *link.File, 330 src_loc: Module.SrcLoc, 331 module_fn: *Module.Fn, 332 air: Air, 333 liveness: Liveness, 334 code: *std.ArrayList(u8), 335 debug_output: DebugInfoOutput, 336 ) CodeGenError!Result { 337 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { 338 @panic("Attempted to compile for architecture that was disabled by build configuration"); 339 } 340 341 const mod = bin_file.options.module.?; 342 const fn_owner_decl = mod.declPtr(module_fn.owner_decl); 343 assert(fn_owner_decl.has_tv); 344 const fn_type = fn_owner_decl.ty; 345 346 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator); 347 defer { 348 assert(branch_stack.items.len == 1); 349 branch_stack.items[0].deinit(bin_file.allocator); 350 branch_stack.deinit(); 351 } 352 try branch_stack.append(.{}); 353 354 var function = Self{ 355 .gpa = bin_file.allocator, 356 .air = air, 357 .liveness = liveness, 358 .debug_output = debug_output, 359 .target = &bin_file.options.target, 360 .bin_file = bin_file, 361 .mod_fn = module_fn, 362 .err_msg = null, 363 .args = undefined, // populated after `resolveCallingConventionValues` 364 .ret_mcv = undefined, // populated after `resolveCallingConventionValues` 365 .fn_type = fn_type, 366 .arg_index = 0, 367 .branch_stack = &branch_stack, 368 .src_loc = src_loc, 369 .stack_align = undefined, 370 .end_di_line = module_fn.rbrace_line, 371 .end_di_column = module_fn.rbrace_column, 372 }; 373 defer function.stack.deinit(bin_file.allocator); 374 defer function.blocks.deinit(bin_file.allocator); 375 defer function.exitlude_jump_relocs.deinit(bin_file.allocator); 376 defer function.dbg_info_relocs.deinit(bin_file.allocator); 377 378 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { 379 error.CodegenFail => return Result{ .fail = function.err_msg.? }, 380 error.OutOfRegisters => return Result{ 381 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), 382 }, 383 else => |e| return e, 384 }; 385 defer call_info.deinit(&function); 386 387 function.args = call_info.args; 388 function.ret_mcv = call_info.return_value; 389 function.stack_align = call_info.stack_align; 390 function.max_end_stack = call_info.stack_byte_count; 391 392 function.gen() catch |err| switch (err) { 393 error.CodegenFail => return Result{ .fail = function.err_msg.? }, 394 error.OutOfRegisters => return Result{ 395 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), 396 }, 397 else => |e| return e, 398 }; 399 400 for (function.dbg_info_relocs.items) |reloc| { 401 try reloc.genDbgInfo(function); 402 } 403 404 var mir = Mir{ 405 .instructions = function.mir_instructions.toOwnedSlice(), 406 .extra = try function.mir_extra.toOwnedSlice(bin_file.allocator), 407 }; 408 defer mir.deinit(bin_file.allocator); 409 410 var emit = Emit{ 411 .mir = mir, 412 .bin_file = bin_file, 413 .debug_output = debug_output, 414 .target = &bin_file.options.target, 415 .src_loc = src_loc, 416 .code = code, 417 .prev_di_pc = 0, 418 .prev_di_line = module_fn.lbrace_line, 419 .prev_di_column = module_fn.lbrace_column, 420 .stack_size = function.max_end_stack, 421 .saved_regs_stack_space = function.saved_regs_stack_space, 422 }; 423 defer emit.deinit(); 424 425 emit.emitMir() catch |err| switch (err) { 426 error.EmitFail => return Result{ .fail = emit.err_msg.? }, 427 else => |e| return e, 428 }; 429 430 if (function.err_msg) |em| { 431 return Result{ .fail = em }; 432 } else { 433 return Result.ok; 434 } 435 } 436 437 fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { 438 const gpa = self.gpa; 439 440 try self.mir_instructions.ensureUnusedCapacity(gpa, 1); 441 442 const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len); 443 self.mir_instructions.appendAssumeCapacity(inst); 444 return result_index; 445 } 446 447 fn addNop(self: *Self) error{OutOfMemory}!Mir.Inst.Index { 448 return try self.addInst(.{ 449 .tag = .nop, 450 .data = .{ .nop = {} }, 451 }); 452 } 453 454 pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 { 455 const fields = std.meta.fields(@TypeOf(extra)); 456 try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len); 457 return self.addExtraAssumeCapacity(extra); 458 } 459 460 pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { 461 const fields = std.meta.fields(@TypeOf(extra)); 462 const result = @intCast(u32, self.mir_extra.items.len); 463 inline for (fields) |field| { 464 self.mir_extra.appendAssumeCapacity(switch (field.type) { 465 u32 => @field(extra, field.name), 466 i32 => @bitCast(u32, @field(extra, field.name)), 467 else => @compileError("bad field type"), 468 }); 469 } 470 return result; 471 } 472 473 fn gen(self: *Self) !void { 474 const cc = self.fn_type.fnCallingConvention(); 475 if (cc != .Naked) { 476 // stp fp, lr, [sp, #-16]! 477 _ = try self.addInst(.{ 478 .tag = .stp, 479 .data = .{ .load_store_register_pair = .{ 480 .rt = .x29, 481 .rt2 = .x30, 482 .rn = .sp, 483 .offset = Instruction.LoadStorePairOffset.pre_index(-16), 484 } }, 485 }); 486 487 // <store other registers> 488 const backpatch_save_registers = try self.addNop(); 489 490 // mov fp, sp 491 _ = try self.addInst(.{ 492 .tag = .mov_to_from_sp, 493 .data = .{ .rr = .{ .rd = .x29, .rn = .sp } }, 494 }); 495 496 // sub sp, sp, #reloc 497 const backpatch_reloc = try self.addNop(); 498 499 if (self.ret_mcv == .stack_offset) { 500 // The address of where to store the return value is in x0 501 // (or w0 when pointer size is 32 bits). As this register 502 // might get overwritten along the way, save the address 503 // to the stack. 504 const ptr_bits = self.target.cpu.arch.ptrBitWidth(); 505 const ptr_bytes = @divExact(ptr_bits, 8); 506 const ret_ptr_reg = self.registerAlias(.x0, Type.usize); 507 508 const stack_offset = try self.allocMem(ptr_bytes, ptr_bytes, null); 509 510 try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = ret_ptr_reg }); 511 self.ret_mcv = MCValue{ .stack_offset = stack_offset }; 512 } 513 514 for (self.args, 0..) |*arg, arg_index| { 515 // Copy register arguments to the stack 516 switch (arg.*) { 517 .register => |reg| { 518 // The first AIR instructions of the main body are guaranteed 519 // to be the functions arguments 520 const inst = self.air.getMainBody()[arg_index]; 521 assert(self.air.instructions.items(.tag)[inst] == .arg); 522 523 const ty = self.air.typeOfIndex(inst); 524 525 const abi_size = @intCast(u32, ty.abiSize(self.target.*)); 526 const abi_align = ty.abiAlignment(self.target.*); 527 const stack_offset = try self.allocMem(abi_size, abi_align, inst); 528 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); 529 530 arg.* = MCValue{ .stack_offset = stack_offset }; 531 }, 532 else => {}, 533 } 534 } 535 536 _ = try self.addInst(.{ 537 .tag = .dbg_prologue_end, 538 .data = .{ .nop = {} }, 539 }); 540 541 try self.genBody(self.air.getMainBody()); 542 543 // Backpatch push callee saved regs 544 var saved_regs: u32 = 0; 545 self.saved_regs_stack_space = 16; 546 inline for (callee_preserved_regs) |reg| { 547 if (self.register_manager.isRegAllocated(reg)) { 548 saved_regs |= @as(u32, 1) << @intCast(u5, reg.id()); 549 self.saved_regs_stack_space += 8; 550 } 551 } 552 553 // Emit.mirPopPushRegs automatically adds extra empty space so 554 // that sp is always aligned to 16 555 if (!std.mem.isAlignedGeneric(u32, self.saved_regs_stack_space, 16)) { 556 self.saved_regs_stack_space += 8; 557 } 558 assert(std.mem.isAlignedGeneric(u32, self.saved_regs_stack_space, 16)); 559 560 self.mir_instructions.set(backpatch_save_registers, .{ 561 .tag = .push_regs, 562 .data = .{ .reg_list = saved_regs }, 563 }); 564 565 // Backpatch stack offset 566 const total_stack_size = self.max_end_stack + self.saved_regs_stack_space; 567 const aligned_total_stack_end = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align); 568 const stack_size = aligned_total_stack_end - self.saved_regs_stack_space; 569 self.max_end_stack = stack_size; 570 if (math.cast(u12, stack_size)) |size| { 571 self.mir_instructions.set(backpatch_reloc, .{ 572 .tag = .sub_immediate, 573 .data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = size } }, 574 }); 575 } else { 576 return self.failSymbol("TODO AArch64: allow larger stacks", .{}); 577 } 578 579 _ = try self.addInst(.{ 580 .tag = .dbg_epilogue_begin, 581 .data = .{ .nop = {} }, 582 }); 583 584 // exitlude jumps 585 if (self.exitlude_jump_relocs.items.len > 0 and 586 self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2) 587 { 588 // If the last Mir instruction (apart from the 589 // dbg_epilogue_begin) is the last exitlude jump 590 // relocation (which would just jump one instruction 591 // further), it can be safely removed 592 self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop()); 593 } 594 595 for (self.exitlude_jump_relocs.items) |jmp_reloc| { 596 self.mir_instructions.set(jmp_reloc, .{ 597 .tag = .b, 598 .data = .{ .inst = @intCast(u32, self.mir_instructions.len) }, 599 }); 600 } 601 602 // add sp, sp, #stack_size 603 _ = try self.addInst(.{ 604 .tag = .add_immediate, 605 .data = .{ .rr_imm12_sh = .{ .rd = .sp, .rn = .sp, .imm12 = @intCast(u12, stack_size) } }, 606 }); 607 608 // <load other registers> 609 _ = try self.addInst(.{ 610 .tag = .pop_regs, 611 .data = .{ .reg_list = saved_regs }, 612 }); 613 614 // ldp fp, lr, [sp], #16 615 _ = try self.addInst(.{ 616 .tag = .ldp, 617 .data = .{ .load_store_register_pair = .{ 618 .rt = .x29, 619 .rt2 = .x30, 620 .rn = .sp, 621 .offset = Instruction.LoadStorePairOffset.post_index(16), 622 } }, 623 }); 624 625 // ret lr 626 _ = try self.addInst(.{ 627 .tag = .ret, 628 .data = .{ .reg = .x30 }, 629 }); 630 } else { 631 _ = try self.addInst(.{ 632 .tag = .dbg_prologue_end, 633 .data = .{ .nop = {} }, 634 }); 635 636 try self.genBody(self.air.getMainBody()); 637 638 _ = try self.addInst(.{ 639 .tag = .dbg_epilogue_begin, 640 .data = .{ .nop = {} }, 641 }); 642 } 643 644 // Drop them off at the rbrace. 645 _ = try self.addInst(.{ 646 .tag = .dbg_line, 647 .data = .{ .dbg_line_column = .{ 648 .line = self.end_di_line, 649 .column = self.end_di_column, 650 } }, 651 }); 652 } 653 654 fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { 655 const air_tags = self.air.instructions.items(.tag); 656 657 for (body) |inst| { 658 const old_air_bookkeeping = self.air_bookkeeping; 659 try self.ensureProcessDeathCapacity(Liveness.bpi); 660 661 switch (air_tags[inst]) { 662 // zig fmt: off 663 .add => try self.airBinOp(inst, .add), 664 .addwrap => try self.airBinOp(inst, .addwrap), 665 .sub => try self.airBinOp(inst, .sub), 666 .subwrap => try self.airBinOp(inst, .subwrap), 667 .mul => try self.airBinOp(inst, .mul), 668 .mulwrap => try self.airBinOp(inst, .mulwrap), 669 .shl => try self.airBinOp(inst, .shl), 670 .shl_exact => try self.airBinOp(inst, .shl_exact), 671 .bool_and => try self.airBinOp(inst, .bool_and), 672 .bool_or => try self.airBinOp(inst, .bool_or), 673 .bit_and => try self.airBinOp(inst, .bit_and), 674 .bit_or => try self.airBinOp(inst, .bit_or), 675 .xor => try self.airBinOp(inst, .xor), 676 .shr => try self.airBinOp(inst, .shr), 677 .shr_exact => try self.airBinOp(inst, .shr_exact), 678 .div_float => try self.airBinOp(inst, .div_float), 679 .div_trunc => try self.airBinOp(inst, .div_trunc), 680 .div_floor => try self.airBinOp(inst, .div_floor), 681 .div_exact => try self.airBinOp(inst, .div_exact), 682 .rem => try self.airBinOp(inst, .rem), 683 .mod => try self.airBinOp(inst, .mod), 684 685 .ptr_add => try self.airPtrArithmetic(inst, .ptr_add), 686 .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub), 687 688 .min => try self.airMinMax(inst), 689 .max => try self.airMinMax(inst), 690 691 .add_sat => try self.airAddSat(inst), 692 .sub_sat => try self.airSubSat(inst), 693 .mul_sat => try self.airMulSat(inst), 694 .shl_sat => try self.airShlSat(inst), 695 .slice => try self.airSlice(inst), 696 697 .sqrt, 698 .sin, 699 .cos, 700 .tan, 701 .exp, 702 .exp2, 703 .log, 704 .log2, 705 .log10, 706 .fabs, 707 .floor, 708 .ceil, 709 .round, 710 .trunc_float, 711 .neg, 712 => try self.airUnaryMath(inst), 713 714 .add_with_overflow => try self.airOverflow(inst), 715 .sub_with_overflow => try self.airOverflow(inst), 716 .mul_with_overflow => try self.airMulWithOverflow(inst), 717 .shl_with_overflow => try self.airShlWithOverflow(inst), 718 719 .cmp_lt => try self.airCmp(inst, .lt), 720 .cmp_lte => try self.airCmp(inst, .lte), 721 .cmp_eq => try self.airCmp(inst, .eq), 722 .cmp_gte => try self.airCmp(inst, .gte), 723 .cmp_gt => try self.airCmp(inst, .gt), 724 .cmp_neq => try self.airCmp(inst, .neq), 725 726 .cmp_vector => try self.airCmpVector(inst), 727 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), 728 729 .alloc => try self.airAlloc(inst), 730 .ret_ptr => try self.airRetPtr(inst), 731 .arg => try self.airArg(inst), 732 .assembly => try self.airAsm(inst), 733 .bitcast => try self.airBitCast(inst), 734 .block => try self.airBlock(inst), 735 .br => try self.airBr(inst), 736 .trap => try self.airTrap(), 737 .breakpoint => try self.airBreakpoint(), 738 .ret_addr => try self.airRetAddr(inst), 739 .frame_addr => try self.airFrameAddress(inst), 740 .fence => try self.airFence(), 741 .cond_br => try self.airCondBr(inst), 742 .dbg_stmt => try self.airDbgStmt(inst), 743 .fptrunc => try self.airFptrunc(inst), 744 .fpext => try self.airFpext(inst), 745 .intcast => try self.airIntCast(inst), 746 .trunc => try self.airTrunc(inst), 747 .bool_to_int => try self.airBoolToInt(inst), 748 .is_non_null => try self.airIsNonNull(inst), 749 .is_non_null_ptr => try self.airIsNonNullPtr(inst), 750 .is_null => try self.airIsNull(inst), 751 .is_null_ptr => try self.airIsNullPtr(inst), 752 .is_non_err => try self.airIsNonErr(inst), 753 .is_non_err_ptr => try self.airIsNonErrPtr(inst), 754 .is_err => try self.airIsErr(inst), 755 .is_err_ptr => try self.airIsErrPtr(inst), 756 .load => try self.airLoad(inst), 757 .loop => try self.airLoop(inst), 758 .not => try self.airNot(inst), 759 .ptrtoint => try self.airPtrToInt(inst), 760 .ret => try self.airRet(inst), 761 .ret_load => try self.airRetLoad(inst), 762 .store => try self.airStore(inst), 763 .struct_field_ptr=> try self.airStructFieldPtr(inst), 764 .struct_field_val=> try self.airStructFieldVal(inst), 765 .array_to_slice => try self.airArrayToSlice(inst), 766 .int_to_float => try self.airIntToFloat(inst), 767 .float_to_int => try self.airFloatToInt(inst), 768 .cmpxchg_strong => try self.airCmpxchg(inst), 769 .cmpxchg_weak => try self.airCmpxchg(inst), 770 .atomic_rmw => try self.airAtomicRmw(inst), 771 .atomic_load => try self.airAtomicLoad(inst), 772 .memcpy => try self.airMemcpy(inst), 773 .memset => try self.airMemset(inst), 774 .set_union_tag => try self.airSetUnionTag(inst), 775 .get_union_tag => try self.airGetUnionTag(inst), 776 .clz => try self.airClz(inst), 777 .ctz => try self.airCtz(inst), 778 .popcount => try self.airPopcount(inst), 779 .byte_swap => try self.airByteSwap(inst), 780 .bit_reverse => try self.airBitReverse(inst), 781 .tag_name => try self.airTagName(inst), 782 .error_name => try self.airErrorName(inst), 783 .splat => try self.airSplat(inst), 784 .select => try self.airSelect(inst), 785 .shuffle => try self.airShuffle(inst), 786 .reduce => try self.airReduce(inst), 787 .aggregate_init => try self.airAggregateInit(inst), 788 .union_init => try self.airUnionInit(inst), 789 .prefetch => try self.airPrefetch(inst), 790 .mul_add => try self.airMulAdd(inst), 791 .addrspace_cast => return self.fail("TODO implement addrspace_cast", .{}), 792 793 .@"try" => try self.airTry(inst), 794 .try_ptr => try self.airTryPtr(inst), 795 796 .dbg_var_ptr, 797 .dbg_var_val, 798 => try self.airDbgVar(inst), 799 800 .dbg_inline_begin, 801 .dbg_inline_end, 802 => try self.airDbgInline(inst), 803 804 .dbg_block_begin, 805 .dbg_block_end, 806 => try self.airDbgBlock(inst), 807 808 .call => try self.airCall(inst, .auto), 809 .call_always_tail => try self.airCall(inst, .always_tail), 810 .call_never_tail => try self.airCall(inst, .never_tail), 811 .call_never_inline => try self.airCall(inst, .never_inline), 812 813 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), 814 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), 815 .atomic_store_release => try self.airAtomicStore(inst, .Release), 816 .atomic_store_seq_cst => try self.airAtomicStore(inst, .SeqCst), 817 818 .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), 819 .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), 820 .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), 821 .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), 822 823 .field_parent_ptr => try self.airFieldParentPtr(inst), 824 825 .switch_br => try self.airSwitch(inst), 826 .slice_ptr => try self.airSlicePtr(inst), 827 .slice_len => try self.airSliceLen(inst), 828 829 .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst), 830 .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst), 831 832 .array_elem_val => try self.airArrayElemVal(inst), 833 .slice_elem_val => try self.airSliceElemVal(inst), 834 .slice_elem_ptr => try self.airSliceElemPtr(inst), 835 .ptr_elem_val => try self.airPtrElemVal(inst), 836 .ptr_elem_ptr => try self.airPtrElemPtr(inst), 837 838 .constant => unreachable, // excluded from function bodies 839 .const_ty => unreachable, // excluded from function bodies 840 .unreach => self.finishAirBookkeeping(), 841 842 .optional_payload => try self.airOptionalPayload(inst), 843 .optional_payload_ptr => try self.airOptionalPayloadPtr(inst), 844 .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst), 845 .unwrap_errunion_err => try self.airUnwrapErrErr(inst), 846 .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), 847 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), 848 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), 849 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst), 850 .err_return_trace => try self.airErrReturnTrace(inst), 851 .set_err_return_trace => try self.airSetErrReturnTrace(inst), 852 .save_err_return_trace_index=> try self.airSaveErrReturnTraceIndex(inst), 853 854 .wrap_optional => try self.airWrapOptional(inst), 855 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), 856 .wrap_errunion_err => try self.airWrapErrUnionErr(inst), 857 858 .add_optimized, 859 .addwrap_optimized, 860 .sub_optimized, 861 .subwrap_optimized, 862 .mul_optimized, 863 .mulwrap_optimized, 864 .div_float_optimized, 865 .div_trunc_optimized, 866 .div_floor_optimized, 867 .div_exact_optimized, 868 .rem_optimized, 869 .mod_optimized, 870 .neg_optimized, 871 .cmp_lt_optimized, 872 .cmp_lte_optimized, 873 .cmp_eq_optimized, 874 .cmp_gte_optimized, 875 .cmp_gt_optimized, 876 .cmp_neq_optimized, 877 .cmp_vector_optimized, 878 .reduce_optimized, 879 .float_to_int_optimized, 880 => return self.fail("TODO implement optimized float mode", .{}), 881 882 .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}), 883 .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), 884 .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}), 885 886 .c_va_arg => return self.fail("TODO implement c_va_arg", .{}), 887 .c_va_copy => return self.fail("TODO implement c_va_copy", .{}), 888 .c_va_end => return self.fail("TODO implement c_va_end", .{}), 889 .c_va_start => return self.fail("TODO implement c_va_start", .{}), 890 891 .wasm_memory_size => unreachable, 892 .wasm_memory_grow => unreachable, 893 // zig fmt: on 894 } 895 896 assert(!self.register_manager.lockedRegsExist()); 897 898 if (std.debug.runtime_safety) { 899 if (self.air_bookkeeping < old_air_bookkeeping + 1) { 900 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] }); 901 } 902 } 903 } 904 } 905 906 /// Asserts there is already capacity to insert into top branch inst_table. 907 fn processDeath(self: *Self, inst: Air.Inst.Index) void { 908 const air_tags = self.air.instructions.items(.tag); 909 if (air_tags[inst] == .constant) return; // Constants are immortal. 910 // When editing this function, note that the logic must synchronize with `reuseOperand`. 911 const prev_value = self.getResolvedInstValue(inst); 912 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 913 branch.inst_table.putAssumeCapacity(inst, .dead); 914 switch (prev_value) { 915 .register => |reg| { 916 self.register_manager.freeReg(reg); 917 }, 918 .register_with_overflow => |rwo| { 919 self.register_manager.freeReg(rwo.reg); 920 self.compare_flags_inst = null; 921 }, 922 .compare_flags => { 923 self.compare_flags_inst = null; 924 }, 925 else => {}, // TODO process stack allocation death 926 } 927 } 928 929 /// Called when there are no operands, and the instruction is always unreferenced. 930 fn finishAirBookkeeping(self: *Self) void { 931 if (std.debug.runtime_safety) { 932 self.air_bookkeeping += 1; 933 } 934 } 935 936 fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void { 937 var tomb_bits = self.liveness.getTombBits(inst); 938 for (operands) |op| { 939 const dies = @truncate(u1, tomb_bits) != 0; 940 tomb_bits >>= 1; 941 if (!dies) continue; 942 const op_int = @enumToInt(op); 943 if (op_int < Air.Inst.Ref.typed_value_map.len) continue; 944 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); 945 self.processDeath(op_index); 946 } 947 const is_used = @truncate(u1, tomb_bits) == 0; 948 if (is_used) { 949 log.debug("%{d} => {}", .{ inst, result }); 950 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 951 branch.inst_table.putAssumeCapacityNoClobber(inst, result); 952 953 switch (result) { 954 .register => |reg| { 955 // In some cases (such as bitcast), an operand 956 // may be the same MCValue as the result. If 957 // that operand died and was a register, it 958 // was freed by processDeath. We have to 959 // "re-allocate" the register. 960 if (self.register_manager.isRegFree(reg)) { 961 self.register_manager.getRegAssumeFree(reg, inst); 962 } 963 }, 964 .register_with_overflow => |rwo| { 965 if (self.register_manager.isRegFree(rwo.reg)) { 966 self.register_manager.getRegAssumeFree(rwo.reg, inst); 967 } 968 self.compare_flags_inst = inst; 969 }, 970 .compare_flags => |_| { 971 self.compare_flags_inst = inst; 972 }, 973 else => {}, 974 } 975 } 976 self.finishAirBookkeeping(); 977 } 978 979 fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { 980 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table; 981 try table.ensureUnusedCapacity(self.gpa, additional_count); 982 } 983 984 fn allocMem( 985 self: *Self, 986 abi_size: u32, 987 abi_align: u32, 988 maybe_inst: ?Air.Inst.Index, 989 ) !u32 { 990 assert(abi_size > 0); 991 assert(abi_align > 0); 992 993 // In order to efficiently load and store stack items that fit 994 // into registers, we bump up the alignment to the next power of 995 // two. 996 const adjusted_align = if (abi_size > 8) 997 abi_align 998 else 999 std.math.ceilPowerOfTwoAssert(u32, abi_size); 1000 1001 // TODO find a free slot instead of always appending 1002 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, adjusted_align) + abi_size; 1003 self.next_stack_offset = offset; 1004 self.max_end_stack = @max(self.max_end_stack, self.next_stack_offset); 1005 1006 if (maybe_inst) |inst| { 1007 try self.stack.putNoClobber(self.gpa, offset, .{ 1008 .inst = inst, 1009 .size = abi_size, 1010 }); 1011 } 1012 1013 return offset; 1014 } 1015 1016 /// Use a pointer instruction as the basis for allocating stack memory. 1017 fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { 1018 const elem_ty = self.air.typeOfIndex(inst).elemType(); 1019 1020 if (!elem_ty.hasRuntimeBits()) { 1021 // return the stack offset 0. Stack offset 0 will be where all 1022 // zero-sized stack allocations live as non-zero-sized 1023 // allocations will always have an offset > 0. 1024 return @as(u32, 0); 1025 } 1026 1027 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { 1028 const mod = self.bin_file.options.module.?; 1029 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); 1030 }; 1031 // TODO swap this for inst.ty.ptrAlign 1032 const abi_align = elem_ty.abiAlignment(self.target.*); 1033 1034 return self.allocMem(abi_size, abi_align, inst); 1035 } 1036 1037 fn allocRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool, maybe_inst: ?Air.Inst.Index) !MCValue { 1038 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse { 1039 const mod = self.bin_file.options.module.?; 1040 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); 1041 }; 1042 const abi_align = elem_ty.abiAlignment(self.target.*); 1043 1044 if (reg_ok) { 1045 // Make sure the type can fit in a register before we try to allocate one. 1046 if (abi_size <= 8) { 1047 if (self.register_manager.tryAllocReg(maybe_inst, gp)) |reg| { 1048 return MCValue{ .register = self.registerAlias(reg, elem_ty) }; 1049 } 1050 } 1051 } 1052 1053 const stack_offset = try self.allocMem(abi_size, abi_align, maybe_inst); 1054 return MCValue{ .stack_offset = stack_offset }; 1055 } 1056 1057 pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { 1058 const stack_mcv = try self.allocRegOrMem(self.air.typeOfIndex(inst), false, inst); 1059 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); 1060 1061 const reg_mcv = self.getResolvedInstValue(inst); 1062 switch (reg_mcv) { 1063 .register => |r| assert(reg.id() == r.id()), 1064 .register_with_overflow => |rwo| assert(rwo.reg.id() == reg.id()), 1065 else => unreachable, // not a register 1066 } 1067 1068 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 1069 try branch.inst_table.put(self.gpa, inst, stack_mcv); 1070 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); 1071 } 1072 1073 /// Save the current instruction stored in the compare flags if 1074 /// occupied 1075 fn spillCompareFlagsIfOccupied(self: *Self) !void { 1076 if (self.compare_flags_inst) |inst_to_save| { 1077 const ty = self.air.typeOfIndex(inst_to_save); 1078 const mcv = self.getResolvedInstValue(inst_to_save); 1079 const new_mcv = switch (mcv) { 1080 .compare_flags => try self.allocRegOrMem(ty, true, inst_to_save), 1081 .register_with_overflow => try self.allocRegOrMem(ty, false, inst_to_save), 1082 else => unreachable, // mcv doesn't occupy the compare flags 1083 }; 1084 1085 try self.setRegOrMem(self.air.typeOfIndex(inst_to_save), new_mcv, mcv); 1086 log.debug("spilling {d} to mcv {any}", .{ inst_to_save, new_mcv }); 1087 1088 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 1089 try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); 1090 1091 self.compare_flags_inst = null; 1092 1093 // TODO consolidate with register manager and spillInstruction 1094 // this call should really belong in the register manager! 1095 switch (mcv) { 1096 .register_with_overflow => |rwo| self.register_manager.freeReg(rwo.reg), 1097 else => {}, 1098 } 1099 } 1100 } 1101 1102 /// Copies a value to a register without tracking the register. The register is not considered 1103 /// allocated. A second call to `copyToTmpRegister` may return the same register. 1104 /// This can have a side effect of spilling instructions to the stack to free up a register. 1105 fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { 1106 const raw_reg = try self.register_manager.allocReg(null, gp); 1107 const reg = self.registerAlias(raw_reg, ty); 1108 try self.genSetReg(ty, reg, mcv); 1109 return reg; 1110 } 1111 1112 /// Allocates a new register and copies `mcv` into it. 1113 /// `reg_owner` is the instruction that gets associated with the register in the register table. 1114 /// This can have a side effect of spilling instructions to the stack to free up a register. 1115 fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { 1116 const raw_reg = try self.register_manager.allocReg(reg_owner, gp); 1117 const ty = self.air.typeOfIndex(reg_owner); 1118 const reg = self.registerAlias(raw_reg, ty); 1119 try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); 1120 return MCValue{ .register = reg }; 1121 } 1122 1123 fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { 1124 const stack_offset = try self.allocMemPtr(inst); 1125 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); 1126 } 1127 1128 fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { 1129 const result: MCValue = switch (self.ret_mcv) { 1130 .none, .register => .{ .ptr_stack_offset = try self.allocMemPtr(inst) }, 1131 .stack_offset => blk: { 1132 // self.ret_mcv is an address to where this function 1133 // should store its result into 1134 const ret_ty = self.fn_type.fnReturnType(); 1135 var ptr_ty_payload: Type.Payload.ElemType = .{ 1136 .base = .{ .tag = .single_mut_pointer }, 1137 .data = ret_ty, 1138 }; 1139 const ptr_ty = Type.initPayload(&ptr_ty_payload.base); 1140 1141 // addr_reg will contain the address of where to store the 1142 // result into 1143 const addr_reg = try self.copyToTmpRegister(ptr_ty, self.ret_mcv); 1144 break :blk .{ .register = addr_reg }; 1145 }, 1146 else => unreachable, // invalid return result 1147 }; 1148 1149 return self.finishAir(inst, result, .{ .none, .none, .none }); 1150 } 1151 1152 fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { 1153 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1154 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch}); 1155 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1156 } 1157 1158 fn airFpext(self: *Self, inst: Air.Inst.Index) !void { 1159 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1160 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch}); 1161 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1162 } 1163 1164 fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { 1165 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1166 if (self.liveness.isUnused(inst)) 1167 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); 1168 1169 const operand = ty_op.operand; 1170 const operand_mcv = try self.resolveInst(operand); 1171 const operand_ty = self.air.typeOf(operand); 1172 const operand_info = operand_ty.intInfo(self.target.*); 1173 1174 const dest_ty = self.air.typeOfIndex(inst); 1175 const dest_info = dest_ty.intInfo(self.target.*); 1176 1177 const result: MCValue = result: { 1178 const operand_lock: ?RegisterLock = switch (operand_mcv) { 1179 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 1180 else => null, 1181 }; 1182 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); 1183 1184 const truncated: MCValue = switch (operand_mcv) { 1185 .register => |r| MCValue{ .register = self.registerAlias(r, dest_ty) }, 1186 else => operand_mcv, 1187 }; 1188 1189 if (dest_info.bits > operand_info.bits) { 1190 const dest_mcv = try self.allocRegOrMem(dest_ty, true, inst); 1191 try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, truncated); 1192 break :result dest_mcv; 1193 } else { 1194 if (self.reuseOperand(inst, operand, 0, truncated)) { 1195 break :result truncated; 1196 } else { 1197 const dest_mcv = try self.allocRegOrMem(dest_ty, true, inst); 1198 try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, truncated); 1199 break :result dest_mcv; 1200 } 1201 } 1202 }; 1203 1204 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1205 } 1206 1207 fn truncRegister( 1208 self: *Self, 1209 operand_reg: Register, 1210 dest_reg: Register, 1211 int_signedness: std.builtin.Signedness, 1212 int_bits: u16, 1213 ) !void { 1214 switch (int_bits) { 1215 1...31, 33...63 => { 1216 _ = try self.addInst(.{ 1217 .tag = switch (int_signedness) { 1218 .signed => .sbfx, 1219 .unsigned => .ubfx, 1220 }, 1221 .data = .{ .rr_lsb_width = .{ 1222 .rd = dest_reg, 1223 .rn = operand_reg, 1224 .lsb = 0, 1225 .width = @intCast(u6, int_bits), 1226 } }, 1227 }); 1228 }, 1229 32, 64 => { 1230 _ = try self.addInst(.{ 1231 .tag = .mov_register, 1232 .data = .{ .rr = .{ 1233 .rd = if (int_bits == 32) dest_reg.toW() else dest_reg.toX(), 1234 .rn = if (int_bits == 32) operand_reg.toW() else operand_reg.toX(), 1235 } }, 1236 }); 1237 }, 1238 else => unreachable, 1239 } 1240 } 1241 1242 fn trunc( 1243 self: *Self, 1244 maybe_inst: ?Air.Inst.Index, 1245 operand: MCValue, 1246 operand_ty: Type, 1247 dest_ty: Type, 1248 ) !MCValue { 1249 const info_a = operand_ty.intInfo(self.target.*); 1250 const info_b = dest_ty.intInfo(self.target.*); 1251 1252 if (info_b.bits <= 64) { 1253 const operand_reg = switch (operand) { 1254 .register => |r| r, 1255 else => operand_reg: { 1256 if (info_a.bits <= 64) { 1257 const raw_reg = try self.copyToTmpRegister(operand_ty, operand); 1258 break :operand_reg self.registerAlias(raw_reg, operand_ty); 1259 } else { 1260 return self.fail("TODO load least significant word into register", .{}); 1261 } 1262 }, 1263 }; 1264 const lock = self.register_manager.lockReg(operand_reg); 1265 defer if (lock) |reg| self.register_manager.unlockReg(reg); 1266 1267 const dest_reg = if (maybe_inst) |inst| blk: { 1268 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1269 1270 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { 1271 break :blk self.registerAlias(operand_reg, dest_ty); 1272 } else { 1273 const raw_reg = try self.register_manager.allocReg(inst, gp); 1274 break :blk self.registerAlias(raw_reg, dest_ty); 1275 } 1276 } else blk: { 1277 const raw_reg = try self.register_manager.allocReg(null, gp); 1278 break :blk self.registerAlias(raw_reg, dest_ty); 1279 }; 1280 1281 try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits); 1282 1283 return MCValue{ .register = dest_reg }; 1284 } else { 1285 return self.fail("TODO: truncate to ints > 64 bits", .{}); 1286 } 1287 } 1288 1289 fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { 1290 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1291 const operand = try self.resolveInst(ty_op.operand); 1292 const operand_ty = self.air.typeOf(ty_op.operand); 1293 const dest_ty = self.air.typeOfIndex(inst); 1294 1295 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { 1296 break :blk try self.trunc(inst, operand, operand_ty, dest_ty); 1297 }; 1298 1299 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1300 } 1301 1302 fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void { 1303 const un_op = self.air.instructions.items(.data)[inst].un_op; 1304 const operand = try self.resolveInst(un_op); 1305 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand; 1306 return self.finishAir(inst, result, .{ un_op, .none, .none }); 1307 } 1308 1309 fn airNot(self: *Self, inst: Air.Inst.Index) !void { 1310 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1311 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1312 const operand = try self.resolveInst(ty_op.operand); 1313 const operand_ty = self.air.typeOf(ty_op.operand); 1314 switch (operand) { 1315 .dead => unreachable, 1316 .unreach => unreachable, 1317 .compare_flags => |cond| break :result MCValue{ .compare_flags = cond.negate() }, 1318 else => { 1319 switch (operand_ty.zigTypeTag()) { 1320 .Bool => { 1321 // TODO convert this to mvn + and 1322 const op_reg = switch (operand) { 1323 .register => |r| r, 1324 else => try self.copyToTmpRegister(operand_ty, operand), 1325 }; 1326 const reg_lock = self.register_manager.lockRegAssumeUnused(op_reg); 1327 defer self.register_manager.unlockReg(reg_lock); 1328 1329 const dest_reg = blk: { 1330 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { 1331 break :blk op_reg; 1332 } 1333 1334 const raw_reg = try self.register_manager.allocReg(null, gp); 1335 break :blk self.registerAlias(raw_reg, operand_ty); 1336 }; 1337 1338 _ = try self.addInst(.{ 1339 .tag = .eor_immediate, 1340 .data = .{ .rr_bitmask = .{ 1341 .rd = dest_reg, 1342 .rn = op_reg, 1343 .imms = 0b000000, 1344 .immr = 0b000000, 1345 .n = 0b0, 1346 } }, 1347 }); 1348 1349 break :result MCValue{ .register = dest_reg }; 1350 }, 1351 .Vector => return self.fail("TODO bitwise not for vectors", .{}), 1352 .Int => { 1353 const int_info = operand_ty.intInfo(self.target.*); 1354 if (int_info.bits <= 64) { 1355 const op_reg = switch (operand) { 1356 .register => |r| r, 1357 else => try self.copyToTmpRegister(operand_ty, operand), 1358 }; 1359 const reg_lock = self.register_manager.lockRegAssumeUnused(op_reg); 1360 defer self.register_manager.unlockReg(reg_lock); 1361 1362 const dest_reg = blk: { 1363 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) { 1364 break :blk op_reg; 1365 } 1366 1367 const raw_reg = try self.register_manager.allocReg(null, gp); 1368 break :blk self.registerAlias(raw_reg, operand_ty); 1369 }; 1370 1371 _ = try self.addInst(.{ 1372 .tag = .mvn, 1373 .data = .{ .rr_imm6_logical_shift = .{ 1374 .rd = dest_reg, 1375 .rm = op_reg, 1376 .imm6 = 0, 1377 .shift = .lsl, 1378 } }, 1379 }); 1380 1381 try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); 1382 1383 break :result MCValue{ .register = dest_reg }; 1384 } else { 1385 return self.fail("TODO AArch64 not on integers > u64/i64", .{}); 1386 } 1387 }, 1388 else => unreachable, 1389 } 1390 }, 1391 } 1392 }; 1393 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1394 } 1395 1396 fn minMax( 1397 self: *Self, 1398 tag: Air.Inst.Tag, 1399 lhs_bind: ReadArg.Bind, 1400 rhs_bind: ReadArg.Bind, 1401 lhs_ty: Type, 1402 rhs_ty: Type, 1403 maybe_inst: ?Air.Inst.Index, 1404 ) !MCValue { 1405 switch (lhs_ty.zigTypeTag()) { 1406 .Float => return self.fail("TODO ARM min/max on floats", .{}), 1407 .Vector => return self.fail("TODO ARM min/max on vectors", .{}), 1408 .Int => { 1409 const mod = self.bin_file.options.module.?; 1410 assert(lhs_ty.eql(rhs_ty, mod)); 1411 const int_info = lhs_ty.intInfo(self.target.*); 1412 if (int_info.bits <= 64) { 1413 var lhs_reg: Register = undefined; 1414 var rhs_reg: Register = undefined; 1415 var dest_reg: Register = undefined; 1416 1417 const read_args = [_]ReadArg{ 1418 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, 1419 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, 1420 }; 1421 const write_args = [_]WriteArg{ 1422 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, 1423 }; 1424 try self.allocRegs( 1425 &read_args, 1426 &write_args, 1427 if (maybe_inst) |inst| .{ 1428 .corresponding_inst = inst, 1429 .operand_mapping = &.{ 0, 1 }, 1430 } else null, 1431 ); 1432 1433 // lhs == reg should have been checked by airMinMax 1434 assert(lhs_reg != rhs_reg); // see note above 1435 1436 _ = try self.addInst(.{ 1437 .tag = .cmp_shifted_register, 1438 .data = .{ .rr_imm6_shift = .{ 1439 .rn = lhs_reg, 1440 .rm = rhs_reg, 1441 .imm6 = 0, 1442 .shift = .lsl, 1443 } }, 1444 }); 1445 1446 const cond_choose_lhs: Condition = switch (tag) { 1447 .max => switch (int_info.signedness) { 1448 .signed => Condition.gt, 1449 .unsigned => Condition.hi, 1450 }, 1451 .min => switch (int_info.signedness) { 1452 .signed => Condition.lt, 1453 .unsigned => Condition.cc, 1454 }, 1455 else => unreachable, 1456 }; 1457 1458 _ = try self.addInst(.{ 1459 .tag = .csel, 1460 .data = .{ .rrr_cond = .{ 1461 .rd = dest_reg, 1462 .rn = lhs_reg, 1463 .rm = rhs_reg, 1464 .cond = cond_choose_lhs, 1465 } }, 1466 }); 1467 1468 return MCValue{ .register = dest_reg }; 1469 } else { 1470 return self.fail("TODO ARM min/max on integers > u32/i32", .{}); 1471 } 1472 }, 1473 else => unreachable, 1474 } 1475 } 1476 1477 fn airMinMax(self: *Self, inst: Air.Inst.Index) !void { 1478 const tag = self.air.instructions.items(.tag)[inst]; 1479 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1480 const lhs_ty = self.air.typeOf(bin_op.lhs); 1481 const rhs_ty = self.air.typeOf(bin_op.rhs); 1482 1483 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1484 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; 1485 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; 1486 1487 const lhs = try self.resolveInst(bin_op.lhs); 1488 if (bin_op.lhs == bin_op.rhs) break :result lhs; 1489 1490 break :result try self.minMax(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst); 1491 }; 1492 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1493 } 1494 1495 fn airSlice(self: *Self, inst: Air.Inst.Index) !void { 1496 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 1497 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 1498 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1499 const ptr = try self.resolveInst(bin_op.lhs); 1500 const ptr_ty = self.air.typeOf(bin_op.lhs); 1501 const len = try self.resolveInst(bin_op.rhs); 1502 const len_ty = self.air.typeOf(bin_op.rhs); 1503 1504 const ptr_bits = self.target.cpu.arch.ptrBitWidth(); 1505 const ptr_bytes = @divExact(ptr_bits, 8); 1506 1507 const stack_offset = try self.allocMem(ptr_bytes * 2, ptr_bytes * 2, inst); 1508 try self.genSetStack(ptr_ty, stack_offset, ptr); 1509 try self.genSetStack(len_ty, stack_offset - ptr_bytes, len); 1510 break :result MCValue{ .stack_offset = stack_offset }; 1511 }; 1512 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1513 } 1514 1515 /// An argument to a Mir instruction which is read (and possibly also 1516 /// written to) by the respective instruction 1517 const ReadArg = struct { 1518 ty: Type, 1519 bind: Bind, 1520 class: RegisterManager.RegisterBitSet, 1521 reg: *Register, 1522 1523 const Bind = union(enum) { 1524 inst: Air.Inst.Ref, 1525 mcv: MCValue, 1526 1527 fn resolveToMcv(bind: Bind, function: *Self) InnerError!MCValue { 1528 return switch (bind) { 1529 .inst => |inst| try function.resolveInst(inst), 1530 .mcv => |mcv| mcv, 1531 }; 1532 } 1533 1534 fn resolveToImmediate(bind: Bind, function: *Self) InnerError!?u64 { 1535 switch (bind) { 1536 .inst => |inst| { 1537 // TODO resolve independently of inst_table 1538 const mcv = try function.resolveInst(inst); 1539 switch (mcv) { 1540 .immediate => |imm| return imm, 1541 else => return null, 1542 } 1543 }, 1544 .mcv => |mcv| { 1545 switch (mcv) { 1546 .immediate => |imm| return imm, 1547 else => return null, 1548 } 1549 }, 1550 } 1551 } 1552 }; 1553 }; 1554 1555 /// An argument to a Mir instruction which is written to (but not read 1556 /// from) by the respective instruction 1557 const WriteArg = struct { 1558 ty: Type, 1559 bind: Bind, 1560 class: RegisterManager.RegisterBitSet, 1561 reg: *Register, 1562 1563 const Bind = union(enum) { 1564 reg: Register, 1565 none: void, 1566 }; 1567 }; 1568 1569 /// Holds all data necessary for enabling the potential reuse of 1570 /// operand registers as destinations 1571 const ReuseMetadata = struct { 1572 corresponding_inst: Air.Inst.Index, 1573 1574 /// Maps every element index of read_args to the corresponding 1575 /// index in the Air instruction 1576 /// 1577 /// When the order of read_args corresponds exactly to the order 1578 /// of the inputs of the Air instruction, this would be e.g. 1579 /// &.{ 0, 1 }. However, when the order is not the same or some 1580 /// inputs to the Air instruction are omitted (e.g. when they can 1581 /// be represented as immediates to the Mir instruction), 1582 /// operand_mapping should reflect that fact. 1583 operand_mapping: []const Liveness.OperandInt, 1584 }; 1585 1586 /// Allocate a set of registers for use as arguments for a Mir 1587 /// instruction 1588 /// 1589 /// If the Mir instruction these registers are allocated for 1590 /// corresponds exactly to a single Air instruction, populate 1591 /// reuse_metadata in order to enable potential reuse of an operand as 1592 /// the destination (provided that that operand dies in this 1593 /// instruction). 1594 /// 1595 /// Reusing an operand register as destination is the only time two 1596 /// arguments may share the same register. In all other cases, 1597 /// allocRegs guarantees that a register will never be allocated to 1598 /// more than one argument. 1599 /// 1600 /// Furthermore, allocReg guarantees that all arguments which are 1601 /// already bound to registers before calling allocRegs will not 1602 /// change their register binding. This is done by locking these 1603 /// registers. 1604 fn allocRegs( 1605 self: *Self, 1606 read_args: []const ReadArg, 1607 write_args: []const WriteArg, 1608 reuse_metadata: ?ReuseMetadata, 1609 ) InnerError!void { 1610 // Air instructions have exactly one output 1611 assert(!(reuse_metadata != null and write_args.len != 1)); // see note above 1612 1613 // The operand mapping is a 1:1 mapping of read args to their 1614 // corresponding operand index in the Air instruction 1615 assert(!(reuse_metadata != null and reuse_metadata.?.operand_mapping.len != read_args.len)); // see note above 1616 1617 const locks = try self.gpa.alloc(?RegisterLock, read_args.len + write_args.len); 1618 defer self.gpa.free(locks); 1619 const read_locks = locks[0..read_args.len]; 1620 const write_locks = locks[read_args.len..]; 1621 1622 std.mem.set(?RegisterLock, locks, null); 1623 defer for (locks) |lock| { 1624 if (lock) |locked_reg| self.register_manager.unlockReg(locked_reg); 1625 }; 1626 1627 // When we reuse a read_arg as a destination, the corresponding 1628 // MCValue of the read_arg will be set to .dead. In that case, we 1629 // skip allocating this read_arg. 1630 var reused_read_arg: ?usize = null; 1631 1632 // Lock all args which are already allocated to registers 1633 for (read_args, 0..) |arg, i| { 1634 const mcv = try arg.bind.resolveToMcv(self); 1635 if (mcv == .register) { 1636 read_locks[i] = self.register_manager.lockReg(mcv.register); 1637 } 1638 } 1639 1640 for (write_args, 0..) |arg, i| { 1641 if (arg.bind == .reg) { 1642 write_locks[i] = self.register_manager.lockReg(arg.bind.reg); 1643 } 1644 } 1645 1646 // Allocate registers for all args which aren't allocated to 1647 // registers yet 1648 for (read_args, 0..) |arg, i| { 1649 const mcv = try arg.bind.resolveToMcv(self); 1650 if (mcv == .register) { 1651 const raw_reg = mcv.register; 1652 arg.reg.* = self.registerAlias(raw_reg, arg.ty); 1653 } else { 1654 const track_inst: ?Air.Inst.Index = switch (arg.bind) { 1655 .inst => |inst| Air.refToIndex(inst).?, 1656 else => null, 1657 }; 1658 const raw_reg = try self.register_manager.allocReg(track_inst, gp); 1659 arg.reg.* = self.registerAlias(raw_reg, arg.ty); 1660 read_locks[i] = self.register_manager.lockRegAssumeUnused(arg.reg.*); 1661 } 1662 } 1663 1664 if (reuse_metadata != null) { 1665 const inst = reuse_metadata.?.corresponding_inst; 1666 const operand_mapping = reuse_metadata.?.operand_mapping; 1667 const arg = write_args[0]; 1668 if (arg.bind == .reg) { 1669 const raw_reg = arg.bind.reg; 1670 arg.reg.* = self.registerAlias(raw_reg, arg.ty); 1671 } else { 1672 reuse_operand: for (read_args, 0..) |read_arg, i| { 1673 if (read_arg.bind == .inst) { 1674 const operand = read_arg.bind.inst; 1675 const mcv = try self.resolveInst(operand); 1676 if (mcv == .register and 1677 std.meta.eql(arg.class, read_arg.class) and 1678 self.reuseOperand(inst, operand, operand_mapping[i], mcv)) 1679 { 1680 const raw_reg = mcv.register; 1681 arg.reg.* = self.registerAlias(raw_reg, arg.ty); 1682 write_locks[0] = null; 1683 reused_read_arg = i; 1684 break :reuse_operand; 1685 } 1686 } 1687 } else { 1688 const raw_reg = try self.register_manager.allocReg(inst, arg.class); 1689 arg.reg.* = self.registerAlias(raw_reg, arg.ty); 1690 write_locks[0] = self.register_manager.lockReg(arg.reg.*); 1691 } 1692 } 1693 } else { 1694 for (write_args, 0..) |arg, i| { 1695 if (arg.bind == .reg) { 1696 const raw_reg = arg.bind.reg; 1697 arg.reg.* = self.registerAlias(raw_reg, arg.ty); 1698 } else { 1699 const raw_reg = try self.register_manager.allocReg(null, arg.class); 1700 arg.reg.* = self.registerAlias(raw_reg, arg.ty); 1701 write_locks[i] = self.register_manager.lockReg(arg.reg.*); 1702 } 1703 } 1704 } 1705 1706 // For all read_args which need to be moved from non-register to 1707 // register, perform the move 1708 for (read_args, 0..) |arg, i| { 1709 if (reused_read_arg) |j| { 1710 // Check whether this read_arg was reused 1711 if (i == j) continue; 1712 } 1713 1714 const mcv = try arg.bind.resolveToMcv(self); 1715 if (mcv != .register) { 1716 if (arg.bind == .inst) { 1717 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 1718 const inst = Air.refToIndex(arg.bind.inst).?; 1719 1720 // Overwrite the MCValue associated with this inst 1721 branch.inst_table.putAssumeCapacity(inst, .{ .register = arg.reg.* }); 1722 1723 // If the previous MCValue occupied some space we track, we 1724 // need to make sure it is marked as free now. 1725 switch (mcv) { 1726 .compare_flags => { 1727 assert(self.compare_flags_inst.? == inst); 1728 self.compare_flags_inst = null; 1729 }, 1730 .register => |prev_reg| { 1731 assert(!self.register_manager.isRegFree(prev_reg)); 1732 self.register_manager.freeReg(prev_reg); 1733 }, 1734 else => {}, 1735 } 1736 } 1737 1738 try self.genSetReg(arg.ty, arg.reg.*, mcv); 1739 } 1740 } 1741 } 1742 1743 /// Wrapper around allocRegs and addInst tailored for specific Mir 1744 /// instructions which are binary operations acting on two registers 1745 /// 1746 /// Returns the destination register 1747 fn binOpRegister( 1748 self: *Self, 1749 mir_tag: Mir.Inst.Tag, 1750 lhs_bind: ReadArg.Bind, 1751 rhs_bind: ReadArg.Bind, 1752 lhs_ty: Type, 1753 rhs_ty: Type, 1754 maybe_inst: ?Air.Inst.Index, 1755 ) !MCValue { 1756 var lhs_reg: Register = undefined; 1757 var rhs_reg: Register = undefined; 1758 var dest_reg: Register = undefined; 1759 1760 const read_args = [_]ReadArg{ 1761 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, 1762 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, 1763 }; 1764 const write_args = [_]WriteArg{ 1765 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, 1766 }; 1767 try self.allocRegs( 1768 &read_args, 1769 &write_args, 1770 if (maybe_inst) |inst| .{ 1771 .corresponding_inst = inst, 1772 .operand_mapping = &.{ 0, 1 }, 1773 } else null, 1774 ); 1775 1776 const mir_data: Mir.Inst.Data = switch (mir_tag) { 1777 .add_shifted_register, 1778 .adds_shifted_register, 1779 .sub_shifted_register, 1780 .subs_shifted_register, 1781 => .{ .rrr_imm6_shift = .{ 1782 .rd = dest_reg, 1783 .rn = lhs_reg, 1784 .rm = rhs_reg, 1785 .imm6 = 0, 1786 .shift = .lsl, 1787 } }, 1788 .mul, 1789 .lsl_register, 1790 .asr_register, 1791 .lsr_register, 1792 .sdiv, 1793 .udiv, 1794 => .{ .rrr = .{ 1795 .rd = dest_reg, 1796 .rn = lhs_reg, 1797 .rm = rhs_reg, 1798 } }, 1799 .smull, 1800 .umull, 1801 => .{ .rrr = .{ 1802 .rd = dest_reg.toX(), 1803 .rn = lhs_reg, 1804 .rm = rhs_reg, 1805 } }, 1806 .and_shifted_register, 1807 .orr_shifted_register, 1808 .eor_shifted_register, 1809 => .{ .rrr_imm6_logical_shift = .{ 1810 .rd = dest_reg, 1811 .rn = lhs_reg, 1812 .rm = rhs_reg, 1813 .imm6 = 0, 1814 .shift = .lsl, 1815 } }, 1816 else => unreachable, 1817 }; 1818 1819 _ = try self.addInst(.{ 1820 .tag = mir_tag, 1821 .data = mir_data, 1822 }); 1823 1824 return MCValue{ .register = dest_reg }; 1825 } 1826 1827 /// Wrapper around allocRegs and addInst tailored for specific Mir 1828 /// instructions which are binary operations acting on a register and 1829 /// an immediate 1830 /// 1831 /// Returns the destination register 1832 fn binOpImmediate( 1833 self: *Self, 1834 mir_tag: Mir.Inst.Tag, 1835 lhs_bind: ReadArg.Bind, 1836 rhs_immediate: u64, 1837 lhs_ty: Type, 1838 lhs_and_rhs_swapped: bool, 1839 maybe_inst: ?Air.Inst.Index, 1840 ) !MCValue { 1841 var lhs_reg: Register = undefined; 1842 var dest_reg: Register = undefined; 1843 1844 const read_args = [_]ReadArg{ 1845 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, 1846 }; 1847 const write_args = [_]WriteArg{ 1848 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, 1849 }; 1850 const operand_mapping: []const Liveness.OperandInt = if (lhs_and_rhs_swapped) &.{1} else &.{0}; 1851 try self.allocRegs( 1852 &read_args, 1853 &write_args, 1854 if (maybe_inst) |inst| .{ 1855 .corresponding_inst = inst, 1856 .operand_mapping = operand_mapping, 1857 } else null, 1858 ); 1859 1860 const mir_data: Mir.Inst.Data = switch (mir_tag) { 1861 .add_immediate, 1862 .adds_immediate, 1863 .sub_immediate, 1864 .subs_immediate, 1865 => .{ .rr_imm12_sh = .{ 1866 .rd = dest_reg, 1867 .rn = lhs_reg, 1868 .imm12 = @intCast(u12, rhs_immediate), 1869 } }, 1870 .lsl_immediate, 1871 .asr_immediate, 1872 .lsr_immediate, 1873 => .{ .rr_shift = .{ 1874 .rd = dest_reg, 1875 .rn = lhs_reg, 1876 .shift = @intCast(u6, rhs_immediate), 1877 } }, 1878 else => unreachable, 1879 }; 1880 1881 _ = try self.addInst(.{ 1882 .tag = mir_tag, 1883 .data = mir_data, 1884 }); 1885 1886 return MCValue{ .register = dest_reg }; 1887 } 1888 1889 fn addSub( 1890 self: *Self, 1891 tag: Air.Inst.Tag, 1892 lhs_bind: ReadArg.Bind, 1893 rhs_bind: ReadArg.Bind, 1894 lhs_ty: Type, 1895 rhs_ty: Type, 1896 maybe_inst: ?Air.Inst.Index, 1897 ) InnerError!MCValue { 1898 const mod = self.bin_file.options.module.?; 1899 switch (lhs_ty.zigTypeTag()) { 1900 .Float => return self.fail("TODO binary operations on floats", .{}), 1901 .Vector => return self.fail("TODO binary operations on vectors", .{}), 1902 .Int => { 1903 assert(lhs_ty.eql(rhs_ty, mod)); 1904 const int_info = lhs_ty.intInfo(self.target.*); 1905 if (int_info.bits <= 64) { 1906 const lhs_immediate = try lhs_bind.resolveToImmediate(self); 1907 const rhs_immediate = try rhs_bind.resolveToImmediate(self); 1908 1909 // Only say yes if the operation is 1910 // commutative, i.e. we can swap both of the 1911 // operands 1912 const lhs_immediate_ok = switch (tag) { 1913 .add => if (lhs_immediate) |imm| imm <= std.math.maxInt(u12) else false, 1914 .sub => false, 1915 else => unreachable, 1916 }; 1917 const rhs_immediate_ok = switch (tag) { 1918 .add, 1919 .sub, 1920 => if (rhs_immediate) |imm| imm <= std.math.maxInt(u12) else false, 1921 else => unreachable, 1922 }; 1923 1924 const mir_tag_register: Mir.Inst.Tag = switch (tag) { 1925 .add => .add_shifted_register, 1926 .sub => .sub_shifted_register, 1927 else => unreachable, 1928 }; 1929 const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { 1930 .add => .add_immediate, 1931 .sub => .sub_immediate, 1932 else => unreachable, 1933 }; 1934 1935 if (rhs_immediate_ok) { 1936 return try self.binOpImmediate(mir_tag_immediate, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst); 1937 } else if (lhs_immediate_ok) { 1938 // swap lhs and rhs 1939 return try self.binOpImmediate(mir_tag_immediate, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst); 1940 } else { 1941 return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); 1942 } 1943 } else { 1944 return self.fail("TODO binary operations on int with bits > 64", .{}); 1945 } 1946 }, 1947 else => unreachable, 1948 } 1949 } 1950 1951 fn mul( 1952 self: *Self, 1953 lhs_bind: ReadArg.Bind, 1954 rhs_bind: ReadArg.Bind, 1955 lhs_ty: Type, 1956 rhs_ty: Type, 1957 maybe_inst: ?Air.Inst.Index, 1958 ) InnerError!MCValue { 1959 const mod = self.bin_file.options.module.?; 1960 switch (lhs_ty.zigTypeTag()) { 1961 .Vector => return self.fail("TODO binary operations on vectors", .{}), 1962 .Int => { 1963 assert(lhs_ty.eql(rhs_ty, mod)); 1964 const int_info = lhs_ty.intInfo(self.target.*); 1965 if (int_info.bits <= 64) { 1966 // TODO add optimisations for multiplication 1967 // with immediates, for example a * 2 can be 1968 // lowered to a << 1 1969 return try self.binOpRegister(.mul, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); 1970 } else { 1971 return self.fail("TODO binary operations on int with bits > 64", .{}); 1972 } 1973 }, 1974 else => unreachable, 1975 } 1976 } 1977 1978 fn divFloat( 1979 self: *Self, 1980 lhs_bind: ReadArg.Bind, 1981 rhs_bind: ReadArg.Bind, 1982 lhs_ty: Type, 1983 rhs_ty: Type, 1984 maybe_inst: ?Air.Inst.Index, 1985 ) InnerError!MCValue { 1986 _ = lhs_bind; 1987 _ = rhs_bind; 1988 _ = rhs_ty; 1989 _ = maybe_inst; 1990 1991 switch (lhs_ty.zigTypeTag()) { 1992 .Float => return self.fail("TODO div_float", .{}), 1993 .Vector => return self.fail("TODO div_float on vectors", .{}), 1994 else => unreachable, 1995 } 1996 } 1997 1998 fn divTrunc( 1999 self: *Self, 2000 lhs_bind: ReadArg.Bind, 2001 rhs_bind: ReadArg.Bind, 2002 lhs_ty: Type, 2003 rhs_ty: Type, 2004 maybe_inst: ?Air.Inst.Index, 2005 ) InnerError!MCValue { 2006 const mod = self.bin_file.options.module.?; 2007 switch (lhs_ty.zigTypeTag()) { 2008 .Float => return self.fail("TODO div on floats", .{}), 2009 .Vector => return self.fail("TODO div on vectors", .{}), 2010 .Int => { 2011 assert(lhs_ty.eql(rhs_ty, mod)); 2012 const int_info = lhs_ty.intInfo(self.target.*); 2013 if (int_info.bits <= 64) { 2014 switch (int_info.signedness) { 2015 .signed => { 2016 // TODO optimize integer division by constants 2017 return try self.binOpRegister(.sdiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); 2018 }, 2019 .unsigned => { 2020 // TODO optimize integer division by constants 2021 return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); 2022 }, 2023 } 2024 } else { 2025 return self.fail("TODO integer division for ints with bits > 64", .{}); 2026 } 2027 }, 2028 else => unreachable, 2029 } 2030 } 2031 2032 fn divFloor( 2033 self: *Self, 2034 lhs_bind: ReadArg.Bind, 2035 rhs_bind: ReadArg.Bind, 2036 lhs_ty: Type, 2037 rhs_ty: Type, 2038 maybe_inst: ?Air.Inst.Index, 2039 ) InnerError!MCValue { 2040 const mod = self.bin_file.options.module.?; 2041 switch (lhs_ty.zigTypeTag()) { 2042 .Float => return self.fail("TODO div on floats", .{}), 2043 .Vector => return self.fail("TODO div on vectors", .{}), 2044 .Int => { 2045 assert(lhs_ty.eql(rhs_ty, mod)); 2046 const int_info = lhs_ty.intInfo(self.target.*); 2047 if (int_info.bits <= 64) { 2048 switch (int_info.signedness) { 2049 .signed => { 2050 return self.fail("TODO div_floor on signed integers", .{}); 2051 }, 2052 .unsigned => { 2053 // TODO optimize integer division by constants 2054 return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); 2055 }, 2056 } 2057 } else { 2058 return self.fail("TODO integer division for ints with bits > 64", .{}); 2059 } 2060 }, 2061 else => unreachable, 2062 } 2063 } 2064 2065 fn divExact( 2066 self: *Self, 2067 lhs_bind: ReadArg.Bind, 2068 rhs_bind: ReadArg.Bind, 2069 lhs_ty: Type, 2070 rhs_ty: Type, 2071 maybe_inst: ?Air.Inst.Index, 2072 ) InnerError!MCValue { 2073 const mod = self.bin_file.options.module.?; 2074 switch (lhs_ty.zigTypeTag()) { 2075 .Float => return self.fail("TODO div on floats", .{}), 2076 .Vector => return self.fail("TODO div on vectors", .{}), 2077 .Int => { 2078 assert(lhs_ty.eql(rhs_ty, mod)); 2079 const int_info = lhs_ty.intInfo(self.target.*); 2080 if (int_info.bits <= 64) { 2081 switch (int_info.signedness) { 2082 .signed => { 2083 // TODO optimize integer division by constants 2084 return try self.binOpRegister(.sdiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); 2085 }, 2086 .unsigned => { 2087 // TODO optimize integer division by constants 2088 return try self.binOpRegister(.udiv, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); 2089 }, 2090 } 2091 } else { 2092 return self.fail("TODO integer division for ints with bits > 64", .{}); 2093 } 2094 }, 2095 else => unreachable, 2096 } 2097 } 2098 2099 fn rem( 2100 self: *Self, 2101 lhs_bind: ReadArg.Bind, 2102 rhs_bind: ReadArg.Bind, 2103 lhs_ty: Type, 2104 rhs_ty: Type, 2105 maybe_inst: ?Air.Inst.Index, 2106 ) InnerError!MCValue { 2107 _ = maybe_inst; 2108 2109 const mod = self.bin_file.options.module.?; 2110 switch (lhs_ty.zigTypeTag()) { 2111 .Float => return self.fail("TODO rem/mod on floats", .{}), 2112 .Vector => return self.fail("TODO rem/mod on vectors", .{}), 2113 .Int => { 2114 assert(lhs_ty.eql(rhs_ty, mod)); 2115 const int_info = lhs_ty.intInfo(self.target.*); 2116 if (int_info.bits <= 64) { 2117 var lhs_reg: Register = undefined; 2118 var rhs_reg: Register = undefined; 2119 var quotient_reg: Register = undefined; 2120 var remainder_reg: Register = undefined; 2121 2122 const read_args = [_]ReadArg{ 2123 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, 2124 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, 2125 }; 2126 const write_args = [_]WriteArg{ 2127 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = "ient_reg }, 2128 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &remainder_reg }, 2129 }; 2130 try self.allocRegs( 2131 &read_args, 2132 &write_args, 2133 null, 2134 ); 2135 2136 _ = try self.addInst(.{ 2137 .tag = switch (int_info.signedness) { 2138 .signed => .sdiv, 2139 .unsigned => .udiv, 2140 }, 2141 .data = .{ .rrr = .{ 2142 .rd = quotient_reg, 2143 .rn = lhs_reg, 2144 .rm = rhs_reg, 2145 } }, 2146 }); 2147 2148 _ = try self.addInst(.{ 2149 .tag = .msub, 2150 .data = .{ .rrrr = .{ 2151 .rd = remainder_reg, 2152 .rn = quotient_reg, 2153 .rm = rhs_reg, 2154 .ra = lhs_reg, 2155 } }, 2156 }); 2157 2158 return MCValue{ .register = remainder_reg }; 2159 } else { 2160 return self.fail("TODO rem/mod for integers with bits > 64", .{}); 2161 } 2162 }, 2163 else => unreachable, 2164 } 2165 } 2166 2167 fn modulo( 2168 self: *Self, 2169 lhs_bind: ReadArg.Bind, 2170 rhs_bind: ReadArg.Bind, 2171 lhs_ty: Type, 2172 rhs_ty: Type, 2173 maybe_inst: ?Air.Inst.Index, 2174 ) InnerError!MCValue { 2175 _ = lhs_bind; 2176 _ = rhs_bind; 2177 _ = rhs_ty; 2178 _ = maybe_inst; 2179 2180 switch (lhs_ty.zigTypeTag()) { 2181 .Float => return self.fail("TODO mod on floats", .{}), 2182 .Vector => return self.fail("TODO mod on vectors", .{}), 2183 .Int => return self.fail("TODO mod on ints", .{}), 2184 else => unreachable, 2185 } 2186 } 2187 2188 fn wrappingArithmetic( 2189 self: *Self, 2190 tag: Air.Inst.Tag, 2191 lhs_bind: ReadArg.Bind, 2192 rhs_bind: ReadArg.Bind, 2193 lhs_ty: Type, 2194 rhs_ty: Type, 2195 maybe_inst: ?Air.Inst.Index, 2196 ) InnerError!MCValue { 2197 switch (lhs_ty.zigTypeTag()) { 2198 .Vector => return self.fail("TODO binary operations on vectors", .{}), 2199 .Int => { 2200 const int_info = lhs_ty.intInfo(self.target.*); 2201 if (int_info.bits <= 64) { 2202 // Generate an add/sub/mul 2203 const result: MCValue = switch (tag) { 2204 .addwrap => try self.addSub(.add, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), 2205 .subwrap => try self.addSub(.sub, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), 2206 .mulwrap => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), 2207 else => unreachable, 2208 }; 2209 2210 // Truncate if necessary 2211 const result_reg = result.register; 2212 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); 2213 return result; 2214 } else { 2215 return self.fail("TODO binary operations on integers > u64/i64", .{}); 2216 } 2217 }, 2218 else => unreachable, 2219 } 2220 } 2221 2222 fn bitwise( 2223 self: *Self, 2224 tag: Air.Inst.Tag, 2225 lhs_bind: ReadArg.Bind, 2226 rhs_bind: ReadArg.Bind, 2227 lhs_ty: Type, 2228 rhs_ty: Type, 2229 maybe_inst: ?Air.Inst.Index, 2230 ) InnerError!MCValue { 2231 const mod = self.bin_file.options.module.?; 2232 switch (lhs_ty.zigTypeTag()) { 2233 .Vector => return self.fail("TODO binary operations on vectors", .{}), 2234 .Int => { 2235 assert(lhs_ty.eql(rhs_ty, mod)); 2236 const int_info = lhs_ty.intInfo(self.target.*); 2237 if (int_info.bits <= 64) { 2238 // TODO implement bitwise operations with immediates 2239 const mir_tag: Mir.Inst.Tag = switch (tag) { 2240 .bit_and => .and_shifted_register, 2241 .bit_or => .orr_shifted_register, 2242 .xor => .eor_shifted_register, 2243 else => unreachable, 2244 }; 2245 2246 return try self.binOpRegister(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); 2247 } else { 2248 return self.fail("TODO binary operations on int with bits > 64", .{}); 2249 } 2250 }, 2251 else => unreachable, 2252 } 2253 } 2254 2255 fn shiftExact( 2256 self: *Self, 2257 tag: Air.Inst.Tag, 2258 lhs_bind: ReadArg.Bind, 2259 rhs_bind: ReadArg.Bind, 2260 lhs_ty: Type, 2261 rhs_ty: Type, 2262 maybe_inst: ?Air.Inst.Index, 2263 ) InnerError!MCValue { 2264 _ = rhs_ty; 2265 2266 switch (lhs_ty.zigTypeTag()) { 2267 .Vector => return self.fail("TODO binary operations on vectors", .{}), 2268 .Int => { 2269 const int_info = lhs_ty.intInfo(self.target.*); 2270 if (int_info.bits <= 64) { 2271 const rhs_immediate = try rhs_bind.resolveToImmediate(self); 2272 2273 const mir_tag_register: Mir.Inst.Tag = switch (tag) { 2274 .shl_exact => .lsl_register, 2275 .shr_exact => switch (int_info.signedness) { 2276 .signed => Mir.Inst.Tag.asr_register, 2277 .unsigned => Mir.Inst.Tag.lsr_register, 2278 }, 2279 else => unreachable, 2280 }; 2281 const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { 2282 .shl_exact => .lsl_immediate, 2283 .shr_exact => switch (int_info.signedness) { 2284 .signed => Mir.Inst.Tag.asr_immediate, 2285 .unsigned => Mir.Inst.Tag.lsr_immediate, 2286 }, 2287 else => unreachable, 2288 }; 2289 2290 if (rhs_immediate) |imm| { 2291 return try self.binOpImmediate(mir_tag_immediate, lhs_bind, imm, lhs_ty, false, maybe_inst); 2292 } else { 2293 // We intentionally pass lhs_ty here in order to 2294 // prevent using the 32-bit register alias when 2295 // lhs_ty is > 32 bits. 2296 return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, lhs_ty, maybe_inst); 2297 } 2298 } else { 2299 return self.fail("TODO binary operations on int with bits > 64", .{}); 2300 } 2301 }, 2302 else => unreachable, 2303 } 2304 } 2305 2306 fn shiftNormal( 2307 self: *Self, 2308 tag: Air.Inst.Tag, 2309 lhs_bind: ReadArg.Bind, 2310 rhs_bind: ReadArg.Bind, 2311 lhs_ty: Type, 2312 rhs_ty: Type, 2313 maybe_inst: ?Air.Inst.Index, 2314 ) InnerError!MCValue { 2315 switch (lhs_ty.zigTypeTag()) { 2316 .Vector => return self.fail("TODO binary operations on vectors", .{}), 2317 .Int => { 2318 const int_info = lhs_ty.intInfo(self.target.*); 2319 if (int_info.bits <= 64) { 2320 // Generate a shl_exact/shr_exact 2321 const result: MCValue = switch (tag) { 2322 .shl => try self.shiftExact(.shl_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), 2323 .shr => try self.shiftExact(.shr_exact, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst), 2324 else => unreachable, 2325 }; 2326 2327 // Truncate if necessary 2328 switch (tag) { 2329 .shr => return result, 2330 .shl => { 2331 const result_reg = result.register; 2332 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits); 2333 return result; 2334 }, 2335 else => unreachable, 2336 } 2337 } else { 2338 return self.fail("TODO binary operations on integers > u64/i64", .{}); 2339 } 2340 }, 2341 else => unreachable, 2342 } 2343 } 2344 2345 fn booleanOp( 2346 self: *Self, 2347 tag: Air.Inst.Tag, 2348 lhs_bind: ReadArg.Bind, 2349 rhs_bind: ReadArg.Bind, 2350 lhs_ty: Type, 2351 rhs_ty: Type, 2352 maybe_inst: ?Air.Inst.Index, 2353 ) InnerError!MCValue { 2354 switch (lhs_ty.zigTypeTag()) { 2355 .Bool => { 2356 assert((try lhs_bind.resolveToImmediate(self)) == null); // should have been handled by Sema 2357 assert((try rhs_bind.resolveToImmediate(self)) == null); // should have been handled by Sema 2358 2359 const mir_tag_register: Mir.Inst.Tag = switch (tag) { 2360 .bool_and => .and_shifted_register, 2361 .bool_or => .orr_shifted_register, 2362 else => unreachable, 2363 }; 2364 2365 return try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst); 2366 }, 2367 else => unreachable, 2368 } 2369 } 2370 2371 fn ptrArithmetic( 2372 self: *Self, 2373 tag: Air.Inst.Tag, 2374 lhs_bind: ReadArg.Bind, 2375 rhs_bind: ReadArg.Bind, 2376 lhs_ty: Type, 2377 rhs_ty: Type, 2378 maybe_inst: ?Air.Inst.Index, 2379 ) InnerError!MCValue { 2380 switch (lhs_ty.zigTypeTag()) { 2381 .Pointer => { 2382 const mod = self.bin_file.options.module.?; 2383 assert(rhs_ty.eql(Type.usize, mod)); 2384 2385 const ptr_ty = lhs_ty; 2386 const elem_ty = switch (ptr_ty.ptrSize()) { 2387 .One => ptr_ty.childType().childType(), // ptr to array, so get array element type 2388 else => ptr_ty.childType(), 2389 }; 2390 const elem_size = elem_ty.abiSize(self.target.*); 2391 2392 const base_tag: Air.Inst.Tag = switch (tag) { 2393 .ptr_add => .add, 2394 .ptr_sub => .sub, 2395 else => unreachable, 2396 }; 2397 2398 if (elem_size == 1) { 2399 return try self.addSub(base_tag, lhs_bind, rhs_bind, Type.usize, Type.usize, maybe_inst); 2400 } else { 2401 // convert the offset into a byte offset by 2402 // multiplying it with elem_size 2403 const imm_bind = ReadArg.Bind{ .mcv = .{ .immediate = elem_size } }; 2404 2405 const offset = try self.mul(rhs_bind, imm_bind, Type.usize, Type.usize, null); 2406 const offset_bind = ReadArg.Bind{ .mcv = offset }; 2407 2408 const addr = try self.addSub(base_tag, lhs_bind, offset_bind, Type.usize, Type.usize, null); 2409 return addr; 2410 } 2411 }, 2412 else => unreachable, 2413 } 2414 } 2415 2416 fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { 2417 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 2418 const lhs_ty = self.air.typeOf(bin_op.lhs); 2419 const rhs_ty = self.air.typeOf(bin_op.rhs); 2420 2421 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 2422 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; 2423 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; 2424 2425 break :result switch (tag) { 2426 .add => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2427 .sub => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2428 2429 .mul => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2430 2431 .div_float => try self.divFloat(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2432 2433 .div_trunc => try self.divTrunc(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2434 2435 .div_floor => try self.divFloor(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2436 2437 .div_exact => try self.divExact(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2438 2439 .rem => try self.rem(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2440 2441 .mod => try self.modulo(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2442 2443 .addwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2444 .subwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2445 .mulwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2446 2447 .bit_and => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2448 .bit_or => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2449 .xor => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2450 2451 .shl_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2452 .shr_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2453 2454 .shl => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2455 .shr => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2456 2457 .bool_and => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2458 .bool_or => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst), 2459 2460 else => unreachable, 2461 }; 2462 }; 2463 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 2464 } 2465 2466 fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { 2467 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 2468 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 2469 const lhs_ty = self.air.typeOf(bin_op.lhs); 2470 const rhs_ty = self.air.typeOf(bin_op.rhs); 2471 2472 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 2473 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; 2474 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; 2475 2476 break :result try self.ptrArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst); 2477 }; 2478 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 2479 } 2480 2481 fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { 2482 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 2483 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch}); 2484 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 2485 } 2486 2487 fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { 2488 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 2489 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch}); 2490 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 2491 } 2492 2493 fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { 2494 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 2495 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch}); 2496 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 2497 } 2498 2499 fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { 2500 const tag = self.air.instructions.items(.tag)[inst]; 2501 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 2502 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 2503 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 2504 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; 2505 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; 2506 const lhs_ty = self.air.typeOf(extra.lhs); 2507 const rhs_ty = self.air.typeOf(extra.rhs); 2508 2509 const tuple_ty = self.air.typeOfIndex(inst); 2510 const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); 2511 const tuple_align = tuple_ty.abiAlignment(self.target.*); 2512 const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*)); 2513 2514 switch (lhs_ty.zigTypeTag()) { 2515 .Vector => return self.fail("TODO implement add_with_overflow/sub_with_overflow for vectors", .{}), 2516 .Int => { 2517 const mod = self.bin_file.options.module.?; 2518 assert(lhs_ty.eql(rhs_ty, mod)); 2519 const int_info = lhs_ty.intInfo(self.target.*); 2520 switch (int_info.bits) { 2521 1...31, 33...63 => { 2522 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); 2523 2524 try self.spillCompareFlagsIfOccupied(); 2525 self.compare_flags_inst = null; 2526 2527 const base_tag: Air.Inst.Tag = switch (tag) { 2528 .add_with_overflow => .add, 2529 .sub_with_overflow => .sub, 2530 else => unreachable, 2531 }; 2532 const dest = try self.addSub(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); 2533 const dest_reg = dest.register; 2534 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); 2535 defer self.register_manager.unlockReg(dest_reg_lock); 2536 2537 const raw_truncated_reg = try self.register_manager.allocReg(null, gp); 2538 const truncated_reg = self.registerAlias(raw_truncated_reg, lhs_ty); 2539 const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); 2540 defer self.register_manager.unlockReg(truncated_reg_lock); 2541 2542 // sbfx/ubfx truncated, dest, #0, #bits 2543 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); 2544 2545 // cmp dest, truncated 2546 _ = try self.addInst(.{ 2547 .tag = .cmp_shifted_register, 2548 .data = .{ .rr_imm6_shift = .{ 2549 .rn = dest_reg, 2550 .rm = truncated_reg, 2551 .imm6 = 0, 2552 .shift = .lsl, 2553 } }, 2554 }); 2555 2556 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); 2557 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags = .ne }); 2558 2559 break :result MCValue{ .stack_offset = stack_offset }; 2560 }, 2561 32, 64 => { 2562 const lhs_immediate = try lhs_bind.resolveToImmediate(self); 2563 const rhs_immediate = try rhs_bind.resolveToImmediate(self); 2564 2565 // Only say yes if the operation is 2566 // commutative, i.e. we can swap both of the 2567 // operands 2568 const lhs_immediate_ok = switch (tag) { 2569 .add_with_overflow => if (lhs_immediate) |imm| imm <= std.math.maxInt(u12) else false, 2570 .sub_with_overflow => false, 2571 else => unreachable, 2572 }; 2573 const rhs_immediate_ok = switch (tag) { 2574 .add_with_overflow, 2575 .sub_with_overflow, 2576 => if (rhs_immediate) |imm| imm <= std.math.maxInt(u12) else false, 2577 else => unreachable, 2578 }; 2579 2580 const mir_tag_register: Mir.Inst.Tag = switch (tag) { 2581 .add_with_overflow => .adds_shifted_register, 2582 .sub_with_overflow => .subs_shifted_register, 2583 else => unreachable, 2584 }; 2585 const mir_tag_immediate: Mir.Inst.Tag = switch (tag) { 2586 .add_with_overflow => .adds_immediate, 2587 .sub_with_overflow => .subs_immediate, 2588 else => unreachable, 2589 }; 2590 2591 try self.spillCompareFlagsIfOccupied(); 2592 self.compare_flags_inst = inst; 2593 2594 const dest = blk: { 2595 if (rhs_immediate_ok) { 2596 break :blk try self.binOpImmediate(mir_tag_immediate, lhs_bind, rhs_immediate.?, lhs_ty, false, null); 2597 } else if (lhs_immediate_ok) { 2598 // swap lhs and rhs 2599 break :blk try self.binOpImmediate(mir_tag_immediate, rhs_bind, lhs_immediate.?, rhs_ty, true, null); 2600 } else { 2601 break :blk try self.binOpRegister(mir_tag_register, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); 2602 } 2603 }; 2604 2605 const flag: bits.Instruction.Condition = switch (int_info.signedness) { 2606 .unsigned => switch (tag) { 2607 .add_with_overflow => bits.Instruction.Condition.cs, 2608 .sub_with_overflow => bits.Instruction.Condition.cc, 2609 else => unreachable, 2610 }, 2611 .signed => .vs, 2612 }; 2613 break :result MCValue{ .register_with_overflow = .{ 2614 .reg = dest.register, 2615 .flag = flag, 2616 } }; 2617 }, 2618 else => return self.fail("TODO overflow operations on integers > u32/i32", .{}), 2619 } 2620 }, 2621 else => unreachable, 2622 } 2623 }; 2624 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); 2625 } 2626 2627 fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { 2628 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 2629 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 2630 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); 2631 const result: MCValue = result: { 2632 const mod = self.bin_file.options.module.?; 2633 2634 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; 2635 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; 2636 const lhs_ty = self.air.typeOf(extra.lhs); 2637 const rhs_ty = self.air.typeOf(extra.rhs); 2638 2639 const tuple_ty = self.air.typeOfIndex(inst); 2640 const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); 2641 const tuple_align = tuple_ty.abiAlignment(self.target.*); 2642 const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*)); 2643 2644 switch (lhs_ty.zigTypeTag()) { 2645 .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}), 2646 .Int => { 2647 assert(lhs_ty.eql(rhs_ty, mod)); 2648 const int_info = lhs_ty.intInfo(self.target.*); 2649 if (int_info.bits <= 32) { 2650 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); 2651 2652 try self.spillCompareFlagsIfOccupied(); 2653 2654 const base_tag: Mir.Inst.Tag = switch (int_info.signedness) { 2655 .signed => .smull, 2656 .unsigned => .umull, 2657 }; 2658 2659 const dest = try self.binOpRegister(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null); 2660 const dest_reg = dest.register; 2661 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg); 2662 defer self.register_manager.unlockReg(dest_reg_lock); 2663 2664 const truncated_reg = try self.register_manager.allocReg(null, gp); 2665 const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg); 2666 defer self.register_manager.unlockReg(truncated_reg_lock); 2667 2668 try self.truncRegister( 2669 dest_reg.toW(), 2670 truncated_reg.toW(), 2671 int_info.signedness, 2672 int_info.bits, 2673 ); 2674 2675 switch (int_info.signedness) { 2676 .signed => { 2677 _ = try self.addInst(.{ 2678 .tag = .cmp_extended_register, 2679 .data = .{ .rr_extend_shift = .{ 2680 .rn = dest_reg.toX(), 2681 .rm = truncated_reg.toW(), 2682 .ext_type = .sxtw, 2683 .imm3 = 0, 2684 } }, 2685 }); 2686 }, 2687 .unsigned => { 2688 _ = try self.addInst(.{ 2689 .tag = .cmp_extended_register, 2690 .data = .{ .rr_extend_shift = .{ 2691 .rn = dest_reg.toX(), 2692 .rm = truncated_reg.toW(), 2693 .ext_type = .uxtw, 2694 .imm3 = 0, 2695 } }, 2696 }); 2697 }, 2698 } 2699 2700 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); 2701 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags = .ne }); 2702 2703 break :result MCValue{ .stack_offset = stack_offset }; 2704 } else if (int_info.bits <= 64) { 2705 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); 2706 2707 try self.spillCompareFlagsIfOccupied(); 2708 2709 var lhs_reg: Register = undefined; 2710 var rhs_reg: Register = undefined; 2711 var dest_reg: Register = undefined; 2712 var dest_high_reg: Register = undefined; 2713 var truncated_reg: Register = undefined; 2714 2715 const read_args = [_]ReadArg{ 2716 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, 2717 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, 2718 }; 2719 const write_args = [_]WriteArg{ 2720 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, 2721 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_high_reg }, 2722 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &truncated_reg }, 2723 }; 2724 try self.allocRegs( 2725 &read_args, 2726 &write_args, 2727 null, 2728 ); 2729 2730 switch (int_info.signedness) { 2731 .signed => { 2732 // mul dest, lhs, rhs 2733 _ = try self.addInst(.{ 2734 .tag = .mul, 2735 .data = .{ .rrr = .{ 2736 .rd = dest_reg, 2737 .rn = lhs_reg, 2738 .rm = rhs_reg, 2739 } }, 2740 }); 2741 2742 // smulh dest_high, lhs, rhs 2743 _ = try self.addInst(.{ 2744 .tag = .smulh, 2745 .data = .{ .rrr = .{ 2746 .rd = dest_high_reg, 2747 .rn = lhs_reg, 2748 .rm = rhs_reg, 2749 } }, 2750 }); 2751 2752 // cmp dest_high, dest, asr #63 2753 _ = try self.addInst(.{ 2754 .tag = .cmp_shifted_register, 2755 .data = .{ .rr_imm6_shift = .{ 2756 .rn = dest_high_reg, 2757 .rm = dest_reg, 2758 .imm6 = 63, 2759 .shift = .asr, 2760 } }, 2761 }); 2762 2763 const shift: u6 = @intCast(u6, @as(u7, 64) - @intCast(u7, int_info.bits)); 2764 if (shift > 0) { 2765 // lsl dest_high, dest, #shift 2766 _ = try self.addInst(.{ 2767 .tag = .lsl_immediate, 2768 .data = .{ .rr_shift = .{ 2769 .rd = dest_high_reg, 2770 .rn = dest_reg, 2771 .shift = shift, 2772 } }, 2773 }); 2774 2775 // cmp dest, dest_high, #shift 2776 _ = try self.addInst(.{ 2777 .tag = .cmp_shifted_register, 2778 .data = .{ .rr_imm6_shift = .{ 2779 .rn = dest_reg, 2780 .rm = dest_high_reg, 2781 .imm6 = shift, 2782 .shift = .asr, 2783 } }, 2784 }); 2785 } 2786 }, 2787 .unsigned => { 2788 // umulh dest_high, lhs, rhs 2789 _ = try self.addInst(.{ 2790 .tag = .umulh, 2791 .data = .{ .rrr = .{ 2792 .rd = dest_high_reg, 2793 .rn = lhs_reg, 2794 .rm = rhs_reg, 2795 } }, 2796 }); 2797 2798 // mul dest, lhs, rhs 2799 _ = try self.addInst(.{ 2800 .tag = .mul, 2801 .data = .{ .rrr = .{ 2802 .rd = dest_reg, 2803 .rn = lhs_reg, 2804 .rm = rhs_reg, 2805 } }, 2806 }); 2807 2808 _ = try self.addInst(.{ 2809 .tag = .cmp_immediate, 2810 .data = .{ .r_imm12_sh = .{ 2811 .rn = dest_high_reg, 2812 .imm12 = 0, 2813 } }, 2814 }); 2815 2816 if (int_info.bits < 64) { 2817 // lsr dest_high, dest, #shift 2818 _ = try self.addInst(.{ 2819 .tag = .lsr_immediate, 2820 .data = .{ .rr_shift = .{ 2821 .rd = dest_high_reg, 2822 .rn = dest_reg, 2823 .shift = @intCast(u6, int_info.bits), 2824 } }, 2825 }); 2826 2827 _ = try self.addInst(.{ 2828 .tag = .cmp_immediate, 2829 .data = .{ .r_imm12_sh = .{ 2830 .rn = dest_high_reg, 2831 .imm12 = 0, 2832 } }, 2833 }); 2834 } 2835 }, 2836 } 2837 2838 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); 2839 2840 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); 2841 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags = .ne }); 2842 2843 break :result MCValue{ .stack_offset = stack_offset }; 2844 } else return self.fail("TODO implement mul_with_overflow for integers > u64/i64", .{}); 2845 }, 2846 else => unreachable, 2847 } 2848 }; 2849 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); 2850 } 2851 2852 fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { 2853 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 2854 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 2855 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); 2856 const result: MCValue = result: { 2857 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs }; 2858 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs }; 2859 const lhs_ty = self.air.typeOf(extra.lhs); 2860 const rhs_ty = self.air.typeOf(extra.rhs); 2861 2862 const tuple_ty = self.air.typeOfIndex(inst); 2863 const tuple_size = @intCast(u32, tuple_ty.abiSize(self.target.*)); 2864 const tuple_align = tuple_ty.abiAlignment(self.target.*); 2865 const overflow_bit_offset = @intCast(u32, tuple_ty.structFieldOffset(1, self.target.*)); 2866 2867 switch (lhs_ty.zigTypeTag()) { 2868 .Vector => return self.fail("TODO implement shl_with_overflow for vectors", .{}), 2869 .Int => { 2870 const int_info = lhs_ty.intInfo(self.target.*); 2871 if (int_info.bits <= 64) { 2872 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); 2873 2874 try self.spillCompareFlagsIfOccupied(); 2875 2876 var lhs_reg: Register = undefined; 2877 var rhs_reg: Register = undefined; 2878 var dest_reg: Register = undefined; 2879 var reconstructed_reg: Register = undefined; 2880 2881 const rhs_immediate = try rhs_bind.resolveToImmediate(self); 2882 if (rhs_immediate) |imm| { 2883 const read_args = [_]ReadArg{ 2884 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, 2885 }; 2886 const write_args = [_]WriteArg{ 2887 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, 2888 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg }, 2889 }; 2890 try self.allocRegs( 2891 &read_args, 2892 &write_args, 2893 null, 2894 ); 2895 2896 // lsl dest, lhs, rhs 2897 _ = try self.addInst(.{ 2898 .tag = .lsl_immediate, 2899 .data = .{ .rr_shift = .{ 2900 .rd = dest_reg, 2901 .rn = lhs_reg, 2902 .shift = @intCast(u6, imm), 2903 } }, 2904 }); 2905 2906 try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); 2907 2908 // asr/lsr reconstructed, dest, rhs 2909 _ = try self.addInst(.{ 2910 .tag = switch (int_info.signedness) { 2911 .signed => Mir.Inst.Tag.asr_immediate, 2912 .unsigned => Mir.Inst.Tag.lsr_immediate, 2913 }, 2914 .data = .{ .rr_shift = .{ 2915 .rd = reconstructed_reg, 2916 .rn = dest_reg, 2917 .shift = @intCast(u6, imm), 2918 } }, 2919 }); 2920 } else { 2921 const read_args = [_]ReadArg{ 2922 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, 2923 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, 2924 }; 2925 const write_args = [_]WriteArg{ 2926 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, 2927 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg }, 2928 }; 2929 try self.allocRegs( 2930 &read_args, 2931 &write_args, 2932 null, 2933 ); 2934 2935 // lsl dest, lhs, rhs 2936 _ = try self.addInst(.{ 2937 .tag = .lsl_register, 2938 .data = .{ .rrr = .{ 2939 .rd = dest_reg, 2940 .rn = lhs_reg, 2941 .rm = rhs_reg, 2942 } }, 2943 }); 2944 2945 try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits); 2946 2947 // asr/lsr reconstructed, dest, rhs 2948 _ = try self.addInst(.{ 2949 .tag = switch (int_info.signedness) { 2950 .signed => Mir.Inst.Tag.asr_register, 2951 .unsigned => Mir.Inst.Tag.lsr_register, 2952 }, 2953 .data = .{ .rrr = .{ 2954 .rd = reconstructed_reg, 2955 .rn = dest_reg, 2956 .rm = rhs_reg, 2957 } }, 2958 }); 2959 } 2960 2961 // cmp lhs, reconstructed 2962 _ = try self.addInst(.{ 2963 .tag = .cmp_shifted_register, 2964 .data = .{ .rr_imm6_shift = .{ 2965 .rn = lhs_reg, 2966 .rm = reconstructed_reg, 2967 .imm6 = 0, 2968 .shift = .lsl, 2969 } }, 2970 }); 2971 2972 try self.genSetStack(lhs_ty, stack_offset, .{ .register = dest_reg }); 2973 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags = .ne }); 2974 2975 break :result MCValue{ .stack_offset = stack_offset }; 2976 } else { 2977 return self.fail("TODO ARM overflow operations on integers > u32/i32", .{}); 2978 } 2979 }, 2980 else => unreachable, 2981 } 2982 }; 2983 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); 2984 } 2985 2986 fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { 2987 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 2988 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}); 2989 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 2990 } 2991 2992 fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { 2993 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2994 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 2995 const optional_ty = self.air.typeOf(ty_op.operand); 2996 const mcv = try self.resolveInst(ty_op.operand); 2997 break :result try self.optionalPayload(inst, mcv, optional_ty); 2998 }; 2999 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3000 } 3001 3002 fn optionalPayload(self: *Self, inst: Air.Inst.Index, mcv: MCValue, optional_ty: Type) !MCValue { 3003 var opt_buf: Type.Payload.ElemType = undefined; 3004 const payload_ty = optional_ty.optionalChild(&opt_buf); 3005 if (!payload_ty.hasRuntimeBits()) return MCValue.none; 3006 if (optional_ty.isPtrLikeOptional()) { 3007 // TODO should we reuse the operand here? 3008 const raw_reg = try self.register_manager.allocReg(inst, gp); 3009 const reg = self.registerAlias(raw_reg, payload_ty); 3010 try self.genSetReg(payload_ty, reg, mcv); 3011 return MCValue{ .register = reg }; 3012 } 3013 3014 switch (mcv) { 3015 .register => { 3016 // TODO should we reuse the operand here? 3017 const raw_reg = try self.register_manager.allocReg(inst, gp); 3018 const dest_reg = raw_reg.toX(); 3019 3020 try self.genSetReg(payload_ty, dest_reg, mcv); 3021 return MCValue{ .register = self.registerAlias(dest_reg, payload_ty) }; 3022 }, 3023 .stack_argument_offset, .stack_offset, .memory => return mcv, 3024 else => unreachable, // invalid MCValue for an error union 3025 } 3026 } 3027 3028 fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { 3029 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3030 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}); 3031 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3032 } 3033 3034 fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { 3035 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3036 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch}); 3037 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3038 } 3039 3040 /// Given an error union, returns the error 3041 fn errUnionErr( 3042 self: *Self, 3043 error_union_bind: ReadArg.Bind, 3044 error_union_ty: Type, 3045 maybe_inst: ?Air.Inst.Index, 3046 ) !MCValue { 3047 const err_ty = error_union_ty.errorUnionSet(); 3048 const payload_ty = error_union_ty.errorUnionPayload(); 3049 if (err_ty.errorSetIsEmpty()) { 3050 return MCValue{ .immediate = 0 }; 3051 } 3052 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { 3053 return try error_union_bind.resolveToMcv(self); 3054 } 3055 3056 const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*)); 3057 switch (try error_union_bind.resolveToMcv(self)) { 3058 .register => { 3059 var operand_reg: Register = undefined; 3060 var dest_reg: Register = undefined; 3061 3062 const read_args = [_]ReadArg{ 3063 .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg }, 3064 }; 3065 const write_args = [_]WriteArg{ 3066 .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg }, 3067 }; 3068 try self.allocRegs( 3069 &read_args, 3070 &write_args, 3071 if (maybe_inst) |inst| .{ 3072 .corresponding_inst = inst, 3073 .operand_mapping = &.{0}, 3074 } else null, 3075 ); 3076 3077 const err_bit_offset = err_offset * 8; 3078 const err_bit_size = @intCast(u32, err_ty.abiSize(self.target.*)) * 8; 3079 3080 _ = try self.addInst(.{ 3081 .tag = .ubfx, // errors are unsigned integers 3082 .data = .{ 3083 .rr_lsb_width = .{ 3084 // Set both registers to the X variant to get the full width 3085 .rd = dest_reg.toX(), 3086 .rn = operand_reg.toX(), 3087 .lsb = @intCast(u6, err_bit_offset), 3088 .width = @intCast(u7, err_bit_size), 3089 }, 3090 }, 3091 }); 3092 3093 return MCValue{ .register = dest_reg }; 3094 }, 3095 .stack_argument_offset => |off| { 3096 return MCValue{ .stack_argument_offset = off + err_offset }; 3097 }, 3098 .stack_offset => |off| { 3099 return MCValue{ .stack_offset = off - err_offset }; 3100 }, 3101 .memory => |addr| { 3102 return MCValue{ .memory = addr + err_offset }; 3103 }, 3104 else => unreachable, // invalid MCValue for an error union 3105 } 3106 } 3107 3108 fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { 3109 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3110 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3111 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; 3112 const error_union_ty = self.air.typeOf(ty_op.operand); 3113 3114 break :result try self.errUnionErr(error_union_bind, error_union_ty, inst); 3115 }; 3116 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3117 } 3118 3119 /// Given an error union, returns the payload 3120 fn errUnionPayload( 3121 self: *Self, 3122 error_union_bind: ReadArg.Bind, 3123 error_union_ty: Type, 3124 maybe_inst: ?Air.Inst.Index, 3125 ) !MCValue { 3126 const err_ty = error_union_ty.errorUnionSet(); 3127 const payload_ty = error_union_ty.errorUnionPayload(); 3128 if (err_ty.errorSetIsEmpty()) { 3129 return try error_union_bind.resolveToMcv(self); 3130 } 3131 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { 3132 return MCValue.none; 3133 } 3134 3135 const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*)); 3136 switch (try error_union_bind.resolveToMcv(self)) { 3137 .register => { 3138 var operand_reg: Register = undefined; 3139 var dest_reg: Register = undefined; 3140 3141 const read_args = [_]ReadArg{ 3142 .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg }, 3143 }; 3144 const write_args = [_]WriteArg{ 3145 .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg }, 3146 }; 3147 try self.allocRegs( 3148 &read_args, 3149 &write_args, 3150 if (maybe_inst) |inst| .{ 3151 .corresponding_inst = inst, 3152 .operand_mapping = &.{0}, 3153 } else null, 3154 ); 3155 3156 const payload_bit_offset = payload_offset * 8; 3157 const payload_bit_size = @intCast(u32, payload_ty.abiSize(self.target.*)) * 8; 3158 3159 _ = try self.addInst(.{ 3160 .tag = if (payload_ty.isSignedInt()) Mir.Inst.Tag.sbfx else .ubfx, 3161 .data = .{ 3162 .rr_lsb_width = .{ 3163 // Set both registers to the X variant to get the full width 3164 .rd = dest_reg.toX(), 3165 .rn = operand_reg.toX(), 3166 .lsb = @intCast(u5, payload_bit_offset), 3167 .width = @intCast(u6, payload_bit_size), 3168 }, 3169 }, 3170 }); 3171 3172 return MCValue{ .register = dest_reg }; 3173 }, 3174 .stack_argument_offset => |off| { 3175 return MCValue{ .stack_argument_offset = off + payload_offset }; 3176 }, 3177 .stack_offset => |off| { 3178 return MCValue{ .stack_offset = off - payload_offset }; 3179 }, 3180 .memory => |addr| { 3181 return MCValue{ .memory = addr + payload_offset }; 3182 }, 3183 else => unreachable, // invalid MCValue for an error union 3184 } 3185 } 3186 3187 fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { 3188 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3189 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3190 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; 3191 const error_union_ty = self.air.typeOf(ty_op.operand); 3192 3193 break :result try self.errUnionPayload(error_union_bind, error_union_ty, inst); 3194 }; 3195 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3196 } 3197 3198 // *(E!T) -> E 3199 fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void { 3200 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3201 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}); 3202 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3203 } 3204 3205 // *(E!T) -> *T 3206 fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { 3207 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3208 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}); 3209 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3210 } 3211 3212 fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { 3213 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3214 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch}); 3215 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3216 } 3217 3218 fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { 3219 const result: MCValue = if (self.liveness.isUnused(inst)) 3220 .dead 3221 else 3222 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch}); 3223 return self.finishAir(inst, result, .{ .none, .none, .none }); 3224 } 3225 3226 fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { 3227 _ = inst; 3228 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); 3229 } 3230 3231 fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void { 3232 _ = inst; 3233 return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch}); 3234 } 3235 3236 fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { 3237 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3238 3239 if (self.liveness.isUnused(inst)) { 3240 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); 3241 } 3242 3243 const result: MCValue = result: { 3244 const payload_ty = self.air.typeOf(ty_op.operand); 3245 if (!payload_ty.hasRuntimeBits()) { 3246 break :result MCValue{ .immediate = 1 }; 3247 } 3248 3249 const optional_ty = self.air.typeOfIndex(inst); 3250 const operand = try self.resolveInst(ty_op.operand); 3251 const operand_lock: ?RegisterLock = switch (operand) { 3252 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 3253 else => null, 3254 }; 3255 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); 3256 3257 if (optional_ty.isPtrLikeOptional()) { 3258 // TODO should we check if we can reuse the operand? 3259 const raw_reg = try self.register_manager.allocReg(inst, gp); 3260 const reg = self.registerAlias(raw_reg, payload_ty); 3261 try self.genSetReg(payload_ty, raw_reg, operand); 3262 break :result MCValue{ .register = reg }; 3263 } 3264 3265 const optional_abi_size = @intCast(u32, optional_ty.abiSize(self.target.*)); 3266 const optional_abi_align = optional_ty.abiAlignment(self.target.*); 3267 const offset = @intCast(u32, payload_ty.abiSize(self.target.*)); 3268 3269 const stack_offset = try self.allocMem(optional_abi_size, optional_abi_align, inst); 3270 try self.genSetStack(payload_ty, stack_offset, operand); 3271 try self.genSetStack(Type.bool, stack_offset - offset, .{ .immediate = 1 }); 3272 3273 break :result MCValue{ .stack_offset = stack_offset }; 3274 }; 3275 3276 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3277 } 3278 3279 /// T to E!T 3280 fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { 3281 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3282 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3283 const error_union_ty = self.air.getRefType(ty_op.ty); 3284 const error_ty = error_union_ty.errorUnionSet(); 3285 const payload_ty = error_union_ty.errorUnionPayload(); 3286 const operand = try self.resolveInst(ty_op.operand); 3287 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result operand; 3288 3289 const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); 3290 const abi_align = error_union_ty.abiAlignment(self.target.*); 3291 const stack_offset = try self.allocMem(abi_size, abi_align, inst); 3292 const payload_off = errUnionPayloadOffset(payload_ty, self.target.*); 3293 const err_off = errUnionErrorOffset(payload_ty, self.target.*); 3294 try self.genSetStack(payload_ty, stack_offset - @intCast(u32, payload_off), operand); 3295 try self.genSetStack(error_ty, stack_offset - @intCast(u32, err_off), .{ .immediate = 0 }); 3296 3297 break :result MCValue{ .stack_offset = stack_offset }; 3298 }; 3299 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3300 } 3301 3302 /// E to E!T 3303 fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { 3304 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3305 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3306 const error_union_ty = self.air.getRefType(ty_op.ty); 3307 const error_ty = error_union_ty.errorUnionSet(); 3308 const payload_ty = error_union_ty.errorUnionPayload(); 3309 const operand = try self.resolveInst(ty_op.operand); 3310 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result operand; 3311 3312 const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); 3313 const abi_align = error_union_ty.abiAlignment(self.target.*); 3314 const stack_offset = try self.allocMem(abi_size, abi_align, inst); 3315 const payload_off = errUnionPayloadOffset(payload_ty, self.target.*); 3316 const err_off = errUnionErrorOffset(payload_ty, self.target.*); 3317 try self.genSetStack(error_ty, stack_offset - @intCast(u32, err_off), operand); 3318 try self.genSetStack(payload_ty, stack_offset - @intCast(u32, payload_off), .undef); 3319 3320 break :result MCValue{ .stack_offset = stack_offset }; 3321 }; 3322 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3323 } 3324 3325 fn slicePtr(mcv: MCValue) MCValue { 3326 switch (mcv) { 3327 .dead, .unreach, .none => unreachable, 3328 .register => unreachable, // a slice doesn't fit in one register 3329 .stack_argument_offset => |off| { 3330 return MCValue{ .stack_argument_offset = off }; 3331 }, 3332 .stack_offset => |off| { 3333 return MCValue{ .stack_offset = off }; 3334 }, 3335 .memory => |addr| { 3336 return MCValue{ .memory = addr }; 3337 }, 3338 else => unreachable, // invalid MCValue for a slice 3339 } 3340 } 3341 3342 fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { 3343 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3344 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3345 const mcv = try self.resolveInst(ty_op.operand); 3346 break :result slicePtr(mcv); 3347 }; 3348 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3349 } 3350 3351 fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { 3352 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3353 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3354 const ptr_bits = self.target.cpu.arch.ptrBitWidth(); 3355 const ptr_bytes = @divExact(ptr_bits, 8); 3356 const mcv = try self.resolveInst(ty_op.operand); 3357 switch (mcv) { 3358 .dead, .unreach, .none => unreachable, 3359 .register => unreachable, // a slice doesn't fit in one register 3360 .stack_argument_offset => |off| { 3361 break :result MCValue{ .stack_argument_offset = off + ptr_bytes }; 3362 }, 3363 .stack_offset => |off| { 3364 break :result MCValue{ .stack_offset = off - ptr_bytes }; 3365 }, 3366 .memory => |addr| { 3367 break :result MCValue{ .memory = addr + ptr_bytes }; 3368 }, 3369 else => return self.fail("TODO implement slice_len for {}", .{mcv}), 3370 } 3371 }; 3372 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3373 } 3374 3375 fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void { 3376 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3377 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3378 const ptr_bits = self.target.cpu.arch.ptrBitWidth(); 3379 const ptr_bytes = @divExact(ptr_bits, 8); 3380 const mcv = try self.resolveInst(ty_op.operand); 3381 switch (mcv) { 3382 .dead, .unreach, .none => unreachable, 3383 .ptr_stack_offset => |off| { 3384 break :result MCValue{ .ptr_stack_offset = off - ptr_bytes }; 3385 }, 3386 else => return self.fail("TODO implement ptr_slice_len_ptr for {}", .{mcv}), 3387 } 3388 }; 3389 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3390 } 3391 3392 fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { 3393 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3394 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3395 const mcv = try self.resolveInst(ty_op.operand); 3396 switch (mcv) { 3397 .dead, .unreach, .none => unreachable, 3398 .ptr_stack_offset => |off| { 3399 break :result MCValue{ .ptr_stack_offset = off }; 3400 }, 3401 else => return self.fail("TODO implement ptr_slice_len_ptr for {}", .{mcv}), 3402 } 3403 }; 3404 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3405 } 3406 3407 fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { 3408 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 3409 const slice_ty = self.air.typeOf(bin_op.lhs); 3410 const result: MCValue = if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: { 3411 var buf: Type.SlicePtrFieldTypeBuffer = undefined; 3412 const ptr_ty = slice_ty.slicePtrFieldType(&buf); 3413 3414 const slice_mcv = try self.resolveInst(bin_op.lhs); 3415 const base_mcv = slicePtr(slice_mcv); 3416 3417 const base_bind: ReadArg.Bind = .{ .mcv = base_mcv }; 3418 const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; 3419 3420 break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst); 3421 }; 3422 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 3423 } 3424 3425 fn ptrElemVal( 3426 self: *Self, 3427 ptr_bind: ReadArg.Bind, 3428 index_bind: ReadArg.Bind, 3429 ptr_ty: Type, 3430 maybe_inst: ?Air.Inst.Index, 3431 ) !MCValue { 3432 const elem_ty = ptr_ty.childType(); 3433 const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*)); 3434 3435 // TODO optimize for elem_sizes of 1, 2, 4, 8 3436 switch (elem_size) { 3437 else => { 3438 const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, Type.usize, null); 3439 3440 const dest = try self.allocRegOrMem(elem_ty, true, maybe_inst); 3441 try self.load(dest, addr, ptr_ty); 3442 return dest; 3443 }, 3444 } 3445 } 3446 3447 fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { 3448 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 3449 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 3450 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3451 const slice_mcv = try self.resolveInst(extra.lhs); 3452 const base_mcv = slicePtr(slice_mcv); 3453 3454 const base_bind: ReadArg.Bind = .{ .mcv = base_mcv }; 3455 const index_bind: ReadArg.Bind = .{ .inst = extra.rhs }; 3456 3457 const slice_ty = self.air.typeOf(extra.lhs); 3458 const index_ty = self.air.typeOf(extra.rhs); 3459 3460 const addr = try self.ptrArithmetic(.ptr_add, base_bind, index_bind, slice_ty, index_ty, null); 3461 break :result addr; 3462 }; 3463 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); 3464 } 3465 3466 fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { 3467 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 3468 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch}); 3469 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 3470 } 3471 3472 fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { 3473 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 3474 const ptr_ty = self.air.typeOf(bin_op.lhs); 3475 const result: MCValue = if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: { 3476 const base_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; 3477 const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; 3478 3479 break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst); 3480 }; 3481 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 3482 } 3483 3484 fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { 3485 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 3486 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 3487 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 3488 const ptr_bind: ReadArg.Bind = .{ .inst = extra.lhs }; 3489 const index_bind: ReadArg.Bind = .{ .inst = extra.rhs }; 3490 3491 const ptr_ty = self.air.typeOf(extra.lhs); 3492 const index_ty = self.air.typeOf(extra.rhs); 3493 3494 const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, index_ty, null); 3495 break :result addr; 3496 }; 3497 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); 3498 } 3499 3500 fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { 3501 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 3502 _ = bin_op; 3503 return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch}); 3504 } 3505 3506 fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { 3507 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3508 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch}); 3509 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3510 } 3511 3512 fn airClz(self: *Self, inst: Air.Inst.Index) !void { 3513 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3514 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch}); 3515 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3516 } 3517 3518 fn airCtz(self: *Self, inst: Air.Inst.Index) !void { 3519 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3520 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch}); 3521 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3522 } 3523 3524 fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { 3525 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3526 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch}); 3527 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3528 } 3529 3530 fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { 3531 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3532 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); 3533 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3534 } 3535 3536 fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { 3537 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3538 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch}); 3539 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3540 } 3541 3542 fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { 3543 const un_op = self.air.instructions.items(.data)[inst].un_op; 3544 const result: MCValue = if (self.liveness.isUnused(inst)) 3545 .dead 3546 else 3547 return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch}); 3548 return self.finishAir(inst, result, .{ un_op, .none, .none }); 3549 } 3550 3551 fn reuseOperand( 3552 self: *Self, 3553 inst: Air.Inst.Index, 3554 operand: Air.Inst.Ref, 3555 op_index: Liveness.OperandInt, 3556 mcv: MCValue, 3557 ) bool { 3558 if (!self.liveness.operandDies(inst, op_index)) 3559 return false; 3560 3561 switch (mcv) { 3562 .register => |reg| { 3563 // If it's in the registers table, need to associate the register with the 3564 // new instruction. 3565 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { 3566 if (!self.register_manager.isRegFree(reg)) { 3567 self.register_manager.registers[index] = inst; 3568 } 3569 } 3570 log.debug("%{d} => {} (reused)", .{ inst, reg }); 3571 }, 3572 .stack_offset => |off| { 3573 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off }); 3574 }, 3575 else => return false, 3576 } 3577 3578 // Prevent the operand deaths processing code from deallocating it. 3579 self.liveness.clearOperandDeath(inst, op_index); 3580 3581 // That makes us responsible for doing the rest of the stuff that processDeath would have done. 3582 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 3583 branch.inst_table.putAssumeCapacity(Air.refToIndex(operand).?, .dead); 3584 3585 return true; 3586 } 3587 3588 fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { 3589 const elem_ty = ptr_ty.elemType(); 3590 const elem_size = elem_ty.abiSize(self.target.*); 3591 3592 switch (ptr) { 3593 .none => unreachable, 3594 .undef => unreachable, 3595 .unreach => unreachable, 3596 .dead => unreachable, 3597 .compare_flags, 3598 .register_with_overflow, 3599 => unreachable, // cannot hold an address 3600 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), 3601 .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), 3602 .register => |addr_reg| { 3603 const addr_reg_lock = self.register_manager.lockReg(addr_reg); 3604 defer if (addr_reg_lock) |reg| self.register_manager.unlockReg(reg); 3605 3606 switch (dst_mcv) { 3607 .dead => unreachable, 3608 .undef => unreachable, 3609 .compare_flags => unreachable, 3610 .register => |dst_reg| { 3611 try self.genLdrRegister(dst_reg, addr_reg, elem_ty); 3612 }, 3613 .stack_offset => |off| { 3614 if (elem_size <= 8) { 3615 const raw_tmp_reg = try self.register_manager.allocReg(null, gp); 3616 const tmp_reg = self.registerAlias(raw_tmp_reg, elem_ty); 3617 const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 3618 defer self.register_manager.unlockReg(tmp_reg_lock); 3619 3620 try self.load(.{ .register = tmp_reg }, ptr, ptr_ty); 3621 try self.genSetStack(elem_ty, off, MCValue{ .register = tmp_reg }); 3622 } else { 3623 // TODO optimize the register allocation 3624 const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp); 3625 const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); 3626 defer for (regs_locks) |reg| { 3627 self.register_manager.unlockReg(reg); 3628 }; 3629 3630 const src_reg = addr_reg; 3631 const dst_reg = regs[0]; 3632 const len_reg = regs[1]; 3633 const count_reg = regs[2]; 3634 const tmp_reg = regs[3]; 3635 3636 // sub dst_reg, fp, #off 3637 try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = off }); 3638 3639 // mov len, #elem_size 3640 try self.genSetReg(Type.usize, len_reg, .{ .immediate = elem_size }); 3641 3642 // memcpy(src, dst, len) 3643 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); 3644 } 3645 }, 3646 else => return self.fail("TODO load from register into {}", .{dst_mcv}), 3647 } 3648 }, 3649 .memory, 3650 .stack_offset, 3651 .stack_argument_offset, 3652 .linker_load, 3653 => { 3654 const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr); 3655 try self.load(dst_mcv, .{ .register = addr_reg }, ptr_ty); 3656 }, 3657 } 3658 } 3659 3660 fn genInlineMemcpy( 3661 self: *Self, 3662 src: Register, 3663 dst: Register, 3664 len: Register, 3665 count: Register, 3666 tmp: Register, 3667 ) !void { 3668 // movz count, #0 3669 _ = try self.addInst(.{ 3670 .tag = .movz, 3671 .data = .{ .r_imm16_sh = .{ 3672 .rd = count, 3673 .imm16 = 0, 3674 } }, 3675 }); 3676 3677 // loop: 3678 // cmp count, len 3679 _ = try self.addInst(.{ 3680 .tag = .cmp_shifted_register, 3681 .data = .{ .rr_imm6_shift = .{ 3682 .rn = count, 3683 .rm = len, 3684 .imm6 = 0, 3685 .shift = .lsl, 3686 } }, 3687 }); 3688 3689 // bge end 3690 _ = try self.addInst(.{ 3691 .tag = .b_cond, 3692 .data = .{ .inst_cond = .{ 3693 .inst = @intCast(u32, self.mir_instructions.len + 5), 3694 .cond = .ge, 3695 } }, 3696 }); 3697 3698 // ldrb tmp, [src, count] 3699 _ = try self.addInst(.{ 3700 .tag = .ldrb_register, 3701 .data = .{ .load_store_register_register = .{ 3702 .rt = tmp, 3703 .rn = src, 3704 .offset = Instruction.LoadStoreOffset.reg(count).register, 3705 } }, 3706 }); 3707 3708 // strb tmp, [dest, count] 3709 _ = try self.addInst(.{ 3710 .tag = .strb_register, 3711 .data = .{ .load_store_register_register = .{ 3712 .rt = tmp, 3713 .rn = dst, 3714 .offset = Instruction.LoadStoreOffset.reg(count).register, 3715 } }, 3716 }); 3717 3718 // add count, count, #1 3719 _ = try self.addInst(.{ 3720 .tag = .add_immediate, 3721 .data = .{ .rr_imm12_sh = .{ 3722 .rd = count, 3723 .rn = count, 3724 .imm12 = 1, 3725 } }, 3726 }); 3727 3728 // b loop 3729 _ = try self.addInst(.{ 3730 .tag = .b, 3731 .data = .{ .inst = @intCast(u32, self.mir_instructions.len - 5) }, 3732 }); 3733 3734 // end: 3735 } 3736 3737 fn genInlineMemset( 3738 self: *Self, 3739 dst: MCValue, 3740 val: MCValue, 3741 len: MCValue, 3742 ) !void { 3743 const dst_reg = switch (dst) { 3744 .register => |r| r, 3745 else => try self.copyToTmpRegister(Type.initTag(.manyptr_u8), dst), 3746 }; 3747 const dst_reg_lock = self.register_manager.lockReg(dst_reg); 3748 defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); 3749 3750 const val_reg = switch (val) { 3751 .register => |r| r, 3752 else => try self.copyToTmpRegister(Type.initTag(.u8), val), 3753 }; 3754 const val_reg_lock = self.register_manager.lockReg(val_reg); 3755 defer if (val_reg_lock) |lock| self.register_manager.unlockReg(lock); 3756 3757 const len_reg = switch (len) { 3758 .register => |r| r, 3759 else => try self.copyToTmpRegister(Type.usize, len), 3760 }; 3761 const len_reg_lock = self.register_manager.lockReg(len_reg); 3762 defer if (len_reg_lock) |lock| self.register_manager.unlockReg(lock); 3763 3764 const count_reg = try self.register_manager.allocReg(null, gp); 3765 3766 try self.genInlineMemsetCode(dst_reg, val_reg, len_reg, count_reg); 3767 } 3768 3769 fn genInlineMemsetCode( 3770 self: *Self, 3771 dst: Register, 3772 val: Register, 3773 len: Register, 3774 count: Register, 3775 ) !void { 3776 // mov count, #0 3777 _ = try self.addInst(.{ 3778 .tag = .movz, 3779 .data = .{ .r_imm16_sh = .{ 3780 .rd = count, 3781 .imm16 = 0, 3782 } }, 3783 }); 3784 3785 // loop: 3786 // cmp count, len 3787 _ = try self.addInst(.{ 3788 .tag = .cmp_shifted_register, 3789 .data = .{ .rr_imm6_shift = .{ 3790 .rn = count, 3791 .rm = len, 3792 .imm6 = 0, 3793 .shift = .lsl, 3794 } }, 3795 }); 3796 3797 // bge end 3798 _ = try self.addInst(.{ 3799 .tag = .b_cond, 3800 .data = .{ .inst_cond = .{ 3801 .inst = @intCast(u32, self.mir_instructions.len + 4), 3802 .cond = .ge, 3803 } }, 3804 }); 3805 3806 // strb val, [src, count] 3807 _ = try self.addInst(.{ 3808 .tag = .strb_register, 3809 .data = .{ .load_store_register_register = .{ 3810 .rt = val, 3811 .rn = dst, 3812 .offset = Instruction.LoadStoreOffset.reg(count).register, 3813 } }, 3814 }); 3815 3816 // add count, count, #1 3817 _ = try self.addInst(.{ 3818 .tag = .add_immediate, 3819 .data = .{ .rr_imm12_sh = .{ 3820 .rd = count, 3821 .rn = count, 3822 .imm12 = 1, 3823 } }, 3824 }); 3825 3826 // b loop 3827 _ = try self.addInst(.{ 3828 .tag = .b, 3829 .data = .{ .inst = @intCast(u32, self.mir_instructions.len - 4) }, 3830 }); 3831 3832 // end: 3833 } 3834 3835 fn airLoad(self: *Self, inst: Air.Inst.Index) !void { 3836 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 3837 const elem_ty = self.air.typeOfIndex(inst); 3838 const elem_size = elem_ty.abiSize(self.target.*); 3839 const result: MCValue = result: { 3840 if (!elem_ty.hasRuntimeBits()) 3841 break :result MCValue.none; 3842 3843 const ptr = try self.resolveInst(ty_op.operand); 3844 const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr(); 3845 if (self.liveness.isUnused(inst) and !is_volatile) 3846 break :result MCValue.dead; 3847 3848 const dst_mcv: MCValue = blk: { 3849 if (elem_size <= 8 and self.reuseOperand(inst, ty_op.operand, 0, ptr)) { 3850 // The MCValue that holds the pointer can be re-used as the value. 3851 break :blk switch (ptr) { 3852 .register => |reg| MCValue{ .register = self.registerAlias(reg, elem_ty) }, 3853 else => ptr, 3854 }; 3855 } else { 3856 break :blk try self.allocRegOrMem(elem_ty, true, inst); 3857 } 3858 }; 3859 try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand)); 3860 break :result dst_mcv; 3861 }; 3862 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 3863 } 3864 3865 fn genLdrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void { 3866 const abi_size = ty.abiSize(self.target.*); 3867 3868 const tag: Mir.Inst.Tag = switch (abi_size) { 3869 1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb_immediate else .ldrb_immediate, 3870 2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh_immediate else .ldrh_immediate, 3871 4 => .ldr_immediate, 3872 8 => .ldr_immediate, 3873 3, 5, 6, 7 => return self.fail("TODO: genLdrRegister for more abi_sizes", .{}), 3874 else => unreachable, 3875 }; 3876 3877 _ = try self.addInst(.{ 3878 .tag = tag, 3879 .data = .{ .load_store_register_immediate = .{ 3880 .rt = value_reg, 3881 .rn = addr_reg, 3882 .offset = Instruction.LoadStoreOffset.none.immediate, 3883 } }, 3884 }); 3885 } 3886 3887 fn genStrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type) !void { 3888 const abi_size = ty.abiSize(self.target.*); 3889 3890 const tag: Mir.Inst.Tag = switch (abi_size) { 3891 1 => .strb_immediate, 3892 2 => .strh_immediate, 3893 4, 8 => .str_immediate, 3894 3, 5, 6, 7 => return self.fail("TODO: genStrRegister for more abi_sizes", .{}), 3895 else => unreachable, 3896 }; 3897 3898 _ = try self.addInst(.{ 3899 .tag = tag, 3900 .data = .{ .load_store_register_immediate = .{ 3901 .rt = value_reg, 3902 .rn = addr_reg, 3903 .offset = Instruction.LoadStoreOffset.none.immediate, 3904 } }, 3905 }); 3906 } 3907 3908 fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { 3909 log.debug("store: storing {} to {}", .{ value, ptr }); 3910 const abi_size = value_ty.abiSize(self.target.*); 3911 3912 switch (ptr) { 3913 .none => unreachable, 3914 .undef => unreachable, 3915 .unreach => unreachable, 3916 .dead => unreachable, 3917 .compare_flags, 3918 .register_with_overflow, 3919 => unreachable, // cannot hold an address 3920 .immediate => |imm| { 3921 try self.setRegOrMem(value_ty, .{ .memory = imm }, value); 3922 }, 3923 .ptr_stack_offset => |off| { 3924 try self.genSetStack(value_ty, off, value); 3925 }, 3926 .register => |addr_reg| { 3927 const addr_reg_lock = self.register_manager.lockReg(addr_reg); 3928 defer if (addr_reg_lock) |reg| self.register_manager.unlockReg(reg); 3929 3930 switch (value) { 3931 .dead => unreachable, 3932 .undef => { 3933 try self.genSetReg(value_ty, addr_reg, value); 3934 }, 3935 .register => |value_reg| { 3936 log.debug("store: register {} to {}", .{ value_reg, addr_reg }); 3937 try self.genStrRegister(value_reg, addr_reg, value_ty); 3938 }, 3939 else => { 3940 if (abi_size <= 8) { 3941 const raw_tmp_reg = try self.register_manager.allocReg(null, gp); 3942 const tmp_reg = self.registerAlias(raw_tmp_reg, value_ty); 3943 const tmp_reg_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 3944 defer self.register_manager.unlockReg(tmp_reg_lock); 3945 3946 try self.genSetReg(value_ty, tmp_reg, value); 3947 try self.store(ptr, .{ .register = tmp_reg }, ptr_ty, value_ty); 3948 } else { 3949 const regs = try self.register_manager.allocRegs(4, .{ null, null, null, null }, gp); 3950 const regs_locks = self.register_manager.lockRegsAssumeUnused(4, regs); 3951 defer for (regs_locks) |reg| { 3952 self.register_manager.unlockReg(reg); 3953 }; 3954 3955 const src_reg = regs[0]; 3956 const dst_reg = addr_reg; 3957 const len_reg = regs[1]; 3958 const count_reg = regs[2]; 3959 const tmp_reg = regs[3]; 3960 3961 switch (value) { 3962 .stack_offset => |off| { 3963 // sub src_reg, fp, #off 3964 try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); 3965 }, 3966 .stack_argument_offset => |off| { 3967 _ = try self.addInst(.{ 3968 .tag = .ldr_ptr_stack_argument, 3969 .data = .{ .load_store_stack = .{ 3970 .rt = src_reg, 3971 .offset = off, 3972 } }, 3973 }); 3974 }, 3975 .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = @intCast(u32, addr) }), 3976 .linker_load => |load_struct| { 3977 const tag: Mir.Inst.Tag = switch (load_struct.type) { 3978 .got => .load_memory_ptr_got, 3979 .direct => .load_memory_ptr_direct, 3980 .import => unreachable, 3981 }; 3982 const atom_index = switch (self.bin_file.tag) { 3983 .macho => blk: { 3984 const macho_file = self.bin_file.cast(link.File.MachO).?; 3985 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); 3986 break :blk macho_file.getAtom(atom).getSymbolIndex().?; 3987 }, 3988 .coff => blk: { 3989 const coff_file = self.bin_file.cast(link.File.Coff).?; 3990 const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); 3991 break :blk coff_file.getAtom(atom).getSymbolIndex().?; 3992 }, 3993 else => unreachable, // unsupported target format 3994 }; 3995 _ = try self.addInst(.{ 3996 .tag = tag, 3997 .data = .{ 3998 .payload = try self.addExtra(Mir.LoadMemoryPie{ 3999 .register = @enumToInt(src_reg), 4000 .atom_index = atom_index, 4001 .sym_index = load_struct.sym_index, 4002 }), 4003 }, 4004 }); 4005 }, 4006 else => return self.fail("TODO store {} to register", .{value}), 4007 } 4008 4009 // mov len, #abi_size 4010 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size }); 4011 4012 // memcpy(src, dst, len) 4013 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); 4014 } 4015 }, 4016 } 4017 }, 4018 .memory, 4019 .stack_offset, 4020 .stack_argument_offset, 4021 .linker_load, 4022 => { 4023 const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr); 4024 try self.store(.{ .register = addr_reg }, value, ptr_ty, value_ty); 4025 }, 4026 } 4027 } 4028 4029 fn airStore(self: *Self, inst: Air.Inst.Index) !void { 4030 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 4031 const ptr = try self.resolveInst(bin_op.lhs); 4032 const value = try self.resolveInst(bin_op.rhs); 4033 const ptr_ty = self.air.typeOf(bin_op.lhs); 4034 const value_ty = self.air.typeOf(bin_op.rhs); 4035 4036 try self.store(ptr, value, ptr_ty, value_ty); 4037 4038 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); 4039 } 4040 4041 fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void { 4042 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 4043 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; 4044 const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index); 4045 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); 4046 } 4047 4048 fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { 4049 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 4050 const result = try self.structFieldPtr(inst, ty_op.operand, index); 4051 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 4052 } 4053 4054 fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue { 4055 return if (self.liveness.isUnused(inst)) .dead else result: { 4056 const mcv = try self.resolveInst(operand); 4057 const ptr_ty = self.air.typeOf(operand); 4058 const struct_ty = ptr_ty.childType(); 4059 const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); 4060 switch (mcv) { 4061 .ptr_stack_offset => |off| { 4062 break :result MCValue{ .ptr_stack_offset = off - struct_field_offset }; 4063 }, 4064 else => { 4065 const lhs_bind: ReadArg.Bind = .{ .mcv = mcv }; 4066 const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } }; 4067 4068 break :result try self.addSub(.add, lhs_bind, rhs_bind, Type.usize, Type.usize, null); 4069 }, 4070 } 4071 }; 4072 } 4073 4074 fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { 4075 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 4076 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; 4077 const operand = extra.struct_operand; 4078 const index = extra.field_index; 4079 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 4080 const mcv = try self.resolveInst(operand); 4081 const struct_ty = self.air.typeOf(operand); 4082 const struct_field_ty = struct_ty.structFieldType(index); 4083 const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); 4084 4085 switch (mcv) { 4086 .dead, .unreach => unreachable, 4087 .stack_argument_offset => |off| { 4088 break :result MCValue{ .stack_argument_offset = off + struct_field_offset }; 4089 }, 4090 .stack_offset => |off| { 4091 break :result MCValue{ .stack_offset = off - struct_field_offset }; 4092 }, 4093 .memory => |addr| { 4094 break :result MCValue{ .memory = addr + struct_field_offset }; 4095 }, 4096 .register_with_overflow => |rwo| { 4097 const reg_lock = self.register_manager.lockRegAssumeUnused(rwo.reg); 4098 defer self.register_manager.unlockReg(reg_lock); 4099 4100 const field: MCValue = switch (index) { 4101 // get wrapped value: return register 4102 0 => MCValue{ .register = rwo.reg }, 4103 4104 // get overflow bit: return C or V flag 4105 1 => MCValue{ .compare_flags = rwo.flag }, 4106 4107 else => unreachable, 4108 }; 4109 4110 if (self.reuseOperand(inst, operand, 0, field)) { 4111 break :result field; 4112 } else { 4113 // Copy to new register 4114 const raw_dest_reg = try self.register_manager.allocReg(null, gp); 4115 const dest_reg = self.registerAlias(raw_dest_reg, struct_field_ty); 4116 try self.genSetReg(struct_field_ty, dest_reg, field); 4117 4118 break :result MCValue{ .register = dest_reg }; 4119 } 4120 }, 4121 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}), 4122 } 4123 }; 4124 4125 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); 4126 } 4127 4128 fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { 4129 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 4130 const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; 4131 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 4132 const field_ptr = try self.resolveInst(extra.field_ptr); 4133 const struct_ty = self.air.getRefType(ty_pl.ty).childType(); 4134 const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(extra.field_index, self.target.*)); 4135 switch (field_ptr) { 4136 .ptr_stack_offset => |off| { 4137 break :result MCValue{ .ptr_stack_offset = off + struct_field_offset }; 4138 }, 4139 else => { 4140 const lhs_bind: ReadArg.Bind = .{ .mcv = field_ptr }; 4141 const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } }; 4142 4143 break :result try self.addSub(.sub, lhs_bind, rhs_bind, Type.usize, Type.usize, null); 4144 }, 4145 } 4146 }; 4147 return self.finishAir(inst, result, .{ extra.field_ptr, .none, .none }); 4148 } 4149 4150 fn airArg(self: *Self, inst: Air.Inst.Index) !void { 4151 // skip zero-bit arguments as they don't have a corresponding arg instruction 4152 var arg_index = self.arg_index; 4153 while (self.args[arg_index] == .none) arg_index += 1; 4154 self.arg_index = arg_index + 1; 4155 4156 const ty = self.air.typeOfIndex(inst); 4157 const tag = self.air.instructions.items(.tag)[inst]; 4158 const src_index = self.air.instructions.items(.data)[inst].arg.src_index; 4159 const name = self.mod_fn.getParamName(self.bin_file.options.module.?, src_index); 4160 4161 try self.dbg_info_relocs.append(self.gpa, .{ 4162 .tag = tag, 4163 .ty = ty, 4164 .name = name, 4165 .mcv = self.args[arg_index], 4166 }); 4167 4168 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else self.args[arg_index]; 4169 return self.finishAir(inst, result, .{ .none, .none, .none }); 4170 } 4171 4172 fn airTrap(self: *Self) !void { 4173 _ = try self.addInst(.{ 4174 .tag = .brk, 4175 .data = .{ .imm16 = 0x0001 }, 4176 }); 4177 return self.finishAirBookkeeping(); 4178 } 4179 4180 fn airBreakpoint(self: *Self) !void { 4181 _ = try self.addInst(.{ 4182 .tag = .brk, 4183 .data = .{ .imm16 = 0xf000 }, 4184 }); 4185 return self.finishAirBookkeeping(); 4186 } 4187 4188 fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void { 4189 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for aarch64", .{}); 4190 return self.finishAir(inst, result, .{ .none, .none, .none }); 4191 } 4192 4193 fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void { 4194 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for aarch64", .{}); 4195 return self.finishAir(inst, result, .{ .none, .none, .none }); 4196 } 4197 4198 fn airFence(self: *Self) !void { 4199 return self.fail("TODO implement fence() for {}", .{self.target.cpu.arch}); 4200 //return self.finishAirBookkeeping(); 4201 } 4202 4203 fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { 4204 if (modifier == .always_tail) return self.fail("TODO implement tail calls for aarch64", .{}); 4205 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 4206 const callee = pl_op.operand; 4207 const extra = self.air.extraData(Air.Call, pl_op.payload); 4208 const args = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); 4209 const ty = self.air.typeOf(callee); 4210 4211 const fn_ty = switch (ty.zigTypeTag()) { 4212 .Fn => ty, 4213 .Pointer => ty.childType(), 4214 else => unreachable, 4215 }; 4216 4217 var info = try self.resolveCallingConventionValues(fn_ty); 4218 defer info.deinit(self); 4219 4220 // According to the Procedure Call Standard for the ARM 4221 // Architecture, compare flags are not preserved across 4222 // calls. Therefore, if some value is currently stored there, we 4223 // need to save it. 4224 // 4225 // TODO once caller-saved registers are implemented, save them 4226 // here too, but crucially *after* we save the compare flags as 4227 // saving compare flags may require a new caller-saved register 4228 try self.spillCompareFlagsIfOccupied(); 4229 4230 if (info.return_value == .stack_offset) { 4231 log.debug("airCall: return by reference", .{}); 4232 const ret_ty = fn_ty.fnReturnType(); 4233 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); 4234 const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*)); 4235 const stack_offset = try self.allocMem(ret_abi_size, ret_abi_align, inst); 4236 4237 const ret_ptr_reg = self.registerAlias(.x0, Type.usize); 4238 4239 var ptr_ty_payload: Type.Payload.ElemType = .{ 4240 .base = .{ .tag = .single_mut_pointer }, 4241 .data = ret_ty, 4242 }; 4243 const ptr_ty = Type.initPayload(&ptr_ty_payload.base); 4244 try self.register_manager.getReg(ret_ptr_reg, null); 4245 try self.genSetReg(ptr_ty, ret_ptr_reg, .{ .ptr_stack_offset = stack_offset }); 4246 4247 info.return_value = .{ .stack_offset = stack_offset }; 4248 } 4249 4250 // Make space for the arguments passed via the stack 4251 self.max_end_stack += info.stack_byte_count; 4252 4253 for (info.args, 0..) |mc_arg, arg_i| { 4254 const arg = args[arg_i]; 4255 const arg_ty = self.air.typeOf(arg); 4256 const arg_mcv = try self.resolveInst(args[arg_i]); 4257 4258 switch (mc_arg) { 4259 .none => continue, 4260 .register => |reg| { 4261 try self.register_manager.getReg(reg, null); 4262 try self.genSetReg(arg_ty, reg, arg_mcv); 4263 }, 4264 .stack_offset => unreachable, 4265 .stack_argument_offset => |offset| try self.genSetStackArgument( 4266 arg_ty, 4267 offset, 4268 arg_mcv, 4269 ), 4270 else => unreachable, 4271 } 4272 } 4273 4274 // Due to incremental compilation, how function calls are generated depends 4275 // on linking. 4276 const mod = self.bin_file.options.module.?; 4277 if (self.air.value(callee)) |func_value| { 4278 if (func_value.castTag(.function)) |func_payload| { 4279 const func = func_payload.data; 4280 4281 if (self.bin_file.cast(link.File.Elf)) |elf_file| { 4282 const atom_index = try elf_file.getOrCreateAtomForDecl(func.owner_decl); 4283 const atom = elf_file.getAtom(atom_index); 4284 const got_addr = @intCast(u32, atom.getOffsetTableAddress(elf_file)); 4285 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = got_addr }); 4286 } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { 4287 const atom = try macho_file.getOrCreateAtomForDecl(func.owner_decl); 4288 const sym_index = macho_file.getAtom(atom).getSymbolIndex().?; 4289 try self.genSetReg(Type.initTag(.u64), .x30, .{ 4290 .linker_load = .{ 4291 .type = .got, 4292 .sym_index = sym_index, 4293 }, 4294 }); 4295 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { 4296 const atom = try coff_file.getOrCreateAtomForDecl(func.owner_decl); 4297 const sym_index = coff_file.getAtom(atom).getSymbolIndex().?; 4298 try self.genSetReg(Type.initTag(.u64), .x30, .{ 4299 .linker_load = .{ 4300 .type = .got, 4301 .sym_index = sym_index, 4302 }, 4303 }); 4304 } else if (self.bin_file.cast(link.File.Plan9)) |p9| { 4305 const decl_block_index = try p9.seeDecl(func.owner_decl); 4306 const decl_block = p9.getDeclBlock(decl_block_index); 4307 const ptr_bits = self.target.cpu.arch.ptrBitWidth(); 4308 const ptr_bytes: u64 = @divExact(ptr_bits, 8); 4309 const got_addr = p9.bases.data; 4310 const got_index = decl_block.got_index.?; 4311 const fn_got_addr = got_addr + got_index * ptr_bytes; 4312 try self.genSetReg(Type.initTag(.usize), .x30, .{ .memory = fn_got_addr }); 4313 } else unreachable; 4314 4315 _ = try self.addInst(.{ 4316 .tag = .blr, 4317 .data = .{ .reg = .x30 }, 4318 }); 4319 } else if (func_value.castTag(.extern_fn)) |func_payload| { 4320 const extern_fn = func_payload.data; 4321 const decl_name = mod.declPtr(extern_fn.owner_decl).name; 4322 if (extern_fn.lib_name) |lib_name| { 4323 log.debug("TODO enforce that '{s}' is expected in '{s}' library", .{ 4324 decl_name, 4325 lib_name, 4326 }); 4327 } 4328 4329 if (self.bin_file.cast(link.File.MachO)) |macho_file| { 4330 const sym_index = try macho_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); 4331 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); 4332 const atom_index = macho_file.getAtom(atom).getSymbolIndex().?; 4333 _ = try self.addInst(.{ 4334 .tag = .call_extern, 4335 .data = .{ 4336 .relocation = .{ 4337 .atom_index = atom_index, 4338 .sym_index = sym_index, 4339 }, 4340 }, 4341 }); 4342 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { 4343 const sym_index = try coff_file.getGlobalSymbol(mem.sliceTo(decl_name, 0)); 4344 try self.genSetReg(Type.initTag(.u64), .x30, .{ 4345 .linker_load = .{ 4346 .type = .import, 4347 .sym_index = sym_index, 4348 }, 4349 }); 4350 _ = try self.addInst(.{ 4351 .tag = .blr, 4352 .data = .{ .reg = .x30 }, 4353 }); 4354 } else { 4355 return self.fail("TODO implement calling extern functions", .{}); 4356 } 4357 } else { 4358 return self.fail("TODO implement calling bitcasted functions", .{}); 4359 } 4360 } else { 4361 assert(ty.zigTypeTag() == .Pointer); 4362 const mcv = try self.resolveInst(callee); 4363 try self.genSetReg(ty, .x30, mcv); 4364 4365 _ = try self.addInst(.{ 4366 .tag = .blr, 4367 .data = .{ .reg = .x30 }, 4368 }); 4369 } 4370 4371 const result: MCValue = result: { 4372 switch (info.return_value) { 4373 .register => |reg| { 4374 if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) { 4375 // Save function return value in a callee saved register 4376 break :result try self.copyToNewRegister(inst, info.return_value); 4377 } 4378 }, 4379 else => {}, 4380 } 4381 break :result info.return_value; 4382 }; 4383 4384 if (args.len + 1 <= Liveness.bpi - 1) { 4385 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); 4386 buf[0] = callee; 4387 std.mem.copy(Air.Inst.Ref, buf[1..], args); 4388 return self.finishAir(inst, result, buf); 4389 } 4390 var bt = try self.iterateBigTomb(inst, 1 + args.len); 4391 bt.feed(callee); 4392 for (args) |arg| { 4393 bt.feed(arg); 4394 } 4395 return bt.finishAir(result); 4396 } 4397 4398 fn airRet(self: *Self, inst: Air.Inst.Index) !void { 4399 const un_op = self.air.instructions.items(.data)[inst].un_op; 4400 const operand = try self.resolveInst(un_op); 4401 const ret_ty = self.fn_type.fnReturnType(); 4402 4403 switch (self.ret_mcv) { 4404 .none => {}, 4405 .immediate => { 4406 assert(ret_ty.isError()); 4407 }, 4408 .register => |reg| { 4409 // Return result by value 4410 try self.genSetReg(ret_ty, reg, operand); 4411 }, 4412 .stack_offset => { 4413 // Return result by reference 4414 // 4415 // self.ret_mcv is an address to where this function 4416 // should store its result into 4417 var ptr_ty_payload: Type.Payload.ElemType = .{ 4418 .base = .{ .tag = .single_mut_pointer }, 4419 .data = ret_ty, 4420 }; 4421 const ptr_ty = Type.initPayload(&ptr_ty_payload.base); 4422 try self.store(self.ret_mcv, operand, ptr_ty, ret_ty); 4423 }, 4424 else => unreachable, 4425 } 4426 4427 // Just add space for an instruction, patch this later 4428 try self.exitlude_jump_relocs.append(self.gpa, try self.addNop()); 4429 4430 return self.finishAir(inst, .dead, .{ un_op, .none, .none }); 4431 } 4432 4433 fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { 4434 const un_op = self.air.instructions.items(.data)[inst].un_op; 4435 const ptr = try self.resolveInst(un_op); 4436 const ptr_ty = self.air.typeOf(un_op); 4437 const ret_ty = self.fn_type.fnReturnType(); 4438 4439 switch (self.ret_mcv) { 4440 .none => {}, 4441 .register => { 4442 // Return result by value 4443 try self.load(self.ret_mcv, ptr, ptr_ty); 4444 }, 4445 .stack_offset => { 4446 // Return result by reference 4447 // 4448 // self.ret_mcv is an address to where this function 4449 // should store its result into 4450 // 4451 // If the operand is a ret_ptr instruction, we are done 4452 // here. Else we need to load the result from the location 4453 // pointed to by the operand and store it to the result 4454 // location. 4455 const op_inst = Air.refToIndex(un_op).?; 4456 if (self.air.instructions.items(.tag)[op_inst] != .ret_ptr) { 4457 const abi_size = @intCast(u32, ret_ty.abiSize(self.target.*)); 4458 const abi_align = ret_ty.abiAlignment(self.target.*); 4459 4460 const offset = try self.allocMem(abi_size, abi_align, null); 4461 4462 const tmp_mcv = MCValue{ .stack_offset = offset }; 4463 try self.load(tmp_mcv, ptr, ptr_ty); 4464 try self.store(self.ret_mcv, tmp_mcv, ptr_ty, ret_ty); 4465 } 4466 }, 4467 else => unreachable, // invalid return result 4468 } 4469 4470 try self.exitlude_jump_relocs.append(self.gpa, try self.addNop()); 4471 4472 return self.finishAir(inst, .dead, .{ un_op, .none, .none }); 4473 } 4474 4475 fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { 4476 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 4477 const lhs_ty = self.air.typeOf(bin_op.lhs); 4478 4479 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: { 4480 break :blk try self.cmp(.{ .inst = bin_op.lhs }, .{ .inst = bin_op.rhs }, lhs_ty, op); 4481 }; 4482 4483 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 4484 } 4485 4486 fn cmp( 4487 self: *Self, 4488 lhs: ReadArg.Bind, 4489 rhs: ReadArg.Bind, 4490 lhs_ty: Type, 4491 op: math.CompareOperator, 4492 ) !MCValue { 4493 var int_buffer: Type.Payload.Bits = undefined; 4494 const int_ty = switch (lhs_ty.zigTypeTag()) { 4495 .Optional => blk: { 4496 var opt_buffer: Type.Payload.ElemType = undefined; 4497 const payload_ty = lhs_ty.optionalChild(&opt_buffer); 4498 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { 4499 break :blk Type.initTag(.u1); 4500 } else if (lhs_ty.isPtrLikeOptional()) { 4501 break :blk Type.usize; 4502 } else { 4503 return self.fail("TODO ARM cmp non-pointer optionals", .{}); 4504 } 4505 }, 4506 .Float => return self.fail("TODO ARM cmp floats", .{}), 4507 .Enum => lhs_ty.intTagType(&int_buffer), 4508 .Int => lhs_ty, 4509 .Bool => Type.initTag(.u1), 4510 .Pointer => Type.usize, 4511 .ErrorSet => Type.initTag(.u16), 4512 else => unreachable, 4513 }; 4514 4515 const int_info = int_ty.intInfo(self.target.*); 4516 if (int_info.bits <= 64) { 4517 try self.spillCompareFlagsIfOccupied(); 4518 4519 var lhs_reg: Register = undefined; 4520 var rhs_reg: Register = undefined; 4521 4522 const rhs_immediate = try rhs.resolveToImmediate(self); 4523 const rhs_immediate_ok = if (rhs_immediate) |imm| imm <= std.math.maxInt(u12) else false; 4524 4525 if (rhs_immediate_ok) { 4526 const read_args = [_]ReadArg{ 4527 .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg }, 4528 }; 4529 try self.allocRegs( 4530 &read_args, 4531 &.{}, 4532 null, // we won't be able to reuse a register as there are no write_regs 4533 ); 4534 4535 _ = try self.addInst(.{ 4536 .tag = .cmp_immediate, 4537 .data = .{ .r_imm12_sh = .{ 4538 .rn = lhs_reg, 4539 .imm12 = @intCast(u12, rhs_immediate.?), 4540 } }, 4541 }); 4542 } else { 4543 const read_args = [_]ReadArg{ 4544 .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg }, 4545 .{ .ty = int_ty, .bind = rhs, .class = gp, .reg = &rhs_reg }, 4546 }; 4547 try self.allocRegs( 4548 &read_args, 4549 &.{}, 4550 null, // we won't be able to reuse a register as there are no write_regs 4551 ); 4552 4553 _ = try self.addInst(.{ 4554 .tag = .cmp_shifted_register, 4555 .data = .{ .rr_imm6_shift = .{ 4556 .rn = lhs_reg, 4557 .rm = rhs_reg, 4558 .imm6 = 0, 4559 .shift = .lsl, 4560 } }, 4561 }); 4562 } 4563 4564 return switch (int_info.signedness) { 4565 .signed => MCValue{ .compare_flags = Condition.fromCompareOperatorSigned(op) }, 4566 .unsigned => MCValue{ .compare_flags = Condition.fromCompareOperatorUnsigned(op) }, 4567 }; 4568 } else { 4569 return self.fail("TODO AArch64 cmp for ints > 64 bits", .{}); 4570 } 4571 } 4572 4573 fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { 4574 _ = inst; 4575 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); 4576 } 4577 4578 fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { 4579 const un_op = self.air.instructions.items(.data)[inst].un_op; 4580 const operand = try self.resolveInst(un_op); 4581 _ = operand; 4582 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch}); 4583 return self.finishAir(inst, result, .{ un_op, .none, .none }); 4584 } 4585 4586 fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { 4587 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; 4588 4589 _ = try self.addInst(.{ 4590 .tag = .dbg_line, 4591 .data = .{ .dbg_line_column = .{ 4592 .line = dbg_stmt.line, 4593 .column = dbg_stmt.column, 4594 } }, 4595 }); 4596 4597 return self.finishAirBookkeeping(); 4598 } 4599 4600 fn airDbgInline(self: *Self, inst: Air.Inst.Index) !void { 4601 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 4602 const function = self.air.values[ty_pl.payload].castTag(.function).?.data; 4603 // TODO emit debug info for function change 4604 _ = function; 4605 return self.finishAir(inst, .dead, .{ .none, .none, .none }); 4606 } 4607 4608 fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void { 4609 // TODO emit debug info lexical block 4610 return self.finishAir(inst, .dead, .{ .none, .none, .none }); 4611 } 4612 4613 fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { 4614 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 4615 const operand = pl_op.operand; 4616 const tag = self.air.instructions.items(.tag)[inst]; 4617 const ty = self.air.typeOf(operand); 4618 const mcv = try self.resolveInst(operand); 4619 const name = self.air.nullTerminatedString(pl_op.payload); 4620 4621 log.debug("airDbgVar: %{d}: {}, {}", .{ inst, ty.fmtDebug(), mcv }); 4622 4623 try self.dbg_info_relocs.append(self.gpa, .{ 4624 .tag = tag, 4625 .ty = ty, 4626 .name = name, 4627 .mcv = mcv, 4628 }); 4629 4630 return self.finishAir(inst, .dead, .{ operand, .none, .none }); 4631 } 4632 4633 fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index { 4634 switch (condition) { 4635 .compare_flags => |cond| return try self.addInst(.{ 4636 .tag = .b_cond, 4637 .data = .{ 4638 .inst_cond = .{ 4639 .inst = undefined, // populated later through performReloc 4640 // Here we map to the opposite condition because the jump is to the false branch. 4641 .cond = cond.negate(), 4642 }, 4643 }, 4644 }), 4645 else => { 4646 const reg = switch (condition) { 4647 .register => |r| r, 4648 else => try self.copyToTmpRegister(Type.bool, condition), 4649 }; 4650 4651 return try self.addInst(.{ 4652 .tag = .cbz, 4653 .data = .{ 4654 .r_inst = .{ 4655 .rt = reg, 4656 .inst = undefined, // populated later through performReloc 4657 }, 4658 }, 4659 }); 4660 }, 4661 } 4662 } 4663 4664 fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { 4665 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 4666 const cond = try self.resolveInst(pl_op.operand); 4667 const extra = self.air.extraData(Air.CondBr, pl_op.payload); 4668 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len]; 4669 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]; 4670 const liveness_condbr = self.liveness.getCondBr(inst); 4671 4672 const reloc = try self.condBr(cond); 4673 4674 // If the condition dies here in this condbr instruction, process 4675 // that death now instead of later as this has an effect on 4676 // whether it needs to be spilled in the branches 4677 if (self.liveness.operandDies(inst, 0)) { 4678 const op_int = @enumToInt(pl_op.operand); 4679 if (op_int >= Air.Inst.Ref.typed_value_map.len) { 4680 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); 4681 self.processDeath(op_index); 4682 } 4683 } 4684 4685 // Capture the state of register and stack allocation state so that we can revert to it. 4686 const parent_next_stack_offset = self.next_stack_offset; 4687 const parent_free_registers = self.register_manager.free_registers; 4688 var parent_stack = try self.stack.clone(self.gpa); 4689 defer parent_stack.deinit(self.gpa); 4690 const parent_registers = self.register_manager.registers; 4691 const parent_compare_flags_inst = self.compare_flags_inst; 4692 4693 try self.branch_stack.append(.{}); 4694 errdefer { 4695 _ = self.branch_stack.pop(); 4696 } 4697 4698 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len); 4699 for (liveness_condbr.then_deaths) |operand| { 4700 self.processDeath(operand); 4701 } 4702 try self.genBody(then_body); 4703 4704 // Revert to the previous register and stack allocation state. 4705 4706 var saved_then_branch = self.branch_stack.pop(); 4707 defer saved_then_branch.deinit(self.gpa); 4708 4709 self.register_manager.registers = parent_registers; 4710 self.compare_flags_inst = parent_compare_flags_inst; 4711 4712 self.stack.deinit(self.gpa); 4713 self.stack = parent_stack; 4714 parent_stack = .{}; 4715 4716 self.next_stack_offset = parent_next_stack_offset; 4717 self.register_manager.free_registers = parent_free_registers; 4718 4719 try self.performReloc(reloc); 4720 const else_branch = self.branch_stack.addOneAssumeCapacity(); 4721 else_branch.* = .{}; 4722 4723 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len); 4724 for (liveness_condbr.else_deaths) |operand| { 4725 self.processDeath(operand); 4726 } 4727 try self.genBody(else_body); 4728 4729 // At this point, each branch will possibly have conflicting values for where 4730 // each instruction is stored. They agree, however, on which instructions are alive/dead. 4731 // We use the first ("then") branch as canonical, and here emit 4732 // instructions into the second ("else") branch to make it conform. 4733 // We continue respect the data structure semantic guarantees of the else_branch so 4734 // that we can use all the code emitting abstractions. This is why at the bottom we 4735 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers 4736 // rather than assigning it. 4737 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2]; 4738 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count()); 4739 4740 const else_slice = else_branch.inst_table.entries.slice(); 4741 const else_keys = else_slice.items(.key); 4742 const else_values = else_slice.items(.value); 4743 for (else_keys, 0..) |else_key, else_idx| { 4744 const else_value = else_values[else_idx]; 4745 const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: { 4746 // The instruction's MCValue is overridden in both branches. 4747 parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value); 4748 if (else_value == .dead) { 4749 assert(then_entry.value == .dead); 4750 continue; 4751 } 4752 break :blk then_entry.value; 4753 } else blk: { 4754 if (else_value == .dead) 4755 continue; 4756 // The instruction is only overridden in the else branch. 4757 var i: usize = self.branch_stack.items.len - 1; 4758 while (true) { 4759 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead? 4760 if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| { 4761 assert(mcv != .dead); 4762 break :blk mcv; 4763 } 4764 } 4765 }; 4766 log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv }); 4767 // TODO make sure the destination stack offset / register does not already have something 4768 // going on there. 4769 try self.setRegOrMem(self.air.typeOfIndex(else_key), canon_mcv, else_value); 4770 // TODO track the new register / stack allocation 4771 } 4772 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count()); 4773 const then_slice = saved_then_branch.inst_table.entries.slice(); 4774 const then_keys = then_slice.items(.key); 4775 const then_values = then_slice.items(.value); 4776 for (then_keys, 0..) |then_key, then_idx| { 4777 const then_value = then_values[then_idx]; 4778 // We already deleted the items from this table that matched the else_branch. 4779 // So these are all instructions that are only overridden in the then branch. 4780 parent_branch.inst_table.putAssumeCapacity(then_key, then_value); 4781 if (then_value == .dead) 4782 continue; 4783 const parent_mcv = blk: { 4784 var i: usize = self.branch_stack.items.len - 1; 4785 while (true) { 4786 i -= 1; 4787 if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| { 4788 assert(mcv != .dead); 4789 break :blk mcv; 4790 } 4791 } 4792 }; 4793 log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value }); 4794 // TODO make sure the destination stack offset / register does not already have something 4795 // going on there. 4796 try self.setRegOrMem(self.air.typeOfIndex(then_key), parent_mcv, then_value); 4797 // TODO track the new register / stack allocation 4798 } 4799 4800 { 4801 var item = self.branch_stack.pop(); 4802 item.deinit(self.gpa); 4803 } 4804 4805 // We already took care of pl_op.operand earlier, so we're going 4806 // to pass .none here 4807 return self.finishAir(inst, .unreach, .{ .none, .none, .none }); 4808 } 4809 4810 fn isNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue { 4811 const sentinel: struct { ty: Type, bind: ReadArg.Bind } = if (!operand_ty.isPtrLikeOptional()) blk: { 4812 var buf: Type.Payload.ElemType = undefined; 4813 const payload_ty = operand_ty.optionalChild(&buf); 4814 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) 4815 break :blk .{ .ty = operand_ty, .bind = operand_bind }; 4816 4817 const offset = @intCast(u32, payload_ty.abiSize(self.target.*)); 4818 const operand_mcv = try operand_bind.resolveToMcv(self); 4819 const new_mcv: MCValue = switch (operand_mcv) { 4820 .register => |source_reg| new: { 4821 // TODO should we reuse the operand here? 4822 const raw_reg = try self.register_manager.allocReg(null, gp); 4823 const dest_reg = raw_reg.toX(); 4824 4825 const shift = @intCast(u6, offset * 8); 4826 if (shift == 0) { 4827 try self.genSetReg(payload_ty, dest_reg, operand_mcv); 4828 } else { 4829 _ = try self.addInst(.{ 4830 .tag = if (payload_ty.isSignedInt()) 4831 Mir.Inst.Tag.asr_immediate 4832 else 4833 Mir.Inst.Tag.lsr_immediate, 4834 .data = .{ .rr_shift = .{ 4835 .rd = dest_reg, 4836 .rn = source_reg.toX(), 4837 .shift = shift, 4838 } }, 4839 }); 4840 } 4841 4842 break :new .{ .register = self.registerAlias(dest_reg, payload_ty) }; 4843 }, 4844 .stack_argument_offset => |off| .{ .stack_argument_offset = off + offset }, 4845 .stack_offset => |off| .{ .stack_offset = off - offset }, 4846 .memory => |addr| .{ .memory = addr + offset }, 4847 else => unreachable, // invalid MCValue for an optional 4848 }; 4849 4850 break :blk .{ .ty = Type.bool, .bind = .{ .mcv = new_mcv } }; 4851 } else .{ .ty = operand_ty, .bind = operand_bind }; 4852 const imm_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = 0 } }; 4853 return self.cmp(sentinel.bind, imm_bind, sentinel.ty, .eq); 4854 } 4855 4856 fn isNonNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue { 4857 const is_null_res = try self.isNull(operand_bind, operand_ty); 4858 assert(is_null_res.compare_flags == .eq); 4859 return MCValue{ .compare_flags = is_null_res.compare_flags.negate() }; 4860 } 4861 4862 fn isErr( 4863 self: *Self, 4864 error_union_bind: ReadArg.Bind, 4865 error_union_ty: Type, 4866 ) !MCValue { 4867 const error_type = error_union_ty.errorUnionSet(); 4868 4869 if (error_type.errorSetIsEmpty()) { 4870 return MCValue{ .immediate = 0 }; // always false 4871 } 4872 4873 const error_mcv = try self.errUnionErr(error_union_bind, error_union_ty, null); 4874 return try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .gt); 4875 } 4876 4877 fn isNonErr( 4878 self: *Self, 4879 error_union_bind: ReadArg.Bind, 4880 error_union_ty: Type, 4881 ) !MCValue { 4882 const is_err_result = try self.isErr(error_union_bind, error_union_ty); 4883 switch (is_err_result) { 4884 .compare_flags => |cond| { 4885 assert(cond == .hi); 4886 return MCValue{ .compare_flags = cond.negate() }; 4887 }, 4888 .immediate => |imm| { 4889 assert(imm == 0); 4890 return MCValue{ .immediate = 1 }; 4891 }, 4892 else => unreachable, 4893 } 4894 } 4895 4896 fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { 4897 const un_op = self.air.instructions.items(.data)[inst].un_op; 4898 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 4899 const operand = try self.resolveInst(un_op); 4900 const operand_ty = self.air.typeOf(un_op); 4901 4902 break :result try self.isNull(.{ .mcv = operand }, operand_ty); 4903 }; 4904 return self.finishAir(inst, result, .{ un_op, .none, .none }); 4905 } 4906 4907 fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { 4908 const un_op = self.air.instructions.items(.data)[inst].un_op; 4909 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 4910 const operand_ptr = try self.resolveInst(un_op); 4911 const ptr_ty = self.air.typeOf(un_op); 4912 const elem_ty = ptr_ty.elemType(); 4913 4914 const operand = try self.allocRegOrMem(elem_ty, true, null); 4915 try self.load(operand, operand_ptr, ptr_ty); 4916 4917 break :result try self.isNull(.{ .mcv = operand }, elem_ty); 4918 }; 4919 return self.finishAir(inst, result, .{ un_op, .none, .none }); 4920 } 4921 4922 fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { 4923 const un_op = self.air.instructions.items(.data)[inst].un_op; 4924 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 4925 const operand = try self.resolveInst(un_op); 4926 const operand_ty = self.air.typeOf(un_op); 4927 4928 break :result try self.isNonNull(.{ .mcv = operand }, operand_ty); 4929 }; 4930 return self.finishAir(inst, result, .{ un_op, .none, .none }); 4931 } 4932 4933 fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { 4934 const un_op = self.air.instructions.items(.data)[inst].un_op; 4935 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 4936 const operand_ptr = try self.resolveInst(un_op); 4937 const ptr_ty = self.air.typeOf(un_op); 4938 const elem_ty = ptr_ty.elemType(); 4939 4940 const operand = try self.allocRegOrMem(elem_ty, true, null); 4941 try self.load(operand, operand_ptr, ptr_ty); 4942 4943 break :result try self.isNonNull(.{ .mcv = operand }, elem_ty); 4944 }; 4945 return self.finishAir(inst, result, .{ un_op, .none, .none }); 4946 } 4947 4948 fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { 4949 const un_op = self.air.instructions.items(.data)[inst].un_op; 4950 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 4951 const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; 4952 const error_union_ty = self.air.typeOf(un_op); 4953 4954 break :result try self.isErr(error_union_bind, error_union_ty); 4955 }; 4956 return self.finishAir(inst, result, .{ un_op, .none, .none }); 4957 } 4958 4959 fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { 4960 const un_op = self.air.instructions.items(.data)[inst].un_op; 4961 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 4962 const operand_ptr = try self.resolveInst(un_op); 4963 const ptr_ty = self.air.typeOf(un_op); 4964 const elem_ty = ptr_ty.elemType(); 4965 4966 const operand = try self.allocRegOrMem(elem_ty, true, null); 4967 try self.load(operand, operand_ptr, ptr_ty); 4968 4969 break :result try self.isErr(.{ .mcv = operand }, elem_ty); 4970 }; 4971 return self.finishAir(inst, result, .{ un_op, .none, .none }); 4972 } 4973 4974 fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { 4975 const un_op = self.air.instructions.items(.data)[inst].un_op; 4976 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 4977 const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; 4978 const error_union_ty = self.air.typeOf(un_op); 4979 4980 break :result try self.isNonErr(error_union_bind, error_union_ty); 4981 }; 4982 return self.finishAir(inst, result, .{ un_op, .none, .none }); 4983 } 4984 4985 fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { 4986 const un_op = self.air.instructions.items(.data)[inst].un_op; 4987 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 4988 const operand_ptr = try self.resolveInst(un_op); 4989 const ptr_ty = self.air.typeOf(un_op); 4990 const elem_ty = ptr_ty.elemType(); 4991 4992 const operand = try self.allocRegOrMem(elem_ty, true, null); 4993 try self.load(operand, operand_ptr, ptr_ty); 4994 4995 break :result try self.isNonErr(.{ .mcv = operand }, elem_ty); 4996 }; 4997 return self.finishAir(inst, result, .{ un_op, .none, .none }); 4998 } 4999 5000 fn airLoop(self: *Self, inst: Air.Inst.Index) !void { 5001 // A loop is a setup to be able to jump back to the beginning. 5002 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 5003 const loop = self.air.extraData(Air.Block, ty_pl.payload); 5004 const body = self.air.extra[loop.end..][0..loop.data.body_len]; 5005 const start_index = @intCast(u32, self.mir_instructions.len); 5006 try self.genBody(body); 5007 try self.jump(start_index); 5008 return self.finishAirBookkeeping(); 5009 } 5010 5011 /// Send control flow to `inst`. 5012 fn jump(self: *Self, inst: Mir.Inst.Index) !void { 5013 _ = try self.addInst(.{ 5014 .tag = .b, 5015 .data = .{ .inst = inst }, 5016 }); 5017 } 5018 5019 fn airBlock(self: *Self, inst: Air.Inst.Index) !void { 5020 try self.blocks.putNoClobber(self.gpa, inst, .{ 5021 // A block is a setup to be able to jump to the end. 5022 .relocs = .{}, 5023 // It also acts as a receptacle for break operands. 5024 // Here we use `MCValue.none` to represent a null value so that the first 5025 // break instruction will choose a MCValue for the block result and overwrite 5026 // this field. Following break instructions will use that MCValue to put their 5027 // block results. 5028 .mcv = MCValue{ .none = {} }, 5029 }); 5030 defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); 5031 5032 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 5033 const extra = self.air.extraData(Air.Block, ty_pl.payload); 5034 const body = self.air.extra[extra.end..][0..extra.data.body_len]; 5035 try self.genBody(body); 5036 5037 // relocations for `br` instructions 5038 const relocs = &self.blocks.getPtr(inst).?.relocs; 5039 if (relocs.items.len > 0 and relocs.items[relocs.items.len - 1] == self.mir_instructions.len - 1) { 5040 // If the last Mir instruction is the last relocation (which 5041 // would just jump one instruction further), it can be safely 5042 // removed 5043 self.mir_instructions.orderedRemove(relocs.pop()); 5044 } 5045 for (relocs.items) |reloc| { 5046 try self.performReloc(reloc); 5047 } 5048 5049 const result = self.blocks.getPtr(inst).?.mcv; 5050 return self.finishAir(inst, result, .{ .none, .none, .none }); 5051 } 5052 5053 fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { 5054 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 5055 const condition_ty = self.air.typeOf(pl_op.operand); 5056 const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload); 5057 const liveness = try self.liveness.getSwitchBr( 5058 self.gpa, 5059 inst, 5060 switch_br.data.cases_len + 1, 5061 ); 5062 defer self.gpa.free(liveness.deaths); 5063 5064 var extra_index: usize = switch_br.end; 5065 var case_i: u32 = 0; 5066 while (case_i < switch_br.data.cases_len) : (case_i += 1) { 5067 const case = self.air.extraData(Air.SwitchBr.Case, extra_index); 5068 const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); 5069 assert(items.len > 0); 5070 const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; 5071 extra_index = case.end + items.len + case_body.len; 5072 5073 // For every item, we compare it to condition and branch into 5074 // the prong if they are equal. After we compared to all 5075 // items, we branch into the next prong (or if no other prongs 5076 // exist out of the switch statement). 5077 // 5078 // cmp condition, item1 5079 // beq prong 5080 // cmp condition, item2 5081 // beq prong 5082 // cmp condition, item3 5083 // beq prong 5084 // b out 5085 // prong: ... 5086 // ... 5087 // out: ... 5088 const branch_into_prong_relocs = try self.gpa.alloc(u32, items.len); 5089 defer self.gpa.free(branch_into_prong_relocs); 5090 5091 for (items, 0..) |item, idx| { 5092 const cmp_result = try self.cmp(.{ .inst = pl_op.operand }, .{ .inst = item }, condition_ty, .neq); 5093 branch_into_prong_relocs[idx] = try self.condBr(cmp_result); 5094 } 5095 5096 const branch_away_from_prong_reloc = try self.addInst(.{ 5097 .tag = .b, 5098 .data = .{ .inst = undefined }, // populated later through performReloc 5099 }); 5100 5101 for (branch_into_prong_relocs) |reloc| { 5102 try self.performReloc(reloc); 5103 } 5104 5105 // Capture the state of register and stack allocation state so that we can revert to it. 5106 const parent_next_stack_offset = self.next_stack_offset; 5107 const parent_free_registers = self.register_manager.free_registers; 5108 const parent_compare_flags_inst = self.compare_flags_inst; 5109 var parent_stack = try self.stack.clone(self.gpa); 5110 defer parent_stack.deinit(self.gpa); 5111 const parent_registers = self.register_manager.registers; 5112 5113 try self.branch_stack.append(.{}); 5114 errdefer { 5115 _ = self.branch_stack.pop(); 5116 } 5117 5118 try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len); 5119 for (liveness.deaths[case_i]) |operand| { 5120 self.processDeath(operand); 5121 } 5122 try self.genBody(case_body); 5123 5124 // Revert to the previous register and stack allocation state. 5125 var saved_case_branch = self.branch_stack.pop(); 5126 defer saved_case_branch.deinit(self.gpa); 5127 5128 self.register_manager.registers = parent_registers; 5129 self.compare_flags_inst = parent_compare_flags_inst; 5130 self.stack.deinit(self.gpa); 5131 self.stack = parent_stack; 5132 parent_stack = .{}; 5133 5134 self.next_stack_offset = parent_next_stack_offset; 5135 self.register_manager.free_registers = parent_free_registers; 5136 5137 try self.performReloc(branch_away_from_prong_reloc); 5138 } 5139 5140 if (switch_br.data.else_body_len > 0) { 5141 const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; 5142 5143 // Capture the state of register and stack allocation state so that we can revert to it. 5144 const parent_next_stack_offset = self.next_stack_offset; 5145 const parent_free_registers = self.register_manager.free_registers; 5146 const parent_compare_flags_inst = self.compare_flags_inst; 5147 var parent_stack = try self.stack.clone(self.gpa); 5148 defer parent_stack.deinit(self.gpa); 5149 const parent_registers = self.register_manager.registers; 5150 5151 try self.branch_stack.append(.{}); 5152 errdefer { 5153 _ = self.branch_stack.pop(); 5154 } 5155 5156 const else_deaths = liveness.deaths.len - 1; 5157 try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len); 5158 for (liveness.deaths[else_deaths]) |operand| { 5159 self.processDeath(operand); 5160 } 5161 try self.genBody(else_body); 5162 5163 // Revert to the previous register and stack allocation state. 5164 var saved_case_branch = self.branch_stack.pop(); 5165 defer saved_case_branch.deinit(self.gpa); 5166 5167 self.register_manager.registers = parent_registers; 5168 self.compare_flags_inst = parent_compare_flags_inst; 5169 self.stack.deinit(self.gpa); 5170 self.stack = parent_stack; 5171 parent_stack = .{}; 5172 5173 self.next_stack_offset = parent_next_stack_offset; 5174 self.register_manager.free_registers = parent_free_registers; 5175 5176 // TODO consolidate returned MCValues between prongs and else branch like we do 5177 // in airCondBr. 5178 } 5179 5180 return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); 5181 } 5182 5183 fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { 5184 const tag = self.mir_instructions.items(.tag)[inst]; 5185 switch (tag) { 5186 .cbz => self.mir_instructions.items(.data)[inst].r_inst.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len), 5187 .b_cond => self.mir_instructions.items(.data)[inst].inst_cond.inst = @intCast(Mir.Inst.Index, self.mir_instructions.len), 5188 .b => self.mir_instructions.items(.data)[inst].inst = @intCast(Mir.Inst.Index, self.mir_instructions.len), 5189 else => unreachable, 5190 } 5191 } 5192 5193 fn airBr(self: *Self, inst: Air.Inst.Index) !void { 5194 const branch = self.air.instructions.items(.data)[inst].br; 5195 try self.br(branch.block_inst, branch.operand); 5196 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); 5197 } 5198 5199 fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { 5200 const block_data = self.blocks.getPtr(block).?; 5201 5202 if (self.air.typeOf(operand).hasRuntimeBits()) { 5203 const operand_mcv = try self.resolveInst(operand); 5204 const block_mcv = block_data.mcv; 5205 if (block_mcv == .none) { 5206 block_data.mcv = switch (operand_mcv) { 5207 .none, .dead, .unreach => unreachable, 5208 .register, .stack_offset, .memory => operand_mcv, 5209 .immediate, .stack_argument_offset, .compare_flags => blk: { 5210 const new_mcv = try self.allocRegOrMem(self.air.typeOfIndex(block), true, block); 5211 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); 5212 break :blk new_mcv; 5213 }, 5214 else => return self.fail("TODO implement block_data.mcv = operand_mcv for {}", .{operand_mcv}), 5215 }; 5216 } else { 5217 try self.setRegOrMem(self.air.typeOfIndex(block), block_mcv, operand_mcv); 5218 } 5219 } 5220 return self.brVoid(block); 5221 } 5222 5223 fn brVoid(self: *Self, block: Air.Inst.Index) !void { 5224 const block_data = self.blocks.getPtr(block).?; 5225 5226 // Emit a jump with a relocation. It will be patched up after the block ends. 5227 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1); 5228 5229 block_data.relocs.appendAssumeCapacity(try self.addInst(.{ 5230 .tag = .b, 5231 .data = .{ .inst = undefined }, // populated later through performReloc 5232 })); 5233 } 5234 5235 fn airAsm(self: *Self, inst: Air.Inst.Index) !void { 5236 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 5237 const extra = self.air.extraData(Air.Asm, ty_pl.payload); 5238 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; 5239 const clobbers_len = @truncate(u31, extra.data.flags); 5240 var extra_i: usize = extra.end; 5241 const outputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); 5242 extra_i += outputs.len; 5243 const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); 5244 extra_i += inputs.len; 5245 5246 const dead = !is_volatile and self.liveness.isUnused(inst); 5247 const result: MCValue = if (dead) .dead else result: { 5248 if (outputs.len > 1) { 5249 return self.fail("TODO implement codegen for asm with more than 1 output", .{}); 5250 } 5251 5252 const output_constraint: ?[]const u8 = for (outputs) |output| { 5253 if (output != .none) { 5254 return self.fail("TODO implement codegen for non-expr asm", .{}); 5255 } 5256 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); 5257 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); 5258 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); 5259 // This equation accounts for the fact that even if we have exactly 4 bytes 5260 // for the string, we still use the next u32 for the null terminator. 5261 extra_i += (constraint.len + name.len + (2 + 3)) / 4; 5262 5263 break constraint; 5264 } else null; 5265 5266 for (inputs) |input| { 5267 const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); 5268 const constraint = std.mem.sliceTo(input_bytes, 0); 5269 const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); 5270 // This equation accounts for the fact that even if we have exactly 4 bytes 5271 // for the string, we still use the next u32 for the null terminator. 5272 extra_i += (constraint.len + name.len + (2 + 3)) / 4; 5273 5274 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { 5275 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); 5276 } 5277 const reg_name = constraint[1 .. constraint.len - 1]; 5278 const reg = parseRegName(reg_name) orelse 5279 return self.fail("unrecognized register: '{s}'", .{reg_name}); 5280 5281 const arg_mcv = try self.resolveInst(input); 5282 try self.register_manager.getReg(reg, null); 5283 try self.genSetReg(self.air.typeOf(input), reg, arg_mcv); 5284 } 5285 5286 { 5287 var clobber_i: u32 = 0; 5288 while (clobber_i < clobbers_len) : (clobber_i += 1) { 5289 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); 5290 // This equation accounts for the fact that even if we have exactly 4 bytes 5291 // for the string, we still use the next u32 for the null terminator. 5292 extra_i += clobber.len / 4 + 1; 5293 5294 // TODO honor these 5295 } 5296 } 5297 5298 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; 5299 5300 if (mem.eql(u8, asm_source, "svc #0")) { 5301 _ = try self.addInst(.{ 5302 .tag = .svc, 5303 .data = .{ .imm16 = 0x0 }, 5304 }); 5305 } else if (mem.eql(u8, asm_source, "svc #0x80")) { 5306 _ = try self.addInst(.{ 5307 .tag = .svc, 5308 .data = .{ .imm16 = 0x80 }, 5309 }); 5310 } else { 5311 return self.fail("TODO implement support for more aarch64 assembly instructions", .{}); 5312 } 5313 5314 if (output_constraint) |output| { 5315 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { 5316 return self.fail("unrecognized asm output constraint: '{s}'", .{output}); 5317 } 5318 const reg_name = output[2 .. output.len - 1]; 5319 const reg = parseRegName(reg_name) orelse 5320 return self.fail("unrecognized register: '{s}'", .{reg_name}); 5321 break :result MCValue{ .register = reg }; 5322 } else { 5323 break :result MCValue{ .none = {} }; 5324 } 5325 }; 5326 5327 simple: { 5328 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); 5329 var buf_index: usize = 0; 5330 for (outputs) |output| { 5331 if (output == .none) continue; 5332 5333 if (buf_index >= buf.len) break :simple; 5334 buf[buf_index] = output; 5335 buf_index += 1; 5336 } 5337 if (buf_index + inputs.len > buf.len) break :simple; 5338 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); 5339 return self.finishAir(inst, result, buf); 5340 } 5341 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); 5342 for (outputs) |output| { 5343 if (output == .none) continue; 5344 5345 bt.feed(output); 5346 } 5347 for (inputs) |input| { 5348 bt.feed(input); 5349 } 5350 return bt.finishAir(result); 5351 } 5352 5353 fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb { 5354 try self.ensureProcessDeathCapacity(operand_count + 1); 5355 return BigTomb{ 5356 .function = self, 5357 .inst = inst, 5358 .lbt = self.liveness.iterateBigTomb(inst), 5359 }; 5360 } 5361 5362 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata. 5363 fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { 5364 switch (loc) { 5365 .none => return, 5366 .register => |reg| return self.genSetReg(ty, reg, val), 5367 .stack_offset => |off| return self.genSetStack(ty, off, val), 5368 .memory => { 5369 return self.fail("TODO implement setRegOrMem for memory", .{}); 5370 }, 5371 else => unreachable, 5372 } 5373 } 5374 5375 fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { 5376 const abi_size = @intCast(u32, ty.abiSize(self.target.*)); 5377 switch (mcv) { 5378 .dead => unreachable, 5379 .unreach, .none => return, // Nothing to do. 5380 .undef => { 5381 if (!self.wantSafety()) 5382 return; // The already existing value will do just fine. 5383 // TODO Upgrade this to a memset call when we have that available. 5384 switch (abi_size) { 5385 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }), 5386 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }), 5387 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), 5388 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), 5389 else => try self.genInlineMemset( 5390 .{ .ptr_stack_offset = stack_offset }, 5391 .{ .immediate = 0xaa }, 5392 .{ .immediate = abi_size }, 5393 ), 5394 } 5395 }, 5396 .compare_flags, 5397 .immediate, 5398 .ptr_stack_offset, 5399 => { 5400 const reg = try self.copyToTmpRegister(ty, mcv); 5401 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); 5402 }, 5403 .register => |reg| { 5404 switch (abi_size) { 5405 1, 2, 4, 8 => { 5406 assert(std.mem.isAlignedGeneric(u32, stack_offset, abi_size)); 5407 5408 const tag: Mir.Inst.Tag = switch (abi_size) { 5409 1 => .strb_stack, 5410 2 => .strh_stack, 5411 4, 8 => .str_stack, 5412 else => unreachable, // unexpected abi size 5413 }; 5414 const rt = self.registerAlias(reg, ty); 5415 5416 _ = try self.addInst(.{ 5417 .tag = tag, 5418 .data = .{ .load_store_stack = .{ 5419 .rt = rt, 5420 .offset = stack_offset, 5421 } }, 5422 }); 5423 }, 5424 else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), 5425 } 5426 }, 5427 .register_with_overflow => |rwo| { 5428 const reg_lock = self.register_manager.lockReg(rwo.reg); 5429 defer if (reg_lock) |locked_reg| self.register_manager.unlockReg(locked_reg); 5430 5431 const wrapped_ty = ty.structFieldType(0); 5432 try self.genSetStack(wrapped_ty, stack_offset, .{ .register = rwo.reg }); 5433 5434 const overflow_bit_ty = ty.structFieldType(1); 5435 const overflow_bit_offset = @intCast(u32, ty.structFieldOffset(1, self.target.*)); 5436 const raw_cond_reg = try self.register_manager.allocReg(null, gp); 5437 const cond_reg = self.registerAlias(raw_cond_reg, overflow_bit_ty); 5438 5439 _ = try self.addInst(.{ 5440 .tag = .cset, 5441 .data = .{ .r_cond = .{ 5442 .rd = cond_reg, 5443 .cond = rwo.flag, 5444 } }, 5445 }); 5446 5447 try self.genSetStack(overflow_bit_ty, stack_offset - overflow_bit_offset, .{ 5448 .register = cond_reg, 5449 }); 5450 }, 5451 .linker_load, 5452 .memory, 5453 .stack_argument_offset, 5454 .stack_offset, 5455 => { 5456 switch (mcv) { 5457 .stack_offset => |off| { 5458 if (stack_offset == off) 5459 return; // Copy stack variable to itself; nothing to do. 5460 }, 5461 else => {}, 5462 } 5463 5464 if (abi_size <= 8) { 5465 const reg = try self.copyToTmpRegister(ty, mcv); 5466 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); 5467 } else { 5468 var ptr_ty_payload: Type.Payload.ElemType = .{ 5469 .base = .{ .tag = .single_mut_pointer }, 5470 .data = ty, 5471 }; 5472 const ptr_ty = Type.initPayload(&ptr_ty_payload.base); 5473 5474 // TODO call extern memcpy 5475 const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp); 5476 const regs_locks = self.register_manager.lockRegsAssumeUnused(5, regs); 5477 defer for (regs_locks) |reg| { 5478 self.register_manager.unlockReg(reg); 5479 }; 5480 5481 const src_reg = regs[0]; 5482 const dst_reg = regs[1]; 5483 const len_reg = regs[2]; 5484 const count_reg = regs[3]; 5485 const tmp_reg = regs[4]; 5486 5487 switch (mcv) { 5488 .stack_offset => |off| { 5489 // sub src_reg, fp, #off 5490 try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); 5491 }, 5492 .stack_argument_offset => |off| { 5493 _ = try self.addInst(.{ 5494 .tag = .ldr_ptr_stack_argument, 5495 .data = .{ .load_store_stack = .{ 5496 .rt = src_reg, 5497 .offset = off, 5498 } }, 5499 }); 5500 }, 5501 .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = addr }), 5502 .linker_load => |load_struct| { 5503 const tag: Mir.Inst.Tag = switch (load_struct.type) { 5504 .got => .load_memory_ptr_got, 5505 .direct => .load_memory_ptr_direct, 5506 .import => unreachable, 5507 }; 5508 const atom_index = switch (self.bin_file.tag) { 5509 .macho => blk: { 5510 const macho_file = self.bin_file.cast(link.File.MachO).?; 5511 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); 5512 break :blk macho_file.getAtom(atom).getSymbolIndex().?; 5513 }, 5514 .coff => blk: { 5515 const coff_file = self.bin_file.cast(link.File.Coff).?; 5516 const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); 5517 break :blk coff_file.getAtom(atom).getSymbolIndex().?; 5518 }, 5519 else => unreachable, // unsupported target format 5520 }; 5521 _ = try self.addInst(.{ 5522 .tag = tag, 5523 .data = .{ 5524 .payload = try self.addExtra(Mir.LoadMemoryPie{ 5525 .register = @enumToInt(src_reg), 5526 .atom_index = atom_index, 5527 .sym_index = load_struct.sym_index, 5528 }), 5529 }, 5530 }); 5531 }, 5532 else => unreachable, 5533 } 5534 5535 // sub dst_reg, fp, #stack_offset 5536 try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset }); 5537 5538 // mov len, #abi_size 5539 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size }); 5540 5541 // memcpy(src, dst, len) 5542 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); 5543 } 5544 }, 5545 } 5546 } 5547 5548 fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { 5549 switch (mcv) { 5550 .dead => unreachable, 5551 .unreach, .none => return, // Nothing to do. 5552 .undef => { 5553 if (!self.wantSafety()) 5554 return; // The already existing value will do just fine. 5555 // Write the debug undefined value. 5556 switch (reg.size()) { 5557 32 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaa }), 5558 64 => return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), 5559 else => unreachable, // unexpected register size 5560 } 5561 }, 5562 .ptr_stack_offset => |off| { 5563 _ = try self.addInst(.{ 5564 .tag = .ldr_ptr_stack, 5565 .data = .{ .load_store_stack = .{ 5566 .rt = reg, 5567 .offset = @intCast(u32, off), 5568 } }, 5569 }); 5570 }, 5571 .compare_flags => |condition| { 5572 _ = try self.addInst(.{ 5573 .tag = .cset, 5574 .data = .{ .r_cond = .{ 5575 .rd = reg, 5576 .cond = condition, 5577 } }, 5578 }); 5579 }, 5580 .immediate => |x| { 5581 _ = try self.addInst(.{ 5582 .tag = .movz, 5583 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x) } }, 5584 }); 5585 5586 if (x & 0x0000_0000_ffff_0000 != 0) { 5587 _ = try self.addInst(.{ 5588 .tag = .movk, 5589 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 16), .hw = 1 } }, 5590 }); 5591 } 5592 5593 if (reg.size() == 64) { 5594 if (x & 0x0000_ffff_0000_0000 != 0) { 5595 _ = try self.addInst(.{ 5596 .tag = .movk, 5597 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 32), .hw = 2 } }, 5598 }); 5599 } 5600 if (x & 0xffff_0000_0000_0000 != 0) { 5601 _ = try self.addInst(.{ 5602 .tag = .movk, 5603 .data = .{ .r_imm16_sh = .{ .rd = reg, .imm16 = @truncate(u16, x >> 48), .hw = 3 } }, 5604 }); 5605 } 5606 } 5607 }, 5608 .register => |src_reg| { 5609 assert(src_reg.size() == reg.size()); 5610 5611 // If the registers are the same, nothing to do. 5612 if (src_reg.id() == reg.id()) 5613 return; 5614 5615 // mov reg, src_reg 5616 _ = try self.addInst(.{ 5617 .tag = .mov_register, 5618 .data = .{ .rr = .{ .rd = reg, .rn = src_reg } }, 5619 }); 5620 }, 5621 .register_with_overflow => unreachable, // doesn't fit into a register 5622 .linker_load => |load_struct| { 5623 const tag: Mir.Inst.Tag = switch (load_struct.type) { 5624 .got => .load_memory_got, 5625 .direct => .load_memory_direct, 5626 .import => .load_memory_import, 5627 }; 5628 const atom_index = switch (self.bin_file.tag) { 5629 .macho => blk: { 5630 const macho_file = self.bin_file.cast(link.File.MachO).?; 5631 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); 5632 break :blk macho_file.getAtom(atom).getSymbolIndex().?; 5633 }, 5634 .coff => blk: { 5635 const coff_file = self.bin_file.cast(link.File.Coff).?; 5636 const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); 5637 break :blk coff_file.getAtom(atom).getSymbolIndex().?; 5638 }, 5639 else => unreachable, // unsupported target format 5640 }; 5641 _ = try self.addInst(.{ 5642 .tag = tag, 5643 .data = .{ 5644 .payload = try self.addExtra(Mir.LoadMemoryPie{ 5645 .register = @enumToInt(reg), 5646 .atom_index = atom_index, 5647 .sym_index = load_struct.sym_index, 5648 }), 5649 }, 5650 }); 5651 }, 5652 .memory => |addr| { 5653 // The value is in memory at a hard-coded address. 5654 // If the type is a pointer, it means the pointer address is at this memory location. 5655 try self.genSetReg(ty, reg.toX(), .{ .immediate = addr }); 5656 try self.genLdrRegister(reg, reg.toX(), ty); 5657 }, 5658 .stack_offset => |off| { 5659 const abi_size = ty.abiSize(self.target.*); 5660 5661 switch (abi_size) { 5662 1, 2, 4, 8 => { 5663 const tag: Mir.Inst.Tag = switch (abi_size) { 5664 1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb_stack else .ldrb_stack, 5665 2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh_stack else .ldrh_stack, 5666 4, 8 => .ldr_stack, 5667 else => unreachable, // unexpected abi size 5668 }; 5669 5670 _ = try self.addInst(.{ 5671 .tag = tag, 5672 .data = .{ .load_store_stack = .{ 5673 .rt = reg, 5674 .offset = @intCast(u32, off), 5675 } }, 5676 }); 5677 }, 5678 3, 5, 6, 7 => return self.fail("TODO implement genSetReg types size {}", .{abi_size}), 5679 else => unreachable, 5680 } 5681 }, 5682 .stack_argument_offset => |off| { 5683 const abi_size = ty.abiSize(self.target.*); 5684 5685 switch (abi_size) { 5686 1, 2, 4, 8 => { 5687 const tag: Mir.Inst.Tag = switch (abi_size) { 5688 1 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsb_stack_argument else .ldrb_stack_argument, 5689 2 => if (ty.isSignedInt()) Mir.Inst.Tag.ldrsh_stack_argument else .ldrh_stack_argument, 5690 4, 8 => .ldr_stack_argument, 5691 else => unreachable, // unexpected abi size 5692 }; 5693 5694 _ = try self.addInst(.{ 5695 .tag = tag, 5696 .data = .{ .load_store_stack = .{ 5697 .rt = reg, 5698 .offset = @intCast(u32, off), 5699 } }, 5700 }); 5701 }, 5702 3, 5, 6, 7 => return self.fail("TODO implement genSetReg types size {}", .{abi_size}), 5703 else => unreachable, 5704 } 5705 }, 5706 } 5707 } 5708 5709 fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { 5710 const abi_size = @intCast(u32, ty.abiSize(self.target.*)); 5711 switch (mcv) { 5712 .dead => unreachable, 5713 .none, .unreach => return, 5714 .undef => { 5715 if (!self.wantSafety()) 5716 return; // The already existing value will do just fine. 5717 // TODO Upgrade this to a memset call when we have that available. 5718 switch (ty.abiSize(self.target.*)) { 5719 1 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaa }), 5720 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }), 5721 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), 5722 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), 5723 else => return self.fail("TODO implement memset", .{}), 5724 } 5725 }, 5726 .register => |reg| { 5727 switch (abi_size) { 5728 1, 2, 4, 8 => { 5729 const tag: Mir.Inst.Tag = switch (abi_size) { 5730 1 => .strb_immediate, 5731 2 => .strh_immediate, 5732 4, 8 => .str_immediate, 5733 else => unreachable, // unexpected abi size 5734 }; 5735 const rt = self.registerAlias(reg, ty); 5736 const offset = switch (abi_size) { 5737 1 => blk: { 5738 if (math.cast(u12, stack_offset)) |imm| { 5739 break :blk Instruction.LoadStoreOffset.imm(imm); 5740 } else { 5741 return self.fail("TODO genSetStackArgument byte with larger offset", .{}); 5742 } 5743 }, 5744 2 => blk: { 5745 assert(std.mem.isAlignedGeneric(u32, stack_offset, 2)); // misaligned stack entry 5746 if (math.cast(u12, @divExact(stack_offset, 2))) |imm| { 5747 break :blk Instruction.LoadStoreOffset.imm(imm); 5748 } else { 5749 return self.fail("TODO getSetStackArgument halfword with larger offset", .{}); 5750 } 5751 }, 5752 4, 8 => blk: { 5753 const alignment = abi_size; 5754 assert(std.mem.isAlignedGeneric(u32, stack_offset, alignment)); // misaligned stack entry 5755 if (math.cast(u12, @divExact(stack_offset, alignment))) |imm| { 5756 break :blk Instruction.LoadStoreOffset.imm(imm); 5757 } else { 5758 return self.fail("TODO genSetStackArgument with larger offset", .{}); 5759 } 5760 }, 5761 else => unreachable, 5762 }; 5763 5764 _ = try self.addInst(.{ 5765 .tag = tag, 5766 .data = .{ .load_store_register_immediate = .{ 5767 .rt = rt, 5768 .rn = .sp, 5769 .offset = offset.immediate, 5770 } }, 5771 }); 5772 }, 5773 else => return self.fail("TODO genSetStackArgument other types abi_size={}", .{abi_size}), 5774 } 5775 }, 5776 .register_with_overflow => { 5777 return self.fail("TODO implement genSetStackArgument {}", .{mcv}); 5778 }, 5779 .linker_load, 5780 .memory, 5781 .stack_argument_offset, 5782 .stack_offset, 5783 => { 5784 if (abi_size <= 4) { 5785 const reg = try self.copyToTmpRegister(ty, mcv); 5786 return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg }); 5787 } else { 5788 var ptr_ty_payload: Type.Payload.ElemType = .{ 5789 .base = .{ .tag = .single_mut_pointer }, 5790 .data = ty, 5791 }; 5792 const ptr_ty = Type.initPayload(&ptr_ty_payload.base); 5793 5794 // TODO call extern memcpy 5795 const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp); 5796 const regs_locks = self.register_manager.lockRegsAssumeUnused(5, regs); 5797 defer for (regs_locks) |reg| { 5798 self.register_manager.unlockReg(reg); 5799 }; 5800 5801 const src_reg = regs[0]; 5802 const dst_reg = regs[1]; 5803 const len_reg = regs[2]; 5804 const count_reg = regs[3]; 5805 const tmp_reg = regs[4]; 5806 5807 switch (mcv) { 5808 .stack_offset => |off| { 5809 // sub src_reg, fp, #off 5810 try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); 5811 }, 5812 .stack_argument_offset => |off| { 5813 _ = try self.addInst(.{ 5814 .tag = .ldr_ptr_stack_argument, 5815 .data = .{ .load_store_stack = .{ 5816 .rt = src_reg, 5817 .offset = off, 5818 } }, 5819 }); 5820 }, 5821 .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(u32, addr) }), 5822 .linker_load => |load_struct| { 5823 const tag: Mir.Inst.Tag = switch (load_struct.type) { 5824 .got => .load_memory_ptr_got, 5825 .direct => .load_memory_ptr_direct, 5826 .import => unreachable, 5827 }; 5828 const atom_index = switch (self.bin_file.tag) { 5829 .macho => blk: { 5830 const macho_file = self.bin_file.cast(link.File.MachO).?; 5831 const atom = try macho_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); 5832 break :blk macho_file.getAtom(atom).getSymbolIndex().?; 5833 }, 5834 .coff => blk: { 5835 const coff_file = self.bin_file.cast(link.File.Coff).?; 5836 const atom = try coff_file.getOrCreateAtomForDecl(self.mod_fn.owner_decl); 5837 break :blk coff_file.getAtom(atom).getSymbolIndex().?; 5838 }, 5839 else => unreachable, // unsupported target format 5840 }; 5841 _ = try self.addInst(.{ 5842 .tag = tag, 5843 .data = .{ 5844 .payload = try self.addExtra(Mir.LoadMemoryPie{ 5845 .register = @enumToInt(src_reg), 5846 .atom_index = atom_index, 5847 .sym_index = load_struct.sym_index, 5848 }), 5849 }, 5850 }); 5851 }, 5852 else => unreachable, 5853 } 5854 5855 // add dst_reg, sp, #stack_offset 5856 _ = try self.addInst(.{ 5857 .tag = .add_immediate, 5858 .data = .{ .rr_imm12_sh = .{ 5859 .rd = dst_reg, 5860 .rn = .sp, 5861 .imm12 = math.cast(u12, stack_offset) orelse { 5862 return self.fail("TODO load: set reg to stack offset with all possible offsets", .{}); 5863 }, 5864 } }, 5865 }); 5866 5867 // mov len, #abi_size 5868 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size }); 5869 5870 // memcpy(src, dst, len) 5871 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); 5872 } 5873 }, 5874 .compare_flags, 5875 .immediate, 5876 .ptr_stack_offset, 5877 => { 5878 const reg = try self.copyToTmpRegister(ty, mcv); 5879 return self.genSetStackArgument(ty, stack_offset, MCValue{ .register = reg }); 5880 }, 5881 } 5882 } 5883 5884 fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { 5885 const un_op = self.air.instructions.items(.data)[inst].un_op; 5886 const result = try self.resolveInst(un_op); 5887 return self.finishAir(inst, result, .{ un_op, .none, .none }); 5888 } 5889 5890 fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { 5891 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 5892 const result = if (self.liveness.isUnused(inst)) .dead else result: { 5893 const operand = try self.resolveInst(ty_op.operand); 5894 if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand; 5895 5896 const operand_lock = switch (operand) { 5897 .register => |reg| self.register_manager.lockReg(reg), 5898 .register_with_overflow => |rwo| self.register_manager.lockReg(rwo.reg), 5899 else => null, 5900 }; 5901 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); 5902 5903 const dest_ty = self.air.typeOfIndex(inst); 5904 const dest = try self.allocRegOrMem(dest_ty, true, inst); 5905 try self.setRegOrMem(dest_ty, dest, operand); 5906 break :result dest; 5907 }; 5908 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 5909 } 5910 5911 fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { 5912 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 5913 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 5914 const ptr_ty = self.air.typeOf(ty_op.operand); 5915 const ptr = try self.resolveInst(ty_op.operand); 5916 const array_ty = ptr_ty.childType(); 5917 const array_len = @intCast(u32, array_ty.arrayLen()); 5918 5919 const ptr_bits = self.target.cpu.arch.ptrBitWidth(); 5920 const ptr_bytes = @divExact(ptr_bits, 8); 5921 5922 const stack_offset = try self.allocMem(ptr_bytes * 2, ptr_bytes * 2, inst); 5923 try self.genSetStack(ptr_ty, stack_offset, ptr); 5924 try self.genSetStack(Type.initTag(.usize), stack_offset - ptr_bytes, .{ .immediate = array_len }); 5925 break :result MCValue{ .stack_offset = stack_offset }; 5926 }; 5927 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 5928 } 5929 5930 fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void { 5931 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 5932 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntToFloat for {}", .{ 5933 self.target.cpu.arch, 5934 }); 5935 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 5936 } 5937 5938 fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { 5939 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 5940 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatToInt for {}", .{ 5941 self.target.cpu.arch, 5942 }); 5943 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 5944 } 5945 5946 fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { 5947 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 5948 const extra = self.air.extraData(Air.Block, ty_pl.payload); 5949 _ = extra; 5950 5951 return self.fail("TODO implement airCmpxchg for {}", .{ 5952 self.target.cpu.arch, 5953 }); 5954 } 5955 5956 fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void { 5957 _ = inst; 5958 return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch}); 5959 } 5960 5961 fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void { 5962 _ = inst; 5963 return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch}); 5964 } 5965 5966 fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void { 5967 _ = inst; 5968 _ = order; 5969 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); 5970 } 5971 5972 fn airMemset(self: *Self, inst: Air.Inst.Index) !void { 5973 _ = inst; 5974 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); 5975 } 5976 5977 fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { 5978 _ = inst; 5979 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); 5980 } 5981 5982 fn airTagName(self: *Self, inst: Air.Inst.Index) !void { 5983 const un_op = self.air.instructions.items(.data)[inst].un_op; 5984 const operand = try self.resolveInst(un_op); 5985 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { 5986 _ = operand; 5987 return self.fail("TODO implement airTagName for aarch64", .{}); 5988 }; 5989 return self.finishAir(inst, result, .{ un_op, .none, .none }); 5990 } 5991 5992 fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { 5993 const un_op = self.air.instructions.items(.data)[inst].un_op; 5994 const operand = try self.resolveInst(un_op); 5995 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { 5996 _ = operand; 5997 return self.fail("TODO implement airErrorName for aarch64", .{}); 5998 }; 5999 return self.finishAir(inst, result, .{ un_op, .none, .none }); 6000 } 6001 6002 fn airSplat(self: *Self, inst: Air.Inst.Index) !void { 6003 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 6004 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for {}", .{self.target.cpu.arch}); 6005 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 6006 } 6007 6008 fn airSelect(self: *Self, inst: Air.Inst.Index) !void { 6009 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 6010 const extra = self.air.extraData(Air.Bin, pl_op.payload).data; 6011 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for {}", .{self.target.cpu.arch}); 6012 return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs }); 6013 } 6014 6015 fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { 6016 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 6017 const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data; 6018 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for {}", .{self.target.cpu.arch}); 6019 return self.finishAir(inst, result, .{ extra.a, extra.b, .none }); 6020 } 6021 6022 fn airReduce(self: *Self, inst: Air.Inst.Index) !void { 6023 const reduce = self.air.instructions.items(.data)[inst].reduce; 6024 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for aarch64", .{}); 6025 return self.finishAir(inst, result, .{ reduce.operand, .none, .none }); 6026 } 6027 6028 fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { 6029 const vector_ty = self.air.typeOfIndex(inst); 6030 const len = vector_ty.vectorLen(); 6031 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 6032 const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); 6033 const result: MCValue = res: { 6034 if (self.liveness.isUnused(inst)) break :res MCValue.dead; 6035 return self.fail("TODO implement airAggregateInit for {}", .{self.target.cpu.arch}); 6036 }; 6037 6038 if (elements.len <= Liveness.bpi - 1) { 6039 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); 6040 std.mem.copy(Air.Inst.Ref, &buf, elements); 6041 return self.finishAir(inst, result, buf); 6042 } 6043 var bt = try self.iterateBigTomb(inst, elements.len); 6044 for (elements) |elem| { 6045 bt.feed(elem); 6046 } 6047 return bt.finishAir(result); 6048 } 6049 6050 fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void { 6051 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 6052 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; 6053 _ = extra; 6054 return self.fail("TODO implement airUnionInit for aarch64", .{}); 6055 } 6056 6057 fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void { 6058 const prefetch = self.air.instructions.items(.data)[inst].prefetch; 6059 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none }); 6060 } 6061 6062 fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { 6063 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 6064 const extra = self.air.extraData(Air.Bin, pl_op.payload).data; 6065 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { 6066 return self.fail("TODO implement airMulAdd for aarch64", .{}); 6067 }; 6068 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); 6069 } 6070 6071 fn airTry(self: *Self, inst: Air.Inst.Index) !void { 6072 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 6073 const extra = self.air.extraData(Air.Try, pl_op.payload); 6074 const body = self.air.extra[extra.end..][0..extra.data.body_len]; 6075 const result: MCValue = result: { 6076 const error_union_bind: ReadArg.Bind = .{ .inst = pl_op.operand }; 6077 const error_union_ty = self.air.typeOf(pl_op.operand); 6078 const error_union_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); 6079 const error_union_align = error_union_ty.abiAlignment(self.target.*); 6080 6081 // The error union will die in the body. However, we need the 6082 // error union after the body in order to extract the payload 6083 // of the error union, so we create a copy of it 6084 const error_union_copy = try self.allocMem(error_union_size, error_union_align, null); 6085 try self.genSetStack(error_union_ty, error_union_copy, try error_union_bind.resolveToMcv(self)); 6086 6087 const is_err_result = try self.isErr(error_union_bind, error_union_ty); 6088 const reloc = try self.condBr(is_err_result); 6089 6090 try self.genBody(body); 6091 try self.performReloc(reloc); 6092 6093 break :result try self.errUnionPayload(.{ .mcv = .{ .stack_offset = error_union_copy } }, error_union_ty, null); 6094 }; 6095 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); 6096 } 6097 6098 fn airTryPtr(self: *Self, inst: Air.Inst.Index) !void { 6099 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 6100 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); 6101 const body = self.air.extra[extra.end..][0..extra.data.body_len]; 6102 _ = body; 6103 return self.fail("TODO implement airTryPtr for arm", .{}); 6104 // return self.finishAir(inst, result, .{ extra.data.ptr, .none, .none }); 6105 } 6106 6107 fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { 6108 // First section of indexes correspond to a set number of constant values. 6109 const ref_int = @enumToInt(inst); 6110 if (ref_int < Air.Inst.Ref.typed_value_map.len) { 6111 const tv = Air.Inst.Ref.typed_value_map[ref_int]; 6112 if (!tv.ty.hasRuntimeBitsIgnoreComptime() and !tv.ty.isError()) { 6113 return MCValue{ .none = {} }; 6114 } 6115 return self.genTypedValue(tv); 6116 } 6117 6118 // If the type has no codegen bits, no need to store it. 6119 const inst_ty = self.air.typeOf(inst); 6120 if (!inst_ty.hasRuntimeBitsIgnoreComptime() and !inst_ty.isError()) 6121 return MCValue{ .none = {} }; 6122 6123 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len); 6124 switch (self.air.instructions.items(.tag)[inst_index]) { 6125 .constant => { 6126 // Constants have static lifetimes, so they are always memoized in the outer most table. 6127 const branch = &self.branch_stack.items[0]; 6128 const gop = try branch.inst_table.getOrPut(self.gpa, inst_index); 6129 if (!gop.found_existing) { 6130 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl; 6131 gop.value_ptr.* = try self.genTypedValue(.{ 6132 .ty = inst_ty, 6133 .val = self.air.values[ty_pl.payload], 6134 }); 6135 } 6136 return gop.value_ptr.*; 6137 }, 6138 .const_ty => unreachable, 6139 else => return self.getResolvedInstValue(inst_index), 6140 } 6141 } 6142 6143 fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { 6144 // Treat each stack item as a "layer" on top of the previous one. 6145 var i: usize = self.branch_stack.items.len; 6146 while (true) { 6147 i -= 1; 6148 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| { 6149 assert(mcv != .dead); 6150 return mcv; 6151 } 6152 } 6153 } 6154 6155 fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue { 6156 const mcv: MCValue = switch (try codegen.genTypedValue( 6157 self.bin_file, 6158 self.src_loc, 6159 arg_tv, 6160 self.mod_fn.owner_decl, 6161 )) { 6162 .mcv => |mcv| switch (mcv) { 6163 .none => .none, 6164 .undef => .undef, 6165 .linker_load => |ll| .{ .linker_load = ll }, 6166 .immediate => |imm| .{ .immediate = imm }, 6167 .memory => |addr| .{ .memory = addr }, 6168 }, 6169 .fail => |msg| { 6170 self.err_msg = msg; 6171 return error.CodegenFail; 6172 }, 6173 }; 6174 return mcv; 6175 } 6176 6177 const CallMCValues = struct { 6178 args: []MCValue, 6179 return_value: MCValue, 6180 stack_byte_count: u32, 6181 stack_align: u32, 6182 6183 fn deinit(self: *CallMCValues, func: *Self) void { 6184 func.gpa.free(self.args); 6185 self.* = undefined; 6186 } 6187 }; 6188 6189 /// Caller must call `CallMCValues.deinit`. 6190 fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { 6191 const cc = fn_ty.fnCallingConvention(); 6192 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen()); 6193 defer self.gpa.free(param_types); 6194 fn_ty.fnParamTypes(param_types); 6195 var result: CallMCValues = .{ 6196 .args = try self.gpa.alloc(MCValue, param_types.len), 6197 // These undefined values must be populated before returning from this function. 6198 .return_value = undefined, 6199 .stack_byte_count = undefined, 6200 .stack_align = undefined, 6201 }; 6202 errdefer self.gpa.free(result.args); 6203 6204 const ret_ty = fn_ty.fnReturnType(); 6205 6206 switch (cc) { 6207 .Naked => { 6208 assert(result.args.len == 0); 6209 result.return_value = .{ .unreach = {} }; 6210 result.stack_byte_count = 0; 6211 result.stack_align = 1; 6212 return result; 6213 }, 6214 .C => { 6215 // ARM64 Procedure Call Standard 6216 var ncrn: usize = 0; // Next Core Register Number 6217 var nsaa: u32 = 0; // Next stacked argument address 6218 6219 if (ret_ty.zigTypeTag() == .NoReturn) { 6220 result.return_value = .{ .unreach = {} }; 6221 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { 6222 result.return_value = .{ .none = {} }; 6223 } else { 6224 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); 6225 if (ret_ty_size == 0) { 6226 assert(ret_ty.isError()); 6227 result.return_value = .{ .immediate = 0 }; 6228 } else if (ret_ty_size <= 8) { 6229 result.return_value = .{ .register = self.registerAlias(c_abi_int_return_regs[0], ret_ty) }; 6230 } else { 6231 return self.fail("TODO support more return types for ARM backend", .{}); 6232 } 6233 } 6234 6235 for (param_types, 0..) |ty, i| { 6236 const param_size = @intCast(u32, ty.abiSize(self.target.*)); 6237 if (param_size == 0) { 6238 result.args[i] = .{ .none = {} }; 6239 continue; 6240 } 6241 6242 // We round up NCRN only for non-Apple platforms which allow the 16-byte aligned 6243 // values to spread across odd-numbered registers. 6244 if (ty.abiAlignment(self.target.*) == 16 and !self.target.isDarwin()) { 6245 // Round up NCRN to the next even number 6246 ncrn += ncrn % 2; 6247 } 6248 6249 if (std.math.divCeil(u32, param_size, 8) catch unreachable <= 8 - ncrn) { 6250 if (param_size <= 8) { 6251 result.args[i] = .{ .register = self.registerAlias(c_abi_int_param_regs[ncrn], ty) }; 6252 ncrn += 1; 6253 } else { 6254 return self.fail("TODO MCValues with multiple registers", .{}); 6255 } 6256 } else if (ncrn < 8 and nsaa == 0) { 6257 return self.fail("TODO MCValues split between registers and stack", .{}); 6258 } else { 6259 ncrn = 8; 6260 // TODO Apple allows the arguments on the stack to be non-8-byte aligned provided 6261 // that the entire stack space consumed by the arguments is 8-byte aligned. 6262 if (ty.abiAlignment(self.target.*) == 8) { 6263 if (nsaa % 8 != 0) { 6264 nsaa += 8 - (nsaa % 8); 6265 } 6266 } 6267 6268 result.args[i] = .{ .stack_argument_offset = nsaa }; 6269 nsaa += param_size; 6270 } 6271 } 6272 6273 result.stack_byte_count = nsaa; 6274 result.stack_align = 16; 6275 }, 6276 .Unspecified => { 6277 if (ret_ty.zigTypeTag() == .NoReturn) { 6278 result.return_value = .{ .unreach = {} }; 6279 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime() and !ret_ty.isError()) { 6280 result.return_value = .{ .none = {} }; 6281 } else { 6282 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); 6283 if (ret_ty_size == 0) { 6284 assert(ret_ty.isError()); 6285 result.return_value = .{ .immediate = 0 }; 6286 } else if (ret_ty_size <= 8) { 6287 result.return_value = .{ .register = self.registerAlias(.x0, ret_ty) }; 6288 } else { 6289 // The result is returned by reference, not by 6290 // value. This means that x0 (or w0 when pointer 6291 // size is 32 bits) will contain the address of 6292 // where this function should write the result 6293 // into. 6294 result.return_value = .{ .stack_offset = 0 }; 6295 } 6296 } 6297 6298 var stack_offset: u32 = 0; 6299 6300 for (param_types, 0..) |ty, i| { 6301 if (ty.abiSize(self.target.*) > 0) { 6302 const param_size = @intCast(u32, ty.abiSize(self.target.*)); 6303 const param_alignment = ty.abiAlignment(self.target.*); 6304 6305 stack_offset = std.mem.alignForwardGeneric(u32, stack_offset, param_alignment); 6306 result.args[i] = .{ .stack_argument_offset = stack_offset }; 6307 stack_offset += param_size; 6308 } else { 6309 result.args[i] = .{ .none = {} }; 6310 } 6311 } 6312 6313 result.stack_byte_count = stack_offset; 6314 result.stack_align = 16; 6315 }, 6316 else => return self.fail("TODO implement function parameters for {} on aarch64", .{cc}), 6317 } 6318 6319 return result; 6320 } 6321 6322 /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`. 6323 fn wantSafety(self: *Self) bool { 6324 return switch (self.bin_file.options.optimize_mode) { 6325 .Debug => true, 6326 .ReleaseSafe => true, 6327 .ReleaseFast => false, 6328 .ReleaseSmall => false, 6329 }; 6330 } 6331 6332 fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { 6333 @setCold(true); 6334 assert(self.err_msg == null); 6335 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); 6336 return error.CodegenFail; 6337 } 6338 6339 fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError { 6340 @setCold(true); 6341 assert(self.err_msg == null); 6342 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); 6343 return error.CodegenFail; 6344 } 6345 6346 fn parseRegName(name: []const u8) ?Register { 6347 if (@hasDecl(Register, "parseRegName")) { 6348 return Register.parseRegName(name); 6349 } 6350 return std.meta.stringToEnum(Register, name); 6351 } 6352 6353 fn registerAlias(self: *Self, reg: Register, ty: Type) Register { 6354 const abi_size = ty.abiSize(self.target.*); 6355 6356 switch (reg.class()) { 6357 .general_purpose => { 6358 if (abi_size == 0) { 6359 unreachable; // should be comptime-known 6360 } else if (abi_size <= 4) { 6361 return reg.toW(); 6362 } else if (abi_size <= 8) { 6363 return reg.toX(); 6364 } else unreachable; 6365 }, 6366 .stack_pointer => unreachable, // we can't store/load the sp 6367 .floating_point => { 6368 return switch (ty.floatBits(self.target.*)) { 6369 16 => reg.toH(), 6370 32 => reg.toS(), 6371 64 => reg.toD(), 6372 128 => reg.toQ(), 6373 6374 80 => unreachable, // f80 registers don't exist 6375 else => unreachable, 6376 }; 6377 }, 6378 } 6379 }