| ... | ... | @@ -122,9 +122,10 @@ const MCValue = union(enum) { |
| 122 | 122 | /// A pointer-sized integer that fits in a register. |
| 123 | 123 | /// If the type is a pointer, this is the pointer address in virtual address space. |
| 124 | 124 | immediate: u64, |
| 125 | | /// The value is in memory at an address not-yet-allocated by the linker. |
| 126 | | /// This traditionally corresponds to a relocation emitted in a relocatable object file. |
| 125 | /// The value doesn't exist in memory yet. |
| 127 | 126 | load_symbol: SymbolOffset, |
| 127 | /// The address of the memory location not-yet-allocated by the linker. |
| 128 | addr_symbol: SymbolOffset, |
| 128 | 129 | /// The value is in a target-specific register. |
| 129 | 130 | register: Register, |
| 130 | 131 | /// The value is split across two registers |
| ... | ... | @@ -169,6 +170,7 @@ const MCValue = union(enum) { |
| 169 | 170 | .indirect, |
| 170 | 171 | .undef, |
| 171 | 172 | .load_symbol, |
| 173 | .addr_symbol, |
| 172 | 174 | .air_ref, |
| 173 | 175 | => false, |
| 174 | 176 | |
| ... | ... | @@ -188,10 +190,14 @@ const MCValue = union(enum) { |
| 188 | 190 | .immediate, |
| 189 | 191 | .ptr_stack_offset, |
| 190 | 192 | .register_offset, |
| 193 | .register_pair, |
| 194 | .register, |
| 191 | 195 | .undef, |
| 192 | 196 | .air_ref, |
| 197 | .addr_symbol, |
| 193 | 198 | => unreachable, // not in memory |
| 194 | 199 | |
| 200 | .load_symbol => |sym_off| .{ .addr_symbol = sym_off }, |
| 195 | 201 | .memory => |addr| .{ .immediate = addr }, |
| 196 | 202 | .stack_offset => |off| .{ .ptr_stack_offset = off }, |
| 197 | 203 | .indirect => |reg_off| switch (reg_off.off) { |
| ... | ... | @@ -219,6 +225,7 @@ const MCValue = union(enum) { |
| 219 | 225 | .ptr_stack_offset => |off| .{ .stack_offset = off }, |
| 220 | 226 | .register => |reg| .{ .indirect = .{ .reg = reg } }, |
| 221 | 227 | .register_offset => |reg_off| .{ .indirect = reg_off }, |
| 228 | .addr_symbol => |sym_off| .{ .load_symbol = sym_off }, |
| 222 | 229 | }; |
| 223 | 230 | } |
| 224 | 231 | |
| ... | ... | @@ -235,6 +242,7 @@ const MCValue = union(enum) { |
| 235 | 242 | .indirect, |
| 236 | 243 | .stack_offset, |
| 237 | 244 | .load_symbol, |
| 245 | .addr_symbol, |
| 238 | 246 | => switch (off) { |
| 239 | 247 | 0 => mcv, |
| 240 | 248 | else => unreachable, // not offsettable |
| ... | ... | @@ -801,6 +809,43 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void { |
| 801 | 809 | try table.ensureUnusedCapacity(self.gpa, additional_count); |
| 802 | 810 | } |
| 803 | 811 | |
| 812 | fn splitType(self: *Self, ty: Type) ![2]Type { |
| 813 | const mod = self.bin_file.comp.module.?; |
| 814 | const classes = mem.sliceTo(&abi.classifySystemV(ty, mod), .none); |
| 815 | var parts: [2]Type = undefined; |
| 816 | if (classes.len == 2) for (&parts, classes, 0..) |*part, class, part_i| { |
| 817 | part.* = switch (class) { |
| 818 | .integer => switch (part_i) { |
| 819 | 0 => Type.u64, |
| 820 | 1 => part: { |
| 821 | const elem_size = ty.abiAlignment(mod).minStrict(.@"8").toByteUnitsOptional().?; |
| 822 | const elem_ty = try mod.intType(.unsigned, @intCast(elem_size * 8)); |
| 823 | break :part switch (@divExact(ty.abiSize(mod) - 8, elem_size)) { |
| 824 | 1 => elem_ty, |
| 825 | else => |len| try mod.arrayType(.{ .len = len, .child = elem_ty.toIntern() }), |
| 826 | }; |
| 827 | }, |
| 828 | else => unreachable, |
| 829 | }, |
| 830 | else => break, |
| 831 | }; |
| 832 | } else if (parts[0].abiSize(mod) + parts[1].abiSize(mod) == ty.abiSize(mod)) return parts; |
| 833 | return std.debug.panic("TODO implement splitType for {}", .{ty.fmt(mod)}); |
| 834 | } |
| 835 | |
| 836 | fn symbolIndex(self: *Self) !u32 { |
| 837 | const mod = self.bin_file.comp.module.?; |
| 838 | const decl_index = mod.funcOwnerDeclIndex(self.func_index); |
| 839 | return switch (self.bin_file.tag) { |
| 840 | .elf => blk: { |
| 841 | const elf_file = self.bin_file.cast(link.File.Elf).?; |
| 842 | const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index); |
| 843 | break :blk atom_index; |
| 844 | }, |
| 845 | else => return self.fail("TODO genSetReg load_symbol for {s}", .{@tagName(self.bin_file.tag)}), |
| 846 | }; |
| 847 | } |
| 848 | |
| 804 | 849 | fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: Alignment) !u32 { |
| 805 | 850 | self.stack_align = self.stack_align.max(abi_align); |
| 806 | 851 | // TODO find a free slot instead of always appending |
| ... | ... | @@ -1610,40 +1655,41 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { |
| 1610 | 1655 | |
| 1611 | 1656 | fn airSlicePtr(self: *Self, inst: Air.Inst.Index) !void { |
| 1612 | 1657 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1613 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1614 | | const mcv = try self.resolveInst(ty_op.operand); |
| 1615 | | break :result try self.slicePtr(mcv); |
| 1658 | const result = result: { |
| 1659 | const src_mcv = try self.resolveInst(ty_op.operand); |
| 1660 | if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result src_mcv; |
| 1661 | |
| 1662 | const dst_mcv = try self.allocRegOrMem(inst, true); |
| 1663 | const dst_ty = self.typeOfIndex(inst); |
| 1664 | try self.genCopy(dst_ty, dst_mcv, src_mcv); |
| 1665 | break :result dst_mcv; |
| 1616 | 1666 | }; |
| 1617 | 1667 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1618 | 1668 | } |
| 1619 | 1669 | |
| 1620 | | fn slicePtr(self: *Self, mcv: MCValue) !MCValue { |
| 1621 | | switch (mcv) { |
| 1622 | | .dead, .unreach, .none => unreachable, |
| 1623 | | .register => unreachable, // a slice doesn't fit in one register |
| 1624 | | .stack_offset => |off| { |
| 1625 | | return MCValue{ .stack_offset = off }; |
| 1626 | | }, |
| 1627 | | .memory => |addr| { |
| 1628 | | return MCValue{ .memory = addr }; |
| 1629 | | }, |
| 1630 | | else => return self.fail("TODO slicePtr {s}", .{@tagName(mcv)}), |
| 1631 | | } |
| 1632 | | } |
| 1633 | | |
| 1634 | 1670 | fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void { |
| 1635 | 1671 | const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; |
| 1636 | 1672 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1637 | | const ptr_bits = 64; |
| 1638 | | const ptr_bytes = @divExact(ptr_bits, 8); |
| 1639 | | const mcv = try self.resolveInst(ty_op.operand); |
| 1640 | | switch (mcv) { |
| 1641 | | .dead, .unreach, .none => unreachable, |
| 1642 | | .register => unreachable, // a slice doesn't fit in one register |
| 1673 | const src_mcv = try self.resolveInst(ty_op.operand); |
| 1674 | switch (src_mcv) { |
| 1643 | 1675 | .stack_offset => |off| { |
| 1644 | | break :result MCValue{ .stack_offset = off + ptr_bytes }; |
| 1676 | const len_mcv: MCValue = .{ .stack_offset = off + 8 }; |
| 1677 | if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result len_mcv; |
| 1678 | |
| 1679 | const dst_mcv = try self.allocRegOrMem(inst, true); |
| 1680 | try self.genCopy(Type.usize, dst_mcv, len_mcv); |
| 1681 | break :result dst_mcv; |
| 1682 | }, |
| 1683 | .register_pair => |pair| { |
| 1684 | const len_mcv: MCValue = .{ .register = pair[1] }; |
| 1685 | |
| 1686 | if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result len_mcv; |
| 1687 | |
| 1688 | const dst_mcv = try self.allocRegOrMem(inst, true); |
| 1689 | try self.genCopy(Type.usize, dst_mcv, len_mcv); |
| 1690 | break :result dst_mcv; |
| 1645 | 1691 | }, |
| 1646 | | else => return self.fail("TODO airSliceLen for {}", .{mcv}), |
| 1692 | else => return self.fail("TODO airSliceLen for {}", .{src_mcv}), |
| 1647 | 1693 | } |
| 1648 | 1694 | }; |
| 1649 | 1695 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| ... | ... | @@ -1978,6 +2024,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr_mcv: MCValue, ptr_ty: Type) InnerErro |
| 1978 | 2024 | .register, |
| 1979 | 2025 | .register_offset, |
| 1980 | 2026 | .ptr_stack_offset, |
| 2027 | .addr_symbol, |
| 1981 | 2028 | => try self.genCopy(dst_ty, dst_mcv, ptr_mcv.deref()), |
| 1982 | 2029 | |
| 1983 | 2030 | .memory, |
| ... | ... | @@ -2019,11 +2066,6 @@ fn store(self: *Self, pointer: MCValue, value: MCValue, ptr_ty: Type, value_ty: |
| 2019 | 2066 | |
| 2020 | 2067 | log.debug("storing {}:{} in {}:{}", .{ value, value_ty.fmt(mod), pointer, ptr_ty.fmt(mod) }); |
| 2021 | 2068 | |
| 2022 | | if (value_ty.isSlice(mod)) { |
| 2023 | | // cheat a bit by loading in two parts |
| 2024 | | |
| 2025 | | } |
| 2026 | | |
| 2027 | 2069 | switch (pointer) { |
| 2028 | 2070 | .none => unreachable, |
| 2029 | 2071 | .undef => unreachable, |
| ... | ... | @@ -2192,7 +2234,11 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void { |
| 2192 | 2234 | |
| 2193 | 2235 | const dst_mcv = switch (src_mcv) { |
| 2194 | 2236 | .register => |src_reg| dst: { |
| 2195 | | try self.register_manager.getReg(src_reg, null); |
| 2237 | self.register_manager.getRegAssumeFree(src_reg, null); |
| 2238 | break :dst src_mcv; |
| 2239 | }, |
| 2240 | .register_pair => |pair| dst: { |
| 2241 | for (pair) |reg| self.register_manager.getRegAssumeFree(reg, null); |
| 2196 | 2242 | break :dst src_mcv; |
| 2197 | 2243 | }, |
| 2198 | 2244 | else => return self.fail("TODO: airArg {s}", .{@tagName(src_mcv)}), |
| ... | ... | @@ -3056,6 +3102,8 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT |
| 3056 | 3102 | |
| 3057 | 3103 | /// Sets the value without any modifications to register allocation metadata or stack allocation metadata. |
| 3058 | 3104 | fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { |
| 3105 | const mod = self.bin_file.comp.module.?; |
| 3106 | |
| 3059 | 3107 | // There isn't anything to store |
| 3060 | 3108 | if (dst_mcv == .none) return; |
| 3061 | 3109 | |
| ... | ... | @@ -3066,7 +3114,6 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { |
| 3066 | 3114 | |
| 3067 | 3115 | switch (dst_mcv) { |
| 3068 | 3116 | .register => |reg| return self.genSetReg(ty, reg, src_mcv), |
| 3069 | | .register_pair => |pair| return self.genSetRegPair(ty, pair, src_mcv), |
| 3070 | 3117 | .register_offset => |dst_reg_off| try self.genSetReg(ty, dst_reg_off.reg, switch (src_mcv) { |
| 3071 | 3118 | .none, |
| 3072 | 3119 | .unreach, |
| ... | ... | @@ -3084,7 +3131,47 @@ fn genCopy(self: *Self, ty: Type, dst_mcv: MCValue, src_mcv: MCValue) !void { |
| 3084 | 3131 | }), |
| 3085 | 3132 | .stack_offset => |off| return self.genSetStack(ty, off, src_mcv), |
| 3086 | 3133 | .memory => |addr| return self.genSetMem(ty, addr, src_mcv), |
| 3087 | | else => return self.fail("TODO: genCopy {s} with {s}", .{ @tagName(dst_mcv), @tagName(src_mcv) }), |
| 3134 | .register_pair => |dst_regs| { |
| 3135 | const src_info: ?struct { addr_reg: Register, addr_lock: RegisterLock } = switch (src_mcv) { |
| 3136 | .register_pair, .memory, .indirect, .stack_offset => null, |
| 3137 | .load_symbol => src: { |
| 3138 | const src_addr_reg, const src_addr_lock = try self.allocReg(); |
| 3139 | errdefer self.register_manager.unlockReg(src_addr_lock); |
| 3140 | |
| 3141 | try self.genSetReg(Type.usize, src_addr_reg, src_mcv.address()); |
| 3142 | break :src .{ .addr_reg = src_addr_reg, .addr_lock = src_addr_lock }; |
| 3143 | }, |
| 3144 | .air_ref => |src_ref| return self.genCopy( |
| 3145 | ty, |
| 3146 | dst_mcv, |
| 3147 | try self.resolveInst(src_ref), |
| 3148 | ), |
| 3149 | else => return self.fail("TODO implement genCopy for {s} of {}", .{ |
| 3150 | @tagName(src_mcv), ty.fmt(mod), |
| 3151 | }), |
| 3152 | }; |
| 3153 | defer if (src_info) |info| self.register_manager.unlockReg(info.addr_lock); |
| 3154 | |
| 3155 | switch (ty.zigTypeTag(mod)) { |
| 3156 | .Optional => return, |
| 3157 | else => {}, |
| 3158 | } |
| 3159 | |
| 3160 | var part_disp: i32 = 0; |
| 3161 | for (dst_regs, try self.splitType(ty), 0..) |dst_reg, dst_ty, part_i| { |
| 3162 | try self.genSetReg(dst_ty, dst_reg, switch (src_mcv) { |
| 3163 | .register_pair => |src_regs| .{ .register = src_regs[part_i] }, |
| 3164 | .memory, .indirect, .stack_offset => src_mcv.address().offset(part_disp).deref(), |
| 3165 | .load_symbol => .{ .indirect = .{ |
| 3166 | .reg = src_info.?.addr_reg, |
| 3167 | .off = part_disp, |
| 3168 | } }, |
| 3169 | else => unreachable, |
| 3170 | }); |
| 3171 | part_disp += @intCast(dst_ty.abiSize(mod)); |
| 3172 | } |
| 3173 | }, |
| 3174 | else => return std.debug.panic("TODO: genCopy {s} with {s}", .{ @tagName(dst_mcv), @tagName(src_mcv) }), |
| 3088 | 3175 | } |
| 3089 | 3176 | } |
| 3090 | 3177 | |
| ... | ... | @@ -3168,14 +3255,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, src_mcv: MCValue) Inner |
| 3168 | 3255 | try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = offset }); |
| 3169 | 3256 | }, |
| 3170 | 3257 | .load_symbol => |sym_off| { |
| 3171 | | const atom_index = atom: { |
| 3172 | | const decl_index = mod.funcOwnerDeclIndex(self.func_index); |
| 3173 | | |
| 3174 | | if (self.bin_file.cast(link.File.Elf)) |elf_file| { |
| 3175 | | const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index); |
| 3176 | | break :atom atom_index; |
| 3177 | | } else return self.fail("TODO genSetStack for {s}", .{@tagName(self.bin_file.tag)}); |
| 3178 | | }; |
| 3258 | const atom_index = try self.symbolIndex(); |
| 3179 | 3259 | |
| 3180 | 3260 | // setup the src pointer |
| 3181 | 3261 | _ = try self.addInst(.{ |
| ... | ... | @@ -3443,16 +3523,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 3443 | 3523 | .load_symbol => |sym_off| { |
| 3444 | 3524 | assert(sym_off.off == 0); |
| 3445 | 3525 | |
| 3446 | | const decl_index = mod.funcOwnerDeclIndex(self.func_index); |
| 3526 | const atom_index = try self.symbolIndex(); |
| 3447 | 3527 | |
| 3448 | | const atom_index = switch (self.bin_file.tag) { |
| 3449 | | .elf => blk: { |
| 3450 | | const elf_file = self.bin_file.cast(link.File.Elf).?; |
| 3451 | | const atom_index = try elf_file.zigObjectPtr().?.getOrCreateMetadataForDecl(elf_file, decl_index); |
| 3452 | | break :blk atom_index; |
| 3453 | | }, |
| 3454 | | else => return self.fail("TODO genSetReg load_symbol for {s}", .{@tagName(self.bin_file.tag)}), |
| 3455 | | }; |
| 3456 | 3528 | _ = try self.addInst(.{ |
| 3457 | 3529 | .tag = .load_symbol, |
| 3458 | 3530 | .data = .{ |
| ... | ... | @@ -3485,27 +3557,23 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, src_mcv: MCValue) InnerError! |
| 3485 | 3557 | }, |
| 3486 | 3558 | }); |
| 3487 | 3559 | }, |
| 3488 | | else => return self.fail("TODO: genSetReg {s}", .{@tagName(src_mcv)}), |
| 3489 | | } |
| 3490 | | } |
| 3491 | | |
| 3492 | | fn genSetRegPair(self: *Self, ty: Type, pair: [2]Register, src_mcv: MCValue) InnerError!void { |
| 3493 | | const mod = self.bin_file.comp.module.?; |
| 3494 | | const abi_size: u32 = @intCast(ty.abiSize(mod)); |
| 3495 | | |
| 3496 | | assert(abi_size > 8 and abi_size <= 16); // must fit only fit into two registers |
| 3560 | .addr_symbol => |sym_off| { |
| 3561 | assert(sym_off.off == 0); |
| 3497 | 3562 | |
| 3498 | | switch (src_mcv) { |
| 3499 | | .air_ref => |ref| return self.genSetRegPair(ty, pair, try self.resolveInst(ref)), |
| 3500 | | .load_symbol => |sym_off| { |
| 3501 | | _ = sym_off; |
| 3502 | | // return self.fail("TODO: genSetRegPair load_symbol", .{}); |
| 3503 | | // commented out just for testing. |
| 3563 | const atom_index = try self.symbolIndex(); |
| 3504 | 3564 | |
| 3505 | | // plan here is to load the address into a temporary register and |
| 3506 | | // copy into the pair. |
| 3565 | _ = try self.addInst(.{ |
| 3566 | .tag = .load_symbol, |
| 3567 | .data = .{ |
| 3568 | .payload = try self.addExtra(Mir.LoadSymbolPayload{ |
| 3569 | .register = reg.id(), |
| 3570 | .atom_index = atom_index, |
| 3571 | .sym_index = sym_off.sym, |
| 3572 | }), |
| 3573 | }, |
| 3574 | }); |
| 3507 | 3575 | }, |
| 3508 | | else => return self.fail("TODO: genSetRegPair {s}", .{@tagName(src_mcv)}), |
| 3576 | else => return self.fail("TODO: genSetReg {s}", .{@tagName(src_mcv)}), |
| 3509 | 3577 | } |
| 3510 | 3578 | } |
| 3511 | 3579 | |