| ... | ... | @@ -33,7 +33,6 @@ const abi = @import("abi.zig"); |
| 33 | 33 | const Register = bits.Register; |
| 34 | 34 | const RegisterManager = abi.RegisterManager; |
| 35 | 35 | const RegisterLock = RegisterManager.RegisterLock; |
| 36 | | const Instruction = abi.Instruction; |
| 37 | 36 | const callee_preserved_regs = abi.callee_preserved_regs; |
| 38 | 37 | const gp = abi.RegisterClass.gp; |
| 39 | 38 | |
| ... | ... | @@ -96,6 +95,8 @@ air_bookkeeping: @TypeOf(air_bookkeeping_init) = air_bookkeeping_init, |
| 96 | 95 | |
| 97 | 96 | const air_bookkeeping_init = if (std.debug.runtime_safety) @as(usize, 0) else {}; |
| 98 | 97 | |
| 98 | const SymbolOffset = struct { sym: u32, off: i32 = 0 }; |
| 99 | |
| 99 | 100 | const MCValue = union(enum) { |
| 100 | 101 | /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. |
| 101 | 102 | /// TODO Look into deleting this tag and using `dead` instead, since every use |
| ... | ... | @@ -110,6 +111,9 @@ const MCValue = union(enum) { |
| 110 | 111 | /// A pointer-sized integer that fits in a register. |
| 111 | 112 | /// If the type is a pointer, this is the pointer address in virtual address space. |
| 112 | 113 | immediate: u64, |
| 114 | /// The value is in memory at an address not-yet-allocated by the linker. |
| 115 | /// This traditionally corresponds to a relocation emitted in a relocatable object file. |
| 116 | load_symbol: SymbolOffset, |
| 113 | 117 | /// The value is in a target-specific register. |
| 114 | 118 | register: Register, |
| 115 | 119 | /// The value is in memory at a hard-coded address. |
| ... | ... | @@ -145,6 +149,7 @@ const MCValue = union(enum) { |
| 145 | 149 | .memory, |
| 146 | 150 | .ptr_stack_offset, |
| 147 | 151 | .undef, |
| 152 | .load_symbol, |
| 148 | 153 | => false, |
| 149 | 154 | |
| 150 | 155 | .register, |
| ... | ... | @@ -165,12 +170,12 @@ const Branch = struct { |
| 165 | 170 | |
| 166 | 171 | const StackAllocation = struct { |
| 167 | 172 | inst: Air.Inst.Index, |
| 168 | | /// TODO do we need size? should be determined by inst.ty.abiSize() |
| 173 | /// TODO: make the size inferred from the bits of the inst |
| 169 | 174 | size: u32, |
| 170 | 175 | }; |
| 171 | 176 | |
| 172 | 177 | const BlockData = struct { |
| 173 | | relocs: std.ArrayListUnmanaged(Reloc), |
| 178 | relocs: std.ArrayListUnmanaged(Mir.Inst.Index), |
| 174 | 179 | /// The first break instruction encounters `null` here and chooses a |
| 175 | 180 | /// machine code value for the block result, populating this field. |
| 176 | 181 | /// Following break instructions encounter that value and use it for |
| ... | ... | @@ -178,18 +183,6 @@ const BlockData = struct { |
| 178 | 183 | mcv: MCValue, |
| 179 | 184 | }; |
| 180 | 185 | |
| 181 | | const Reloc = union(enum) { |
| 182 | | /// The value is an offset into the `Function` `code` from the beginning. |
| 183 | | /// To perform the reloc, write 32-bit signed little-endian integer |
| 184 | | /// which is a relative jump, based on the address following the reloc. |
| 185 | | rel32: usize, |
| 186 | | /// A branch in the ARM instruction set |
| 187 | | arm_branch: struct { |
| 188 | | pos: usize, |
| 189 | | cond: @import("../arm/bits.zig").Condition, |
| 190 | | }, |
| 191 | | }; |
| 192 | | |
| 193 | 186 | const BigTomb = struct { |
| 194 | 187 | function: *Self, |
| 195 | 188 | inst: Air.Inst.Index, |
| ... | ... | @@ -272,6 +265,7 @@ pub fn generate( |
| 272 | 265 | }, |
| 273 | 266 | else => |e| return e, |
| 274 | 267 | }; |
| 268 | |
| 275 | 269 | defer call_info.deinit(&function); |
| 276 | 270 | |
| 277 | 271 | function.args = call_info.args; |
| ... | ... | @@ -328,6 +322,13 @@ fn addInst(self: *Self, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { |
| 328 | 322 | return result_index; |
| 329 | 323 | } |
| 330 | 324 | |
| 325 | fn addNop(self: *Self) error{OutOfMemory}!Mir.Inst.Index { |
| 326 | return try self.addInst(.{ |
| 327 | .tag = .nop, |
| 328 | .data = .{ .nop = {} }, |
| 329 | }); |
| 330 | } |
| 331 | |
| 331 | 332 | pub fn addExtra(self: *Self, extra: anytype) Allocator.Error!u32 { |
| 332 | 333 | const fields = std.meta.fields(@TypeOf(extra)); |
| 333 | 334 | try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len); |
| ... | ... | @@ -350,115 +351,45 @@ pub fn addExtraAssumeCapacity(self: *Self, extra: anytype) u32 { |
| 350 | 351 | fn gen(self: *Self) !void { |
| 351 | 352 | const mod = self.bin_file.comp.module.?; |
| 352 | 353 | const cc = self.fn_type.fnCallingConvention(mod); |
| 353 | | if (cc != .Naked) { |
| 354 | | // TODO Finish function prologue and epilogue for riscv64. |
| 355 | | |
| 356 | | // TODO Backpatch stack offset |
| 357 | | // addi sp, sp, -16 |
| 358 | | _ = try self.addInst(.{ |
| 359 | | .tag = .addi, |
| 360 | | .data = .{ .i_type = .{ |
| 361 | | .rd = .sp, |
| 362 | | .rs1 = .sp, |
| 363 | | .imm12 = -16, |
| 364 | | } }, |
| 365 | | }); |
| 366 | 354 | |
| 367 | | // sd ra, 8(sp) |
| 368 | | _ = try self.addInst(.{ |
| 369 | | .tag = .sd, |
| 370 | | .data = .{ .i_type = .{ |
| 371 | | .rd = .ra, |
| 372 | | .rs1 = .sp, |
| 373 | | .imm12 = 8, |
| 374 | | } }, |
| 375 | | }); |
| 376 | | |
| 377 | | // sd s0, 0(sp) |
| 378 | | _ = try self.addInst(.{ |
| 379 | | .tag = .sd, |
| 380 | | .data = .{ .i_type = .{ |
| 381 | | .rd = .s0, |
| 382 | | .rs1 = .sp, |
| 383 | | .imm12 = 0, |
| 384 | | } }, |
| 385 | | }); |
| 355 | if (cc == .Naked) return self.fail("TODO: gen support callconv(.{s})", .{@tagName(cc)}); |
| 386 | 356 | |
| 387 | | _ = try self.addInst(.{ |
| 388 | | .tag = .dbg_prologue_end, |
| 389 | | .data = .{ .nop = {} }, |
| 390 | | }); |
| 391 | | |
| 392 | | try self.genBody(self.air.getMainBody()); |
| 393 | | |
| 394 | | _ = try self.addInst(.{ |
| 395 | | .tag = .dbg_epilogue_begin, |
| 396 | | .data = .{ .nop = {} }, |
| 397 | | }); |
| 398 | | |
| 399 | | // exitlude jumps |
| 400 | | if (self.exitlude_jump_relocs.items.len > 0 and |
| 401 | | self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2) |
| 402 | | { |
| 403 | | // If the last Mir instruction (apart from the |
| 404 | | // dbg_epilogue_begin) is the last exitlude jump |
| 405 | | // relocation (which would just jump one instruction |
| 406 | | // further), it can be safely removed |
| 407 | | self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop()); |
| 408 | | } |
| 409 | | |
| 410 | | for (self.exitlude_jump_relocs.items) |jmp_reloc| { |
| 411 | | _ = jmp_reloc; |
| 412 | | return self.fail("TODO add branches in RISCV64", .{}); |
| 413 | | } |
| 357 | _ = try self.addInst(.{ |
| 358 | .tag = .psuedo_prologue, |
| 359 | .data = .{ .imm12 = 0 }, // Backpatched later. |
| 360 | }); |
| 414 | 361 | |
| 415 | | // ld ra, 8(sp) |
| 416 | | _ = try self.addInst(.{ |
| 417 | | .tag = .ld, |
| 418 | | .data = .{ .i_type = .{ |
| 419 | | .rd = .ra, |
| 420 | | .rs1 = .sp, |
| 421 | | .imm12 = 8, |
| 422 | | } }, |
| 423 | | }); |
| 362 | _ = try self.addInst(.{ |
| 363 | .tag = .dbg_prologue_end, |
| 364 | .data = .{ .nop = {} }, |
| 365 | }); |
| 424 | 366 | |
| 425 | | // ld s0, 0(sp) |
| 426 | | _ = try self.addInst(.{ |
| 427 | | .tag = .ld, |
| 428 | | .data = .{ .i_type = .{ |
| 429 | | .rd = .s0, |
| 430 | | .rs1 = .sp, |
| 431 | | .imm12 = 0, |
| 432 | | } }, |
| 433 | | }); |
| 367 | try self.genBody(self.air.getMainBody()); |
| 434 | 368 | |
| 435 | | // addi sp, sp, 16 |
| 436 | | _ = try self.addInst(.{ |
| 437 | | .tag = .addi, |
| 438 | | .data = .{ .i_type = .{ |
| 439 | | .rd = .sp, |
| 440 | | .rs1 = .sp, |
| 441 | | .imm12 = 16, |
| 442 | | } }, |
| 443 | | }); |
| 369 | // Backpatch prologue stack size |
| 370 | if (math.cast(i12, self.max_end_stack)) |casted_stack_size| { |
| 371 | self.mir_instructions.items(.data)[0].imm12 = casted_stack_size; |
| 372 | } else return self.fail("TODO support larger stack sizes, got {}", .{self.max_end_stack}); |
| 444 | 373 | |
| 445 | | // ret |
| 446 | | _ = try self.addInst(.{ |
| 447 | | .tag = .ret, |
| 448 | | .data = .{ .nop = {} }, |
| 449 | | }); |
| 450 | | } else { |
| 451 | | _ = try self.addInst(.{ |
| 452 | | .tag = .dbg_prologue_end, |
| 453 | | .data = .{ .nop = {} }, |
| 454 | | }); |
| 374 | _ = try self.addInst(.{ |
| 375 | .tag = .dbg_epilogue_begin, |
| 376 | .data = .{ .nop = {} }, |
| 377 | }); |
| 455 | 378 | |
| 456 | | try self.genBody(self.air.getMainBody()); |
| 379 | // exitlude jumps |
| 380 | if (self.exitlude_jump_relocs.items.len > 0 and |
| 381 | self.exitlude_jump_relocs.items[self.exitlude_jump_relocs.items.len - 1] == self.mir_instructions.len - 2) |
| 382 | { |
| 383 | // If the last Mir instruction (apart from the |
| 384 | // dbg_epilogue_begin) is the last exitlude jump |
| 385 | // relocation (which would just jump one instruction |
| 386 | // further), it can be safely removed |
| 387 | self.mir_instructions.orderedRemove(self.exitlude_jump_relocs.pop()); |
| 388 | } |
| 457 | 389 | |
| 458 | | _ = try self.addInst(.{ |
| 459 | | .tag = .dbg_epilogue_begin, |
| 460 | | .data = .{ .nop = {} }, |
| 461 | | }); |
| 390 | for (self.exitlude_jump_relocs.items) |jmp_reloc| { |
| 391 | _ = jmp_reloc; |
| 392 | return self.fail("TODO add branches in RISCV64", .{}); |
| 462 | 393 | } |
| 463 | 394 | |
| 464 | 395 | // Drop them off at the rbrace. |
| ... | ... | @@ -535,12 +466,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 535 | 466 | |
| 536 | 467 | .div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst), |
| 537 | 468 | |
| 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), |
| 469 | .cmp_lt => try self.airCmp(inst), |
| 470 | .cmp_lte => try self.airCmp(inst), |
| 471 | .cmp_eq => try self.airCmp(inst), |
| 472 | .cmp_gte => try self.airCmp(inst), |
| 473 | .cmp_gt => try self.airCmp(inst), |
| 474 | .cmp_neq => try self.airCmp(inst), |
| 544 | 475 | |
| 545 | 476 | .cmp_vector => try self.airCmpVector(inst), |
| 546 | 477 | .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst), |
| ... | ... | @@ -565,6 +496,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 565 | 496 | .frame_addr => try self.airFrameAddress(inst), |
| 566 | 497 | .fence => try self.airFence(), |
| 567 | 498 | .cond_br => try self.airCondBr(inst), |
| 499 | .dbg_stmt => try self.airDbgStmt(inst), |
| 568 | 500 | .fptrunc => try self.airFptrunc(inst), |
| 569 | 501 | .fpext => try self.airFpext(inst), |
| 570 | 502 | .intcast => try self.airIntCast(inst), |
| ... | ... | @@ -617,17 +549,17 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 617 | 549 | .union_init => try self.airUnionInit(inst), |
| 618 | 550 | .prefetch => try self.airPrefetch(inst), |
| 619 | 551 | .mul_add => try self.airMulAdd(inst), |
| 620 | | .addrspace_cast => @panic("TODO"), |
| 552 | .addrspace_cast => return self.fail("TODO: addrspace_cast", .{}), |
| 621 | 553 | |
| 622 | | .@"try" => @panic("TODO"), |
| 623 | | .try_ptr => @panic("TODO"), |
| 554 | .@"try" => return self.fail("TODO: try", .{}), |
| 555 | .try_ptr => return self.fail("TODO: try_ptr", .{}), |
| 624 | 556 | |
| 625 | | .dbg_stmt => try self.airDbgStmt(inst), |
| 626 | | .dbg_inline_block => try self.airDbgInlineBlock(inst), |
| 627 | 557 | .dbg_var_ptr, |
| 628 | 558 | .dbg_var_val, |
| 629 | 559 | => try self.airDbgVar(inst), |
| 630 | 560 | |
| 561 | .dbg_inline_block => try self.airDbgInlineBlock(inst), |
| 562 | |
| 631 | 563 | .call => try self.airCall(inst, .auto), |
| 632 | 564 | .call_always_tail => try self.airCall(inst, .always_tail), |
| 633 | 565 | .call_never_tail => try self.airCall(inst, .never_tail), |
| ... | ... | @@ -1019,17 +951,20 @@ fn binOpRegister( |
| 1019 | 951 | const mir_tag: Mir.Inst.Tag = switch (tag) { |
| 1020 | 952 | .add => .add, |
| 1021 | 953 | .sub => .sub, |
| 1022 | | else => unreachable, |
| 954 | .cmp_eq => .cmp_eq, |
| 955 | .cmp_gt => .cmp_gt, |
| 956 | else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}), |
| 1023 | 957 | }; |
| 1024 | 958 | const mir_data: Mir.Inst.Data = switch (tag) { |
| 1025 | 959 | .add, |
| 1026 | 960 | .sub, |
| 961 | .cmp_eq, |
| 1027 | 962 | => .{ .r_type = .{ |
| 1028 | 963 | .rd = dest_reg, |
| 1029 | 964 | .rs1 = lhs_reg, |
| 1030 | 965 | .rs2 = rhs_reg, |
| 1031 | 966 | } }, |
| 1032 | | else => unreachable, |
| 967 | else => return self.fail("TODO: binOpRegister {s}", .{@tagName(tag)}), |
| 1033 | 968 | }; |
| 1034 | 969 | |
| 1035 | 970 | _ = try self.addInst(.{ |
| ... | ... | @@ -1052,6 +987,8 @@ fn binOpRegister( |
| 1052 | 987 | /// looks at the lhs and rhs and determines which kind of lowering |
| 1053 | 988 | /// would be best suitable and then delegates the lowering to other |
| 1054 | 989 | /// functions. |
| 990 | /// |
| 991 | /// `maybe_inst` **needs** to be a bin_op, make sure of that. |
| 1055 | 992 | fn binOp( |
| 1056 | 993 | self: *Self, |
| 1057 | 994 | tag: Air.Inst.Tag, |
| ... | ... | @@ -1066,6 +1003,12 @@ fn binOp( |
| 1066 | 1003 | // Arithmetic operations on integers and floats |
| 1067 | 1004 | .add, |
| 1068 | 1005 | .sub, |
| 1006 | .cmp_eq, |
| 1007 | .cmp_neq, |
| 1008 | .cmp_gt, |
| 1009 | .cmp_gte, |
| 1010 | .cmp_lt, |
| 1011 | .cmp_lte, |
| 1069 | 1012 | => { |
| 1070 | 1013 | switch (lhs_ty.zigTypeTag(mod)) { |
| 1071 | 1014 | .Float => return self.fail("TODO binary operations on floats", .{}), |
| ... | ... | @@ -1180,8 +1123,19 @@ fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1180 | 1123 | } |
| 1181 | 1124 | |
| 1182 | 1125 | fn airAddWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1183 | | _ = inst; |
| 1184 | | return self.fail("TODO implement airAddWithOverflow for {}", .{self.target.cpu.arch}); |
| 1126 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 1127 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 1128 | |
| 1129 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1130 | const lhs = try self.resolveInst(extra.lhs); |
| 1131 | const rhs = try self.resolveInst(extra.rhs); |
| 1132 | const lhs_ty = self.typeOf(extra.lhs); |
| 1133 | const rhs_ty = self.typeOf(extra.rhs); |
| 1134 | |
| 1135 | break :result try self.binOp(.add, null, lhs, rhs, lhs_ty, rhs_ty); |
| 1136 | }; |
| 1137 | |
| 1138 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 1185 | 1139 | } |
| 1186 | 1140 | |
| 1187 | 1141 | fn airSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -1352,13 +1306,30 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { |
| 1352 | 1306 | |
| 1353 | 1307 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1354 | 1308 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1355 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_ptr for {}", .{self.target.cpu.arch}); |
| 1309 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1310 | const mcv = try self.resolveInst(ty_op.operand); |
| 1311 | break :result try self.slicePtr(mcv); |
| 1312 | }; |
| 1356 | 1313 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1357 | 1314 | } |
| 1358 | 1315 | |
| 1316 | fn slicePtr(self: *Self, mcv: MCValue) !MCValue { |
| 1317 | switch (mcv) { |
| 1318 | .dead, .unreach, .none => unreachable, |
| 1319 | .register => unreachable, // a slice doesn't fit in one register |
| 1320 | .stack_offset => |off| { |
| 1321 | return MCValue{ .stack_offset = off }; |
| 1322 | }, |
| 1323 | .memory => |addr| { |
| 1324 | return MCValue{ .memory = addr }; |
| 1325 | }, |
| 1326 | else => return self.fail("TODO slicePtr {s}", .{@tagName(mcv)}), |
| 1327 | } |
| 1328 | } |
| 1329 | |
| 1359 | 1330 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { |
| 1360 | 1331 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1361 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement slice_len for {}", .{self.target.cpu.arch}); |
| 1332 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airSliceLen for {}", .{self.target.cpu.arch}); |
| 1362 | 1333 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1363 | 1334 | } |
| 1364 | 1335 | |
| ... | ... | @@ -1500,6 +1471,7 @@ fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_ind |
| 1500 | 1471 | fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!void { |
| 1501 | 1472 | const mod = self.bin_file.comp.module.?; |
| 1502 | 1473 | const elem_ty = ptr_ty.childType(mod); |
| 1474 | |
| 1503 | 1475 | switch (ptr) { |
| 1504 | 1476 | .none => unreachable, |
| 1505 | 1477 | .undef => unreachable, |
| ... | ... | @@ -1507,9 +1479,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 1507 | 1479 | .dead => unreachable, |
| 1508 | 1480 | .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), |
| 1509 | 1481 | .ptr_stack_offset => |off| try self.setRegOrMem(elem_ty, dst_mcv, .{ .stack_offset = off }), |
| 1510 | | .register => { |
| 1511 | | return self.fail("TODO implement loading from MCValue.register", .{}); |
| 1512 | | }, |
| 1482 | .register => |src_reg| try self.setRegOrMem(elem_ty, dst_mcv, .{ .register = src_reg }), |
| 1513 | 1483 | .memory, |
| 1514 | 1484 | .stack_offset, |
| 1515 | 1485 | => { |
| ... | ... | @@ -1520,6 +1490,10 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 1520 | 1490 | try self.genSetReg(ptr_ty, reg, ptr); |
| 1521 | 1491 | try self.load(dst_mcv, .{ .register = reg }, ptr_ty); |
| 1522 | 1492 | }, |
| 1493 | .load_symbol => { |
| 1494 | const reg = try self.copyToTmpRegister(ptr_ty, ptr); |
| 1495 | try self.load(dst_mcv, .{ .register = reg }, ptr_ty); |
| 1496 | }, |
| 1523 | 1497 | } |
| 1524 | 1498 | } |
| 1525 | 1499 | |
| ... | ... | @@ -1553,6 +1527,8 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 1553 | 1527 | fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) !void { |
| 1554 | 1528 | _ = ptr_ty; |
| 1555 | 1529 | |
| 1530 | log.debug("storing {s}", .{@tagName(ptr)}); |
| 1531 | |
| 1556 | 1532 | switch (ptr) { |
| 1557 | 1533 | .none => unreachable, |
| 1558 | 1534 | .undef => unreachable, |
| ... | ... | @@ -1573,6 +1549,9 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 1573 | 1549 | .stack_offset => { |
| 1574 | 1550 | return self.fail("TODO implement storing to MCValue.stack_offset", .{}); |
| 1575 | 1551 | }, |
| 1552 | .load_symbol => { |
| 1553 | return self.fail("TODO implement storing to MCValue.load_symbol", .{}); |
| 1554 | }, |
| 1576 | 1555 | } |
| 1577 | 1556 | } |
| 1578 | 1557 | |
| ... | ... | @@ -1596,27 +1575,32 @@ fn airStore(self: *Self, inst: Air.Inst.Index, safety: bool) !void { |
| 1596 | 1575 | fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1597 | 1576 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 1598 | 1577 | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 1599 | | return self.structFieldPtr(extra.struct_operand, ty_pl.ty, extra.field_index); |
| 1578 | const result = try self.structFieldPtr(inst, extra.struct_operand, ty_pl.ty, extra.field_index); |
| 1579 | return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); |
| 1600 | 1580 | } |
| 1601 | 1581 | |
| 1602 | 1582 | fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { |
| 1603 | 1583 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1604 | | return self.structFieldPtr(ty_op.operand, ty_op.ty, index); |
| 1584 | const result = try self.structFieldPtr(inst, ty_op.operand, ty_op.ty, index); |
| 1585 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1605 | 1586 | } |
| 1606 | | fn structFieldPtr(self: *Self, operand: Air.Inst.Ref, ty: Air.Inst.Ref, index: u32) !void { |
| 1587 | |
| 1588 | fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, ty: Air.Inst.Ref, index: u32) !MCValue { |
| 1589 | _ = inst; |
| 1607 | 1590 | _ = operand; |
| 1608 | 1591 | _ = ty; |
| 1609 | 1592 | _ = index; |
| 1610 | | return self.fail("TODO implement codegen struct_field_ptr", .{}); |
| 1611 | | //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); |
| 1593 | |
| 1594 | return self.fail("TODO: structFieldPtr", .{}); |
| 1612 | 1595 | } |
| 1613 | 1596 | |
| 1614 | 1597 | fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 1615 | 1598 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 1616 | | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 1617 | | _ = extra; |
| 1618 | | return self.fail("TODO implement codegen struct_field_val", .{}); |
| 1619 | | //return self.finishAir(inst, result, .{ extra.struct_ptr, .none, .none }); |
| 1599 | _ = ty_pl; |
| 1600 | |
| 1601 | return self.fail("TODO: airStructFieldVal", .{}); |
| 1602 | |
| 1603 | // return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); |
| 1620 | 1604 | } |
| 1621 | 1605 | |
| 1622 | 1606 | fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -1732,12 +1716,13 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 1732 | 1716 | try self.register_manager.getReg(reg, null); |
| 1733 | 1717 | try self.genSetReg(arg_ty, reg, arg_mcv); |
| 1734 | 1718 | }, |
| 1735 | | .stack_offset => { |
| 1736 | | return self.fail("TODO implement calling with parameters in memory", .{}); |
| 1737 | | }, |
| 1719 | .stack_offset => |off| try self.genSetStack(arg_ty, off, arg_mcv), |
| 1738 | 1720 | .ptr_stack_offset => { |
| 1739 | 1721 | return self.fail("TODO implement calling with MCValue.ptr_stack_offset arg", .{}); |
| 1740 | 1722 | }, |
| 1723 | .load_symbol => { |
| 1724 | return self.fail("TODO implement calling with MCValue.load_symbol", .{}); |
| 1725 | }, |
| 1741 | 1726 | } |
| 1742 | 1727 | } |
| 1743 | 1728 | |
| ... | ... | @@ -1747,7 +1732,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallModifier |
| 1747 | 1732 | const sym_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, func.owner_decl); |
| 1748 | 1733 | const sym = elf_file.symbol(sym_index); |
| 1749 | 1734 | _ = try sym.getOrCreateZigGotEntry(sym_index, elf_file); |
| 1750 | | const got_addr: u32 = @intCast(sym.zigGotAddress(elf_file)); |
| 1735 | const got_addr = sym.zigGotAddress(elf_file); |
| 1751 | 1736 | try self.genSetReg(Type.usize, .ra, .{ .memory = got_addr }); |
| 1752 | 1737 | _ = try self.addInst(.{ |
| 1753 | 1738 | .tag = .jalr, |
| ... | ... | @@ -1830,7 +1815,8 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 1830 | 1815 | //return self.finishAir(inst, .dead, .{ un_op, .none, .none }); |
| 1831 | 1816 | } |
| 1832 | 1817 | |
| 1833 | | fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 1818 | fn airCmp(self: *Self, inst: Air.Inst.Index) !void { |
| 1819 | const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; |
| 1834 | 1820 | const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 1835 | 1821 | if (self.liveness.isUnused(inst)) |
| 1836 | 1822 | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| ... | ... | @@ -1842,12 +1828,12 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void { |
| 1842 | 1828 | |
| 1843 | 1829 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1844 | 1830 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1845 | | _ = op; |
| 1846 | | _ = lhs; |
| 1847 | | _ = rhs; |
| 1831 | const lhs_ty = self.typeOf(bin_op.lhs); |
| 1832 | const rhs_ty = self.typeOf(bin_op.rhs); |
| 1848 | 1833 | |
| 1849 | | return self.fail("TODO implement cmp for {}", .{self.target.cpu.arch}); |
| 1850 | | // return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1834 | const result = try self.binOp(tag, null, lhs, rhs, lhs_ty, rhs_ty); |
| 1835 | |
| 1836 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1851 | 1837 | } |
| 1852 | 1838 | |
| 1853 | 1839 | fn airCmpVector(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -1878,13 +1864,11 @@ fn airDbgStmt(self: *Self, inst: Air.Inst.Index) !void { |
| 1878 | 1864 | } |
| 1879 | 1865 | |
| 1880 | 1866 | fn airDbgInlineBlock(self: *Self, inst: Air.Inst.Index) !void { |
| 1881 | | const mod = self.bin_file.comp.module.?; |
| 1882 | 1867 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 1883 | 1868 | const extra = self.air.extraData(Air.DbgInlineBlock, ty_pl.payload); |
| 1884 | | const func = mod.funcInfo(extra.data.func); |
| 1885 | | // TODO emit debug info for function change |
| 1886 | | _ = func; |
| 1887 | | try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); |
| 1869 | _ = extra; |
| 1870 | // TODO: emit debug info for this block |
| 1871 | return self.finishAir(inst, .dead, .{ .none, .none, .none }); |
| 1888 | 1872 | } |
| 1889 | 1873 | |
| 1890 | 1874 | fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -1897,10 +1881,165 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { |
| 1897 | 1881 | } |
| 1898 | 1882 | |
| 1899 | 1883 | fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 1900 | | _ = inst; |
| 1884 | const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; |
| 1885 | const cond = try self.resolveInst(pl_op.operand); |
| 1886 | const cond_ty = self.typeOf(pl_op.operand); |
| 1887 | const extra = self.air.extraData(Air.CondBr, pl_op.payload); |
| 1888 | const then_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.then_body_len]); |
| 1889 | const else_body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]); |
| 1890 | const liveness_condbr = self.liveness.getCondBr(inst); |
| 1891 | |
| 1892 | // A branch to the false section. Uses beq |
| 1893 | const reloc = try self.condBr(cond_ty, cond); |
| 1894 | |
| 1895 | // If the condition dies here in this condbr instruction, process |
| 1896 | // that death now instead of later as this has an effect on |
| 1897 | // whether it needs to be spilled in the branches |
| 1898 | if (self.liveness.operandDies(inst, 0)) { |
| 1899 | if (pl_op.operand.toIndex()) |op_index| { |
| 1900 | self.processDeath(op_index); |
| 1901 | } |
| 1902 | } |
| 1903 | |
| 1904 | // Save state |
| 1905 | const parent_next_stack_offset = self.next_stack_offset; |
| 1906 | const parent_free_registers = self.register_manager.free_registers; |
| 1907 | var parent_stack = try self.stack.clone(self.gpa); |
| 1908 | defer parent_stack.deinit(self.gpa); |
| 1909 | const parent_registers = self.register_manager.registers; |
| 1910 | |
| 1911 | try self.branch_stack.append(.{}); |
| 1912 | errdefer { |
| 1913 | _ = self.branch_stack.pop(); |
| 1914 | } |
| 1915 | |
| 1916 | try self.ensureProcessDeathCapacity(liveness_condbr.then_deaths.len); |
| 1917 | for (liveness_condbr.then_deaths) |operand| { |
| 1918 | self.processDeath(operand); |
| 1919 | } |
| 1920 | try self.genBody(then_body); |
| 1921 | |
| 1922 | // Revert to the previous register and stack allocation state. |
| 1923 | |
| 1924 | var saved_then_branch = self.branch_stack.pop(); |
| 1925 | defer saved_then_branch.deinit(self.gpa); |
| 1926 | |
| 1927 | self.register_manager.registers = parent_registers; |
| 1928 | |
| 1929 | self.stack.deinit(self.gpa); |
| 1930 | self.stack = parent_stack; |
| 1931 | parent_stack = .{}; |
| 1932 | |
| 1933 | self.next_stack_offset = parent_next_stack_offset; |
| 1934 | self.register_manager.free_registers = parent_free_registers; |
| 1935 | |
| 1936 | try self.performReloc(reloc); |
| 1937 | const else_branch = self.branch_stack.addOneAssumeCapacity(); |
| 1938 | else_branch.* = .{}; |
| 1939 | |
| 1940 | try self.ensureProcessDeathCapacity(liveness_condbr.else_deaths.len); |
| 1941 | for (liveness_condbr.else_deaths) |operand| { |
| 1942 | self.processDeath(operand); |
| 1943 | } |
| 1944 | try self.genBody(else_body); |
| 1945 | |
| 1946 | // At this point, each branch will possibly have conflicting values for where |
| 1947 | // each instruction is stored. They agree, however, on which instructions are alive/dead. |
| 1948 | // We use the first ("then") branch as canonical, and here emit |
| 1949 | // instructions into the second ("else") branch to make it conform. |
| 1950 | // We continue respect the data structure semantic guarantees of the else_branch so |
| 1951 | // that we can use all the code emitting abstractions. This is why at the bottom we |
| 1952 | // assert that parent_branch.free_registers equals the saved_then_branch.free_registers |
| 1953 | // rather than assigning it. |
| 1954 | const parent_branch = &self.branch_stack.items[self.branch_stack.items.len - 2]; |
| 1955 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, else_branch.inst_table.count()); |
| 1956 | const else_slice = else_branch.inst_table.entries.slice(); |
| 1957 | const else_keys = else_slice.items(.key); |
| 1958 | const else_values = else_slice.items(.value); |
| 1959 | for (else_keys, 0..) |else_key, else_idx| { |
| 1960 | const else_value = else_values[else_idx]; |
| 1961 | const canon_mcv = if (saved_then_branch.inst_table.fetchSwapRemove(else_key)) |then_entry| blk: { |
| 1962 | // The instruction's MCValue is overridden in both branches. |
| 1963 | log.debug("condBr put branch table (key = %{d}, value = {})", .{ else_key, then_entry.value }); |
| 1964 | parent_branch.inst_table.putAssumeCapacity(else_key, then_entry.value); |
| 1965 | if (else_value == .dead) { |
| 1966 | assert(then_entry.value == .dead); |
| 1967 | continue; |
| 1968 | } |
| 1969 | break :blk then_entry.value; |
| 1970 | } else blk: { |
| 1971 | if (else_value == .dead) |
| 1972 | continue; |
| 1973 | // The instruction is only overridden in the else branch. |
| 1974 | var i: usize = self.branch_stack.items.len - 2; |
| 1975 | while (true) { |
| 1976 | i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead? |
| 1977 | if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| { |
| 1978 | assert(mcv != .dead); |
| 1979 | break :blk mcv; |
| 1980 | } |
| 1981 | } |
| 1982 | }; |
| 1983 | log.debug("consolidating else_entry {d} {}=>{}", .{ else_key, else_value, canon_mcv }); |
| 1984 | // TODO make sure the destination stack offset / register does not already have something |
| 1985 | // going on there. |
| 1986 | try self.setRegOrMem(self.typeOfIndex(else_key), canon_mcv, else_value); |
| 1987 | // TODO track the new register / stack allocation |
| 1988 | } |
| 1989 | try parent_branch.inst_table.ensureUnusedCapacity(self.gpa, saved_then_branch.inst_table.count()); |
| 1990 | const then_slice = saved_then_branch.inst_table.entries.slice(); |
| 1991 | const then_keys = then_slice.items(.key); |
| 1992 | const then_values = then_slice.items(.value); |
| 1993 | for (then_keys, 0..) |then_key, then_idx| { |
| 1994 | const then_value = then_values[then_idx]; |
| 1995 | // We already deleted the items from this table that matched the else_branch. |
| 1996 | // So these are all instructions that are only overridden in the then branch. |
| 1997 | parent_branch.inst_table.putAssumeCapacity(then_key, then_value); |
| 1998 | if (then_value == .dead) |
| 1999 | continue; |
| 2000 | const parent_mcv = blk: { |
| 2001 | var i: usize = self.branch_stack.items.len - 2; |
| 2002 | while (true) { |
| 2003 | i -= 1; |
| 2004 | if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| { |
| 2005 | assert(mcv != .dead); |
| 2006 | break :blk mcv; |
| 2007 | } |
| 2008 | } |
| 2009 | }; |
| 2010 | log.debug("consolidating then_entry {d} {}=>{}", .{ then_key, parent_mcv, then_value }); |
| 2011 | // TODO make sure the destination stack offset / register does not already have something |
| 2012 | // going on there. |
| 2013 | try self.setRegOrMem(self.typeOfIndex(then_key), parent_mcv, then_value); |
| 2014 | // TODO track the new register / stack allocation |
| 2015 | } |
| 1901 | 2016 | |
| 1902 | | return self.fail("TODO implement condbr {}", .{self.target.cpu.arch}); |
| 1903 | | // return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); |
| 2017 | { |
| 2018 | var item = self.branch_stack.pop(); |
| 2019 | item.deinit(self.gpa); |
| 2020 | } |
| 2021 | |
| 2022 | return self.finishAir(inst, .unreach, .{ .none, .none, .none }); |
| 2023 | } |
| 2024 | |
| 2025 | fn condBr(self: *Self, cond_ty: Type, condition: MCValue) !Mir.Inst.Index { |
| 2026 | _ = cond_ty; |
| 2027 | |
| 2028 | const reg = switch (condition) { |
| 2029 | .register => |r| r, |
| 2030 | else => try self.copyToTmpRegister(Type.bool, condition), |
| 2031 | }; |
| 2032 | |
| 2033 | return try self.addInst(.{ |
| 2034 | .tag = .beq, |
| 2035 | .data = .{ |
| 2036 | .b_type = .{ |
| 2037 | .rs1 = reg, |
| 2038 | .rs2 = .zero, |
| 2039 | .imm12 = 0, // patched later. |
| 2040 | }, |
| 2041 | }, |
| 2042 | }); |
| 1904 | 2043 | } |
| 1905 | 2044 | |
| 1906 | 2045 | fn isNull(self: *Self, operand: MCValue) !MCValue { |
| ... | ... | @@ -2044,25 +2183,26 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void { |
| 2044 | 2183 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 2045 | 2184 | const loop = self.air.extraData(Air.Block, ty_pl.payload); |
| 2046 | 2185 | const body: []const Air.Inst.Index = @ptrCast(self.air.extra[loop.end..][0..loop.data.body_len]); |
| 2047 | | const start_index = self.code.items.len; |
| 2186 | |
| 2187 | const start_index: Mir.Inst.Index = @intCast(self.code.items.len); |
| 2188 | |
| 2048 | 2189 | try self.genBody(body); |
| 2049 | 2190 | try self.jump(start_index); |
| 2191 | |
| 2050 | 2192 | return self.finishAirBookkeeping(); |
| 2051 | 2193 | } |
| 2052 | 2194 | |
| 2053 | 2195 | /// Send control flow to the `index` of `self.code`. |
| 2054 | | fn jump(self: *Self, index: usize) !void { |
| 2055 | | _ = index; |
| 2056 | | return self.fail("TODO implement jump for {}", .{self.target.cpu.arch}); |
| 2196 | fn jump(self: *Self, index: Mir.Inst.Index) !void { |
| 2197 | _ = try self.addInst(.{ |
| 2198 | .tag = .psuedo_jump, |
| 2199 | .data = .{ |
| 2200 | .inst = index, |
| 2201 | }, |
| 2202 | }); |
| 2057 | 2203 | } |
| 2058 | 2204 | |
| 2059 | 2205 | fn airBlock(self: *Self, inst: Air.Inst.Index) !void { |
| 2060 | | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 2061 | | const extra = self.air.extraData(Air.Block, ty_pl.payload); |
| 2062 | | try self.lowerBlock(inst, @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len])); |
| 2063 | | } |
| 2064 | | |
| 2065 | | fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void { |
| 2066 | 2206 | try self.blocks.putNoClobber(self.gpa, inst, .{ |
| 2067 | 2207 | // A block is a setup to be able to jump to the end. |
| 2068 | 2208 | .relocs = .{}, |
| ... | ... | @@ -2074,10 +2214,16 @@ fn lowerBlock(self: *Self, inst: Air.Inst.Index, body: []const Air.Inst.Index) ! |
| 2074 | 2214 | .mcv = MCValue{ .none = {} }, |
| 2075 | 2215 | }); |
| 2076 | 2216 | defer self.blocks.getPtr(inst).?.relocs.deinit(self.gpa); |
| 2217 | |
| 2218 | const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 2219 | const extra = self.air.extraData(Air.Block, ty_pl.payload); |
| 2220 | const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]); |
| 2077 | 2221 | // TODO emit debug info lexical block |
| 2078 | 2222 | try self.genBody(body); |
| 2079 | 2223 | |
| 2080 | | for (self.blocks.getPtr(inst).?.relocs.items) |reloc| try self.performReloc(reloc); |
| 2224 | for (self.blocks.getPtr(inst).?.relocs.items) |reloc| { |
| 2225 | try self.performReloc(reloc); |
| 2226 | } |
| 2081 | 2227 | |
| 2082 | 2228 | const result = self.blocks.getPtr(inst).?.mcv; |
| 2083 | 2229 | return self.finishAir(inst, result, .{ .none, .none, .none }); |
| ... | ... | @@ -2091,11 +2237,12 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 2091 | 2237 | // return self.finishAir(inst, .dead, .{ condition, .none, .none }); |
| 2092 | 2238 | } |
| 2093 | 2239 | |
| 2094 | | fn performReloc(self: *Self, reloc: Reloc) !void { |
| 2095 | | _ = self; |
| 2096 | | switch (reloc) { |
| 2097 | | .rel32 => unreachable, |
| 2098 | | .arm_branch => unreachable, |
| 2240 | fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { |
| 2241 | const tag = self.mir_instructions.items(.tag)[inst]; |
| 2242 | |
| 2243 | switch (tag) { |
| 2244 | .beq => self.mir_instructions.items(.data)[inst].b_type.imm12 = @intCast(inst), |
| 2245 | else => return self.fail("TODO: performReloc {s}", .{@tagName(tag)}), |
| 2099 | 2246 | } |
| 2100 | 2247 | } |
| 2101 | 2248 | |
| ... | ... | @@ -2135,7 +2282,15 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void { |
| 2135 | 2282 | // Emit a jump with a relocation. It will be patched up after the block ends. |
| 2136 | 2283 | try block_data.relocs.ensureUnusedCapacity(self.gpa, 1); |
| 2137 | 2284 | |
| 2138 | | return self.fail("TODO implement brvoid for {}", .{self.target.cpu.arch}); |
| 2285 | block_data.relocs.appendAssumeCapacity(try self.addInst(.{ |
| 2286 | .tag = .jal, |
| 2287 | .data = .{ |
| 2288 | .j_type = .{ |
| 2289 | .rd = .ra, |
| 2290 | .imm21 = undefined, // populated later through performReloc |
| 2291 | }, |
| 2292 | }, |
| 2293 | })); |
| 2139 | 2294 | } |
| 2140 | 2295 | |
| 2141 | 2296 | fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -2261,28 +2416,138 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT |
| 2261 | 2416 | |
| 2262 | 2417 | /// Sets the value without any modifications to register allocation metadata or stack allocation metadata. |
| 2263 | 2418 | fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void { |
| 2419 | if (!loc.isMutable()) { |
| 2420 | return std.debug.panic("tried to setRegOrMem immutable: {s}", .{@tagName(loc)}); |
| 2421 | } |
| 2422 | |
| 2264 | 2423 | switch (loc) { |
| 2265 | 2424 | .none => return, |
| 2266 | 2425 | .register => |reg| return self.genSetReg(ty, reg, val), |
| 2267 | 2426 | .stack_offset => |off| return self.genSetStack(ty, off, val), |
| 2268 | | .memory => { |
| 2269 | | return self.fail("TODO implement setRegOrMem for memory", .{}); |
| 2270 | | }, |
| 2271 | | else => unreachable, |
| 2427 | else => return self.fail("TODO: setRegOrMem {s}", .{@tagName(loc)}), |
| 2272 | 2428 | } |
| 2273 | 2429 | } |
| 2274 | 2430 | |
| 2275 | 2431 | fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerError!void { |
| 2276 | | _ = ty; |
| 2277 | | _ = stack_offset; |
| 2278 | | _ = mcv; |
| 2279 | | return self.fail("TODO implement getSetStack for {}", .{self.target.cpu.arch}); |
| 2432 | const mod = self.bin_file.comp.module.?; |
| 2433 | const abi_size: u32 = @intCast(ty.abiSize(mod)); |
| 2434 | |
| 2435 | switch (mcv) { |
| 2436 | .none => return, |
| 2437 | .dead => unreachable, |
| 2438 | .immediate => { |
| 2439 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 2440 | return self.genSetStack(ty, stack_offset, .{ .register = reg }); |
| 2441 | }, |
| 2442 | .register => |reg| { |
| 2443 | switch (abi_size) { |
| 2444 | 1, 2, 4, 8 => { |
| 2445 | assert(std.mem.isAlignedGeneric(u32, stack_offset, abi_size)); |
| 2446 | |
| 2447 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| 2448 | 1 => .sb, |
| 2449 | 2 => .sh, |
| 2450 | 4 => .sw, |
| 2451 | 8 => .sd, |
| 2452 | else => unreachable, |
| 2453 | }; |
| 2454 | |
| 2455 | _ = try self.addInst(.{ |
| 2456 | .tag = tag, |
| 2457 | .data = .{ .i_type = .{ |
| 2458 | .rd = reg, |
| 2459 | .rs1 = .sp, |
| 2460 | .imm12 = @intCast(stack_offset), |
| 2461 | } }, |
| 2462 | }); |
| 2463 | }, |
| 2464 | else => return self.fail("TODO: genSetStack for size={d}", .{abi_size}), |
| 2465 | } |
| 2466 | }, |
| 2467 | .stack_offset, .load_symbol => { |
| 2468 | if (abi_size <= 8) { |
| 2469 | const reg = try self.copyToTmpRegister(ty, mcv); |
| 2470 | return self.genSetStack(ty, stack_offset, MCValue{ .register = reg }); |
| 2471 | } |
| 2472 | |
| 2473 | const ptr_ty = try mod.singleMutPtrType(ty); |
| 2474 | |
| 2475 | // TODO call extern memcpy |
| 2476 | const regs = try self.register_manager.allocRegs(5, .{ null, null, null, null, null }, gp); |
| 2477 | const regs_locks = self.register_manager.lockRegsAssumeUnused(5, regs); |
| 2478 | defer for (regs_locks) |reg| { |
| 2479 | self.register_manager.unlockReg(reg); |
| 2480 | }; |
| 2481 | |
| 2482 | const src_reg = regs[0]; |
| 2483 | const dst_reg = regs[1]; |
| 2484 | const len_reg = regs[2]; |
| 2485 | const count_reg = regs[3]; |
| 2486 | const tmp_reg = regs[4]; |
| 2487 | |
| 2488 | switch (mcv) { |
| 2489 | .stack_offset => |offset| { |
| 2490 | if (offset == stack_offset) return; |
| 2491 | try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = offset }); |
| 2492 | }, |
| 2493 | .load_symbol => |sym_off| { |
| 2494 | const atom_index = atom: { |
| 2495 | const decl_index = mod.funcOwnerDeclIndex(self.func_index); |
| 2496 | |
| 2497 | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 2498 | const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index); |
| 2499 | break :atom atom_index; |
| 2500 | } else return self.fail("TODO genSetStack for {s}", .{@tagName(self.bin_file.tag)}); |
| 2501 | }; |
| 2502 | |
| 2503 | _ = try self.addInst(.{ |
| 2504 | .tag = .load_symbol, |
| 2505 | .data = .{ |
| 2506 | .payload = try self.addExtra(Mir.LoadSymbolPayload{ |
| 2507 | .register = @intFromEnum(src_reg), |
| 2508 | .atom_index = atom_index, |
| 2509 | .sym_index = sym_off.sym, |
| 2510 | }), |
| 2511 | }, |
| 2512 | }); |
| 2513 | }, |
| 2514 | else => return self.fail("TODO: genSetStack unreachable {s}", .{@tagName(mcv)}), |
| 2515 | } |
| 2516 | |
| 2517 | try self.genSetReg(ptr_ty, dst_reg, .{ .ptr_stack_offset = stack_offset }); |
| 2518 | try self.genSetReg(Type.usize, len_reg, .{ .immediate = abi_size }); |
| 2519 | |
| 2520 | // memcpy(src, dst, len) |
| 2521 | try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); |
| 2522 | }, |
| 2523 | else => return self.fail("TODO: genSetStack {s}", .{@tagName(mcv)}), |
| 2524 | } |
| 2525 | } |
| 2526 | |
| 2527 | fn genInlineMemcpy( |
| 2528 | self: *Self, |
| 2529 | src: Register, |
| 2530 | dst: Register, |
| 2531 | len: Register, |
| 2532 | count: Register, |
| 2533 | tmp: Register, |
| 2534 | ) !void { |
| 2535 | _ = src; |
| 2536 | _ = dst; |
| 2537 | _ = len; |
| 2538 | _ = count; |
| 2539 | _ = tmp; |
| 2540 | |
| 2541 | return self.fail("TODO: genInlineMemcpy", .{}); |
| 2280 | 2542 | } |
| 2281 | 2543 | |
| 2282 | 2544 | fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void { |
| 2545 | const mod = self.bin_file.comp.module.?; |
| 2546 | const abi_size: u32 = @intCast(ty.abiSize(mod)); |
| 2547 | |
| 2283 | 2548 | switch (mcv) { |
| 2284 | 2549 | .dead => unreachable, |
| 2285 | | .ptr_stack_offset => unreachable, |
| 2550 | .ptr_stack_offset => return self.fail("TODO genSetReg ptr_stack_offset", .{}), |
| 2286 | 2551 | .unreach, .none => return, // Nothing to do. |
| 2287 | 2552 | .undef => { |
| 2288 | 2553 | if (!self.wantSafety()) |
| ... | ... | @@ -2343,8 +2608,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2343 | 2608 | }); |
| 2344 | 2609 | }, |
| 2345 | 2610 | .memory => |addr| { |
| 2346 | | // The value is in memory at a hard-coded address. |
| 2347 | | // If the type is a pointer, it means the pointer address is at this memory location. |
| 2348 | 2611 | try self.genSetReg(ty, reg, .{ .immediate = addr }); |
| 2349 | 2612 | |
| 2350 | 2613 | _ = try self.addInst(.{ |
| ... | ... | @@ -2355,11 +2618,51 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 2355 | 2618 | .imm12 = 0, |
| 2356 | 2619 | } }, |
| 2357 | 2620 | }); |
| 2358 | | // LOAD imm=[i12 offset = 0], rs1 = |
| 2359 | 2621 | |
| 2360 | | // return self.fail("TODO implement genSetReg memory for riscv64"); |
| 2622 | // LOAD imm=[i12 offset = 0], rs1 |
| 2623 | }, |
| 2624 | .stack_offset => |off| { |
| 2625 | const tag: Mir.Inst.Tag = switch (abi_size) { |
| 2626 | 1 => .lb, |
| 2627 | 2 => .lh, |
| 2628 | 4 => .lw, |
| 2629 | 8 => .ld, |
| 2630 | else => return self.fail("TODO: genSetReg for size {d}", .{abi_size}), |
| 2631 | }; |
| 2632 | |
| 2633 | _ = try self.addInst(.{ |
| 2634 | .tag = tag, |
| 2635 | .data = .{ .i_type = .{ |
| 2636 | .rd = reg, |
| 2637 | .rs1 = .sp, |
| 2638 | .imm12 = @intCast(off), |
| 2639 | } }, |
| 2640 | }); |
| 2641 | }, |
| 2642 | .load_symbol => |sym_off| { |
| 2643 | assert(sym_off.off == 0); |
| 2644 | |
| 2645 | const decl_index = mod.funcOwnerDeclIndex(self.func_index); |
| 2646 | |
| 2647 | const atom_index = switch (self.bin_file.tag) { |
| 2648 | .elf => blk: { |
| 2649 | const elf_file = self.bin_file.cast(link.File.Elf).?; |
| 2650 | const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index); |
| 2651 | break :blk atom_index; |
| 2652 | }, |
| 2653 | else => return self.fail("TODO genSetReg load_symbol for {s}", .{@tagName(self.bin_file.tag)}), |
| 2654 | }; |
| 2655 | _ = try self.addInst(.{ |
| 2656 | .tag = .load_symbol, |
| 2657 | .data = .{ |
| 2658 | .payload = try self.addExtra(Mir.LoadSymbolPayload{ |
| 2659 | .register = @intFromEnum(reg), |
| 2660 | .atom_index = atom_index, |
| 2661 | .sym_index = sym_off.sym, |
| 2662 | }), |
| 2663 | }, |
| 2664 | }); |
| 2361 | 2665 | }, |
| 2362 | | else => return self.fail("TODO implement getSetReg for riscv64 {}", .{mcv}), |
| 2363 | 2666 | } |
| 2364 | 2667 | } |
| 2365 | 2668 | |
| ... | ... | @@ -2579,9 +2882,12 @@ fn genTypedValue(self: *Self, val: Value) InnerError!MCValue { |
| 2579 | 2882 | .mcv => |mcv| switch (mcv) { |
| 2580 | 2883 | .none => .none, |
| 2581 | 2884 | .undef => .undef, |
| 2582 | | .load_got, .load_symbol, .load_direct, .load_tlv => unreachable, // TODO |
| 2885 | .load_symbol => |sym_index| .{ .load_symbol = .{ .sym = sym_index } }, |
| 2583 | 2886 | .immediate => |imm| .{ .immediate = imm }, |
| 2584 | 2887 | .memory => |addr| .{ .memory = addr }, |
| 2888 | .load_got, .load_direct, .load_tlv => { |
| 2889 | return self.fail("TODO: genTypedValue {s}", .{@tagName(mcv)}); |
| 2890 | }, |
| 2585 | 2891 | }, |
| 2586 | 2892 | .fail => |msg| { |
| 2587 | 2893 | self.err_msg = msg; |
| ... | ... | @@ -2634,41 +2940,17 @@ fn resolveCallingConventionValues(self: *Self, fn_ty: Type) !CallMCValues { |
| 2634 | 2940 | // TODO make this generic with other ABIs, in particular |
| 2635 | 2941 | // with different hardware floating-point calling |
| 2636 | 2942 | // conventions |
| 2637 | | var next_register: usize = 0; |
| 2638 | | var next_stack_offset: u32 = 0; |
| 2639 | | // TODO: this is never assigned, which is a bug, but I don't know how this code works |
| 2640 | | // well enough to try and fix it. I *think* `next_register += next_stack_offset` is |
| 2641 | | // supposed to be `next_stack_offset += param_size` in every case where it appears. |
| 2642 | | _ = &next_stack_offset; |
| 2643 | | |
| 2644 | | const argument_registers = [_]Register{ .a0, .a1, .a2, .a3, .a4, .a5, .a6, .a7 }; |
| 2943 | var stack_offset: u32 = 0; |
| 2645 | 2944 | |
| 2646 | 2945 | for (fn_info.param_types.get(ip), result.args) |ty, *result_arg| { |
| 2647 | | const param_size: u32 = @intCast(Type.fromInterned(ty).abiSize(mod)); |
| 2648 | | if (param_size <= 8) { |
| 2649 | | if (next_register < argument_registers.len) { |
| 2650 | | result_arg.* = .{ .register = argument_registers[next_register] }; |
| 2651 | | next_register += 1; |
| 2652 | | } else { |
| 2653 | | result_arg.* = .{ .stack_offset = next_stack_offset }; |
| 2654 | | next_register += next_stack_offset; |
| 2655 | | } |
| 2656 | | } else if (param_size <= 16) { |
| 2657 | | if (next_register < argument_registers.len - 1) { |
| 2658 | | return self.fail("TODO MCValues with 2 registers", .{}); |
| 2659 | | } else if (next_register < argument_registers.len) { |
| 2660 | | return self.fail("TODO MCValues split register + stack", .{}); |
| 2661 | | } else { |
| 2662 | | result_arg.* = .{ .stack_offset = next_stack_offset }; |
| 2663 | | next_register += next_stack_offset; |
| 2664 | | } |
| 2665 | | } else { |
| 2666 | | result_arg.* = .{ .stack_offset = next_stack_offset }; |
| 2667 | | next_register += next_stack_offset; |
| 2668 | | } |
| 2946 | const param_type = Type.fromInterned(ty); |
| 2947 | const param_size: u32 = @intCast(param_type.abiSize(mod)); |
| 2948 | |
| 2949 | result_arg.* = .{ .stack_offset = stack_offset }; |
| 2950 | stack_offset += param_size; |
| 2669 | 2951 | } |
| 2670 | 2952 | |
| 2671 | | result.stack_byte_count = next_stack_offset; |
| 2953 | result.stack_byte_count = stack_offset; |
| 2672 | 2954 | result.stack_align = .@"16"; |
| 2673 | 2955 | }, |
| 2674 | 2956 | else => return self.fail("TODO implement function parameters for {} on riscv64", .{cc}), |