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