| 1 | const std = @import("std"); |
| 2 | const build_options = @import("build_options"); |
| 3 | const builtin = @import("builtin"); |
| 4 | const assert = std.debug.assert; |
| 5 | const link = @import("link.zig"); |
| 6 | const log = std.log.scoped(.codegen); |
| 7 | const mem = std.mem; |
| 8 | const math = std.math; |
| 9 | const target_util = @import("target.zig"); |
| 10 | const trace = @import("tracy.zig").trace; |
| 11 | |
| 12 | const Air = @import("Air.zig"); |
| 13 | const Allocator = mem.Allocator; |
| 14 | const Compilation = @import("Compilation.zig"); |
| 15 | const ErrorMsg = Zcu.ErrorMsg; |
| 16 | const InternPool = @import("InternPool.zig"); |
| 17 | const Zcu = @import("Zcu.zig"); |
| 18 | |
| 19 | const Type = @import("Type.zig"); |
| 20 | const Value = @import("Value.zig"); |
| 21 | const Zir = std.zig.Zir; |
| 22 | const Alignment = InternPool.Alignment; |
| 23 | const dev = @import("dev.zig"); |
| 24 | |
| 25 | pub const aarch64 = @import("codegen/aarch64.zig"); |
| 26 | pub const loongarch = @import("codegen/loongarch.zig"); |
| 27 | |
| 28 | pub const Error = link.Error; |
| 29 | |
| 30 | fn devFeatureForBackend(backend: std.lang.CompilerBackend) dev.Feature { |
| 31 | return switch (backend) { |
| 32 | .other, .stage1 => unreachable, |
| 33 | .stage2_aarch64 => .aarch64_backend, |
| 34 | .stage2_arm => .arm_backend, |
| 35 | .stage2_c => .c_backend, |
| 36 | .stage2_llvm => .llvm_backend, |
| 37 | .stage2_loongarch => .loongarch_backend, |
| 38 | .stage2_powerpc => unreachable, |
| 39 | .stage2_riscv64 => .riscv64_backend, |
| 40 | .stage2_sparc64 => .sparc64_backend, |
| 41 | .zsf_spork8 => .spork8_backend, |
| 42 | .stage2_spirv => .spirv_backend, |
| 43 | .stage2_wasm => .wasm_backend, |
| 44 | .stage2_x86 => .x86_backend, |
| 45 | .stage2_x86_64 => .x86_64_backend, |
| 46 | _ => unreachable, |
| 47 | }; |
| 48 | } |
| 49 | |
| 50 | fn importBackend(comptime backend: std.lang.CompilerBackend) type { |
| 51 | return switch (backend) { |
| 52 | .other, .stage1 => unreachable, |
| 53 | .stage2_aarch64 => aarch64, |
| 54 | .stage2_arm => unreachable, |
| 55 | .stage2_c => @import("codegen/c.zig"), |
| 56 | .stage2_llvm => @import("codegen/llvm.zig"), |
| 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 | } |