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:
jacobly0
2022-10-18 07:02:10 -04:00
committed by GitHub
parent 71f8762959
commit bd0dd225e8
14 changed files with 97 additions and 56 deletions

View File

@@ -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);

View File

@@ -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;

View File

@@ -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: {

View File

@@ -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,

View File

@@ -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");

View File

@@ -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);

View File

@@ -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");

View 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);
}

View 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 {}

View File

@@ -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);

View File

@@ -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(".");

View File

@@ -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);

View File

@@ -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);

View File

@@ -83,7 +83,7 @@ const test_targets = blk: {
.cpu_arch = .arm,
.os_tag = .linux,
},
.backend = .stage2_wasm,
.backend = .stage2_arm,
},
.{
.target = CrossTarget.parse(.{