Commit bd0dd225e8

jacobly0 <jacobly0@users.noreply.github.com>
2022-10-18 13:02:10
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
1 parent 71f8762
Changed files (14)
lib
src
test
link
macho
dead_strip
dylib
entry
linksection
needed_library
search_strategy
stack_size
weak_library
lib/std/build/CheckObjectStep.zig
@@ -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);
src/Module.zig
@@ -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;
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: {
test/link/macho/dead_strip/build.zig
@@ -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");
test/link/macho/dylib/build.zig
@@ -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);
 
test/link/macho/entry/build.zig
@@ -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");
test/link/macho/linksection/build.zig
@@ -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);
+}
test/link/macho/linksection/main.zig
@@ -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 {}
test/link/macho/needed_library/build.zig
@@ -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);
test/link/macho/search_strategy/build.zig
@@ -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(".");
test/link/macho/stack_size/build.zig
@@ -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);
test/link/macho/weak_library/build.zig
@@ -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);
test/link.zig
@@ -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,
test/tests.zig
@@ -83,7 +83,7 @@ const test_targets = blk: {
                 .cpu_arch = .arm,
                 .os_tag = .linux,
             },
-            .backend = .stage2_wasm,
+            .backend = .stage2_arm,
         },
         .{
             .target = CrossTarget.parse(.{