x86_64: add support for pie executables

This commit is contained in:
Jacob Young 2025-06-04 21:45:31 -04:00 committed by Andrew Kelley
parent 178ee8aef1
commit 0bf8617d96
45 changed files with 884 additions and 640 deletions

View file

@ -519,6 +519,7 @@ set(ZIG_STAGE2_SOURCES
src/Air/Legalize.zig src/Air/Legalize.zig
src/Air/Liveness.zig src/Air/Liveness.zig
src/Air/Liveness/Verify.zig src/Air/Liveness/Verify.zig
src/Air/print.zig
src/Air/types_resolved.zig src/Air/types_resolved.zig
src/Builtin.zig src/Builtin.zig
src/Compilation.zig src/Compilation.zig
@ -675,7 +676,6 @@ set(ZIG_STAGE2_SOURCES
src/libs/mingw.zig src/libs/mingw.zig
src/libs/musl.zig src/libs/musl.zig
src/mutable_value.zig src/mutable_value.zig
src/print_air.zig
src/print_env.zig src/print_env.zig
src/print_targets.zig src/print_targets.zig
src/print_value.zig src/print_value.zig

View file

@ -61,7 +61,7 @@ pub const StackTrace = struct {
/// This data structure is used by the Zig language code generation and /// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation. /// therefore must be kept in sync with the compiler implementation.
pub const GlobalLinkage = enum { pub const GlobalLinkage = enum(u2) {
internal, internal,
strong, strong,
weak, weak,
@ -70,7 +70,7 @@ pub const GlobalLinkage = enum {
/// This data structure is used by the Zig language code generation and /// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation. /// therefore must be kept in sync with the compiler implementation.
pub const SymbolVisibility = enum { pub const SymbolVisibility = enum(u2) {
default, default,
hidden, hidden,
protected, protected,
@ -1030,8 +1030,19 @@ pub const ExternOptions = struct {
name: []const u8, name: []const u8,
library_name: ?[]const u8 = null, library_name: ?[]const u8 = null,
linkage: GlobalLinkage = .strong, linkage: GlobalLinkage = .strong,
visibility: SymbolVisibility = .default,
/// Setting this to `true` makes the `@extern` a runtime value.
is_thread_local: bool = false, is_thread_local: bool = false,
is_dll_import: bool = false, is_dll_import: bool = false,
relocation: Relocation = .any,
pub const Relocation = enum(u1) {
/// Any type of relocation is allowed.
any,
/// A program-counter-relative relocation is required.
/// Using this value makes the `@extern` a runtime value.
pcrel,
};
}; };
/// This data structure is used by the Zig language code generation and /// This data structure is used by the Zig language code generation and

View file

@ -83,13 +83,16 @@ const RDebug = extern struct {
r_ldbase: usize, r_ldbase: usize,
}; };
/// TODO make it possible to reference this same external symbol 2x so we don't need this /// TODO fix comparisons of extern symbol pointers so we don't need this helper function.
/// helper function. pub fn get_DYNAMIC() ?[*]const elf.Dyn {
pub fn get_DYNAMIC() ?[*]elf.Dyn { return @extern([*]const elf.Dyn, .{
return @extern([*]elf.Dyn, .{ .name = "_DYNAMIC", .linkage = .weak }); .name = "_DYNAMIC",
.linkage = .weak,
.visibility = .hidden,
});
} }
pub fn linkmap_iterator(phdrs: []elf.Phdr) error{InvalidExe}!LinkMap.Iterator { pub fn linkmap_iterator(phdrs: []const elf.Phdr) error{InvalidExe}!LinkMap.Iterator {
_ = phdrs; _ = phdrs;
const _DYNAMIC = get_DYNAMIC() orelse { const _DYNAMIC = get_DYNAMIC() orelse {
// No PT_DYNAMIC means this is either a statically-linked program or a // No PT_DYNAMIC means this is either a statically-linked program or a

View file

@ -39,167 +39,175 @@ const R_RELATIVE = switch (builtin.cpu.arch) {
// Obtain a pointer to the _DYNAMIC array. // Obtain a pointer to the _DYNAMIC array.
// We have to compute its address as a PC-relative quantity not to require a // We have to compute its address as a PC-relative quantity not to require a
// relocation that, at this point, is not yet applied. // relocation that, at this point, is not yet applied.
inline fn getDynamicSymbol() [*]elf.Dyn { inline fn getDynamicSymbol() [*]const elf.Dyn {
return switch (builtin.cpu.arch) { return switch (builtin.zig_backend) {
.x86 => asm volatile ( else => switch (builtin.cpu.arch) {
\\ .weak _DYNAMIC .x86 => asm volatile (
\\ .hidden _DYNAMIC \\ .weak _DYNAMIC
\\ call 1f \\ .hidden _DYNAMIC
\\ 1: pop %[ret] \\ call 1f
\\ lea _DYNAMIC-1b(%[ret]), %[ret] \\ 1: pop %[ret]
: [ret] "=r" (-> [*]elf.Dyn), \\ lea _DYNAMIC-1b(%[ret]), %[ret]
), : [ret] "=r" (-> [*]const elf.Dyn),
.x86_64 => asm volatile ( ),
\\ .weak _DYNAMIC .x86_64 => asm volatile (
\\ .hidden _DYNAMIC \\ .weak _DYNAMIC
\\ lea _DYNAMIC(%%rip), %[ret] \\ .hidden _DYNAMIC
: [ret] "=r" (-> [*]elf.Dyn), \\ lea _DYNAMIC(%%rip), %[ret]
), : [ret] "=r" (-> [*]const elf.Dyn),
.arc => asm volatile ( ),
\\ .weak _DYNAMIC .arc => asm volatile (
\\ .hidden _DYNAMIC \\ .weak _DYNAMIC
\\ add %[ret], pcl, _DYNAMIC@pcl \\ .hidden _DYNAMIC
: [ret] "=r" (-> [*]elf.Dyn), \\ add %[ret], pcl, _DYNAMIC@pcl
), : [ret] "=r" (-> [*]const elf.Dyn),
// Work around the limited offset range of `ldr` ),
.arm, .armeb, .thumb, .thumbeb => asm volatile ( // Work around the limited offset range of `ldr`
\\ .weak _DYNAMIC .arm, .armeb, .thumb, .thumbeb => asm volatile (
\\ .hidden _DYNAMIC \\ .weak _DYNAMIC
\\ ldr %[ret], 1f \\ .hidden _DYNAMIC
\\ add %[ret], pc \\ ldr %[ret], 1f
\\ b 2f \\ add %[ret], pc
\\ 1: .word _DYNAMIC-1b \\ b 2f
\\ 2: \\ 1: .word _DYNAMIC-1b
: [ret] "=r" (-> [*]elf.Dyn), \\ 2:
), : [ret] "=r" (-> [*]const elf.Dyn),
// A simple `adr` is not enough as it has a limited offset range ),
.aarch64, .aarch64_be => asm volatile ( // A simple `adr` is not enough as it has a limited offset range
\\ .weak _DYNAMIC .aarch64, .aarch64_be => asm volatile (
\\ .hidden _DYNAMIC \\ .weak _DYNAMIC
\\ adrp %[ret], _DYNAMIC \\ .hidden _DYNAMIC
\\ add %[ret], %[ret], #:lo12:_DYNAMIC \\ adrp %[ret], _DYNAMIC
: [ret] "=r" (-> [*]elf.Dyn), \\ add %[ret], %[ret], #:lo12:_DYNAMIC
), : [ret] "=r" (-> [*]const elf.Dyn),
// The CSKY ABI requires the gb register to point to the GOT. Additionally, the first ),
// entry in the GOT is defined to hold the address of _DYNAMIC. // The CSKY ABI requires the gb register to point to the GOT. Additionally, the first
.csky => asm volatile ( // entry in the GOT is defined to hold the address of _DYNAMIC.
\\ mov %[ret], gb .csky => asm volatile (
\\ ldw %[ret], %[ret] \\ mov %[ret], gb
: [ret] "=r" (-> [*]elf.Dyn), \\ ldw %[ret], %[ret]
), : [ret] "=r" (-> [*]const elf.Dyn),
.hexagon => asm volatile ( ),
\\ .weak _DYNAMIC .hexagon => asm volatile (
\\ .hidden _DYNAMIC \\ .weak _DYNAMIC
\\ jump 1f \\ .hidden _DYNAMIC
\\ .word _DYNAMIC - . \\ jump 1f
\\ 1: \\ .word _DYNAMIC - .
\\ r1 = pc \\ 1:
\\ r1 = add(r1, #-4) \\ r1 = pc
\\ %[ret] = memw(r1) \\ r1 = add(r1, #-4)
\\ %[ret] = add(r1, %[ret]) \\ %[ret] = memw(r1)
: [ret] "=r" (-> [*]elf.Dyn), \\ %[ret] = add(r1, %[ret])
: : [ret] "=r" (-> [*]const elf.Dyn),
: "r1" :
), : "r1"
.loongarch32, .loongarch64 => asm volatile ( ),
\\ .weak _DYNAMIC .loongarch32, .loongarch64 => asm volatile (
\\ .hidden _DYNAMIC \\ .weak _DYNAMIC
\\ la.local %[ret], _DYNAMIC \\ .hidden _DYNAMIC
: [ret] "=r" (-> [*]elf.Dyn), \\ la.local %[ret], _DYNAMIC
), : [ret] "=r" (-> [*]const elf.Dyn),
// Note that the - 8 is needed because pc in the second lea instruction points into the ),
// middle of that instruction. (The first lea is 6 bytes, the second is 4 bytes.) // Note that the - 8 is needed because pc in the second lea instruction points into the
.m68k => asm volatile ( // middle of that instruction. (The first lea is 6 bytes, the second is 4 bytes.)
\\ .weak _DYNAMIC .m68k => asm volatile (
\\ .hidden _DYNAMIC \\ .weak _DYNAMIC
\\ lea _DYNAMIC - . - 8, %[ret] \\ .hidden _DYNAMIC
\\ lea (%[ret], %%pc), %[ret] \\ lea _DYNAMIC - . - 8, %[ret]
: [ret] "=r" (-> [*]elf.Dyn), \\ lea (%[ret], %%pc), %[ret]
), : [ret] "=r" (-> [*]const elf.Dyn),
.mips, .mipsel => asm volatile ( ),
\\ .weak _DYNAMIC .mips, .mipsel => asm volatile (
\\ .hidden _DYNAMIC \\ .weak _DYNAMIC
\\ bal 1f \\ .hidden _DYNAMIC
\\ .gpword _DYNAMIC \\ bal 1f
\\ 1: \\ .gpword _DYNAMIC
\\ lw %[ret], 0($ra) \\ 1:
\\ addu %[ret], %[ret], $gp \\ lw %[ret], 0($ra)
: [ret] "=r" (-> [*]elf.Dyn), \\ addu %[ret], %[ret], $gp
: : [ret] "=r" (-> [*]const elf.Dyn),
: "lr" :
), : "lr"
.mips64, .mips64el => asm volatile ( ),
\\ .weak _DYNAMIC .mips64, .mips64el => asm volatile (
\\ .hidden _DYNAMIC \\ .weak _DYNAMIC
\\ .balign 8 \\ .hidden _DYNAMIC
\\ bal 1f \\ .balign 8
\\ .gpdword _DYNAMIC \\ bal 1f
\\ 1: \\ .gpdword _DYNAMIC
\\ ld %[ret], 0($ra) \\ 1:
\\ daddu %[ret], %[ret], $gp \\ ld %[ret], 0($ra)
: [ret] "=r" (-> [*]elf.Dyn), \\ daddu %[ret], %[ret], $gp
: : [ret] "=r" (-> [*]const elf.Dyn),
: "lr" :
), : "lr"
.powerpc, .powerpcle => asm volatile ( ),
\\ .weak _DYNAMIC .powerpc, .powerpcle => asm volatile (
\\ .hidden _DYNAMIC \\ .weak _DYNAMIC
\\ bl 1f \\ .hidden _DYNAMIC
\\ .long _DYNAMIC - . \\ bl 1f
\\ 1: \\ .long _DYNAMIC - .
\\ mflr %[ret] \\ 1:
\\ lwz 4, 0(%[ret]) \\ mflr %[ret]
\\ add %[ret], 4, %[ret] \\ lwz 4, 0(%[ret])
: [ret] "=r" (-> [*]elf.Dyn), \\ add %[ret], 4, %[ret]
: : [ret] "=r" (-> [*]const elf.Dyn),
: "lr", "r4" :
), : "lr", "r4"
.powerpc64, .powerpc64le => asm volatile ( ),
\\ .weak _DYNAMIC .powerpc64, .powerpc64le => asm volatile (
\\ .hidden _DYNAMIC \\ .weak _DYNAMIC
\\ bl 1f \\ .hidden _DYNAMIC
\\ .quad _DYNAMIC - . \\ bl 1f
\\ 1: \\ .quad _DYNAMIC - .
\\ mflr %[ret] \\ 1:
\\ ld 4, 0(%[ret]) \\ mflr %[ret]
\\ add %[ret], 4, %[ret] \\ ld 4, 0(%[ret])
: [ret] "=r" (-> [*]elf.Dyn), \\ add %[ret], 4, %[ret]
: : [ret] "=r" (-> [*]const elf.Dyn),
: "lr", "r4" :
), : "lr", "r4"
.riscv32, .riscv64 => asm volatile ( ),
\\ .weak _DYNAMIC .riscv32, .riscv64 => asm volatile (
\\ .hidden _DYNAMIC \\ .weak _DYNAMIC
\\ lla %[ret], _DYNAMIC \\ .hidden _DYNAMIC
: [ret] "=r" (-> [*]elf.Dyn), \\ lla %[ret], _DYNAMIC
), : [ret] "=r" (-> [*]const elf.Dyn),
.s390x => asm volatile ( ),
\\ .weak _DYNAMIC .s390x => asm volatile (
\\ .hidden _DYNAMIC \\ .weak _DYNAMIC
\\ larl %[ret], 1f \\ .hidden _DYNAMIC
\\ ag %[ret], 0(%[ret]) \\ larl %[ret], 1f
\\ jg 2f \\ ag %[ret], 0(%[ret])
\\ 1: .quad _DYNAMIC - . \\ jg 2f
\\ 2: \\ 1: .quad _DYNAMIC - .
: [ret] "=r" (-> [*]elf.Dyn), \\ 2:
), : [ret] "=r" (-> [*]const elf.Dyn),
// The compiler does not necessarily have any obligation to load the `l7` register (pointing ),
// to the GOT), so do it ourselves just in case. // The compiler does not necessarily have any obligation to load the `l7` register (pointing
.sparc, .sparc64 => asm volatile ( // to the GOT), so do it ourselves just in case.
\\ sethi %%hi(_GLOBAL_OFFSET_TABLE_ - 4), %%l7 .sparc, .sparc64 => asm volatile (
\\ call 1f \\ sethi %%hi(_GLOBAL_OFFSET_TABLE_ - 4), %%l7
\\ add %%l7, %%lo(_GLOBAL_OFFSET_TABLE_ + 4), %%l7 \\ call 1f
\\ 1: \\ add %%l7, %%lo(_GLOBAL_OFFSET_TABLE_ + 4), %%l7
\\ add %%l7, %%o7, %[ret] \\ 1:
: [ret] "=r" (-> [*]elf.Dyn), \\ add %%l7, %%o7, %[ret]
), : [ret] "=r" (-> [*]const elf.Dyn),
else => { ),
@compileError("PIE startup is not yet supported for this target!"); else => {
@compileError("PIE startup is not yet supported for this target!");
},
}, },
.stage2_x86_64 => @extern([*]const elf.Dyn, .{
.name = "_DYNAMIC",
.linkage = .weak,
.visibility = .hidden,
.relocation = .pcrel,
}).?,
}; };
} }
pub fn relocate(phdrs: []elf.Phdr) void { pub fn relocate(phdrs: []const elf.Phdr) void {
@setRuntimeSafety(false); @setRuntimeSafety(false);
@disableInstrumentation(); @disableInstrumentation();
@ -256,10 +264,9 @@ pub fn relocate(phdrs: []elf.Phdr) void {
const rel = sorted_dynv[elf.DT_REL]; const rel = sorted_dynv[elf.DT_REL];
if (rel != 0) { if (rel != 0) {
const rels = @call(.always_inline, std.mem.bytesAsSlice, .{ const rels: []const elf.Rel = @alignCast(@ptrCast(
elf.Rel, @as([*]align(@alignOf(elf.Rel)) const u8, @ptrFromInt(base_addr + rel))[0..sorted_dynv[elf.DT_RELSZ]],
@as([*]u8, @ptrFromInt(base_addr + rel))[0..sorted_dynv[elf.DT_RELSZ]], ));
});
for (rels) |r| { for (rels) |r| {
if (r.r_type() != R_RELATIVE) continue; if (r.r_type() != R_RELATIVE) continue;
@as(*usize, @ptrFromInt(base_addr + r.r_offset)).* += base_addr; @as(*usize, @ptrFromInt(base_addr + r.r_offset)).* += base_addr;
@ -268,10 +275,9 @@ pub fn relocate(phdrs: []elf.Phdr) void {
const rela = sorted_dynv[elf.DT_RELA]; const rela = sorted_dynv[elf.DT_RELA];
if (rela != 0) { if (rela != 0) {
const relas = @call(.always_inline, std.mem.bytesAsSlice, .{ const relas: []const elf.Rela = @alignCast(@ptrCast(
elf.Rela, @as([*]align(@alignOf(elf.Rela)) const u8, @ptrFromInt(base_addr + rela))[0..sorted_dynv[elf.DT_RELASZ]],
@as([*]u8, @ptrFromInt(base_addr + rela))[0..sorted_dynv[elf.DT_RELASZ]], ));
});
for (relas) |r| { for (relas) |r| {
if (r.r_type() != R_RELATIVE) continue; if (r.r_type() != R_RELATIVE) continue;
@as(*usize, @ptrFromInt(base_addr + r.r_offset)).* = base_addr + @as(usize, @bitCast(r.r_addend)); @as(*usize, @ptrFromInt(base_addr + r.r_offset)).* = base_addr + @as(usize, @bitCast(r.r_addend));
@ -280,10 +286,9 @@ pub fn relocate(phdrs: []elf.Phdr) void {
const relr = sorted_dynv[elf.DT_RELR]; const relr = sorted_dynv[elf.DT_RELR];
if (relr != 0) { if (relr != 0) {
const relrs = @call(.always_inline, std.mem.bytesAsSlice, .{ const relrs: []const elf.Relr = @ptrCast(
elf.Relr, @as([*]align(@alignOf(elf.Relr)) const u8, @ptrFromInt(base_addr + relr))[0..sorted_dynv[elf.DT_RELRSZ]],
@as([*]u8, @ptrFromInt(base_addr + relr))[0..sorted_dynv[elf.DT_RELRSZ]], );
});
var current: [*]usize = undefined; var current: [*]usize = undefined;
for (relrs) |r| { for (relrs) |r| {
if ((r & 1) == 0) { if ((r & 1) == 0) {

View file

@ -163,7 +163,7 @@ fn exit2(code: usize) noreturn {
// exits(0) // exits(0)
.plan9 => std.os.plan9.exits(null), .plan9 => std.os.plan9.exits(null),
.windows => { .windows => {
std.os.windows.ntdll.RtlExitUserProcess(@as(u32, @truncate(code))); std.os.windows.ntdll.RtlExitUserProcess(@truncate(code));
}, },
else => @compileError("TODO"), else => @compileError("TODO"),
} }
@ -511,7 +511,7 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn {
// Code coverage instrumentation might try to use thread local variables. // Code coverage instrumentation might try to use thread local variables.
@disableInstrumentation(); @disableInstrumentation();
const argc = argc_argv_ptr[0]; const argc = argc_argv_ptr[0];
const argv = @as([*][*:0]u8, @ptrCast(argc_argv_ptr + 1)); const argv: [*][*:0]u8 = @ptrCast(argc_argv_ptr + 1);
const envp_optional: [*:null]?[*:0]u8 = @ptrCast(@alignCast(argv + argc + 1)); const envp_optional: [*:null]?[*:0]u8 = @ptrCast(@alignCast(argv + argc + 1));
var envp_count: usize = 0; var envp_count: usize = 0;
@ -573,11 +573,11 @@ fn posixCallMainAndExit(argc_argv_ptr: [*]usize) callconv(.c) noreturn {
expandStackSize(phdrs); expandStackSize(phdrs);
} }
const opt_init_array_start = @extern([*]*const fn () callconv(.c) void, .{ const opt_init_array_start = @extern([*]const *const fn () callconv(.c) void, .{
.name = "__init_array_start", .name = "__init_array_start",
.linkage = .weak, .linkage = .weak,
}); });
const opt_init_array_end = @extern([*]*const fn () callconv(.c) void, .{ const opt_init_array_end = @extern([*]const *const fn () callconv(.c) void, .{
.name = "__init_array_end", .name = "__init_array_end",
.linkage = .weak, .linkage = .weak,
}); });
@ -651,7 +651,7 @@ fn main(c_argc: c_int, c_argv: [*][*:0]c_char, c_envp: [*:null]?[*:0]c_char) cal
} }
fn mainWithoutEnv(c_argc: c_int, c_argv: [*][*:0]c_char) callconv(.c) c_int { fn mainWithoutEnv(c_argc: c_int, c_argv: [*][*:0]c_char) callconv(.c) c_int {
std.os.argv = @as([*][*:0]u8, @ptrCast(c_argv))[0..@as(usize, @intCast(c_argc))]; std.os.argv = @as([*][*:0]u8, @ptrCast(c_argv))[0..@intCast(c_argc)];
return callMain(); return callMain();
} }
@ -701,7 +701,7 @@ pub inline fn callMain() u8 {
pub fn call_wWinMain() std.os.windows.INT { pub fn call_wWinMain() std.os.windows.INT {
const peb = std.os.windows.peb(); const peb = std.os.windows.peb();
const MAIN_HINSTANCE = @typeInfo(@TypeOf(root.wWinMain)).@"fn".params[0].type.?; const MAIN_HINSTANCE = @typeInfo(@TypeOf(root.wWinMain)).@"fn".params[0].type.?;
const hInstance = @as(MAIN_HINSTANCE, @ptrCast(peb.ImageBaseAddress)); const hInstance: MAIN_HINSTANCE = @ptrCast(peb.ImageBaseAddress);
const lpCmdLine: [*:0]u16 = @ptrCast(peb.ProcessParameters.CommandLine.Buffer); const lpCmdLine: [*:0]u16 = @ptrCast(peb.ProcessParameters.CommandLine.Buffer);
// There are various types used for the 'show window' variable through the Win32 APIs: // There are various types used for the 'show window' variable through the Win32 APIs:

View file

@ -1823,6 +1823,14 @@ pub const Visibility = enum(u2) {
hidden = 1, hidden = 1,
protected = 2, protected = 2,
pub fn fromSymbolVisibility(sv: std.builtin.SymbolVisibility) Visibility {
return switch (sv) {
.default => .default,
.hidden => .hidden,
.protected => .protected,
};
}
pub fn format( pub fn format(
self: Visibility, self: Visibility,
comptime _: []const u8, comptime _: []const u8,
@ -2555,6 +2563,10 @@ pub const Variable = struct {
return self.ptrConst(builder).global.setLinkage(linkage, builder); return self.ptrConst(builder).global.setLinkage(linkage, builder);
} }
pub fn setVisibility(self: Index, visibility: Visibility, builder: *Builder) void {
return self.ptrConst(builder).global.setVisibility(visibility, builder);
}
pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void { pub fn setDllStorageClass(self: Index, class: DllStorageClass, builder: *Builder) void {
return self.ptrConst(builder).global.setDllStorageClass(class, builder); return self.ptrConst(builder).global.setDllStorageClass(class, builder);
} }

View file

@ -272,6 +272,15 @@
#define zig_linksection_fn zig_linksection #define zig_linksection_fn zig_linksection
#endif #endif
#if zig_has_attribute(visibility)
#define zig_visibility(name) __attribute__((visibility(#name)))
#else
#define zig_visibility(name) zig_visibility_##name
#define zig_visibility_default
#define zig_visibility_hidden zig_visibility_hidden_unavailable
#define zig_visibility_protected zig_visibility_protected_unavailable
#endif
#if zig_has_builtin(unreachable) || defined(zig_gcc) || defined(zig_tinyc) #if zig_has_builtin(unreachable) || defined(zig_gcc) || defined(zig_tinyc)
#define zig_unreachable() __builtin_unreachable() #define zig_unreachable() __builtin_unreachable()
#elif defined(zig_msvc) #elif defined(zig_msvc)

View file

@ -13,6 +13,7 @@ const InternPool = @import("InternPool.zig");
const Type = @import("Type.zig"); const Type = @import("Type.zig");
const Value = @import("Value.zig"); const Value = @import("Value.zig");
const Zcu = @import("Zcu.zig"); const Zcu = @import("Zcu.zig");
const print = @import("Air/print.zig");
const types_resolved = @import("Air/types_resolved.zig"); const types_resolved = @import("Air/types_resolved.zig");
pub const Legalize = @import("Air/Legalize.zig"); pub const Legalize = @import("Air/Legalize.zig");
@ -863,16 +864,17 @@ pub const Inst = struct {
/// Uses the `vector_store_elem` field. /// Uses the `vector_store_elem` field.
vector_store_elem, vector_store_elem,
/// Compute a pointer to a threadlocal or dllimport `Nav`, meaning one of: /// Compute a pointer to a `Nav` at runtime, always one of:
/// ///
/// * `threadlocal var` /// * `threadlocal var`
/// * `extern threadlocal var` (or corresponding `@extern`) /// * `extern threadlocal var` (or corresponding `@extern`)
/// * `@extern` with `.is_dll_import = true` /// * `@extern` with `.is_dll_import = true`
/// * `@extern` with `.relocation = .pcrel`
/// ///
/// Such pointers are runtime values, so cannot be represented with an InternPool index. /// Such pointers are runtime values, so cannot be represented with an InternPool index.
/// ///
/// Uses the `ty_nav` field. /// Uses the `ty_nav` field.
tlv_dllimport_ptr, runtime_nav_ptr,
/// Implements @cVaArg builtin. /// Implements @cVaArg builtin.
/// Uses the `ty_op` field. /// Uses the `ty_op` field.
@ -1708,7 +1710,7 @@ pub fn typeOfIndex(air: *const Air, inst: Air.Inst.Index, ip: *const InternPool)
return .fromInterned(ip.indexToKey(err_union_ty.ip_index).error_union_type.payload_type); return .fromInterned(ip.indexToKey(err_union_ty.ip_index).error_union_type.payload_type);
}, },
.tlv_dllimport_ptr => return .fromInterned(datas[@intFromEnum(inst)].ty_nav.ty), .runtime_nav_ptr => return .fromInterned(datas[@intFromEnum(inst)].ty_nav.ty),
.work_item_id, .work_item_id,
.work_group_size, .work_group_size,
@ -1983,7 +1985,7 @@ pub fn mustLower(air: Air, inst: Air.Inst.Index, ip: *const InternPool) bool {
.err_return_trace, .err_return_trace,
.addrspace_cast, .addrspace_cast,
.save_err_return_trace_index, .save_err_return_trace_index,
.tlv_dllimport_ptr, .runtime_nav_ptr,
.work_item_id, .work_item_id,
.work_group_size, .work_group_size,
.work_group_id, .work_group_id,
@ -2141,6 +2143,10 @@ pub const typesFullyResolved = types_resolved.typesFullyResolved;
pub const typeFullyResolved = types_resolved.checkType; pub const typeFullyResolved = types_resolved.checkType;
pub const valFullyResolved = types_resolved.checkVal; pub const valFullyResolved = types_resolved.checkVal;
pub const legalize = Legalize.legalize; pub const legalize = Legalize.legalize;
pub const write = print.write;
pub const writeInst = print.writeInst;
pub const dump = print.dump;
pub const dumpInst = print.dumpInst;
pub const CoveragePoint = enum(u1) { pub const CoveragePoint = enum(u1) {
/// Indicates the block is not a place of interest corresponding to /// Indicates the block is not a place of interest corresponding to

View file

@ -622,7 +622,7 @@ fn legalizeBody(l: *Legalize, body_start: usize, body_len: usize) Error!void {
.addrspace_cast, .addrspace_cast,
.save_err_return_trace_index, .save_err_return_trace_index,
.vector_store_elem, .vector_store_elem,
.tlv_dllimport_ptr, .runtime_nav_ptr,
.c_va_arg, .c_va_arg,
.c_va_copy, .c_va_copy,
.c_va_end, .c_va_end,

View file

@ -339,7 +339,7 @@ pub fn categorizeOperand(
.wasm_memory_size, .wasm_memory_size,
.err_return_trace, .err_return_trace,
.save_err_return_trace_index, .save_err_return_trace_index,
.tlv_dllimport_ptr, .runtime_nav_ptr,
.c_va_start, .c_va_start,
.work_item_id, .work_item_id,
.work_group_size, .work_group_size,
@ -972,7 +972,7 @@ fn analyzeInst(
.wasm_memory_size, .wasm_memory_size,
.err_return_trace, .err_return_trace,
.save_err_return_trace_index, .save_err_return_trace_index,
.tlv_dllimport_ptr, .runtime_nav_ptr,
.c_va_start, .c_va_start,
.work_item_id, .work_item_id,
.work_group_size, .work_group_size,

View file

@ -63,7 +63,7 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void {
.wasm_memory_size, .wasm_memory_size,
.err_return_trace, .err_return_trace,
.save_err_return_trace_index, .save_err_return_trace_index,
.tlv_dllimport_ptr, .runtime_nav_ptr,
.c_va_start, .c_va_start,
.work_item_id, .work_item_id,
.work_group_size, .work_group_size,

View file

@ -2,13 +2,15 @@ const std = @import("std");
const Allocator = std.mem.Allocator; const Allocator = std.mem.Allocator;
const fmtIntSizeBin = std.fmt.fmtIntSizeBin; const fmtIntSizeBin = std.fmt.fmtIntSizeBin;
const Zcu = @import("Zcu.zig"); const build_options = @import("build_options");
const Value = @import("Value.zig"); const Zcu = @import("../Zcu.zig");
const Type = @import("Type.zig"); const Value = @import("../Value.zig");
const Air = @import("Air.zig"); const Type = @import("../Type.zig");
const InternPool = @import("InternPool.zig"); const Air = @import("../Air.zig");
const InternPool = @import("../InternPool.zig");
pub fn write(stream: anytype, pt: Zcu.PerThread, air: Air, liveness: ?Air.Liveness) void { pub fn write(air: Air, stream: anytype, pt: Zcu.PerThread, liveness: ?Air.Liveness) void {
comptime std.debug.assert(build_options.enable_debug_extensions);
const instruction_bytes = air.instructions.len * const instruction_bytes = air.instructions.len *
// Here we don't use @sizeOf(Air.Inst.Data) because it would include // Here we don't use @sizeOf(Air.Inst.Data) because it would include
// the debug safety tag but we want to measure release size. // the debug safety tag but we want to measure release size.
@ -52,12 +54,13 @@ pub fn write(stream: anytype, pt: Zcu.PerThread, air: Air, liveness: ?Air.Livene
} }
pub fn writeInst( pub fn writeInst(
air: Air,
stream: anytype, stream: anytype,
inst: Air.Inst.Index, inst: Air.Inst.Index,
pt: Zcu.PerThread, pt: Zcu.PerThread,
air: Air,
liveness: ?Air.Liveness, liveness: ?Air.Liveness,
) void { ) void {
comptime std.debug.assert(build_options.enable_debug_extensions);
var writer: Writer = .{ var writer: Writer = .{
.pt = pt, .pt = pt,
.gpa = pt.zcu.gpa, .gpa = pt.zcu.gpa,
@ -69,12 +72,12 @@ pub fn writeInst(
writer.writeInst(stream, inst) catch return; writer.writeInst(stream, inst) catch return;
} }
pub fn dump(pt: Zcu.PerThread, air: Air, liveness: ?Air.Liveness) void { pub fn dump(air: Air, pt: Zcu.PerThread, liveness: ?Air.Liveness) void {
write(std.io.getStdErr().writer(), pt, air, liveness); air.write(std.io.getStdErr().writer(), pt, liveness);
} }
pub fn dumpInst(inst: Air.Inst.Index, pt: Zcu.PerThread, air: Air, liveness: ?Air.Liveness) void { pub fn dumpInst(air: Air, inst: Air.Inst.Index, pt: Zcu.PerThread, liveness: ?Air.Liveness) void {
writeInst(std.io.getStdErr().writer(), inst, pt, air, liveness); air.writeInst(std.io.getStdErr().writer(), inst, pt, liveness);
} }
const Writer = struct { const Writer = struct {
@ -320,7 +323,7 @@ const Writer = struct {
.reduce, .reduce_optimized => try w.writeReduce(s, inst), .reduce, .reduce_optimized => try w.writeReduce(s, inst),
.cmp_vector, .cmp_vector_optimized => try w.writeCmpVector(s, inst), .cmp_vector, .cmp_vector_optimized => try w.writeCmpVector(s, inst),
.vector_store_elem => try w.writeVectorStoreElem(s, inst), .vector_store_elem => try w.writeVectorStoreElem(s, inst),
.tlv_dllimport_ptr => try w.writeTlvDllimportPtr(s, inst), .runtime_nav_ptr => try w.writeRuntimeNavPtr(s, inst),
.work_item_id, .work_item_id,
.work_group_size, .work_group_size,
@ -578,7 +581,7 @@ const Writer = struct {
try w.writeOperand(s, inst, 2, extra.rhs); try w.writeOperand(s, inst, 2, extra.rhs);
} }
fn writeTlvDllimportPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void { fn writeRuntimeNavPtr(w: *Writer, s: anytype, inst: Air.Inst.Index) @TypeOf(s).Error!void {
const ip = &w.pt.zcu.intern_pool; const ip = &w.pt.zcu.intern_pool;
const ty_nav = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; const ty_nav = w.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav;
try w.writeType(s, .fromInterned(ty_nav.ty)); try w.writeType(s, .fromInterned(ty_nav.ty));

View file

@ -321,7 +321,7 @@ fn checkBody(air: Air, body: []const Air.Inst.Index, zcu: *Zcu) bool {
if (!checkRef(bin.rhs, zcu)) return false; if (!checkRef(bin.rhs, zcu)) return false;
}, },
.tlv_dllimport_ptr => { .runtime_nav_ptr => {
if (!checkType(.fromInterned(data.ty_nav.ty), zcu)) return false; if (!checkType(.fromInterned(data.ty_nav.ty), zcu)) return false;
}, },

View file

@ -345,11 +345,19 @@ pub fn resolve(options: Options) ResolveError!Config {
} else false; } else false;
}; };
const is_dyn_lib = switch (options.output_mode) {
.Obj, .Exe => false,
.Lib => link_mode == .dynamic,
};
// Make a decision on whether to use LLVM backend for machine code generation. // Make a decision on whether to use LLVM backend for machine code generation.
// Note that using the LLVM backend does not necessarily mean using LLVM libraries. // Note that using the LLVM backend does not necessarily mean using LLVM libraries.
// For example, Zig can emit .bc and .ll files directly, and this is still considered // For example, Zig can emit .bc and .ll files directly, and this is still considered
// using "the LLVM backend". // using "the LLVM backend".
const prefer_llvm = b: { const use_llvm = b: {
// If we have no zig code to compile, no need for LLVM.
if (!options.have_zcu) break :b false;
// If emitting to LLVM bitcode object format, must use LLVM backend. // If emitting to LLVM bitcode object format, must use LLVM backend.
if (options.emit_llvm_ir or options.emit_llvm_bc) { if (options.emit_llvm_ir or options.emit_llvm_bc) {
if (options.use_llvm == false) if (options.use_llvm == false)
@ -382,9 +390,9 @@ pub fn resolve(options: Options) ResolveError!Config {
// Prefer LLVM for release builds. // Prefer LLVM for release builds.
if (root_optimize_mode != .Debug) break :b true; if (root_optimize_mode != .Debug) break :b true;
// Self-hosted backends can't handle the inline assembly in std.pie yet // load_dynamic_library standalone test not passing on this combination
// https://github.com/ziglang/zig/issues/24046 // https://github.com/ziglang/zig/issues/24080
if (pie) break :b true; if (target.os.tag == .macos and is_dyn_lib) break :b true;
// At this point we would prefer to use our own self-hosted backend, // At this point we would prefer to use our own self-hosted backend,
// because the compilation speed is better than LLVM. But only do it if // because the compilation speed is better than LLVM. But only do it if
@ -392,13 +400,6 @@ pub fn resolve(options: Options) ResolveError!Config {
break :b !target_util.selfHostedBackendIsAsRobustAsLlvm(target); break :b !target_util.selfHostedBackendIsAsRobustAsLlvm(target);
}; };
const use_llvm = b: {
// If we have no zig code to compile, no need for LLVM.
if (!options.have_zcu) break :b false;
break :b prefer_llvm;
};
if (options.emit_bin and options.have_zcu) { if (options.emit_bin and options.have_zcu) {
if (!use_lib_llvm and use_llvm) { if (!use_lib_llvm and use_llvm) {
// Explicit request to use LLVM to produce an object file, but without // Explicit request to use LLVM to produce an object file, but without
@ -435,7 +436,13 @@ pub fn resolve(options: Options) ResolveError!Config {
} }
if (options.use_lld) |x| break :b x; if (options.use_lld) |x| break :b x;
break :b prefer_llvm;
// If we have no zig code to compile, no need for the self-hosted linker.
if (!options.have_zcu) break :b true;
// If we do have zig code, match the decision for whether to use the llvm backend,
// so that the llvm backend defaults to lld and the self-hosted backends do not.
break :b use_llvm;
}; };
const lto: std.zig.LtoMode = b: { const lto: std.zig.LtoMode = b: {

View file

@ -526,10 +526,10 @@ pub const Nav = struct {
/// The type of this `Nav` is resolved; the value is queued for resolution. /// The type of this `Nav` is resolved; the value is queued for resolution.
type_resolved: struct { type_resolved: struct {
type: InternPool.Index, type: InternPool.Index,
is_const: bool,
alignment: Alignment, alignment: Alignment,
@"linksection": OptionalNullTerminatedString, @"linksection": OptionalNullTerminatedString,
@"addrspace": std.builtin.AddressSpace, @"addrspace": std.builtin.AddressSpace,
is_const: bool,
is_threadlocal: bool, is_threadlocal: bool,
/// This field is whether this `Nav` is a literal `extern` definition. /// This field is whether this `Nav` is a literal `extern` definition.
/// It does *not* tell you whether this might alias an extern fn (see #21027). /// It does *not* tell you whether this might alias an extern fn (see #21027).
@ -538,6 +538,7 @@ pub const Nav = struct {
/// The value of this `Nav` is resolved. /// The value of this `Nav` is resolved.
fully_resolved: struct { fully_resolved: struct {
val: InternPool.Index, val: InternPool.Index,
is_const: bool,
alignment: Alignment, alignment: Alignment,
@"linksection": OptionalNullTerminatedString, @"linksection": OptionalNullTerminatedString,
@"addrspace": std.builtin.AddressSpace, @"addrspace": std.builtin.AddressSpace,
@ -727,12 +728,12 @@ pub const Nav = struct {
const Bits = packed struct(u16) { const Bits = packed struct(u16) {
status: enum(u2) { unresolved, type_resolved, fully_resolved, type_resolved_extern_decl }, status: enum(u2) { unresolved, type_resolved, fully_resolved, type_resolved_extern_decl },
/// Populated only if `bits.status != .unresolved`. /// Populated only if `bits.status != .unresolved`.
is_const: bool,
/// Populated only if `bits.status != .unresolved`.
alignment: Alignment, alignment: Alignment,
/// Populated only if `bits.status != .unresolved`. /// Populated only if `bits.status != .unresolved`.
@"addrspace": std.builtin.AddressSpace, @"addrspace": std.builtin.AddressSpace,
/// Populated only if `bits.status == .type_resolved`. /// Populated only if `bits.status == .type_resolved`.
is_const: bool,
/// Populated only if `bits.status == .type_resolved`.
is_threadlocal: bool, is_threadlocal: bool,
is_usingnamespace: bool, is_usingnamespace: bool,
}; };
@ -753,15 +754,16 @@ pub const Nav = struct {
.unresolved => .unresolved, .unresolved => .unresolved,
.type_resolved, .type_resolved_extern_decl => .{ .type_resolved = .{ .type_resolved, .type_resolved_extern_decl => .{ .type_resolved = .{
.type = repr.type_or_val, .type = repr.type_or_val,
.is_const = repr.bits.is_const,
.alignment = repr.bits.alignment, .alignment = repr.bits.alignment,
.@"linksection" = repr.@"linksection", .@"linksection" = repr.@"linksection",
.@"addrspace" = repr.bits.@"addrspace", .@"addrspace" = repr.bits.@"addrspace",
.is_const = repr.bits.is_const,
.is_threadlocal = repr.bits.is_threadlocal, .is_threadlocal = repr.bits.is_threadlocal,
.is_extern_decl = repr.bits.status == .type_resolved_extern_decl, .is_extern_decl = repr.bits.status == .type_resolved_extern_decl,
} }, } },
.fully_resolved => .{ .fully_resolved = .{ .fully_resolved => .{ .fully_resolved = .{
.val = repr.type_or_val, .val = repr.type_or_val,
.is_const = repr.bits.is_const,
.alignment = repr.bits.alignment, .alignment = repr.bits.alignment,
.@"linksection" = repr.@"linksection", .@"linksection" = repr.@"linksection",
.@"addrspace" = repr.bits.@"addrspace", .@"addrspace" = repr.bits.@"addrspace",
@ -792,26 +794,26 @@ pub const Nav = struct {
.bits = switch (nav.status) { .bits = switch (nav.status) {
.unresolved => .{ .unresolved => .{
.status = .unresolved, .status = .unresolved,
.is_const = false,
.alignment = .none, .alignment = .none,
.@"addrspace" = .generic, .@"addrspace" = .generic,
.is_usingnamespace = nav.is_usingnamespace, .is_usingnamespace = nav.is_usingnamespace,
.is_const = false,
.is_threadlocal = false, .is_threadlocal = false,
}, },
.type_resolved => |r| .{ .type_resolved => |r| .{
.status = if (r.is_extern_decl) .type_resolved_extern_decl else .type_resolved, .status = if (r.is_extern_decl) .type_resolved_extern_decl else .type_resolved,
.is_const = r.is_const,
.alignment = r.alignment, .alignment = r.alignment,
.@"addrspace" = r.@"addrspace", .@"addrspace" = r.@"addrspace",
.is_usingnamespace = nav.is_usingnamespace, .is_usingnamespace = nav.is_usingnamespace,
.is_const = r.is_const,
.is_threadlocal = r.is_threadlocal, .is_threadlocal = r.is_threadlocal,
}, },
.fully_resolved => |r| .{ .fully_resolved => |r| .{
.status = .fully_resolved, .status = .fully_resolved,
.is_const = r.is_const,
.alignment = r.alignment, .alignment = r.alignment,
.@"addrspace" = r.@"addrspace", .@"addrspace" = r.@"addrspace",
.is_usingnamespace = nav.is_usingnamespace, .is_usingnamespace = nav.is_usingnamespace,
.is_const = false,
.is_threadlocal = false, .is_threadlocal = false,
}, },
}, },
@ -2221,7 +2223,6 @@ pub const Key = union(enum) {
init: Index, init: Index,
owner_nav: Nav.Index, owner_nav: Nav.Index,
is_threadlocal: bool, is_threadlocal: bool,
is_weak_linkage: bool,
}; };
pub const Extern = struct { pub const Extern = struct {
@ -2234,10 +2235,12 @@ pub const Key = union(enum) {
/// For example `extern "c" fn write(...) usize` would have 'c' as library name. /// For example `extern "c" fn write(...) usize` would have 'c' as library name.
/// Index into the string table bytes. /// Index into the string table bytes.
lib_name: OptionalNullTerminatedString, lib_name: OptionalNullTerminatedString,
is_const: bool, linkage: std.builtin.GlobalLinkage,
visibility: std.builtin.SymbolVisibility,
is_threadlocal: bool, is_threadlocal: bool,
is_weak_linkage: bool,
is_dll_import: bool, is_dll_import: bool,
relocation: std.builtin.ExternOptions.Relocation,
is_const: bool,
alignment: Alignment, alignment: Alignment,
@"addrspace": std.builtin.AddressSpace, @"addrspace": std.builtin.AddressSpace,
/// The ZIR instruction which created this extern; used only for source locations. /// The ZIR instruction which created this extern; used only for source locations.
@ -2844,9 +2847,10 @@ pub const Key = union(enum) {
.@"extern" => |e| Hash.hash(seed, asBytes(&e.name) ++ .@"extern" => |e| Hash.hash(seed, asBytes(&e.name) ++
asBytes(&e.ty) ++ asBytes(&e.lib_name) ++ asBytes(&e.ty) ++ asBytes(&e.lib_name) ++
asBytes(&e.is_const) ++ asBytes(&e.is_threadlocal) ++ asBytes(&e.linkage) ++ asBytes(&e.visibility) ++
asBytes(&e.is_weak_linkage) ++ asBytes(&e.alignment) ++ asBytes(&e.is_threadlocal) ++ asBytes(&e.is_dll_import) ++
asBytes(&e.is_dll_import) ++ asBytes(&e.@"addrspace") ++ asBytes(&e.relocation) ++
asBytes(&e.is_const) ++ asBytes(&e.alignment) ++ asBytes(&e.@"addrspace") ++
asBytes(&e.zir_index)), asBytes(&e.zir_index)),
}; };
} }
@ -2928,21 +2932,22 @@ pub const Key = union(enum) {
.variable => |a_info| { .variable => |a_info| {
const b_info = b.variable; const b_info = b.variable;
return a_info.owner_nav == b_info.owner_nav and return a_info.ty == b_info.ty and
a_info.ty == b_info.ty and
a_info.init == b_info.init and a_info.init == b_info.init and
a_info.is_threadlocal == b_info.is_threadlocal and a_info.owner_nav == b_info.owner_nav and
a_info.is_weak_linkage == b_info.is_weak_linkage; a_info.is_threadlocal == b_info.is_threadlocal;
}, },
.@"extern" => |a_info| { .@"extern" => |a_info| {
const b_info = b.@"extern"; const b_info = b.@"extern";
return a_info.name == b_info.name and return a_info.name == b_info.name and
a_info.ty == b_info.ty and a_info.ty == b_info.ty and
a_info.lib_name == b_info.lib_name and a_info.lib_name == b_info.lib_name and
a_info.is_const == b_info.is_const and a_info.linkage == b_info.linkage and
a_info.visibility == b_info.visibility and
a_info.is_threadlocal == b_info.is_threadlocal and a_info.is_threadlocal == b_info.is_threadlocal and
a_info.is_weak_linkage == b_info.is_weak_linkage and
a_info.is_dll_import == b_info.is_dll_import and a_info.is_dll_import == b_info.is_dll_import and
a_info.relocation == b_info.relocation and
a_info.is_const == b_info.is_const and
a_info.alignment == b_info.alignment and a_info.alignment == b_info.alignment and
a_info.@"addrspace" == b_info.@"addrspace" and a_info.@"addrspace" == b_info.@"addrspace" and
a_info.zir_index == b_info.zir_index; a_info.zir_index == b_info.zir_index;
@ -4889,6 +4894,7 @@ pub const Index = enum(u32) {
float_c_longdouble_f128: struct { data: *Float128 }, float_c_longdouble_f128: struct { data: *Float128 },
float_comptime_float: struct { data: *Float128 }, float_comptime_float: struct { data: *Float128 },
variable: struct { data: *Tag.Variable }, variable: struct { data: *Tag.Variable },
threadlocal_variable: struct { data: *Tag.Variable },
@"extern": struct { data: *Tag.Extern }, @"extern": struct { data: *Tag.Extern },
func_decl: struct { func_decl: struct {
const @"data.analysis.inferred_error_set" = opaque {}; const @"data.analysis.inferred_error_set" = opaque {};
@ -5548,6 +5554,9 @@ pub const Tag = enum(u8) {
/// A global variable. /// A global variable.
/// data is extra index to Variable. /// data is extra index to Variable.
variable, variable,
/// A global threadlocal variable.
/// data is extra index to Variable.
threadlocal_variable,
/// An extern function or variable. /// An extern function or variable.
/// data is extra index to Extern. /// data is extra index to Extern.
/// Some parts of the key are stored in `owner_nav`. /// Some parts of the key are stored in `owner_nav`.
@ -5863,6 +5872,7 @@ pub const Tag = enum(u8) {
.float_c_longdouble_f128 = .{ .summary = .@"@as(c_longdouble, {.payload%value})", .payload = f128 }, .float_c_longdouble_f128 = .{ .summary = .@"@as(c_longdouble, {.payload%value})", .payload = f128 },
.float_comptime_float = .{ .summary = .@"{.payload%value}", .payload = f128 }, .float_comptime_float = .{ .summary = .@"{.payload%value}", .payload = f128 },
.variable = .{ .summary = .@"{.payload.owner_nav.fqn%summary#\"}", .payload = Variable }, .variable = .{ .summary = .@"{.payload.owner_nav.fqn%summary#\"}", .payload = Variable },
.threadlocal_variable = .{ .summary = .@"{.payload.owner_nav.fqn%summary#\"}", .payload = Variable },
.@"extern" = .{ .summary = .@"{.payload.owner_nav.fqn%summary#\"}", .payload = Extern }, .@"extern" = .{ .summary = .@"{.payload.owner_nav.fqn%summary#\"}", .payload = Extern },
.func_decl = .{ .func_decl = .{
.summary = .@"{.payload.owner_nav.fqn%summary#\"}", .summary = .@"{.payload.owner_nav.fqn%summary#\"}",
@ -5913,24 +5923,24 @@ pub const Tag = enum(u8) {
/// May be `none`. /// May be `none`.
init: Index, init: Index,
owner_nav: Nav.Index, owner_nav: Nav.Index,
flags: Flags,
pub const Flags = packed struct(u32) {
is_const: bool,
is_threadlocal: bool,
is_weak_linkage: bool,
is_dll_import: bool,
_: u28 = 0,
};
}; };
pub const Extern = struct { pub const Extern = struct {
// name, alignment, addrspace come from `owner_nav`. // name, is_const, alignment, addrspace come from `owner_nav`.
ty: Index, ty: Index,
lib_name: OptionalNullTerminatedString, lib_name: OptionalNullTerminatedString,
flags: Variable.Flags, flags: Flags,
owner_nav: Nav.Index, owner_nav: Nav.Index,
zir_index: TrackedInst.Index, zir_index: TrackedInst.Index,
pub const Flags = packed struct(u32) {
linkage: std.builtin.GlobalLinkage,
visibility: std.builtin.SymbolVisibility,
is_threadlocal: bool,
is_dll_import: bool,
relocation: std.builtin.ExternOptions.Relocation,
_: u25 = 0,
};
}; };
/// Trailing: /// Trailing:
@ -7248,14 +7258,17 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.ty = .comptime_float_type, .ty = .comptime_float_type,
.storage = .{ .f128 = extraData(unwrapped_index.getExtra(ip), Float128, data).get() }, .storage = .{ .f128 = extraData(unwrapped_index.getExtra(ip), Float128, data).get() },
} }, } },
.variable => { .variable, .threadlocal_variable => {
const extra = extraData(unwrapped_index.getExtra(ip), Tag.Variable, data); const extra = extraData(unwrapped_index.getExtra(ip), Tag.Variable, data);
return .{ .variable = .{ return .{ .variable = .{
.ty = extra.ty, .ty = extra.ty,
.init = extra.init, .init = extra.init,
.owner_nav = extra.owner_nav, .owner_nav = extra.owner_nav,
.is_threadlocal = extra.flags.is_threadlocal, .is_threadlocal = switch (item.tag) {
.is_weak_linkage = extra.flags.is_weak_linkage, else => unreachable,
.variable => false,
.threadlocal_variable => true,
},
} }; } };
}, },
.@"extern" => { .@"extern" => {
@ -7265,10 +7278,12 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
.name = nav.name, .name = nav.name,
.ty = extra.ty, .ty = extra.ty,
.lib_name = extra.lib_name, .lib_name = extra.lib_name,
.is_const = extra.flags.is_const, .linkage = extra.flags.linkage,
.visibility = extra.flags.visibility,
.is_threadlocal = extra.flags.is_threadlocal, .is_threadlocal = extra.flags.is_threadlocal,
.is_weak_linkage = extra.flags.is_weak_linkage,
.is_dll_import = extra.flags.is_dll_import, .is_dll_import = extra.flags.is_dll_import,
.relocation = extra.flags.relocation,
.is_const = nav.status.fully_resolved.is_const,
.alignment = nav.status.fully_resolved.alignment, .alignment = nav.status.fully_resolved.alignment,
.@"addrspace" = nav.status.fully_resolved.@"addrspace", .@"addrspace" = nav.status.fully_resolved.@"addrspace",
.zir_index = extra.zir_index, .zir_index = extra.zir_index,
@ -7895,17 +7910,14 @@ pub fn get(ip: *InternPool, gpa: Allocator, tid: Zcu.PerThread.Id, key: Key) All
const has_init = variable.init != .none; const has_init = variable.init != .none;
if (has_init) assert(variable.ty == ip.typeOf(variable.init)); if (has_init) assert(variable.ty == ip.typeOf(variable.init));
items.appendAssumeCapacity(.{ items.appendAssumeCapacity(.{
.tag = .variable, .tag = switch (variable.is_threadlocal) {
false => .variable,
true => .threadlocal_variable,
},
.data = try addExtra(extra, Tag.Variable{ .data = try addExtra(extra, Tag.Variable{
.ty = variable.ty, .ty = variable.ty,
.init = variable.init, .init = variable.init,
.owner_nav = variable.owner_nav, .owner_nav = variable.owner_nav,
.flags = .{
.is_const = false,
.is_threadlocal = variable.is_threadlocal,
.is_weak_linkage = variable.is_weak_linkage,
.is_dll_import = false,
},
}), }),
}); });
}, },
@ -9128,6 +9140,7 @@ pub fn getExtern(
.name = key.name, .name = key.name,
.fqn = key.name, .fqn = key.name,
.val = extern_index, .val = extern_index,
.is_const = key.is_const,
.alignment = key.alignment, .alignment = key.alignment,
.@"linksection" = .none, .@"linksection" = .none,
.@"addrspace" = key.@"addrspace", .@"addrspace" = key.@"addrspace",
@ -9136,10 +9149,11 @@ pub fn getExtern(
.ty = key.ty, .ty = key.ty,
.lib_name = key.lib_name, .lib_name = key.lib_name,
.flags = .{ .flags = .{
.is_const = key.is_const, .linkage = key.linkage,
.visibility = key.visibility,
.is_threadlocal = key.is_threadlocal, .is_threadlocal = key.is_threadlocal,
.is_weak_linkage = key.is_weak_linkage,
.is_dll_import = key.is_dll_import, .is_dll_import = key.is_dll_import,
.relocation = key.relocation,
}, },
.zir_index = key.zir_index, .zir_index = key.zir_index,
.owner_nav = owner_nav, .owner_nav = owner_nav,
@ -9714,6 +9728,7 @@ fn finishFuncInstance(
.name = nav_name, .name = nav_name,
.fqn = try ip.namespacePtr(fn_namespace).internFullyQualifiedName(ip, gpa, tid, nav_name), .fqn = try ip.namespacePtr(fn_namespace).internFullyQualifiedName(ip, gpa, tid, nav_name),
.val = func_index, .val = func_index,
.is_const = fn_owner_nav.status.fully_resolved.is_const,
.alignment = fn_owner_nav.status.fully_resolved.alignment, .alignment = fn_owner_nav.status.fully_resolved.alignment,
.@"linksection" = fn_owner_nav.status.fully_resolved.@"linksection", .@"linksection" = fn_owner_nav.status.fully_resolved.@"linksection",
.@"addrspace" = fn_owner_nav.status.fully_resolved.@"addrspace", .@"addrspace" = fn_owner_nav.status.fully_resolved.@"addrspace",
@ -10300,13 +10315,13 @@ fn addExtraAssumeCapacity(extra: Local.Extra.Mutable, item: anytype) u32 {
u32, u32,
i32, i32,
FuncAnalysis, FuncAnalysis,
Tag.Extern.Flags,
Tag.TypePointer.Flags, Tag.TypePointer.Flags,
Tag.TypeFunction.Flags, Tag.TypeFunction.Flags,
Tag.TypePointer.PackedOffset, Tag.TypePointer.PackedOffset,
Tag.TypeUnion.Flags, Tag.TypeUnion.Flags,
Tag.TypeStruct.Flags, Tag.TypeStruct.Flags,
Tag.TypeStructPacked.Flags, Tag.TypeStructPacked.Flags,
Tag.Variable.Flags,
=> @bitCast(@field(item, field.name)), => @bitCast(@field(item, field.name)),
else => @compileError("bad field type: " ++ @typeName(field.type)), else => @compileError("bad field type: " ++ @typeName(field.type)),
@ -10361,13 +10376,13 @@ fn extraDataTrail(extra: Local.Extra, comptime T: type, index: u32) struct { dat
u32, u32,
i32, i32,
Tag.Extern.Flags,
Tag.TypePointer.Flags, Tag.TypePointer.Flags,
Tag.TypeFunction.Flags, Tag.TypeFunction.Flags,
Tag.TypePointer.PackedOffset, Tag.TypePointer.PackedOffset,
Tag.TypeUnion.Flags, Tag.TypeUnion.Flags,
Tag.TypeStruct.Flags, Tag.TypeStruct.Flags,
Tag.TypeStructPacked.Flags, Tag.TypeStructPacked.Flags,
Tag.Variable.Flags,
FuncAnalysis, FuncAnalysis,
=> @bitCast(extra_item), => @bitCast(extra_item),
@ -11162,7 +11177,7 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void {
.float_c_longdouble_f80 => @sizeOf(Float80), .float_c_longdouble_f80 => @sizeOf(Float80),
.float_c_longdouble_f128 => @sizeOf(Float128), .float_c_longdouble_f128 => @sizeOf(Float128),
.float_comptime_float => @sizeOf(Float128), .float_comptime_float => @sizeOf(Float128),
.variable => @sizeOf(Tag.Variable), .variable, .threadlocal_variable => @sizeOf(Tag.Variable),
.@"extern" => @sizeOf(Tag.Extern), .@"extern" => @sizeOf(Tag.Extern),
.func_decl => @sizeOf(Tag.FuncDecl), .func_decl => @sizeOf(Tag.FuncDecl),
.func_instance => b: { .func_instance => b: {
@ -11282,6 +11297,7 @@ fn dumpAllFallible(ip: *const InternPool) anyerror!void {
.float_c_longdouble_f128, .float_c_longdouble_f128,
.float_comptime_float, .float_comptime_float,
.variable, .variable,
.threadlocal_variable,
.@"extern", .@"extern",
.func_decl, .func_decl,
.func_instance, .func_instance,
@ -11414,6 +11430,7 @@ pub fn createNav(
name: NullTerminatedString, name: NullTerminatedString,
fqn: NullTerminatedString, fqn: NullTerminatedString,
val: InternPool.Index, val: InternPool.Index,
is_const: bool,
alignment: Alignment, alignment: Alignment,
@"linksection": OptionalNullTerminatedString, @"linksection": OptionalNullTerminatedString,
@"addrspace": std.builtin.AddressSpace, @"addrspace": std.builtin.AddressSpace,
@ -11430,6 +11447,7 @@ pub fn createNav(
.analysis = null, .analysis = null,
.status = .{ .fully_resolved = .{ .status = .{ .fully_resolved = .{
.val = opts.val, .val = opts.val,
.is_const = opts.is_const,
.alignment = opts.alignment, .alignment = opts.alignment,
.@"linksection" = opts.@"linksection", .@"linksection" = opts.@"linksection",
.@"addrspace" = opts.@"addrspace", .@"addrspace" = opts.@"addrspace",
@ -11482,10 +11500,10 @@ pub fn resolveNavType(
nav: Nav.Index, nav: Nav.Index,
resolved: struct { resolved: struct {
type: InternPool.Index, type: InternPool.Index,
is_const: bool,
alignment: Alignment, alignment: Alignment,
@"linksection": OptionalNullTerminatedString, @"linksection": OptionalNullTerminatedString,
@"addrspace": std.builtin.AddressSpace, @"addrspace": std.builtin.AddressSpace,
is_const: bool,
is_threadlocal: bool, is_threadlocal: bool,
is_extern_decl: bool, is_extern_decl: bool,
}, },
@ -11512,9 +11530,9 @@ pub fn resolveNavType(
var bits = nav_bits[unwrapped.index]; var bits = nav_bits[unwrapped.index];
bits.status = if (resolved.is_extern_decl) .type_resolved_extern_decl else .type_resolved; bits.status = if (resolved.is_extern_decl) .type_resolved_extern_decl else .type_resolved;
bits.is_const = resolved.is_const;
bits.alignment = resolved.alignment; bits.alignment = resolved.alignment;
bits.@"addrspace" = resolved.@"addrspace"; bits.@"addrspace" = resolved.@"addrspace";
bits.is_const = resolved.is_const;
bits.is_threadlocal = resolved.is_threadlocal; bits.is_threadlocal = resolved.is_threadlocal;
@atomicStore(Nav.Repr.Bits, &nav_bits[unwrapped.index], bits, .release); @atomicStore(Nav.Repr.Bits, &nav_bits[unwrapped.index], bits, .release);
} }
@ -11526,6 +11544,7 @@ pub fn resolveNavValue(
nav: Nav.Index, nav: Nav.Index,
resolved: struct { resolved: struct {
val: InternPool.Index, val: InternPool.Index,
is_const: bool,
alignment: Alignment, alignment: Alignment,
@"linksection": OptionalNullTerminatedString, @"linksection": OptionalNullTerminatedString,
@"addrspace": std.builtin.AddressSpace, @"addrspace": std.builtin.AddressSpace,
@ -11553,6 +11572,7 @@ pub fn resolveNavValue(
var bits = nav_bits[unwrapped.index]; var bits = nav_bits[unwrapped.index];
bits.status = .fully_resolved; bits.status = .fully_resolved;
bits.is_const = resolved.is_const;
bits.alignment = resolved.alignment; bits.alignment = resolved.alignment;
bits.@"addrspace" = resolved.@"addrspace"; bits.@"addrspace" = resolved.@"addrspace";
@atomicStore(Nav.Repr.Bits, &nav_bits[unwrapped.index], bits, .release); @atomicStore(Nav.Repr.Bits, &nav_bits[unwrapped.index], bits, .release);
@ -12007,6 +12027,7 @@ pub fn typeOf(ip: *const InternPool, index: Index) Index {
.error_union_error, .error_union_error,
.enum_tag, .enum_tag,
.variable, .variable,
.threadlocal_variable,
.@"extern", .@"extern",
.func_decl, .func_decl,
.func_instance, .func_instance,
@ -12391,6 +12412,7 @@ pub fn zigTypeTag(ip: *const InternPool, index: Index) std.builtin.TypeId {
.float_c_longdouble_f128, .float_c_longdouble_f128,
.float_comptime_float, .float_comptime_float,
.variable, .variable,
.threadlocal_variable,
.@"extern", .@"extern",
.func_decl, .func_decl,
.func_instance, .func_instance,

View file

@ -26089,10 +26089,12 @@ fn resolveExternOptions(
zir_ref: Zir.Inst.Ref, zir_ref: Zir.Inst.Ref,
) CompileError!struct { ) CompileError!struct {
name: InternPool.NullTerminatedString, name: InternPool.NullTerminatedString,
library_name: InternPool.OptionalNullTerminatedString = .none, library_name: InternPool.OptionalNullTerminatedString,
linkage: std.builtin.GlobalLinkage = .strong, linkage: std.builtin.GlobalLinkage,
is_thread_local: bool = false, visibility: std.builtin.SymbolVisibility,
is_dll_import: bool = false, is_thread_local: bool,
is_dll_import: bool,
relocation: std.builtin.ExternOptions.Relocation,
} { } {
const pt = sema.pt; const pt = sema.pt;
const zcu = pt.zcu; const zcu = pt.zcu;
@ -26105,8 +26107,10 @@ fn resolveExternOptions(
const name_src = block.src(.{ .init_field_name = src.offset.node_offset_builtin_call_arg.builtin_call_node }); const name_src = block.src(.{ .init_field_name = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node }); const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node }); const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const visibility_src = block.src(.{ .init_field_visibility = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node }); const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node }); const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const relocation_src = block.src(.{ .init_field_relocation = src.offset.node_offset_builtin_call_arg.builtin_call_node });
const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src); const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "name", .no_embedded_nulls), name_src);
const name = try sema.toConstString(block, name_src, name_ref, .{ .simple = .extern_options }); const name = try sema.toConstString(block, name_src, name_ref, .{ .simple = .extern_options });
@ -26118,6 +26122,10 @@ fn resolveExternOptions(
const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_ref, .{ .simple = .extern_options }); const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_ref, .{ .simple = .extern_options });
const linkage = try sema.interpretBuiltinType(block, linkage_src, linkage_val, std.builtin.GlobalLinkage); const linkage = try sema.interpretBuiltinType(block, linkage_src, linkage_val, std.builtin.GlobalLinkage);
const visibility_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "visibility", .no_embedded_nulls), visibility_src);
const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_ref, .{ .simple = .extern_options });
const visibility = try sema.interpretBuiltinType(block, visibility_src, visibility_val, std.builtin.SymbolVisibility);
const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_thread_local", .no_embedded_nulls), thread_local_src); const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_thread_local", .no_embedded_nulls), thread_local_src);
const is_thread_local_val = try sema.resolveConstDefinedValue(block, thread_local_src, is_thread_local, .{ .simple = .extern_options }); const is_thread_local_val = try sema.resolveConstDefinedValue(block, thread_local_src, is_thread_local, .{ .simple = .extern_options });
@ -26133,6 +26141,10 @@ fn resolveExternOptions(
const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src); const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src);
const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{ .simple = .extern_options }); const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{ .simple = .extern_options });
const relocation_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, pt.tid, "relocation", .no_embedded_nulls), relocation_src);
const relocation_val = try sema.resolveConstDefinedValue(block, relocation_src, relocation_ref, .{ .simple = .extern_options });
const relocation = try sema.interpretBuiltinType(block, relocation_src, relocation_val, std.builtin.ExternOptions.Relocation);
if (name.len == 0) { if (name.len == 0) {
return sema.fail(block, name_src, "extern symbol name cannot be empty", .{}); return sema.fail(block, name_src, "extern symbol name cannot be empty", .{});
} }
@ -26145,8 +26157,10 @@ fn resolveExternOptions(
.name = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls), .name = try ip.getOrPutString(gpa, pt.tid, name, .no_embedded_nulls),
.library_name = try ip.getOrPutStringOpt(gpa, pt.tid, library_name, .no_embedded_nulls), .library_name = try ip.getOrPutStringOpt(gpa, pt.tid, library_name, .no_embedded_nulls),
.linkage = linkage, .linkage = linkage,
.visibility = visibility,
.is_thread_local = is_thread_local_val.toBool(), .is_thread_local = is_thread_local_val.toBool(),
.is_dll_import = is_dll_import_val.toBool(), .is_dll_import = is_dll_import_val.toBool(),
.relocation = relocation,
}; };
} }
@ -26178,6 +26192,17 @@ fn zirBuiltinExtern(
} }
const options = try sema.resolveExternOptions(block, options_src, extra.rhs); const options = try sema.resolveExternOptions(block, options_src, extra.rhs);
switch (options.linkage) {
.internal => if (options.visibility != .default) {
return sema.fail(block, options_src, "internal symbol cannot have non-default visibility", .{});
},
.strong, .weak => {},
.link_once => return sema.fail(block, options_src, "external symbol cannot have link once linkage", .{}),
}
switch (options.relocation) {
.any => {},
.pcrel => if (options.visibility == .default) return sema.fail(block, options_src, "cannot require a pc-relative relocation to a symbol with default visibility", .{}),
}
// TODO: error for threadlocal functions, non-const functions, etc // TODO: error for threadlocal functions, non-const functions, etc
@ -26190,10 +26215,12 @@ fn zirBuiltinExtern(
.name = options.name, .name = options.name,
.ty = ptr_info.child, .ty = ptr_info.child,
.lib_name = options.library_name, .lib_name = options.library_name,
.is_const = ptr_info.flags.is_const, .linkage = options.linkage,
.visibility = options.visibility,
.is_threadlocal = options.is_thread_local, .is_threadlocal = options.is_thread_local,
.is_weak_linkage = options.linkage == .weak,
.is_dll_import = options.is_dll_import, .is_dll_import = options.is_dll_import,
.relocation = options.relocation,
.is_const = ptr_info.flags.is_const,
.alignment = ptr_info.flags.alignment, .alignment = ptr_info.flags.alignment,
.@"addrspace" = ptr_info.flags.address_space, .@"addrspace" = ptr_info.flags.address_space,
// This instruction is just for source locations. // This instruction is just for source locations.
@ -31685,12 +31712,15 @@ fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_inde
const nav_status = ip.getNav(nav_index).status; const nav_status = ip.getNav(nav_index).status;
const is_tlv_or_dllimport = switch (nav_status) { const is_runtime = switch (nav_status) {
.unresolved => unreachable, .unresolved => unreachable,
// dllimports go straight to `fully_resolved`; the only option is threadlocal // dllimports go straight to `fully_resolved`; the only option is threadlocal
.type_resolved => |r| r.is_threadlocal, .type_resolved => |r| r.is_threadlocal,
.fully_resolved => |r| switch (ip.indexToKey(r.val)) { .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
.@"extern" => |e| e.is_threadlocal or e.is_dll_import, .@"extern" => |e| e.is_threadlocal or e.is_dll_import or switch (e.relocation) {
.any => false,
.pcrel => true,
},
.variable => |v| v.is_threadlocal, .variable => |v| v.is_threadlocal,
else => false, else => false,
}, },
@ -31699,7 +31729,7 @@ fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_inde
const ty, const alignment, const @"addrspace", const is_const = switch (nav_status) { const ty, const alignment, const @"addrspace", const is_const = switch (nav_status) {
.unresolved => unreachable, .unresolved => unreachable,
.type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const }, .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const },
.fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", zcu.navValIsConst(r.val) }, .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", r.is_const },
}; };
const ptr_ty = try pt.ptrTypeSema(.{ const ptr_ty = try pt.ptrTypeSema(.{
.child = ty, .child = ty,
@ -31710,10 +31740,10 @@ fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_inde
}, },
}); });
if (is_tlv_or_dllimport) { if (is_runtime) {
// This pointer is runtime-known; we need to emit an AIR instruction to create it. // This pointer is runtime-known; we need to emit an AIR instruction to create it.
return block.addInst(.{ return block.addInst(.{
.tag = .tlv_dllimport_ptr, .tag = .runtime_nav_ptr,
.data = .{ .ty_nav = .{ .data = .{ .ty_nav = .{
.ty = ptr_ty.toIntern(), .ty = ptr_ty.toIntern(),
.nav = nav_index, .nav = nav_index,
@ -36432,6 +36462,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
.float_c_longdouble_f128, .float_c_longdouble_f128,
.float_comptime_float, .float_comptime_float,
.variable, .variable,
.threadlocal_variable,
.@"extern", .@"extern",
.func_decl, .func_decl,
.func_instance, .func_instance,

View file

@ -2047,6 +2047,7 @@ pub const SrcLoc = struct {
.init_field_library, .init_field_library,
.init_field_thread_local, .init_field_thread_local,
.init_field_dll_import, .init_field_dll_import,
.init_field_relocation,
=> |builtin_call_node| { => |builtin_call_node| {
const wanted = switch (src_loc.lazy) { const wanted = switch (src_loc.lazy) {
.init_field_name => "name", .init_field_name => "name",
@ -2059,6 +2060,7 @@ pub const SrcLoc = struct {
.init_field_library => "library", .init_field_library => "library",
.init_field_thread_local => "thread_local", .init_field_thread_local => "thread_local",
.init_field_dll_import => "dll_import", .init_field_dll_import => "dll_import",
.init_field_relocation => "relocation",
else => unreachable, else => unreachable,
}; };
const tree = try src_loc.file_scope.getTree(zcu); const tree = try src_loc.file_scope.getTree(zcu);
@ -2506,6 +2508,7 @@ pub const LazySrcLoc = struct {
init_field_library: Ast.Node.Offset, init_field_library: Ast.Node.Offset,
init_field_thread_local: Ast.Node.Offset, init_field_thread_local: Ast.Node.Offset,
init_field_dll_import: Ast.Node.Offset, init_field_dll_import: Ast.Node.Offset,
init_field_relocation: Ast.Node.Offset,
/// The source location points to the value of an item in a specific /// The source location points to the value of an item in a specific
/// case of a `switch`. /// case of a `switch`.
switch_case_item: SwitchItem, switch_case_item: SwitchItem,
@ -4562,15 +4565,6 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu
return .ok; return .ok;
} }
/// Given that a `Nav` has value `val`, determine if a ref of that `Nav` gives a `const` pointer.
pub fn navValIsConst(zcu: *const Zcu, val: InternPool.Index) bool {
return switch (zcu.intern_pool.indexToKey(val)) {
.variable => false,
.@"extern" => |e| e.is_const,
else => true,
};
}
pub const CodegenFailError = error{ pub const CodegenFailError = error{
/// Indicates the error message has been already stored at `Zcu.failed_codegen`. /// Indicates the error message has been already stored at `Zcu.failed_codegen`.
CodegenFail, CodegenFail,

View file

@ -1153,18 +1153,23 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
// First, we must resolve the declaration's type. To do this, we analyze the type body if available, // First, we must resolve the declaration's type. To do this, we analyze the type body if available,
// or otherwise, we analyze the value body, populating `early_val` in the process. // or otherwise, we analyze the value body, populating `early_val` in the process.
switch (zir_decl.kind) { const is_const = is_const: switch (zir_decl.kind) {
.@"comptime" => unreachable, // this is not a Nav .@"comptime" => unreachable, // this is not a Nav
.unnamed_test, .@"test", .decltest => assert(nav_ty.zigTypeTag(zcu) == .@"fn"), .unnamed_test, .@"test", .decltest => {
.@"usingnamespace" => {}, assert(nav_ty.zigTypeTag(zcu) == .@"fn");
.@"const" => {}, break :is_const true;
.@"var" => try sema.validateVarType( },
&block, .@"usingnamespace", .@"const" => true,
if (zir_decl.type_body != null) ty_src else init_src, .@"var" => {
nav_ty, try sema.validateVarType(
zir_decl.linkage == .@"extern", &block,
), if (zir_decl.type_body != null) ty_src else init_src,
} nav_ty,
zir_decl.linkage == .@"extern",
);
break :is_const false;
},
};
// Now that we know the type, we can evaluate the alignment, linksection, and addrspace, to determine // Now that we know the type, we can evaluate the alignment, linksection, and addrspace, to determine
// the full pointer type of this declaration. // the full pointer type of this declaration.
@ -1195,7 +1200,6 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
.init = final_val.?.toIntern(), .init = final_val.?.toIntern(),
.owner_nav = nav_id, .owner_nav = nav_id,
.is_threadlocal = zir_decl.is_threadlocal, .is_threadlocal = zir_decl.is_threadlocal,
.is_weak_linkage = false,
} })), } })),
else => final_val.?, else => final_val.?,
}, },
@ -1212,10 +1216,12 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
.name = old_nav.name, .name = old_nav.name,
.ty = nav_ty.toIntern(), .ty = nav_ty.toIntern(),
.lib_name = try ip.getOrPutStringOpt(gpa, pt.tid, lib_name, .no_embedded_nulls), .lib_name = try ip.getOrPutStringOpt(gpa, pt.tid, lib_name, .no_embedded_nulls),
.is_const = zir_decl.kind == .@"const",
.is_threadlocal = zir_decl.is_threadlocal, .is_threadlocal = zir_decl.is_threadlocal,
.is_weak_linkage = false, .linkage = .strong,
.visibility = .default,
.is_dll_import = false, .is_dll_import = false,
.relocation = .any,
.is_const = is_const,
.alignment = modifiers.alignment, .alignment = modifiers.alignment,
.@"addrspace" = modifiers.@"addrspace", .@"addrspace" = modifiers.@"addrspace",
.zir_index = old_nav.analysis.?.zir_index, // `declaration` instruction .zir_index = old_nav.analysis.?.zir_index, // `declaration` instruction
@ -1243,6 +1249,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
} }
ip.resolveNavValue(nav_id, .{ ip.resolveNavValue(nav_id, .{
.val = nav_val.toIntern(), .val = nav_val.toIntern(),
.is_const = is_const,
.alignment = .none, .alignment = .none,
.@"linksection" = .none, .@"linksection" = .none,
.@"addrspace" = .generic, .@"addrspace" = .generic,
@ -1286,6 +1293,7 @@ fn analyzeNavVal(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileErr
ip.resolveNavValue(nav_id, .{ ip.resolveNavValue(nav_id, .{
.val = nav_val.toIntern(), .val = nav_val.toIntern(),
.is_const = is_const,
.alignment = modifiers.alignment, .alignment = modifiers.alignment,
.@"linksection" = modifiers.@"linksection", .@"linksection" = modifiers.@"linksection",
.@"addrspace" = modifiers.@"addrspace", .@"addrspace" = modifiers.@"addrspace",
@ -1515,8 +1523,6 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr
// the pointer modifiers, i.e. alignment, linksection, addrspace. // the pointer modifiers, i.e. alignment, linksection, addrspace.
const modifiers = try sema.resolveNavPtrModifiers(&block, zir_decl, inst_resolved.inst, resolved_ty); const modifiers = try sema.resolveNavPtrModifiers(&block, zir_decl, inst_resolved.inst, resolved_ty);
// Usually, we can infer this information from the resolved `Nav` value; see `Zcu.navValIsConst`.
// However, since we don't have one, we need to quickly check the ZIR to figure this out.
const is_const = switch (zir_decl.kind) { const is_const = switch (zir_decl.kind) {
.@"comptime" => unreachable, .@"comptime" => unreachable,
.unnamed_test, .@"test", .decltest, .@"usingnamespace", .@"const" => true, .unnamed_test, .@"test", .decltest, .@"usingnamespace", .@"const" => true,
@ -1542,7 +1548,7 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr
r.alignment != modifiers.alignment or r.alignment != modifiers.alignment or
r.@"linksection" != modifiers.@"linksection" or r.@"linksection" != modifiers.@"linksection" or
r.@"addrspace" != modifiers.@"addrspace" or r.@"addrspace" != modifiers.@"addrspace" or
zcu.navValIsConst(r.val) != is_const or r.is_const != is_const or
(old_nav.getExtern(ip) != null) != is_extern_decl, (old_nav.getExtern(ip) != null) != is_extern_decl,
}; };
@ -1550,10 +1556,10 @@ fn analyzeNavType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Zcu.CompileEr
ip.resolveNavType(nav_id, .{ ip.resolveNavType(nav_id, .{
.type = resolved_ty.toIntern(), .type = resolved_ty.toIntern(),
.is_const = is_const,
.alignment = modifiers.alignment, .alignment = modifiers.alignment,
.@"linksection" = modifiers.@"linksection", .@"linksection" = modifiers.@"linksection",
.@"addrspace" = modifiers.@"addrspace", .@"addrspace" = modifiers.@"addrspace",
.is_const = is_const,
.is_threadlocal = zir_decl.is_threadlocal, .is_threadlocal = zir_decl.is_threadlocal,
.is_extern_decl = is_extern_decl, .is_extern_decl = is_extern_decl,
}); });
@ -1750,7 +1756,7 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: *A
if (build_options.enable_debug_extensions and comp.verbose_air) { if (build_options.enable_debug_extensions and comp.verbose_air) {
std.debug.print("# Begin Function AIR: {}:\n", .{nav.fqn.fmt(ip)}); std.debug.print("# Begin Function AIR: {}:\n", .{nav.fqn.fmt(ip)});
@import("../print_air.zig").dump(pt, air.*, liveness); air.dump(pt, liveness);
std.debug.print("# End Function AIR: {}\n\n", .{nav.fqn.fmt(ip)}); std.debug.print("# End Function AIR: {}\n\n", .{nav.fqn.fmt(ip)});
} }
@ -3577,8 +3583,10 @@ pub fn getCoerced(pt: Zcu.PerThread, val: Value, new_ty: Type) Allocator.Error!V
.lib_name = e.lib_name, .lib_name = e.lib_name,
.is_const = e.is_const, .is_const = e.is_const,
.is_threadlocal = e.is_threadlocal, .is_threadlocal = e.is_threadlocal,
.is_weak_linkage = e.is_weak_linkage, .linkage = e.linkage,
.visibility = e.visibility,
.is_dll_import = e.is_dll_import, .is_dll_import = e.is_dll_import,
.relocation = e.relocation,
.alignment = e.alignment, .alignment = e.alignment,
.@"addrspace" = e.@"addrspace", .@"addrspace" = e.@"addrspace",
.zir_index = e.zir_index, .zir_index = e.zir_index,
@ -3954,7 +3962,7 @@ pub fn navPtrType(pt: Zcu.PerThread, nav_id: InternPool.Nav.Index) Allocator.Err
const ty, const alignment, const @"addrspace", const is_const = switch (ip.getNav(nav_id).status) { const ty, const alignment, const @"addrspace", const is_const = switch (ip.getNav(nav_id).status) {
.unresolved => unreachable, .unresolved => unreachable,
.type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const }, .type_resolved => |r| .{ r.type, r.alignment, r.@"addrspace", r.is_const },
.fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", zcu.navValIsConst(r.val) }, .fully_resolved => |r| .{ ip.typeOf(r.val), r.alignment, r.@"addrspace", r.is_const },
}; };
return pt.ptrType(.{ return pt.ptrType(.{
.child = ty, .child = ty,

View file

@ -880,7 +880,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}), .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}),
.error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}),
.vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}), .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}),
.tlv_dllimport_ptr => return self.fail("TODO implement tlv_dllimport_ptr", .{}), .runtime_nav_ptr => return self.fail("TODO implement runtime_nav_ptr", .{}),
.c_va_arg => return self.fail("TODO implement c_va_arg", .{}), .c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
.c_va_copy => return self.fail("TODO implement c_va_copy", .{}), .c_va_copy => return self.fail("TODO implement c_va_copy", .{}),

View file

@ -869,7 +869,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}), .is_named_enum_value => return self.fail("TODO implement is_named_enum_value", .{}),
.error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}), .error_set_has_value => return self.fail("TODO implement error_set_has_value", .{}),
.vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}), .vector_store_elem => return self.fail("TODO implement vector_store_elem", .{}),
.tlv_dllimport_ptr => return self.fail("TODO implement tlv_dllimport_ptr", .{}), .runtime_nav_ptr => return self.fail("TODO implement runtime_nav_ptr", .{}),
.c_va_arg => return self.fail("TODO implement c_va_arg", .{}), .c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
.c_va_copy => return self.fail("TODO implement c_va_copy", .{}), .c_va_copy => return self.fail("TODO implement c_va_copy", .{}),

View file

@ -1041,12 +1041,7 @@ fn formatAir(
_: std.fmt.FormatOptions, _: std.fmt.FormatOptions,
writer: anytype, writer: anytype,
) @TypeOf(writer).Error!void { ) @TypeOf(writer).Error!void {
@import("../../print_air.zig").dumpInst( data.func.air.dumpInst(data.inst, data.func.pt, data.func.liveness);
data.inst,
data.func.pt,
data.func.air,
data.func.liveness,
);
} }
fn fmtAir(func: *Func, inst: Air.Inst.Index) std.fmt.Formatter(formatAir) { fn fmtAir(func: *Func, inst: Air.Inst.Index) std.fmt.Formatter(formatAir) {
return .{ .data = .{ .func = func, .inst = inst } }; return .{ .data = .{ .func = func, .inst = inst } };
@ -1656,7 +1651,7 @@ fn genBody(func: *Func, body: []const Air.Inst.Index) InnerError!void {
.wrap_errunion_payload => try func.airWrapErrUnionPayload(inst), .wrap_errunion_payload => try func.airWrapErrUnionPayload(inst),
.wrap_errunion_err => try func.airWrapErrUnionErr(inst), .wrap_errunion_err => try func.airWrapErrUnionErr(inst),
.tlv_dllimport_ptr => try func.airTlvDllimportPtr(inst), .runtime_nav_ptr => try func.airRuntimeNavPtr(inst),
.add_optimized, .add_optimized,
.sub_optimized, .sub_optimized,
@ -3626,7 +3621,7 @@ fn airWrapErrUnionErr(func: *Func, inst: Air.Inst.Index) !void {
return func.finishAir(inst, result, .{ ty_op.operand, .none, .none }); return func.finishAir(inst, result, .{ ty_op.operand, .none, .none });
} }
fn airTlvDllimportPtr(func: *Func, inst: Air.Inst.Index) !void { fn airRuntimeNavPtr(func: *Func, inst: Air.Inst.Index) !void {
const zcu = func.pt.zcu; const zcu = func.pt.zcu;
const ip = &zcu.intern_pool; const ip = &zcu.intern_pool;
const ty_nav = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; const ty_nav = func.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav;
@ -3641,7 +3636,7 @@ fn airTlvDllimportPtr(func: *Func, inst: Air.Inst.Index) !void {
break :sym sym; break :sym sym;
} }
break :sym try zo.getOrCreateMetadataForNav(zcu, ty_nav.nav); break :sym try zo.getOrCreateMetadataForNav(zcu, ty_nav.nav);
} else return func.fail("TODO tlv_dllimport_ptr on {}", .{func.bin_file.tag}); } else return func.fail("TODO runtime_nav_ptr on {}", .{func.bin_file.tag});
const dest_mcv = try func.allocRegOrMem(ptr_ty, inst, true); const dest_mcv = try func.allocRegOrMem(ptr_ty, inst, true);
if (dest_mcv.isRegister()) { if (dest_mcv.isRegister()) {

View file

@ -723,7 +723,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.is_named_enum_value => @panic("TODO implement is_named_enum_value"), .is_named_enum_value => @panic("TODO implement is_named_enum_value"),
.error_set_has_value => @panic("TODO implement error_set_has_value"), .error_set_has_value => @panic("TODO implement error_set_has_value"),
.vector_store_elem => @panic("TODO implement vector_store_elem"), .vector_store_elem => @panic("TODO implement vector_store_elem"),
.tlv_dllimport_ptr => @panic("TODO implement tlv_dllimport_ptr"), .runtime_nav_ptr => @panic("TODO implement runtime_nav_ptr"),
.c_va_arg => return self.fail("TODO implement c_va_arg", .{}), .c_va_arg => return self.fail("TODO implement c_va_arg", .{}),
.c_va_copy => return self.fail("TODO implement c_va_copy", .{}), .c_va_copy => return self.fail("TODO implement c_va_copy", .{}),

View file

@ -2057,7 +2057,7 @@ fn genInst(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
.error_set_has_value => cg.airErrorSetHasValue(inst), .error_set_has_value => cg.airErrorSetHasValue(inst),
.frame_addr => cg.airFrameAddress(inst), .frame_addr => cg.airFrameAddress(inst),
.tlv_dllimport_ptr => cg.airTlvDllimportPtr(inst), .runtime_nav_ptr => cg.airRuntimeNavPtr(inst),
.assembly, .assembly,
.is_err_ptr, .is_err_ptr,
@ -7616,7 +7616,7 @@ fn airFrameAddress(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
return cg.finishAir(inst, .stack, &.{}); return cg.finishAir(inst, .stack, &.{});
} }
fn airTlvDllimportPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void { fn airRuntimeNavPtr(cg: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const ty_nav = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; const ty_nav = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav;
const mod = cg.pt.zcu.navFileScope(cg.owner_nav).mod.?; const mod = cg.pt.zcu.navFileScope(cg.owner_nav).mod.?;
if (mod.single_threaded) { if (mod.single_threaded) {

View file

@ -274,6 +274,12 @@ pub const MCValue = union(enum) {
load_symbol: bits.SymbolOffset, load_symbol: bits.SymbolOffset,
/// The address of the memory location not-yet-allocated by the linker. /// The address of the memory location not-yet-allocated by the linker.
lea_symbol: bits.SymbolOffset, lea_symbol: bits.SymbolOffset,
/// The value is in memory at an address not-yet-allocated by the linker.
/// This must use a non-got pc-relative relocation.
load_pcrel: bits.SymbolOffset,
/// The address of the memory location not-yet-allocated by the linker.
/// This must use a non-got pc-relative relocation.
lea_pcrel: bits.SymbolOffset,
/// The value is in memory at a constant offset from the address in a register. /// The value is in memory at a constant offset from the address in a register.
indirect: bits.RegisterOffset, indirect: bits.RegisterOffset,
/// The value is in memory. /// The value is in memory.
@ -314,6 +320,7 @@ pub const MCValue = union(enum) {
.eflags, .eflags,
.register_overflow, .register_overflow,
.lea_symbol, .lea_symbol,
.lea_pcrel,
.lea_direct, .lea_direct,
.lea_got, .lea_got,
.lea_frame, .lea_frame,
@ -327,6 +334,7 @@ pub const MCValue = union(enum) {
.register_quadruple, .register_quadruple,
.memory, .memory,
.load_symbol, .load_symbol,
.load_pcrel,
.load_got, .load_got,
.load_direct, .load_direct,
.indirect, .indirect,
@ -429,6 +437,7 @@ pub const MCValue = union(enum) {
.register_overflow, .register_overflow,
.register_mask, .register_mask,
.lea_symbol, .lea_symbol,
.lea_pcrel,
.lea_direct, .lea_direct,
.lea_got, .lea_got,
.lea_frame, .lea_frame,
@ -445,6 +454,7 @@ pub const MCValue = union(enum) {
.load_got => |sym_index| .{ .lea_got = sym_index }, .load_got => |sym_index| .{ .lea_got = sym_index },
.load_frame => |frame_addr| .{ .lea_frame = frame_addr }, .load_frame => |frame_addr| .{ .lea_frame = frame_addr },
.load_symbol => |sym_off| .{ .lea_symbol = sym_off }, .load_symbol => |sym_off| .{ .lea_symbol = sym_off },
.load_pcrel => |sym_off| .{ .lea_pcrel = sym_off },
}; };
} }
@ -466,6 +476,7 @@ pub const MCValue = union(enum) {
.load_got, .load_got,
.load_frame, .load_frame,
.load_symbol, .load_symbol,
.load_pcrel,
.elementwise_args, .elementwise_args,
.reserved_frame, .reserved_frame,
.air_ref, .air_ref,
@ -477,6 +488,7 @@ pub const MCValue = union(enum) {
.lea_got => |sym_index| .{ .load_got = sym_index }, .lea_got => |sym_index| .{ .load_got = sym_index },
.lea_frame => |frame_addr| .{ .load_frame = frame_addr }, .lea_frame => |frame_addr| .{ .load_frame = frame_addr },
.lea_symbol => |sym_index| .{ .load_symbol = sym_index }, .lea_symbol => |sym_index| .{ .load_symbol = sym_index },
.lea_pcrel => |sym_index| .{ .load_pcrel = sym_index },
}; };
} }
@ -505,6 +517,8 @@ pub const MCValue = union(enum) {
.load_frame, .load_frame,
.load_symbol, .load_symbol,
.lea_symbol, .lea_symbol,
.load_pcrel,
.lea_pcrel,
=> switch (off) { => switch (off) {
0 => mcv, 0 => mcv,
else => unreachable, // not offsettable else => unreachable, // not offsettable
@ -543,6 +557,7 @@ pub const MCValue = union(enum) {
.elementwise_args, .elementwise_args,
.reserved_frame, .reserved_frame,
.lea_symbol, .lea_symbol,
.lea_pcrel,
=> unreachable, => unreachable,
.memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| .{ .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| .{
.base = .{ .reg = .ds }, .base = .{ .reg = .ds },
@ -583,6 +598,18 @@ pub const MCValue = union(enum) {
} }, } },
}; };
}, },
.load_pcrel => |sym_off| {
assert(sym_off.off == 0);
return .{
.base = .{ .pcrel = sym_off.sym_index },
.mod = .{ .rm = .{
.size = mod_rm.size,
.index = mod_rm.index,
.scale = mod_rm.scale,
.disp = sym_off.off + mod_rm.disp,
} },
};
},
.air_ref => |ref| (try function.resolveInst(ref)).mem(function, mod_rm), .air_ref => |ref| (try function.resolveInst(ref)).mem(function, mod_rm),
}; };
} }
@ -618,6 +645,8 @@ pub const MCValue = union(enum) {
}), }),
.load_symbol => |pl| try writer.print("[sym:{} + 0x{x}]", .{ pl.sym_index, pl.off }), .load_symbol => |pl| try writer.print("[sym:{} + 0x{x}]", .{ pl.sym_index, pl.off }),
.lea_symbol => |pl| try writer.print("sym:{} + 0x{x}", .{ pl.sym_index, pl.off }), .lea_symbol => |pl| try writer.print("sym:{} + 0x{x}", .{ pl.sym_index, pl.off }),
.load_pcrel => |pl| try writer.print("[sym@pcrel:{} + 0x{x}]", .{ pl.sym_index, pl.off }),
.lea_pcrel => |pl| try writer.print("sym@pcrel:{} + 0x{x}", .{ pl.sym_index, pl.off }),
.indirect => |pl| try writer.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }), .indirect => |pl| try writer.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }),
.load_direct => |pl| try writer.print("[direct:{d}]", .{pl}), .load_direct => |pl| try writer.print("[direct:{d}]", .{pl}),
.lea_direct => |pl| try writer.print("direct:{d}", .{pl}), .lea_direct => |pl| try writer.print("direct:{d}", .{pl}),
@ -655,6 +684,8 @@ const InstTracking = struct {
.lea_frame, .lea_frame,
.load_symbol, .load_symbol,
.lea_symbol, .lea_symbol,
.load_pcrel,
.lea_pcrel,
=> result, => result,
.dead, .dead,
.elementwise_args, .elementwise_args,
@ -755,6 +786,8 @@ const InstTracking = struct {
.lea_frame, .lea_frame,
.load_symbol, .load_symbol,
.lea_symbol, .lea_symbol,
.load_pcrel,
.lea_pcrel,
=> assert(std.meta.eql(self.long, target.long)), => assert(std.meta.eql(self.long, target.long)),
.dead, .dead,
.eflags, .eflags,
@ -1228,12 +1261,7 @@ fn formatAir(
_: std.fmt.FormatOptions, _: std.fmt.FormatOptions,
writer: anytype, writer: anytype,
) @TypeOf(writer).Error!void { ) @TypeOf(writer).Error!void {
@import("../../print_air.zig").dumpInst( data.self.air.dumpInst(data.inst, data.self.pt, data.self.liveness);
data.inst,
data.self.pt,
data.self.air,
data.self.liveness,
);
} }
fn fmtAir(self: *CodeGen, inst: Air.Inst.Index) std.fmt.Formatter(formatAir) { fn fmtAir(self: *CodeGen, inst: Air.Inst.Index) std.fmt.Formatter(formatAir) {
return .{ .data = .{ .self = self, .inst = inst } }; return .{ .data = .{ .self = self, .inst = inst } };
@ -163487,31 +163515,49 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
}; };
for (ops) |op| try op.die(cg); for (ops) |op| try op.die(cg);
}, },
.tlv_dllimport_ptr => switch (cg.bin_file.tag) { .runtime_nav_ptr => switch (cg.bin_file.tag) {
.elf, .macho => { .elf, .macho => {
const ty_nav = air_datas[@intFromEnum(inst)].ty_nav; const ty_nav = air_datas[@intFromEnum(inst)].ty_nav;
const nav = ip.getNav(ty_nav.nav); const nav = ip.getNav(ty_nav.nav);
const tlv_sym_index = sym: { const sym_index, const relocation = sym: {
if (cg.bin_file.cast(.elf)) |elf_file| { if (cg.bin_file.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?; const zo = elf_file.zigObjectPtr().?;
if (nav.getExtern(ip)) |e| { if (nav.getExtern(ip)) |e| {
const sym = try elf_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip)); const sym = try elf_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip));
zo.symbol(sym).flags.is_extern_ptr = true; linkage: switch (e.linkage) {
break :sym sym; .internal => {},
} .strong => switch (e.visibility) {
break :sym try zo.getOrCreateMetadataForNav(zcu, ty_nav.nav); .default => zo.symbol(sym).flags.is_extern_ptr = true,
} .hidden, .protected => {},
if (cg.bin_file.cast(.macho)) |macho_file| { },
.weak => {
zo.symbol(sym).flags.weak = true;
continue :linkage .strong;
},
.link_once => unreachable,
}
break :sym .{ sym, e.relocation };
} else break :sym .{ try zo.getOrCreateMetadataForNav(zcu, ty_nav.nav), .any };
} else if (cg.bin_file.cast(.macho)) |macho_file| {
const zo = macho_file.getZigObject().?; const zo = macho_file.getZigObject().?;
if (nav.getExtern(ip)) |e| { if (nav.getExtern(ip)) |e| {
const sym = try macho_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip)); const sym = try macho_file.getGlobalSymbol(nav.name.toSlice(ip), e.lib_name.toSlice(ip));
zo.symbols.items[sym].flags.is_extern_ptr = true; linkage: switch (e.linkage) {
break :sym sym; .internal => {},
} .strong => switch (e.visibility) {
break :sym try zo.getOrCreateMetadataForNav(macho_file, ty_nav.nav); .default => zo.symbols.items[sym].flags.is_extern_ptr = true,
} .hidden, .protected => {},
unreachable; },
.weak => {
zo.symbols.items[sym].flags.weak = true;
continue :linkage .strong;
},
.link_once => unreachable,
}
break :sym .{ sym, e.relocation };
} else break :sym .{ try zo.getOrCreateMetadataForNav(macho_file, ty_nav.nav), .any };
} else unreachable;
}; };
if (cg.mod.pic) { if (cg.mod.pic) {
@ -163520,13 +163566,14 @@ fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void {
try cg.spillRegisters(&.{.rax}); try cg.spillRegisters(&.{.rax});
} }
var slot = try cg.tempInit(.usize, .{ .lea_symbol = .{ var slot = try cg.tempInit(.usize, switch (relocation) {
.sym_index = tlv_sym_index, .any => .{ .lea_symbol = .{ .sym_index = sym_index } },
} }); .pcrel => .{ .lea_pcrel = .{ .sym_index = sym_index } },
});
while (try slot.toRegClass(true, .general_purpose, cg)) {} while (try slot.toRegClass(true, .general_purpose, cg)) {}
try slot.finish(inst, &.{}, &.{}, cg); try slot.finish(inst, &.{}, &.{}, cg);
}, },
else => return cg.fail("TODO implement tlv/dllimport on {}", .{cg.bin_file.tag}), else => return cg.fail("TODO implement runtime_nav_ptr on {}", .{cg.bin_file.tag}),
}, },
.c_va_arg => try cg.airVaArg(inst), .c_va_arg => try cg.airVaArg(inst),
.c_va_copy => try cg.airVaCopy(inst), .c_va_copy => try cg.airVaCopy(inst),
@ -169189,6 +169236,7 @@ fn load(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerE
.register, .register,
.register_offset, .register_offset,
.lea_symbol, .lea_symbol,
.lea_pcrel,
.lea_direct, .lea_direct,
.lea_got, .lea_got,
.lea_frame, .lea_frame,
@ -169196,6 +169244,7 @@ fn load(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerE
.memory, .memory,
.indirect, .indirect,
.load_symbol, .load_symbol,
.load_pcrel,
.load_direct, .load_direct,
.load_got, .load_got,
.load_frame, .load_frame,
@ -169407,6 +169456,7 @@ fn store(
.register, .register,
.register_offset, .register_offset,
.lea_symbol, .lea_symbol,
.lea_pcrel,
.lea_direct, .lea_direct,
.lea_got, .lea_got,
.lea_frame, .lea_frame,
@ -169414,6 +169464,7 @@ fn store(
.memory, .memory,
.indirect, .indirect,
.load_symbol, .load_symbol,
.load_pcrel,
.load_direct, .load_direct,
.load_got, .load_got,
.load_frame, .load_frame,
@ -169883,6 +169934,7 @@ fn genUnOpMir(self: *CodeGen, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv:
.register_overflow, .register_overflow,
.register_mask, .register_mask,
.lea_symbol, .lea_symbol,
.lea_pcrel,
.lea_direct, .lea_direct,
.lea_got, .lea_got,
.lea_frame, .lea_frame,
@ -169892,7 +169944,7 @@ fn genUnOpMir(self: *CodeGen, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv:
=> unreachable, // unmodifiable destination => unreachable, // unmodifiable destination
.register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)), .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)),
.register_pair, .register_triple, .register_quadruple => unreachable, // unimplemented .register_pair, .register_triple, .register_quadruple => unreachable, // unimplemented
.memory, .load_symbol, .load_got, .load_direct => { .memory, .load_symbol, .load_pcrel, .load_got, .load_direct => {
const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
defer self.register_manager.unlockReg(addr_reg_lock); defer self.register_manager.unlockReg(addr_reg_lock);
@ -171552,6 +171604,8 @@ fn genBinOp(
.register_mask, .register_mask,
.load_symbol, .load_symbol,
.lea_symbol, .lea_symbol,
.load_pcrel,
.lea_pcrel,
.load_direct, .load_direct,
.lea_direct, .lea_direct,
.load_got, .load_got,
@ -172740,6 +172794,7 @@ fn genBinOpMir(
.lea_got, .lea_got,
.lea_frame, .lea_frame,
.lea_symbol, .lea_symbol,
.lea_pcrel,
.elementwise_args, .elementwise_args,
.reserved_frame, .reserved_frame,
.air_ref, .air_ref,
@ -172831,6 +172886,8 @@ fn genBinOpMir(
.indirect, .indirect,
.load_symbol, .load_symbol,
.lea_symbol, .lea_symbol,
.load_pcrel,
.lea_pcrel,
.load_direct, .load_direct,
.lea_direct, .lea_direct,
.load_got, .load_got,
@ -172906,7 +172963,7 @@ fn genBinOpMir(
} }
} }
}, },
.memory, .indirect, .load_symbol, .load_got, .load_direct, .load_frame => { .memory, .indirect, .load_symbol, .load_pcrel, .load_got, .load_direct, .load_frame => {
const OpInfo = ?struct { addr_reg: Register, addr_lock: RegisterLock }; const OpInfo = ?struct { addr_reg: Register, addr_lock: RegisterLock };
const limb_abi_size: u32 = @min(abi_size, 8); const limb_abi_size: u32 = @min(abi_size, 8);
@ -172953,8 +173010,9 @@ fn genBinOpMir(
.load_frame, .load_frame,
.lea_frame, .lea_frame,
.lea_symbol, .lea_symbol,
.lea_pcrel,
=> null, => null,
.memory, .load_symbol, .load_got, .load_direct => src: { .memory, .load_symbol, .load_pcrel, .load_got, .load_direct => src: {
switch (resolved_src_mcv) { switch (resolved_src_mcv) {
.memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr))) != null and .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr))) != null and
std.math.cast(i32, @as(i64, @bitCast(addr)) + abi_size - limb_abi_size) != null) std.math.cast(i32, @as(i64, @bitCast(addr)) + abi_size - limb_abi_size) != null)
@ -173093,6 +173151,8 @@ fn genBinOpMir(
.indirect, .indirect,
.load_symbol, .load_symbol,
.lea_symbol, .lea_symbol,
.load_pcrel,
.lea_pcrel,
.load_direct, .load_direct,
.lea_direct, .lea_direct,
.load_got, .load_got,
@ -173160,6 +173220,7 @@ fn genIntMulComplexOpMir(self: *CodeGen, dst_ty: Type, dst_mcv: MCValue, src_mcv
.register_overflow, .register_overflow,
.register_mask, .register_mask,
.lea_symbol, .lea_symbol,
.lea_pcrel,
.lea_direct, .lea_direct,
.lea_got, .lea_got,
.lea_frame, .lea_frame,
@ -173222,6 +173283,8 @@ fn genIntMulComplexOpMir(self: *CodeGen, dst_ty: Type, dst_mcv: MCValue, src_mcv
.eflags, .eflags,
.load_symbol, .load_symbol,
.lea_symbol, .lea_symbol,
.load_pcrel,
.lea_pcrel,
.load_direct, .load_direct,
.lea_direct, .lea_direct,
.load_got, .load_got,
@ -173281,7 +173344,7 @@ fn genIntMulComplexOpMir(self: *CodeGen, dst_ty: Type, dst_mcv: MCValue, src_mcv
} }
}, },
.register_pair, .register_triple, .register_quadruple => unreachable, // unimplemented .register_pair, .register_triple, .register_quadruple => unreachable, // unimplemented
.memory, .indirect, .load_symbol, .load_direct, .load_got, .load_frame => { .memory, .indirect, .load_symbol, .load_pcrel, .load_direct, .load_got, .load_frame => {
const tmp_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); const tmp_reg = try self.copyToTmpRegister(dst_ty, dst_mcv);
const tmp_mcv = MCValue{ .register = tmp_reg }; const tmp_mcv = MCValue{ .register = tmp_reg };
const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
@ -173450,7 +173513,8 @@ fn genLocalDebugInfo(
.disp = frame_addr.off, .disp = frame_addr.off,
} }, } },
}), }),
.lea_symbol => |sym_off| try self.asmAirMemory(.dbg_local, inst, .{ // debug info should explicitly ignore pcrel requirements
.lea_symbol, .lea_pcrel => |sym_off| try self.asmAirMemory(.dbg_local, inst, .{
.base = .{ .reloc = sym_off.sym_index }, .base = .{ .reloc = sym_off.sym_index },
.mod = .{ .rm = .{ .mod = .{ .rm = .{
.size = .qword, .size = .qword,
@ -174108,12 +174172,13 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v
.lea_got, .lea_got,
.lea_frame, .lea_frame,
.lea_symbol, .lea_symbol,
.lea_pcrel,
.elementwise_args, .elementwise_args,
.reserved_frame, .reserved_frame,
.air_ref, .air_ref,
=> unreachable, => unreachable,
.register, .register_pair, .register_triple, .register_quadruple, .load_frame => null, .register, .register_pair, .register_triple, .register_quadruple, .load_frame => null,
.memory, .load_symbol, .load_got, .load_direct => dst: { .memory, .load_symbol, .load_pcrel, .load_got, .load_direct => dst: {
switch (resolved_dst_mcv) { switch (resolved_dst_mcv) {
.memory => |addr| if (std.math.cast( .memory => |addr| if (std.math.cast(
i32, i32,
@ -174122,7 +174187,7 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v
i32, i32,
@as(i64, @bitCast(addr)) + abi_size - 8, @as(i64, @bitCast(addr)) + abi_size - 8,
) != null) break :dst null, ) != null) break :dst null,
.load_symbol, .load_got, .load_direct => {}, .load_symbol, .load_pcrel, .load_got, .load_direct => {},
else => unreachable, else => unreachable,
} }
@ -174160,6 +174225,7 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v
.register_mask, .register_mask,
.indirect, .indirect,
.lea_symbol, .lea_symbol,
.lea_pcrel,
.lea_direct, .lea_direct,
.lea_got, .lea_got,
.lea_frame, .lea_frame,
@ -174168,7 +174234,7 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v
.air_ref, .air_ref,
=> unreachable, => unreachable,
.register_pair, .register_triple, .register_quadruple, .load_frame => null, .register_pair, .register_triple, .register_quadruple, .load_frame => null,
.memory, .load_symbol, .load_got, .load_direct => src: { .memory, .load_symbol, .load_pcrel, .load_got, .load_direct => src: {
switch (resolved_src_mcv) { switch (resolved_src_mcv) {
.memory => |addr| if (std.math.cast( .memory => |addr| if (std.math.cast(
i32, i32,
@ -174177,7 +174243,7 @@ fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !v
i32, i32,
@as(i64, @bitCast(addr)) + abi_size - 8, @as(i64, @bitCast(addr)) + abi_size - 8,
) != null) break :src null, ) != null) break :src null,
.load_symbol, .load_got, .load_direct => {}, .load_symbol, .load_pcrel, .load_got, .load_direct => {},
else => unreachable, else => unreachable,
} }
@ -174568,6 +174634,7 @@ fn isNull(self: *CodeGen, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue)
.lea_direct, .lea_direct,
.lea_got, .lea_got,
.lea_symbol, .lea_symbol,
.lea_pcrel,
.elementwise_args, .elementwise_args,
.reserved_frame, .reserved_frame,
.air_ref, .air_ref,
@ -174616,6 +174683,7 @@ fn isNull(self: *CodeGen, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue)
.memory, .memory,
.load_symbol, .load_symbol,
.load_pcrel,
.load_got, .load_got,
.load_direct, .load_direct,
=> { => {
@ -176625,6 +176693,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C
.lea_got, .lea_got,
.lea_frame, .lea_frame,
.lea_symbol, .lea_symbol,
.lea_pcrel,
.elementwise_args, .elementwise_args,
.reserved_frame, .reserved_frame,
.air_ref, .air_ref,
@ -176719,7 +176788,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C
} }
return; return;
}, },
.load_symbol, .load_direct, .load_got => { .load_symbol, .load_pcrel, .load_direct, .load_got => {
const src_addr_reg = const src_addr_reg =
(try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64(); (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64();
const src_addr_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg); const src_addr_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg);
@ -176752,7 +176821,7 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C
.undef => if (opts.safety and part_i > 0) .{ .register = dst_regs[0] } else .undef, .undef => if (opts.safety and part_i > 0) .{ .register = dst_regs[0] } else .undef,
dst_tag => |src_regs| .{ .register = src_regs[part_i] }, dst_tag => |src_regs| .{ .register = src_regs[part_i] },
.memory, .indirect, .load_frame => src_mcv.address().offset(part_disp).deref(), .memory, .indirect, .load_frame => src_mcv.address().offset(part_disp).deref(),
.load_symbol, .load_direct, .load_got => .{ .indirect = .{ .load_symbol, .load_pcrel, .load_direct, .load_got => .{ .indirect = .{
.reg = src_info.?.addr_reg, .reg = src_info.?.addr_reg,
.off = part_disp, .off = part_disp,
} }, } },
@ -176773,11 +176842,11 @@ fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: C
src_mcv, src_mcv,
opts, opts,
), ),
.memory, .load_symbol, .load_direct, .load_got => { .memory, .load_symbol, .load_pcrel, .load_direct, .load_got => {
switch (dst_mcv) { switch (dst_mcv) {
.memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr|
return self.genSetMem(.{ .reg = .ds }, small_addr, ty, src_mcv, opts), return self.genSetMem(.{ .reg = .ds }, small_addr, ty, src_mcv, opts),
.load_symbol, .load_direct, .load_got => {}, .load_symbol, .load_pcrel, .load_direct, .load_got => {},
else => unreachable, else => unreachable,
} }
@ -177234,7 +177303,7 @@ fn genSetReg(
if (src_reg_mask.info.inverted) try self.asmRegister(.{ ._, .not }, registerAlias(bits_reg, abi_size)); if (src_reg_mask.info.inverted) try self.asmRegister(.{ ._, .not }, registerAlias(bits_reg, abi_size));
try self.genSetReg(dst_reg, ty, .{ .register = bits_reg }, .{}); try self.genSetReg(dst_reg, ty, .{ .register = bits_reg }, .{});
}, },
.memory, .load_symbol, .load_direct, .load_got => { .memory, .load_symbol, .load_pcrel, .load_direct, .load_got => {
switch (src_mcv) { switch (src_mcv) {
.memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr|
return (try self.moveStrategy( return (try self.moveStrategy(
@ -177263,6 +177332,21 @@ fn genSetReg(
.segment, .mmx, .ip, .cr, .dr => unreachable, .segment, .mmx, .ip, .cr, .dr => unreachable,
.x87, .sse => {}, .x87, .sse => {},
}, },
.load_pcrel => |sym_off| switch (dst_reg.class()) {
.general_purpose, .gphi => {
assert(sym_off.off == 0);
try self.asmRegisterMemory(.{ ._, .mov }, dst_alias, .{
.base = .{ .pcrel = sym_off.sym_index },
.mod = .{ .rm = .{
.size = self.memSize(ty),
.disp = sym_off.off,
} },
});
return;
},
.segment, .mmx, .ip, .cr, .dr => unreachable,
.x87, .sse => {},
},
.load_direct => |sym_index| switch (dst_reg.class()) { .load_direct => |sym_index| switch (dst_reg.class()) {
.general_purpose, .gphi => { .general_purpose, .gphi => {
_ = try self.addInst(.{ _ = try self.addInst(.{
@ -177313,6 +177397,28 @@ fn genSetReg(
@tagName(self.bin_file.tag), @tagName(self.bin_file.tag),
}), }),
}, },
.lea_pcrel => |sym_off| switch (self.bin_file.tag) {
.elf, .macho => {
try self.asmRegisterMemory(
.{ ._, .lea },
dst_reg.to64(),
.{
.base = .{ .pcrel = sym_off.sym_index },
},
);
if (sym_off.off != 0) try self.asmRegisterMemory(
.{ ._, .lea },
dst_reg.to64(),
.{
.base = .{ .reg = dst_reg.to64() },
.mod = .{ .rm = .{ .disp = sym_off.off } },
},
);
},
else => return self.fail("TODO emit symbol sequence on {s}", .{
@tagName(self.bin_file.tag),
}),
},
.lea_direct, .lea_got => |sym_index| _ = try self.addInst(.{ .lea_direct, .lea_got => |sym_index| _ = try self.addInst(.{
.tag = switch (src_mcv) { .tag = switch (src_mcv) {
.lea_direct => .lea, .lea_direct => .lea,
@ -177350,6 +177456,7 @@ fn genSetMem(
.frame => |base_frame_index| .{ .lea_frame = .{ .index = base_frame_index, .off = disp } }, .frame => |base_frame_index| .{ .lea_frame = .{ .index = base_frame_index, .off = disp } },
.table, .rip_inst => unreachable, .table, .rip_inst => unreachable,
.reloc => |sym_index| .{ .lea_symbol = .{ .sym_index = sym_index, .off = disp } }, .reloc => |sym_index| .{ .lea_symbol = .{ .sym_index = sym_index, .off = disp } },
.pcrel => |sym_index| .{ .lea_pcrel = .{ .sym_index = sym_index, .off = disp } },
}; };
switch (src_mcv) { switch (src_mcv) {
.none, .none,
@ -177466,7 +177573,7 @@ fn genSetMem(
.off = disp, .off = disp,
}).compare(.gte, src_align), }).compare(.gte, src_align),
.table, .rip_inst => unreachable, .table, .rip_inst => unreachable,
.reloc => false, .reloc, .pcrel => false,
})).write( })).write(
self, self,
.{ .base = base, .mod = .{ .rm = .{ .{ .base = base, .mod = .{ .rm = .{
@ -177557,6 +177664,8 @@ fn genSetMem(
.lea_frame, .lea_frame,
.load_symbol, .load_symbol,
.lea_symbol, .lea_symbol,
.load_pcrel,
.lea_pcrel,
=> switch (abi_size) { => switch (abi_size) {
0 => {}, 0 => {},
1, 2, 4, 8 => { 1, 2, 4, 8 => {
@ -178110,7 +178219,7 @@ fn airCmpxchg(self: *CodeGen, inst: Air.Inst.Index) !void {
.off => return self.fail("TODO airCmpxchg with {s}", .{@tagName(ptr_mcv)}), .off => return self.fail("TODO airCmpxchg with {s}", .{@tagName(ptr_mcv)}),
} }
const ptr_lock = switch (ptr_mem.base) { const ptr_lock = switch (ptr_mem.base) {
.none, .frame, .reloc => null, .none, .frame, .reloc, .pcrel => null,
.reg => |reg| self.register_manager.lockReg(reg), .reg => |reg| self.register_manager.lockReg(reg),
.table, .rip_inst => unreachable, .table, .rip_inst => unreachable,
}; };
@ -178193,7 +178302,7 @@ fn atomicOp(
.off => return self.fail("TODO airCmpxchg with {s}", .{@tagName(ptr_mcv)}), .off => return self.fail("TODO airCmpxchg with {s}", .{@tagName(ptr_mcv)}),
} }
const mem_lock = switch (ptr_mem.base) { const mem_lock = switch (ptr_mem.base) {
.none, .frame, .reloc => null, .none, .frame, .reloc, .pcrel => null,
.reg => |reg| self.register_manager.lockReg(reg), .reg => |reg| self.register_manager.lockReg(reg),
.table, .rip_inst => unreachable, .table, .rip_inst => unreachable,
}; };
@ -182266,6 +182375,8 @@ const Temp = struct {
.memory, .memory,
.load_symbol, .load_symbol,
.lea_symbol, .lea_symbol,
.load_pcrel,
.lea_pcrel,
.indirect, .indirect,
.load_direct, .load_direct,
.lea_direct, .lea_direct,
@ -182427,6 +182538,22 @@ const Temp = struct {
assert(limb_index == 0); assert(limb_index == 0);
new_temp_index.tracking(cg).* = .init(.{ .lea_symbol = sym_off }); new_temp_index.tracking(cg).* = .init(.{ .lea_symbol = sym_off });
}, },
.load_pcrel => |sym_off| {
const new_reg =
try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp);
new_temp_index.tracking(cg).* = .init(.{ .register = new_reg });
try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{
.base = .{ .pcrel = sym_off.sym_index },
.mod = .{ .rm = .{
.size = .qword,
.disp = sym_off.off + @as(u31, limb_index) * 8,
} },
});
},
.lea_pcrel => |sym_off| {
assert(limb_index == 0);
new_temp_index.tracking(cg).* = .init(.{ .lea_pcrel = sym_off });
},
.load_frame => |frame_addr| { .load_frame => |frame_addr| {
const new_reg = const new_reg =
try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp);
@ -182721,11 +182848,12 @@ const Temp = struct {
.memory, .memory,
.indirect, .indirect,
.load_symbol, .load_symbol,
.load_pcrel,
.load_direct, .load_direct,
.load_got, .load_got,
.load_frame, .load_frame,
=> return temp.toRegClass(true, .general_purpose, cg), => return temp.toRegClass(true, .general_purpose, cg),
.lea_symbol => |sym_off| { .lea_symbol, .lea_pcrel => |sym_off| {
const off = sym_off.off; const off = sym_off.off;
// hack around linker relocation bugs // hack around linker relocation bugs
if (false and off == 0) return false; if (false and off == 0) return false;
@ -187464,6 +187592,8 @@ const Temp = struct {
.memory, .memory,
.load_symbol, .load_symbol,
.lea_symbol, .lea_symbol,
.load_pcrel,
.lea_pcrel,
.indirect, .indirect,
.load_direct, .load_direct,
.lea_direct, .lea_direct,
@ -190044,6 +190174,7 @@ const Select = struct {
.register => |base_reg| .{ .reg = base_reg.toSize(.ptr, s.cg.target) }, .register => |base_reg| .{ .reg = base_reg.toSize(.ptr, s.cg.target) },
.register_offset => |base_reg_off| .{ .reg = base_reg_off.reg.toSize(.ptr, s.cg.target) }, .register_offset => |base_reg_off| .{ .reg = base_reg_off.reg.toSize(.ptr, s.cg.target) },
.lea_symbol => |base_sym_off| .{ .reloc = base_sym_off.sym_index }, .lea_symbol => |base_sym_off| .{ .reloc = base_sym_off.sym_index },
.lea_pcrel => |base_sym_off| .{ .pcrel = base_sym_off.sym_index },
}, },
.mod = .{ .rm = .{ .mod = .{ .rm = .{
.size = op.flags.base.size, .size = op.flags.base.size,

View file

@ -189,12 +189,12 @@ pub fn emitMir(emit: *Emit) Error!void {
.r_addend = lowered_relocs[0].off, .r_addend = lowered_relocs[0].off,
}, zo); }, zo);
}, },
.linker_reloc => |sym_index| if (emit.lower.bin_file.cast(.elf)) |elf_file| { .linker_reloc, .linker_pcrel => |sym_index| if (emit.lower.bin_file.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?; const zo = elf_file.zigObjectPtr().?;
const atom = zo.symbol(emit.atom_index).atom(elf_file).?; const atom = zo.symbol(emit.atom_index).atom(elf_file).?;
const sym = zo.symbol(sym_index); const sym = zo.symbol(sym_index);
if (emit.lower.pic) { if (emit.lower.pic) {
const r_type: u32 = if (sym.flags.is_extern_ptr) const r_type: u32 = if (sym.flags.is_extern_ptr and lowered_relocs[0].target != .linker_pcrel)
@intFromEnum(std.elf.R_X86_64.GOTPCREL) @intFromEnum(std.elf.R_X86_64.GOTPCREL)
else else
@intFromEnum(std.elf.R_X86_64.PC32); @intFromEnum(std.elf.R_X86_64.PC32);
@ -218,7 +218,7 @@ pub fn emitMir(emit: *Emit) Error!void {
const zo = macho_file.getZigObject().?; const zo = macho_file.getZigObject().?;
const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?; const atom = zo.symbols.items[emit.atom_index].getAtom(macho_file).?;
const sym = &zo.symbols.items[sym_index]; const sym = &zo.symbols.items[sym_index];
const @"type": link.File.MachO.Relocation.Type = if (sym.flags.is_extern_ptr) const @"type": link.File.MachO.Relocation.Type = if (sym.flags.is_extern_ptr and lowered_relocs[0].target != .linker_pcrel)
.got_load .got_load
else if (sym.flags.tlv) else if (sym.flags.tlv)
.tlv .tlv
@ -438,6 +438,7 @@ pub fn emitMir(emit: *Emit) Error!void {
.reg => |reg| .{ .breg = reg.dwarfNum() }, .reg => |reg| .{ .breg = reg.dwarfNum() },
.frame, .table, .rip_inst => unreachable, .frame, .table, .rip_inst => unreachable,
.reloc => |sym_index| .{ .addr_reloc = sym_index }, .reloc => |sym_index| .{ .addr_reloc = sym_index },
.pcrel => unreachable,
}; };
break :base &loc_buf[0]; break :base &loc_buf[0];
}, },

View file

@ -66,6 +66,7 @@ pub const Reloc = struct {
inst: Mir.Inst.Index, inst: Mir.Inst.Index,
table, table,
linker_reloc: u32, linker_reloc: u32,
linker_pcrel: u32,
linker_tlsld: u32, linker_tlsld: u32,
linker_dtpoff: u32, linker_dtpoff: u32,
linker_extern_fn: u32, linker_extern_fn: u32,
@ -421,9 +422,9 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
for (emit_ops, ops, 0..) |*emit_op, op, op_index| { for (emit_ops, ops, 0..) |*emit_op, op, op_index| {
emit_op.* = switch (op) { emit_op.* = switch (op) {
else => op, else => op,
.mem => |mem_op| switch (mem_op.base()) { .mem => |mem_op| op: switch (mem_op.base()) {
else => op, else => op,
.reloc => |sym_index| op: { .reloc => |sym_index| {
assert(prefix == .none); assert(prefix == .none);
assert(mem_op.sib.disp == 0); assert(mem_op.sib.disp == 0);
assert(mem_op.sib.scale_index.scale == 0); assert(mem_op.sib.scale_index.scale == 0);
@ -559,6 +560,22 @@ fn emit(lower: *Lower, prefix: Prefix, mnemonic: Mnemonic, ops: []const Operand)
return lower.fail("TODO: bin format '{s}'", .{@tagName(lower.bin_file.tag)}); return lower.fail("TODO: bin format '{s}'", .{@tagName(lower.bin_file.tag)});
} }
}, },
.pcrel => |sym_index| {
assert(prefix == .none);
assert(mem_op.sib.disp == 0);
assert(mem_op.sib.scale_index.scale == 0);
_ = lower.reloc(@intCast(op_index), .{ .linker_pcrel = sym_index }, 0);
break :op switch (lower.bin_file.tag) {
.elf => op,
.macho => switch (mnemonic) {
.lea => .{ .mem = Memory.initRip(.none, 0) },
.mov => .{ .mem = Memory.initRip(mem_op.sib.ptr_size, 0) },
else => unreachable,
},
else => |tag| return lower.fail("TODO: bin format '{s}'", .{@tagName(tag)}),
};
},
}, },
}; };
} }

View file

@ -1866,7 +1866,7 @@ pub const Memory = struct {
.none, .table => undefined, .none, .table => undefined,
.reg => |reg| @intFromEnum(reg), .reg => |reg| @intFromEnum(reg),
.frame => |frame_index| @intFromEnum(frame_index), .frame => |frame_index| @intFromEnum(frame_index),
.reloc => |sym_index| sym_index, .reloc, .pcrel => |sym_index| sym_index,
.rip_inst => |inst_index| inst_index, .rip_inst => |inst_index| inst_index,
}, },
.off = switch (mem.mod) { .off = switch (mem.mod) {
@ -1895,6 +1895,7 @@ pub const Memory = struct {
.frame => .{ .frame = @enumFromInt(mem.base) }, .frame => .{ .frame = @enumFromInt(mem.base) },
.table => .table, .table => .table,
.reloc => .{ .reloc = mem.base }, .reloc => .{ .reloc = mem.base },
.pcrel => .{ .pcrel = mem.base },
.rip_inst => .{ .rip_inst = mem.base }, .rip_inst => .{ .rip_inst = mem.base },
}, },
.scale_index = switch (mem.info.index) { .scale_index = switch (mem.info.index) {
@ -1959,7 +1960,7 @@ pub fn resolveFrameAddr(mir: Mir, frame_addr: bits.FrameAddr) bits.RegisterOffse
pub fn resolveFrameLoc(mir: Mir, mem: Memory) Memory { pub fn resolveFrameLoc(mir: Mir, mem: Memory) Memory {
return switch (mem.info.base) { return switch (mem.info.base) {
.none, .reg, .table, .reloc, .rip_inst => mem, .none, .reg, .table, .reloc, .pcrel, .rip_inst => mem,
.frame => if (mir.frame_locs.len > 0) .{ .frame => if (mir.frame_locs.len > 0) .{
.info = .{ .info = .{
.base = .reg, .base = .reg,

View file

@ -762,6 +762,7 @@ pub const Memory = struct {
frame: FrameIndex, frame: FrameIndex,
table, table,
reloc: u32, reloc: u32,
pcrel: u32,
rip_inst: Mir.Inst.Index, rip_inst: Mir.Inst.Index,
pub const Tag = @typeInfo(Base).@"union".tag_type.?; pub const Tag = @typeInfo(Base).@"union".tag_type.?;

View file

@ -138,7 +138,7 @@ pub const Instruction = struct {
.moffs => true, .moffs => true,
.rip => false, .rip => false,
.sib => |s| switch (s.base) { .sib => |s| switch (s.base) {
.none, .frame, .table, .reloc, .rip_inst => false, .none, .frame, .table, .reloc, .pcrel, .rip_inst => false,
.reg => |reg| reg.isClass(.segment), .reg => |reg| reg.isClass(.segment),
}, },
}; };
@ -211,7 +211,7 @@ pub const Instruction = struct {
.none, .imm => 0b00, .none, .imm => 0b00,
.reg => |reg| @truncate(reg.enc() >> 3), .reg => |reg| @truncate(reg.enc() >> 3),
.mem => |mem| switch (mem.base()) { .mem => |mem| switch (mem.base()) {
.none, .frame, .table, .reloc, .rip_inst => 0b00, // rsp, rbp, and rip are not extended .none, .frame, .table, .reloc, .pcrel, .rip_inst => 0b00, // rsp, rbp, and rip are not extended
.reg => |reg| @truncate(reg.enc() >> 3), .reg => |reg| @truncate(reg.enc() >> 3),
}, },
.bytes => unreachable, .bytes => unreachable,
@ -282,6 +282,7 @@ pub const Instruction = struct {
.frame => |frame_index| try writer.print("{}", .{frame_index}), .frame => |frame_index| try writer.print("{}", .{frame_index}),
.table => try writer.print("Table", .{}), .table => try writer.print("Table", .{}),
.reloc => |sym_index| try writer.print("Symbol({d})", .{sym_index}), .reloc => |sym_index| try writer.print("Symbol({d})", .{sym_index}),
.pcrel => |sym_index| try writer.print("PcRelSymbol({d})", .{sym_index}),
.rip_inst => |inst_index| try writer.print("RipInst({d})", .{inst_index}), .rip_inst => |inst_index| try writer.print("RipInst({d})", .{inst_index}),
} }
if (mem.scaleIndex()) |si| { if (mem.scaleIndex()) |si| {
@ -721,7 +722,7 @@ pub const Instruction = struct {
try encoder.modRm_indirectDisp32(operand_enc, 0); try encoder.modRm_indirectDisp32(operand_enc, 0);
try encoder.disp32(undefined); try encoder.disp32(undefined);
} else return error.CannotEncode, } else return error.CannotEncode,
.rip_inst => { .pcrel, .rip_inst => {
try encoder.modRm_RIPDisp32(operand_enc); try encoder.modRm_RIPDisp32(operand_enc);
try encoder.disp32(sib.disp); try encoder.disp32(sib.disp);
}, },

View file

@ -921,41 +921,74 @@ fn genNavRef(
const nav = ip.getNav(nav_index); const nav = ip.getNav(nav_index);
assert(!nav.isThreadlocal(ip)); assert(!nav.isThreadlocal(ip));
const is_extern, const lib_name = if (nav.getExtern(ip)) |e| const lib_name, const linkage, const visibility = if (nav.getExtern(ip)) |e|
.{ true, e.lib_name } .{ e.lib_name, e.linkage, e.visibility }
else else
.{ false, .none }; .{ .none, .internal, .default };
const name = nav.name; const name = nav.name;
if (lf.cast(.elf)) |elf_file| { if (lf.cast(.elf)) |elf_file| {
const zo = elf_file.zigObjectPtr().?; const zo = elf_file.zigObjectPtr().?;
if (is_extern) { switch (linkage) {
const sym_index = try elf_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip)); .internal => {
zo.symbol(sym_index).flags.is_extern_ptr = true; const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index);
return .{ .mcv = .{ .lea_symbol = sym_index } }; return .{ .mcv = .{ .lea_symbol = sym_index } };
},
.strong, .weak => {
const sym_index = try elf_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip));
switch (linkage) {
.internal => unreachable,
.strong => {},
.weak => zo.symbol(sym_index).flags.weak = true,
.link_once => unreachable,
}
switch (visibility) {
.default => zo.symbol(sym_index).flags.is_extern_ptr = true,
.hidden, .protected => {},
}
return .{ .mcv = .{ .lea_symbol = sym_index } };
},
.link_once => unreachable,
} }
const sym_index = try zo.getOrCreateMetadataForNav(zcu, nav_index);
return .{ .mcv = .{ .lea_symbol = sym_index } };
} else if (lf.cast(.macho)) |macho_file| { } else if (lf.cast(.macho)) |macho_file| {
const zo = macho_file.getZigObject().?; const zo = macho_file.getZigObject().?;
if (is_extern) { switch (linkage) {
const sym_index = try macho_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip)); .internal => {
zo.symbols.items[sym_index].flags.is_extern_ptr = true; const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index);
return .{ .mcv = .{ .lea_symbol = sym_index } }; const sym = zo.symbols.items[sym_index];
return .{ .mcv = .{ .lea_symbol = sym.nlist_idx } };
},
.strong, .weak => {
const sym_index = try macho_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip));
switch (linkage) {
.internal => unreachable,
.strong => {},
.weak => zo.symbols.items[sym_index].flags.weak = true,
.link_once => unreachable,
}
switch (visibility) {
.default => zo.symbols.items[sym_index].flags.is_extern_ptr = true,
.hidden, .protected => {},
}
return .{ .mcv = .{ .lea_symbol = sym_index } };
},
.link_once => unreachable,
} }
const sym_index = try zo.getOrCreateMetadataForNav(macho_file, nav_index);
const sym = zo.symbols.items[sym_index];
return .{ .mcv = .{ .lea_symbol = sym.nlist_idx } };
} else if (lf.cast(.coff)) |coff_file| { } else if (lf.cast(.coff)) |coff_file| {
if (is_extern) { // TODO audit this
// TODO audit this switch (linkage) {
const global_index = try coff_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip)); .internal => {
try coff_file.need_got_table.put(gpa, global_index, {}); // needs GOT const atom_index = try coff_file.getOrCreateAtomForNav(nav_index);
return .{ .mcv = .{ .load_got = link.File.Coff.global_symbol_bit | global_index } }; const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
return .{ .mcv = .{ .load_got = sym_index } };
},
.strong, .weak => {
const global_index = try coff_file.getGlobalSymbol(name.toSlice(ip), lib_name.toSlice(ip));
try coff_file.need_got_table.put(gpa, global_index, {}); // needs GOT
return .{ .mcv = .{ .load_got = link.File.Coff.global_symbol_bit | global_index } };
},
.link_once => unreachable,
} }
const atom_index = try coff_file.getOrCreateAtomForNav(nav_index);
const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?;
return .{ .mcv = .{ .load_got = sym_index } };
} else if (lf.cast(.plan9)) |p9| { } else if (lf.cast(.plan9)) |p9| {
const atom_index = try p9.seeNav(pt, nav_index); const atom_index = try p9.seeNav(pt, nav_index);
const atom = p9.getAtom(atom_index); const atom = p9.getAtom(atom_index);

View file

@ -2255,19 +2255,30 @@ pub const DeclGen = struct {
fn renderFwdDecl( fn renderFwdDecl(
dg: *DeclGen, dg: *DeclGen,
nav_index: InternPool.Nav.Index, nav_index: InternPool.Nav.Index,
flags: struct { flags: packed struct {
is_extern: bool,
is_const: bool, is_const: bool,
is_threadlocal: bool, is_threadlocal: bool,
is_weak_linkage: bool, linkage: std.builtin.GlobalLinkage,
visibility: std.builtin.SymbolVisibility,
}, },
) !void { ) !void {
const zcu = dg.pt.zcu; const zcu = dg.pt.zcu;
const ip = &zcu.intern_pool; const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index); const nav = ip.getNav(nav_index);
const fwd = dg.fwdDeclWriter(); const fwd = dg.fwdDeclWriter();
try fwd.writeAll(if (flags.is_extern) "zig_extern " else "static "); try fwd.writeAll(switch (flags.linkage) {
if (flags.is_weak_linkage) try fwd.writeAll("zig_weak_linkage "); .internal => "static ",
.strong, .weak, .link_once => "zig_extern ",
});
switch (flags.linkage) {
.internal, .strong => {},
.weak => try fwd.writeAll("zig_weak_linkage "),
.link_once => return dg.fail("TODO: CBE: implement linkonce linkage?", .{}),
}
switch (flags.linkage) {
.internal => {},
.strong, .weak, .link_once => try fwd.print("zig_visibility({s}) ", .{@tagName(flags.visibility)}),
}
if (flags.is_threadlocal and !dg.mod.single_threaded) try fwd.writeAll("zig_threadlocal "); if (flags.is_threadlocal and !dg.mod.single_threaded) try fwd.writeAll("zig_threadlocal ");
try dg.renderTypeAndName( try dg.renderTypeAndName(
fwd, fwd,
@ -2994,10 +3005,10 @@ pub fn genDecl(o: *Object) !void {
switch (ip.indexToKey(nav.status.fully_resolved.val)) { switch (ip.indexToKey(nav.status.fully_resolved.val)) {
.@"extern" => |@"extern"| { .@"extern" => |@"extern"| {
if (!ip.isFunctionType(nav_ty.toIntern())) return o.dg.renderFwdDecl(o.dg.pass.nav, .{ if (!ip.isFunctionType(nav_ty.toIntern())) return o.dg.renderFwdDecl(o.dg.pass.nav, .{
.is_extern = true,
.is_const = @"extern".is_const, .is_const = @"extern".is_const,
.is_threadlocal = @"extern".is_threadlocal, .is_threadlocal = @"extern".is_threadlocal,
.is_weak_linkage = @"extern".is_weak_linkage, .linkage = @"extern".linkage,
.visibility = @"extern".visibility,
}); });
const fwd = o.dg.fwdDeclWriter(); const fwd = o.dg.fwdDeclWriter();
@ -3016,13 +3027,12 @@ pub fn genDecl(o: *Object) !void {
}, },
.variable => |variable| { .variable => |variable| {
try o.dg.renderFwdDecl(o.dg.pass.nav, .{ try o.dg.renderFwdDecl(o.dg.pass.nav, .{
.is_extern = false,
.is_const = false, .is_const = false,
.is_threadlocal = variable.is_threadlocal, .is_threadlocal = variable.is_threadlocal,
.is_weak_linkage = variable.is_weak_linkage, .linkage = .internal,
.visibility = .default,
}); });
const w = o.writer(); const w = o.writer();
if (variable.is_weak_linkage) try w.writeAll("zig_weak_linkage ");
if (variable.is_threadlocal and !o.dg.mod.single_threaded) try w.writeAll("zig_threadlocal "); if (variable.is_threadlocal and !o.dg.mod.single_threaded) try w.writeAll("zig_threadlocal ");
if (nav.status.fully_resolved.@"linksection".toSlice(&zcu.intern_pool)) |s| if (nav.status.fully_resolved.@"linksection".toSlice(&zcu.intern_pool)) |s|
try w.print("zig_linksection({s}) ", .{fmtStringLiteral(s, null)}); try w.print("zig_linksection({s}) ", .{fmtStringLiteral(s, null)});
@ -3467,7 +3477,7 @@ fn genBodyInner(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail,
.error_set_has_value => return f.fail("TODO: C backend: implement error_set_has_value", .{}), .error_set_has_value => return f.fail("TODO: C backend: implement error_set_has_value", .{}),
.vector_store_elem => return f.fail("TODO: C backend: implement vector_store_elem", .{}), .vector_store_elem => return f.fail("TODO: C backend: implement vector_store_elem", .{}),
.tlv_dllimport_ptr => try airTlvDllimportPtr(f, inst), .runtime_nav_ptr => try airRuntimeNavPtr(f, inst),
.c_va_start => try airCVaStart(f, inst), .c_va_start => try airCVaStart(f, inst),
.c_va_arg => try airCVaArg(f, inst), .c_va_arg => try airCVaArg(f, inst),
@ -7672,7 +7682,7 @@ fn airMulAdd(f: *Function, inst: Air.Inst.Index) !CValue {
return local; return local;
} }
fn airTlvDllimportPtr(f: *Function, inst: Air.Inst.Index) !CValue { fn airRuntimeNavPtr(f: *Function, inst: Air.Inst.Index) !CValue {
const ty_nav = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; const ty_nav = f.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav;
const writer = f.object.writer(); const writer = f.object.writer();
const local = try f.allocLocal(inst, .fromInterned(ty_nav.ty)); const local = try f.allocLocal(inst, .fromInterned(ty_nav.ty));

View file

@ -2979,36 +2979,49 @@ pub const Object = struct {
const zcu = pt.zcu; const zcu = pt.zcu;
const ip = &zcu.intern_pool; const ip = &zcu.intern_pool;
const nav = ip.getNav(nav_index); const nav = ip.getNav(nav_index);
const is_extern, const is_threadlocal, const is_weak_linkage, const is_dll_import = switch (nav.status) { const linkage: std.builtin.GlobalLinkage, const visibility: Builder.Visibility, const is_threadlocal, const is_dll_import = switch (nav.status) {
.unresolved => unreachable, .unresolved => unreachable,
.fully_resolved => |r| switch (ip.indexToKey(r.val)) { .fully_resolved => |r| switch (ip.indexToKey(r.val)) {
.variable => |variable| .{ false, variable.is_threadlocal, variable.is_weak_linkage, false }, .variable => |variable| .{ .internal, .default, variable.is_threadlocal, false },
.@"extern" => |@"extern"| .{ true, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import }, .@"extern" => |@"extern"| .{ @"extern".linkage, .fromSymbolVisibility(@"extern".visibility), @"extern".is_threadlocal, @"extern".is_dll_import },
else => .{ false, false, false, false }, else => .{ .internal, .default, false, false },
}, },
// This means it's a source declaration which is not `extern`! // This means it's a source declaration which is not `extern`!
.type_resolved => |r| .{ false, r.is_threadlocal, false, false }, .type_resolved => |r| .{ .internal, .default, r.is_threadlocal, false },
}; };
const variable_index = try o.builder.addVariable( const variable_index = try o.builder.addVariable(
try o.builder.strtabString((if (is_extern) nav.name else nav.fqn).toSlice(ip)), try o.builder.strtabString(switch (linkage) {
.internal => nav.fqn,
.strong, .weak => nav.name,
.link_once => unreachable,
}.toSlice(ip)),
try o.lowerType(Type.fromInterned(nav.typeOf(ip))), try o.lowerType(Type.fromInterned(nav.typeOf(ip))),
toLlvmGlobalAddressSpace(nav.getAddrspace(), zcu.getTarget()), toLlvmGlobalAddressSpace(nav.getAddrspace(), zcu.getTarget()),
); );
gop.value_ptr.* = variable_index.ptrConst(&o.builder).global; gop.value_ptr.* = variable_index.ptrConst(&o.builder).global;
// This is needed for declarations created by `@extern`. // This is needed for declarations created by `@extern`.
if (is_extern) { switch (linkage) {
variable_index.setLinkage(.external, &o.builder); .internal => {
variable_index.setUnnamedAddr(.default, &o.builder); variable_index.setLinkage(.internal, &o.builder);
if (is_threadlocal and !zcu.navFileScope(nav_index).mod.?.single_threaded) variable_index.setUnnamedAddr(.unnamed_addr, &o.builder);
variable_index.setThreadLocal(.generaldynamic, &o.builder); },
if (is_weak_linkage) variable_index.setLinkage(.extern_weak, &o.builder); .strong, .weak => {
if (is_dll_import) variable_index.setDllStorageClass(.dllimport, &o.builder); variable_index.setLinkage(switch (linkage) {
} else { .internal => unreachable,
variable_index.setLinkage(.internal, &o.builder); .strong => .external,
variable_index.setUnnamedAddr(.unnamed_addr, &o.builder); .weak => .extern_weak,
.link_once => unreachable,
}, &o.builder);
variable_index.setUnnamedAddr(.default, &o.builder);
if (is_threadlocal and !zcu.navFileScope(nav_index).mod.?.single_threaded)
variable_index.setThreadLocal(.generaldynamic, &o.builder);
if (is_dll_import) variable_index.setDllStorageClass(.dllimport, &o.builder);
},
.link_once => unreachable,
} }
variable_index.setVisibility(visibility, &o.builder);
return variable_index; return variable_index;
} }
@ -4530,14 +4543,14 @@ pub const NavGen = struct {
const nav = ip.getNav(nav_index); const nav = ip.getNav(nav_index);
const resolved = nav.status.fully_resolved; const resolved = nav.status.fully_resolved;
const is_extern, const lib_name, const is_threadlocal, const is_weak_linkage, const is_dll_import, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) { const lib_name, const linkage, const visibility: Builder.Visibility, const is_threadlocal, const is_dll_import, const is_const, const init_val, const owner_nav = switch (ip.indexToKey(resolved.val)) {
.variable => |variable| .{ false, .none, variable.is_threadlocal, variable.is_weak_linkage, false, false, variable.init, variable.owner_nav }, .variable => |variable| .{ .none, .internal, .default, variable.is_threadlocal, false, false, variable.init, variable.owner_nav },
.@"extern" => |@"extern"| .{ true, @"extern".lib_name, @"extern".is_threadlocal, @"extern".is_weak_linkage, @"extern".is_dll_import, @"extern".is_const, .none, @"extern".owner_nav }, .@"extern" => |@"extern"| .{ @"extern".lib_name, @"extern".linkage, .fromSymbolVisibility(@"extern".visibility), @"extern".is_threadlocal, @"extern".is_dll_import, @"extern".is_const, .none, @"extern".owner_nav },
else => .{ false, .none, false, false, false, true, resolved.val, nav_index }, else => .{ .none, .internal, .default, false, false, true, resolved.val, nav_index },
}; };
const ty = Type.fromInterned(nav.typeOf(ip)); const ty = Type.fromInterned(nav.typeOf(ip));
if (is_extern and ip.isFunctionType(ty.toIntern())) { if (linkage != .internal and ip.isFunctionType(ty.toIntern())) {
_ = try o.resolveLlvmFunction(owner_nav); _ = try o.resolveLlvmFunction(owner_nav);
} else { } else {
const variable_index = try o.resolveGlobalNav(nav_index); const variable_index = try o.resolveGlobalNav(nav_index);
@ -4549,6 +4562,7 @@ pub const NavGen = struct {
.none => .no_init, .none => .no_init,
else => try o.lowerValue(init_val), else => try o.lowerValue(init_val),
}, &o.builder); }, &o.builder);
variable_index.setVisibility(visibility, &o.builder);
const file_scope = zcu.navFileScopeIndex(nav_index); const file_scope = zcu.navFileScopeIndex(nav_index);
const mod = zcu.fileByIndex(file_scope).mod.?; const mod = zcu.fileByIndex(file_scope).mod.?;
@ -4568,7 +4582,7 @@ pub const NavGen = struct {
line_number, line_number,
try o.lowerDebugType(ty), try o.lowerDebugType(ty),
variable_index, variable_index,
.{ .local = !is_extern }, .{ .local = linkage == .internal },
); );
const debug_expression = try o.builder.debugExpression(&.{}); const debug_expression = try o.builder.debugExpression(&.{});
@ -4583,38 +4597,47 @@ pub const NavGen = struct {
} }
} }
if (is_extern) { switch (linkage) {
const global_index = o.nav_map.get(nav_index).?; .internal => {},
.strong, .weak => {
const global_index = o.nav_map.get(nav_index).?;
const decl_name = decl_name: { const decl_name = decl_name: {
if (zcu.getTarget().cpu.arch.isWasm() and ty.zigTypeTag(zcu) == .@"fn") { if (zcu.getTarget().cpu.arch.isWasm() and ty.zigTypeTag(zcu) == .@"fn") {
if (lib_name.toSlice(ip)) |lib_name_slice| { if (lib_name.toSlice(ip)) |lib_name_slice| {
if (!std.mem.eql(u8, lib_name_slice, "c")) { if (!std.mem.eql(u8, lib_name_slice, "c")) {
break :decl_name try o.builder.strtabStringFmt("{}|{s}", .{ nav.name.fmt(ip), lib_name_slice }); break :decl_name try o.builder.strtabStringFmt("{}|{s}", .{ nav.name.fmt(ip), lib_name_slice });
}
} }
} }
break :decl_name try o.builder.strtabString(nav.name.toSlice(ip));
};
if (o.builder.getGlobal(decl_name)) |other_global| {
if (other_global != global_index) {
// Another global already has this name; just use it in place of this global.
try global_index.replace(other_global, &o.builder);
return;
}
} }
break :decl_name try o.builder.strtabString(nav.name.toSlice(ip));
};
if (o.builder.getGlobal(decl_name)) |other_global| { try global_index.rename(decl_name, &o.builder);
if (other_global != global_index) { global_index.setUnnamedAddr(.default, &o.builder);
// Another global already has this name; just use it in place of this global. if (is_dll_import) {
try global_index.replace(other_global, &o.builder); global_index.setDllStorageClass(.dllimport, &o.builder);
return; } else if (zcu.comp.config.dll_export_fns) {
global_index.setDllStorageClass(.default, &o.builder);
} }
}
try global_index.rename(decl_name, &o.builder); global_index.setLinkage(switch (linkage) {
global_index.setLinkage(.external, &o.builder); .internal => unreachable,
global_index.setUnnamedAddr(.default, &o.builder); .strong => .external,
if (is_dll_import) { .weak => .extern_weak,
global_index.setDllStorageClass(.dllimport, &o.builder); .link_once => unreachable,
} else if (zcu.comp.config.dll_export_fns) { }, &o.builder);
global_index.setDllStorageClass(.default, &o.builder); global_index.setVisibility(visibility, &o.builder);
} },
.link_once => unreachable,
if (is_weak_linkage) global_index.setLinkage(.extern_weak, &o.builder);
} }
} }
}; };
@ -5023,7 +5046,7 @@ pub const FuncGen = struct {
.vector_store_elem => try self.airVectorStoreElem(inst), .vector_store_elem => try self.airVectorStoreElem(inst),
.tlv_dllimport_ptr => try self.airTlvDllimportPtr(inst), .runtime_nav_ptr => try self.airRuntimeNavPtr(inst),
.inferred_alloc, .inferred_alloc_comptime => unreachable, .inferred_alloc, .inferred_alloc_comptime => unreachable,
@ -8122,7 +8145,7 @@ pub const FuncGen = struct {
return .none; return .none;
} }
fn airTlvDllimportPtr(fg: *FuncGen, inst: Air.Inst.Index) !Builder.Value { fn airRuntimeNavPtr(fg: *FuncGen, inst: Air.Inst.Index) !Builder.Value {
const o = fg.ng.object; const o = fg.ng.object;
const ty_nav = fg.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav; const ty_nav = fg.air.instructions.items(.data)[@intFromEnum(inst)].ty_nav;
const llvm_ptr_const = try o.lowerNavRefValue(ty_nav.nav); const llvm_ptr_const = try o.lowerNavRefValue(ty_nav.nav);

View file

@ -785,7 +785,6 @@ const Entry = struct {
} }
const Index = enum(u32) { const Index = enum(u32) {
//got_proc,
_, _,
const Optional = enum(u32) { const Optional = enum(u32) {
@ -1041,7 +1040,7 @@ const Entry = struct {
const symbol = zo.symbol(reloc.target_sym); const symbol = zo.symbol(reloc.target_sym);
try dwarf.resolveReloc( try dwarf.resolveReloc(
entry_off + reloc.source_off, entry_off + reloc.source_off,
@bitCast(symbol.address(.{}, elf_file) + @as(i64, @intCast(@intFromEnum(reloc.target_off))) - @bitCast(symbol.address(.{}, elf_file) + @as(i64, @intCast(reloc.target_off)) -
if (symbol.flags.is_tls) elf_file.dtpAddress() else 0), if (symbol.flags.is_tls) elf_file.dtpAddress() else 0),
@intFromEnum(dwarf.address_size), @intFromEnum(dwarf.address_size),
); );
@ -1052,7 +1051,7 @@ const Entry = struct {
const ref = zo.getSymbolRef(reloc.target_sym, macho_file); const ref = zo.getSymbolRef(reloc.target_sym, macho_file);
try dwarf.resolveReloc( try dwarf.resolveReloc(
entry_off + reloc.source_off, entry_off + reloc.source_off,
ref.getSymbol(macho_file).?.getAddress(.{}, macho_file) + @as(i64, @intCast(@intFromEnum(reloc.target_off))), ref.getSymbol(macho_file).?.getAddress(.{}, macho_file) + @as(i64, @intCast(reloc.target_off)),
@intFromEnum(dwarf.address_size), @intFromEnum(dwarf.address_size),
); );
} }
@ -1081,27 +1080,12 @@ const CrossSectionReloc = struct {
const ExternalReloc = struct { const ExternalReloc = struct {
source_off: u32 = 0, source_off: u32 = 0,
target_sym: u32, target_sym: u32,
target_off: enum(u64) { target_off: u64 = 0,
none = 0,
got = std.math.maxInt(i64) + 1,
_,
pub fn rel(off: u64) @This() {
const res: @This() = @enumFromInt(off);
switch (res) {
.none => {},
_ => {},
.got => unreachable, // assertion failure
}
return res;
}
} = .none,
}; };
pub const Loc = union(enum) { pub const Loc = union(enum) {
empty, empty,
addr_reloc: u32, addr_reloc: u32,
got_reloc: u32,
deref: *const Loc, deref: *const Loc,
constu: u64, constu: u64,
consts: i64, consts: i64,
@ -1166,10 +1150,6 @@ pub const Loc = union(enum) {
try addr.write(adapter); try addr.write(adapter);
try writer.writeByte(DW.OP.deref); try writer.writeByte(DW.OP.deref);
}, },
.got_reloc => |sym_index| {
try writer.writeByte(DW.OP.const4s);
try adapter.gotSym(sym_index);
},
.constu => |constu| if (std.math.cast(u5, constu)) |lit| { .constu => |constu| if (std.math.cast(u5, constu)) |lit| {
try writer.writeByte(@as(u8, DW.OP.lit0) + lit); try writer.writeByte(@as(u8, DW.OP.lit0) + lit);
} else if (std.math.cast(u8, constu)) |const1u| { } else if (std.math.cast(u8, constu)) |const1u| {
@ -1766,9 +1746,6 @@ pub const WipNav = struct {
fn endian(_: ExprLocCounter) std.builtin.Endian { fn endian(_: ExprLocCounter) std.builtin.Endian {
return @import("builtin").cpu.arch.endian(); return @import("builtin").cpu.arch.endian();
} }
fn gotSym(counter: *ExprLocCounter, _: u32) error{}!void {
counter.stream.bytes_written += 4;
}
fn addrSym(counter: *ExprLocCounter, _: u32) error{}!void { fn addrSym(counter: *ExprLocCounter, _: u32) error{}!void {
counter.stream.bytes_written += @intFromEnum(counter.address_size); counter.stream.bytes_written += @intFromEnum(counter.address_size);
} }
@ -1789,14 +1766,6 @@ pub const WipNav = struct {
fn endian(ctx: @This()) std.builtin.Endian { fn endian(ctx: @This()) std.builtin.Endian {
return ctx.wip_nav.dwarf.endian; return ctx.wip_nav.dwarf.endian;
} }
fn gotSym(ctx: @This(), sym_index: u32) UpdateError!void {
try ctx.wip_nav.infoExternalReloc(.{
.source_off = @intCast(ctx.wip_nav.debug_info.items.len),
.target_sym = sym_index,
.target_off = .got,
});
try ctx.wip_nav.debug_info.appendNTimes(ctx.wip_nav.dwarf.gpa, 0, 4);
}
fn addrSym(ctx: @This(), sym_index: u32) UpdateError!void { fn addrSym(ctx: @This(), sym_index: u32) UpdateError!void {
try ctx.wip_nav.infoAddrSym(sym_index, 0); try ctx.wip_nav.infoAddrSym(sym_index, 0);
} }
@ -1812,7 +1781,7 @@ pub const WipNav = struct {
try wip_nav.infoExternalReloc(.{ try wip_nav.infoExternalReloc(.{
.source_off = @intCast(wip_nav.debug_info.items.len), .source_off = @intCast(wip_nav.debug_info.items.len),
.target_sym = sym_index, .target_sym = sym_index,
.target_off = .rel(sym_off), .target_off = sym_off,
}); });
try wip_nav.debug_info.appendNTimes(wip_nav.dwarf.gpa, 0, @intFromEnum(wip_nav.dwarf.address_size)); try wip_nav.debug_info.appendNTimes(wip_nav.dwarf.gpa, 0, @intFromEnum(wip_nav.dwarf.address_size));
} }
@ -1829,14 +1798,6 @@ pub const WipNav = struct {
fn endian(ctx: @This()) std.builtin.Endian { fn endian(ctx: @This()) std.builtin.Endian {
return ctx.wip_nav.dwarf.endian; return ctx.wip_nav.dwarf.endian;
} }
fn gotSym(ctx: @This(), sym_index: u32) UpdateError!void {
try ctx.wip_nav.frameExternalReloc(.{
.source_off = @intCast(ctx.wip_nav.debug_frame.items.len),
.target_sym = sym_index,
.target_off = .got,
});
try ctx.wip_nav.debug_frame.appendNTimes(ctx.wip_nav.dwarf.gpa, 0, 4);
}
fn addrSym(ctx: @This(), sym_index: u32) UpdateError!void { fn addrSym(ctx: @This(), sym_index: u32) UpdateError!void {
try ctx.wip_nav.frameAddrSym(sym_index, 0); try ctx.wip_nav.frameAddrSym(sym_index, 0);
} }
@ -1852,7 +1813,7 @@ pub const WipNav = struct {
try wip_nav.frameExternalReloc(.{ try wip_nav.frameExternalReloc(.{
.source_off = @intCast(wip_nav.debug_frame.items.len), .source_off = @intCast(wip_nav.debug_frame.items.len),
.target_sym = sym_index, .target_sym = sym_index,
.target_off = .rel(sym_off), .target_off = sym_off,
}); });
try wip_nav.debug_frame.appendNTimes(wip_nav.dwarf.gpa, 0, @intFromEnum(wip_nav.dwarf.address_size)); try wip_nav.debug_frame.appendNTimes(wip_nav.dwarf.gpa, 0, @intFromEnum(wip_nav.dwarf.address_size));
} }
@ -2338,81 +2299,50 @@ fn getUnit(dwarf: *Dwarf, mod: *Module) !Unit.Index {
const mod_gop = try dwarf.mods.getOrPut(dwarf.gpa, mod); const mod_gop = try dwarf.mods.getOrPut(dwarf.gpa, mod);
const unit: Unit.Index = @enumFromInt(mod_gop.index); const unit: Unit.Index = @enumFromInt(mod_gop.index);
if (!mod_gop.found_existing) { if (!mod_gop.found_existing) {
{ errdefer _ = dwarf.mods.pop();
errdefer _ = dwarf.mods.pop(); mod_gop.value_ptr.* = .{
mod_gop.value_ptr.* = .{ .root_dir_path = undefined,
.root_dir_path = undefined, .dirs = .empty,
.dirs = .empty, .files = .empty,
.files = .empty, };
}; errdefer mod_gop.value_ptr.dirs.deinit(dwarf.gpa);
errdefer mod_gop.value_ptr.dirs.deinit(dwarf.gpa); try mod_gop.value_ptr.dirs.putNoClobber(dwarf.gpa, unit, {});
try mod_gop.value_ptr.dirs.putNoClobber(dwarf.gpa, unit, {}); assert(try dwarf.debug_aranges.section.addUnit(
assert(try dwarf.debug_aranges.section.addUnit( DebugAranges.headerBytes(dwarf),
DebugAranges.headerBytes(dwarf), DebugAranges.trailerBytes(dwarf),
DebugAranges.trailerBytes(dwarf), dwarf,
dwarf, ) == unit);
) == unit); errdefer dwarf.debug_aranges.section.popUnit(dwarf.gpa);
errdefer dwarf.debug_aranges.section.popUnit(dwarf.gpa); assert(try dwarf.debug_frame.section.addUnit(
assert(try dwarf.debug_frame.section.addUnit( DebugFrame.headerBytes(dwarf),
DebugFrame.headerBytes(dwarf), DebugFrame.trailerBytes(dwarf),
DebugFrame.trailerBytes(dwarf), dwarf,
dwarf, ) == unit);
) == unit); errdefer dwarf.debug_frame.section.popUnit(dwarf.gpa);
errdefer dwarf.debug_frame.section.popUnit(dwarf.gpa); assert(try dwarf.debug_info.section.addUnit(
assert(try dwarf.debug_info.section.addUnit( DebugInfo.headerBytes(dwarf),
DebugInfo.headerBytes(dwarf), DebugInfo.trailer_bytes,
DebugInfo.trailer_bytes, dwarf,
dwarf, ) == unit);
) == unit); errdefer dwarf.debug_info.section.popUnit(dwarf.gpa);
errdefer dwarf.debug_info.section.popUnit(dwarf.gpa); assert(try dwarf.debug_line.section.addUnit(
assert(try dwarf.debug_line.section.addUnit( DebugLine.headerBytes(dwarf, 5, 25),
DebugLine.headerBytes(dwarf, 5, 25), DebugLine.trailer_bytes,
DebugLine.trailer_bytes, dwarf,
dwarf, ) == unit);
) == unit); errdefer dwarf.debug_line.section.popUnit(dwarf.gpa);
errdefer dwarf.debug_line.section.popUnit(dwarf.gpa); assert(try dwarf.debug_loclists.section.addUnit(
assert(try dwarf.debug_loclists.section.addUnit( DebugLocLists.headerBytes(dwarf),
DebugLocLists.headerBytes(dwarf), DebugLocLists.trailer_bytes,
DebugLocLists.trailer_bytes, dwarf,
dwarf, ) == unit);
) == unit); errdefer dwarf.debug_loclists.section.popUnit(dwarf.gpa);
errdefer dwarf.debug_loclists.section.popUnit(dwarf.gpa); assert(try dwarf.debug_rnglists.section.addUnit(
assert(try dwarf.debug_rnglists.section.addUnit( DebugRngLists.headerBytes(dwarf),
DebugRngLists.headerBytes(dwarf), DebugRngLists.trailer_bytes,
DebugRngLists.trailer_bytes, dwarf,
dwarf, ) == unit);
) == unit); errdefer dwarf.debug_rnglists.section.popUnit(dwarf.gpa);
errdefer dwarf.debug_rnglists.section.popUnit(dwarf.gpa);
}
//if (dwarf.bin_file.cast(.elf)) |elf_file| {
// if (unit == .main) assert(try dwarf.addCommonEntry(unit) == .got_proc);
// if (mod.pic and dwarf.debug_info.section.getUnit(.main).getEntry(.got_proc).len == 0) {
// var wip_nav: WipNav = .{
// .dwarf = dwarf,
// .pt = undefined,
// .unit = .main,
// .entry = .got_proc,
// .any_children = false,
// .func = .none,
// .func_sym_index = undefined,
// .func_high_pc = undefined,
// .blocks = undefined,
// .cfi = undefined,
// .debug_frame = .empty,
// .debug_info = .empty,
// .debug_line = .empty,
// .debug_loclists = .empty,
// .pending_lazy = .empty,
// };
// defer wip_nav.deinit();
// try wip_nav.abbrevCode(.proc);
// try wip_nav.infoExprLoc(.{ .deref = &.{ .plus = .{
// &.empty,
// &.{ .addr_reloc = try elf_file.zigObjectPtr().?.getGlobalSymbol(elf_file, "_GLOBAL_OFFSET_TABLE_", null) },
// } } });
// try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.items);
// }
//}
} }
return unit; return unit;
} }
@ -2510,16 +2440,7 @@ fn initWipNavInner(
try wip_nav.strp(nav.fqn.toSlice(ip)); try wip_nav.strp(nav.fqn.toSlice(ip));
const ty: Type = nav_val.typeOf(zcu); const ty: Type = nav_val.typeOf(zcu);
const addr: Loc = .{ .addr_reloc = sym_index }; const addr: Loc = .{ .addr_reloc = sym_index };
const loc: Loc = loc: { const loc: Loc = if (decl.is_threadlocal) .{ .form_tls_address = &addr } else addr;
if (dwarf.bin_file.cast(.elf)) |elf_file| if (decl.linkage == .@"extern" and mod.pic)
// TODO: lldb doesn't support call :(
//.{ .call = .{ .args = &.{.{ .got_reloc = sym_index }}, .unit = .main, .entry = .got_proc } }
break :loc .{ .deref = &.{ .plus = .{
&.{ .addr_reloc = try elf_file.zigObjectPtr().?.getGlobalSymbol(elf_file, "_GLOBAL_OFFSET_TABLE_", null) },
&.{ .got_reloc = sym_index },
} } };
break :loc if (decl.is_threadlocal) .{ .form_tls_address = &addr } else addr;
};
switch (decl.kind) { switch (decl.kind) {
.unnamed_test, .@"test", .decltest, .@"comptime", .@"usingnamespace" => unreachable, .unnamed_test, .@"test", .decltest, .@"comptime", .@"usingnamespace" => unreachable,
.@"const" => { .@"const" => {
@ -2605,7 +2526,7 @@ fn initWipNavInner(
try wip_nav.infoAddrSym(sym_index, 0); try wip_nav.infoAddrSym(sym_index, 0);
wip_nav.func_high_pc = @intCast(wip_nav.debug_info.items.len); wip_nav.func_high_pc = @intCast(wip_nav.debug_info.items.len);
try diw.writeInt(u32, 0, dwarf.endian); try diw.writeInt(u32, 0, dwarf.endian);
const target = file.mod.?.resolved_target.result; const target = mod.resolved_target.result;
try uleb128(diw, switch (nav.status.fully_resolved.alignment) { try uleb128(diw, switch (nav.status.fully_resolved.alignment) {
.none => target_info.defaultFunctionAlignment(target), .none => target_info.defaultFunctionAlignment(target),
else => |a| a.maxStrict(target_info.minFunctionAlignment(target)), else => |a| a.maxStrict(target_info.minFunctionAlignment(target)),
@ -2742,7 +2663,7 @@ pub fn finishWipNavFunc(
.{ .{
.source_off = 1 + @intFromEnum(dwarf.address_size), .source_off = 1 + @intFromEnum(dwarf.address_size),
.target_sym = wip_nav.func_sym_index, .target_sym = wip_nav.func_sym_index,
.target_off = .rel(code_size), .target_off = code_size,
}, },
}); });
try dwarf.debug_rnglists.section.replaceEntry( try dwarf.debug_rnglists.section.replaceEntry(
@ -4981,7 +4902,6 @@ const AbbrevCode = enum {
comptime_value_field_comptime_state, comptime_value_field_comptime_state,
comptime_value_elem_runtime_bits, comptime_value_elem_runtime_bits,
comptime_value_elem_comptime_state, comptime_value_elem_comptime_state,
//proc,
const decl_bytes = uleb128Bytes(@intFromEnum(AbbrevCode.decl_instance_func_generic)); const decl_bytes = uleb128Bytes(@intFromEnum(AbbrevCode.decl_instance_func_generic));
comptime { comptime {
@ -5851,12 +5771,6 @@ const AbbrevCode = enum {
.{ .ZIG_comptime_value, .ref_addr }, .{ .ZIG_comptime_value, .ref_addr },
}, },
}, },
//.proc = .{
// .tag = .dwarf_procedure,
// .attrs = &.{
// .{ .location, .exprloc },
// },
//},
.null = undefined, .null = undefined,
}); });
}; };

View file

@ -959,6 +959,12 @@ fn flushModuleInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {
self.rela_plt.clearRetainingCapacity(); self.rela_plt.clearRetainingCapacity();
if (self.zigObjectPtr()) |zo| { if (self.zigObjectPtr()) |zo| {
var undefs: std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)) = .init(gpa);
defer {
for (undefs.values()) |*refs| refs.deinit();
undefs.deinit();
}
var has_reloc_errors = false; var has_reloc_errors = false;
for (zo.atoms_indexes.items) |atom_index| { for (zo.atoms_indexes.items) |atom_index| {
const atom_ptr = zo.atom(atom_index) orelse continue; const atom_ptr = zo.atom(atom_index) orelse continue;
@ -969,7 +975,10 @@ fn flushModuleInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {
const code = try zo.codeAlloc(self, atom_index); const code = try zo.codeAlloc(self, atom_index);
defer gpa.free(code); defer gpa.free(code);
const file_offset = atom_ptr.offset(self); const file_offset = atom_ptr.offset(self);
atom_ptr.resolveRelocsAlloc(self, code) catch |err| switch (err) { (if (shdr.sh_flags & elf.SHF_ALLOC == 0)
atom_ptr.resolveRelocsNonAlloc(self, code, &undefs)
else
atom_ptr.resolveRelocsAlloc(self, code)) catch |err| switch (err) {
error.RelocFailure, error.RelaxFailure => has_reloc_errors = true, error.RelocFailure, error.RelaxFailure => has_reloc_errors = true,
error.UnsupportedCpuArch => { error.UnsupportedCpuArch => {
try self.reportUnsupportedCpuArch(); try self.reportUnsupportedCpuArch();
@ -980,6 +989,8 @@ fn flushModuleInner(self: *Elf, arena: Allocator, tid: Zcu.PerThread.Id) !void {
try self.pwriteAll(code, file_offset); try self.pwriteAll(code, file_offset);
} }
try self.reportUndefinedSymbols(&undefs);
if (has_reloc_errors) return error.LinkFailure; if (has_reloc_errors) return error.LinkFailure;
} }
@ -1392,11 +1403,9 @@ fn scanRelocs(self: *Elf) !void {
const gpa = self.base.comp.gpa; const gpa = self.base.comp.gpa;
const shared_objects = self.shared_objects.values(); const shared_objects = self.shared_objects.values();
var undefs = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)).init(gpa); var undefs: std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)) = .init(gpa);
defer { defer {
for (undefs.values()) |*refs| { for (undefs.values()) |*refs| refs.deinit();
refs.deinit();
}
undefs.deinit(); undefs.deinit();
} }
@ -2702,15 +2711,16 @@ fn initSyntheticSections(self: *Elf) !void {
}); });
} }
const needs_interp = blk: { const is_exe_or_dyn_lib = switch (comp.config.output_mode) {
// On Ubuntu with musl-gcc, we get a weird combo of options looking like this: .Exe => true,
// -dynamic-linker=<path> -static .Lib => comp.config.link_mode == .dynamic,
// In this case, if we do generate .interp section and segment, we will get .Obj => false,
// a segfault in the dynamic linker trying to load a binary that is static
// and doesn't contain .dynamic section.
if (self.base.isStatic() and !comp.config.pie) break :blk false;
break :blk target.dynamic_linker.get() != null;
}; };
const have_dynamic_linker = comp.config.link_mode == .dynamic and is_exe_or_dyn_lib and !target.dynamic_linker.eql(.none);
const needs_interp = have_dynamic_linker and
(comp.config.link_libc or comp.root_mod.resolved_target.is_explicit_dynamic_linker);
if (needs_interp and self.section_indexes.interp == null) { if (needs_interp and self.section_indexes.interp == null) {
self.section_indexes.interp = try self.addSection(.{ self.section_indexes.interp = try self.addSection(.{
.name = try self.insertShString(".interp"), .name = try self.insertShString(".interp"),
@ -3707,11 +3717,9 @@ fn allocateSpecialPhdrs(self: *Elf) void {
fn writeAtoms(self: *Elf) !void { fn writeAtoms(self: *Elf) !void {
const gpa = self.base.comp.gpa; const gpa = self.base.comp.gpa;
var undefs = std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)).init(gpa); var undefs: std.AutoArrayHashMap(SymbolResolver.Index, std.ArrayList(Ref)) = .init(gpa);
defer { defer {
for (undefs.values()) |*refs| { for (undefs.values()) |*refs| refs.deinit();
refs.deinit();
}
undefs.deinit(); undefs.deinit();
} }

View file

@ -497,14 +497,14 @@ fn dynAbsRelocAction(symbol: *const Symbol, elf_file: *Elf) RelocAction {
} }
fn outputType(elf_file: *Elf) u2 { fn outputType(elf_file: *Elf) u2 {
const comp = elf_file.base.comp;
assert(!elf_file.base.isRelocatable()); assert(!elf_file.base.isRelocatable());
return switch (elf_file.base.comp.config.output_mode) { const config = &elf_file.base.comp.config;
return switch (config.output_mode) {
.Obj => unreachable, .Obj => unreachable,
.Lib => 0, .Lib => 0,
.Exe => switch (elf_file.getTarget().os.tag) { .Exe => switch (elf_file.getTarget().os.tag) {
.haiku => 0, .haiku => 0,
else => if (comp.config.pie) 1 else 2, else => if (config.pie) 1 else 2,
}, },
}; };
} }

View file

@ -463,11 +463,8 @@ pub fn flush(self: *ZigObject, elf_file: *Elf, tid: Zcu.PerThread.Id) !void {
for (entry.external_relocs.items) |reloc| { for (entry.external_relocs.items) |reloc| {
const target_sym = self.symbol(reloc.target_sym); const target_sym = self.symbol(reloc.target_sym);
const r_offset = entry_off + reloc.source_off; const r_offset = entry_off + reloc.source_off;
const r_addend: i64 = switch (reloc.target_off) { const r_addend: i64 = @intCast(reloc.target_off);
.none, .got => 0, const r_type = relocation.dwarf.externalRelocType(target_sym.*, sect_index, dwarf.address_size, cpu_arch);
else => |off| @intCast(@intFromEnum(off)),
};
const r_type = relocation.dwarf.externalRelocType(target_sym.*, reloc.target_off == .got, sect_index, dwarf.address_size, cpu_arch);
atom_ptr.addRelocAssumeCapacity(.{ atom_ptr.addRelocAssumeCapacity(.{
.r_offset = r_offset, .r_offset = r_offset,
.r_addend = r_addend, .r_addend = r_addend,
@ -660,6 +657,7 @@ pub fn scanRelocs(self: *ZigObject, elf_file: *Elf, undefs: anytype) !void {
const atom_ptr = self.atom(atom_index) orelse continue; const atom_ptr = self.atom(atom_index) orelse continue;
if (!atom_ptr.alive) continue; if (!atom_ptr.alive) continue;
const shdr = atom_ptr.inputShdr(elf_file); const shdr = atom_ptr.inputShdr(elf_file);
if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
if (shdr.sh_type == elf.SHT_NOBITS) continue; if (shdr.sh_type == elf.SHT_NOBITS) continue;
if (atom_ptr.scanRelocsRequiresCode(elf_file)) { if (atom_ptr.scanRelocsRequiresCode(elf_file)) {
// TODO ideally we don't have to fetch the code here. // TODO ideally we don't have to fetch the code here.
@ -950,7 +948,7 @@ pub fn getNavVAddr(
.dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{ .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{
.source_off = @intCast(reloc_info.offset), .source_off = @intCast(reloc_info.offset),
.target_sym = this_sym_index, .target_sym = this_sym_index,
.target_off = .rel(reloc_info.addend), .target_off = reloc_info.addend,
}), }),
.plan9 => unreachable, .plan9 => unreachable,
.none => unreachable, .none => unreachable,
@ -983,7 +981,7 @@ pub fn getUavVAddr(
.dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{ .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{
.source_off = @intCast(reloc_info.offset), .source_off = @intCast(reloc_info.offset),
.target_sym = sym_index, .target_sym = sym_index,
.target_off = .rel(reloc_info.addend), .target_off = reloc_info.addend,
}), }),
.plan9 => unreachable, .plan9 => unreachable,
.none => unreachable, .none => unreachable,

View file

@ -108,13 +108,12 @@ pub const dwarf = struct {
pub fn externalRelocType( pub fn externalRelocType(
target: Symbol, target: Symbol,
is_got: bool,
source_section: Dwarf.Section.Index, source_section: Dwarf.Section.Index,
address_size: Dwarf.AddressSize, address_size: Dwarf.AddressSize,
cpu_arch: std.Target.Cpu.Arch, cpu_arch: std.Target.Cpu.Arch,
) u32 { ) u32 {
return switch (cpu_arch) { return switch (cpu_arch) {
.x86_64 => @intFromEnum(@as(elf.R_X86_64, if (is_got) .GOT32 else switch (source_section) { .x86_64 => @intFromEnum(@as(elf.R_X86_64, switch (source_section) {
else => switch (address_size) { else => switch (address_size) {
.@"32" => if (target.flags.is_tls) .DTPOFF32 else .@"32", .@"32" => if (target.flags.is_tls) .DTPOFF32 else .@"32",
.@"64" => if (target.flags.is_tls) .DTPOFF64 else .@"64", .@"64" => if (target.flags.is_tls) .DTPOFF64 else .@"64",

View file

@ -648,7 +648,7 @@ pub fn getNavVAddr(
.dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{ .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{
.source_off = @intCast(reloc_info.offset), .source_off = @intCast(reloc_info.offset),
.target_sym = sym_index, .target_sym = sym_index,
.target_off = .rel(reloc_info.addend), .target_off = reloc_info.addend,
}), }),
.plan9 => unreachable, .plan9 => unreachable,
.none => unreachable, .none => unreachable,
@ -688,7 +688,7 @@ pub fn getUavVAddr(
.dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{ .dwarf => |wip_nav| try wip_nav.infoExternalReloc(.{
.source_off = @intCast(reloc_info.offset), .source_off = @intCast(reloc_info.offset),
.target_sym = sym_index, .target_sym = sym_index,
.target_off = .rel(reloc_info.addend), .target_off = reloc_info.addend,
}), }),
.plan9 => unreachable, .plan9 => unreachable,
.none => unreachable, .none => unreachable,

View file

@ -2376,7 +2376,7 @@ pub const FunctionImportId = enum(u32) {
const zcu = wasm.base.comp.zcu.?; const zcu = wasm.base.comp.zcu.?;
const ip = &zcu.intern_pool; const ip = &zcu.intern_pool;
const ext = ip.getNav(i.ptr(wasm).*).getResolvedExtern(ip).?; const ext = ip.getNav(i.ptr(wasm).*).getResolvedExtern(ip).?;
return !ext.is_weak_linkage and ext.lib_name != .none; return ext.linkage != .weak and ext.lib_name != .none;
}, },
}; };
} }

View file

@ -117,9 +117,9 @@ export fn testMutablePointer() void {
// tmp.zig:37:38: note: imported here // tmp.zig:37:38: note: imported here
// neg_inf.zon:1:1: error: expected type '?u8' // neg_inf.zon:1:1: error: expected type '?u8'
// tmp.zig:57:28: note: imported here // tmp.zig:57:28: note: imported here
// neg_inf.zon:1:1: error: expected type 'tmp.testNonExhaustiveEnum__enum_522' // neg_inf.zon:1:1: error: expected type 'tmp.testNonExhaustiveEnum__enum_525'
// tmp.zig:62:39: note: imported here // tmp.zig:62:39: note: imported here
// neg_inf.zon:1:1: error: expected type 'tmp.testUntaggedUnion__union_524' // neg_inf.zon:1:1: error: expected type 'tmp.testUntaggedUnion__union_527'
// tmp.zig:67:44: note: imported here // tmp.zig:67:44: note: imported here
// neg_inf.zon:1:1: error: expected type 'tmp.testTaggedUnionVoid__union_527' // neg_inf.zon:1:1: error: expected type 'tmp.testTaggedUnionVoid__union_530'
// tmp.zig:72:50: note: imported here // tmp.zig:72:50: note: imported here

View file

@ -15,6 +15,6 @@ pub export fn entry() void {
// error // error
// //
// :7:25: error: unable to resolve comptime value // :7:25: error: unable to resolve comptime value
// :7:25: note: initializer of comptime-only struct 'tmp.S.foo__anon_496.C' must be comptime-known // :7:25: note: initializer of comptime-only struct 'tmp.S.foo__anon_499.C' must be comptime-known
// :4:16: note: struct requires comptime because of this field // :4:16: note: struct requires comptime because of this field
// :4:16: note: types are not available at runtime // :4:16: note: types are not available at runtime

View file

@ -16,5 +16,5 @@ pub export fn entry2() void {
// //
// :3:6: error: no field or member function named 'copy' in '[]const u8' // :3:6: error: no field or member function named 'copy' in '[]const u8'
// :9:8: error: no field or member function named 'bar' in '@TypeOf(.{})' // :9:8: error: no field or member function named 'bar' in '@TypeOf(.{})'
// :12:18: error: no field or member function named 'bar' in 'tmp.entry2__struct_500' // :12:18: error: no field or member function named 'bar' in 'tmp.entry2__struct_503'
// :12:6: note: struct declared here // :12:6: note: struct declared here

View file

@ -6,6 +6,6 @@ export fn foo() void {
// error // error
// //
// :4:16: error: expected type 'tmp.T', found 'tmp.foo__struct_489' // :4:16: error: expected type 'tmp.T', found 'tmp.foo__struct_492'
// :3:16: note: struct declared here // :3:16: note: struct declared here
// :1:11: note: struct declared here // :1:11: note: struct declared here

View file

@ -44,9 +44,9 @@ comptime {
// //
// :5:23: error: expected error union type, found 'comptime_int' // :5:23: error: expected error union type, found 'comptime_int'
// :10:23: error: expected error union type, found '@TypeOf(.{})' // :10:23: error: expected error union type, found '@TypeOf(.{})'
// :15:23: error: expected error union type, found 'tmp.test2__struct_526' // :15:23: error: expected error union type, found 'tmp.test2__struct_529'
// :15:23: note: struct declared here // :15:23: note: struct declared here
// :20:27: error: expected error union type, found 'tmp.test3__struct_528' // :20:27: error: expected error union type, found 'tmp.test3__struct_531'
// :20:27: note: struct declared here // :20:27: note: struct declared here
// :25:23: error: expected error union type, found 'struct { comptime *const [5:0]u8 = "hello" }' // :25:23: error: expected error union type, found 'struct { comptime *const [5:0]u8 = "hello" }'
// :31:13: error: expected error union type, found 'u32' // :31:13: error: expected error union type, found 'u32'

View file

@ -1598,7 +1598,9 @@ const c_abi_targets = blk: {
break :blk [_]CAbiTarget{ break :blk [_]CAbiTarget{
// Native Targets // Native Targets
.{}, .{
.use_llvm = true,
},
// Linux Targets // Linux Targets
@ -1837,7 +1839,6 @@ const c_abi_targets = blk: {
.abi = .musl, .abi = .musl,
}, },
.use_llvm = false, .use_llvm = false,
.use_lld = false,
.c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"},
}, },
.{ .{
@ -1848,7 +1849,6 @@ const c_abi_targets = blk: {
.abi = .musl, .abi = .musl,
}, },
.use_llvm = false, .use_llvm = false,
.use_lld = false,
.strip = true, .strip = true,
.c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"},
}, },
@ -1860,7 +1860,6 @@ const c_abi_targets = blk: {
.abi = .musl, .abi = .musl,
}, },
.use_llvm = false, .use_llvm = false,
.use_lld = false,
.pic = true, .pic = true,
.c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"}, .c_defines = &.{"ZIG_BACKEND_STAGE2_X86_64"},
}, },
@ -1870,6 +1869,7 @@ const c_abi_targets = blk: {
.os_tag = .linux, .os_tag = .linux,
.abi = .musl, .abi = .musl,
}, },
.use_llvm = true,
}, },
.{ .{
.target = .{ .target = .{
@ -1877,6 +1877,7 @@ const c_abi_targets = blk: {
.os_tag = .linux, .os_tag = .linux,
.abi = .muslx32, .abi = .muslx32,
}, },
.use_llvm = true,
}, },
// WASI Targets // WASI Targets