blob 4e91c085 (125087B) - 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"); 12 const link = @import("../../link.zig"); 13 const Module = @import("../../Module.zig"); 14 const InternPool = @import("../../InternPool.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 codegen = @import("../../codegen.zig"); 25 const Alignment = InternPool.Alignment; 26 27 const CodeGenError = codegen.CodeGenError; 28 const Result = codegen.Result; 29 const DebugInfoOutput = codegen.DebugInfoOutput; 30 31 const bits = @import("bits.zig"); 32 const abi = @import("abi.zig"); 33 const Register = bits.Register; 34 const RegisterManager = abi.RegisterManager; 35 const RegisterLock = RegisterManager.RegisterLock; 36 const callee_preserved_regs = abi.callee_preserved_regs; 37 /// General Purpose 38 const gp = abi.RegisterClass.gp; 39 /// Function Args 40 const fa = abi.RegisterClass.fa; 41 42 const InnerError = CodeGenError || error{OutOfRegisters}; 43 44 gpa: Allocator, 45 air: Air, 46 liveness: Liveness, 47 bin_file: *link.File, 48 target: *const std.Target, 49 func_index: InternPool.Index, 50 code: *std.ArrayList(u8), 51 debug_output: DebugInfoOutput, 52 err_msg: ?*ErrorMsg, 53 args: []MCValue, 54 ret_mcv: MCValue, 55 fn_type: Type, 56 arg_index: usize, 57 src_loc: Module.SrcLoc, 58 stack_align: Alignment, 59 60 /// MIR Instructions 61 mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, 62 /// MIR extra data 63 mir_extra: std.ArrayListUnmanaged(u32) = .{}, 64 65 /// Byte offset within the source file of the ending curly. 66 end_di_line: u32, 67 end_di_column: u32, 68 69 /// The value is an offset into the `Function` `code` from the beginning. 70 /// To perform the reloc, write 32-bit signed little-endian integer 71 /// which is a relative jump, based on the address following the reloc. 72 exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{}, 73 74 /// Whenever there is a runtime branch, we push a Branch onto this stack, 75 /// and pop it off when the runtime branch joins. This provides an "overlay" 76 /// of the table of mappings from instructions to `MCValue` from within the branch. 77 /// This way we can modify the `MCValue` for an instruction in different ways 78 /// within different branches. Special consideration is needed when a branch 79 /// joins with its parent, to make sure all instructions have the same MCValue 80 /// across each runtime branch upon joining. 81 branch_stack: *std.ArrayList(Branch), 82 83 // Key is the block instruction 84 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, 85 86 register_manager: RegisterManager = .{}, 87 /// Maps offset to what is stored there. 88 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, 89 90 /// Offset from the stack base, representing the end of the stack frame. 91 max_end_stack: u32 = 0, 92 /// Represents the current end stack offset. If there is no existing slot 93 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`. 94 next_stack_offset: u32 = 0, 95 96 /// Debug field, used to find bugs in the compiler. 97 air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, 98 99 const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; 100 101 const SymbolOffset = struct { sym: u32, off: i32 = 0 }; 102 103 const MCValue = union(enum) { 104 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. 105 /// TODO Look into deleting this tag and using `dead` instead, since every use 106 /// of MCValue.none should be instead looking at the type and noticing it is 0 bits. 107 none, 108 /// Control flow will not allow this value to be observed. 109 unreach, 110 /// No more references to this value remain. 111 dead, 112 /// The value is undefined. 113 undef, 114 /// A pointer-sized integer that fits in a register. 115 /// If the type is a pointer, this is the pointer address in virtual address space. 116 immediate: u64, 117 /// The value is in memory at an address not-yet-allocated by the linker. 118 /// This traditionally corresponds to a relocation emitted in a relocatable object file. 119 load_symbol: SymbolOffset, 120 /// The value is in a target-specific register. 121 register: Register, 122 /// The value is in memory at a hard-coded address. 123 /// If the type is a pointer, it means the pointer address is at this memory location. 124 memory: u64, 125 /// The value is one of the stack variables. 126 /// If the type is a pointer, it means the pointer address is in the stack at this offset. 127 stack_offset: u32, 128 /// The value is a pointer to one of the stack variables (payload is stack offset). 129 ptr_stack_offset: u32, 130 131 fn isMemory(mcv: MCValue) bool { 132 return switch (mcv) { 133 .memory, .stack_offset => true, 134 else => false, 135 }; 136 } 137 138 fn isImmediate(mcv: MCValue) bool { 139 return switch (mcv) { 140 .immediate => true, 141 else => false, 142 }; 143 } 144 145 fn isMutable(mcv: MCValue) bool { 146 return switch (mcv) { 147 .none => unreachable, 148 .unreach => unreachable, 149 .dead => unreachable, 150 151 .immediate, 152 .memory, 153 .ptr_stack_offset, 154 .undef, 155 .load_symbol, 156 => false, 157 158 .register, 159 .stack_offset, 160 => true, 161 }; 162 } 163 }; 164 165 const Branch = struct { 166 inst_table: std.AutoArrayHashMapUnmanaged(Air.Inst.Index, MCValue) = .{}, 167 168 fn deinit(self: *Branch, gpa: Allocator) void { 169 self.inst_table.deinit(gpa); 170 self.* = undefined; 171 } 172 }; 173 174 const StackAllocation = struct { 175 inst: Air.Inst.Index, 176 /// TODO: make the size inferred from the bits of the inst 177 size: u32, 178 }; 179 180 const BlockData = struct { 181 relocs: std.ArrayListUnmanaged(Mir.Inst.Index), 182 /// The first break instruction encounters `null` here and chooses a 183 /// machine code value for the block result, populating this field. 184 /// Following break instructions encounter that value and use it for 185 /// the location to store their block results. 186 mcv: MCValue, 187 }; 188 189 const BigTomb = struct { 190 function: *Self, 191 inst: Air.Inst.Index, 192 lbt: Liveness.BigTomb, 193 194 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void { 195 const dies = bt.lbt.feed(); 196 const op_index = op_ref.toIndex() orelse return; 197 if (!dies) return; 198 bt.function.processDeath(op_index); 199 } 200 201 fn finishAir(bt: *BigTomb, result: MCValue) void { 202 const is_used = !bt.function.liveness.isUnused(bt.inst); 203 if (is_used) { 204 log.debug("%{d} => {}", .{ bt.inst, result }); 205 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1]; 206 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result); 207 } 208 bt.function.finishAirBookkeeping(); 209 } 210 }; 211 212 const Self = @This(); 213 214 pub fn generate( 215 lf: *link.File, 216 src_loc: Module.SrcLoc, 217 func_index: InternPool.Index, 218 air: Air, 219 liveness: Liveness, 220 code: *std.ArrayList(u8), 221 debug_output: DebugInfoOutput, 222 ) CodeGenError!Result { 223 const gpa = lf.comp.gpa; 224 const zcu = lf.comp.module.?; 225 const func = zcu.funcInfo(func_index); 226 const fn_owner_decl = zcu.declPtr(func.owner_decl); 227 assert(fn_owner_decl.has_tv); 228 const fn_type = fn_owner_decl.typeOf(zcu); 229 const namespace = zcu.namespacePtr(fn_owner_decl.src_namespace); 230 const target = &namespace.file_scope.mod.resolved_target.result; 231 232 var branch_stack = std.ArrayList(Branch).init(gpa); 233 defer { 234 assert(branch_stack.items.len == 1); 235 branch_stack.items[0].deinit(gpa); 236 branch_stack.deinit(); 237 } 238 try branch_stack.append(.{}); 239 240 var function = Self{ 241 .gpa = gpa, 242 .air = air, 243 .liveness = liveness, 244 .target = target, 245 .bin_file = lf, 246 .func_index = func_index, 247 .code = code, 248 .debug_output = debug_output, 249 .err_msg = null, 250 .args = undefined, // populated after `resolveCallingConventionValues` 251 .ret_mcv = undefined, // populated after `resolveCallingConventionValues` 252 .fn_type = fn_type, 253 .arg_index = 0, 254 .branch_stack = &branch_stack, 255 .src_loc = src_loc, 256 .stack_align = undefined, 257 .end_di_line = func.rbrace_line, 258 .end_di_column = func.rbrace_column, 259 }; 260 defer function.stack.deinit(gpa); 261 defer function.blocks.deinit(gpa); 262 defer function.exitlude_jump_relocs.deinit(gpa); 263 264 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { 265 error.CodegenFail => return Result{ .fail = function.err_msg.? }, 266 error.OutOfRegisters => return Result{ 267 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), 268 }, 269 else => |e| return e, 270 }; 271 272 defer call_info.deinit(&function); 273 274 function.args = call_info.args; 275 function.ret_mcv = call_info.return_value; 276 function.stack_align = call_info.stack_align; 277 function.max_end_stack = call_info.stack_byte_count; 278 279 function.gen() catch |err| switch (err) { 280 error.CodegenFail => return Result{ .fail = function.err_msg.? }, 281 error.OutOfRegisters => return Result{ 282 .fail = try ErrorMsg.create(gpa, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), 283 }, 284 else => |e| return e, 285 }; 286 287 var mir = Mir{ 288 .instructions = function.mir_instructions.toOwnedSlice(), 289 .extra = try function.mir_extra.toOwnedSlice(gpa), 290 }; 291 defer mir.deinit(gpa); 292 293 var emit = Emit{ 294 .mir = mir, 295 .bin_file = lf, 296 .debug_output = debug_output, 297 .target = target, 298 .src_loc = src_loc, 299 .code = code, 300 .prev_di_pc = 0, 301 .prev_di_line = func.lbrace_line, 302 .prev_di_column = func.lbrace_column, 303 .stack_size = @max(32, function.max_end_stack), 304 }; 305 defer emit.deinit(); 306 307 emit.emitMir() catch |err| switch (err) { 308 error.EmitFail => return Result{ .fail = emit.err_msg.? }, 309 else => |e| return e, 310 }; 311 312 if (function.err_msg) |em| { 313 return Result{ .fail = em }; 314 } else { 315 return Result.ok; 316 } 317 } 318 319 fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { 320 const gpa = self.gpa; 321 322 try self.mir_instructions.ensureUnusedCapacity(gpa, 1); 323 324 const result_index: Mir.Inst.Index = @intCast(self.mir_instructions.len); 325 self.mir_instructions.appendAssumeCapacity(inst); 326 return result_index; 327 } 328 329 fn addNop(self: *Self) error{OutOfMemory}!Mir.Inst.Index { 330 return try self.addInst(.{ 331 .tag = .nop, 332 .data = .{ .nop = {} }, 333 }); 334 } 335 336 pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 { 337 const fields = std.meta.fields(@TypeOf(extra)); 338 try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len); 339 return self.addExtraAssumeCapacity(extra); 340 } 341 342 pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { 343 const fields = std.meta.fields(@TypeOf(extra)); 344 const result: u32 = @intCast(self.mir_extra.items.len); 345 inline for (fields) |field| { 346 self.mir_extra.appendAssumeCapacity(switch (field.type) { 347 u32 => @field(extra, field.name), 348 i32 => @bitCast(@field(extra, field.name)), 349 else => @compileError("bad field type"), 350 }); 351 } 352 return result; 353 } 354 355 fn gen(self: *Self) !void { 356 _ = try self.addInst(.{ 357 .tag = .psuedo_prologue, 358 .data = .{ .nop = {} }, // Backpatched later. 359 }); 360 361 _ = try self.addInst(.{ 362 .tag = .dbg_prologue_end, 363 .data = .{ .nop = {} }, 364 }); 365 366 try self.genBody(self.air.getMainBody()); 367 368 // Drop them off at the rbrace. 369 _ = try self.addInst(.{ 370 .tag = .dbg_line, 371 .data = .{ .dbg_line_column = .{ 372 .line = self.end_di_line, 373 .column = self.end_di_column, 374 } }, 375 }); 376 } 377 378 fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { 379 const mod = self.bin_file.comp.module.?; 380 const ip = &mod.intern_pool; 381 const air_tags = self.air.instructions.items(.tag); 382 383 for (body) |inst| { 384 // TODO: remove now-redundant isUnused calls from AIR handler functions 385 if (self.liveness.isUnused(inst) and !self.air.mustLower(inst, ip)) 386 continue; 387 388 const old_air_bookkeeping = self.air_bookkeeping; 389 try self.ensureProcessDeathCapacity(Liveness.bpi); 390 391 switch (air_tags[@intFromEnum(inst)]) { 392 // zig fmt: off 393 .ptr_add => try self.airPtrArithmetic(inst, .ptr_add), 394 .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub), 395 396 .add => try self.airBinOp(inst, .add), 397 .sub => try self.airBinOp(inst, .sub), 398 399 .add_safe, 400 .sub_safe, 401 .mul_safe, 402 => return self.fail("TODO implement safety_checked_instructions", .{}), 403 404 .add_wrap => try self.airAddWrap(inst), 405 .add_sat => try self.airAddSat(inst), 406 .sub_wrap => try self.airSubWrap(inst), 407 .sub_sat => try self.airSubSat(inst), 408 .mul => try self.airMul(inst), 409 .mul_wrap => try self.airMulWrap(inst), 410 .mul_sat => try self.airMulSat(inst), 411 .rem => try self.airRem(inst), 412 .mod => try self.airMod(inst), 413 .shl, .shl_exact => try self.airShl(inst), 414 .shl_sat => try self.airShlSat(inst), 415 .min => try self.airMin(inst), 416 .max => try self.airMax(inst), 417 .slice => try self.airSlice(inst), 418 419 .sqrt, 420 .sin, 421 .cos, 422 .tan, 423 .exp, 424 .exp2, 425 .log, 426 .log2, 427 .log10, 428 .floor, 429 .ceil, 430 .round, 431 .trunc_float, 432 .neg, 433 => try self.airUnaryMath(inst), 434 435 .add_with_overflow => try self.airAddWithOverflow(inst), 436 .sub_with_overflow => try self.airSubWithOverflow(inst), 437 .mul_with_overflow => try self.airMulWithOverflow(inst), 438 .shl_with_overflow => try self.airShlWithOverflow(inst), 439 440 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), 441 442 .cmp_lt => try self.airCmp(inst), 443 .cmp_lte => try self.airCmp(inst), 444 .cmp_eq => try self.airCmp(inst), 445 .cmp_gte => try self.airCmp(inst), 446 .cmp_gt => try self.airCmp(inst), 447 .cmp_neq => try self.airCmp(inst), 448 449 .cmp_vector => try self.airCmpVector(inst), 450 .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), 451 452 .bool_and => try self.airBoolOp(inst), 453 .bool_or => try self.airBoolOp(inst), 454 .bit_and => try self.airBitAnd(inst), 455 .bit_or => try self.airBitOr(inst), 456 .xor => try self.airXor(inst), 457 .shr, .shr_exact => try self.airShr(inst), 458 459 .alloc => try self.airAlloc(inst), 460 .ret_ptr => try self.airRetPtr(inst), 461 .arg => try self.airArg(inst), 462 .assembly => try self.airAsm(inst), 463 .bitcast => try self.airBitCast(inst), 464 .block => try self.airBlock(inst), 465 .br => try self.airBr(inst), 466 .trap => try self.airTrap(), 467 .breakpoint => try self.airBreakpoint(), 468 .ret_addr => try self.airRetAddr(inst), 469 .frame_addr => try self.airFrameAddress(inst), 470 .fence => try self.airFence(), 471 .cond_br => try self.airCondBr(inst), 472 .dbg_stmt => try self.airDbgStmt(inst), 473 .fptrunc => try self.airFptrunc(inst), 474 .fpext => try self.airFpext(inst), 475 .intcast => try self.airIntCast(inst), 476 .trunc => try self.airTrunc(inst), 477 .int_from_bool => try self.airIntFromBool(inst), 478 .is_non_null => try self.airIsNonNull(inst), 479 .is_non_null_ptr => try self.airIsNonNullPtr(inst), 480 .is_null => try self.airIsNull(inst), 481 .is_null_ptr => try self.airIsNullPtr(inst), 482 .is_non_err => try self.airIsNonErr(inst), 483 .is_non_err_ptr => try self.airIsNonErrPtr(inst), 484 .is_err => try self.airIsErr(inst), 485 .is_err_ptr => try self.airIsErrPtr(inst), 486 .load => try self.airLoad(inst), 487 .loop => try self.airLoop(inst), 488 .not => try self.airNot(inst), 489 .int_from_ptr => try self.airIntFromPtr(inst), 490 .ret => try self.airRet(inst, false), 491 .ret_safe => try self.airRet(inst, true), 492 .ret_load => try self.airRetLoad(inst), 493 .store => try self.airStore(inst, false), 494 .store_safe => try self.airStore(inst, true), 495 .struct_field_ptr=> try self.airStructFieldPtr(inst), 496 .struct_field_val=> try self.airStructFieldVal(inst), 497 .array_to_slice => try self.airArrayToSlice(inst), 498 .float_from_int => try self.airFloatFromInt(inst), 499 .int_from_float => try self.airIntFromFloat(inst), 500 .cmpxchg_strong => try self.airCmpxchg(inst), 501 .cmpxchg_weak => try self.airCmpxchg(inst), 502 .atomic_rmw => try self.airAtomicRmw(inst), 503 .atomic_load => try self.airAtomicLoad(inst), 504 .memcpy => try self.airMemcpy(inst), 505 .memset => try self.airMemset(inst, false), 506 .memset_safe => try self.airMemset(inst, true), 507 .set_union_tag => try self.airSetUnionTag(inst), 508 .get_union_tag => try self.airGetUnionTag(inst), 509 .clz => try self.airClz(inst), 510 .ctz => try self.airCtz(inst), 511 .popcount => try self.airPopcount(inst), 512 .abs => try self.airAbs(inst), 513 .byte_swap => try self.airByteSwap(inst), 514 .bit_reverse => try self.airBitReverse(inst), 515 .tag_name => try self.airTagName(inst), 516 .error_name => try self.airErrorName(inst), 517 .splat => try self.airSplat(inst), 518 .select => try self.airSelect(inst), 519 .shuffle => try self.airShuffle(inst), 520 .reduce => try self.airReduce(inst), 521 .aggregate_init => try self.airAggregateInit(inst), 522 .union_init => try self.airUnionInit(inst), 523 .prefetch => try self.airPrefetch(inst), 524 .mul_add => try self.airMulAdd(inst), 525 .addrspace_cast => return self.fail("TODO: addrspace_cast", .{}), 526 527 .@"try" => return self.fail("TODO: try", .{}), 528 .try_ptr => return self.fail("TODO: try_ptr", .{}), 529 530 .dbg_var_ptr, 531 .dbg_var_val, 532 => try self.airDbgVar(inst), 533 534 .dbg_inline_block => try self.airDbgInlineBlock(inst), 535 536 .call => try self.airCall(inst, .auto), 537 .call_always_tail => try self.airCall(inst, .always_tail), 538 .call_never_tail => try self.airCall(inst, .never_tail), 539 .call_never_inline => try self.airCall(inst, .never_inline), 540 541 .atomic_store_unordered => try self.airAtomicStore(inst, .unordered), 542 .atomic_store_monotonic => try self.airAtomicStore(inst, .monotonic), 543 .atomic_store_release => try self.airAtomicStore(inst, .release), 544 .atomic_store_seq_cst => try self.airAtomicStore(inst, .seq_cst), 545 546 .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), 547 .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), 548 .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), 549 .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), 550 551 .field_parent_ptr => try self.airFieldParentPtr(inst), 552 553 .switch_br => try self.airSwitch(inst), 554 .slice_ptr => try self.airSlicePtr(inst), 555 .slice_len => try self.airSliceLen(inst), 556 557 .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst), 558 .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst), 559 560 .array_elem_val => try self.airArrayElemVal(inst), 561 .slice_elem_val => try self.airSliceElemVal(inst), 562 .slice_elem_ptr => try self.airSliceElemPtr(inst), 563 .ptr_elem_val => try self.airPtrElemVal(inst), 564 .ptr_elem_ptr => try self.airPtrElemPtr(inst), 565 566 .inferred_alloc, .inferred_alloc_comptime => unreachable, 567 .unreach => self.finishAirBookkeeping(), 568 569 .optional_payload => try self.airOptionalPayload(inst), 570 .optional_payload_ptr => try self.airOptionalPayloadPtr(inst), 571 .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst), 572 .unwrap_errunion_err => try self.airUnwrapErrErr(inst), 573 .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), 574 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), 575 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), 576 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst), 577 .err_return_trace => try self.airErrReturnTrace(inst), 578 .set_err_return_trace => try self.airSetErrReturnTrace(inst), 579 .save_err_return_trace_index=> try self.airSaveErrReturnTraceIndex(inst), 580 581 .wrap_optional => try self.airWrapOptional(inst), 582 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), 583 .wrap_errunion_err => try self.airWrapErrUnionErr(inst), 584 585 .add_optimized, 586 .sub_optimized, 587 .mul_optimized, 588 .div_float_optimized, 589 .div_trunc_optimized, 590 .div_floor_optimized, 591 .div_exact_optimized, 592 .rem_optimized, 593 .mod_optimized, 594 .neg_optimized, 595 .cmp_lt_optimized, 596 .cmp_lte_optimized, 597 .cmp_eq_optimized, 598 .cmp_gte_optimized, 599 .cmp_gt_optimized, 600 .cmp_neq_optimized, 601 .cmp_vector_optimized, 602 .reduce_optimized, 603 .int_from_float_optimized, 604 => return self.fail("TODO implement optimized float mode", .{}), 605 606 .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}), 607 .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), 608 .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}), 609 610 .c_va_arg => return self.fail("TODO implement c_va_arg", .{}), 611 .c_va_copy => return self.fail("TODO implement c_va_copy", .{}), 612 .c_va_end => return self.fail("TODO implement c_va_end", .{}), 613 .c_va_start => return self.fail("TODO implement c_va_start", .{}), 614 615 .wasm_memory_size => unreachable, 616 .wasm_memory_grow => unreachable, 617 618 .work_item_id => unreachable, 619 .work_group_size => unreachable, 620 .work_group_id => unreachable, 621 // zig fmt: on 622 } 623 if (std.debug.runtime_safety) { 624 if (self.air_bookkeeping < old_air_bookkeeping + 1) { 625 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[@intFromEnum(inst)] }); 626 } 627 } 628 } 629 } 630 631 /// Asserts there is already capacity to insert into top branch inst_table. 632 fn processDeath(self: *Self, inst: Air.Inst.Index) void { 633 // When editing this function, note that the logic must synchronize with `reuseOperand`. 634 const prev_value = self.getResolvedInstValue(inst); 635 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 636 branch.inst_table.putAssumeCapacity(inst, .dead); 637 switch (prev_value) { 638 .register => |reg| { 639 self.register_manager.freeReg(reg); 640 }, 641 else => {}, // TODO process stack allocation death 642 } 643 } 644 645 /// Called when there are no operands, and the instruction is always unreferenced. 646 fn finishAirBookkeeping(self: *Self) void { 647 if (std.debug.runtime_safety) { 648 self.air_bookkeeping += 1; 649 } 650 } 651 652 fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void { 653 var tomb_bits = self.liveness.getTombBits(inst); 654 for (operands) |op| { 655 const dies = @as(u1, @truncate(tomb_bits)) != 0; 656 tomb_bits >>= 1; 657 if (!dies) continue; 658 const op_index = op.toIndex() orelse continue; 659 self.processDeath(op_index); 660 } 661 const is_used = @as(u1, @truncate(tomb_bits)) == 0; 662 if (is_used) { 663 log.debug("%{d} => {}", .{ inst, result }); 664 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 665 branch.inst_table.putAssumeCapacityNoClobber(inst, result); 666 667 switch (result) { 668 .register => |reg| { 669 // In some cases (such as bitcast), an operand 670 // may be the same MCValue as the result. If 671 // that operand died and was a register, it 672 // was freed by processDeath. We have to 673 // "re-allocate" the register. 674 if (self.register_manager.isRegFree(reg)) { 675 self.register_manager.getRegAssumeFree(reg, inst); 676 } 677 }, 678 else => {}, 679 } 680 } 681 self.finishAirBookkeeping(); 682 } 683 684 fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { 685 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table; 686 try table.ensureUnusedCapacity(self.gpa, additional_count); 687 } 688 689 fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: Alignment) !u32 { 690 self.stack_align = self.stack_align.max(abi_align); 691 // TODO find a free slot instead of always appending 692 const offset: u32 = @intCast(abi_align.forward(self.next_stack_offset)); 693 self.next_stack_offset = offset + abi_size; 694 if (self.next_stack_offset > self.max_end_stack) 695 self.max_end_stack = self.next_stack_offset; 696 try self.stack.putNoClobber(self.gpa, offset, .{ 697 .inst = inst, 698 .size = abi_size, 699 }); 700 return offset; 701 } 702 703 /// Use a pointer instruction as the basis for allocating stack memory. 704 fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { 705 const mod = self.bin_file.comp.module.?; 706 const elem_ty = self.typeOfIndex(inst).childType(mod); 707 const abi_size = math.cast(u32, elem_ty.abiSize(mod)) orelse { 708 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); 709 }; 710 // TODO swap this for inst.ty.ptrAlign 711 const abi_align = elem_ty.abiAlignment(mod); 712 return self.allocMem(inst, abi_size, abi_align); 713 } 714 715 fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { 716 const mod = self.bin_file.comp.module.?; 717 const elem_ty = self.typeOfIndex(inst); 718 const abi_size = math.cast(u32, elem_ty.abiSize(mod)) orelse { 719 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)}); 720 }; 721 const abi_align = elem_ty.abiAlignment(mod); 722 self.stack_align = self.stack_align.max(abi_align); 723 724 if (reg_ok) { 725 // Make sure the type can fit in a register before we try to allocate one. 726 const ptr_bits = self.target.ptrBitWidth(); 727 const ptr_bytes: u64 = @divExact(ptr_bits, 8); 728 if (abi_size <= ptr_bytes) { 729 if (self.register_manager.tryAllocReg(inst, gp)) |reg| { 730 return MCValue{ .register = reg }; 731 } 732 } 733 } 734 const stack_offset = try self.allocMem(inst, abi_size, abi_align); 735 return MCValue{ .stack_offset = stack_offset }; 736 } 737 738 pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { 739 const stack_mcv = try self.allocRegOrMem(inst, false); 740 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); 741 const reg_mcv = self.getResolvedInstValue(inst); 742 assert(reg == reg_mcv.register); 743 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 744 try branch.inst_table.put(self.gpa, inst, stack_mcv); 745 try self.genSetStack(self.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); 746 } 747 748 /// Copies a value to a register without tracking the register. The register is not considered 749 /// allocated. A second call to `copyToTmpRegister` may return the same register. 750 /// This can have a side effect of spilling instructions to the stack to free up a register. 751 fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { 752 const reg = try self.register_manager.allocReg(null, gp); 753 try self.genSetReg(ty, reg, mcv); 754 return reg; 755 } 756 757 /// Allocates a new register and copies `mcv` into it. 758 /// `reg_owner` is the instruction that gets associated with the register in the register table. 759 /// This can have a side effect of spilling instructions to the stack to free up a register. 760 fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { 761 const reg = try self.register_manager.allocReg(reg_owner, gp); 762 try self.genSetReg(self.typeOfIndex(reg_owner), reg, mcv); 763 return MCValue{ .register = reg }; 764 } 765 766 fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { 767 const stack_offset = try self.allocMemPtr(inst); 768 log.debug("airAlloc offset: {}", .{stack_offset}); 769 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); 770 } 771 772 fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { 773 const stack_offset = try self.allocMemPtr(inst); 774 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); 775 } 776 777 fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { 778 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 779 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch}); 780 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 781 } 782 783 fn airFpext(self: *Self, inst: Air.Inst.Index) !void { 784 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 785 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch}); 786 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 787 } 788 789 fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { 790 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 791 if (self.liveness.isUnused(inst)) 792 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); 793 794 const mod = self.bin_file.comp.module.?; 795 const operand_ty = self.typeOf(ty_op.operand); 796 const operand = try self.resolveInst(ty_op.operand); 797 const info_a = operand_ty.intInfo(mod); 798 const info_b = self.typeOfIndex(inst).intInfo(mod); 799 if (info_a.signedness != info_b.signedness) 800 return self.fail("TODO gen intcast sign safety in semantic analysis", .{}); 801 802 if (info_a.bits == info_b.bits) 803 return self.finishAir(inst, operand, .{ ty_op.operand, .none, .none }); 804 805 return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch}); 806 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 807 } 808 809 fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { 810 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 811 if (self.liveness.isUnused(inst)) 812 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); 813 814 const operand = try self.resolveInst(ty_op.operand); 815 _ = operand; 816 return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch}); 817 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 818 } 819 820 fn airIntFromBool(self: *Self, inst: Air.Inst.Index) !void { 821 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 822 const operand = try self.resolveInst(un_op); 823 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand; 824 return self.finishAir(inst, result, .{ un_op, .none, .none }); 825 } 826 827 fn airNot(self: *Self, inst: Air.Inst.Index) !void { 828 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 829 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement NOT for {}", .{self.target.cpu.arch}); 830 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 831 } 832 833 fn airMin(self: *Self, inst: Air.Inst.Index) !void { 834 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 835 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement min for {}", .{self.target.cpu.arch}); 836 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 837 } 838 839 fn airMax(self: *Self, inst: Air.Inst.Index) !void { 840 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 841 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement max for {}", .{self.target.cpu.arch}); 842 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 843 } 844 845 fn airSlice(self: *Self, inst: Air.Inst.Index) !void { 846 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 847 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 848 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice for {}", .{self.target.cpu.arch}); 849 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 850 } 851 852 /// Don't call this function directly. Use binOp instead. 853 /// 854 /// Calling this function signals an intention to generate a Mir 855 /// instruction of the form 856 /// 857 /// op dest, lhs, rhs 858 /// 859 /// Asserts that generating an instruction of that form is possible. 860 fn binOpRegister( 861 self: *Self, 862 tag: Air.Inst.Tag, 863 maybe_inst: ?Air.Inst.Index, 864 lhs: MCValue, 865 rhs: MCValue, 866 lhs_ty: Type, 867 rhs_ty: Type, 868 ) !MCValue { 869 const lhs_is_register = lhs == .register; 870 const rhs_is_register = rhs == .register; 871 872 const lhs_lock: ?RegisterLock = if (lhs_is_register) 873 self.register_manager.lockReg(lhs.register) 874 else 875 null; 876 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg); 877 878 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 879 880 const lhs_reg = if (lhs_is_register) lhs.register else blk: { 881 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { 882 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 883 break :inst bin_op.lhs.toIndex().?; 884 } else null; 885 886 const reg = try self.register_manager.allocReg(track_inst, gp); 887 888 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); 889 890 break :blk reg; 891 }; 892 const new_lhs_lock = self.register_manager.lockReg(lhs_reg); 893 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg); 894 895 const rhs_reg = if (rhs_is_register) rhs.register else blk: { 896 const track_inst: ?Air.Inst.Index = if (maybe_inst) |inst| inst: { 897 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 898 break :inst bin_op.rhs.toIndex().?; 899 } else null; 900 901 const reg = try self.register_manager.allocReg(track_inst, gp); 902 903 if (track_inst) |inst| branch.inst_table.putAssumeCapacity(inst, .{ .register = reg }); 904 905 break :blk reg; 906 }; 907 const new_rhs_lock = self.register_manager.lockReg(rhs_reg); 908 defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg); 909 910 const dest_reg = if (maybe_inst) |inst| blk: { 911 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 912 913 if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) { 914 break :blk lhs_reg; 915 } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) { 916 break :blk rhs_reg; 917 } else { 918 break :blk try self.register_manager.allocReg(inst, gp); 919 } 920 } else try self.register_manager.allocReg(null, gp); 921 922 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs); 923 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs); 924 925 const mir_tag: Mir.Inst.Tag = switch (tag) { 926 .add => .add, 927 .sub => .sub, 928 .cmp_eq => .cmp_eq, 929 .cmp_gt => .cmp_gt, 930 else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}), 931 }; 932 const mir_data: Mir.Inst.Data = switch (tag) { 933 .add, 934 .sub, 935 .cmp_eq, 936 => .{ .r_type = .{ 937 .rd = dest_reg, 938 .rs1 = lhs_reg, 939 .rs2 = rhs_reg, 940 } }, 941 else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}), 942 }; 943 944 _ = try self.addInst(.{ 945 .tag = mir_tag, 946 .data = mir_data, 947 }); 948 949 return MCValue{ .register = dest_reg }; 950 } 951 952 /// For all your binary operation needs, this function will generate 953 /// the corresponding Mir instruction(s). Returns the location of the 954 /// result. 955 /// 956 /// If the binary operation itself happens to be an Air instruction, 957 /// pass the corresponding index in the inst parameter. That helps 958 /// this function do stuff like reusing operands. 959 /// 960 /// This function does not do any lowering to Mir itself, but instead 961 /// looks at the lhs and rhs and determines which kind of lowering 962 /// would be best suitable and then delegates the lowering to other 963 /// functions. 964 /// 965 /// `maybe_inst` **needs** to be a bin_op, make sure of that. 966 fn binOp( 967 self: *Self, 968 tag: Air.Inst.Tag, 969 maybe_inst: ?Air.Inst.Index, 970 lhs: MCValue, 971 rhs: MCValue, 972 lhs_ty: Type, 973 rhs_ty: Type, 974 ) InnerError!MCValue { 975 const mod = self.bin_file.comp.module.?; 976 switch (tag) { 977 // Arithmetic operations on integers and floats 978 .add, 979 .sub, 980 .cmp_eq, 981 .cmp_neq, 982 .cmp_gt, 983 .cmp_gte, 984 .cmp_lt, 985 .cmp_lte, 986 => { 987 switch (lhs_ty.zigTypeTag(mod)) { 988 .Float => return self.fail("TODO binary operations on floats", .{}), 989 .Vector => return self.fail("TODO binary operations on vectors", .{}), 990 .Int => { 991 assert(lhs_ty.eql(rhs_ty, mod)); 992 const int_info = lhs_ty.intInfo(mod); 993 if (int_info.bits <= 64) { 994 // TODO immediate operands 995 return try self.binOpRegister(tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); 996 } else { 997 return self.fail("TODO binary operations on int with bits > 64", .{}); 998 } 999 }, 1000 else => unreachable, 1001 } 1002 }, 1003 .ptr_add, 1004 .ptr_sub, 1005 => { 1006 switch (lhs_ty.zigTypeTag(mod)) { 1007 .Pointer => { 1008 const ptr_ty = lhs_ty; 1009 const elem_ty = switch (ptr_ty.ptrSize(mod)) { 1010 .One => ptr_ty.childType(mod).childType(mod), // ptr to array, so get array element type 1011 else => ptr_ty.childType(mod), 1012 }; 1013 const elem_size = elem_ty.abiSize(mod); 1014 1015 if (elem_size == 1) { 1016 const base_tag: Air.Inst.Tag = switch (tag) { 1017 .ptr_add => .add, 1018 .ptr_sub => .sub, 1019 else => unreachable, 1020 }; 1021 1022 return try self.binOpRegister(base_tag, maybe_inst, lhs, rhs, lhs_ty, rhs_ty); 1023 } else { 1024 return self.fail("TODO ptr_add with elem_size > 1", .{}); 1025 } 1026 }, 1027 else => unreachable, 1028 } 1029 }, 1030 else => unreachable, 1031 } 1032 } 1033 1034 fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { 1035 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1036 const lhs = try self.resolveInst(bin_op.lhs); 1037 const rhs = try self.resolveInst(bin_op.rhs); 1038 const lhs_ty = self.typeOf(bin_op.lhs); 1039 const rhs_ty = self.typeOf(bin_op.rhs); 1040 1041 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); 1042 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1043 } 1044 1045 fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { 1046 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 1047 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 1048 const lhs = try self.resolveInst(bin_op.lhs); 1049 const rhs = try self.resolveInst(bin_op.rhs); 1050 const lhs_ty = self.typeOf(bin_op.lhs); 1051 const rhs_ty = self.typeOf(bin_op.rhs); 1052 1053 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else try self.binOp(tag, inst, lhs, rhs, lhs_ty, rhs_ty); 1054 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1055 } 1056 1057 fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void { 1058 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1059 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch}); 1060 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1061 } 1062 1063 fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { 1064 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1065 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch}); 1066 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1067 } 1068 1069 fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void { 1070 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1071 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch}); 1072 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1073 } 1074 1075 fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { 1076 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1077 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch}); 1078 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1079 } 1080 1081 fn airMul(self: *Self, inst: Air.Inst.Index) !void { 1082 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1083 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul for {}", .{self.target.cpu.arch}); 1084 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1085 } 1086 1087 fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void { 1088 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1089 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch}); 1090 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1091 } 1092 1093 fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { 1094 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1095 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch}); 1096 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1097 } 1098 1099 fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { 1100 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 1101 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 1102 1103 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1104 const lhs = try self.resolveInst(extra.lhs); 1105 const rhs = try self.resolveInst(extra.rhs); 1106 const lhs_ty = self.typeOf(extra.lhs); 1107 const rhs_ty = self.typeOf(extra.rhs); 1108 1109 break :result try self.binOp(.add, null, lhs, rhs, lhs_ty, rhs_ty); 1110 }; 1111 1112 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); 1113 } 1114 1115 fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { 1116 _ = inst; 1117 return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch}); 1118 } 1119 1120 fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { 1121 _ = inst; 1122 return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch}); 1123 } 1124 1125 fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { 1126 _ = inst; 1127 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch}); 1128 } 1129 1130 fn airDiv(self: *Self, inst: Air.Inst.Index) !void { 1131 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1132 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch}); 1133 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1134 } 1135 1136 fn airRem(self: *Self, inst: Air.Inst.Index) !void { 1137 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1138 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement rem for {}", .{self.target.cpu.arch}); 1139 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1140 } 1141 1142 fn airMod(self: *Self, inst: Air.Inst.Index) !void { 1143 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1144 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mod for {}", .{self.target.cpu.arch}); 1145 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1146 } 1147 1148 fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void { 1149 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1150 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement bitwise and for {}", .{self.target.cpu.arch}); 1151 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1152 } 1153 1154 fn airBitOr(self: *Self, inst: Air.Inst.Index) !void { 1155 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1156 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement bitwise or for {}", .{self.target.cpu.arch}); 1157 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1158 } 1159 1160 fn airXor(self: *Self, inst: Air.Inst.Index) !void { 1161 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1162 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement xor for {}", .{self.target.cpu.arch}); 1163 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1164 } 1165 1166 fn airShl(self: *Self, inst: Air.Inst.Index) !void { 1167 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1168 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl for {}", .{self.target.cpu.arch}); 1169 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1170 } 1171 1172 fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { 1173 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1174 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}); 1175 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1176 } 1177 1178 fn airShr(self: *Self, inst: Air.Inst.Index) !void { 1179 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1180 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shr for {}", .{self.target.cpu.arch}); 1181 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1182 } 1183 1184 fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { 1185 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1186 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch}); 1187 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1188 } 1189 1190 fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { 1191 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1192 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}); 1193 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1194 } 1195 1196 fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { 1197 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1198 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch}); 1199 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1200 } 1201 1202 fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { 1203 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1204 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union error for {}", .{self.target.cpu.arch}); 1205 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1206 } 1207 1208 fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { 1209 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1210 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch}); 1211 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1212 } 1213 1214 // *(E!T) -> E 1215 fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void { 1216 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1217 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}); 1218 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1219 } 1220 1221 // *(E!T) -> *T 1222 fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { 1223 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1224 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}); 1225 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1226 } 1227 1228 fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { 1229 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1230 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch}); 1231 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1232 } 1233 1234 fn airErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { 1235 const result: MCValue = if (self.liveness.isUnused(inst)) 1236 .dead 1237 else 1238 return self.fail("TODO implement airErrReturnTrace for {}", .{self.target.cpu.arch}); 1239 return self.finishAir(inst, result, .{ .none, .none, .none }); 1240 } 1241 1242 fn airSetErrReturnTrace(self: *Self, inst: Air.Inst.Index) !void { 1243 _ = inst; 1244 return self.fail("TODO implement airSetErrReturnTrace for {}", .{self.target.cpu.arch}); 1245 } 1246 1247 fn airSaveErrReturnTraceIndex(self: *Self, inst: Air.Inst.Index) !void { 1248 _ = inst; 1249 return self.fail("TODO implement airSaveErrReturnTraceIndex for {}", .{self.target.cpu.arch}); 1250 } 1251 1252 fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { 1253 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1254 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1255 const mod = self.bin_file.comp.module.?; 1256 const optional_ty = self.typeOfIndex(inst); 1257 1258 // Optional with a zero-bit payload type is just a boolean true 1259 if (optional_ty.abiSize(mod) == 1) 1260 break :result MCValue{ .immediate = 1 }; 1261 1262 return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}); 1263 }; 1264 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1265 } 1266 1267 /// T to E!T 1268 fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { 1269 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1270 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch}); 1271 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1272 } 1273 1274 /// E to E!T 1275 fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { 1276 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1277 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement wrap errunion error for {}", .{self.target.cpu.arch}); 1278 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1279 } 1280 1281 fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { 1282 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1283 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1284 const mcv = try self.resolveInst(ty_op.operand); 1285 break :result try self.slicePtr(mcv); 1286 }; 1287 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1288 } 1289 1290 fn slicePtr(self: *Self, mcv: MCValue) !MCValue { 1291 switch (mcv) { 1292 .dead, .unreach, .none => unreachable, 1293 .register => unreachable, // a slice doesn't fit in one register 1294 .stack_offset => |off| { 1295 return MCValue{ .stack_offset = off }; 1296 }, 1297 .memory => |addr| { 1298 return MCValue{ .memory = addr }; 1299 }, 1300 else => return self.fail("TODO slicePtr {s}", .{@tagName(mcv)}), 1301 } 1302 } 1303 1304 fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { 1305 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1306 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSliceLen for {}", .{self.target.cpu.arch}); 1307 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1308 } 1309 1310 fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void { 1311 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1312 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_slice_len_ptr for {}", .{self.target.cpu.arch}); 1313 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1314 } 1315 1316 fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { 1317 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1318 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_slice_ptr_ptr for {}", .{self.target.cpu.arch}); 1319 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1320 } 1321 1322 fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { 1323 const is_volatile = false; // TODO 1324 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1325 const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_elem_val for {}", .{self.target.cpu.arch}); 1326 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1327 } 1328 1329 fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { 1330 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 1331 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 1332 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_elem_ptr for {}", .{self.target.cpu.arch}); 1333 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); 1334 } 1335 1336 fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { 1337 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1338 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch}); 1339 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1340 } 1341 1342 fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { 1343 const is_volatile = false; // TODO 1344 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1345 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}); 1346 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1347 } 1348 1349 fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { 1350 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 1351 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 1352 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_ptr for {}", .{self.target.cpu.arch}); 1353 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); 1354 } 1355 1356 fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { 1357 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1358 _ = bin_op; 1359 return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch}); 1360 // return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1361 } 1362 1363 fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { 1364 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1365 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch}); 1366 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1367 } 1368 1369 fn airClz(self: *Self, inst: Air.Inst.Index) !void { 1370 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1371 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch}); 1372 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1373 } 1374 1375 fn airCtz(self: *Self, inst: Air.Inst.Index) !void { 1376 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1377 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch}); 1378 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1379 } 1380 1381 fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { 1382 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1383 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch}); 1384 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1385 } 1386 1387 fn airAbs(self: *Self, inst: Air.Inst.Index) !void { 1388 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1389 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airAbs for {}", .{self.target.cpu.arch}); 1390 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1391 } 1392 1393 fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { 1394 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1395 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); 1396 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1397 } 1398 1399 fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { 1400 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1401 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch}); 1402 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1403 } 1404 1405 fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { 1406 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 1407 const result: MCValue = if (self.liveness.isUnused(inst)) 1408 .dead 1409 else 1410 return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch}); 1411 return self.finishAir(inst, result, .{ un_op, .none, .none }); 1412 } 1413 1414 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { 1415 if (!self.liveness.operandDies(inst, op_index)) 1416 return false; 1417 1418 switch (mcv) { 1419 .register => |reg| { 1420 // If it's in the registers table, need to associate the register with the 1421 // new instruction. 1422 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { 1423 if (!self.register_manager.isRegFree(reg)) { 1424 self.register_manager.registers[index] = inst; 1425 } 1426 } 1427 log.debug("%{d} => {} (reused)", .{ inst, reg }); 1428 }, 1429 .stack_offset => |off| { 1430 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off }); 1431 }, 1432 else => return false, 1433 } 1434 1435 // Prevent the operand deaths processing code from deallocating it. 1436 self.liveness.clearOperandDeath(inst, op_index); 1437 1438 // That makes us responsible for doing the rest of the stuff that processDeath would have done. 1439 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 1440 branch.inst_table.putAssumeCapacity(operand.toIndex().?, .dead); 1441 1442 return true; 1443 } 1444 1445 fn load(self: *Self, dst_mcv: MCValue, src_ptr: MCValue, ptr_ty: Type) InnerError!void { 1446 const mod = self.bin_file.comp.module.?; 1447 const elem_ty = ptr_ty.childType(mod); 1448 1449 switch (src_ptr) { 1450 .none => unreachable, 1451 .undef => unreachable, 1452 .unreach => unreachable, 1453 .dead => unreachable, 1454 .immediate => |imm| try self.setValue(elem_ty, dst_mcv, .{ .memory = imm }), 1455 .ptr_stack_offset => |off| try self.setValue(elem_ty, dst_mcv, .{ .stack_offset = off }), 1456 .register => try self.setValue(elem_ty, dst_mcv, src_ptr), 1457 .memory, 1458 .stack_offset, 1459 => { 1460 const reg = try self.register_manager.allocReg(null, gp); 1461 const reg_lock = self.register_manager.lockRegAssumeUnused(reg); 1462 errdefer self.register_manager.unlockReg(reg_lock); 1463 1464 try self.genSetReg(ptr_ty, reg, src_ptr); 1465 try self.load(dst_mcv, .{ .register = reg }, ptr_ty); 1466 }, 1467 .load_symbol => { 1468 const reg = try self.copyToTmpRegister(ptr_ty, src_ptr); 1469 try self.load(dst_mcv, .{ .register = reg }, ptr_ty); 1470 }, 1471 } 1472 } 1473 1474 fn airLoad(self: *Self, inst: Air.Inst.Index) !void { 1475 const mod = self.bin_file.comp.module.?; 1476 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1477 const elem_ty = self.typeOfIndex(inst); 1478 const result: MCValue = result: { 1479 if (!elem_ty.hasRuntimeBits(mod)) 1480 break :result MCValue.none; 1481 1482 const ptr = try self.resolveInst(ty_op.operand); 1483 const is_volatile = self.typeOf(ty_op.operand).isVolatilePtr(mod); 1484 if (self.liveness.isUnused(inst) and !is_volatile) 1485 break :result MCValue.dead; 1486 1487 const dst_mcv: MCValue = blk: { 1488 if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) { 1489 // The MCValue that holds the pointer can be re-used as the value. 1490 break :blk ptr; 1491 } else { 1492 break :blk try self.allocRegOrMem(inst, true); 1493 } 1494 }; 1495 try self.load(dst_mcv, ptr, self.typeOf(ty_op.operand)); 1496 break :result dst_mcv; 1497 }; 1498 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1499 } 1500 1501 fn store(self: *Self, dst_ptr: MCValue, src_val: MCValue, ptr_ty: Type, value_ty: Type) !void { 1502 _ = ptr_ty; 1503 1504 log.debug("storing {s}", .{@tagName(dst_ptr)}); 1505 1506 switch (dst_ptr) { 1507 .none => unreachable, 1508 .undef => unreachable, 1509 .unreach => unreachable, 1510 .dead => unreachable, 1511 .ptr_stack_offset => |off| try self.genSetStack(value_ty, off, src_val), 1512 else => return self.fail("TODO implement storing to MCValue.{s}", .{@tagName(dst_ptr)}), 1513 } 1514 } 1515 1516 fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void { 1517 if (safety) { 1518 // TODO if the value is undef, write 0xaa bytes to dest 1519 } else { 1520 // TODO if the value is undef, don't lower this instruction 1521 } 1522 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1523 const ptr = try self.resolveInst(bin_op.lhs); 1524 const value = try self.resolveInst(bin_op.rhs); 1525 const ptr_ty = self.typeOf(bin_op.lhs); 1526 const value_ty = self.typeOf(bin_op.rhs); 1527 1528 try self.store(ptr, value, ptr_ty, value_ty); 1529 1530 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); 1531 } 1532 1533 fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void { 1534 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 1535 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; 1536 const result = try self.structFieldPtr(inst, extra.struct_operand, ty_pl.ty, extra.field_index); 1537 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); 1538 } 1539 1540 fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { 1541 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 1542 const result = try self.structFieldPtr(inst, ty_op.operand, ty_op.ty, index); 1543 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1544 } 1545 1546 fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, ty: Air.Inst.Ref, index: u32) !MCValue { 1547 _ = inst; 1548 _ = operand; 1549 _ = ty; 1550 _ = index; 1551 1552 return self.fail("TODO: structFieldPtr", .{}); 1553 } 1554 1555 fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { 1556 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 1557 _ = ty_pl; 1558 1559 return self.fail("TODO: airStructFieldVal", .{}); 1560 1561 // return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); 1562 } 1563 1564 fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { 1565 _ = inst; 1566 return self.fail("TODO implement codegen airFieldParentPtr", .{}); 1567 } 1568 1569 fn genArgDbgInfo(self: Self, inst: Air.Inst.Index, mcv: MCValue) !void { 1570 const mod = self.bin_file.comp.module.?; 1571 const arg = self.air.instructions.items(.data)[@intFromEnum(inst)].arg; 1572 const ty = arg.ty.toType(); 1573 const owner_decl = mod.funcOwnerDeclIndex(self.func_index); 1574 const name = mod.getParamName(self.func_index, arg.src_index); 1575 1576 switch (self.debug_output) { 1577 .dwarf => |dw| switch (mcv) { 1578 .register => |reg| try dw.genArgDbgInfo(name, ty, owner_decl, .{ 1579 .register = reg.dwarfLocOp(), 1580 }), 1581 .stack_offset => {}, 1582 else => {}, 1583 }, 1584 .plan9 => {}, 1585 .none => {}, 1586 } 1587 } 1588 1589 fn airArg(self: *Self, inst: Air.Inst.Index) !void { 1590 var arg_index = self.arg_index; 1591 1592 // we skip over args that have no bits 1593 while (self.args[arg_index] == .none) arg_index += 1; 1594 self.arg_index = arg_index + 1; 1595 1596 const result: MCValue = if (self.liveness.isUnused(inst)) .unreach else result: { 1597 const arg_ty = self.typeOfIndex(inst); 1598 _ = arg_ty; 1599 const src_mcv = self.args[arg_index]; 1600 1601 const dst_mcv = switch (src_mcv) { 1602 .register => |src_reg| dst: { 1603 self.register_manager.getRegAssumeFree(src_reg, inst); 1604 break :dst src_mcv; 1605 }, 1606 // don't need to allocate anything, can just be used immediately. 1607 .stack_offset => src_mcv, 1608 else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}), 1609 }; 1610 1611 try self.genArgDbgInfo(inst, src_mcv); 1612 break :result dst_mcv; 1613 }; 1614 1615 return self.finishAir(inst, result, .{ .none, .none, .none }); 1616 } 1617 1618 fn airTrap(self: *Self) !void { 1619 _ = try self.addInst(.{ 1620 .tag = .unimp, 1621 .data = .{ .nop = {} }, 1622 }); 1623 return self.finishAirBookkeeping(); 1624 } 1625 1626 fn airBreakpoint(self: *Self) !void { 1627 _ = try self.addInst(.{ 1628 .tag = .ebreak, 1629 .data = .{ .nop = {} }, 1630 }); 1631 return self.finishAirBookkeeping(); 1632 } 1633 1634 fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void { 1635 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for riscv64", .{}); 1636 return self.finishAir(inst, result, .{ .none, .none, .none }); 1637 } 1638 1639 fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void { 1640 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for riscv64", .{}); 1641 return self.finishAir(inst, result, .{ .none, .none, .none }); 1642 } 1643 1644 fn airFence(self: *Self) !void { 1645 return self.fail("TODO implement fence() for {}", .{self.target.cpu.arch}); 1646 //return self.finishAirBookkeeping(); 1647 } 1648 1649 fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier) !void { 1650 const mod = self.bin_file.comp.module.?; 1651 if (modifier == .always_tail) return self.fail("TODO implement tail calls for riscv64", .{}); 1652 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 1653 const fn_ty = self.typeOf(pl_op.operand); 1654 const callee = pl_op.operand; 1655 const extra = self.air.extraData(Air.Call, pl_op.payload); 1656 const args: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len]); 1657 1658 var info = try self.resolveCallingConventionValues(fn_ty); 1659 defer info.deinit(self); 1660 1661 // Due to incremental compilation, how function calls are generated depends 1662 // on linking. 1663 if (self.bin_file.cast(link.File.Elf)) |elf_file| { 1664 for (info.args, 0..) |mc_arg, arg_i| { 1665 const arg = args[arg_i]; 1666 const arg_ty = self.typeOf(arg); 1667 const arg_mcv = try self.resolveInst(args[arg_i]); 1668 try self.setValue(arg_ty, mc_arg, arg_mcv); 1669 } 1670 1671 if (try self.air.value(callee, mod)) |func_value| { 1672 switch (mod.intern_pool.indexToKey(func_value.ip_index)) { 1673 .func => |func| { 1674 const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl); 1675 const sym = elf_file.symbol(sym_index); 1676 _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); 1677 const got_addr = sym.zigGotAddress(elf_file); 1678 try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr }); 1679 _ = try self.addInst(.{ 1680 .tag = .jalr, 1681 .data = .{ .i_type = .{ 1682 .rd = .ra, 1683 .rs1 = .ra, 1684 .imm12 = 0, 1685 } }, 1686 }); 1687 }, 1688 .extern_func => { 1689 return self.fail("TODO implement calling extern functions", .{}); 1690 }, 1691 else => { 1692 return self.fail("TODO implement calling bitcasted functions", .{}); 1693 }, 1694 } 1695 } else { 1696 return self.fail("TODO implement calling runtime-known function pointer", .{}); 1697 } 1698 } else if (self.bin_file.cast(link.File.Coff)) |_| { 1699 return self.fail("TODO implement calling in COFF for {}", .{self.target.cpu.arch}); 1700 } else if (self.bin_file.cast(link.File.MachO)) |_| { 1701 unreachable; // unsupported architecture for MachO 1702 } else if (self.bin_file.cast(link.File.Plan9)) |_| { 1703 return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}); 1704 } else unreachable; 1705 1706 const result: MCValue = result: { 1707 switch (info.return_value) { 1708 .register => |reg| { 1709 if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) { 1710 // Save function return value in a callee saved register 1711 break :result try self.copyToNewRegister(inst, info.return_value); 1712 } 1713 }, 1714 else => {}, 1715 } 1716 break :result info.return_value; 1717 }; 1718 1719 if (args.len <= Liveness.bpi - 2) { 1720 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); 1721 buf[0] = callee; 1722 @memcpy(buf[1..][0..args.len], args); 1723 return self.finishAir(inst, result, buf); 1724 } 1725 var bt = try self.iterateBigTomb(inst, 1 + args.len); 1726 bt.feed(callee); 1727 for (args) |arg| { 1728 bt.feed(arg); 1729 } 1730 return bt.finishAir(result); 1731 } 1732 1733 fn ret(self: *Self, mcv: MCValue) !void { 1734 const mod = self.bin_file.comp.module.?; 1735 const ret_ty = self.fn_type.fnReturnType(mod); 1736 try self.setValue(ret_ty, self.ret_mcv, mcv); 1737 1738 // Just add space for an instruction, patch this later 1739 const index = try self.addInst(.{ 1740 .tag = .ret, 1741 .data = .{ .nop = {} }, 1742 }); 1743 1744 try self.exitlude_jump_relocs.append(self.gpa, index); 1745 } 1746 1747 fn airRet(self: *Self, inst: Air.Inst.Index, safety: bool) !void { 1748 if (safety) { 1749 // safe 1750 } else { 1751 // not safe 1752 } 1753 1754 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 1755 const operand = try self.resolveInst(un_op); 1756 1757 _ = try self.addInst(.{ 1758 .tag = .dbg_epilogue_begin, 1759 .data = .{ .nop = {} }, 1760 }); 1761 1762 _ = try self.addInst(.{ 1763 .tag = .psuedo_epilogue, 1764 .data = .{ .nop = {} }, 1765 }); 1766 1767 try self.ret(operand); 1768 1769 return self.finishAir(inst, .dead, .{ un_op, .none, .none }); 1770 } 1771 1772 fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { 1773 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 1774 const ptr = try self.resolveInst(un_op); 1775 _ = ptr; 1776 return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch}); 1777 //return self.finishAir(inst, .dead, .{ un_op, .none, .none }); 1778 } 1779 1780 fn airCmp(self: *Self, inst: Air.Inst.Index) !void { 1781 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; 1782 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 1783 if (self.liveness.isUnused(inst)) 1784 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); 1785 const ty = self.typeOf(bin_op.lhs); 1786 const mod = self.bin_file.comp.module.?; 1787 assert(ty.eql(self.typeOf(bin_op.rhs), mod)); 1788 if (ty.zigTypeTag(mod) == .ErrorSet) 1789 return self.fail("TODO implement cmp for errors", .{}); 1790 1791 const lhs = try self.resolveInst(bin_op.lhs); 1792 const rhs = try self.resolveInst(bin_op.rhs); 1793 const lhs_ty = self.typeOf(bin_op.lhs); 1794 const rhs_ty = self.typeOf(bin_op.rhs); 1795 1796 const result = try self.binOp(tag, null, lhs, rhs, lhs_ty, rhs_ty); 1797 1798 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1799 } 1800 1801 fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { 1802 _ = inst; 1803 return self.fail("TODO implement airCmpVector for {}", .{self.target.cpu.arch}); 1804 } 1805 1806 fn airCmpLtErrorsLen(self: *Self, inst: Air.Inst.Index) !void { 1807 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 1808 const operand = try self.resolveInst(un_op); 1809 _ = operand; 1810 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch}); 1811 return self.finishAir(inst, result, .{ un_op, .none, .none }); 1812 } 1813 1814 fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { 1815 const dbg_stmt = self.air.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt; 1816 1817 _ = try self.addInst(.{ 1818 .tag = .dbg_line, 1819 .data = .{ .dbg_line_column = .{ 1820 .line = dbg_stmt.line, 1821 .column = dbg_stmt.column, 1822 } }, 1823 }); 1824 1825 return self.finishAirBookkeeping(); 1826 } 1827 1828 fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void { 1829 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 1830 const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload); 1831 _ = extra; 1832 // TODO: emit debug info for this block 1833 return self.finishAir(inst, .dead, .{ .none, .none, .none }); 1834 } 1835 1836 fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { 1837 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 1838 const name = self.air.nullTerminatedString(pl_op.payload); 1839 const operand = pl_op.operand; 1840 // TODO emit debug info for this variable 1841 _ = name; 1842 return self.finishAir(inst, .dead, .{ operand, .none, .none }); 1843 } 1844 1845 fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { 1846 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 1847 const cond = try self.resolveInst(pl_op.operand); 1848 const cond_ty = self.typeOf(pl_op.operand); 1849 const extra = self.air.extraData(Air.CondBr, pl_op.payload); 1850 const then_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.then_body_len]); 1851 const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]); 1852 const liveness_condbr = self.liveness.getCondBr(inst); 1853 1854 // A branch to the false section. Uses beq 1855 const reloc = try self.condBr(cond_ty, cond); 1856 1857 // If the condition dies here in this condbr instruction, process 1858 // that death now instead of later as this has an effect on 1859 // whether it needs to be spilled in the branches 1860 if (self.liveness.operandDies(inst, 0)) { 1861 if (pl_op.operand.toIndex()) |op_index| { 1862 self.processDeath(op_index); 1863 } 1864 } 1865 1866 // Save state 1867 const parent_next_stack_offset = self.next_stack_offset; 1868 const parent_free_registers = self.register_manager.free_registers; 1869 var parent_stack = try self.stack.clone(self.gpa); 1870 defer parent_stack.deinit(self.gpa); 1871 const parent_registers = self.register_manager.registers; 1872 1873 try self.branch_stack.append(.{}); 1874 errdefer { 1875 _ = self.branch_stack.pop(); 1876 } 1877 1878 try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len); 1879 for (liveness_condbr.then_deaths) |operand| { 1880 self.processDeath(operand); 1881 } 1882 try self.genBody(then_body); 1883 1884 // Revert to the previous register and stack allocation state. 1885 1886 var saved_then_branch = self.branch_stack.pop(); 1887 defer saved_then_branch.deinit(self.gpa); 1888 1889 self.register_manager.registers = parent_registers; 1890 1891 self.stack.deinit(self.gpa); 1892 self.stack = parent_stack; 1893 parent_stack = .{}; 1894 1895 self.next_stack_offset = parent_next_stack_offset; 1896 self.register_manager.free_registers = parent_free_registers; 1897 1898 try self.performReloc(reloc); 1899 const else_branch = self.branch_stack.addOneAssumeCapacity(); 1900 else_branch.* = .{}; 1901 1902 try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len); 1903 for (liveness_condbr.else_deaths) |operand| { 1904 self.processDeath(operand); 1905 } 1906 try self.genBody(else_body); 1907 1908 // At this point, each branch will possibly have conflicting values for where 1909 // each instruction is stored. They agree, however, on which instructions are alive/dead. 1910 // We use the first ("then") branch as canonical, and here emit 1911 // instructions into the second ("else") branch to make it conform. 1912 // We continue respect the data structure semantic guarantees of the else_branch so 1913 // that we can use all the code emitting abstractions. This is why at the bottom we 1914 // assert that parent_branch.free_registers equals the saved_then_branch.free_registers 1915 // rather than assigning it. 1916 const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2]; 1917 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count()); 1918 const else_slice = else_branch.inst_table.entries.slice(); 1919 const else_keys = else_slice.items(.key); 1920 const else_values = else_slice.items(.value); 1921 for (else_keys, 0..) |else_key, else_idx| { 1922 const else_value = else_values[else_idx]; 1923 const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: { 1924 // The instruction's MCValue is overridden in both branches. 1925 log.debug("condBr put branch table (key = %{d}, value = {})", .{ else_key, then_entry.value }); 1926 parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value); 1927 if (else_value == .dead) { 1928 assert(then_entry.value == .dead); 1929 continue; 1930 } 1931 break :blk then_entry.value; 1932 } else blk: { 1933 if (else_value == .dead) 1934 continue; 1935 // The instruction is only overridden in the else branch. 1936 var i: usize = self.branch_stack.items.len - 2; 1937 while (true) { 1938 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead? 1939 if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| { 1940 assert(mcv != .dead); 1941 break :blk mcv; 1942 } 1943 } 1944 }; 1945 log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv }); 1946 // TODO make sure the destination stack offset / register does not already have something 1947 // going on there. 1948 try self.setValue(self.typeOfIndex(else_key), canon_mcv, else_value); 1949 // TODO track the new register / stack allocation 1950 } 1951 try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count()); 1952 const then_slice = saved_then_branch.inst_table.entries.slice(); 1953 const then_keys = then_slice.items(.key); 1954 const then_values = then_slice.items(.value); 1955 for (then_keys, 0..) |then_key, then_idx| { 1956 const then_value = then_values[then_idx]; 1957 // We already deleted the items from this table that matched the else_branch. 1958 // So these are all instructions that are only overridden in the then branch. 1959 parent_branch.inst_table.putAssumeCapacity(then_key, then_value); 1960 if (then_value == .dead) 1961 continue; 1962 const parent_mcv = blk: { 1963 var i: usize = self.branch_stack.items.len - 2; 1964 while (true) { 1965 i -= 1; 1966 if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| { 1967 assert(mcv != .dead); 1968 break :blk mcv; 1969 } 1970 } 1971 }; 1972 log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value }); 1973 // TODO make sure the destination stack offset / register does not already have something 1974 // going on there. 1975 try self.setValue(self.typeOfIndex(then_key), parent_mcv, then_value); 1976 // TODO track the new register / stack allocation 1977 } 1978 1979 { 1980 var item = self.branch_stack.pop(); 1981 item.deinit(self.gpa); 1982 } 1983 1984 return self.finishAir(inst, .unreach, .{ .none, .none, .none }); 1985 } 1986 1987 fn condBr(self: *Self, cond_ty: Type, condition: MCValue) !Mir.Inst.Index { 1988 _ = cond_ty; 1989 1990 const reg = switch (condition) { 1991 .register => |r| r, 1992 else => try self.copyToTmpRegister(Type.bool, condition), 1993 }; 1994 1995 return try self.addInst(.{ 1996 .tag = .beq, 1997 .data = .{ 1998 .b_type = .{ 1999 .rs1 = reg, 2000 .rs2 = .zero, 2001 .imm12 = 0, // patched later. 2002 }, 2003 }, 2004 }); 2005 } 2006 2007 fn isNull(self: *Self, operand: MCValue) !MCValue { 2008 _ = operand; 2009 // Here you can specialize this instruction if it makes sense to, otherwise the default 2010 // will call isNonNull and invert the result. 2011 return self.fail("TODO call isNonNull and invert the result", .{}); 2012 } 2013 2014 fn isNonNull(self: *Self, operand: MCValue) !MCValue { 2015 _ = operand; 2016 // Here you can specialize this instruction if it makes sense to, otherwise the default 2017 // will call isNull and invert the result. 2018 return self.fail("TODO call isNull and invert the result", .{}); 2019 } 2020 2021 fn isErr(self: *Self, operand: MCValue) !MCValue { 2022 _ = operand; 2023 // Here you can specialize this instruction if it makes sense to, otherwise the default 2024 // will call isNonNull and invert the result. 2025 return self.fail("TODO call isNonErr and invert the result", .{}); 2026 } 2027 2028 fn isNonErr(self: *Self, operand: MCValue) !MCValue { 2029 _ = operand; 2030 // Here you can specialize this instruction if it makes sense to, otherwise the default 2031 // will call isNull and invert the result. 2032 return self.fail("TODO call isErr and invert the result", .{}); 2033 } 2034 2035 fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { 2036 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 2037 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 2038 const operand = try self.resolveInst(un_op); 2039 break :result try self.isNull(operand); 2040 }; 2041 return self.finishAir(inst, result, .{ un_op, .none, .none }); 2042 } 2043 2044 fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { 2045 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 2046 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 2047 const operand_ptr = try self.resolveInst(un_op); 2048 const operand: MCValue = blk: { 2049 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { 2050 // The MCValue that holds the pointer can be re-used as the value. 2051 break :blk operand_ptr; 2052 } else { 2053 break :blk try self.allocRegOrMem(inst, true); 2054 } 2055 }; 2056 try self.load(operand, operand_ptr, self.typeOf(un_op)); 2057 break :result try self.isNull(operand); 2058 }; 2059 return self.finishAir(inst, result, .{ un_op, .none, .none }); 2060 } 2061 2062 fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { 2063 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 2064 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 2065 const operand = try self.resolveInst(un_op); 2066 break :result try self.isNonNull(operand); 2067 }; 2068 return self.finishAir(inst, result, .{ un_op, .none, .none }); 2069 } 2070 2071 fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { 2072 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 2073 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 2074 const operand_ptr = try self.resolveInst(un_op); 2075 const operand: MCValue = blk: { 2076 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { 2077 // The MCValue that holds the pointer can be re-used as the value. 2078 break :blk operand_ptr; 2079 } else { 2080 break :blk try self.allocRegOrMem(inst, true); 2081 } 2082 }; 2083 try self.load(operand, operand_ptr, self.typeOf(un_op)); 2084 break :result try self.isNonNull(operand); 2085 }; 2086 return self.finishAir(inst, result, .{ un_op, .none, .none }); 2087 } 2088 2089 fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { 2090 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 2091 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 2092 const operand = try self.resolveInst(un_op); 2093 break :result try self.isErr(operand); 2094 }; 2095 return self.finishAir(inst, result, .{ un_op, .none, .none }); 2096 } 2097 2098 fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { 2099 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 2100 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 2101 const operand_ptr = try self.resolveInst(un_op); 2102 const operand: MCValue = blk: { 2103 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { 2104 // The MCValue that holds the pointer can be re-used as the value. 2105 break :blk operand_ptr; 2106 } else { 2107 break :blk try self.allocRegOrMem(inst, true); 2108 } 2109 }; 2110 try self.load(operand, operand_ptr, self.typeOf(un_op)); 2111 break :result try self.isErr(operand); 2112 }; 2113 return self.finishAir(inst, result, .{ un_op, .none, .none }); 2114 } 2115 2116 fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { 2117 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 2118 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 2119 const operand = try self.resolveInst(un_op); 2120 break :result try self.isNonErr(operand); 2121 }; 2122 return self.finishAir(inst, result, .{ un_op, .none, .none }); 2123 } 2124 2125 fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { 2126 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 2127 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 2128 const operand_ptr = try self.resolveInst(un_op); 2129 const operand: MCValue = blk: { 2130 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { 2131 // The MCValue that holds the pointer can be re-used as the value. 2132 break :blk operand_ptr; 2133 } else { 2134 break :blk try self.allocRegOrMem(inst, true); 2135 } 2136 }; 2137 try self.load(operand, operand_ptr, self.typeOf(un_op)); 2138 break :result try self.isNonErr(operand); 2139 }; 2140 return self.finishAir(inst, result, .{ un_op, .none, .none }); 2141 } 2142 2143 fn airLoop(self: *Self, inst: Air.Inst.Index) !void { 2144 // A loop is a setup to be able to jump back to the beginning. 2145 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 2146 const loop = self.air.extraData(Air.Block, ty_pl.payload); 2147 const body: []const Air.Inst.Index = @ptrCast(self.air.extra[loop.end..][0..loop.data.body_len]); 2148 2149 const start_index: Mir.Inst.Index = @intCast(self.code.items.len); 2150 2151 try self.genBody(body); 2152 try self.jump(start_index); 2153 2154 return self.finishAirBookkeeping(); 2155 } 2156 2157 /// Send control flow to the `index` of `self.code`. 2158 fn jump(self: *Self, index: Mir.Inst.Index) !void { 2159 _ = try self.addInst(.{ 2160 .tag = .j, 2161 .data = .{ 2162 .inst = index, 2163 }, 2164 }); 2165 } 2166 2167 fn airBlock(self: *Self, inst: Air.Inst.Index) !void { 2168 try self.blocks.putNoClobber(self.gpa, inst, .{ 2169 // A block is a setup to be able to jump to the end. 2170 .relocs = .{}, 2171 // It also acts as a receptacle for break operands. 2172 // Here we use `MCValue.none` to represent a null value so that the first 2173 // break instruction will choose a MCValue for the block result and overwrite 2174 // this field. Following break instructions will use that MCValue to put their 2175 // block results. 2176 .mcv = MCValue{ .none = {} }, 2177 }); 2178 defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); 2179 2180 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 2181 const extra = self.air.extraData(Air.Block, ty_pl.payload); 2182 const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]); 2183 // TODO emit debug info lexical block 2184 try self.genBody(body); 2185 2186 for (self.blocks.getPtr(inst).?.relocs.items) |reloc| { 2187 try self.performReloc(reloc); 2188 } 2189 2190 const result = self.blocks.getPtr(inst).?.mcv; 2191 return self.finishAir(inst, result, .{ .none, .none, .none }); 2192 } 2193 2194 fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { 2195 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 2196 const condition = pl_op.operand; 2197 _ = condition; 2198 return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch}); 2199 // return self.finishAir(inst, .dead, .{ condition, .none, .none }); 2200 } 2201 2202 fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { 2203 const tag = self.mir_instructions.items(.tag)[inst]; 2204 2205 switch (tag) { 2206 .beq => self.mir_instructions.items(.data)[inst].b_type.imm12 = @intCast(inst), 2207 else => return self.fail("TODO: performReloc {s}", .{@tagName(tag)}), 2208 } 2209 } 2210 2211 fn airBr(self: *Self, inst: Air.Inst.Index) !void { 2212 const branch = self.air.instructions.items(.data)[@intFromEnum(inst)].br; 2213 try self.br(branch.block_inst, branch.operand); 2214 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); 2215 } 2216 2217 fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void { 2218 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 2219 const air_tags = self.air.instructions.items(.tag); 2220 _ = air_tags; 2221 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement boolean operations for {}", .{self.target.cpu.arch}); 2222 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 2223 } 2224 2225 fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { 2226 const block_data = self.blocks.getPtr(block).?; 2227 2228 const mod = self.bin_file.comp.module.?; 2229 if (self.typeOf(operand).hasRuntimeBits(mod)) { 2230 const operand_mcv = try self.resolveInst(operand); 2231 const block_mcv = block_data.mcv; 2232 if (block_mcv == .none) { 2233 block_data.mcv = operand_mcv; 2234 } else { 2235 try self.setValue(self.typeOfIndex(block), block_mcv, operand_mcv); 2236 } 2237 } 2238 return self.brVoid(block); 2239 } 2240 2241 fn brVoid(self: *Self, block: Air.Inst.Index) !void { 2242 const block_data = self.blocks.getPtr(block).?; 2243 2244 // Emit a jump with a relocation. It will be patched up after the block ends. 2245 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1); 2246 2247 block_data.relocs.appendAssumeCapacity(try self.addInst(.{ 2248 .tag = .jal, 2249 .data = .{ 2250 .j_type = .{ 2251 .rd = .ra, 2252 .imm21 = undefined, // populated later through performReloc 2253 }, 2254 }, 2255 })); 2256 } 2257 2258 fn airAsm(self: *Self, inst: Air.Inst.Index) !void { 2259 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 2260 const extra = self.air.extraData(Air.Asm, ty_pl.payload); 2261 const is_volatile = @as(u1, @truncate(extra.data.flags >> 31)) != 0; 2262 const clobbers_len: u31 = @truncate(extra.data.flags); 2263 var extra_i: usize = extra.end; 2264 const outputs: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra_i..][0..extra.data.outputs_len]); 2265 extra_i += outputs.len; 2266 const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra_i..][0..extra.data.inputs_len]); 2267 extra_i += inputs.len; 2268 2269 const dead = !is_volatile and self.liveness.isUnused(inst); 2270 const result: MCValue = if (dead) .dead else result: { 2271 if (outputs.len > 1) { 2272 return self.fail("TODO implement codegen for asm with more than 1 output", .{}); 2273 } 2274 2275 const output_constraint: ?[]const u8 = for (outputs) |output| { 2276 if (output != .none) { 2277 return self.fail("TODO implement codegen for non-expr asm", .{}); 2278 } 2279 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); 2280 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); 2281 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); 2282 // This equation accounts for the fact that even if we have exactly 4 bytes 2283 // for the string, we still use the next u32 for the null terminator. 2284 extra_i += (constraint.len + name.len + (2 + 3)) / 4; 2285 2286 break constraint; 2287 } else null; 2288 2289 for (inputs) |input| { 2290 const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); 2291 const constraint = std.mem.sliceTo(input_bytes, 0); 2292 const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); 2293 // This equation accounts for the fact that even if we have exactly 4 bytes 2294 // for the string, we still use the next u32 for the null terminator. 2295 extra_i += (constraint.len + name.len + (2 + 3)) / 4; 2296 2297 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { 2298 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); 2299 } 2300 const reg_name = constraint[1 .. constraint.len - 1]; 2301 const reg = parseRegName(reg_name) orelse 2302 return self.fail("unrecognized register: '{s}'", .{reg_name}); 2303 2304 const arg_mcv = try self.resolveInst(input); 2305 try self.register_manager.getReg(reg, null); 2306 try self.genSetReg(self.typeOf(input), reg, arg_mcv); 2307 } 2308 2309 { 2310 var clobber_i: u32 = 0; 2311 while (clobber_i < clobbers_len) : (clobber_i += 1) { 2312 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); 2313 // This equation accounts for the fact that even if we have exactly 4 bytes 2314 // for the string, we still use the next u32 for the null terminator. 2315 extra_i += clobber.len / 4 + 1; 2316 2317 // TODO honor these 2318 } 2319 } 2320 2321 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; 2322 2323 if (mem.eql(u8, asm_source, "ecall")) { 2324 _ = try self.addInst(.{ 2325 .tag = .ecall, 2326 .data = .{ .nop = {} }, 2327 }); 2328 } else { 2329 return self.fail("TODO implement support for more riscv64 assembly instructions", .{}); 2330 } 2331 2332 if (output_constraint) |output| { 2333 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { 2334 return self.fail("unrecognized asm output constraint: '{s}'", .{output}); 2335 } 2336 const reg_name = output[2 .. output.len - 1]; 2337 const reg = parseRegName(reg_name) orelse 2338 return self.fail("unrecognized register: '{s}'", .{reg_name}); 2339 break :result MCValue{ .register = reg }; 2340 } else { 2341 break :result MCValue{ .none = {} }; 2342 } 2343 }; 2344 simple: { 2345 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); 2346 var buf_index: usize = 0; 2347 for (outputs) |output| { 2348 if (output == .none) continue; 2349 2350 if (buf_index >= buf.len) break :simple; 2351 buf[buf_index] = output; 2352 buf_index += 1; 2353 } 2354 if (buf_index + inputs.len > buf.len) break :simple; 2355 @memcpy(buf[buf_index..][0..inputs.len], inputs); 2356 return self.finishAir(inst, result, buf); 2357 } 2358 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); 2359 for (outputs) |output| { 2360 if (output == .none) continue; 2361 2362 bt.feed(output); 2363 } 2364 for (inputs) |input| { 2365 bt.feed(input); 2366 } 2367 return bt.finishAir(result); 2368 } 2369 2370 fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb { 2371 try self.ensureProcessDeathCapacity(operand_count + 1); 2372 return BigTomb{ 2373 .function = self, 2374 .inst = inst, 2375 .lbt = self.liveness.iterateBigTomb(inst), 2376 }; 2377 } 2378 2379 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata. 2380 fn setValue(self: *Self, ty: Type, dst_val: MCValue, src_val: MCValue) !void { 2381 // There isn't anything to store 2382 if (dst_val == .none) return; 2383 2384 if (!dst_val.isMutable()) { 2385 return std.debug.panic("tried to setValue immutable: {s}", .{@tagName(dst_val)}); 2386 } 2387 2388 switch (dst_val) { 2389 .register => |reg| return self.genSetReg(ty, reg, src_val), 2390 .stack_offset => |off| return self.genSetStack(ty, off, src_val), 2391 .memory => |addr| return self.genSetMem(ty, addr, src_val), 2392 else => return self.fail("TODO: setValue {s}", .{@tagName(dst_val)}), 2393 } 2394 } 2395 2396 /// Sets the value of `src_val` into stack memory at `stack_offset`. 2397 fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_val: MCValue) InnerError!void { 2398 const mod = self.bin_file.comp.module.?; 2399 const abi_size: u32 = @intCast(ty.abiSize(mod)); 2400 2401 switch (src_val) { 2402 .none => return, 2403 .dead => unreachable, 2404 .immediate => { 2405 const reg = try self.copyToTmpRegister(ty, src_val); 2406 return self.genSetStack(ty, stack_offset, .{ .register = reg }); 2407 }, 2408 .register => |reg| { 2409 switch (abi_size) { 2410 1, 2, 4, 8 => { 2411 assert(std.mem.isAlignedGeneric(u32, stack_offset, abi_size)); 2412 2413 const tag: Mir.Inst.Tag = switch (abi_size) { 2414 1 => .sb, 2415 2 => .sh, 2416 4 => .sw, 2417 8 => .sd, 2418 else => unreachable, 2419 }; 2420 2421 _ = try self.addInst(.{ 2422 .tag = tag, 2423 .data = .{ .i_type = .{ 2424 .rd = reg, 2425 .rs1 = .s0, 2426 .imm12 = math.cast(i12, stack_offset) orelse { 2427 return self.fail("TODO: genSetStack bigger stack values", .{}); 2428 }, 2429 } }, 2430 }); 2431 }, 2432 else => return self.fail("TODO: genSetStack for size={d}", .{abi_size}), 2433 } 2434 }, 2435 .stack_offset, .load_symbol => { 2436 if (abi_size <= 8) { 2437 const reg = try self.copyToTmpRegister(ty, src_val); 2438 return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); 2439 } 2440 2441 const ptr_ty = try mod.singleMutPtrType(ty); 2442 2443 // TODO call extern memcpy 2444 const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp); 2445 const regs_locks = self.register_manager.lockRegsAssumeUnused(5, regs); 2446 defer for (regs_locks) |reg| { 2447 self.register_manager.unlockReg(reg); 2448 }; 2449 2450 const src_reg = regs[0]; 2451 const dst_reg = regs[1]; 2452 const len_reg = regs[2]; 2453 const count_reg = regs[3]; 2454 const tmp_reg = regs[4]; 2455 2456 switch (src_val) { 2457 .stack_offset => |offset| { 2458 if (offset == stack_offset) return; 2459 try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = offset }); 2460 }, 2461 .load_symbol => |sym_off| { 2462 const atom_index = atom: { 2463 const decl_index = mod.funcOwnerDeclIndex(self.func_index); 2464 2465 if (self.bin_file.cast(link.File.Elf)) |elf_file| { 2466 const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index); 2467 break :atom atom_index; 2468 } else return self.fail("TODO genSetStack for {s}", .{@tagName(self.bin_file.tag)}); 2469 }; 2470 2471 _ = try self.addInst(.{ 2472 .tag = .load_symbol, 2473 .data = .{ 2474 .payload = try self.addExtra(Mir.LoadSymbolPayload{ 2475 .register = @intFromEnum(src_reg), 2476 .atom_index = atom_index, 2477 .sym_index = sym_off.sym, 2478 }), 2479 }, 2480 }); 2481 }, 2482 else => return self.fail("TODO: genSetStack unreachable {s}", .{@tagName(src_val)}), 2483 } 2484 2485 try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset }); 2486 try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size }); 2487 2488 // memcpy(src, dst, len) 2489 try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); 2490 }, 2491 else => return self.fail("TODO: genSetStack {s}", .{@tagName(src_val)}), 2492 } 2493 } 2494 2495 fn genSetMem(self: *Self, ty: Type, addr: u64, src_val: MCValue) InnerError!void { 2496 const mod = self.bin_file.comp.module.?; 2497 const abi_size: u32 = @intCast(ty.abiSize(mod)); 2498 _ = abi_size; 2499 _ = addr; 2500 _ = src_val; 2501 2502 return self.fail("TODO: genSetMem", .{}); 2503 } 2504 2505 fn genInlineMemcpy( 2506 self: *Self, 2507 src: Register, 2508 dst: Register, 2509 len: Register, 2510 count: Register, 2511 tmp: Register, 2512 ) !void { 2513 _ = src; 2514 _ = dst; 2515 _ = len; 2516 _ = count; 2517 _ = tmp; 2518 2519 return self.fail("TODO: genInlineMemcpy", .{}); 2520 } 2521 2522 /// Sets the value of `src_val` into `reg`. Assumes you have a lock on it. 2523 fn genSetReg(self: *Self, ty: Type, reg: Register, src_val: MCValue) InnerError!void { 2524 const mod = self.bin_file.comp.module.?; 2525 const abi_size: u32 = @intCast(ty.abiSize(mod)); 2526 2527 switch (src_val) { 2528 .dead => unreachable, 2529 .ptr_stack_offset => return self.fail("TODO genSetReg ptr_stack_offset", .{}), 2530 .unreach, .none => return, // Nothing to do. 2531 .undef => { 2532 if (!self.wantSafety()) 2533 return; // The already existing value will do just fine. 2534 // Write the debug undefined value. 2535 return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); 2536 }, 2537 .immediate => |unsigned_x| { 2538 const x: i64 = @bitCast(unsigned_x); 2539 if (math.minInt(i12) <= x and x <= math.maxInt(i12)) { 2540 _ = try self.addInst(.{ 2541 .tag = .addi, 2542 .data = .{ .i_type = .{ 2543 .rd = reg, 2544 .rs1 = .zero, 2545 .imm12 = @intCast(x), 2546 } }, 2547 }); 2548 } else if (math.minInt(i32) <= x and x <= math.maxInt(i32)) { 2549 const lo12: i12 = @truncate(x); 2550 const carry: i32 = if (lo12 < 0) 1 else 0; 2551 const hi20: i20 = @truncate((x >> 12) +% carry); 2552 2553 // TODO: add test case for 32-bit immediate 2554 _ = try self.addInst(.{ 2555 .tag = .lui, 2556 .data = .{ .u_type = .{ 2557 .rd = reg, 2558 .imm20 = hi20, 2559 } }, 2560 }); 2561 _ = try self.addInst(.{ 2562 .tag = .addi, 2563 .data = .{ .i_type = .{ 2564 .rd = reg, 2565 .rs1 = reg, 2566 .imm12 = lo12, 2567 } }, 2568 }); 2569 } else { 2570 // li rd, immediate 2571 // "Myriad sequences" 2572 return self.fail("TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf 2573 } 2574 }, 2575 .register => |src_reg| { 2576 // If the registers are the same, nothing to do. 2577 if (src_reg.id() == reg.id()) 2578 return; 2579 2580 // mov reg, src_reg 2581 _ = try self.addInst(.{ 2582 .tag = .mv, 2583 .data = .{ .rr = .{ 2584 .rd = reg, 2585 .rs = src_reg, 2586 } }, 2587 }); 2588 }, 2589 .memory => |addr| { 2590 try self.genSetReg(ty, reg, .{ .immediate = addr }); 2591 2592 _ = try self.addInst(.{ 2593 .tag = .ld, 2594 .data = .{ .i_type = .{ 2595 .rd = reg, 2596 .rs1 = reg, 2597 .imm12 = 0, 2598 } }, 2599 }); 2600 2601 // LOAD imm=[i12 offset = 0], rs1 2602 }, 2603 .stack_offset => |off| { 2604 const tag: Mir.Inst.Tag = switch (abi_size) { 2605 1 => .lb, 2606 2 => .lh, 2607 4 => .lw, 2608 8 => .ld, 2609 else => return self.fail("TODO: genSetReg for size {d}", .{abi_size}), 2610 }; 2611 2612 _ = try self.addInst(.{ 2613 .tag = tag, 2614 .data = .{ .i_type = .{ 2615 .rd = reg, 2616 .rs1 = .s0, 2617 .imm12 = math.cast(i12, off) orelse { 2618 return self.fail("TODO: genSetReg support larger stack sizes", .{}); 2619 }, 2620 } }, 2621 }); 2622 }, 2623 .load_symbol => |sym_off| { 2624 assert(sym_off.off == 0); 2625 2626 const decl_index = mod.funcOwnerDeclIndex(self.func_index); 2627 2628 const atom_index = switch (self.bin_file.tag) { 2629 .elf => blk: { 2630 const elf_file = self.bin_file.cast(link.File.Elf).?; 2631 const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index); 2632 break :blk atom_index; 2633 }, 2634 else => return self.fail("TODO genSetReg load_symbol for {s}", .{@tagName(self.bin_file.tag)}), 2635 }; 2636 _ = try self.addInst(.{ 2637 .tag = .load_symbol, 2638 .data = .{ 2639 .payload = try self.addExtra(Mir.LoadSymbolPayload{ 2640 .register = @intFromEnum(reg), 2641 .atom_index = atom_index, 2642 .sym_index = sym_off.sym, 2643 }), 2644 }, 2645 }); 2646 }, 2647 } 2648 } 2649 2650 fn airIntFromPtr(self: *Self, inst: Air.Inst.Index) !void { 2651 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 2652 const result = try self.resolveInst(un_op); 2653 return self.finishAir(inst, result, .{ un_op, .none, .none }); 2654 } 2655 2656 fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { 2657 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 2658 const result = if (self.liveness.isUnused(inst)) .dead else result: { 2659 const operand = try self.resolveInst(ty_op.operand); 2660 if (self.reuseOperand(inst, ty_op.operand, 0, operand)) break :result operand; 2661 2662 const operand_lock = switch (operand) { 2663 .register => |reg| self.register_manager.lockReg(reg), 2664 else => null, 2665 }; 2666 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); 2667 2668 const dest = try self.allocRegOrMem(inst, true); 2669 try self.setValue(self.typeOfIndex(inst), dest, operand); 2670 break :result dest; 2671 }; 2672 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2673 } 2674 2675 fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { 2676 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 2677 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airArrayToSlice for {}", .{ 2678 self.target.cpu.arch, 2679 }); 2680 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2681 } 2682 2683 fn airFloatFromInt(self: *Self, inst: Air.Inst.Index) !void { 2684 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 2685 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatFromInt for {}", .{ 2686 self.target.cpu.arch, 2687 }); 2688 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2689 } 2690 2691 fn airIntFromFloat(self: *Self, inst: Air.Inst.Index) !void { 2692 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 2693 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntFromFloat for {}", .{ 2694 self.target.cpu.arch, 2695 }); 2696 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2697 } 2698 2699 fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { 2700 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 2701 const extra = self.air.extraData(Air.Block, ty_pl.payload); 2702 _ = extra; 2703 return self.fail("TODO implement airCmpxchg for {}", .{ 2704 self.target.cpu.arch, 2705 }); 2706 // return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value }); 2707 } 2708 2709 fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void { 2710 _ = inst; 2711 return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch}); 2712 } 2713 2714 fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void { 2715 _ = inst; 2716 return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch}); 2717 } 2718 2719 fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void { 2720 _ = inst; 2721 _ = order; 2722 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); 2723 } 2724 2725 fn airMemset(self: *Self, inst: Air.Inst.Index, safety: bool) !void { 2726 _ = inst; 2727 if (safety) { 2728 // TODO if the value is undef, write 0xaa bytes to dest 2729 } else { 2730 // TODO if the value is undef, don't lower this instruction 2731 } 2732 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); 2733 } 2734 2735 fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { 2736 _ = inst; 2737 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); 2738 } 2739 2740 fn airTagName(self: *Self, inst: Air.Inst.Index) !void { 2741 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 2742 const operand = try self.resolveInst(un_op); 2743 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { 2744 _ = operand; 2745 return self.fail("TODO implement airTagName for riscv64", .{}); 2746 }; 2747 return self.finishAir(inst, result, .{ un_op, .none, .none }); 2748 } 2749 2750 fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { 2751 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 2752 const operand = try self.resolveInst(un_op); 2753 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { 2754 _ = operand; 2755 return self.fail("TODO implement airErrorName for riscv64", .{}); 2756 }; 2757 return self.finishAir(inst, result, .{ un_op, .none, .none }); 2758 } 2759 2760 fn airSplat(self: *Self, inst: Air.Inst.Index) !void { 2761 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 2762 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for riscv64", .{}); 2763 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2764 } 2765 2766 fn airSelect(self: *Self, inst: Air.Inst.Index) !void { 2767 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 2768 const extra = self.air.extraData(Air.Bin, pl_op.payload).data; 2769 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSelect for riscv64", .{}); 2770 return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs }); 2771 } 2772 2773 fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { 2774 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 2775 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for riscv64", .{}); 2776 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2777 } 2778 2779 fn airReduce(self: *Self, inst: Air.Inst.Index) !void { 2780 const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce; 2781 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airReduce for riscv64", .{}); 2782 return self.finishAir(inst, result, .{ reduce.operand, .none, .none }); 2783 } 2784 2785 fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { 2786 const mod = self.bin_file.comp.module.?; 2787 const vector_ty = self.typeOfIndex(inst); 2788 const len = vector_ty.vectorLen(mod); 2789 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 2790 const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra[ty_pl.payload..][0..len]); 2791 const result: MCValue = res: { 2792 if (self.liveness.isUnused(inst)) break :res MCValue.dead; 2793 return self.fail("TODO implement airAggregateInit for riscv64", .{}); 2794 }; 2795 2796 if (elements.len <= Liveness.bpi - 1) { 2797 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); 2798 @memcpy(buf[0..elements.len], elements); 2799 return self.finishAir(inst, result, buf); 2800 } 2801 var bt = try self.iterateBigTomb(inst, elements.len); 2802 for (elements) |elem| { 2803 bt.feed(elem); 2804 } 2805 return bt.finishAir(result); 2806 } 2807 2808 fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void { 2809 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 2810 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; 2811 _ = extra; 2812 return self.fail("TODO implement airUnionInit for riscv64", .{}); 2813 // return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value }); 2814 } 2815 2816 fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void { 2817 const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch; 2818 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none }); 2819 } 2820 2821 fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { 2822 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 2823 const extra = self.air.extraData(Air.Bin, pl_op.payload).data; 2824 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { 2825 return self.fail("TODO implement airMulAdd for riscv64", .{}); 2826 }; 2827 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); 2828 } 2829 2830 fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { 2831 const mod = self.bin_file.comp.module.?; 2832 2833 // If the type has no codegen bits, no need to store it. 2834 const inst_ty = self.typeOf(inst); 2835 if (!inst_ty.hasRuntimeBits(mod)) 2836 return MCValue{ .none = {} }; 2837 2838 const inst_index = inst.toIndex() orelse return self.genTypedValue((try self.air.value(inst, mod)).?); 2839 2840 return self.getResolvedInstValue(inst_index); 2841 } 2842 2843 fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { 2844 // Treat each stack item as a "layer" on top of the previous one. 2845 var i: usize = self.branch_stack.items.len; 2846 while (true) { 2847 i -= 1; 2848 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| { 2849 assert(mcv != .dead); 2850 return mcv; 2851 } 2852 } 2853 } 2854 2855 fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { 2856 const mod = self.bin_file.comp.module.?; 2857 const mcv: MCValue = switch (try codegen.genTypedValue( 2858 self.bin_file, 2859 self.src_loc, 2860 val, 2861 mod.funcOwnerDeclIndex(self.func_index), 2862 )) { 2863 .mcv => |mcv| switch (mcv) { 2864 .none => .none, 2865 .undef => .undef, 2866 .load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } }, 2867 .immediate => |imm| .{ .immediate = imm }, 2868 .memory => |addr| .{ .memory = addr }, 2869 .load_got, .load_direct, .load_tlv => { 2870 return self.fail("TODO: genTypedValue {s}", .{@tagName(mcv)}); 2871 }, 2872 }, 2873 .fail => |msg| { 2874 self.err_msg = msg; 2875 return error.CodegenFail; 2876 }, 2877 }; 2878 return mcv; 2879 } 2880 2881 const CallMCValues = struct { 2882 args: []MCValue, 2883 return_value: MCValue, 2884 stack_byte_count: u32, 2885 stack_align: Alignment, 2886 2887 fn deinit(self: *CallMCValues, func: *Self) void { 2888 func.gpa.free(self.args); 2889 self.* = undefined; 2890 } 2891 }; 2892 2893 /// Caller must call `CallMCValues.deinit`. 2894 fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { 2895 const mod = self.bin_file.comp.module.?; 2896 const fn_info = mod.typeToFunc(fn_ty).?; 2897 const cc = fn_info.cc; 2898 var result: CallMCValues = .{ 2899 .args = try self.gpa.alloc(MCValue, fn_info.param_types.len), 2900 // These undefined values must be populated before returning from this function. 2901 .return_value = undefined, 2902 .stack_byte_count = undefined, 2903 .stack_align = undefined, 2904 }; 2905 errdefer self.gpa.free(result.args); 2906 2907 const ret_ty = fn_ty.fnReturnType(mod); 2908 2909 switch (cc) { 2910 .Naked => { 2911 assert(result.args.len == 0); 2912 result.return_value = .{ .unreach = {} }; 2913 result.stack_byte_count = 0; 2914 result.stack_align = .@"1"; 2915 return result; 2916 }, 2917 .Unspecified, .C => { 2918 if (result.args.len > 8) { 2919 return self.fail("TODO: support more than 8 function args", .{}); 2920 } 2921 2922 for (0..result.args.len) |i| { 2923 const arg_reg = try self.register_manager.allocReg(null, fa); 2924 result.args[i] = .{ .register = arg_reg }; 2925 } 2926 2927 // stack_offset = num s registers spilled + local var space 2928 var stack_offset: u32 = 0; 2929 _ = &stack_offset; 2930 // TODO: spill used s registers here 2931 2932 result.stack_byte_count = stack_offset; 2933 result.stack_align = .@"16"; 2934 }, 2935 else => return self.fail("TODO implement function parameters for {} on riscv64", .{cc}), 2936 } 2937 2938 if (ret_ty.zigTypeTag(mod) == .NoReturn) { 2939 result.return_value = .{ .unreach = {} }; 2940 } else if (!ret_ty.hasRuntimeBits(mod)) { 2941 result.return_value = .{ .none = {} }; 2942 } else switch (cc) { 2943 .Naked => unreachable, 2944 .Unspecified, .C => { 2945 const ret_ty_size: u32 = @intCast(ret_ty.abiSize(mod)); 2946 if (ret_ty_size <= 8) { 2947 result.return_value = .{ .register = .a0 }; 2948 } else if (ret_ty_size <= 16) { 2949 return self.fail("TODO support MCValue 2 registers", .{}); 2950 } else { 2951 return self.fail("TODO support return by reference", .{}); 2952 } 2953 }, 2954 else => return self.fail("TODO implement function return values for {}", .{cc}), 2955 } 2956 return result; 2957 } 2958 2959 /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`. 2960 fn wantSafety(self: *Self) bool { 2961 return switch (self.bin_file.comp.root_mod.optimize_mode) { 2962 .Debug => true, 2963 .ReleaseSafe => true, 2964 .ReleaseFast => false, 2965 .ReleaseSmall => false, 2966 }; 2967 } 2968 2969 fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { 2970 @setCold(true); 2971 assert(self.err_msg == null); 2972 self.err_msg = try ErrorMsg.create(self.gpa, self.src_loc, format, args); 2973 return error.CodegenFail; 2974 } 2975 2976 fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError { 2977 @setCold(true); 2978 assert(self.err_msg == null); 2979 self.err_msg = try ErrorMsg.create(self.gpa, self.src_loc, format, args); 2980 return error.CodegenFail; 2981 } 2982 2983 fn parseRegName(name: []const u8) ?Register { 2984 if (@hasDecl(Register, "parseRegName")) { 2985 return Register.parseRegName(name); 2986 } 2987 return std.meta.stringToEnum(Register, name); 2988 } 2989 2990 fn typeOf(self: *Self, inst: Air.Inst.Ref) Type { 2991 const mod = self.bin_file.comp.module.?; 2992 return self.air.typeOf(inst, &mod.intern_pool); 2993 } 2994 2995 fn typeOfIndex(self: *Self, inst: Air.Inst.Index) Type { 2996 const mod = self.bin_file.comp.module.?; 2997 return self.air.typeOfIndex(inst, &mod.intern_pool); 2998 }