blob 1604f045 (110036B) - Raw
1 const std = @import("std"); 2 const builtin = @import("builtin"); 3 const mem = std.mem; 4 const math = std.math; 5 const assert = std.debug.assert; 6 const Air = @import("../../Air.zig"); 7 const Mir = @import("Mir.zig"); 8 const Emit = @import("Emit.zig"); 9 const Liveness = @import("../../Liveness.zig"); 10 const Type = @import("../../type.zig").Type; 11 const Value = @import("../../value.zig").Value; 12 const TypedValue = @import("../../TypedValue.zig"); 13 const link = @import("../../link.zig"); 14 const Module = @import("../../Module.zig"); 15 const Compilation = @import("../../Compilation.zig"); 16 const ErrorMsg = Module.ErrorMsg; 17 const Target = std.Target; 18 const Allocator = mem.Allocator; 19 const trace = @import("../../tracy.zig").trace; 20 const DW = std.dwarf; 21 const leb128 = std.leb; 22 const log = std.log.scoped(.codegen); 23 const build_options = @import("build_options"); 24 const RegisterManagerFn = @import("../../register_manager.zig").RegisterManager; 25 const RegisterManager = RegisterManagerFn(Self, Register, &callee_preserved_regs); 26 27 const FnResult = @import("../../codegen.zig").FnResult; 28 const GenerateSymbolError = @import("../../codegen.zig").GenerateSymbolError; 29 const DebugInfoOutput = @import("../../codegen.zig").DebugInfoOutput; 30 31 const bits = @import("bits.zig"); 32 const abi = @import("abi.zig"); 33 const Register = bits.Register; 34 const Instruction = abi.Instruction; 35 const callee_preserved_regs = abi.callee_preserved_regs; 36 37 const InnerError = error{ 38 OutOfMemory, 39 CodegenFail, 40 OutOfRegisters, 41 }; 42 43 gpa: Allocator, 44 air: Air, 45 liveness: Liveness, 46 bin_file: *link.File, 47 target: *const std.Target, 48 mod_fn: *const Module.Fn, 49 code: *std.ArrayList(u8), 50 debug_output: DebugInfoOutput, 51 err_msg: ?*ErrorMsg, 52 args: []MCValue, 53 ret_mcv: MCValue, 54 fn_type: Type, 55 arg_index: usize, 56 src_loc: Module.SrcLoc, 57 stack_align: u32, 58 59 /// MIR Instructions 60 mir_instructions: std.MultiArrayList(Mir.Inst) = .{}, 61 /// MIR extra data 62 mir_extra: std.ArrayListUnmanaged(u32) = .{}, 63 64 /// Byte offset within the source file of the ending curly. 65 end_di_line: u32, 66 end_di_column: u32, 67 68 /// The value is an offset into the `Function` `code` from the beginning. 69 /// To perform the reloc, write 32-bit signed little-endian integer 70 /// which is a relative jump, based on the address following the reloc. 71 exitlude_jump_relocs: std.ArrayListUnmanaged(usize) = .{}, 72 73 /// Whenever there is a runtime branch, we push a Branch onto this stack, 74 /// and pop it off when the runtime branch joins. This provides an "overlay" 75 /// of the table of mappings from instructions to `MCValue` from within the branch. 76 /// This way we can modify the `MCValue` for an instruction in different ways 77 /// within different branches. Special consideration is needed when a branch 78 /// joins with its parent, to make sure all instructions have the same MCValue 79 /// across each runtime branch upon joining. 80 branch_stack: *std.ArrayList(Branch), 81 82 // Key is the block instruction 83 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .{}, 84 85 register_manager: RegisterManager = .{}, 86 /// Maps offset to what is stored there. 87 stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, 88 89 /// Offset from the stack base, representing the end of the stack frame. 90 max_end_stack: u32 = 0, 91 /// Represents the current end stack offset. If there is no existing slot 92 /// to place a new stack allocation, it goes here, and then bumps `max_end_stack`. 93 next_stack_offset: u32 = 0, 94 95 /// Debug field, used to find bugs in the compiler. 96 air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, 97 98 const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; 99 100 const MCValue = union(enum) { 101 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. 102 /// TODO Look into deleting this tag and using `dead` instead, since every use 103 /// of MCValue.none should be instead looking at the type and noticing it is 0 bits. 104 none, 105 /// Control flow will not allow this value to be observed. 106 unreach, 107 /// No more references to this value remain. 108 dead, 109 /// The value is undefined. 110 undef, 111 /// A pointer-sized integer that fits in a register. 112 /// If the type is a pointer, this is the pointer address in virtual address space. 113 immediate: u64, 114 /// The constant was emitted into the code, at this offset. 115 /// If the type is a pointer, it means the pointer address is embedded in the code. 116 embedded_in_code: usize, 117 /// The value is a pointer to a constant which was emitted into the code, at this offset. 118 ptr_embedded_in_code: usize, 119 /// The value is in a target-specific register. 120 register: Register, 121 /// The value is in memory at a hard-coded address. 122 /// If the type is a pointer, it means the pointer address is at this memory location. 123 memory: u64, 124 /// The value is one of the stack variables. 125 /// If the type is a pointer, it means the pointer address is in the stack at this offset. 126 stack_offset: u32, 127 /// The value is a pointer to one of the stack variables (payload is stack offset). 128 ptr_stack_offset: u32, 129 130 fn isMemory(mcv: MCValue) bool { 131 return switch (mcv) { 132 .embedded_in_code, .memory, .stack_offset => true, 133 else => false, 134 }; 135 } 136 137 fn isImmediate(mcv: MCValue) bool { 138 return switch (mcv) { 139 .immediate => true, 140 else => false, 141 }; 142 } 143 144 fn isMutable(mcv: MCValue) bool { 145 return switch (mcv) { 146 .none => unreachable, 147 .unreach => unreachable, 148 .dead => unreachable, 149 150 .immediate, 151 .embedded_in_code, 152 .memory, 153 .ptr_stack_offset, 154 .ptr_embedded_in_code, 155 .undef, 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 do we need size? should be determined by inst.ty.abiSize() 177 size: u32, 178 }; 179 180 const BlockData = struct { 181 relocs: std.ArrayListUnmanaged(Reloc), 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 Reloc = union(enum) { 190 /// The value is an offset into the `Function` `code` from the beginning. 191 /// To perform the reloc, write 32-bit signed little-endian integer 192 /// which is a relative jump, based on the address following the reloc. 193 rel32: usize, 194 /// A branch in the ARM instruction set 195 arm_branch: struct { 196 pos: usize, 197 cond: @import("../arm/bits.zig").Condition, 198 }, 199 }; 200 201 const BigTomb = struct { 202 function: *Self, 203 inst: Air.Inst.Index, 204 tomb_bits: Liveness.Bpi, 205 big_tomb_bits: u32, 206 bit_index: usize, 207 208 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void { 209 const this_bit_index = bt.bit_index; 210 bt.bit_index += 1; 211 212 const op_int = @enumToInt(op_ref); 213 if (op_int < Air.Inst.Ref.typed_value_map.len) return; 214 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); 215 216 if (this_bit_index < Liveness.bpi - 1) { 217 const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0; 218 if (!dies) return; 219 } else { 220 const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1)); 221 const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0; 222 if (!dies) return; 223 } 224 bt.function.processDeath(op_index); 225 } 226 227 fn finishAir(bt: *BigTomb, result: MCValue) void { 228 const is_used = !bt.function.liveness.isUnused(bt.inst); 229 if (is_used) { 230 log.debug("%{d} => {}", .{ bt.inst, result }); 231 const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1]; 232 branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result); 233 } 234 bt.function.finishAirBookkeeping(); 235 } 236 }; 237 238 const Self = @This(); 239 240 pub fn generate( 241 bin_file: *link.File, 242 src_loc: Module.SrcLoc, 243 module_fn: *Module.Fn, 244 air: Air, 245 liveness: Liveness, 246 code: *std.ArrayList(u8), 247 debug_output: DebugInfoOutput, 248 ) GenerateSymbolError!FnResult { 249 if (build_options.skip_non_native and builtin.cpu.arch != bin_file.options.target.cpu.arch) { 250 @panic("Attempted to compile for architecture that was disabled by build configuration"); 251 } 252 253 assert(module_fn.owner_decl.has_tv); 254 const fn_type = module_fn.owner_decl.ty; 255 256 var branch_stack = std.ArrayList(Branch).init(bin_file.allocator); 257 defer { 258 assert(branch_stack.items.len == 1); 259 branch_stack.items[0].deinit(bin_file.allocator); 260 branch_stack.deinit(); 261 } 262 try branch_stack.append(.{}); 263 264 var function = Self{ 265 .gpa = bin_file.allocator, 266 .air = air, 267 .liveness = liveness, 268 .target = &bin_file.options.target, 269 .bin_file = bin_file, 270 .mod_fn = module_fn, 271 .code = code, 272 .debug_output = debug_output, 273 .err_msg = null, 274 .args = undefined, // populated after `resolveCallingConventionValues` 275 .ret_mcv = undefined, // populated after `resolveCallingConventionValues` 276 .fn_type = fn_type, 277 .arg_index = 0, 278 .branch_stack = &branch_stack, 279 .src_loc = src_loc, 280 .stack_align = undefined, 281 .end_di_line = module_fn.rbrace_line, 282 .end_di_column = module_fn.rbrace_column, 283 }; 284 defer function.stack.deinit(bin_file.allocator); 285 defer function.blocks.deinit(bin_file.allocator); 286 defer function.exitlude_jump_relocs.deinit(bin_file.allocator); 287 288 var call_info = function.resolveCallingConventionValues(fn_type) catch |err| switch (err) { 289 error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, 290 error.OutOfRegisters => return FnResult{ 291 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), 292 }, 293 else => |e| return e, 294 }; 295 defer call_info.deinit(&function); 296 297 function.args = call_info.args; 298 function.ret_mcv = call_info.return_value; 299 function.stack_align = call_info.stack_align; 300 function.max_end_stack = call_info.stack_byte_count; 301 302 function.gen() catch |err| switch (err) { 303 error.CodegenFail => return FnResult{ .fail = function.err_msg.? }, 304 error.OutOfRegisters => return FnResult{ 305 .fail = try ErrorMsg.create(bin_file.allocator, src_loc, "CodeGen ran out of registers. This is a bug in the Zig compiler.", .{}), 306 }, 307 else => |e| return e, 308 }; 309 310 var mir = Mir{ 311 .instructions = function.mir_instructions.toOwnedSlice(), 312 .extra = function.mir_extra.toOwnedSlice(bin_file.allocator), 313 }; 314 defer mir.deinit(bin_file.allocator); 315 316 var emit = Emit{ 317 .mir = mir, 318 .bin_file = bin_file, 319 .debug_output = debug_output, 320 .target = &bin_file.options.target, 321 .src_loc = src_loc, 322 .code = code, 323 .prev_di_pc = 0, 324 .prev_di_line = module_fn.lbrace_line, 325 .prev_di_column = module_fn.lbrace_column, 326 }; 327 defer emit.deinit(); 328 329 emit.emitMir() catch |err| switch (err) { 330 error.EmitFail => return FnResult{ .fail = emit.err_msg.? }, 331 else => |e| return e, 332 }; 333 334 if (function.err_msg) |em| { 335 return FnResult{ .fail = em }; 336 } else { 337 return FnResult{ .appended = {} }; 338 } 339 } 340 341 fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { 342 const gpa = self.gpa; 343 344 try self.mir_instructions.ensureUnusedCapacity(gpa, 1); 345 346 const result_index = @intCast(Air.Inst.Index, self.mir_instructions.len); 347 self.mir_instructions.appendAssumeCapacity(inst); 348 return result_index; 349 } 350 351 pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 { 352 const fields = std.meta.fields(@TypeOf(extra)); 353 try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len); 354 return self.addExtraAssumeCapacity(extra); 355 } 356 357 pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { 358 const fields = std.meta.fields(@TypeOf(extra)); 359 const result = @intCast(u32, self.mir_extra.items.len); 360 inline for (fields) |field| { 361 self.mir_extra.appendAssumeCapacity(switch (field.field_type) { 362 u32 => @field(extra, field.name), 363 i32 => @bitCast(u32, @field(extra, field.name)), 364 else => @compileError("bad field type"), 365 }); 366 } 367 return result; 368 } 369 370 fn gen(self: *Self) !void { 371 const cc = self.fn_type.fnCallingConvention(); 372 if (cc != .Naked) { 373 // TODO Finish function prologue and epilogue for riscv64. 374 375 // TODO Backpatch stack offset 376 // addi sp, sp, -16 377 _ = try self.addInst(.{ 378 .tag = .addi, 379 .data = .{ .i_type = .{ 380 .rd = .sp, 381 .rs1 = .sp, 382 .imm12 = -16, 383 } }, 384 }); 385 386 // sd ra, 8(sp) 387 _ = try self.addInst(.{ 388 .tag = .sd, 389 .data = .{ .i_type = .{ 390 .rd = .ra, 391 .rs1 = .sp, 392 .imm12 = 8, 393 } }, 394 }); 395 396 // sd s0, 0(sp) 397 _ = try self.addInst(.{ 398 .tag = .sd, 399 .data = .{ .i_type = .{ 400 .rd = .s0, 401 .rs1 = .sp, 402 .imm12 = 0, 403 } }, 404 }); 405 406 _ = try self.addInst(.{ 407 .tag = .dbg_prologue_end, 408 .data = .{ .nop = {} }, 409 }); 410 411 try self.genBody(self.air.getMainBody()); 412 413 _ = try self.addInst(.{ 414 .tag = .dbg_epilogue_begin, 415 .data = .{ .nop = {} }, 416 }); 417 418 // exitlude jumps 419 if (self.exitlude_jump_relocs.items.len == 1) { 420 // There is only one relocation. Hence, 421 // this relocation must be at the end of 422 // the code. Therefore, we can just delete 423 // the space initially reserved for the 424 // jump 425 self.mir_instructions.len -= 1; 426 } else for (self.exitlude_jump_relocs.items) |jmp_reloc| { 427 _ = jmp_reloc; 428 return self.fail("TODO add branches in RISCV64", .{}); 429 } 430 431 // ld ra, 8(sp) 432 _ = try self.addInst(.{ 433 .tag = .ld, 434 .data = .{ .i_type = .{ 435 .rd = .ra, 436 .rs1 = .sp, 437 .imm12 = 8, 438 } }, 439 }); 440 441 // ld s0, 0(sp) 442 _ = try self.addInst(.{ 443 .tag = .ld, 444 .data = .{ .i_type = .{ 445 .rd = .s0, 446 .rs1 = .sp, 447 .imm12 = 0, 448 } }, 449 }); 450 451 // addi sp, sp, 16 452 _ = try self.addInst(.{ 453 .tag = .addi, 454 .data = .{ .i_type = .{ 455 .rd = .sp, 456 .rs1 = .sp, 457 .imm12 = 16, 458 } }, 459 }); 460 461 // ret 462 _ = try self.addInst(.{ 463 .tag = .ret, 464 .data = .{ .nop = {} }, 465 }); 466 } else { 467 _ = try self.addInst(.{ 468 .tag = .dbg_prologue_end, 469 .data = .{ .nop = {} }, 470 }); 471 472 try self.genBody(self.air.getMainBody()); 473 474 _ = try self.addInst(.{ 475 .tag = .dbg_epilogue_begin, 476 .data = .{ .nop = {} }, 477 }); 478 } 479 480 // Drop them off at the rbrace. 481 _ = try self.addInst(.{ 482 .tag = .dbg_line, 483 .data = .{ .dbg_line_column = .{ 484 .line = self.end_di_line, 485 .column = self.end_di_column, 486 } }, 487 }); 488 } 489 490 fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { 491 const air_tags = self.air.instructions.items(.tag); 492 493 for (body) |inst| { 494 const old_air_bookkeeping = self.air_bookkeeping; 495 try self.ensureProcessDeathCapacity(Liveness.bpi); 496 497 switch (air_tags[inst]) { 498 // zig fmt: off 499 .add, .ptr_add => try self.airAdd(inst), 500 .addwrap => try self.airAddWrap(inst), 501 .add_sat => try self.airAddSat(inst), 502 .sub, .ptr_sub => try self.airSub(inst), 503 .subwrap => try self.airSubWrap(inst), 504 .sub_sat => try self.airSubSat(inst), 505 .mul => try self.airMul(inst), 506 .mulwrap => try self.airMulWrap(inst), 507 .mul_sat => try self.airMulSat(inst), 508 .rem => try self.airRem(inst), 509 .mod => try self.airMod(inst), 510 .shl, .shl_exact => try self.airShl(inst), 511 .shl_sat => try self.airShlSat(inst), 512 .min => try self.airMin(inst), 513 .max => try self.airMax(inst), 514 .slice => try self.airSlice(inst), 515 516 .sqrt, 517 .sin, 518 .cos, 519 .exp, 520 .exp2, 521 .log, 522 .log2, 523 .log10, 524 .fabs, 525 .floor, 526 .ceil, 527 .round, 528 .trunc_float, 529 => try self.airUnaryMath(inst), 530 531 .add_with_overflow => try self.airAddWithOverflow(inst), 532 .sub_with_overflow => try self.airSubWithOverflow(inst), 533 .mul_with_overflow => try self.airMulWithOverflow(inst), 534 .shl_with_overflow => try self.airShlWithOverflow(inst), 535 536 .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), 537 538 .cmp_lt => try self.airCmp(inst, .lt), 539 .cmp_lte => try self.airCmp(inst, .lte), 540 .cmp_eq => try self.airCmp(inst, .eq), 541 .cmp_gte => try self.airCmp(inst, .gte), 542 .cmp_gt => try self.airCmp(inst, .gt), 543 .cmp_neq => try self.airCmp(inst, .neq), 544 545 .bool_and => try self.airBoolOp(inst), 546 .bool_or => try self.airBoolOp(inst), 547 .bit_and => try self.airBitAnd(inst), 548 .bit_or => try self.airBitOr(inst), 549 .xor => try self.airXor(inst), 550 .shr, .shr_exact => try self.airShr(inst), 551 552 .alloc => try self.airAlloc(inst), 553 .ret_ptr => try self.airRetPtr(inst), 554 .arg => try self.airArg(inst), 555 .assembly => try self.airAsm(inst), 556 .bitcast => try self.airBitCast(inst), 557 .block => try self.airBlock(inst), 558 .br => try self.airBr(inst), 559 .breakpoint => try self.airBreakpoint(), 560 .ret_addr => try self.airRetAddr(inst), 561 .frame_addr => try self.airFrameAddress(inst), 562 .fence => try self.airFence(), 563 .cond_br => try self.airCondBr(inst), 564 .dbg_stmt => try self.airDbgStmt(inst), 565 .dbg_func => try self.airDbgFunc(inst), 566 .fptrunc => try self.airFptrunc(inst), 567 .fpext => try self.airFpext(inst), 568 .intcast => try self.airIntCast(inst), 569 .trunc => try self.airTrunc(inst), 570 .bool_to_int => try self.airBoolToInt(inst), 571 .is_non_null => try self.airIsNonNull(inst), 572 .is_non_null_ptr => try self.airIsNonNullPtr(inst), 573 .is_null => try self.airIsNull(inst), 574 .is_null_ptr => try self.airIsNullPtr(inst), 575 .is_non_err => try self.airIsNonErr(inst), 576 .is_non_err_ptr => try self.airIsNonErrPtr(inst), 577 .is_err => try self.airIsErr(inst), 578 .is_err_ptr => try self.airIsErrPtr(inst), 579 .load => try self.airLoad(inst), 580 .loop => try self.airLoop(inst), 581 .not => try self.airNot(inst), 582 .ptrtoint => try self.airPtrToInt(inst), 583 .ret => try self.airRet(inst), 584 .ret_load => try self.airRetLoad(inst), 585 .store => try self.airStore(inst), 586 .struct_field_ptr=> try self.airStructFieldPtr(inst), 587 .struct_field_val=> try self.airStructFieldVal(inst), 588 .array_to_slice => try self.airArrayToSlice(inst), 589 .int_to_float => try self.airIntToFloat(inst), 590 .float_to_int => try self.airFloatToInt(inst), 591 .cmpxchg_strong => try self.airCmpxchg(inst), 592 .cmpxchg_weak => try self.airCmpxchg(inst), 593 .atomic_rmw => try self.airAtomicRmw(inst), 594 .atomic_load => try self.airAtomicLoad(inst), 595 .memcpy => try self.airMemcpy(inst), 596 .memset => try self.airMemset(inst), 597 .set_union_tag => try self.airSetUnionTag(inst), 598 .get_union_tag => try self.airGetUnionTag(inst), 599 .clz => try self.airClz(inst), 600 .ctz => try self.airCtz(inst), 601 .popcount => try self.airPopcount(inst), 602 .byte_swap => try self.airByteSwap(inst), 603 .bit_reverse => try self.airBitReverse(inst), 604 .tag_name => try self.airTagName(inst), 605 .error_name => try self.airErrorName(inst), 606 .splat => try self.airSplat(inst), 607 .shuffle => try self.airShuffle(inst), 608 .aggregate_init => try self.airAggregateInit(inst), 609 .union_init => try self.airUnionInit(inst), 610 .prefetch => try self.airPrefetch(inst), 611 .mul_add => try self.airMulAdd(inst), 612 613 .dbg_var_ptr, 614 .dbg_var_val, 615 => try self.airDbgVar(inst), 616 617 .call => try self.airCall(inst, .auto), 618 .call_always_tail => try self.airCall(inst, .always_tail), 619 .call_never_tail => try self.airCall(inst, .never_tail), 620 .call_never_inline => try self.airCall(inst, .never_inline), 621 622 .atomic_store_unordered => try self.airAtomicStore(inst, .Unordered), 623 .atomic_store_monotonic => try self.airAtomicStore(inst, .Monotonic), 624 .atomic_store_release => try self.airAtomicStore(inst, .Release), 625 .atomic_store_seq_cst => try self.airAtomicStore(inst, .SeqCst), 626 627 .struct_field_ptr_index_0 => try self.airStructFieldPtrIndex(inst, 0), 628 .struct_field_ptr_index_1 => try self.airStructFieldPtrIndex(inst, 1), 629 .struct_field_ptr_index_2 => try self.airStructFieldPtrIndex(inst, 2), 630 .struct_field_ptr_index_3 => try self.airStructFieldPtrIndex(inst, 3), 631 632 .field_parent_ptr => try self.airFieldParentPtr(inst), 633 634 .switch_br => try self.airSwitch(inst), 635 .slice_ptr => try self.airSlicePtr(inst), 636 .slice_len => try self.airSliceLen(inst), 637 638 .ptr_slice_len_ptr => try self.airPtrSliceLenPtr(inst), 639 .ptr_slice_ptr_ptr => try self.airPtrSlicePtrPtr(inst), 640 641 .array_elem_val => try self.airArrayElemVal(inst), 642 .slice_elem_val => try self.airSliceElemVal(inst), 643 .slice_elem_ptr => try self.airSliceElemPtr(inst), 644 .ptr_elem_val => try self.airPtrElemVal(inst), 645 .ptr_elem_ptr => try self.airPtrElemPtr(inst), 646 647 .constant => unreachable, // excluded from function bodies 648 .const_ty => unreachable, // excluded from function bodies 649 .unreach => self.finishAirBookkeeping(), 650 651 .optional_payload => try self.airOptionalPayload(inst), 652 .optional_payload_ptr => try self.airOptionalPayloadPtr(inst), 653 .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst), 654 .unwrap_errunion_err => try self.airUnwrapErrErr(inst), 655 .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst), 656 .unwrap_errunion_err_ptr => try self.airUnwrapErrErrPtr(inst), 657 .unwrap_errunion_payload_ptr=> try self.airUnwrapErrPayloadPtr(inst), 658 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst), 659 660 .wrap_optional => try self.airWrapOptional(inst), 661 .wrap_errunion_payload => try self.airWrapErrUnionPayload(inst), 662 .wrap_errunion_err => try self.airWrapErrUnionErr(inst), 663 664 .wasm_memory_size => unreachable, 665 .wasm_memory_grow => unreachable, 666 // zig fmt: on 667 } 668 if (std.debug.runtime_safety) { 669 if (self.air_bookkeeping < old_air_bookkeeping + 1) { 670 std.debug.panic("in codegen.zig, handling of AIR instruction %{d} ('{}') did not do proper bookkeeping. Look for a missing call to finishAir.", .{ inst, air_tags[inst] }); 671 } 672 } 673 } 674 } 675 676 /// Asserts there is already capacity to insert into top branch inst_table. 677 fn processDeath(self: *Self, inst: Air.Inst.Index) void { 678 const air_tags = self.air.instructions.items(.tag); 679 if (air_tags[inst] == .constant) return; // Constants are immortal. 680 // When editing this function, note that the logic must synchronize with `reuseOperand`. 681 const prev_value = self.getResolvedInstValue(inst); 682 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 683 branch.inst_table.putAssumeCapacity(inst, .dead); 684 switch (prev_value) { 685 .register => |reg| { 686 self.register_manager.freeReg(reg); 687 }, 688 else => {}, // TODO process stack allocation death 689 } 690 } 691 692 /// Called when there are no operands, and the instruction is always unreferenced. 693 fn finishAirBookkeeping(self: *Self) void { 694 if (std.debug.runtime_safety) { 695 self.air_bookkeeping += 1; 696 } 697 } 698 699 fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Liveness.bpi - 1]Air.Inst.Ref) void { 700 var tomb_bits = self.liveness.getTombBits(inst); 701 for (operands) |op| { 702 const dies = @truncate(u1, tomb_bits) != 0; 703 tomb_bits >>= 1; 704 if (!dies) continue; 705 const op_int = @enumToInt(op); 706 if (op_int < Air.Inst.Ref.typed_value_map.len) continue; 707 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len); 708 self.processDeath(op_index); 709 } 710 const is_used = @truncate(u1, tomb_bits) == 0; 711 if (is_used) { 712 log.debug("%{d} => {}", .{ inst, result }); 713 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 714 branch.inst_table.putAssumeCapacityNoClobber(inst, result); 715 716 switch (result) { 717 .register => |reg| { 718 // In some cases (such as bitcast), an operand 719 // may be the same MCValue as the result. If 720 // that operand died and was a register, it 721 // was freed by processDeath. We have to 722 // "re-allocate" the register. 723 if (self.register_manager.isRegFree(reg)) { 724 self.register_manager.getRegAssumeFree(reg, inst); 725 } 726 }, 727 else => {}, 728 } 729 } 730 self.finishAirBookkeeping(); 731 } 732 733 fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { 734 const table = &self.branch_stack.items[self.branch_stack.items.len - 1].inst_table; 735 try table.ensureUnusedCapacity(self.gpa, additional_count); 736 } 737 738 /// Adds a Type to the .debug_info at the current position. The bytes will be populated later, 739 /// after codegen for this symbol is done. 740 fn addDbgInfoTypeReloc(self: *Self, ty: Type) !void { 741 switch (self.debug_output) { 742 .dwarf => |dbg_out| { 743 assert(ty.hasRuntimeBits()); 744 const index = dbg_out.dbg_info.items.len; 745 try dbg_out.dbg_info.resize(index + 4); // DW.AT.type, DW.FORM.ref4 746 747 const gop = try dbg_out.dbg_info_type_relocs.getOrPut(self.gpa, ty); 748 if (!gop.found_existing) { 749 gop.value_ptr.* = .{ 750 .off = undefined, 751 .relocs = .{}, 752 }; 753 } 754 try gop.value_ptr.relocs.append(self.gpa, @intCast(u32, index)); 755 }, 756 .plan9 => {}, 757 .none => {}, 758 } 759 } 760 761 fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 { 762 if (abi_align > self.stack_align) 763 self.stack_align = abi_align; 764 // TODO find a free slot instead of always appending 765 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align); 766 self.next_stack_offset = offset + abi_size; 767 if (self.next_stack_offset > self.max_end_stack) 768 self.max_end_stack = self.next_stack_offset; 769 try self.stack.putNoClobber(self.gpa, offset, .{ 770 .inst = inst, 771 .size = abi_size, 772 }); 773 return offset; 774 } 775 776 /// Use a pointer instruction as the basis for allocating stack memory. 777 fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 { 778 const elem_ty = self.air.typeOfIndex(inst).elemType(); 779 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { 780 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty}); 781 }; 782 // TODO swap this for inst.ty.ptrAlign 783 const abi_align = elem_ty.abiAlignment(self.target.*); 784 return self.allocMem(inst, abi_size, abi_align); 785 } 786 787 fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue { 788 const elem_ty = self.air.typeOfIndex(inst); 789 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) catch { 790 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty}); 791 }; 792 const abi_align = elem_ty.abiAlignment(self.target.*); 793 if (abi_align > self.stack_align) 794 self.stack_align = abi_align; 795 796 if (reg_ok) { 797 // Make sure the type can fit in a register before we try to allocate one. 798 const ptr_bits = self.target.cpu.arch.ptrBitWidth(); 799 const ptr_bytes: u64 = @divExact(ptr_bits, 8); 800 if (abi_size <= ptr_bytes) { 801 if (self.register_manager.tryAllocReg(inst)) |reg| { 802 return MCValue{ .register = reg }; 803 } 804 } 805 } 806 const stack_offset = try self.allocMem(inst, abi_size, abi_align); 807 return MCValue{ .stack_offset = stack_offset }; 808 } 809 810 pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void { 811 const stack_mcv = try self.allocRegOrMem(inst, false); 812 log.debug("spilling {d} to stack mcv {any}", .{ inst, stack_mcv }); 813 const reg_mcv = self.getResolvedInstValue(inst); 814 assert(reg == reg_mcv.register); 815 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 816 try branch.inst_table.put(self.gpa, inst, stack_mcv); 817 try self.genSetStack(self.air.typeOfIndex(inst), stack_mcv.stack_offset, reg_mcv); 818 } 819 820 /// Copies a value to a register without tracking the register. The register is not considered 821 /// allocated. A second call to `copyToTmpRegister` may return the same register. 822 /// This can have a side effect of spilling instructions to the stack to free up a register. 823 fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register { 824 const reg = try self.register_manager.allocReg(null); 825 try self.genSetReg(ty, reg, mcv); 826 return reg; 827 } 828 829 /// Allocates a new register and copies `mcv` into it. 830 /// `reg_owner` is the instruction that gets associated with the register in the register table. 831 /// This can have a side effect of spilling instructions to the stack to free up a register. 832 fn copyToNewRegister(self: *Self, reg_owner: Air.Inst.Index, mcv: MCValue) !MCValue { 833 const reg = try self.register_manager.allocReg(reg_owner); 834 try self.genSetReg(self.air.typeOfIndex(reg_owner), reg, mcv); 835 return MCValue{ .register = reg }; 836 } 837 838 fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { 839 const stack_offset = try self.allocMemPtr(inst); 840 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); 841 } 842 843 fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { 844 const stack_offset = try self.allocMemPtr(inst); 845 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); 846 } 847 848 fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { 849 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 850 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFptrunc for {}", .{self.target.cpu.arch}); 851 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 852 } 853 854 fn airFpext(self: *Self, inst: Air.Inst.Index) !void { 855 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 856 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFpext for {}", .{self.target.cpu.arch}); 857 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 858 } 859 860 fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { 861 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 862 if (self.liveness.isUnused(inst)) 863 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); 864 865 const operand_ty = self.air.typeOf(ty_op.operand); 866 const operand = try self.resolveInst(ty_op.operand); 867 const info_a = operand_ty.intInfo(self.target.*); 868 const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*); 869 if (info_a.signedness != info_b.signedness) 870 return self.fail("TODO gen intcast sign safety in semantic analysis", .{}); 871 872 if (info_a.bits == info_b.bits) 873 return self.finishAir(inst, operand, .{ ty_op.operand, .none, .none }); 874 875 return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch}); 876 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 877 } 878 879 fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { 880 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 881 if (self.liveness.isUnused(inst)) 882 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); 883 884 const operand = try self.resolveInst(ty_op.operand); 885 _ = operand; 886 return self.fail("TODO implement trunc for {}", .{self.target.cpu.arch}); 887 // return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 888 } 889 890 fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void { 891 const un_op = self.air.instructions.items(.data)[inst].un_op; 892 const operand = try self.resolveInst(un_op); 893 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand; 894 return self.finishAir(inst, result, .{ un_op, .none, .none }); 895 } 896 897 fn airNot(self: *Self, inst: Air.Inst.Index) !void { 898 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 899 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement NOT for {}", .{self.target.cpu.arch}); 900 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 901 } 902 903 fn airMin(self: *Self, inst: Air.Inst.Index) !void { 904 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 905 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement min for {}", .{self.target.cpu.arch}); 906 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 907 } 908 909 fn airMax(self: *Self, inst: Air.Inst.Index) !void { 910 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 911 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement max for {}", .{self.target.cpu.arch}); 912 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 913 } 914 915 fn airSlice(self: *Self, inst: Air.Inst.Index) !void { 916 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 917 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 918 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice for {}", .{self.target.cpu.arch}); 919 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 920 } 921 922 fn airAdd(self: *Self, inst: Air.Inst.Index) !void { 923 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 924 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add for {}", .{self.target.cpu.arch}); 925 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 926 } 927 928 fn airAddWrap(self: *Self, inst: Air.Inst.Index) !void { 929 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 930 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement addwrap for {}", .{self.target.cpu.arch}); 931 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 932 } 933 934 fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { 935 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 936 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch}); 937 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 938 } 939 940 fn airSub(self: *Self, inst: Air.Inst.Index) !void { 941 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 942 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub for {}", .{self.target.cpu.arch}); 943 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 944 } 945 946 fn airSubWrap(self: *Self, inst: Air.Inst.Index) !void { 947 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 948 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement subwrap for {}", .{self.target.cpu.arch}); 949 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 950 } 951 952 fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { 953 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 954 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch}); 955 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 956 } 957 958 fn airMul(self: *Self, inst: Air.Inst.Index) !void { 959 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 960 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul for {}", .{self.target.cpu.arch}); 961 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 962 } 963 964 fn airMulWrap(self: *Self, inst: Air.Inst.Index) !void { 965 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 966 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mulwrap for {}", .{self.target.cpu.arch}); 967 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 968 } 969 970 fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { 971 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 972 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch}); 973 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 974 } 975 976 fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { 977 _ = inst; 978 return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch}); 979 } 980 981 fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { 982 _ = inst; 983 return self.fail("TODO implement airSubWithOverflow for {}", .{self.target.cpu.arch}); 984 } 985 986 fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { 987 _ = inst; 988 return self.fail("TODO implement airMulWithOverflow for {}", .{self.target.cpu.arch}); 989 } 990 991 fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { 992 _ = inst; 993 return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch}); 994 } 995 996 fn airDiv(self: *Self, inst: Air.Inst.Index) !void { 997 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 998 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement div for {}", .{self.target.cpu.arch}); 999 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1000 } 1001 1002 fn airRem(self: *Self, inst: Air.Inst.Index) !void { 1003 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1004 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement rem for {}", .{self.target.cpu.arch}); 1005 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1006 } 1007 1008 fn airMod(self: *Self, inst: Air.Inst.Index) !void { 1009 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1010 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mod for {}", .{self.target.cpu.arch}); 1011 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1012 } 1013 1014 fn airBitAnd(self: *Self, inst: Air.Inst.Index) !void { 1015 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1016 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement bitwise and for {}", .{self.target.cpu.arch}); 1017 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1018 } 1019 1020 fn airBitOr(self: *Self, inst: Air.Inst.Index) !void { 1021 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1022 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement bitwise or for {}", .{self.target.cpu.arch}); 1023 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1024 } 1025 1026 fn airXor(self: *Self, inst: Air.Inst.Index) !void { 1027 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1028 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement xor for {}", .{self.target.cpu.arch}); 1029 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1030 } 1031 1032 fn airShl(self: *Self, inst: Air.Inst.Index) !void { 1033 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1034 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl for {}", .{self.target.cpu.arch}); 1035 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1036 } 1037 1038 fn airShlSat(self: *Self, inst: Air.Inst.Index) !void { 1039 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1040 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}); 1041 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1042 } 1043 1044 fn airShr(self: *Self, inst: Air.Inst.Index) !void { 1045 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1046 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shr for {}", .{self.target.cpu.arch}); 1047 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1048 } 1049 1050 fn airOptionalPayload(self: *Self, inst: Air.Inst.Index) !void { 1051 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1052 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload for {}", .{self.target.cpu.arch}); 1053 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1054 } 1055 1056 fn airOptionalPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { 1057 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1058 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}); 1059 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1060 } 1061 1062 fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { 1063 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1064 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .optional_payload_ptr_set for {}", .{self.target.cpu.arch}); 1065 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1066 } 1067 1068 fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { 1069 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1070 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union error for {}", .{self.target.cpu.arch}); 1071 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1072 } 1073 1074 fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { 1075 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1076 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch}); 1077 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1078 } 1079 1080 // *(E!T) -> E 1081 fn airUnwrapErrErrPtr(self: *Self, inst: Air.Inst.Index) !void { 1082 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1083 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}); 1084 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1085 } 1086 1087 // *(E!T) -> *T 1088 fn airUnwrapErrPayloadPtr(self: *Self, inst: Air.Inst.Index) !void { 1089 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1090 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}); 1091 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1092 } 1093 1094 fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { 1095 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1096 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch}); 1097 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1098 } 1099 1100 fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { 1101 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1102 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1103 const optional_ty = self.air.typeOfIndex(inst); 1104 1105 // Optional with a zero-bit payload type is just a boolean true 1106 if (optional_ty.abiSize(self.target.*) == 1) 1107 break :result MCValue{ .immediate = 1 }; 1108 1109 return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}); 1110 }; 1111 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1112 } 1113 1114 /// T to E!T 1115 fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { 1116 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1117 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch}); 1118 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1119 } 1120 1121 /// E to E!T 1122 fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { 1123 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1124 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement wrap errunion error for {}", .{self.target.cpu.arch}); 1125 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1126 } 1127 1128 fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { 1129 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1130 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_ptr for {}", .{self.target.cpu.arch}); 1131 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1132 } 1133 1134 fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { 1135 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1136 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_len for {}", .{self.target.cpu.arch}); 1137 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1138 } 1139 1140 fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void { 1141 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1142 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_slice_len_ptr for {}", .{self.target.cpu.arch}); 1143 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1144 } 1145 1146 fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void { 1147 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1148 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_slice_ptr_ptr for {}", .{self.target.cpu.arch}); 1149 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1150 } 1151 1152 fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { 1153 const is_volatile = false; // TODO 1154 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1155 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}); 1156 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1157 } 1158 1159 fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { 1160 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 1161 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 1162 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_elem_ptr for {}", .{self.target.cpu.arch}); 1163 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); 1164 } 1165 1166 fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { 1167 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1168 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch}); 1169 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1170 } 1171 1172 fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { 1173 const is_volatile = false; // TODO 1174 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1175 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}); 1176 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1177 } 1178 1179 fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { 1180 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 1181 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 1182 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_ptr for {}", .{self.target.cpu.arch}); 1183 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); 1184 } 1185 1186 fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { 1187 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1188 _ = bin_op; 1189 return self.fail("TODO implement airSetUnionTag for {}", .{self.target.cpu.arch}); 1190 // return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1191 } 1192 1193 fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { 1194 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1195 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airGetUnionTag for {}", .{self.target.cpu.arch}); 1196 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1197 } 1198 1199 fn airClz(self: *Self, inst: Air.Inst.Index) !void { 1200 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1201 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airClz for {}", .{self.target.cpu.arch}); 1202 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1203 } 1204 1205 fn airCtz(self: *Self, inst: Air.Inst.Index) !void { 1206 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1207 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCtz for {}", .{self.target.cpu.arch}); 1208 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1209 } 1210 1211 fn airPopcount(self: *Self, inst: Air.Inst.Index) !void { 1212 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1213 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airPopcount for {}", .{self.target.cpu.arch}); 1214 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1215 } 1216 1217 fn airByteSwap(self: *Self, inst: Air.Inst.Index) !void { 1218 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1219 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airByteSwap for {}", .{self.target.cpu.arch}); 1220 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1221 } 1222 1223 fn airBitReverse(self: *Self, inst: Air.Inst.Index) !void { 1224 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1225 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airBitReverse for {}", .{self.target.cpu.arch}); 1226 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1227 } 1228 1229 fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void { 1230 const un_op = self.air.instructions.items(.data)[inst].un_op; 1231 const result: MCValue = if (self.liveness.isUnused(inst)) 1232 .dead 1233 else 1234 return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch}); 1235 return self.finishAir(inst, result, .{ un_op, .none, .none }); 1236 } 1237 1238 fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool { 1239 if (!self.liveness.operandDies(inst, op_index)) 1240 return false; 1241 1242 switch (mcv) { 1243 .register => |reg| { 1244 // If it's in the registers table, need to associate the register with the 1245 // new instruction. 1246 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { 1247 if (!self.register_manager.isRegFree(reg)) { 1248 self.register_manager.registers[index] = inst; 1249 } 1250 } 1251 log.debug("%{d} => {} (reused)", .{ inst, reg }); 1252 }, 1253 .stack_offset => |off| { 1254 log.debug("%{d} => stack offset {d} (reused)", .{ inst, off }); 1255 }, 1256 else => return false, 1257 } 1258 1259 // Prevent the operand deaths processing code from deallocating it. 1260 self.liveness.clearOperandDeath(inst, op_index); 1261 1262 // That makes us responsible for doing the rest of the stuff that processDeath would have done. 1263 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; 1264 branch.inst_table.putAssumeCapacity(Air.refToIndex(operand).?, .dead); 1265 1266 return true; 1267 } 1268 1269 fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { 1270 const elem_ty = ptr_ty.elemType(); 1271 switch (ptr) { 1272 .none => unreachable, 1273 .undef => unreachable, 1274 .unreach => unreachable, 1275 .dead => unreachable, 1276 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), 1277 .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), 1278 .ptr_embedded_in_code => |off| { 1279 try self.setRegOrMem(elem_ty, dst_mcv, .{ .embedded_in_code = off }); 1280 }, 1281 .embedded_in_code => { 1282 return self.fail("TODO implement loading from MCValue.embedded_in_code", .{}); 1283 }, 1284 .register => { 1285 return self.fail("TODO implement loading from MCValue.register", .{}); 1286 }, 1287 .memory, 1288 .stack_offset, 1289 => { 1290 const reg = try self.register_manager.allocReg(null); 1291 self.register_manager.freezeRegs(&.{reg}); 1292 defer self.register_manager.unfreezeRegs(&.{reg}); 1293 1294 try self.genSetReg(ptr_ty, reg, ptr); 1295 try self.load(dst_mcv, .{ .register = reg }, ptr_ty); 1296 }, 1297 } 1298 } 1299 1300 fn airLoad(self: *Self, inst: Air.Inst.Index) !void { 1301 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1302 const elem_ty = self.air.typeOfIndex(inst); 1303 const result: MCValue = result: { 1304 if (!elem_ty.hasRuntimeBits()) 1305 break :result MCValue.none; 1306 1307 const ptr = try self.resolveInst(ty_op.operand); 1308 const is_volatile = self.air.typeOf(ty_op.operand).isVolatilePtr(); 1309 if (self.liveness.isUnused(inst) and !is_volatile) 1310 break :result MCValue.dead; 1311 1312 const dst_mcv: MCValue = blk: { 1313 if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) { 1314 // The MCValue that holds the pointer can be re-used as the value. 1315 break :blk ptr; 1316 } else { 1317 break :blk try self.allocRegOrMem(inst, true); 1318 } 1319 }; 1320 try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand)); 1321 break :result dst_mcv; 1322 }; 1323 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 1324 } 1325 1326 fn airStore(self: *Self, inst: Air.Inst.Index) !void { 1327 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1328 const ptr = try self.resolveInst(bin_op.lhs); 1329 const value = try self.resolveInst(bin_op.rhs); 1330 const elem_ty = self.air.typeOf(bin_op.rhs); 1331 switch (ptr) { 1332 .none => unreachable, 1333 .undef => unreachable, 1334 .unreach => unreachable, 1335 .dead => unreachable, 1336 .immediate => |imm| { 1337 try self.setRegOrMem(elem_ty, .{ .memory = imm }, value); 1338 }, 1339 .ptr_stack_offset => |off| { 1340 try self.genSetStack(elem_ty, off, value); 1341 }, 1342 .ptr_embedded_in_code => |off| { 1343 try self.setRegOrMem(elem_ty, .{ .embedded_in_code = off }, value); 1344 }, 1345 .embedded_in_code => { 1346 return self.fail("TODO implement storing to MCValue.embedded_in_code", .{}); 1347 }, 1348 .register => { 1349 return self.fail("TODO implement storing to MCValue.register", .{}); 1350 }, 1351 .memory => { 1352 return self.fail("TODO implement storing to MCValue.memory", .{}); 1353 }, 1354 .stack_offset => { 1355 return self.fail("TODO implement storing to MCValue.stack_offset", .{}); 1356 }, 1357 } 1358 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); 1359 } 1360 1361 fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void { 1362 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 1363 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; 1364 return self.structFieldPtr(extra.struct_operand, ty_pl.ty, extra.field_index); 1365 } 1366 1367 fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { 1368 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 1369 return self.structFieldPtr(ty_op.operand, ty_op.ty, index); 1370 } 1371 fn structFieldPtr(self: *Self, operand: Air.Inst.Ref, ty: Air.Inst.Ref, index: u32) !void { 1372 _ = self; 1373 _ = operand; 1374 _ = ty; 1375 _ = index; 1376 return self.fail("TODO implement codegen struct_field_ptr", .{}); 1377 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); 1378 } 1379 1380 fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { 1381 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 1382 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; 1383 _ = extra; 1384 return self.fail("TODO implement codegen struct_field_val", .{}); 1385 //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); 1386 } 1387 1388 fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { 1389 _ = self; 1390 _ = inst; 1391 return self.fail("TODO implement codegen airFieldParentPtr", .{}); 1392 } 1393 1394 fn genArgDbgInfo(self: *Self, inst: Air.Inst.Index, mcv: MCValue, arg_index: u32) !void { 1395 const ty = self.air.instructions.items(.data)[inst].ty; 1396 const name = self.mod_fn.getParamName(arg_index); 1397 const name_with_null = name.ptr[0 .. name.len + 1]; 1398 1399 switch (mcv) { 1400 .register => |reg| { 1401 switch (self.debug_output) { 1402 .dwarf => |dbg_out| { 1403 try dbg_out.dbg_info.ensureUnusedCapacity(3); 1404 dbg_out.dbg_info.appendAssumeCapacity(link.File.Dwarf.abbrev_parameter); 1405 dbg_out.dbg_info.appendSliceAssumeCapacity(&[2]u8{ // DW.AT.location, DW.FORM.exprloc 1406 1, // ULEB128 dwarf expression length 1407 reg.dwarfLocOp(), 1408 }); 1409 try dbg_out.dbg_info.ensureUnusedCapacity(5 + name_with_null.len); 1410 try self.addDbgInfoTypeReloc(ty); // DW.AT.type, DW.FORM.ref4 1411 dbg_out.dbg_info.appendSliceAssumeCapacity(name_with_null); // DW.AT.name, DW.FORM.string 1412 }, 1413 .plan9 => {}, 1414 .none => {}, 1415 } 1416 }, 1417 .stack_offset => |offset| { 1418 _ = offset; 1419 switch (self.debug_output) { 1420 .dwarf => {}, 1421 .plan9 => {}, 1422 .none => {}, 1423 } 1424 }, 1425 else => {}, 1426 } 1427 } 1428 1429 fn airArg(self: *Self, inst: Air.Inst.Index) !void { 1430 const arg_index = self.arg_index; 1431 self.arg_index += 1; 1432 1433 const ty = self.air.typeOfIndex(inst); 1434 _ = ty; 1435 1436 const result = self.args[arg_index]; 1437 // TODO support stack-only arguments 1438 // TODO Copy registers to the stack 1439 const mcv = result; 1440 try self.genArgDbgInfo(inst, mcv, @intCast(u32, arg_index)); 1441 1442 if (self.liveness.isUnused(inst)) 1443 return self.finishAirBookkeeping(); 1444 1445 switch (mcv) { 1446 .register => |reg| { 1447 self.register_manager.getRegAssumeFree(reg, inst); 1448 }, 1449 else => {}, 1450 } 1451 1452 return self.finishAir(inst, mcv, .{ .none, .none, .none }); 1453 } 1454 1455 fn airBreakpoint(self: *Self) !void { 1456 _ = try self.addInst(.{ 1457 .tag = .ebreak, 1458 .data = .{ .nop = {} }, 1459 }); 1460 return self.finishAirBookkeeping(); 1461 } 1462 1463 fn airRetAddr(self: *Self, inst: Air.Inst.Index) !void { 1464 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airRetAddr for riscv64", .{}); 1465 return self.finishAir(inst, result, .{ .none, .none, .none }); 1466 } 1467 1468 fn airFrameAddress(self: *Self, inst: Air.Inst.Index) !void { 1469 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFrameAddress for riscv64", .{}); 1470 return self.finishAir(inst, result, .{ .none, .none, .none }); 1471 } 1472 1473 fn airFence(self: *Self) !void { 1474 return self.fail("TODO implement fence() for {}", .{self.target.cpu.arch}); 1475 //return self.finishAirBookkeeping(); 1476 } 1477 1478 fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.Modifier) !void { 1479 if (modifier == .always_tail) return self.fail("TODO implement tail calls for riscv64", .{}); 1480 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 1481 const fn_ty = self.air.typeOf(pl_op.operand); 1482 const callee = pl_op.operand; 1483 const extra = self.air.extraData(Air.Call, pl_op.payload); 1484 const args = @bitCast([]const Air.Inst.Ref, self.air.extra[extra.end..][0..extra.data.args_len]); 1485 1486 var info = try self.resolveCallingConventionValues(fn_ty); 1487 defer info.deinit(self); 1488 1489 // Due to incremental compilation, how function calls are generated depends 1490 // on linking. 1491 if (self.bin_file.tag == link.File.Elf.base_tag or self.bin_file.tag == link.File.Coff.base_tag) { 1492 for (info.args) |mc_arg, arg_i| { 1493 const arg = args[arg_i]; 1494 const arg_ty = self.air.typeOf(arg); 1495 const arg_mcv = try self.resolveInst(args[arg_i]); 1496 1497 switch (mc_arg) { 1498 .none => continue, 1499 .undef => unreachable, 1500 .immediate => unreachable, 1501 .unreach => unreachable, 1502 .dead => unreachable, 1503 .embedded_in_code => unreachable, 1504 .memory => unreachable, 1505 .register => |reg| { 1506 try self.register_manager.getReg(reg, null); 1507 try self.genSetReg(arg_ty, reg, arg_mcv); 1508 }, 1509 .stack_offset => { 1510 return self.fail("TODO implement calling with parameters in memory", .{}); 1511 }, 1512 .ptr_stack_offset => { 1513 return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); 1514 }, 1515 .ptr_embedded_in_code => { 1516 return self.fail("TODO implement calling with MCValue.ptr_embedded_in_code arg", .{}); 1517 }, 1518 } 1519 } 1520 1521 if (self.air.value(callee)) |func_value| { 1522 if (func_value.castTag(.function)) |func_payload| { 1523 const func = func_payload.data; 1524 1525 const ptr_bits = self.target.cpu.arch.ptrBitWidth(); 1526 const ptr_bytes: u64 = @divExact(ptr_bits, 8); 1527 const got_addr = if (self.bin_file.cast(link.File.Elf)) |elf_file| blk: { 1528 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; 1529 break :blk @intCast(u32, got.p_vaddr + func.owner_decl.link.elf.offset_table_index * ptr_bytes); 1530 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| 1531 coff_file.offset_table_virtual_address + func.owner_decl.link.coff.offset_table_index * ptr_bytes 1532 else 1533 unreachable; 1534 1535 try self.genSetReg(Type.initTag(.usize), .ra, .{ .memory = got_addr }); 1536 _ = try self.addInst(.{ 1537 .tag = .jalr, 1538 .data = .{ .i_type = .{ 1539 .rd = .ra, 1540 .rs1 = .ra, 1541 .imm12 = 0, 1542 } }, 1543 }); 1544 } else if (func_value.castTag(.extern_fn)) |_| { 1545 return self.fail("TODO implement calling extern functions", .{}); 1546 } else { 1547 return self.fail("TODO implement calling bitcasted functions", .{}); 1548 } 1549 } else { 1550 return self.fail("TODO implement calling runtime known function pointer", .{}); 1551 } 1552 } else if (self.bin_file.cast(link.File.MachO)) |_| { 1553 unreachable; // unsupported architecture for MachO 1554 } else if (self.bin_file.cast(link.File.Plan9)) |_| { 1555 return self.fail("TODO implement call on plan9 for {}", .{self.target.cpu.arch}); 1556 } else unreachable; 1557 1558 const result: MCValue = result: { 1559 switch (info.return_value) { 1560 .register => |reg| { 1561 if (RegisterManager.indexOfReg(&callee_preserved_regs, reg) == null) { 1562 // Save function return value in a callee saved register 1563 break :result try self.copyToNewRegister(inst, info.return_value); 1564 } 1565 }, 1566 else => {}, 1567 } 1568 break :result info.return_value; 1569 }; 1570 1571 if (args.len <= Liveness.bpi - 2) { 1572 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); 1573 buf[0] = callee; 1574 std.mem.copy(Air.Inst.Ref, buf[1..], args); 1575 return self.finishAir(inst, result, buf); 1576 } 1577 var bt = try self.iterateBigTomb(inst, 1 + args.len); 1578 bt.feed(callee); 1579 for (args) |arg| { 1580 bt.feed(arg); 1581 } 1582 return bt.finishAir(result); 1583 } 1584 1585 fn ret(self: *Self, mcv: MCValue) !void { 1586 const ret_ty = self.fn_type.fnReturnType(); 1587 try self.setRegOrMem(ret_ty, self.ret_mcv, mcv); 1588 // Just add space for an instruction, patch this later 1589 const index = try self.addInst(.{ 1590 .tag = .nop, 1591 .data = .{ .nop = {} }, 1592 }); 1593 try self.exitlude_jump_relocs.append(self.gpa, index); 1594 } 1595 1596 fn airRet(self: *Self, inst: Air.Inst.Index) !void { 1597 const un_op = self.air.instructions.items(.data)[inst].un_op; 1598 const operand = try self.resolveInst(un_op); 1599 try self.ret(operand); 1600 return self.finishAir(inst, .dead, .{ un_op, .none, .none }); 1601 } 1602 1603 fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { 1604 const un_op = self.air.instructions.items(.data)[inst].un_op; 1605 const ptr = try self.resolveInst(un_op); 1606 _ = ptr; 1607 return self.fail("TODO implement airRetLoad for {}", .{self.target.cpu.arch}); 1608 //return self.finishAir(inst, .dead, .{ un_op, .none, .none }); 1609 } 1610 1611 fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { 1612 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1613 if (self.liveness.isUnused(inst)) 1614 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); 1615 const ty = self.air.typeOf(bin_op.lhs); 1616 assert(ty.eql(self.air.typeOf(bin_op.rhs))); 1617 if (ty.zigTypeTag() == .ErrorSet) 1618 return self.fail("TODO implement cmp for errors", .{}); 1619 1620 const lhs = try self.resolveInst(bin_op.lhs); 1621 const rhs = try self.resolveInst(bin_op.rhs); 1622 _ = op; 1623 _ = lhs; 1624 _ = rhs; 1625 1626 return self.fail("TODO implement cmp for {}", .{self.target.cpu.arch}); 1627 // return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1628 } 1629 1630 fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { 1631 const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt; 1632 1633 _ = try self.addInst(.{ 1634 .tag = .dbg_line, 1635 .data = .{ .dbg_line_column = .{ 1636 .line = dbg_stmt.line, 1637 .column = dbg_stmt.column, 1638 } }, 1639 }); 1640 1641 return self.finishAirBookkeeping(); 1642 } 1643 1644 fn airDbgFunc(self: *Self, inst: Air.Inst.Index) !void { 1645 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 1646 const function = self.air.values[ty_pl.payload].castTag(.function).?.data; 1647 // TODO emit debug info for function change 1648 _ = function; 1649 return self.finishAir(inst, .dead, .{ .none, .none, .none }); 1650 } 1651 1652 fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { 1653 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 1654 const name = self.air.nullTerminatedString(pl_op.payload); 1655 const operand = pl_op.operand; 1656 // TODO emit debug info for this variable 1657 _ = name; 1658 return self.finishAir(inst, .dead, .{ operand, .none, .none }); 1659 } 1660 1661 fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { 1662 _ = inst; 1663 1664 return self.fail("TODO implement condbr {}", .{self.target.cpu.arch}); 1665 // return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); 1666 } 1667 1668 fn isNull(self: *Self, operand: MCValue) !MCValue { 1669 _ = operand; 1670 // Here you can specialize this instruction if it makes sense to, otherwise the default 1671 // will call isNonNull and invert the result. 1672 return self.fail("TODO call isNonNull and invert the result", .{}); 1673 } 1674 1675 fn isNonNull(self: *Self, operand: MCValue) !MCValue { 1676 _ = operand; 1677 // Here you can specialize this instruction if it makes sense to, otherwise the default 1678 // will call isNull and invert the result. 1679 return self.fail("TODO call isNull and invert the result", .{}); 1680 } 1681 1682 fn isErr(self: *Self, operand: MCValue) !MCValue { 1683 _ = operand; 1684 // Here you can specialize this instruction if it makes sense to, otherwise the default 1685 // will call isNonNull and invert the result. 1686 return self.fail("TODO call isNonErr and invert the result", .{}); 1687 } 1688 1689 fn isNonErr(self: *Self, operand: MCValue) !MCValue { 1690 _ = operand; 1691 // Here you can specialize this instruction if it makes sense to, otherwise the default 1692 // will call isNull and invert the result. 1693 return self.fail("TODO call isErr and invert the result", .{}); 1694 } 1695 1696 fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { 1697 const un_op = self.air.instructions.items(.data)[inst].un_op; 1698 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1699 const operand = try self.resolveInst(un_op); 1700 break :result try self.isNull(operand); 1701 }; 1702 return self.finishAir(inst, result, .{ un_op, .none, .none }); 1703 } 1704 1705 fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { 1706 const un_op = self.air.instructions.items(.data)[inst].un_op; 1707 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1708 const operand_ptr = try self.resolveInst(un_op); 1709 const operand: MCValue = blk: { 1710 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { 1711 // The MCValue that holds the pointer can be re-used as the value. 1712 break :blk operand_ptr; 1713 } else { 1714 break :blk try self.allocRegOrMem(inst, true); 1715 } 1716 }; 1717 try self.load(operand, operand_ptr, self.air.typeOf(un_op)); 1718 break :result try self.isNull(operand); 1719 }; 1720 return self.finishAir(inst, result, .{ un_op, .none, .none }); 1721 } 1722 1723 fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void { 1724 const un_op = self.air.instructions.items(.data)[inst].un_op; 1725 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1726 const operand = try self.resolveInst(un_op); 1727 break :result try self.isNonNull(operand); 1728 }; 1729 return self.finishAir(inst, result, .{ un_op, .none, .none }); 1730 } 1731 1732 fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { 1733 const un_op = self.air.instructions.items(.data)[inst].un_op; 1734 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1735 const operand_ptr = try self.resolveInst(un_op); 1736 const operand: MCValue = blk: { 1737 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { 1738 // The MCValue that holds the pointer can be re-used as the value. 1739 break :blk operand_ptr; 1740 } else { 1741 break :blk try self.allocRegOrMem(inst, true); 1742 } 1743 }; 1744 try self.load(operand, operand_ptr, self.air.typeOf(un_op)); 1745 break :result try self.isNonNull(operand); 1746 }; 1747 return self.finishAir(inst, result, .{ un_op, .none, .none }); 1748 } 1749 1750 fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { 1751 const un_op = self.air.instructions.items(.data)[inst].un_op; 1752 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1753 const operand = try self.resolveInst(un_op); 1754 break :result try self.isErr(operand); 1755 }; 1756 return self.finishAir(inst, result, .{ un_op, .none, .none }); 1757 } 1758 1759 fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { 1760 const un_op = self.air.instructions.items(.data)[inst].un_op; 1761 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1762 const operand_ptr = try self.resolveInst(un_op); 1763 const operand: MCValue = blk: { 1764 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { 1765 // The MCValue that holds the pointer can be re-used as the value. 1766 break :blk operand_ptr; 1767 } else { 1768 break :blk try self.allocRegOrMem(inst, true); 1769 } 1770 }; 1771 try self.load(operand, operand_ptr, self.air.typeOf(un_op)); 1772 break :result try self.isErr(operand); 1773 }; 1774 return self.finishAir(inst, result, .{ un_op, .none, .none }); 1775 } 1776 1777 fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { 1778 const un_op = self.air.instructions.items(.data)[inst].un_op; 1779 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1780 const operand = try self.resolveInst(un_op); 1781 break :result try self.isNonErr(operand); 1782 }; 1783 return self.finishAir(inst, result, .{ un_op, .none, .none }); 1784 } 1785 1786 fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { 1787 const un_op = self.air.instructions.items(.data)[inst].un_op; 1788 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { 1789 const operand_ptr = try self.resolveInst(un_op); 1790 const operand: MCValue = blk: { 1791 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { 1792 // The MCValue that holds the pointer can be re-used as the value. 1793 break :blk operand_ptr; 1794 } else { 1795 break :blk try self.allocRegOrMem(inst, true); 1796 } 1797 }; 1798 try self.load(operand, operand_ptr, self.air.typeOf(un_op)); 1799 break :result try self.isNonErr(operand); 1800 }; 1801 return self.finishAir(inst, result, .{ un_op, .none, .none }); 1802 } 1803 1804 fn airLoop(self: *Self, inst: Air.Inst.Index) !void { 1805 // A loop is a setup to be able to jump back to the beginning. 1806 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 1807 const loop = self.air.extraData(Air.Block, ty_pl.payload); 1808 const body = self.air.extra[loop.end..][0..loop.data.body_len]; 1809 const start_index = self.code.items.len; 1810 try self.genBody(body); 1811 try self.jump(start_index); 1812 return self.finishAirBookkeeping(); 1813 } 1814 1815 /// Send control flow to the `index` of `self.code`. 1816 fn jump(self: *Self, index: usize) !void { 1817 _ = index; 1818 return self.fail("TODO implement jump for {}", .{self.target.cpu.arch}); 1819 } 1820 1821 fn airBlock(self: *Self, inst: Air.Inst.Index) !void { 1822 try self.blocks.putNoClobber(self.gpa, inst, .{ 1823 // A block is a setup to be able to jump to the end. 1824 .relocs = .{}, 1825 // It also acts as a receptacle for break operands. 1826 // Here we use `MCValue.none` to represent a null value so that the first 1827 // break instruction will choose a MCValue for the block result and overwrite 1828 // this field. Following break instructions will use that MCValue to put their 1829 // block results. 1830 .mcv = MCValue{ .none = {} }, 1831 }); 1832 defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); 1833 1834 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 1835 const extra = self.air.extraData(Air.Block, ty_pl.payload); 1836 const body = self.air.extra[extra.end..][0..extra.data.body_len]; 1837 try self.genBody(body); 1838 1839 for (self.blocks.getPtr(inst).?.relocs.items) |reloc| try self.performReloc(reloc); 1840 1841 const result = self.blocks.getPtr(inst).?.mcv; 1842 return self.finishAir(inst, result, .{ .none, .none, .none }); 1843 } 1844 1845 fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { 1846 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 1847 const condition = pl_op.operand; 1848 _ = condition; 1849 return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch}); 1850 // return self.finishAir(inst, .dead, .{ condition, .none, .none }); 1851 } 1852 1853 fn performReloc(self: *Self, reloc: Reloc) !void { 1854 _ = self; 1855 switch (reloc) { 1856 .rel32 => unreachable, 1857 .arm_branch => unreachable, 1858 } 1859 } 1860 1861 fn airBr(self: *Self, inst: Air.Inst.Index) !void { 1862 const branch = self.air.instructions.items(.data)[inst].br; 1863 try self.br(branch.block_inst, branch.operand); 1864 return self.finishAir(inst, .dead, .{ branch.operand, .none, .none }); 1865 } 1866 1867 fn airBoolOp(self: *Self, inst: Air.Inst.Index) !void { 1868 const bin_op = self.air.instructions.items(.data)[inst].bin_op; 1869 const air_tags = self.air.instructions.items(.tag); 1870 _ = air_tags; 1871 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement boolean operations for {}", .{self.target.cpu.arch}); 1872 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 1873 } 1874 1875 fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { 1876 const block_data = self.blocks.getPtr(block).?; 1877 1878 if (self.air.typeOf(operand).hasRuntimeBits()) { 1879 const operand_mcv = try self.resolveInst(operand); 1880 const block_mcv = block_data.mcv; 1881 if (block_mcv == .none) { 1882 block_data.mcv = operand_mcv; 1883 } else { 1884 try self.setRegOrMem(self.air.typeOfIndex(block), block_mcv, operand_mcv); 1885 } 1886 } 1887 return self.brVoid(block); 1888 } 1889 1890 fn brVoid(self: *Self, block: Air.Inst.Index) !void { 1891 const block_data = self.blocks.getPtr(block).?; 1892 1893 // Emit a jump with a relocation. It will be patched up after the block ends. 1894 try block_data.relocs.ensureUnusedCapacity(self.gpa, 1); 1895 1896 return self.fail("TODO implement brvoid for {}", .{self.target.cpu.arch}); 1897 } 1898 1899 fn airAsm(self: *Self, inst: Air.Inst.Index) !void { 1900 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 1901 const extra = self.air.extraData(Air.Asm, ty_pl.payload); 1902 const is_volatile = @truncate(u1, extra.data.flags >> 31) != 0; 1903 const clobbers_len = @truncate(u31, extra.data.flags); 1904 var extra_i: usize = extra.end; 1905 const outputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.outputs_len]); 1906 extra_i += outputs.len; 1907 const inputs = @bitCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); 1908 extra_i += inputs.len; 1909 1910 const dead = !is_volatile and self.liveness.isUnused(inst); 1911 const result: MCValue = if (dead) .dead else result: { 1912 if (outputs.len > 1) { 1913 return self.fail("TODO implement codegen for asm with more than 1 output", .{}); 1914 } 1915 1916 const output_constraint: ?[]const u8 = for (outputs) |output| { 1917 if (output != .none) { 1918 return self.fail("TODO implement codegen for non-expr asm", .{}); 1919 } 1920 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); 1921 // This equation accounts for the fact that even if we have exactly 4 bytes 1922 // for the string, we still use the next u32 for the null terminator. 1923 extra_i += constraint.len / 4 + 1; 1924 1925 break constraint; 1926 } else null; 1927 1928 for (inputs) |input| { 1929 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); 1930 // This equation accounts for the fact that even if we have exactly 4 bytes 1931 // for the string, we still use the next u32 for the null terminator. 1932 extra_i += constraint.len / 4 + 1; 1933 1934 if (constraint.len < 3 or constraint[0] != '{' or constraint[constraint.len - 1] != '}') { 1935 return self.fail("unrecognized asm input constraint: '{s}'", .{constraint}); 1936 } 1937 const reg_name = constraint[1 .. constraint.len - 1]; 1938 const reg = parseRegName(reg_name) orelse 1939 return self.fail("unrecognized register: '{s}'", .{reg_name}); 1940 1941 const arg_mcv = try self.resolveInst(input); 1942 try self.register_manager.getReg(reg, null); 1943 try self.genSetReg(self.air.typeOf(input), reg, arg_mcv); 1944 } 1945 1946 { 1947 var clobber_i: u32 = 0; 1948 while (clobber_i < clobbers_len) : (clobber_i += 1) { 1949 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); 1950 // This equation accounts for the fact that even if we have exactly 4 bytes 1951 // for the string, we still use the next u32 for the null terminator. 1952 extra_i += clobber.len / 4 + 1; 1953 1954 // TODO honor these 1955 } 1956 } 1957 1958 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; 1959 1960 if (mem.eql(u8, asm_source, "ecall")) { 1961 _ = try self.addInst(.{ 1962 .tag = .ecall, 1963 .data = .{ .nop = {} }, 1964 }); 1965 } else { 1966 return self.fail("TODO implement support for more riscv64 assembly instructions", .{}); 1967 } 1968 1969 if (output_constraint) |output| { 1970 if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { 1971 return self.fail("unrecognized asm output constraint: '{s}'", .{output}); 1972 } 1973 const reg_name = output[2 .. output.len - 1]; 1974 const reg = parseRegName(reg_name) orelse 1975 return self.fail("unrecognized register: '{s}'", .{reg_name}); 1976 break :result MCValue{ .register = reg }; 1977 } else { 1978 break :result MCValue{ .none = {} }; 1979 } 1980 }; 1981 simple: { 1982 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); 1983 var buf_index: usize = 0; 1984 for (outputs) |output| { 1985 if (output == .none) continue; 1986 1987 if (buf_index >= buf.len) break :simple; 1988 buf[buf_index] = output; 1989 buf_index += 1; 1990 } 1991 if (buf_index + inputs.len > buf.len) break :simple; 1992 std.mem.copy(Air.Inst.Ref, buf[buf_index..], inputs); 1993 return self.finishAir(inst, result, buf); 1994 } 1995 var bt = try self.iterateBigTomb(inst, outputs.len + inputs.len); 1996 for (outputs) |output| { 1997 if (output == .none) continue; 1998 1999 bt.feed(output); 2000 } 2001 for (inputs) |input| { 2002 bt.feed(input); 2003 } 2004 return bt.finishAir(result); 2005 } 2006 2007 fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb { 2008 try self.ensureProcessDeathCapacity(operand_count + 1); 2009 return BigTomb{ 2010 .function = self, 2011 .inst = inst, 2012 .tomb_bits = self.liveness.getTombBits(inst), 2013 .big_tomb_bits = self.liveness.special.get(inst) orelse 0, 2014 .bit_index = 0, 2015 }; 2016 } 2017 2018 /// Sets the value without any modifications to register allocation metadata or stack allocation metadata. 2019 fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { 2020 switch (loc) { 2021 .none => return, 2022 .register => |reg| return self.genSetReg(ty, reg, val), 2023 .stack_offset => |off| return self.genSetStack(ty, off, val), 2024 .memory => { 2025 return self.fail("TODO implement setRegOrMem for memory", .{}); 2026 }, 2027 else => unreachable, 2028 } 2029 } 2030 2031 fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { 2032 _ = ty; 2033 _ = stack_offset; 2034 _ = mcv; 2035 return self.fail("TODO implement getSetStack for {}", .{self.target.cpu.arch}); 2036 } 2037 2038 fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { 2039 switch (mcv) { 2040 .dead => unreachable, 2041 .ptr_stack_offset => unreachable, 2042 .ptr_embedded_in_code => unreachable, 2043 .unreach, .none => return, // Nothing to do. 2044 .undef => { 2045 if (!self.wantSafety()) 2046 return; // The already existing value will do just fine. 2047 // Write the debug undefined value. 2048 return self.genSetReg(ty, reg, .{ .immediate = 0xaaaaaaaaaaaaaaaa }); 2049 }, 2050 .immediate => |unsigned_x| { 2051 const x = @bitCast(i64, unsigned_x); 2052 if (math.minInt(i12) <= x and x <= math.maxInt(i12)) { 2053 _ = try self.addInst(.{ 2054 .tag = .addi, 2055 .data = .{ .i_type = .{ 2056 .rd = reg, 2057 .rs1 = .zero, 2058 .imm12 = @intCast(i12, x), 2059 } }, 2060 }); 2061 } else if (math.minInt(i32) <= x and x <= math.maxInt(i32)) { 2062 const lo12 = @truncate(i12, x); 2063 const carry: i32 = if (lo12 < 0) 1 else 0; 2064 const hi20 = @truncate(i20, (x >> 12) +% carry); 2065 2066 // TODO: add test case for 32-bit immediate 2067 _ = try self.addInst(.{ 2068 .tag = .lui, 2069 .data = .{ .u_type = .{ 2070 .rd = reg, 2071 .imm20 = hi20, 2072 } }, 2073 }); 2074 _ = try self.addInst(.{ 2075 .tag = .addi, 2076 .data = .{ .i_type = .{ 2077 .rd = reg, 2078 .rs1 = reg, 2079 .imm12 = lo12, 2080 } }, 2081 }); 2082 } else { 2083 // li rd, immediate 2084 // "Myriad sequences" 2085 return self.fail("TODO genSetReg 33-64 bit immediates for riscv64", .{}); // glhf 2086 } 2087 }, 2088 .memory => |addr| { 2089 // The value is in memory at a hard-coded address. 2090 // If the type is a pointer, it means the pointer address is at this memory location. 2091 try self.genSetReg(ty, reg, .{ .immediate = addr }); 2092 2093 _ = try self.addInst(.{ 2094 .tag = .ld, 2095 .data = .{ .i_type = .{ 2096 .rd = reg, 2097 .rs1 = reg, 2098 .imm12 = 0, 2099 } }, 2100 }); 2101 // LOAD imm=[i12 offset = 0], rs1 = 2102 2103 // return self.fail("TODO implement genSetReg memory for riscv64"); 2104 }, 2105 else => return self.fail("TODO implement getSetReg for riscv64 {}", .{mcv}), 2106 } 2107 } 2108 2109 fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void { 2110 const un_op = self.air.instructions.items(.data)[inst].un_op; 2111 const result = try self.resolveInst(un_op); 2112 return self.finishAir(inst, result, .{ un_op, .none, .none }); 2113 } 2114 2115 fn airBitCast(self: *Self, inst: Air.Inst.Index) !void { 2116 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2117 const result = try self.resolveInst(ty_op.operand); 2118 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2119 } 2120 2121 fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void { 2122 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2123 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airArrayToSlice for {}", .{ 2124 self.target.cpu.arch, 2125 }); 2126 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2127 } 2128 2129 fn airIntToFloat(self: *Self, inst: Air.Inst.Index) !void { 2130 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2131 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airIntToFloat for {}", .{ 2132 self.target.cpu.arch, 2133 }); 2134 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2135 } 2136 2137 fn airFloatToInt(self: *Self, inst: Air.Inst.Index) !void { 2138 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2139 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFloatToInt for {}", .{ 2140 self.target.cpu.arch, 2141 }); 2142 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2143 } 2144 2145 fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { 2146 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 2147 const extra = self.air.extraData(Air.Block, ty_pl.payload); 2148 _ = extra; 2149 return self.fail("TODO implement airCmpxchg for {}", .{ 2150 self.target.cpu.arch, 2151 }); 2152 // return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value }); 2153 } 2154 2155 fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void { 2156 _ = inst; 2157 return self.fail("TODO implement airCmpxchg for {}", .{self.target.cpu.arch}); 2158 } 2159 2160 fn airAtomicLoad(self: *Self, inst: Air.Inst.Index) !void { 2161 _ = inst; 2162 return self.fail("TODO implement airAtomicLoad for {}", .{self.target.cpu.arch}); 2163 } 2164 2165 fn airAtomicStore(self: *Self, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void { 2166 _ = inst; 2167 _ = order; 2168 return self.fail("TODO implement airAtomicStore for {}", .{self.target.cpu.arch}); 2169 } 2170 2171 fn airMemset(self: *Self, inst: Air.Inst.Index) !void { 2172 _ = inst; 2173 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch}); 2174 } 2175 2176 fn airMemcpy(self: *Self, inst: Air.Inst.Index) !void { 2177 _ = inst; 2178 return self.fail("TODO implement airMemcpy for {}", .{self.target.cpu.arch}); 2179 } 2180 2181 fn airTagName(self: *Self, inst: Air.Inst.Index) !void { 2182 const un_op = self.air.instructions.items(.data)[inst].un_op; 2183 const operand = try self.resolveInst(un_op); 2184 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { 2185 _ = operand; 2186 return self.fail("TODO implement airTagName for riscv64", .{}); 2187 }; 2188 return self.finishAir(inst, result, .{ un_op, .none, .none }); 2189 } 2190 2191 fn airErrorName(self: *Self, inst: Air.Inst.Index) !void { 2192 const un_op = self.air.instructions.items(.data)[inst].un_op; 2193 const operand = try self.resolveInst(un_op); 2194 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { 2195 _ = operand; 2196 return self.fail("TODO implement airErrorName for riscv64", .{}); 2197 }; 2198 return self.finishAir(inst, result, .{ un_op, .none, .none }); 2199 } 2200 2201 fn airSplat(self: *Self, inst: Air.Inst.Index) !void { 2202 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2203 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSplat for riscv64", .{}); 2204 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2205 } 2206 2207 fn airShuffle(self: *Self, inst: Air.Inst.Index) !void { 2208 const ty_op = self.air.instructions.items(.data)[inst].ty_op; 2209 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airShuffle for riscv64", .{}); 2210 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 2211 } 2212 2213 fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void { 2214 const vector_ty = self.air.typeOfIndex(inst); 2215 const len = vector_ty.vectorLen(); 2216 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 2217 const elements = @bitCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]); 2218 const result: MCValue = res: { 2219 if (self.liveness.isUnused(inst)) break :res MCValue.dead; 2220 return self.fail("TODO implement airAggregateInit for riscv64", .{}); 2221 }; 2222 2223 if (elements.len <= Liveness.bpi - 1) { 2224 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); 2225 std.mem.copy(Air.Inst.Ref, &buf, elements); 2226 return self.finishAir(inst, result, buf); 2227 } 2228 var bt = try self.iterateBigTomb(inst, elements.len); 2229 for (elements) |elem| { 2230 bt.feed(elem); 2231 } 2232 return bt.finishAir(result); 2233 } 2234 2235 fn airUnionInit(self: *Self, inst: Air.Inst.Index) !void { 2236 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; 2237 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; 2238 _ = extra; 2239 return self.fail("TODO implement airUnionInit for riscv64", .{}); 2240 // return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value }); 2241 } 2242 2243 fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void { 2244 const prefetch = self.air.instructions.items(.data)[inst].prefetch; 2245 return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none }); 2246 } 2247 2248 fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void { 2249 const pl_op = self.air.instructions.items(.data)[inst].pl_op; 2250 const extra = self.air.extraData(Air.Bin, pl_op.payload).data; 2251 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else { 2252 return self.fail("TODO implement airMulAdd for riscv64", .{}); 2253 }; 2254 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand }); 2255 } 2256 2257 fn resolveInst(self: *Self, inst: Air.Inst.Ref) InnerError!MCValue { 2258 // First section of indexes correspond to a set number of constant values. 2259 const ref_int = @enumToInt(inst); 2260 if (ref_int < Air.Inst.Ref.typed_value_map.len) { 2261 const tv = Air.Inst.Ref.typed_value_map[ref_int]; 2262 if (!tv.ty.hasRuntimeBits()) { 2263 return MCValue{ .none = {} }; 2264 } 2265 return self.genTypedValue(tv); 2266 } 2267 2268 // If the type has no codegen bits, no need to store it. 2269 const inst_ty = self.air.typeOf(inst); 2270 if (!inst_ty.hasRuntimeBits()) 2271 return MCValue{ .none = {} }; 2272 2273 const inst_index = @intCast(Air.Inst.Index, ref_int - Air.Inst.Ref.typed_value_map.len); 2274 switch (self.air.instructions.items(.tag)[inst_index]) { 2275 .constant => { 2276 // Constants have static lifetimes, so they are always memoized in the outer most table. 2277 const branch = &self.branch_stack.items[0]; 2278 const gop = try branch.inst_table.getOrPut(self.gpa, inst_index); 2279 if (!gop.found_existing) { 2280 const ty_pl = self.air.instructions.items(.data)[inst_index].ty_pl; 2281 gop.value_ptr.* = try self.genTypedValue(.{ 2282 .ty = inst_ty, 2283 .val = self.air.values[ty_pl.payload], 2284 }); 2285 } 2286 return gop.value_ptr.*; 2287 }, 2288 .const_ty => unreachable, 2289 else => return self.getResolvedInstValue(inst_index), 2290 } 2291 } 2292 2293 fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { 2294 // Treat each stack item as a "layer" on top of the previous one. 2295 var i: usize = self.branch_stack.items.len; 2296 while (true) { 2297 i -= 1; 2298 if (self.branch_stack.items[i].inst_table.get(inst)) |mcv| { 2299 assert(mcv != .dead); 2300 return mcv; 2301 } 2302 } 2303 } 2304 2305 /// If the MCValue is an immediate, and it does not fit within this type, 2306 /// we put it in a register. 2307 /// A potential opportunity for future optimization here would be keeping track 2308 /// of the fact that the instruction is available both as an immediate 2309 /// and as a register. 2310 fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCValue { 2311 const mcv = try self.resolveInst(operand); 2312 const ti = @typeInfo(T).Int; 2313 switch (mcv) { 2314 .immediate => |imm| { 2315 // This immediate is unsigned. 2316 const U = std.meta.Int(.unsigned, ti.bits - @boolToInt(ti.signedness == .signed)); 2317 if (imm >= math.maxInt(U)) { 2318 return MCValue{ .register = try self.copyToTmpRegister(Type.initTag(.usize), mcv) }; 2319 } 2320 }, 2321 else => {}, 2322 } 2323 return mcv; 2324 } 2325 2326 fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCValue { 2327 const ptr_bits = self.target.cpu.arch.ptrBitWidth(); 2328 const ptr_bytes: u64 = @divExact(ptr_bits, 8); 2329 decl.alive = true; 2330 if (self.bin_file.cast(link.File.Elf)) |elf_file| { 2331 const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; 2332 const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; 2333 return MCValue{ .memory = got_addr }; 2334 } else if (self.bin_file.cast(link.File.MachO)) |_| { 2335 // TODO I'm hacking my way through here by repurposing .memory for storing 2336 // index to the GOT target symbol index. 2337 return MCValue{ .memory = decl.link.macho.local_sym_index }; 2338 } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { 2339 const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; 2340 return MCValue{ .memory = got_addr }; 2341 } else if (self.bin_file.cast(link.File.Plan9)) |p9| { 2342 try p9.seeDecl(decl); 2343 const got_addr = p9.bases.data + decl.link.plan9.got_index.? * ptr_bytes; 2344 return MCValue{ .memory = got_addr }; 2345 } else { 2346 return self.fail("TODO codegen non-ELF const Decl pointer", .{}); 2347 } 2348 _ = tv; 2349 } 2350 2351 fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { 2352 if (typed_value.val.isUndef()) 2353 return MCValue{ .undef = {} }; 2354 2355 if (typed_value.val.castTag(.decl_ref)) |payload| { 2356 return self.lowerDeclRef(typed_value, payload.data); 2357 } 2358 if (typed_value.val.castTag(.decl_ref_mut)) |payload| { 2359 return self.lowerDeclRef(typed_value, payload.data.decl); 2360 } 2361 const ptr_bits = self.target.cpu.arch.ptrBitWidth(); 2362 switch (typed_value.ty.zigTypeTag()) { 2363 .Pointer => switch (typed_value.ty.ptrSize()) { 2364 .Slice => { 2365 var buf: Type.SlicePtrFieldTypeBuffer = undefined; 2366 const ptr_type = typed_value.ty.slicePtrFieldType(&buf); 2367 const ptr_mcv = try self.genTypedValue(.{ .ty = ptr_type, .val = typed_value.val }); 2368 const slice_len = typed_value.val.sliceLen(); 2369 // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean 2370 // the Sema code needs to use anonymous Decls or alloca instructions to store data. 2371 const ptr_imm = ptr_mcv.memory; 2372 _ = slice_len; 2373 _ = ptr_imm; 2374 // We need more general support for const data being stored in memory to make this work. 2375 return self.fail("TODO codegen for const slices", .{}); 2376 }, 2377 else => { 2378 if (typed_value.val.tag() == .int_u64) { 2379 return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; 2380 } 2381 return self.fail("TODO codegen more kinds of const pointers", .{}); 2382 }, 2383 }, 2384 .Int => { 2385 const info = typed_value.ty.intInfo(self.target.*); 2386 if (info.bits > ptr_bits or info.signedness == .signed) { 2387 return self.fail("TODO const int bigger than ptr and signed int", .{}); 2388 } 2389 return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; 2390 }, 2391 .Bool => { 2392 return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; 2393 }, 2394 .ComptimeInt => unreachable, // semantic analysis prevents this 2395 .ComptimeFloat => unreachable, // semantic analysis prevents this 2396 .Optional => { 2397 if (typed_value.ty.isPtrLikeOptional()) { 2398 if (typed_value.val.isNull()) 2399 return MCValue{ .immediate = 0 }; 2400 2401 var buf: Type.Payload.ElemType = undefined; 2402 return self.genTypedValue(.{ 2403 .ty = typed_value.ty.optionalChild(&buf), 2404 .val = typed_value.val, 2405 }); 2406 } else if (typed_value.ty.abiSize(self.target.*) == 1) { 2407 return MCValue{ .immediate = @boolToInt(typed_value.val.isNull()) }; 2408 } 2409 return self.fail("TODO non pointer optionals", .{}); 2410 }, 2411 .Enum => { 2412 if (typed_value.val.castTag(.enum_field_index)) |field_index| { 2413 switch (typed_value.ty.tag()) { 2414 .enum_simple => { 2415 return MCValue{ .immediate = field_index.data }; 2416 }, 2417 .enum_full, .enum_nonexhaustive => { 2418 const enum_full = typed_value.ty.cast(Type.Payload.EnumFull).?.data; 2419 if (enum_full.values.count() != 0) { 2420 const tag_val = enum_full.values.keys()[field_index.data]; 2421 return self.genTypedValue(.{ .ty = enum_full.tag_ty, .val = tag_val }); 2422 } else { 2423 return MCValue{ .immediate = field_index.data }; 2424 } 2425 }, 2426 else => unreachable, 2427 } 2428 } else { 2429 var int_tag_buffer: Type.Payload.Bits = undefined; 2430 const int_tag_ty = typed_value.ty.intTagType(&int_tag_buffer); 2431 return self.genTypedValue(.{ .ty = int_tag_ty, .val = typed_value.val }); 2432 } 2433 }, 2434 .ErrorSet => { 2435 switch (typed_value.val.tag()) { 2436 .@"error" => { 2437 const err_name = typed_value.val.castTag(.@"error").?.data.name; 2438 const module = self.bin_file.options.module.?; 2439 const global_error_set = module.global_error_set; 2440 const error_index = global_error_set.get(err_name).?; 2441 return MCValue{ .immediate = error_index }; 2442 }, 2443 else => { 2444 // In this case we are rendering an error union which has a 0 bits payload. 2445 return MCValue{ .immediate = 0 }; 2446 }, 2447 } 2448 }, 2449 .ErrorUnion => { 2450 const error_type = typed_value.ty.errorUnionSet(); 2451 const payload_type = typed_value.ty.errorUnionPayload(); 2452 const sub_val = typed_value.val.castTag(.eu_payload).?.data; 2453 2454 if (!payload_type.hasRuntimeBits()) { 2455 // We use the error type directly as the type. 2456 return self.genTypedValue(.{ .ty = error_type, .val = sub_val }); 2457 } 2458 2459 return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty}); 2460 }, 2461 else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}), 2462 } 2463 } 2464 2465 const CallMCValues = struct { 2466 args: []MCValue, 2467 return_value: MCValue, 2468 stack_byte_count: u32, 2469 stack_align: u32, 2470 2471 fn deinit(self: *CallMCValues, func: *Self) void { 2472 func.gpa.free(self.args); 2473 self.* = undefined; 2474 } 2475 }; 2476 2477 /// Caller must call `CallMCValues.deinit`. 2478 fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { 2479 const cc = fn_ty.fnCallingConvention(); 2480 const param_types = try self.gpa.alloc(Type, fn_ty.fnParamLen()); 2481 defer self.gpa.free(param_types); 2482 fn_ty.fnParamTypes(param_types); 2483 var result: CallMCValues = .{ 2484 .args = try self.gpa.alloc(MCValue, param_types.len), 2485 // These undefined values must be populated before returning from this function. 2486 .return_value = undefined, 2487 .stack_byte_count = undefined, 2488 .stack_align = undefined, 2489 }; 2490 errdefer self.gpa.free(result.args); 2491 2492 const ret_ty = fn_ty.fnReturnType(); 2493 2494 switch (cc) { 2495 .Naked => { 2496 assert(result.args.len == 0); 2497 result.return_value = .{ .unreach = {} }; 2498 result.stack_byte_count = 0; 2499 result.stack_align = 1; 2500 return result; 2501 }, 2502 .Unspecified, .C => { 2503 // LP64D ABI 2504 // 2505 // TODO make this generic with other ABIs, in particular 2506 // with different hardware floating-point calling 2507 // conventions 2508 var next_register: usize = 0; 2509 var next_stack_offset: u32 = 0; 2510 const argument_registers = [_]Register{ .a0, .a1, .a2, .a3, .a4, .a5, .a6, .a7 }; 2511 2512 for (param_types) |ty, i| { 2513 const param_size = @intCast(u32, ty.abiSize(self.target.*)); 2514 if (param_size <= 8) { 2515 if (next_register < argument_registers.len) { 2516 result.args[i] = .{ .register = argument_registers[next_register] }; 2517 next_register += 1; 2518 } else { 2519 result.args[i] = .{ .stack_offset = next_stack_offset }; 2520 next_register += next_stack_offset; 2521 } 2522 } else if (param_size <= 16) { 2523 if (next_register < argument_registers.len - 1) { 2524 return self.fail("TODO MCValues with 2 registers", .{}); 2525 } else if (next_register < argument_registers.len) { 2526 return self.fail("TODO MCValues split register + stack", .{}); 2527 } else { 2528 result.args[i] = .{ .stack_offset = next_stack_offset }; 2529 next_register += next_stack_offset; 2530 } 2531 } else { 2532 result.args[i] = .{ .stack_offset = next_stack_offset }; 2533 next_register += next_stack_offset; 2534 } 2535 } 2536 2537 result.stack_byte_count = next_stack_offset; 2538 result.stack_align = 16; 2539 }, 2540 else => return self.fail("TODO implement function parameters for {} on riscv64", .{cc}), 2541 } 2542 2543 if (ret_ty.zigTypeTag() == .NoReturn) { 2544 result.return_value = .{ .unreach = {} }; 2545 } else if (!ret_ty.hasRuntimeBits()) { 2546 result.return_value = .{ .none = {} }; 2547 } else switch (cc) { 2548 .Naked => unreachable, 2549 .Unspecified, .C => { 2550 const ret_ty_size = @intCast(u32, ret_ty.abiSize(self.target.*)); 2551 if (ret_ty_size <= 8) { 2552 result.return_value = .{ .register = .a0 }; 2553 } else if (ret_ty_size <= 16) { 2554 return self.fail("TODO support MCValue 2 registers", .{}); 2555 } else { 2556 return self.fail("TODO support return by reference", .{}); 2557 } 2558 }, 2559 else => return self.fail("TODO implement function return values for {}", .{cc}), 2560 } 2561 return result; 2562 } 2563 2564 /// TODO support scope overrides. Also note this logic is duplicated with `Module.wantSafety`. 2565 fn wantSafety(self: *Self) bool { 2566 return switch (self.bin_file.options.optimize_mode) { 2567 .Debug => true, 2568 .ReleaseSafe => true, 2569 .ReleaseFast => false, 2570 .ReleaseSmall => false, 2571 }; 2572 } 2573 2574 fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError { 2575 @setCold(true); 2576 assert(self.err_msg == null); 2577 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); 2578 return error.CodegenFail; 2579 } 2580 2581 fn failSymbol(self: *Self, comptime format: []const u8, args: anytype) InnerError { 2582 @setCold(true); 2583 assert(self.err_msg == null); 2584 self.err_msg = try ErrorMsg.create(self.bin_file.allocator, self.src_loc, format, args); 2585 return error.CodegenFail; 2586 } 2587 2588 fn parseRegName(name: []const u8) ?Register { 2589 if (@hasDecl(Register, "parseRegName")) { 2590 return Register.parseRegName(name); 2591 } 2592 return std.meta.stringToEnum(Register, name); 2593 }