╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ codegen.zig ──────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭ find file ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╮ ╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │ │ │http│ ssh │ │ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─╯ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
57 .stage2_loongarch => loongarch, 58 .stage2_powerpc => unreachable, 59 .stage2_riscv64 => @import("codegen/riscv64/CodeGen.zig"), 60 .stage2_sparc64 => @import("codegen/sparc64/CodeGen.zig"), 61 .stage2_spirv => @import("codegen/spirv/CodeGen.zig"), 62 .zsf_spork8 => @import("codegen/spork8/CodeGen.zig"), 63 .stage2_wasm => @import("codegen/wasm/CodeGen.zig"), 64 .stage2_x86, .stage2_x86_64 => @import("codegen/x86_64/CodeGen.zig"), 65 _ => unreachable, 66 }; 67 } 68 69 pub fn legalizeFeatures(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) ?*const Air.Legalize.Features { 70 const zcu = pt.zcu; 71 const target = &zcu.navFileScope(nav_index).mod.?.resolved_target.result; 72 switch (target_util.zigBackend(target, zcu.comp.config.use_llvm)) { 73 else => unreachable, 74 inline .stage2_llvm, 75 .stage2_c, 76 .stage2_wasm, 77 .stage2_x86_64, 78 .stage2_aarch64, 79 .stage2_loongarch, 80 .stage2_x86, 81 .stage2_riscv64, 82 .stage2_sparc64, 83 .zsf_spork8, 84 .stage2_spirv, 85 => |backend| { 86 dev.check(devFeatureForBackend(backend)); 87 return importBackend(backend).legalizeFeatures(target); 88 }, 89 } 90 } 91 92 pub fn wantsLiveness(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) bool { 93 const zcu = pt.zcu; 94 const target = &zcu.navFileScope(nav_index).mod.?.resolved_target.result; 95 return switch (target_util.zigBackend(target, zcu.comp.config.use_llvm)) { 96 else => true, 97 .stage2_aarch64, .stage2_loongarch => false, 98 }; 99 } 100 101 /// Every code generation backend has a different MIR representation. However, we want to pass 102 /// MIR from codegen to the linker *regardless* of which backend is in use. So, we use this: a 103 /// union of all MIR types. The active tag is known from the backend in use; see `AnyMir.tag`. 104 pub const AnyMir = union { 105 aarch64: if (dev.env.supports(.aarch64_backend)) @import("codegen/aarch64/Mir.zig") else noreturn, 106 loongarch: if (dev.env.supports(.loongarch_backend)) @import("codegen/loongarch/Mir.zig") else noreturn, 107 riscv64: if (dev.env.supports(.riscv64_backend)) @import("codegen/riscv64/Mir.zig") else noreturn, 108 sparc64: if (dev.env.supports(.sparc64_backend)) @import("codegen/sparc64/Mir.zig") else noreturn, 109 x86_64: if (dev.env.supports(.x86_64_backend)) @import("codegen/x86_64/Mir.zig") else noreturn, 110 wasm: if (dev.env.supports(.wasm_backend)) @import("codegen/wasm/Mir.zig") else noreturn, 111 c: if (dev.env.supports(.c_backend)) @import("codegen/c.zig").Mir else noreturn, 112 spirv: if (dev.env.supports(.spirv_backend)) @import("codegen/spirv/Mir.zig") else noreturn, 113 spork8: if (dev.env.supports(.spork8_backend)) @import("codegen/spork8/Mir.zig") else noreturn, 114 115 pub inline fn tag(comptime backend: std.lang.CompilerBackend) []const u8 { 116 return switch (backend) { 117 .stage2_aarch64 => "aarch64", 118 .stage2_loongarch => "loongarch", 119 .stage2_riscv64 => "riscv64", 120 .stage2_sparc64 => "sparc64", 121 .stage2_x86_64 => "x86_64", 122 .stage2_wasm => "wasm", 123 .stage2_c => "c", 124 .stage2_spirv => "spirv", 125 .zsf_spork8 => "spork8", 126 else => unreachable, 127 }; 128 } 129 130 pub fn deinit(mir: *AnyMir, zcu: *const Zcu) void { 131 const gpa = zcu.gpa; 132 const backend = target_util.zigBackend(&zcu.root_mod.resolved_target.result, zcu.comp.config.use_llvm); 133 switch (backend) { 134 else => unreachable, 135 inline .stage2_aarch64, 136 .stage2_loongarch, 137 .stage2_riscv64, 138 .stage2_sparc64, 139 .stage2_x86_64, 140 .stage2_wasm, 141 .stage2_c, 142 .stage2_spirv, 143 .zsf_spork8, 144 => |backend_ct| @field(mir, tag(backend_ct)).deinit(gpa), 145 } 146 } 147 }; 148 149 /// Runs code generation for a function. This process converts the `Air` emitted by `Sema`, 150 /// alongside annotated `Liveness` data, to machine code in the form of MIR (see `AnyMir`). 151 /// 152 /// This is supposed to be a "pure" process, but some backends are currently buggy; see 153 /// `Zcu.Feature.separate_thread` for details. 154 pub fn generateFunction( 155 lf: *link.File, 156 pt: Zcu.PerThread, 157 func_index: InternPool.Index, 158 air: *const Air, 159 liveness: *const ?Air.Liveness, 160 ) Error!AnyMir { 161 const zcu = pt.zcu; 162 const func = zcu.funcInfo(func_index); 163 const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result; 164 switch (target_util.zigBackend(target, false)) { 165 else => unreachable, 166 inline .stage2_aarch64, 167 .stage2_loongarch, 168 .stage2_riscv64, 169 .stage2_sparc64, 170 .stage2_x86_64, 171 .stage2_wasm, 172 .stage2_c, 173 .zsf_spork8, 174 .stage2_spirv, 175 => |backend| { 176 dev.check(devFeatureForBackend(backend)); 177 const CodeGen = importBackend(backend); 178 const mir = try CodeGen.generate(lf, pt, func_index, air, liveness); 179 return @unionInit(AnyMir, AnyMir.tag(backend), mir); 180 }, 181 } 182 } 183 184 /// Converts the MIR returned by `generateFunction` to finalized machine code to be placed in 185 /// the output binary. This is called from linker implementations, and may query linker state. 186 /// 187 /// This function is not called for the C backend, as `link.C` directly understands its MIR. 188 /// 189 /// The `air` parameter is not supposed to exist, but some backends are currently buggy; see 190 /// `Zcu.Feature.separate_thread` for details. 191 pub fn emitFunction( 192 lf: *link.File, 193 pt: Zcu.PerThread, 194 func_index: InternPool.Index, 195 atom_id: link.File.AtomId, 196 any_mir: *const AnyMir, 197 w: *std.Io.Writer, 198 debug_output: link.File.DebugInfoOutput, 199 ) link.EmitError!void { 200 const zcu = pt.zcu; 201 const func = zcu.funcInfo(func_index); 202 const target = &zcu.navFileScope(func.owner_nav).mod.?.resolved_target.result; 203 204 const tracy_trace = trace(@src()); 205 defer tracy_trace.end(); 206 tracy_trace.addText(zcu.intern_pool.getNav(func.owner_nav).fqn.toSlice(&zcu.intern_pool)); 207 tracy_trace.addTextFmt("func_ip_index={d}", .{func_index}); 208 209 switch (target_util.zigBackend(target, zcu.comp.config.use_llvm)) { 210 else => unreachable, 211 inline .stage2_aarch64, 212 .stage2_loongarch, 213 .stage2_riscv64, 214 .stage2_sparc64, 215 .stage2_x86_64, 216 => |backend| { 217 dev.check(devFeatureForBackend(backend)); 218 const mir = &@field(any_mir, AnyMir.tag(backend)); 219 return mir.emit(lf, pt, func_index, atom_id, w, debug_output); 220 }, 221 } 222 } 223 224 pub fn generateLazyFunction( 225 lf: *link.File, 226 pt: Zcu.PerThread, 227 lazy_sym: link.File.LazySymbol, 228 atom_id: link.File.AtomId, 229 w: *std.Io.Writer, 230 debug_output: link.File.DebugInfoOutput, 231 ) link.EmitError!void { 232 const zcu = pt.zcu; 233 const target = if (Type.fromInterned(lazy_sym.ty).typeDeclInstAllowGeneratedTag(zcu)) |inst_index| 234 &zcu.fileByIndex(inst_index.resolveFile(&zcu.intern_pool)).mod.?.resolved_target.result 235 else 236 zcu.getTarget(); 237 switch (target_util.zigBackend(target, zcu.comp.config.use_llvm)) { 238 else => unreachable, 239 inline .stage2_riscv64, .stage2_x86_64 => |backend| { 240 dev.check(devFeatureForBackend(backend)); 241 return importBackend(backend).generateLazy(lf, pt, lazy_sym, atom_id, w, debug_output); 242 }, 243 } 244 } 245 246 pub fn generateLazySymbol( 247 bin_file: *link.File, 248 pt: Zcu.PerThread, 249 lazy_sym: link.File.LazySymbol, 250 // TODO don't use an "out" parameter like this; put it in the result instead 251 alignment: *Alignment, 252 w: *std.Io.Writer, 253 debug_output: link.File.DebugInfoOutput, 254 reloc_parent: link.File.RelocInfo.Parent, 255 ) link.EmitError!void { 256 const tracy = trace(@src()); 257 defer tracy.end(); 258 tracy.addTextFmt("{t}, {f}", .{ lazy_sym.kind, Type.fromInterned(lazy_sym.ty).fmt(pt) }); 259 260 const comp = bin_file.comp; 261 const zcu = pt.zcu; 262 const ip = &zcu.intern_pool; 263 const target = &comp.root_mod.resolved_target.result; 264 const endian = target.cpu.arch.endian(); 265 266 log.debug("generateLazySymbol: kind = {s}, ty = {f}", .{ 267 @tagName(lazy_sym.kind), 268 Type.fromInterned(lazy_sym.ty).fmt(pt), 269 }); 270 271 if (lazy_sym.kind == .code) { 272 alignment.* = target_util.defaultFunctionAlignment(target); 273 return generateLazyFunction(bin_file, pt, lazy_sym, reloc_parent.atom_index, w, debug_output); 274 } 275 276 if (lazy_sym.ty == .anyerror_type) { 277 alignment.* = .@"4"; 278 const err_names = ip.global_error_set.getNamesFromMainThread(); 279 const strings_start: u32 = @intCast(4 * (1 + err_names.len + @intFromBool(err_names.len > 0))); 280 var string_index = strings_start; 281 try w.rebase(w.end, string_index); 282 w.writeInt(u32, @intCast(err_names.len), endian) catch unreachable; 283 if (err_names.len == 0) return; 284 for (err_names) |err_name_nts| { 285 w.writeInt(u32, string_index, endian) catch unreachable; 286 string_index += @intCast(err_name_nts.toSlice(ip).len + 1); 287 } 288 w.writeInt(u32, string_index, endian) catch unreachable; 289 try w.rebase(w.end, string_index - strings_start); 290 for (err_names) |err_name_nts| { 291 w.writeAll(err_name_nts.toSlice(ip)) catch unreachable; 292 w.writeByte(0) catch unreachable; 293 } 294 } else if (Type.fromInterned(lazy_sym.ty).zigTypeTag(zcu) == .@"enum") { 295 alignment.* = .@"1"; 296 const enum_ty = Type.fromInterned(lazy_sym.ty); 297 const tag_names = enum_ty.enumFields(zcu); 298 for (0..tag_names.len) |tag_index| { 299 const tag_name = tag_names.get(ip)[tag_index].toSlice(ip); 300 try w.rebase(w.end, tag_name.len + 1); 301 w.writeAll(tag_name) catch unreachable; 302 w.writeByte(0) catch unreachable; 303 } 304 } else { 305 return zcu.codegenFailType(lazy_sym.ty, "TODO implement generateLazySymbol for {s} {f}", .{ 306 @tagName(lazy_sym.kind), Type.fromInterned(lazy_sym.ty).fmt(pt), 307 }); 308 } 309 } 310 311 pub fn generateSymbol( 312 bin_file: *link.File, 313 pt: Zcu.PerThread, 314 val: Value, 315 w: *std.Io.Writer, 316 reloc_parent: link.File.RelocInfo.Parent, 317 ) link.EmitError!void { 318 const tracy = trace(@src()); 319 defer tracy.end(); 320 321 const zcu = pt.zcu; 322 const ip = &zcu.intern_pool; 323 const ty = val.typeOf(zcu); 324 325 const target = zcu.getTarget(); 326 const endian = target.cpu.arch.endian(); 327 328 log.debug("generateSymbol: val = {f}", .{val.fmtValue(pt)}); 329 330 const abi_size = math.cast(usize, ty.abiSize(zcu)) orelse { 331 return zcu.comp.link_diags.fail("failed to generate symbol: type size overflow", .{}); 332 }; 333 334 if (val.isUndef(zcu)) { 335 try w.splatByteAll(0xaa, abi_size); 336 return; 337 } 338 339 switch (ip.indexToKey(val.toIntern())) { 340 .int_type, 341 .ptr_type, 342 .array_type, 343 .vector_type, 344 .opt_type, 345 .anyframe_type, 346 .error_union_type, 347 .simple_type, 348 .struct_type, 349 .tuple_type, 350 .union_type, 351 .opaque_type, 352 .spirv_type, 353 .enum_type, 354 .func_type, 355 .error_set_type, 356 .inferred_error_set_type, 357 => unreachable, // types, not values 358 359 .undef => unreachable, // handled above 360 .simple_value => |simple_value| switch (simple_value) { 361 .void => unreachable, // non-runtime value 362 .null => unreachable, // non-runtime value 363 .@"unreachable" => unreachable, // non-runtime value 364 .false, .true => try w.writeByte(switch (simple_value) { 365 .false => 0, 366 .true => 1, 367 else => unreachable, 368 }), 369 }, 370 .@"extern", 371 .func, 372 .enum_literal, 373 => unreachable, // non-runtime values 374 .int => { 375 var space: Value.BigIntSpace = undefined; 376 const int_val = val.toBigInt(&space, zcu); 377 int_val.writeTwosComplement(try w.writableSlice(abi_size), endian); 378 }, 379 .err => |err| { 380 const int = try pt.getErrorValue(err.name); 381 try w.writeInt(u16, @intCast(int), endian); 382 }, 383 .error_union => |error_union| { 384 const payload_ty = ty.errorUnionPayload(zcu); 385 const err_val: u16 = switch (error_union.val) { 386 .err_name => |err_name| @intCast(try pt.getErrorValue(err_name)), 387 .payload => 0, 388 }; 389 390 if (!payload_ty.hasRuntimeBits(zcu)) { 391 try w.writeInt(u16, err_val, endian); 392 return; 393 } 394 395 const payload_align = payload_ty.abiAlignment(zcu); 396 const error_align = Type.anyerror.abiAlignment(zcu); 397 const abi_align = ty.abiAlignment(zcu); 398 399 // error value first when its type is larger than the error union's payload 400 if (error_align.order(payload_align) == .gt) { 401 try w.writeInt(u16, err_val, endian); 402 } 403 404 // emit payload part of the error union 405 { 406 const begin = w.end; 407 try generateSymbol(bin_file, pt, Value.fromInterned(switch (error_union.val) { 408 .err_name => try pt.intern(.{ .undef = payload_ty.toIntern() }), 409 .payload => |payload| payload, 410 }), w, reloc_parent); 411 const unpadded_end = w.end - begin; 412 const padded_end = abi_align.forward(unpadded_end); 413 const padding: usize = @intCast(padded_end - unpadded_end); 414 415 if (padding > 0) { 416 try w.splatByteAll(0, padding); 417 } 418 } 419 420 // Payload size is larger than error set, so emit our error set last 421 if (error_align.compare(.lte, payload_align)) { 422 const begin = w.end; 423 try w.writeInt(u16, err_val, endian); 424 const unpadded_end = w.end - begin; 425 const padded_end = abi_align.forward(unpadded_end); 426 const padding: usize = @intCast(padded_end - unpadded_end); 427 428 if (padding > 0) { 429 try w.splatByteAll(0, padding); 430 } 431 } 432 }, 433 .enum_tag => |enum_tag| { 434 const int_tag_ty = ty.backingIntType(zcu); 435 try generateSymbol(bin_file, pt, try pt.getCoerced(Value.fromInterned(enum_tag.int), int_tag_ty), w, reloc_parent); 436 }, 437 .float => |float| storage: switch (float.storage) { 438 .f16 => |f16_val| try w.writeInt(u16, @bitCast(f16_val), endian), 439 .f32 => |f32_val| try w.writeInt(u32, @bitCast(f32_val), endian), 440 .f64 => |f64_val| try w.writeInt(u64, @bitCast(f64_val), endian), 441 .f80 => |f80_val| { 442 try w.writeInt(u80, @bitCast(f80_val), endian); 443 try w.splatByteAll(0, abi_size - 10); 444 }, 445 .f128 => |f128_val| switch (Type.fromInterned(float.ty).floatBits(target)) { 446 else => unreachable, 447 16 => continue :storage .{ .f16 = @floatCast(f128_val) }, 448 32 => continue :storage .{ .f32 = @floatCast(f128_val) }, 449 64 => continue :storage .{ .f64 = @floatCast(f128_val) }, 450 128 => try w.writeInt(u128, @bitCast(f128_val), endian), 451 }, 452 }, 453 .ptr => try lowerPtr(bin_file, pt, val.toIntern(), w, reloc_parent, 0), 454 .slice => |slice| { 455 try generateSymbol(bin_file, pt, Value.fromInterned(slice.ptr), w, reloc_parent); 456 try generateSymbol(bin_file, pt, Value.fromInterned(slice.len), w, reloc_parent); 457 }, 458 .opt => { 459 const payload_type = ty.optionalChild(zcu); 460 const payload_val = val.optionalValue(zcu); 461 462 if (ty.optionalReprIsPayload(zcu)) { 463 if (payload_val) |value| { 464 try generateSymbol(bin_file, pt, value, w, reloc_parent); 465 } else { 466 try w.splatByteAll(0, abi_size); 467 } 468 } else { 469 const padding = abi_size - @as(usize, @intCast(payload_type.abiSize(zcu))) - 1; 470 if (payload_type.hasRuntimeBits(zcu)) { 471 const value = payload_val orelse Value.fromInterned(try pt.intern(.{ 472 .undef = payload_type.toIntern(), 473 })); 474 try generateSymbol(bin_file, pt, value, w, reloc_parent); 475 } 476 try w.writeByte(@intFromBool(payload_val != null)); 477 try w.splatByteAll(0, padding); 478 } 479 }, 480 .aggregate => |aggregate| switch (ip.indexToKey(ty.toIntern())) { 481 .array_type => |array_type| switch (aggregate.storage) { 482 .bytes => |bytes| try w.writeAll(bytes.toSlice(array_type.lenIncludingSentinel(), ip)), 483 .elems, .repeated_elem => { 484 var index: u64 = 0; 485 while (index < array_type.lenIncludingSentinel()) : (index += 1) { 486 try generateSymbol(bin_file, pt, Value.fromInterned(switch (aggregate.storage) { 487 .bytes => unreachable, 488 .elems => |elems| elems[@intCast(index)], 489 .repeated_elem => |elem| if (index < array_type.len) 490 elem 491 else 492 array_type.sentinel, 493 }), w, reloc_parent); 494 } 495 }, 496 }, 497 .vector_type => |vector_type| { 498 const vector_bool_bitpacked = switch (zcu.comp.getZigBackend()) { 499 .stage2_wasm => false, 500 else => true, 501 }; 502 if (vector_type.child == .bool_type and vector_bool_bitpacked) { 503 const bytes = try w.writableSlice(abi_size); 504 @memset(bytes, 0xaa); 505 var index: usize = 0; 506 const len = math.cast(usize, vector_type.len) orelse { 507 return zcu.comp.link_diags.fail("failed to generate symbol: vector length overflow", .{}); 508 }; 509 while (index < len) : (index += 1) { 510 const bit_index = switch (endian) { 511 .big => len - 1 - index, 512 .little => index, 513 }; 514 const byte = &bytes[bit_index / 8]; 515 const mask = @as(u8, 1) << @truncate(bit_index); 516 if (switch (switch (aggregate.storage) { 517 .bytes => unreachable, 518 .elems => |elems| elems[index], 519 .repeated_elem => |elem| elem, 520 }) { 521 .bool_true => true, 522 .bool_false => false, 523 else => |elem| switch (ip.indexToKey(elem)) { 524 .undef => continue, 525 .int => |int| switch (int.storage) { 526 .u64 => |x| switch (x) { 527 0 => false, 528 1 => true, 529 else => unreachable, 530 }, 531 .i64 => |x| switch (x) { 532 -1 => true, 533 0 => false, 534 else => unreachable, 535 }, 536 else => unreachable, 537 }, 538 else => unreachable, 539 }, 540 }) byte.* |= mask else byte.* &= ~mask; 541 } 542 } else { 543 switch (aggregate.storage) { 544 .bytes => |bytes| try w.writeAll(bytes.toSlice(vector_type.len, ip)), 545 .elems, .repeated_elem => { 546 var index: u64 = 0; 547 while (index < vector_type.len) : (index += 1) { 548 try generateSymbol(bin_file, pt, Value.fromInterned(switch (aggregate.storage) { 549 .bytes => unreachable, 550 .elems => |elems| elems[@intCast(index)], 551 .repeated_elem => |elem| elem, 552 }), w, reloc_parent); 553 } 554 }, 555 } 556 557 const padding = abi_size - @as(usize, @intCast(Type.fromInterned(vector_type.child).abiSize(zcu) * vector_type.len)); 558 if (padding > 0) try w.splatByteAll(0, padding); 559 } 560 }, 561 .tuple_type => |tuple| { 562 const struct_begin = w.end; 563 for (tuple.types.get(ip), tuple.values.get(ip), 0..) |field_ty, field_val, field_index| { 564 if (field_val != .none) continue; 565 if (!Type.fromInterned(field_ty).hasRuntimeBits(zcu)) continue; 566 567 try w.splatByteAll(0, @intCast(struct_begin + 568 Type.fromInterned(field_ty).abiAlignment(zcu).forward(w.end - struct_begin) - w.end)); 569 try generateSymbol(bin_file, pt, .fromInterned(switch (aggregate.storage) { 570 .bytes => |bytes| try pt.intern(.{ .int = .{ 571 .ty = field_ty, 572 .storage = .{ .u64 = bytes.at(field_index, ip) }, 573 } }), 574 .elems => |elems| elems[field_index], 575 .repeated_elem => |elem| elem, 576 }), w, reloc_parent); 577 } 578 try w.splatByteAll(0, @intCast(struct_begin + ty.abiSize(zcu) - w.end)); 579 }, 580 .struct_type => { 581 const struct_type = ip.loadStructType(ty.toIntern()); 582 switch (struct_type.layout) { 583 .@"packed" => unreachable, 584 .auto, .@"extern" => { 585 const struct_begin = w.end; 586 const field_types = struct_type.field_types.get(ip); 587 const offsets = struct_type.field_offsets.get(ip); 588 589 var it = struct_type.iterateRuntimeOrder(ip); 590 while (it.next()) |field_index| { 591 const field_ty = field_types[field_index]; 592 if (!Type.fromInterned(field_ty).hasRuntimeBits(zcu)) continue; 593 594 const field_val = switch (ip.indexToKey(val.toIntern()).aggregate.storage) { 595 .bytes => |bytes| try pt.intern(.{ .int = .{ 596 .ty = field_ty, 597 .storage = .{ .u64 = bytes.at(field_index, ip) }, 598 } }), 599 .elems => |elems| elems[field_index], 600 .repeated_elem => |elem| elem, 601 }; 602 603 const padding: usize = @intCast(offsets[field_index] - (w.end - struct_begin)); 604 if (padding > 0) try w.splatByteAll(0, padding); 605 606 try generateSymbol(bin_file, pt, Value.fromInterned(field_val), w, reloc_parent); 607 } 608 609 assert(struct_type.alignment.check(struct_type.size)); 610 611 const padding: usize = @intCast(struct_type.size - (w.end - struct_begin)); 612 if (padding > 0) try w.splatByteAll(0, padding); 613 }, 614 } 615 }, 616 else => unreachable, 617 }, 618 .un => |un| { 619 const layout = ty.unionGetLayout(zcu); 620 621 if (layout.payload_size == 0) { 622 return generateSymbol(bin_file, pt, Value.fromInterned(un.tag), w, reloc_parent); 623 } 624 625 // Check if we should store the tag first. 626 if (layout.tag_size > 0 and layout.tag_align.compare(.gte, layout.payload_align)) { 627 try generateSymbol(bin_file, pt, Value.fromInterned(un.tag), w, reloc_parent); 628 } 629 630 const union_obj = zcu.typeToUnion(ty).?; 631 if (un.tag != .none) { 632 const field_index = ty.unionTagFieldIndex(Value.fromInterned(un.tag), zcu).?; 633 const field_ty = Type.fromInterned(union_obj.field_types.get(ip)[field_index]); 634 if (!field_ty.hasRuntimeBits(zcu)) { 635 try w.splatByteAll(0xaa, @intCast(layout.payload_size)); 636 } else { 637 try generateSymbol(bin_file, pt, Value.fromInterned(un.val), w, reloc_parent); 638 639 const padding: usize = @intCast(layout.payload_size - field_ty.abiSize(zcu)); 640 if (padding > 0) { 641 try w.splatByteAll(0, padding); 642 } 643 } 644 } else { 645 try generateSymbol(bin_file, pt, Value.fromInterned(un.val), w, reloc_parent); 646 } 647 648 if (layout.tag_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) { 649 try generateSymbol(bin_file, pt, Value.fromInterned(un.tag), w, reloc_parent); 650 651 if (layout.padding > 0) { 652 try w.splatByteAll(0, layout.padding); 653 } 654 } 655 }, 656 .bitpack => |bitpack| try generateSymbol(bin_file, pt, .fromInterned(bitpack.backing_int_val), w, reloc_parent), 657 .memoized_call => unreachable, 658 } 659 } 660 661 fn lowerPtr( 662 bin_file: *link.File, 663 pt: Zcu.PerThread, 664 ptr_val: InternPool.Index, 665 w: *std.Io.Writer, 666 reloc_parent: link.File.RelocInfo.Parent, 667 prev_offset: u64, 668 ) link.EmitError!void { 669 const zcu = pt.zcu; 670 const ptr = zcu.intern_pool.indexToKey(ptr_val).ptr; 671 const offset: u64 = prev_offset + ptr.byte_offset; 672 return switch (ptr.base_addr) { 673 .nav => |nav| try lowerNavRef(bin_file, pt, nav, w, reloc_parent, offset), 674 .uav => |uav| try lowerUavRef(bin_file, pt, uav, w, reloc_parent, offset), 675 .int => try generateSymbol(bin_file, pt, try pt.intValue(Type.usize, offset), w, reloc_parent), 676 .eu_payload => |eu_ptr| try lowerPtr( 677 bin_file, 678 pt, 679 eu_ptr, 680 w, 681 reloc_parent, 682 offset + errUnionPayloadOffset( 683 Value.fromInterned(eu_ptr).typeOf(zcu).childType(zcu).errorUnionPayload(zcu), 684 zcu, 685 ), 686 ), 687 .opt_payload => |opt_ptr| try lowerPtr(bin_file, pt, opt_ptr, w, reloc_parent, offset), 688 .field => |field| { 689 const base_ptr = Value.fromInterned(field.base); 690 const base_ty = base_ptr.typeOf(zcu).childType(zcu); 691 const field_off: u64 = switch (base_ty.zigTypeTag(zcu)) { 692 .pointer => off: { 693 assert(base_ty.isSlice(zcu)); 694 break :off switch (field.index) { 695 Value.slice_ptr_index => 0, 696 Value.slice_len_index => @divExact(zcu.getTarget().ptrBitWidth(), 8), 697 else => unreachable, 698 }; 699 }, 700 .@"struct", .@"union" => switch (base_ty.containerLayout(zcu)) { 701 .auto => base_ty.structFieldOffset(@intCast(field.index), zcu), 702 .@"extern", .@"packed" => unreachable, 703 }, 704 else => unreachable, 705 }; 706 return lowerPtr(bin_file, pt, field.base, w, reloc_parent, offset + field_off); 707 }, 708 .arr_elem => |arr_elem| { 709 const base_ptr_ty = Value.fromInterned(arr_elem.base).typeOf(zcu); 710 assert(base_ptr_ty.ptrSize(zcu) == .many); 711 const elem_size = base_ptr_ty.childType(zcu).abiSize(zcu); 712 return lowerPtr(bin_file, pt, arr_elem.base, w, reloc_parent, offset + elem_size * arr_elem.index); 713 }, 714 .comptime_alloc => unreachable, 715 .comptime_field => unreachable, 716 }; 717 } 718 719 fn lowerUavRef( 720 lf: *link.File, 721 pt: Zcu.PerThread, 722 uav: InternPool.Key.Ptr.BaseAddr.Uav, 723 w: *std.Io.Writer, 724 reloc_parent: link.File.RelocInfo.Parent, 725 offset: u64, 726 ) link.EmitError!void { 727 const zcu = pt.zcu; 728 const ip = &zcu.intern_pool; 729 const comp = lf.comp; 730 const target = &comp.root_mod.resolved_target.result; 731 const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); 732 const uav_val = uav.val; 733 const uav_ty = Type.fromInterned(ip.typeOf(uav_val)); 734 const is_fn_body = uav_ty.zigTypeTag(zcu) == .@"fn"; 735 736 log.debug("lowerUavRef: ty = {f}", .{uav_ty.fmt(pt)}); 737 738 if (!is_fn_body and !uav_ty.hasRuntimeBits(zcu)) { 739 try w.splatByteAll(0xaa, ptr_width_bytes); 740 return; 741 } 742 743 switch (lf.tag) { 744 .c => unreachable, 745 .spirv => unreachable, 746 .wasm => { 747 const wasm = lf.cast(.wasm).?; 748 assert(reloc_parent == .none); 749 try wasm.addUavReloc(w.end, uav.val, uav.orig_ty, @intCast(offset)); 750 try w.splatByteAll(0, ptr_width_bytes); 751 return; 752 }, 753 else => {}, 754 } 755 756 const uav_align = Type.fromInterned(uav.orig_ty).ptrAlignment(zcu); 757 _ = try lf.lowerUav(pt, uav_val, uav_align); 758 759 const vaddr = lf.getUavVAddr(uav_val, .{ 760 .parent = reloc_parent, 761 .offset = w.end, 762 .addend = @intCast(offset), 763 }) catch |err| switch (err) { 764 error.OutOfMemory => |e| return e, 765 else => |e| std.debug.panic("TODO rework lowerUav. internal error: {t}", .{e}), 766 }; 767 const endian = target.cpu.arch.endian(); 768 switch (ptr_width_bytes) { 769 2 => try w.writeInt(u16, @intCast(vaddr), endian), 770 4 => try w.writeInt(u32, @intCast(vaddr), endian), 771 8 => try w.writeInt(u64, vaddr, endian), 772 else => unreachable, 773 } 774 } 775 776 fn lowerNavRef( 777 lf: *link.File, 778 pt: Zcu.PerThread, 779 nav_index: InternPool.Nav.Index, 780 w: *std.Io.Writer, 781 reloc_parent: link.File.RelocInfo.Parent, 782 offset: u64, 783 ) link.EmitError!void { 784 const zcu = pt.zcu; 785 const ip = &zcu.intern_pool; 786 const target = &zcu.navFileScope(nav_index).mod.?.resolved_target.result; 787 const ptr_width_bytes = @divExact(target.ptrBitWidth(), 8); 788 const nav_ty = Type.fromInterned(ip.getNav(nav_index).resolved.?.type); 789 790 if (!nav_ty.isRuntimeFnOrHasRuntimeBits(zcu) and ip.getNav(nav_index).getExtern(ip) == null) { 791 try w.splatByteAll(0xaa, ptr_width_bytes); 792 return; 793 } 794 795 switch (lf.tag) { 796 .c => unreachable, 797 .spirv => unreachable, 798 .wasm => { 799 const wasm = lf.cast(.wasm).?; 800 assert(reloc_parent == .none); 801 try wasm.addNavReloc(w.end, nav_index, nav_ty, @intCast(offset)); 802 try w.splatByteAll(0, ptr_width_bytes); 803 return; 804 }, 805 else => {}, 806 } 807 808 const vaddr = lf.getNavVAddr(pt, nav_index, .{ 809 .parent = reloc_parent, 810 .offset = w.end, 811 .addend = @intCast(offset), 812 }) catch @panic("TODO rework getNavVAddr"); 813 const endian = target.cpu.arch.endian(); 814 switch (ptr_width_bytes) { 815 2 => try w.writeInt(u16, @intCast(vaddr), endian), 816 4 => try w.writeInt(u32, @intCast(vaddr), endian), 817 8 => try w.writeInt(u64, vaddr, endian), 818 else => unreachable, 819 } 820 } 821 822 pub fn genNavRef( 823 lf: *link.File, 824 pt: Zcu.PerThread, 825 nav_index: InternPool.Nav.Index, 826 ) Error!link.File.SymbolId { 827 const zcu = pt.zcu; 828 const ip = &zcu.intern_pool; 829 const nav = ip.getNav(nav_index); 830 log.debug("genNavRef({f})", .{nav.fqn.fmt(ip)}); 831 832 const is_threadlocal = nav.resolved.?.@"threadlocal" and zcu.comp.config.any_non_single_threaded; 833 const lib_name, const linkage = if (nav.getExtern(ip)) |e| 834 .{ e.lib_name, e.linkage } 835 else 836 .{ .none, .internal }; 837 if (lf.cast(.elf)) |elf_file| { 838 const zo = elf_file.zigObjectPtr().?; 839 switch (linkage) { 840 .internal => { 841 const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index); 842 if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true; 843 return @fromBackingInt(@intCast(sym_index)); 844 }, 845 .strong, .weak => { 846 const sym_index = try elf_file.getGlobalSymbol(nav.name.toSlice(ip), lib_name.toSlice(ip)); 847 switch (linkage) { 848 .internal => unreachable, 849 .strong => {}, 850 .weak => zo.symbol(sym_index).flags.weak = true, 851 .link_once => unreachable, 852 } 853 if (is_threadlocal) zo.symbol(sym_index).flags.is_tls = true; 854 return @fromBackingInt(@intCast(sym_index)); 855 }, 856 .link_once => unreachable, 857 } 858 } else if (lf.cast(.elf2)) |elf| { 859 return elf.navSymbol(nav_index); 860 } else if (lf.cast(.macho)) |macho_file| { 861 const zo = macho_file.getZigObject().?; 862 switch (linkage) { 863 .internal => { 864 const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index); 865 if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true; 866 return @fromBackingInt(@intCast(sym_index)); 867 }, 868 .strong, .weak => { 869 const sym_index = try macho_file.getGlobalSymbol(nav.name.toSlice(ip), lib_name.toSlice(ip)); 870 switch (linkage) { 871 .internal => unreachable, 872 .strong => {}, 873 .weak => zo.symbols.items[sym_index].flags.weak = true, 874 .link_once => unreachable, 875 } 876 if (is_threadlocal) zo.symbols.items[sym_index].flags.tlv = true; 877 return @fromBackingInt(@intCast(sym_index)); 878 }, 879 .link_once => unreachable, 880 } 881 } else if (lf.cast(.coff2)) |coff| { 882 return @fromBackingInt(@intCast(@backingInt(try coff.navSymbol(zcu, nav_index)))); 883 } else { 884 std.debug.panic("TODO genNavRef for '{t}'", .{lf.tag}); 885 } 886 } 887 888 /// deprecated legacy type 889 pub const MCValue = union(enum) { 890 none, 891 undef, 892 /// The bit-width of the immediate may be smaller than `u64`. For example, on 32-bit targets 893 /// such as ARM, the immediate will never exceed 32-bits. 894 immediate: u64, 895 /// Decl with address deferred until the linker allocates everything in virtual memory. 896 /// Payload is a symbol index. 897 load_direct: link.File.SymbolId, 898 /// Decl with address deferred until the linker allocates everything in virtual memory. 899 /// Payload is a symbol index. 900 lea_direct: link.File.SymbolId, 901 /// Decl referenced via GOT with address deferred until the linker allocates 902 /// everything in virtual memory. 903 /// Payload is a symbol index. 904 load_got: link.File.SymbolId, 905 /// Direct by-address reference to memory location. 906 memory: u64, 907 /// Reference to memory location but deferred until linker allocated the Decl in memory. 908 /// Traditionally, this corresponds to emitting a relocation in a relocatable object file. 909 load_symbol: link.File.SymbolId, 910 /// Reference to memory location but deferred until linker allocated the Decl in memory. 911 /// Traditionally, this corresponds to emitting a relocation in a relocatable object file. 912 lea_symbol: link.File.SymbolId, 913 }; 914 915 /// deprecated legacy code path 916 pub fn genTypedValue( 917 lf: *link.File, 918 pt: Zcu.PerThread, 919 val: Value, 920 target: *const std.Target, 921 ) Error!MCValue { 922 const res = try lowerValue(pt, val, target); 923 return switch (res) { 924 .none => .none, 925 .undef => .undef, 926 .immediate => |imm| .{ .immediate = imm }, 927 .lea_nav => |nav| .{ .lea_symbol = try genNavRef(lf, pt, nav) }, 928 .load_uav => |uav| .{ .load_symbol = try lf.lowerUav( 929 pt, 930 uav.val, 931 Type.fromInterned(uav.orig_ty).ptrAlignment(pt.zcu), 932 ) }, 933 .lea_uav => |uav| .{ .lea_symbol = try lf.lowerUav( 934 pt, 935 uav.val, 936 Type.fromInterned(uav.orig_ty).ptrAlignment(pt.zcu), 937 ) }, 938 }; 939 } 940 941 const LowerResult = union(enum) { 942 none, 943 undef, 944 /// The bit-width of the immediate may be smaller than `u64`. For example, on 32-bit targets 945 /// such as ARM, the immediate will never exceed 32-bits. 946 immediate: u64, 947 lea_nav: InternPool.Nav.Index, 948 load_uav: InternPool.Key.Ptr.BaseAddr.Uav, 949 lea_uav: InternPool.Key.Ptr.BaseAddr.Uav, 950 }; 951 952 pub fn lowerValue(pt: Zcu.PerThread, val: Value, target: *const std.Target) Allocator.Error!LowerResult { 953 const zcu = pt.zcu; 954 const ip = &zcu.intern_pool; 955 const ty = val.typeOf(zcu); 956 957 log.debug("lowerValue(@as({f}, {f}))", .{ ty.fmt(pt), val.fmtValue(pt) }); 958 959 if (val.isUndef(zcu)) return .undef; 960 961 switch (ty.zigTypeTag(zcu)) { 962 .void => return .none, 963 .bool => return .{ .immediate = @intFromBool(val.toBool()) }, 964 .pointer => switch (ty.ptrSize(zcu)) { 965 .slice => {}, 966 .one, .many, .c => { 967 const ptr = ip.indexToKey(val.toIntern()).ptr; 968 if (ptr.base_addr == .int) return .{ .immediate = ptr.byte_offset }; 969 if (ptr.byte_offset == 0) switch (ptr.base_addr) { 970 .int => unreachable, // handled above 971 972 .nav => |nav_index| { 973 const nav = ip.getNav(nav_index); 974 const nav_ty: Type = .fromInterned(nav.resolved.?.type); 975 if (nav_ty.isRuntimeFnOrHasRuntimeBits(zcu) or nav.getExtern(ip) != null) { 976 return .{ .lea_nav = nav_index }; 977 } else { 978 // Create the 0xaa bit pattern... 979 const undef_ptr_bits: u64 = @intCast((@as(u66, 1) << @intCast(target.ptrBitWidth() + 1)) / 3); 980 // ...but align the pointer 981 const alignment = zcu.navAlignment(nav_index); 982 return .{ .immediate = alignment.forward(undef_ptr_bits) }; 983 } 984 }, 985 986 .uav => |uav| if (Value.fromInterned(uav.val).typeOf(zcu).isRuntimeFnOrHasRuntimeBits(zcu)) { 987 return .{ .lea_uav = uav }; 988 } else { 989 // Create the 0xaa bit pattern... 990 const undef_ptr_bits: u64 = @intCast((@as(u66, 1) << @intCast(target.ptrBitWidth() + 1)) / 3); 991 // ...but align the pointer 992 const alignment = Type.fromInterned(uav.orig_ty).ptrAlignment(zcu); 993 return .{ .immediate = alignment.forward(undef_ptr_bits) }; 994 }, 995 996 else => {}, 997 }; 998 }, 999 }, 1000 .int => { 1001 const info = ty.intInfo(zcu); 1002 if (info.bits <= target.ptrBitWidth()) { 1003 const unsigned: u64 = switch (info.signedness) { 1004 .signed => @bitCast(val.toSignedInt(zcu)), 1005 .unsigned => val.toUnsignedInt(zcu), 1006 }; 1007 return .{ .immediate = unsigned }; 1008 } 1009 }, 1010 .optional => { 1011 if (ty.isPtrLikeOptional(zcu)) { 1012 return lowerValue( 1013 pt, 1014 val.optionalValue(zcu) orelse return .{ .immediate = 0 }, 1015 target, 1016 ); 1017 } else if (ty.abiSize(zcu) == 1) { 1018 return .{ .immediate = @intFromBool(!val.isNull(zcu)) }; 1019 } 1020 }, 1021 .@"enum" => { 1022 const enum_tag = ip.indexToKey(val.toIntern()).enum_tag; 1023 return lowerValue( 1024 pt, 1025 Value.fromInterned(enum_tag.int), 1026 target, 1027 ); 1028 }, 1029 .@"struct", .@"union" => if (ty.containerLayout(zcu) == .@"packed") { 1030 const bitpack = ip.indexToKey(val.toIntern()).bitpack; 1031 return lowerValue(pt, .fromInterned(bitpack.backing_int_val), target); 1032 }, 1033 .error_set => { 1034 const err_name = ip.indexToKey(val.toIntern()).err.name; 1035 const error_index = ip.getErrorValueIfExists(err_name).?; 1036 return .{ .immediate = error_index }; 1037 }, 1038 .error_union => { 1039 const err_type = ty.errorUnionSet(zcu); 1040 const payload_type = ty.errorUnionPayload(zcu); 1041 if (!payload_type.hasRuntimeBits(zcu)) { 1042 // We use the error type directly as the type. 1043 const err_int_ty = try pt.errorIntType(); 1044 switch (ip.indexToKey(val.toIntern()).error_union.val) { 1045 .err_name => |err_name| return lowerValue( 1046 pt, 1047 Value.fromInterned(try pt.intern(.{ .err = .{ 1048 .ty = err_type.toIntern(), 1049 .name = err_name, 1050 } })), 1051 target, 1052 ), 1053 .payload => return lowerValue( 1054 pt, 1055 try pt.intValue(err_int_ty, 0), 1056 target, 1057 ), 1058 } 1059 } 1060 }, 1061 1062 .comptime_int => unreachable, 1063 .comptime_float => unreachable, 1064 .type => unreachable, 1065 .enum_literal => unreachable, 1066 .noreturn => unreachable, 1067 .undefined => unreachable, 1068 .null => unreachable, 1069 .@"opaque" => unreachable, 1070 1071 else => {}, 1072 } 1073 1074 return .{ .load_uav = .{ 1075 .val = val.toIntern(), 1076 .orig_ty = (try pt.singleConstPtrType(ty)).toIntern(), 1077 } }; 1078 } 1079 1080 pub fn errUnionPayloadOffset(payload_ty: Type, zcu: *Zcu) u64 { 1081 if (!payload_ty.hasRuntimeBits(zcu)) return 0; 1082 const payload_align = payload_ty.abiAlignment(zcu); 1083 const error_align = Type.anyerror.abiAlignment(zcu); 1084 if (payload_align.compare(.gte, error_align) or !payload_ty.hasRuntimeBits(zcu)) { 1085 return 0; 1086 } else { 1087 return payload_align.forward(Type.anyerror.abiSize(zcu)); 1088 } 1089 } 1090 1091 pub fn errUnionErrorOffset(payload_ty: Type, zcu: *Zcu) u64 { 1092 if (!payload_ty.hasRuntimeBits(zcu)) return 0; 1093 const payload_align = payload_ty.abiAlignment(zcu); 1094 const error_align = Type.anyerror.abiAlignment(zcu); 1095 if (payload_align.compare(.gte, error_align) and payload_ty.hasRuntimeBits(zcu)) { 1096 return error_align.forward(payload_ty.abiSize(zcu)); 1097 } else { 1098 return 0; 1099 } 1100 } 1101 1102 pub fn fieldOffset(ptr_agg_ty: Type, ptr_field_ty: Type, field_index: u32, zcu: *Zcu) u64 { 1103 const agg_ty = ptr_agg_ty.childType(zcu); 1104 return switch (agg_ty.containerLayout(zcu)) { 1105 .auto, .@"extern" => agg_ty.structFieldOffset(field_index, zcu), 1106 .@"packed" => @divExact(@as(u64, ptr_agg_ty.ptrInfo(zcu).packed_offset.bit_offset) + 1107 (if (zcu.typeToPackedStruct(agg_ty)) |loaded_struct| zcu.structPackedFieldBitOffset(loaded_struct, field_index) else 0) - 1108 ptr_field_ty.ptrInfo(zcu).packed_offset.bit_offset, 8), 1109 }; 1110 } 1111 1112 pub const FlattenedItem = struct { offset: u64, type: ?Type }; 1113 pub fn flattenType(items_buf: []FlattenedItem, ty: Type, zcu: *Zcu, opts: struct { 1114 offset: u64 = 0, 1115 allow_arrays: bool = true, 1116 fn increaseOffset(opts: @This(), offset: u64) @This() { 1117 return .{ 1118 .offset = opts.offset + offset, 1119 .allow_arrays = opts.allow_arrays, 1120 }; 1121 } 1122 }) ?[]FlattenedItem { 1123 const ip = &zcu.intern_pool; 1124 switch (ip.indexToKey(ty.toIntern())) { 1125 .int_type => |int_type| { 1126 if (int_type.bits == 0) return items_buf[0..0]; 1127 if (items_buf.len < 1) return null; 1128 const items = items_buf[0..1]; 1129 items.* = .{.{ .offset = opts.offset, .type = ty }}; 1130 return items; 1131 }, 1132 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 1133 .one, .many, .c => { 1134 if (items_buf.len < 1) return null; 1135 const items = items_buf[0..1]; 1136 items.* = .{.{ .offset = opts.offset, .type = ty }}; 1137 return items; 1138 }, 1139 .slice => { 1140 if (items_buf.len < 2) return null; 1141 const items = items_buf[0..2]; 1142 const ptr_field_ty = ty.slicePtrFieldType(zcu); 1143 items.* = .{ 1144 .{ .offset = opts.offset, .type = ptr_field_ty }, 1145 .{ .offset = opts.offset + ptr_field_ty.abiSize(zcu), .type = .usize }, 1146 }; 1147 return items; 1148 }, 1149 }, 1150 .array_type => |array_type| { 1151 const len = array_type.lenIncludingSentinel(); 1152 if (len == 0) return items_buf[0..0]; 1153 const elem_ty: Type = .fromInterned(array_type.child); 1154 const elem_items = flattenType(items_buf, elem_ty, zcu, opts) orelse return null; 1155 if (elem_items.len == 0) return items_buf[0..0]; 1156 if (!opts.allow_arrays) return null; 1157 const items_len, const items_overflow = @mulWithOverflow(elem_items.len, len); 1158 if (items_overflow != 0 or items_buf.len < items_len) return null; 1159 var items_index = elem_items.len; 1160 const elem_size = elem_ty.abiSize(zcu); 1161 var elem_offset: u64 = elem_size; 1162 while (items_index != items_len) : ({ 1163 items_index += elem_items.len; 1164 elem_offset += elem_size; 1165 }) for (items_buf[items_index..][0..elem_items.len], elem_items) |*item, elem_item| { 1166 item.* = .{ .offset = elem_offset + elem_item.offset, .type = elem_item.type }; 1167 }; 1168 return items_buf[0..@intCast(items_len)]; 1169 }, 1170 .vector_type => |vector_type| { 1171 if (vector_type.len == 0) return items_buf[0..0]; 1172 if (items_buf.len < 1) return null; 1173 const items = items_buf[0..1]; 1174 items.* = .{.{ .offset = opts.offset, .type = ty }}; 1175 return items; 1176 }, 1177 .opt_type, .error_union_type => return null, 1178 .simple_type => |simple_type| switch (simple_type) { 1179 .f16, 1180 .f32, 1181 .f64, 1182 .f80, 1183 .f128, 1184 .usize, 1185 .isize, 1186 .c_char, 1187 .c_short, 1188 .c_ushort, 1189 .c_int, 1190 .c_uint, 1191 .c_long, 1192 .c_ulong, 1193 .c_longlong, 1194 .c_ulonglong, 1195 .c_longdouble, 1196 .bool, 1197 .anyerror, 1198 => { 1199 if (items_buf.len < 1) return null; 1200 const items = items_buf[0..1]; 1201 items.* = .{.{ .offset = opts.offset, .type = ty }}; 1202 return items; 1203 }, 1204 .anyopaque, .noreturn => return null, 1205 .void, 1206 .type, 1207 .comptime_int, 1208 .comptime_float, 1209 .null, 1210 .undefined, 1211 .enum_literal, 1212 => return items_buf[0..0], 1213 .adhoc_inferred_error_set, .generic_poison => unreachable, 1214 }, 1215 .struct_type => { 1216 const loaded_struct = ip.loadStructType(ty.toIntern()); 1217 switch (loaded_struct.layout) { 1218 .auto, .@"extern" => {}, 1219 .@"packed" => return flattenType(items_buf, .fromInterned( 1220 loaded_struct.packed_backing_int_type, 1221 ), zcu, opts), 1222 } 1223 var items_len: usize = 0; 1224 var offset: u64 = 0; 1225 var field_it = loaded_struct.iterateRuntimeOrder(ip); 1226 while (field_it.next()) |field_index| { 1227 const field_ty: Type = .fromInterned(loaded_struct.field_types.get(ip)[field_index]); 1228 const field_offset = loaded_struct.field_offsets.get(ip)[field_index]; 1229 if (field_offset - offset > 0 and 1230 (items_len == 0 or items_buf[items_len - 1].type != null)) 1231 { 1232 if (items_len == items_buf.len) return null; 1233 items_buf[items_len] = .{ .offset = offset, .type = null }; 1234 items_len += 1; 1235 } 1236 items_len += (flattenType(items_buf[items_len..], field_ty, zcu, opts.increaseOffset( 1237 field_offset, 1238 )) orelse return null).len; 1239 offset = field_offset + field_ty.abiSize(zcu); 1240 } 1241 if (ty.abiSize(zcu) - offset > 0 and 1242 (items_len == 0 or items_buf[items_len - 1].type != null)) 1243 { 1244 if (items_len == items_buf.len) return null; 1245 items_buf[items_len] = .{ .offset = offset, .type = null }; 1246 items_len += 1; 1247 } 1248 return items_buf[0..items_len]; 1249 }, 1250 .tuple_type => |tuple_type| { 1251 if (items_buf.len < tuple_type.types.len) return null; 1252 var items_len: usize = 0; 1253 var offset: u64 = 0; 1254 for (tuple_type.types.get(ip)) |field_ty_ip| { 1255 const field_ty: Type = .fromInterned(field_ty_ip); 1256 offset = field_ty.abiAlignment(zcu).forward(offset); 1257 items_len += (flattenType(items_buf[items_len..], field_ty, zcu, opts.increaseOffset( 1258 offset, 1259 )) orelse return null).len; 1260 offset += field_ty.abiSize(zcu); 1261 } 1262 return items_buf[0..items_len]; 1263 }, 1264 .union_type => { 1265 const loaded_union = ip.loadUnionType(ty.toIntern()); 1266 return switch (loaded_union.layout) { 1267 .auto, .@"extern" => return null, 1268 .@"packed" => return flattenType(items_buf, .fromInterned( 1269 loaded_union.packed_backing_int_type, 1270 ), zcu, opts), 1271 }; 1272 }, 1273 .opaque_type, .spirv_type, .func_type => return null, 1274 .enum_type => return flattenType(items_buf, .fromInterned( 1275 ip.loadEnumType(ty.toIntern()).int_tag_type, 1276 ), zcu, opts), 1277 .error_set_type, .inferred_error_set_type => { 1278 if (items_buf.len < 1) return null; 1279 const items = items_buf[0..1]; 1280 items.* = .{.{ .offset = opts.offset, .type = ty }}; 1281 return items; 1282 }, 1283 .anyframe_type, 1284 // values, not types 1285 .undef, 1286 .simple_value, 1287 .@"extern", 1288 .func, 1289 .int, 1290 .err, 1291 .error_union, 1292 .enum_literal, 1293 .enum_tag, 1294 .float, 1295 .ptr, 1296 .slice, 1297 .opt, 1298 .aggregate, 1299 .un, 1300 .bitpack, 1301 // memoization, not types 1302 .memoized_call, 1303 => unreachable, 1304 } 1305 } 1306 1307 test { 1308 _ = aarch64; 1309 _ = loongarch; 1310 } 1311