| author | |
| committer | |
| log | 5bba041bae8d76b70e72c848ec1a7116e70a5464 |
| tree | 5d0311237037472505658b6adac4571924530314 |
| parent | 9c82f3ae6f96e41757cdb5985aae7793bd3907e4 |
This matches the current design in x86_64 backend and significantly
simplifies handling of PIE targets in aarch64 backend.3 files changed, 138 insertions(+), 87 deletions(-)
src/arch/aarch64/CodeGen.zig+52-19| ... | @@ -115,6 +115,14 @@ const MCValue = union(enum) { | ... | @@ -115,6 +115,14 @@ const MCValue = union(enum) { |
| 115 | /// The value is in memory at a hard-coded address. | 115 | /// The value is in memory at a hard-coded address. |
| 116 | /// If the type is a pointer, it means the pointer address is at this memory location. | 116 | /// If the type is a pointer, it means the pointer address is at this memory location. |
| 117 | memory: u64, | 117 | memory: u64, |
| 118 | /// The value is in memory referenced indirectly via a GOT entry index. | ||
| 119 | /// If the type is a pointer, it means the pointer is referenced indirectly via GOT. | ||
| 120 | /// When lowered, linker will emit relocations of type ARM64_RELOC_GOT_LOAD_PAGE21 and ARM64_RELOC_GOT_LOAD_PAGEOFF12. | ||
| 121 | got_load: u32, | ||
| 122 | /// The value is in memory referenced directly via symbol index. | ||
| 123 | /// If the type is a pointer, it means the pointer is referenced directly via symbol index. | ||
| 124 | /// When lowered, linker will emit a relocation of type ARM64_RELOC_PAGE21 and ARM64_RELOC_PAGEOFF12. | ||
| 125 | direct_load: u32, | ||
| 118 | /// The value is one of the stack variables. | 126 | /// The value is one of the stack variables. |
| 119 | /// If the type is a pointer, it means the pointer address is in the stack at this offset. | 127 | /// If the type is a pointer, it means the pointer address is in the stack at this offset. |
| 120 | stack_offset: u32, | 128 | stack_offset: u32, |
| ... | @@ -1802,6 +1810,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo | ... | @@ -1802,6 +1810,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 1802 | }, | 1810 | }, |
| 1803 | .memory, | 1811 | .memory, |
| 1804 | .stack_offset, | 1812 | .stack_offset, |
| 1813 | .got_load, | ||
| 1814 | .direct_load, | ||
| 1805 | => { | 1815 | => { |
| 1806 | const reg = try self.register_manager.allocReg(null); | 1816 | const reg = try self.register_manager.allocReg(null); |
| 1807 | self.register_manager.freezeRegs(&.{reg}); | 1817 | self.register_manager.freezeRegs(&.{reg}); |
| ... | @@ -1946,6 +1956,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type | ... | @@ -1946,6 +1956,11 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 1946 | const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr); | 1956 | const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr); |
| 1947 | try self.store(.{ .register = addr_reg }, value, ptr_ty, value_ty); | 1957 | try self.store(.{ .register = addr_reg }, value, ptr_ty, value_ty); |
| 1948 | }, | 1958 | }, |
| 1959 | .got_load, | ||
| 1960 | .direct_load, | ||
| 1961 | => { | ||
| 1962 | return self.fail("TODO implement storing to {}", .{ptr}); | ||
| 1963 | }, | ||
| 1949 | } | 1964 | } |
| 1950 | } | 1965 | } |
| 1951 | 1966 | ||
| ... | @@ -2114,6 +2129,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2114,6 +2129,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2114 | .memory => unreachable, | 2129 | .memory => unreachable, |
| 2115 | .compare_flags_signed => unreachable, | 2130 | .compare_flags_signed => unreachable, |
| 2116 | .compare_flags_unsigned => unreachable, | 2131 | .compare_flags_unsigned => unreachable, |
| 2132 | .got_load => unreachable, | ||
| 2133 | .direct_load => unreachable, | ||
| 2117 | .register => |reg| { | 2134 | .register => |reg| { |
| 2118 | try self.register_manager.getReg(reg, null); | 2135 | try self.register_manager.getReg(reg, null); |
| 2119 | try self.genSetReg(arg_ty, reg, arg_mcv); | 2136 | try self.genSetReg(arg_ty, reg, arg_mcv); |
| ... | @@ -2160,10 +2177,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2160,10 +2177,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index) !void { |
| 2160 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { | 2177 | } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { |
| 2161 | if (func_value.castTag(.function)) |func_payload| { | 2178 | if (func_value.castTag(.function)) |func_payload| { |
| 2162 | const func = func_payload.data; | 2179 | const func = func_payload.data; |
| 2163 | // TODO I'm hacking my way through here by repurposing .memory for storing | ||
| 2164 | // index to the GOT target symbol index. | ||
| 2165 | try self.genSetReg(Type.initTag(.u64), .x30, .{ | 2180 | try self.genSetReg(Type.initTag(.u64), .x30, .{ |
| 2166 | .memory = func.owner_decl.link.macho.local_sym_index, | 2181 | .got_load = func.owner_decl.link.macho.local_sym_index, |
| 2167 | }); | 2182 | }); |
| 2168 | // blr x30 | 2183 | // blr x30 |
| 2169 | _ = try self.addInst(.{ | 2184 | _ = try self.addInst(.{ |
| ... | @@ -3015,6 +3030,12 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro | ... | @@ -3015,6 +3030,12 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 3015 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), | 3030 | else => return self.fail("TODO implement storing other types abi_size={}", .{abi_size}), |
| 3016 | } | 3031 | } |
| 3017 | }, | 3032 | }, |
| 3033 | .got_load, | ||
| 3034 | .direct_load, | ||
| 3035 | => |sym_index| { | ||
| 3036 | _ = sym_index; | ||
| 3037 | return self.fail("TODO implement set stack variable from {}", .{mcv}); | ||
| 3038 | }, | ||
| 3018 | .memory => |vaddr| { | 3039 | .memory => |vaddr| { |
| 3019 | _ = vaddr; | 3040 | _ = vaddr; |
| 3020 | return self.fail("TODO implement set stack variable from memory vaddr", .{}); | 3041 | return self.fail("TODO implement set stack variable from memory vaddr", .{}); |
| ... | @@ -3151,22 +3172,34 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void | ... | @@ -3151,22 +3172,34 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 3151 | .data = .{ .rr = .{ .rd = reg, .rn = src_reg } }, | 3172 | .data = .{ .rr = .{ .rd = reg, .rn = src_reg } }, |
| 3152 | }); | 3173 | }); |
| 3153 | }, | 3174 | }, |
| 3154 | .memory => |addr| { | 3175 | .got_load, |
| 3155 | const owner_decl = self.mod_fn.owner_decl; | 3176 | .direct_load, |
| 3156 | // TODO when refactoring LinkBlock, make this into a generic function. | 3177 | => |sym_index| { |
| 3157 | const atom_index = switch (self.bin_file.tag) { | 3178 | const tag: Mir.Inst.Tag = switch (mcv) { |
| 3158 | .macho => owner_decl.link.macho.local_sym_index, | 3179 | .got_load => .load_memory_got, |
| 3159 | .elf => owner_decl.link.elf.local_sym_index, | 3180 | .direct_load => .load_memory_direct, |
| 3160 | .plan9 => @intCast(u32, owner_decl.link.plan9.sym_index orelse 0), | 3181 | else => unreachable, |
| 3161 | else => return self.fail("TODO handle aarch64 load memory in {}", .{self.bin_file.tag}), | ||
| 3162 | }; | 3182 | }; |
| 3183 | _ = try self.addInst(.{ | ||
| 3184 | .tag = tag, | ||
| 3185 | .data = .{ | ||
| 3186 | .payload = try self.addExtra(Mir.LoadMemoryPie{ | ||
| 3187 | .register = @enumToInt(reg), | ||
| 3188 | .atom_index = self.mod_fn.owner_decl.link.macho.local_sym_index, | ||
| 3189 | .sym_index = sym_index, | ||
| 3190 | }), | ||
| 3191 | }, | ||
| 3192 | }); | ||
| 3193 | }, | ||
| 3194 | .memory => |addr| { | ||
| 3163 | _ = try self.addInst(.{ | 3195 | _ = try self.addInst(.{ |
| 3164 | .tag = .load_memory, | 3196 | .tag = .load_memory, |
| 3165 | .data = .{ .payload = try self.addExtra(Mir.LoadMemory{ | 3197 | .data = .{ |
| 3166 | .atom_index = atom_index, | 3198 | .load_memory = .{ |
| 3167 | .register = @enumToInt(reg), | 3199 | .register = @enumToInt(reg), |
| 3168 | .addr = @intCast(u32, addr), | 3200 | .addr = @intCast(u32, addr), |
| 3169 | }) }, | 3201 | }, |
| 3202 | }, | ||
| 3170 | }); | 3203 | }); |
| 3171 | }, | 3204 | }, |
| 3172 | .stack_offset => |unadjusted_off| { | 3205 | .stack_offset => |unadjusted_off| { |
| ... | @@ -3385,9 +3418,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa | ... | @@ -3385,9 +3418,9 @@ fn lowerDeclRef(self: *Self, tv: TypedValue, decl: *Module.Decl) InnerError!MCVa |
| 3385 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; | 3418 | const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; |
| 3386 | return MCValue{ .memory = got_addr }; | 3419 | return MCValue{ .memory = got_addr }; |
| 3387 | } else if (self.bin_file.cast(link.File.MachO)) |_| { | 3420 | } else if (self.bin_file.cast(link.File.MachO)) |_| { |
| 3388 | // TODO I'm hacking my way through here by repurposing .memory for storing | 3421 | // Because MachO is PIE-always-on, we defer memory address resolution until |
| 3389 | // index to the GOT target symbol index. | 3422 | // the linker has enough info to perform relocations. |
| 3390 | return MCValue{ .memory = decl.link.macho.local_sym_index }; | 3423 | return MCValue{ .got_load = decl.link.macho.local_sym_index }; |
| 3391 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { | 3424 | } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { |
| 3392 | const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; | 3425 | const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; |
| 3393 | return MCValue{ .memory = got_addr }; | 3426 | return MCValue{ .memory = got_addr }; |
src/arch/aarch64/Emit.zig+72-62| ... | @@ -109,6 +109,8 @@ pub fn emitMir( | ... | @@ -109,6 +109,8 @@ pub fn emitMir( |
| 109 | .eor_shifted_register => try emit.mirLogicalShiftedRegister(inst), | 109 | .eor_shifted_register => try emit.mirLogicalShiftedRegister(inst), |
| 110 | 110 | ||
| 111 | .load_memory => try emit.mirLoadMemory(inst), | 111 | .load_memory => try emit.mirLoadMemory(inst), |
| 112 | .load_memory_got => try emit.mirLoadMemoryPie(inst), | ||
| 113 | .load_memory_direct => try emit.mirLoadMemoryPie(inst), | ||
| 112 | 114 | ||
| 113 | .ldp => try emit.mirLoadStoreRegisterPair(inst), | 115 | .ldp => try emit.mirLoadStoreRegisterPair(inst), |
| 114 | .stp => try emit.mirLoadStoreRegisterPair(inst), | 116 | .stp => try emit.mirLoadStoreRegisterPair(inst), |
| ... | @@ -205,21 +207,18 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize { | ... | @@ -205,21 +207,18 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize { |
| 205 | } | 207 | } |
| 206 | 208 | ||
| 207 | switch (tag) { | 209 | switch (tag) { |
| 210 | .load_memory_got, | ||
| 211 | .load_memory_direct, | ||
| 212 | => return 2 * 4, | ||
| 208 | .load_memory => { | 213 | .load_memory => { |
| 209 | if (emit.bin_file.options.pie) { | 214 | const load_memory = emit.mir.instructions.items(.data)[inst].load_memory; |
| 210 | // adrp, ldr | 215 | const addr = load_memory.addr; |
| 211 | return 2 * 4; | 216 | |
| 212 | } else { | 217 | // movz, [movk, ...], ldr |
| 213 | const payload = emit.mir.instructions.items(.data)[inst].payload; | 218 | if (addr <= math.maxInt(u16)) return 2 * 4; |
| 214 | const load_memory = emit.mir.extraData(Mir.LoadMemory, payload).data; | 219 | if (addr <= math.maxInt(u32)) return 3 * 4; |
| 215 | const addr = load_memory.addr; | 220 | if (addr <= math.maxInt(u48)) return 4 * 4; |
| 216 | 221 | return 5 * 4; | |
| 217 | // movz, [movk, ...], ldr | ||
| 218 | if (addr <= math.maxInt(u16)) return 2 * 4; | ||
| 219 | if (addr <= math.maxInt(u32)) return 3 * 4; | ||
| 220 | if (addr <= math.maxInt(u48)) return 4 * 4; | ||
| 221 | return 5 * 4; | ||
| 222 | } | ||
| 223 | }, | 222 | }, |
| 224 | .pop_regs, .push_regs => { | 223 | .pop_regs, .push_regs => { |
| 225 | const reg_list = emit.mir.instructions.items(.data)[inst].reg_list; | 224 | const reg_list = emit.mir.instructions.items(.data)[inst].reg_list; |
| ... | @@ -658,58 +657,69 @@ fn mirLogicalShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void { | ... | @@ -658,58 +657,69 @@ fn mirLogicalShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 658 | 657 | ||
| 659 | fn mirLoadMemory(emit: *Emit, inst: Mir.Inst.Index) !void { | 658 | fn mirLoadMemory(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 660 | assert(emit.mir.instructions.items(.tag)[inst] == .load_memory); | 659 | assert(emit.mir.instructions.items(.tag)[inst] == .load_memory); |
| 661 | const payload = emit.mir.instructions.items(.data)[inst].payload; | 660 | const load_memory = emit.mir.instructions.items(.data)[inst].load_memory; |
| 662 | const load_memory = emit.mir.extraData(Mir.LoadMemory, payload).data; | ||
| 663 | const reg = @intToEnum(Register, load_memory.register); | 661 | const reg = @intToEnum(Register, load_memory.register); |
| 664 | const addr = load_memory.addr; | 662 | const addr = load_memory.addr; |
| 663 | // The value is in memory at a hard-coded address. | ||
| 664 | // If the type is a pointer, it means the pointer address is at this memory location. | ||
| 665 | try emit.moveImmediate(reg, addr); | ||
| 666 | try emit.writeInstruction(Instruction.ldr( | ||
| 667 | reg, | ||
| 668 | reg, | ||
| 669 | Instruction.LoadStoreOffset.none, | ||
| 670 | )); | ||
| 671 | } | ||
| 665 | 672 | ||
| 666 | if (emit.bin_file.options.pie) { | 673 | fn mirLoadMemoryPie(emit: *Emit, inst: Mir.Inst.Index) !void { |
| 667 | // PC-relative displacement to the entry in the GOT table. | 674 | const tag = emit.mir.instructions.items(.tag)[inst]; |
| 668 | // adrp | 675 | const payload = emit.mir.instructions.items(.data)[inst].payload; |
| 669 | const offset = @intCast(u32, emit.code.items.len); | 676 | const data = emit.mir.extraData(Mir.LoadMemoryPie, payload).data; |
| 670 | try emit.writeInstruction(Instruction.adrp(reg, 0)); | 677 | const reg = @intToEnum(Register, data.register); |
| 671 | 678 | ||
| 672 | // ldr reg, reg, offset | 679 | // PC-relative displacement to the entry in the GOT table. |
| 673 | try emit.writeInstruction(Instruction.ldr( | 680 | // adrp |
| 674 | reg, | 681 | const offset = @intCast(u32, emit.code.items.len); |
| 675 | reg, | 682 | try emit.writeInstruction(Instruction.adrp(reg, 0)); |
| 676 | Instruction.LoadStoreOffset.imm(0), | 683 | |
| 677 | )); | 684 | // ldr reg, reg, offset |
| 678 | 685 | try emit.writeInstruction(Instruction.ldr( | |
| 679 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { | 686 | reg, |
| 680 | const atom = macho_file.atom_by_index_table.get(load_memory.atom_index).?; | 687 | reg, |
| 681 | // Page reloc for adrp instruction. | 688 | Instruction.LoadStoreOffset.imm(0), |
| 682 | try atom.relocs.append(emit.bin_file.allocator, .{ | 689 | )); |
| 683 | .offset = offset, | 690 | |
| 684 | .target = .{ .local = addr }, | 691 | if (emit.bin_file.cast(link.File.MachO)) |macho_file| { |
| 685 | .addend = 0, | 692 | const atom = macho_file.atom_by_index_table.get(data.atom_index).?; |
| 686 | .subtractor = null, | 693 | // Page reloc for adrp instruction. |
| 687 | .pcrel = true, | 694 | try atom.relocs.append(emit.bin_file.allocator, .{ |
| 688 | .length = 2, | 695 | .offset = offset, |
| 689 | .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21), | 696 | .target = .{ .local = data.sym_index }, |
| 690 | }); | 697 | .addend = 0, |
| 691 | // Pageoff reloc for adrp instruction. | 698 | .subtractor = null, |
| 692 | try atom.relocs.append(emit.bin_file.allocator, .{ | 699 | .pcrel = true, |
| 693 | .offset = offset + 4, | 700 | .length = 2, |
| 694 | .target = .{ .local = addr }, | 701 | .@"type" = switch (tag) { |
| 695 | .addend = 0, | 702 | .load_memory_got => @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGE21), |
| 696 | .subtractor = null, | 703 | .load_memory_direct => @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_PAGE21), |
| 697 | .pcrel = false, | 704 | else => unreachable, |
| 698 | .length = 2, | 705 | }, |
| 699 | .@"type" = @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12), | 706 | }); |
| 700 | }); | 707 | // Pageoff reloc for adrp instruction. |
| 701 | } else { | 708 | try atom.relocs.append(emit.bin_file.allocator, .{ |
| 702 | return emit.fail("TODO implement load_memory for PIE GOT indirection on this platform", .{}); | 709 | .offset = offset + 4, |
| 703 | } | 710 | .target = .{ .local = data.sym_index }, |
| 711 | .addend = 0, | ||
| 712 | .subtractor = null, | ||
| 713 | .pcrel = false, | ||
| 714 | .length = 2, | ||
| 715 | .@"type" = switch (tag) { | ||
| 716 | .load_memory_got => @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_GOT_LOAD_PAGEOFF12), | ||
| 717 | .load_memory_direct => @enumToInt(std.macho.reloc_type_arm64.ARM64_RELOC_PAGEOFF12), | ||
| 718 | else => unreachable, | ||
| 719 | }, | ||
| 720 | }); | ||
| 704 | } else { | 721 | } else { |
| 705 | // The value is in memory at a hard-coded address. | 722 | return emit.fail("TODO implement load_memory for PIE GOT indirection on this platform", .{}); |
| 706 | // If the type is a pointer, it means the pointer address is at this memory location. | ||
| 707 | try emit.moveImmediate(reg, addr); | ||
| 708 | try emit.writeInstruction(Instruction.ldr( | ||
| 709 | reg, | ||
| 710 | reg, | ||
| 711 | Instruction.LoadStoreOffset.none, | ||
| 712 | )); | ||
| 713 | } | 723 | } |
| 714 | } | 724 | } |
| 715 | 725 |
src/arch/aarch64/Mir.zig+14-6| ... | @@ -58,8 +58,12 @@ pub const Inst = struct { | ... | @@ -58,8 +58,12 @@ pub const Inst = struct { |
| 58 | eor_shifted_register, | 58 | eor_shifted_register, |
| 59 | /// Pseudo-instruction: Load memory | 59 | /// Pseudo-instruction: Load memory |
| 60 | /// | 60 | /// |
| 61 | /// Payload is `LoadMemory` | 61 | /// Payload is `load_memory` |
| 62 | load_memory, | 62 | load_memory, |
| 63 | /// Payload is `LoadMemoryPie` | ||
| 64 | load_memory_got, | ||
| 65 | /// Payload is `LoadMemoryPie` | ||
| 66 | load_memory_direct, | ||
| 63 | /// Load Pair of Registers | 67 | /// Load Pair of Registers |
| 64 | ldp, | 68 | ldp, |
| 65 | /// Pseudo-instruction: Load from stack | 69 | /// Pseudo-instruction: Load from stack |
| ... | @@ -157,8 +161,6 @@ pub const Inst = struct { | ... | @@ -157,8 +161,6 @@ pub const Inst = struct { |
| 157 | /// Used by e.g. svc | 161 | /// Used by e.g. svc |
| 158 | imm16: u16, | 162 | imm16: u16, |
| 159 | /// Index into `extra`. Meaning of what can be found there is context-dependent. | 163 | /// Index into `extra`. Meaning of what can be found there is context-dependent. |
| 160 | /// | ||
| 161 | /// Used by e.g. load_memory | ||
| 162 | payload: u32, | 164 | payload: u32, |
| 163 | /// A register | 165 | /// A register |
| 164 | /// | 166 | /// |
| ... | @@ -298,6 +300,10 @@ pub const Inst = struct { | ... | @@ -298,6 +300,10 @@ pub const Inst = struct { |
| 298 | line: u32, | 300 | line: u32, |
| 299 | column: u32, | 301 | column: u32, |
| 300 | }, | 302 | }, |
| 303 | load_memory: struct { | ||
| 304 | register: u32, | ||
| 305 | addr: u32, | ||
| 306 | }, | ||
| 301 | }; | 307 | }; |
| 302 | 308 | ||
| 303 | // Make sure we don't accidentally make instructions bigger than expected. | 309 | // Make sure we don't accidentally make instructions bigger than expected. |
| ... | @@ -335,8 +341,10 @@ pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end | ... | @@ -335,8 +341,10 @@ pub fn extraData(mir: Mir, comptime T: type, index: usize) struct { data: T, end |
| 335 | }; | 341 | }; |
| 336 | } | 342 | } |
| 337 | 343 | ||
| 338 | pub const LoadMemory = struct { | 344 | pub const LoadMemoryPie = struct { |
| 339 | atom_index: u32, | ||
| 340 | register: u32, | 345 | register: u32, |
| 341 | addr: u32, | 346 | /// Index of the containing atom. |
| 347 | atom_index: u32, | ||
| 348 | /// Index into the linker's symbol table. | ||
| 349 | sym_index: u32, | ||
| 342 | }; | 350 | }; |