Sema: implement linksection on functions
* Sema: implement linksection on functions * Implement function linksection in Sema. * Don't clobber function linksection/align/addrspace in Sema. * Fix copy-paste typo in tests. * Add a bunch of missing test_step.dependOn. * Fix checkInSymtab match. Closes #12546
This commit is contained in:
@@ -436,6 +436,7 @@ const MachODumper = struct {
|
||||
}
|
||||
|
||||
if (opts.dump_symtab) {
|
||||
try writer.print("{s}\n", .{symtab_label});
|
||||
for (symtab) |sym| {
|
||||
if (sym.stab()) continue;
|
||||
const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0);
|
||||
|
||||
@@ -4592,40 +4592,6 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
|
||||
const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = 0 };
|
||||
const init_src: LazySrcLoc = .{ .node_offset_var_decl_init = 0 };
|
||||
const decl_tv = try sema.resolveInstValue(&block_scope, init_src, result_ref, undefined);
|
||||
const decl_align: u32 = blk: {
|
||||
const align_ref = decl.zirAlignRef();
|
||||
if (align_ref == .none) break :blk 0;
|
||||
break :blk try sema.resolveAlign(&block_scope, align_src, align_ref);
|
||||
};
|
||||
const decl_linksection: ?[*:0]const u8 = blk: {
|
||||
const linksection_ref = decl.zirLinksectionRef();
|
||||
if (linksection_ref == .none) break :blk null;
|
||||
const bytes = try sema.resolveConstString(&block_scope, section_src, linksection_ref, "linksection must be comptime-known");
|
||||
if (mem.indexOfScalar(u8, bytes, 0) != null) {
|
||||
return sema.fail(&block_scope, section_src, "linksection cannot contain null bytes", .{});
|
||||
} else if (bytes.len == 0) {
|
||||
return sema.fail(&block_scope, section_src, "linksection cannot be empty", .{});
|
||||
}
|
||||
break :blk (try decl_arena_allocator.dupeZ(u8, bytes)).ptr;
|
||||
};
|
||||
const target = sema.mod.getTarget();
|
||||
const address_space = blk: {
|
||||
const addrspace_ctx: Sema.AddressSpaceContext = switch (decl_tv.val.tag()) {
|
||||
.function, .extern_fn => .function,
|
||||
.variable => .variable,
|
||||
else => .constant,
|
||||
};
|
||||
|
||||
break :blk switch (decl.zirAddrspaceRef()) {
|
||||
.none => switch (addrspace_ctx) {
|
||||
.function => target_util.defaultAddressSpace(target, .function),
|
||||
.variable => target_util.defaultAddressSpace(target, .global_mutable),
|
||||
.constant => target_util.defaultAddressSpace(target, .global_constant),
|
||||
else => unreachable,
|
||||
},
|
||||
else => |addrspace_ref| try sema.analyzeAddressSpace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx),
|
||||
};
|
||||
};
|
||||
|
||||
// Note this resolves the type of the Decl, not the value; if this Decl
|
||||
// is a struct, for example, this resolves `type` (which needs no resolution),
|
||||
@@ -4679,9 +4645,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
|
||||
|
||||
decl.ty = try decl_tv.ty.copy(decl_arena_allocator);
|
||||
decl.val = try decl_tv.val.copy(decl_arena_allocator);
|
||||
decl.@"align" = decl_align;
|
||||
decl.@"linksection" = decl_linksection;
|
||||
decl.@"addrspace" = address_space;
|
||||
// linksection, align, and addrspace were already set by Sema
|
||||
decl.has_tv = true;
|
||||
decl.owns_tv = owns_tv;
|
||||
decl_arena_state.* = decl_arena.state;
|
||||
@@ -4759,9 +4723,40 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
|
||||
|
||||
decl.ty = try decl_tv.ty.copy(decl_arena_allocator);
|
||||
decl.val = try decl_tv.val.copy(decl_arena_allocator);
|
||||
decl.@"align" = decl_align;
|
||||
decl.@"linksection" = decl_linksection;
|
||||
decl.@"addrspace" = address_space;
|
||||
decl.@"align" = blk: {
|
||||
const align_ref = decl.zirAlignRef();
|
||||
if (align_ref == .none) break :blk 0;
|
||||
break :blk try sema.resolveAlign(&block_scope, align_src, align_ref);
|
||||
};
|
||||
decl.@"linksection" = blk: {
|
||||
const linksection_ref = decl.zirLinksectionRef();
|
||||
if (linksection_ref == .none) break :blk null;
|
||||
const bytes = try sema.resolveConstString(&block_scope, section_src, linksection_ref, "linksection must be comptime-known");
|
||||
if (mem.indexOfScalar(u8, bytes, 0) != null) {
|
||||
return sema.fail(&block_scope, section_src, "linksection cannot contain null bytes", .{});
|
||||
} else if (bytes.len == 0) {
|
||||
return sema.fail(&block_scope, section_src, "linksection cannot be empty", .{});
|
||||
}
|
||||
break :blk (try decl_arena_allocator.dupeZ(u8, bytes)).ptr;
|
||||
};
|
||||
decl.@"addrspace" = blk: {
|
||||
const addrspace_ctx: Sema.AddressSpaceContext = switch (decl_tv.val.tag()) {
|
||||
.function, .extern_fn => .function,
|
||||
.variable => .variable,
|
||||
else => .constant,
|
||||
};
|
||||
|
||||
const target = sema.mod.getTarget();
|
||||
break :blk switch (decl.zirAddrspaceRef()) {
|
||||
.none => switch (addrspace_ctx) {
|
||||
.function => target_util.defaultAddressSpace(target, .function),
|
||||
.variable => target_util.defaultAddressSpace(target, .global_mutable),
|
||||
.constant => target_util.defaultAddressSpace(target, .global_constant),
|
||||
else => unreachable,
|
||||
},
|
||||
else => |addrspace_ref| try sema.analyzeAddressSpace(&block_scope, address_space_src, addrspace_ref, addrspace_ctx),
|
||||
};
|
||||
};
|
||||
decl.has_tv = true;
|
||||
decl_arena_state.* = decl_arena.state;
|
||||
decl.value_arena = decl_arena_state;
|
||||
|
||||
28
src/Sema.zig
28
src/Sema.zig
@@ -7899,7 +7899,7 @@ fn handleExternLibName(
|
||||
const FuncLinkSection = union(enum) {
|
||||
generic,
|
||||
default,
|
||||
explicit: [*:0]const u8,
|
||||
explicit: []const u8,
|
||||
};
|
||||
|
||||
fn funcCommon(
|
||||
@@ -8185,15 +8185,13 @@ fn funcCommon(
|
||||
});
|
||||
};
|
||||
|
||||
if (sema.owner_decl.owns_tv) {
|
||||
switch (section) {
|
||||
.generic => sema.owner_decl.@"linksection" = undefined,
|
||||
.default => sema.owner_decl.@"linksection" = null,
|
||||
.explicit => |s| sema.owner_decl.@"linksection" = s,
|
||||
}
|
||||
if (alignment) |a| sema.owner_decl.@"align" = a;
|
||||
if (address_space) |a| sema.owner_decl.@"addrspace" = a;
|
||||
}
|
||||
sema.owner_decl.@"linksection" = switch (section) {
|
||||
.generic => undefined,
|
||||
.default => null,
|
||||
.explicit => |section_name| try sema.perm_arena.dupeZ(u8, section_name),
|
||||
};
|
||||
sema.owner_decl.@"align" = alignment orelse 0;
|
||||
sema.owner_decl.@"addrspace" = address_space orelse .generic;
|
||||
|
||||
if (is_extern) {
|
||||
const new_extern_fn = try sema.gpa.create(Module.ExternFn);
|
||||
@@ -20717,22 +20715,22 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
|
||||
const body = sema.code.extra[extra_index..][0..body_len];
|
||||
extra_index += body.len;
|
||||
|
||||
const val = try sema.resolveGenericBody(block, section_src, body, inst, Type.initTag(.const_slice_u8), "linksection must be comptime-known");
|
||||
const ty = Type.initTag(.const_slice_u8);
|
||||
const val = try sema.resolveGenericBody(block, section_src, body, inst, ty, "linksection must be comptime-known");
|
||||
if (val.tag() == .generic_poison) {
|
||||
break :blk FuncLinkSection{ .generic = {} };
|
||||
}
|
||||
return sema.fail(block, section_src, "TODO implement linksection on functions", .{});
|
||||
break :blk FuncLinkSection{ .explicit = try val.toAllocatedBytes(ty, sema.arena, sema.mod) };
|
||||
} else if (extra.data.bits.has_section_ref) blk: {
|
||||
const section_ref = @intToEnum(Zir.Inst.Ref, sema.code.extra[extra_index]);
|
||||
extra_index += 1;
|
||||
const section_tv = sema.resolveInstConst(block, section_src, section_ref, "linksection must be comptime-known") catch |err| switch (err) {
|
||||
const section_name = sema.resolveConstString(block, section_src, section_ref, "linksection must be comptime-known") catch |err| switch (err) {
|
||||
error.GenericPoison => {
|
||||
break :blk FuncLinkSection{ .generic = {} };
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
_ = section_tv;
|
||||
return sema.fail(block, section_src, "TODO implement linksection on functions", .{});
|
||||
break :blk FuncLinkSection{ .explicit = section_name };
|
||||
} else FuncLinkSection{ .default = {} };
|
||||
|
||||
const cc: ?std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: {
|
||||
|
||||
@@ -92,6 +92,10 @@ fn addMachOCases(cases: *tests.StandaloneContext) void {
|
||||
.requires_macos_sdk = true,
|
||||
});
|
||||
|
||||
cases.addBuildFile("test/link/macho/linksection/build.zig", .{
|
||||
.build_modes = true,
|
||||
});
|
||||
|
||||
cases.addBuildFile("test/link/macho/needed_framework/build.zig", .{
|
||||
.build_modes = true,
|
||||
.requires_macos_sdk = true,
|
||||
|
||||
@@ -16,6 +16,7 @@ pub fn build(b: *Builder) void {
|
||||
const check = exe.checkObject(.macho);
|
||||
check.checkInSymtab();
|
||||
check.checkNext("{*} (__TEXT,__text) external _iAmUnused");
|
||||
test_step.dependOn(&check.step);
|
||||
|
||||
const run_cmd = check.runAndCompare();
|
||||
run_cmd.expectStdOutEqual("Hello!\n");
|
||||
@@ -30,6 +31,7 @@ pub fn build(b: *Builder) void {
|
||||
const check = exe.checkObject(.macho);
|
||||
check.checkInSymtab();
|
||||
check.checkNotPresent("{*} (__TEXT,__text) external _iAmUnused");
|
||||
test_step.dependOn(&check.step);
|
||||
|
||||
const run_cmd = check.runAndCompare();
|
||||
run_cmd.expectStdOutEqual("Hello!\n");
|
||||
|
||||
@@ -40,6 +40,8 @@ pub fn build(b: *Builder) void {
|
||||
check_exe.checkNext("current version 10000");
|
||||
check_exe.checkNext("compatibility version 10000");
|
||||
|
||||
test_step.dependOn(&check_exe.step);
|
||||
|
||||
check_exe.checkStart("cmd RPATH");
|
||||
check_exe.checkNext(std.fmt.allocPrint(b.allocator, "path {s}", .{b.pathFromRoot("zig-out/lib")}) catch unreachable);
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ pub fn build(b: *Builder) void {
|
||||
check_exe.checkNext("{n_value} (__TEXT,__text) external _non_main");
|
||||
|
||||
check_exe.checkComputeCompare("vmaddr entryoff +", .{ .op = .eq, .value = .{ .variable = "n_value" } });
|
||||
test_step.dependOn(&check_exe.step);
|
||||
|
||||
const run = check_exe.runAndCompare();
|
||||
run.expectStdOutEqual("42");
|
||||
|
||||
28
test/link/macho/linksection/build.zig
Normal file
28
test/link/macho/linksection/build.zig
Normal file
@@ -0,0 +1,28 @@
|
||||
const std = @import("std");
|
||||
|
||||
pub fn build(b: *std.build.Builder) void {
|
||||
const mode = b.standardReleaseOptions();
|
||||
const target = std.zig.CrossTarget{ .os_tag = .macos };
|
||||
|
||||
const test_step = b.step("test", "Test");
|
||||
test_step.dependOn(b.getInstallStep());
|
||||
|
||||
const obj = b.addObject("test", "main.zig");
|
||||
obj.setBuildMode(mode);
|
||||
obj.setTarget(target);
|
||||
|
||||
const check = obj.checkObject(.macho);
|
||||
|
||||
check.checkInSymtab();
|
||||
check.checkNext("{*} (__DATA,__TestGlobal) external _test_global");
|
||||
|
||||
check.checkInSymtab();
|
||||
check.checkNext("{*} (__TEXT,__TestFn) external _testFn");
|
||||
|
||||
if (mode == .Debug) {
|
||||
check.checkInSymtab();
|
||||
check.checkNext("{*} (__TEXT,__TestGenFnA) _main.testGenericFn__anon_{*}");
|
||||
}
|
||||
|
||||
test_step.dependOn(&check.step);
|
||||
}
|
||||
5
test/link/macho/linksection/main.zig
Normal file
5
test/link/macho/linksection/main.zig
Normal file
@@ -0,0 +1,5 @@
|
||||
export var test_global: u32 linksection("__DATA,__TestGlobal") = undefined;
|
||||
export fn testFn() linksection("__TEXT,__TestFn") callconv(.C) void {
|
||||
testGenericFn("A");
|
||||
}
|
||||
fn testGenericFn(comptime suffix: []const u8) linksection("__TEXT,__TestGenFn" ++ suffix) void {}
|
||||
@@ -31,6 +31,7 @@ pub fn build(b: *Builder) void {
|
||||
const check = exe.checkObject(.macho);
|
||||
check.checkStart("cmd LOAD_DYLIB");
|
||||
check.checkNext("name @rpath/liba.dylib");
|
||||
test_step.dependOn(&check.step);
|
||||
|
||||
const run_cmd = check.runAndCompare();
|
||||
test_step.dependOn(&run_cmd.step);
|
||||
|
||||
@@ -17,6 +17,7 @@ pub fn build(b: *Builder) void {
|
||||
const check = exe.checkObject(.macho);
|
||||
check.checkStart("cmd LOAD_DYLIB");
|
||||
check.checkNext("name @rpath/liba.dylib");
|
||||
test_step.dependOn(&check.step);
|
||||
|
||||
const run = check.runAndCompare();
|
||||
run.cwd = b.pathFromRoot(".");
|
||||
|
||||
@@ -18,6 +18,7 @@ pub fn build(b: *Builder) void {
|
||||
const check_exe = exe.checkObject(.macho);
|
||||
check_exe.checkStart("cmd MAIN");
|
||||
check_exe.checkNext("stacksize 100000000");
|
||||
test_step.dependOn(&check_exe.step);
|
||||
|
||||
const run = check_exe.runAndCompare();
|
||||
test_step.dependOn(&run.step);
|
||||
|
||||
@@ -33,6 +33,8 @@ pub fn build(b: *Builder) void {
|
||||
check.checkNext("(undefined) weak external _a (from liba)");
|
||||
check.checkNext("(undefined) weak external _asStr (from liba)");
|
||||
|
||||
test_step.dependOn(&check.step);
|
||||
|
||||
const run_cmd = check.runAndCompare();
|
||||
run_cmd.expectStdOutEqual("42 42");
|
||||
test_step.dependOn(&run_cmd.step);
|
||||
|
||||
@@ -83,7 +83,7 @@ const test_targets = blk: {
|
||||
.cpu_arch = .arm,
|
||||
.os_tag = .linux,
|
||||
},
|
||||
.backend = .stage2_wasm,
|
||||
.backend = .stage2_arm,
|
||||
},
|
||||
.{
|
||||
.target = CrossTarget.parse(.{
|
||||
|
||||
Reference in New Issue
Block a user