From 00da182e6875845d5727c399b3738a13b262832e Mon Sep 17 00:00:00 2001 From: Jacob Young Date: Sun, 30 Jun 2024 00:11:51 -0400 Subject: [PATCH] cbe: fix for export changes --- lib/zig.h | 8 +- src/Compilation.zig | 3 + src/Zcu.zig | 14 ++ src/codegen/c.zig | 378 +++++++++++++++++--------------------------- src/link.zig | 1 - src/link/C.zig | 158 +++++++++++++----- 6 files changed, 286 insertions(+), 276 deletions(-) diff --git a/lib/zig.h b/lib/zig.h index 1171c7efac..f3b3897186 100644 --- a/lib/zig.h +++ b/lib/zig.h @@ -207,16 +207,16 @@ typedef char bool; __asm(zig_mangle_c(name) " = " zig_mangle_c(symbol)) #endif +#define zig_mangled_tentative zig_mangled +#define zig_mangled_final zig_mangled #if _MSC_VER -#define zig_mangled_tentative(mangled, unmangled) -#define zig_mangled_final(mangled, unmangled) ; \ +#define zig_mangled(mangled, unmangled) ; \ zig_export(#mangled, unmangled) #define zig_mangled_export(mangled, unmangled, symbol) \ zig_export(unmangled, #mangled) \ zig_export(symbol, unmangled) #else /* _MSC_VER */ -#define zig_mangled_tentative(mangled, unmangled) __asm(zig_mangle_c(unmangled)) -#define zig_mangled_final(mangled, unmangled) zig_mangled_tentative(mangled, unmangled) +#define zig_mangled(mangled, unmangled) __asm(zig_mangle_c(unmangled)) #define zig_mangled_export(mangled, unmangled, symbol) \ zig_mangled_final(mangled, unmangled) \ zig_export(symbol, unmangled) diff --git a/src/Compilation.zig b/src/Compilation.zig index 7447d589fd..185a9a6260 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -3466,6 +3466,9 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: std.Progress.Node) !vo }; }, .emit_h_decl => |decl_index| { + if (true) @panic("regressed compiler feature: emit-h should hook into updateExports, " ++ + "not decl analysis, which is too early to know about @export calls"); + const module = comp.module.?; const decl = module.declPtr(decl_index); diff --git a/src/Zcu.zig b/src/Zcu.zig index e3b85e957d..adfe60e678 100644 --- a/src/Zcu.zig +++ b/src/Zcu.zig @@ -268,6 +268,20 @@ pub const Exported = union(enum) { decl_index: Decl.Index, /// Constant value being exported. value: InternPool.Index, + + pub fn getValue(exported: Exported, zcu: *Zcu) Value { + return switch (exported) { + .decl_index => |decl_index| zcu.declPtr(decl_index).val, + .value => |value| Value.fromInterned(value), + }; + } + + pub fn getAlign(exported: Exported, zcu: *Zcu) Alignment { + return switch (exported) { + .decl_index => |decl_index| zcu.declPtr(decl_index).alignment, + .value => .none, + }; + } }; pub const Export = struct { diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 13d9e67519..92e9edb433 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -731,8 +731,6 @@ pub const DeclGen = struct { if (decl.val.getExternFunc(zcu)) |extern_func| if (extern_func.decl != decl_index) return dg.renderDeclValue(writer, extern_func.decl, location); - if (decl.val.getVariable(zcu)) |variable| try dg.renderFwdDecl(decl_index, variable, .tentative); - // We shouldn't cast C function pointers as this is UB (when you call // them). The analysis until now should ensure that the C function // pointers are compatible. If they are not, then there is a bug @@ -748,7 +746,7 @@ pub const DeclGen = struct { try writer.writeByte(')'); } try writer.writeByte('&'); - try dg.renderDeclName(writer, decl_index, 0); + try dg.renderDeclName(writer, decl_index); if (need_cast) try writer.writeByte(')'); } @@ -1765,19 +1763,22 @@ pub const DeclGen = struct { fn renderFunctionSignature( dg: *DeclGen, w: anytype, - fn_decl_index: InternPool.DeclIndex, + fn_val: Value, + fn_align: InternPool.Alignment, kind: CType.Kind, name: union(enum) { - export_index: u32, - ident: []const u8, + decl: InternPool.DeclIndex, fmt_ctype_pool_string: std.fmt.Formatter(formatCTypePoolString), + @"export": struct { + main_name: InternPool.NullTerminatedString, + extern_name: InternPool.NullTerminatedString, + }, }, ) !void { const zcu = dg.zcu; const ip = &zcu.intern_pool; - const fn_decl = zcu.declPtr(fn_decl_index); - const fn_ty = fn_decl.typeOf(zcu); + const fn_ty = fn_val.typeOf(zcu); const fn_ctype = try dg.ctypeFromType(fn_ty, kind); const fn_info = zcu.typeToFunc(fn_ty).?; @@ -1788,7 +1789,7 @@ pub const DeclGen = struct { else => unreachable, } } - if (fn_decl.val.getFunction(zcu)) |func| if (func.analysis(ip).is_cold) + if (fn_val.getFunction(zcu)) |func| if (func.analysis(ip).is_cold) try w.writeAll("zig_cold "); if (fn_info.return_type == .noreturn_type) try w.writeAll("zig_noreturn "); @@ -1799,22 +1800,11 @@ pub const DeclGen = struct { trailing = .maybe_space; } - switch (kind) { - .forward => {}, - .complete => if (fn_decl.alignment.toByteUnits()) |a| { - try w.print("{}zig_align_fn({})", .{ trailing, a }); - trailing = .maybe_space; - }, - else => unreachable, - } - + try w.print("{}", .{trailing}); switch (name) { - .export_index => |export_index| { - try w.print("{}", .{trailing}); - try dg.renderDeclName(w, fn_decl_index, export_index); - }, - .ident => |ident| try w.print("{}{ }", .{ trailing, fmtIdent(ident) }), - .fmt_ctype_pool_string => |fmt| try w.print("{}{ }", .{ trailing, fmt }), + .decl => |decl_index| try dg.renderDeclName(w, decl_index), + .fmt_ctype_pool_string => |fmt| try w.print("{ }", .{fmt}), + .@"export" => |@"export"| try w.print("{ }", .{fmtIdent(@"export".extern_name.toSlice(ip))}), } try renderTypeSuffix( @@ -1833,44 +1823,30 @@ pub const DeclGen = struct { switch (kind) { .forward => { - if (fn_decl.alignment.toByteUnits()) |a| { - try w.print(" zig_align_fn({})", .{a}); - } + if (fn_align.toByteUnits()) |a| try w.print(" zig_align_fn({})", .{a}); switch (name) { - .export_index => |export_index| mangled: { - const maybe_exports = zcu.decl_exports.get(fn_decl_index); - const external_name = (if (maybe_exports) |exports| - exports.items[export_index].opts.name - else if (fn_decl.isExtern(zcu)) - fn_decl.name - else - break :mangled).toSlice(ip); - const is_mangled = isMangledIdent(external_name, true); - const is_export = export_index > 0; + .decl, .fmt_ctype_pool_string => {}, + .@"export" => |@"export"| { + const extern_name = @"export".extern_name.toSlice(ip); + const is_mangled = isMangledIdent(extern_name, true); + const is_export = @"export".extern_name != @"export".main_name; if (is_mangled and is_export) { try w.print(" zig_mangled_export({ }, {s}, {s})", .{ - fmtIdent(external_name), - fmtStringLiteral(external_name, null), - fmtStringLiteral( - maybe_exports.?.items[0].opts.name.toSlice(ip), - null, - ), + fmtIdent(extern_name), + fmtStringLiteral(extern_name, null), + fmtStringLiteral(@"export".main_name.toSlice(ip), null), }); } else if (is_mangled) { - try w.print(" zig_mangled_final({ }, {s})", .{ - fmtIdent(external_name), fmtStringLiteral(external_name, null), + try w.print(" zig_mangled({ }, {s})", .{ + fmtIdent(extern_name), fmtStringLiteral(extern_name, null), }); } else if (is_export) { try w.print(" zig_export({s}, {s})", .{ - fmtStringLiteral( - maybe_exports.?.items[0].opts.name.toSlice(ip), - null, - ), - fmtStringLiteral(external_name, null), + fmtStringLiteral(@"export".main_name.toSlice(ip), null), + fmtStringLiteral(extern_name, null), }); } }, - .ident, .fmt_ctype_pool_string => {}, } }, .complete => {}, @@ -2085,21 +2061,11 @@ pub const DeclGen = struct { try renderTypeSuffix(dg.pass, &dg.ctype_pool, dg.zcu, w, ctype, .suffix, .{}); } - fn declIsGlobal(dg: *DeclGen, val: Value) bool { - const zcu = dg.zcu; - return switch (zcu.intern_pool.indexToKey(val.toIntern())) { - .variable => |variable| zcu.decl_exports.contains(variable.decl), - .extern_func => true, - .func => |func| zcu.decl_exports.contains(func.owner_decl), - else => unreachable, - }; - } - fn writeName(dg: *DeclGen, w: anytype, c_value: CValue) !void { switch (c_value) { .new_local, .local => |i| try w.print("t{d}", .{i}), .constant => |val| try renderAnonDeclName(w, val), - .decl => |decl| try dg.renderDeclName(w, decl, 0), + .decl => |decl| try dg.renderDeclName(w, decl), .identifier => |ident| try w.print("{ }", .{fmtIdent(ident)}), else => unreachable, } @@ -2111,10 +2077,10 @@ pub const DeclGen = struct { .constant => |val| try renderAnonDeclName(w, val), .arg, .arg_array => unreachable, .field => |i| try w.print("f{d}", .{i}), - .decl => |decl| try dg.renderDeclName(w, decl, 0), + .decl => |decl| try dg.renderDeclName(w, decl), .decl_ref => |decl| { try w.writeByte('&'); - try dg.renderDeclName(w, decl, 0); + try dg.renderDeclName(w, decl); }, .undef => |ty| try dg.renderUndefValue(w, ty, .Other), .identifier => |ident| try w.print("{ }", .{fmtIdent(ident)}), @@ -2142,10 +2108,10 @@ pub const DeclGen = struct { .field => |i| try w.print("f{d}", .{i}), .decl => |decl| { try w.writeAll("(*"); - try dg.renderDeclName(w, decl, 0); + try dg.renderDeclName(w, decl); try w.writeByte(')'); }, - .decl_ref => |decl| try dg.renderDeclName(w, decl, 0), + .decl_ref => |decl| try dg.renderDeclName(w, decl), .undef => unreachable, .identifier => |ident| try w.print("(*{ })", .{fmtIdent(ident)}), .payload_identifier => |ident| try w.print("(*{ }.{ })", .{ @@ -2195,19 +2161,12 @@ pub const DeclGen = struct { dg: *DeclGen, decl_index: InternPool.DeclIndex, variable: InternPool.Key.Variable, - fwd_kind: enum { tentative, final }, ) !void { const zcu = dg.zcu; const decl = zcu.declPtr(decl_index); const fwd = dg.fwdDeclWriter(); - const is_global = variable.is_extern or dg.declIsGlobal(decl.val); - try fwd.writeAll(if (is_global) "zig_extern " else "static "); - const maybe_exports = zcu.decl_exports.get(decl_index); - const export_weak_linkage = if (maybe_exports) |exports| - exports.items[0].opts.linkage == .weak - else - false; - if (variable.is_weak_linkage or export_weak_linkage) try fwd.writeAll("zig_weak_linkage "); + try fwd.writeAll(if (variable.is_extern) "zig_extern " else "static "); + if (variable.is_weak_linkage) try fwd.writeAll("zig_weak_linkage "); if (variable.is_threadlocal and !dg.mod.single_threaded) try fwd.writeAll("zig_threadlocal "); try dg.renderTypeAndName( fwd, @@ -2217,38 +2176,17 @@ pub const DeclGen = struct { decl.alignment, .complete, ); - mangled: { - const external_name = (if (maybe_exports) |exports| - exports.items[0].opts.name - else if (variable.is_extern) - decl.name - else - break :mangled).toSlice(&zcu.intern_pool); - if (isMangledIdent(external_name, true)) { - try fwd.print(" zig_mangled_{s}({ }, {s})", .{ - @tagName(fwd_kind), - fmtIdent(external_name), - fmtStringLiteral(external_name, null), - }); - } - } try fwd.writeAll(";\n"); } - fn renderDeclName(dg: *DeclGen, writer: anytype, decl_index: InternPool.DeclIndex, export_index: u32) !void { + fn renderDeclName(dg: *DeclGen, writer: anytype, decl_index: InternPool.DeclIndex) !void { const zcu = dg.zcu; const ip = &zcu.intern_pool; const decl = zcu.declPtr(decl_index); - if (zcu.decl_exports.get(decl_index)) |exports| { - try writer.print("{ }", .{ - fmtIdent(exports.items[export_index].opts.name.toSlice(ip)), - }); - } else if (decl.getExternDecl(zcu).unwrap()) |extern_decl_index| { - try writer.print("{ }", .{ - fmtIdent(zcu.declPtr(extern_decl_index).name.toSlice(ip)), - }); - } else { + if (decl.getExternDecl(zcu).unwrap()) |extern_decl_index| try writer.print("{ }", .{ + fmtIdent(zcu.declPtr(extern_decl_index).name.toSlice(ip)), + }) else { // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case), // expand to 3x the length of its input, but let's cut it off at a much shorter limit. var name: [100]u8 = undefined; @@ -2761,69 +2699,6 @@ pub fn genErrDecls(o: *Object) !void { try writer.writeAll("};\n"); } -fn genExports(o: *Object) !void { - const tracy = trace(@src()); - defer tracy.end(); - - const zcu = o.dg.zcu; - const ip = &zcu.intern_pool; - const decl_index = switch (o.dg.pass) { - .decl => |decl| decl, - .anon, .flush => return, - }; - const decl = zcu.declPtr(decl_index); - const fwd = o.dg.fwdDeclWriter(); - - const exports = zcu.decl_exports.get(decl_index) orelse return; - if (exports.items.len < 2) return; - - const is_variable_const = switch (ip.indexToKey(decl.val.toIntern())) { - .func => return for (exports.items[1..], 1..) |@"export", i| { - try fwd.writeAll("zig_extern "); - if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage_fn "); - try o.dg.renderFunctionSignature( - fwd, - decl_index, - .forward, - .{ .export_index = @intCast(i) }, - ); - try fwd.writeAll(";\n"); - }, - .extern_func => { - // TODO: when sema allows re-exporting extern decls - unreachable; - }, - .variable => |variable| variable.is_const, - else => true, - }; - for (exports.items[1..]) |@"export"| { - try fwd.writeAll("zig_extern "); - if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage "); - const export_name = @"export".opts.name.toSlice(ip); - try o.dg.renderTypeAndName( - fwd, - decl.typeOf(zcu), - .{ .identifier = export_name }, - CQualifiers.init(.{ .@"const" = is_variable_const }), - decl.alignment, - .complete, - ); - if (isMangledIdent(export_name, true)) { - try fwd.print(" zig_mangled_export({ }, {s}, {s})", .{ - fmtIdent(export_name), - fmtStringLiteral(export_name, null), - fmtStringLiteral(exports.items[0].opts.name.toSlice(ip), null), - }); - } else { - try fwd.print(" zig_export({s}, {s})", .{ - fmtStringLiteral(exports.items[0].opts.name.toSlice(ip), null), - fmtStringLiteral(export_name, null), - }); - } - try fwd.writeAll(";\n"); - } -} - pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFnMap.Entry) !void { const zcu = o.dg.zcu; const ip = &zcu.intern_pool; @@ -2885,19 +2760,19 @@ pub fn genLazyFn(o: *Object, lazy_ctype_pool: *const CType.Pool, lazy_fn: LazyFn const fn_info = fn_ctype.info(ctype_pool).function; const fn_name = fmtCTypePoolString(val.fn_name, lazy_ctype_pool); - const fwd_decl_writer = o.dg.fwdDeclWriter(); - try fwd_decl_writer.print("static zig_{s} ", .{@tagName(key)}); - try o.dg.renderFunctionSignature(fwd_decl_writer, fn_decl_index, .forward, .{ + const fwd = o.dg.fwdDeclWriter(); + try fwd.print("static zig_{s} ", .{@tagName(key)}); + try o.dg.renderFunctionSignature(fwd, fn_decl.val, fn_decl.alignment, .forward, .{ .fmt_ctype_pool_string = fn_name, }); - try fwd_decl_writer.writeAll(";\n"); + try fwd.writeAll(";\n"); - try w.print("static zig_{s} ", .{@tagName(key)}); - try o.dg.renderFunctionSignature(w, fn_decl_index, .complete, .{ + try w.print("zig_{s} ", .{@tagName(key)}); + try o.dg.renderFunctionSignature(w, fn_decl.val, .none, .complete, .{ .fmt_ctype_pool_string = fn_name, }); try w.writeAll(" {\n return "); - try o.dg.renderDeclName(w, fn_decl_index, 0); + try o.dg.renderDeclName(w, fn_decl_index); try w.writeByte('('); for (0..fn_info.param_ctypes.len) |arg| { if (arg > 0) try w.writeAll(", "); @@ -2921,21 +2796,26 @@ pub fn genFunc(f: *Function) !void { o.code_header = std.ArrayList(u8).init(gpa); defer o.code_header.deinit(); - const is_global = o.dg.declIsGlobal(decl.val); - const fwd_decl_writer = o.dg.fwdDeclWriter(); - try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); + const fwd = o.dg.fwdDeclWriter(); + try fwd.writeAll("static "); + try o.dg.renderFunctionSignature( + fwd, + decl.val, + decl.alignment, + .forward, + .{ .decl = decl_index }, + ); + try fwd.writeAll(";\n"); - if (zcu.decl_exports.get(decl_index)) |exports| - if (exports.items[0].opts.linkage == .weak) try fwd_decl_writer.writeAll("zig_weak_linkage_fn "); - try o.dg.renderFunctionSignature(fwd_decl_writer, decl_index, .forward, .{ .export_index = 0 }); - try fwd_decl_writer.writeAll(";\n"); - try genExports(o); - - try o.indent_writer.insertNewline(); - if (!is_global) try o.writer().writeAll("static "); if (decl.@"linksection".toSlice(&zcu.intern_pool)) |s| try o.writer().print("zig_linksection_fn({s}) ", .{fmtStringLiteral(s, null)}); - try o.dg.renderFunctionSignature(o.writer(), decl_index, .complete, .{ .export_index = 0 }); + try o.dg.renderFunctionSignature( + o.writer(), + decl.val, + .none, + .complete, + .{ .decl = decl_index }, + ); try o.writer().writeByte(' '); // In case we need to use the header, populate it with a copy of the function @@ -2949,7 +2829,6 @@ pub fn genFunc(f: *Function) !void { const main_body = f.air.getMainBody(); try genBodyResolveState(f, undefined, &.{}, main_body, false); - try o.indent_writer.insertNewline(); // Take advantage of the free_locals map to bucket locals per type. All @@ -3007,20 +2886,25 @@ pub fn genDecl(o: *Object) !void { if (!decl_ty.isFnOrHasRuntimeBitsIgnoreComptime(zcu)) return; if (decl.val.getExternFunc(zcu)) |_| { - const fwd_decl_writer = o.dg.fwdDeclWriter(); - try fwd_decl_writer.writeAll("zig_extern "); - try o.dg.renderFunctionSignature(fwd_decl_writer, decl_index, .forward, .{ .export_index = 0 }); - try fwd_decl_writer.writeAll(";\n"); - try genExports(o); + const fwd = o.dg.fwdDeclWriter(); + try fwd.writeAll("zig_extern "); + try o.dg.renderFunctionSignature( + fwd, + decl.val, + decl.alignment, + .forward, + .{ .@"export" = .{ + .main_name = decl.name, + .extern_name = decl.name, + } }, + ); + try fwd.writeAll(";\n"); } else if (decl.val.getVariable(zcu)) |variable| { - try o.dg.renderFwdDecl(decl_index, variable, .final); - try genExports(o); + try o.dg.renderFwdDecl(decl_index, variable); if (variable.is_extern) return; - const is_global = variable.is_extern or o.dg.declIsGlobal(decl.val); const w = o.writer(); - if (!is_global) try w.writeAll("static "); 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 (decl.@"linksection".toSlice(&zcu.intern_pool)) |s| @@ -3032,46 +2916,27 @@ pub fn genDecl(o: *Object) !void { try w.writeByte(';'); try o.indent_writer.insertNewline(); } else { - const is_global = o.dg.zcu.decl_exports.contains(decl_index); const decl_c_value = .{ .decl = decl_index }; - try genDeclValue(o, decl.val, is_global, decl_c_value, decl.alignment, decl.@"linksection"); + try genDeclValue(o, decl.val, decl_c_value, decl.alignment, decl.@"linksection"); } } pub fn genDeclValue( o: *Object, val: Value, - is_global: bool, decl_c_value: CValue, alignment: Alignment, @"linksection": InternPool.OptionalNullTerminatedString, ) !void { const zcu = o.dg.zcu; - const fwd_decl_writer = o.dg.fwdDeclWriter(); - const ty = val.typeOf(zcu); - try fwd_decl_writer.writeAll(if (is_global) "zig_extern " else "static "); - try o.dg.renderTypeAndName(fwd_decl_writer, ty, decl_c_value, Const, alignment, .complete); - switch (o.dg.pass) { - .decl => |decl_index| { - if (zcu.decl_exports.get(decl_index)) |exports| { - const export_name = exports.items[0].opts.name.toSlice(&zcu.intern_pool); - if (isMangledIdent(export_name, true)) { - try fwd_decl_writer.print(" zig_mangled_final({ }, {s})", .{ - fmtIdent(export_name), fmtStringLiteral(export_name, null), - }); - } - } - }, - .anon => {}, - .flush => unreachable, - } - try fwd_decl_writer.writeAll(";\n"); - try genExports(o); + const fwd = o.dg.fwdDeclWriter(); + try fwd.writeAll("static "); + try o.dg.renderTypeAndName(fwd, ty, decl_c_value, Const, alignment, .complete); + try fwd.writeAll(";\n"); const w = o.writer(); - if (!is_global) try w.writeAll("static "); if (@"linksection".toSlice(&zcu.intern_pool)) |s| try w.print("zig_linksection({s}) ", .{fmtStringLiteral(s, null)}); try o.dg.renderTypeAndName(w, ty, decl_c_value, Const, alignment, .complete); @@ -3080,24 +2945,73 @@ pub fn genDeclValue( try w.writeAll(";\n"); } -pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { - if (true) @panic("TODO jacobly"); - - const tracy = trace(@src()); - defer tracy.end(); - +pub fn genExports(dg: *DeclGen, exported: Zcu.Exported, export_indices: []const u32) !void { const zcu = dg.zcu; - const decl_index = dg.pass.decl; - const decl = zcu.declPtr(decl_index); - const writer = dg.fwdDeclWriter(); + const ip = &zcu.intern_pool; + const fwd = dg.fwdDeclWriter(); - switch (decl.typeOf(zcu).zigTypeTag(zcu)) { - .Fn => if (dg.declIsGlobal(decl.val)) { - try writer.writeAll("zig_extern "); - try dg.renderFunctionSignature(writer, dg.pass.decl, .complete, .{ .export_index = 0 }); - try dg.fwd_decl.appendSlice(";\n"); + const main_name = zcu.all_exports.items[export_indices[0]].opts.name; + try fwd.writeAll("#define "); + switch (exported) { + .decl_index => |decl_index| try dg.renderDeclName(fwd, decl_index), + .value => |value| try DeclGen.renderAnonDeclName(fwd, Value.fromInterned(value)), + } + try fwd.writeByte(' '); + try fwd.print("{ }", .{fmtIdent(main_name.toSlice(ip))}); + try fwd.writeByte('\n'); + + const is_const = switch (ip.indexToKey(exported.getValue(zcu).toIntern())) { + .func, .extern_func => return for (export_indices) |export_index| { + const @"export" = &zcu.all_exports.items[export_index]; + try fwd.writeAll("zig_extern "); + if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage_fn "); + try dg.renderFunctionSignature( + fwd, + exported.getValue(zcu), + exported.getAlign(zcu), + .forward, + .{ .@"export" = .{ + .main_name = main_name, + .extern_name = @"export".opts.name, + } }, + ); + try fwd.writeAll(";\n"); }, - else => {}, + .variable => |variable| variable.is_const, + else => true, + }; + for (export_indices) |export_index| { + const @"export" = &zcu.all_exports.items[export_index]; + try fwd.writeAll("zig_extern "); + if (@"export".opts.linkage == .weak) try fwd.writeAll("zig_weak_linkage "); + const extern_name = @"export".opts.name.toSlice(ip); + const is_mangled = isMangledIdent(extern_name, true); + const is_export = @"export".opts.name != main_name; + try dg.renderTypeAndName( + fwd, + exported.getValue(zcu).typeOf(zcu), + .{ .identifier = extern_name }, + CQualifiers.init(.{ .@"const" = is_const }), + exported.getAlign(zcu), + .complete, + ); + if (is_mangled and is_export) { + try fwd.print(" zig_mangled_export({ }, {s}, {s})", .{ + fmtIdent(extern_name), + fmtStringLiteral(extern_name, null), + fmtStringLiteral(main_name.toSlice(ip), null), + }); + } else if (is_mangled) { + try fwd.print(" zig_mangled({ }, {s})", .{ + fmtIdent(extern_name), fmtStringLiteral(extern_name, null), + }); + } else if (is_export) { + try fwd.print(" zig_export({s}, {s})", .{ + fmtStringLiteral(main_name.toSlice(ip), null), + fmtStringLiteral(extern_name, null), + }); + } + try fwd.writeAll(";\n"); } } @@ -4554,7 +4468,7 @@ fn airCall( }; }; switch (modifier) { - .auto, .always_tail => try f.object.dg.renderDeclName(writer, fn_decl, 0), + .auto, .always_tail => try f.object.dg.renderDeclName(writer, fn_decl), inline .never_tail, .never_inline => |m| try writer.writeAll(try f.getLazyFnName( @unionInit(LazyFnKey, @tagName(m), fn_decl), @unionInit(LazyFnValue.Data, @tagName(m), {}), diff --git a/src/link.zig b/src/link.zig index 009b38a681..298d81d80c 100644 --- a/src/link.zig +++ b/src/link.zig @@ -679,7 +679,6 @@ pub const File = struct { if (build_options.only_c) @compileError("unreachable"); switch (base.tag) { .plan9, - .c, .spirv, .nvptx, => {}, diff --git a/src/link/C.zig b/src/link/C.zig index 8372029d2d..be8397e196 100644 --- a/src/link/C.zig +++ b/src/link/C.zig @@ -39,6 +39,9 @@ anon_decls: std.AutoArrayHashMapUnmanaged(InternPool.Index, DeclBlock) = .{}, /// the keys of `anon_decls`. aligned_anon_decls: std.AutoArrayHashMapUnmanaged(InternPool.Index, Alignment) = .{}, +exported_decls: std.AutoArrayHashMapUnmanaged(InternPool.DeclIndex, ExportedBlock) = .{}, +exported_values: std.AutoArrayHashMapUnmanaged(InternPool.Index, ExportedBlock) = .{}, + /// Optimization, `updateDecl` reuses this buffer rather than creating a new /// one with every call. fwd_decl_buf: std.ArrayListUnmanaged(u8) = .{}, @@ -80,6 +83,11 @@ pub const DeclBlock = struct { } }; +/// Per-exported-symbol data. +pub const ExportedBlock = struct { + fwd_decl: String = String.empty, +}; + pub fn getString(this: C, s: String) []const u8 { return this.string_bytes.items[s.start..][0..s.len]; } @@ -183,8 +191,6 @@ pub fn updateFunc( air: Air, liveness: Liveness, ) !void { - if (true) @panic("TODO jacobly"); - const gpa = self.base.comp.gpa; const func = zcu.funcInfo(func_index); @@ -240,9 +246,13 @@ pub fn updateFunc( function.deinit(); } + try zcu.failed_analysis.ensureUnusedCapacity(gpa, 1); codegen.genFunc(&function) catch |err| switch (err) { error.AnalysisFail => { - try zcu.failed_decls.put(gpa, decl_index, function.object.dg.error_msg.?); + zcu.failed_analysis.putAssumeCapacityNoClobber( + InternPool.AnalUnit.wrap(.{ .decl = decl_index }), + function.object.dg.error_msg.?, + ); return; }, else => |e| return e, @@ -252,8 +262,6 @@ pub fn updateFunc( } fn updateAnonDecl(self: *C, zcu: *Zcu, i: usize) !void { - if (true) @panic("TODO jacobly"); - const gpa = self.base.comp.gpa; const anon_decl = self.anon_decls.keys()[i]; @@ -292,7 +300,7 @@ fn updateAnonDecl(self: *C, zcu: *Zcu, i: usize) !void { const c_value: codegen.CValue = .{ .constant = Value.fromInterned(anon_decl) }; const alignment: Alignment = self.aligned_anon_decls.get(anon_decl) orelse .none; - codegen.genDeclValue(&object, c_value.constant, false, c_value, alignment, .none) catch |err| switch (err) { + codegen.genDeclValue(&object, c_value.constant, c_value, alignment, .none) catch |err| switch (err) { error.AnalysisFail => { @panic("TODO: C backend AnalysisFail on anonymous decl"); //try zcu.failed_decls.put(gpa, decl_index, object.dg.error_msg.?); @@ -310,8 +318,6 @@ fn updateAnonDecl(self: *C, zcu: *Zcu, i: usize) !void { } pub fn updateDecl(self: *C, zcu: *Zcu, decl_index: InternPool.DeclIndex) !void { - if (true) @panic("TODO jacobly"); - const tracy = trace(@src()); defer tracy.end(); @@ -357,9 +363,13 @@ pub fn updateDecl(self: *C, zcu: *Zcu, decl_index: InternPool.DeclIndex) !void { code.* = object.code.moveToUnmanaged(); } + try zcu.failed_analysis.ensureUnusedCapacity(gpa, 1); codegen.genDecl(&object) catch |err| switch (err) { error.AnalysisFail => { - try zcu.failed_decls.put(gpa, decl_index, object.dg.error_msg.?); + zcu.failed_analysis.putAssumeCapacityNoClobber( + InternPool.AnalUnit.wrap(.{ .decl = decl_index }), + object.dg.error_msg.?, + ); return; }, else => |e| return e, @@ -396,8 +406,6 @@ fn abiDefines(self: *C, target: std.Target) !std.ArrayList(u8) { } pub fn flushModule(self: *C, arena: Allocator, prog_node: std.Progress.Node) !void { - if (true) @panic("TODO jacobly"); - _ = arena; // Has the same lifetime as the call to Compilation.update. const tracy = trace(@src()); @@ -460,26 +468,39 @@ pub fn flushModule(self: *C, arena: Allocator, prog_node: std.Progress.Node) !vo var export_names: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void) = .{}; defer export_names.deinit(gpa); try export_names.ensureTotalCapacity(gpa, @intCast(zcu.single_exports.count())); - for (zcu.single_exports.values()) |export_idx| { - export_names.putAssumeCapacity(gpa, zcu.all_exports.items[export_idx].opts.name, {}); + for (zcu.single_exports.values()) |export_index| { + export_names.putAssumeCapacity(zcu.all_exports.items[export_index].opts.name, {}); } for (zcu.multi_exports.values()) |info| { - try export_names.ensureUnusedCapacity(info.len); - for (zcu.all_exports.items[info.index..][0..info.len]) |export_idx| { - export_names.putAssumeCapacity(gpa, zcu.all_exports.items[export_idx].opts.name, {}); + try export_names.ensureUnusedCapacity(gpa, info.len); + for (zcu.all_exports.items[info.index..][0..info.len]) |@"export"| { + export_names.putAssumeCapacity(@"export".opts.name, {}); } } - for (self.anon_decls.values()) |*decl_block| { - try self.flushDeclBlock(zcu, zcu.root_mod, &f, decl_block, export_names, .none); - } + for (self.anon_decls.keys(), self.anon_decls.values()) |value, *decl_block| try self.flushDeclBlock( + zcu, + zcu.root_mod, + &f, + decl_block, + self.exported_values.getPtr(value), + export_names, + .none, + ); for (self.decl_table.keys(), self.decl_table.values()) |decl_index, *decl_block| { const decl = zcu.declPtr(decl_index); - assert(decl.has_tv); - const extern_symbol_name = if (decl.isExtern(zcu)) decl.name.toOptional() else .none; + const extern_name = if (decl.isExtern(zcu)) decl.name.toOptional() else .none; const mod = zcu.namespacePtr(decl.src_namespace).file_scope.mod; - try self.flushDeclBlock(zcu, mod, &f, decl_block, export_names, extern_symbol_name); + try self.flushDeclBlock( + zcu, + mod, + &f, + decl_block, + self.exported_decls.getPtr(decl_index), + export_names, + extern_name, + ); } } @@ -512,12 +533,16 @@ pub fn flushModule(self: *C, arena: Allocator, prog_node: std.Progress.Node) !vo f.file_size += lazy_fwd_decl_len; // Now the code. - const anon_decl_values = self.anon_decls.values(); - const decl_values = self.decl_table.values(); - try f.all_buffers.ensureUnusedCapacity(gpa, 1 + anon_decl_values.len + decl_values.len); + try f.all_buffers.ensureUnusedCapacity(gpa, 1 + (self.anon_decls.count() + self.decl_table.count()) * 2); f.appendBufAssumeCapacity(self.lazy_code_buf.items); - for (anon_decl_values) |db| f.appendBufAssumeCapacity(self.getString(db.code)); - for (decl_values) |db| f.appendBufAssumeCapacity(self.getString(db.code)); + for (self.anon_decls.keys(), self.anon_decls.values()) |anon_decl, decl_block| f.appendCodeAssumeCapacity( + self.exported_values.contains(anon_decl), + self.getString(decl_block.code), + ); + for (self.decl_table.keys(), self.decl_table.values()) |decl_index, decl_block| f.appendCodeAssumeCapacity( + self.exported_decls.contains(decl_index), + self.getString(decl_block.code), + ); const file = self.base.file.?; try file.setEndPos(f.file_size); @@ -547,6 +572,12 @@ const Flush = struct { f.file_size += buf.len; } + fn appendCodeAssumeCapacity(f: *Flush, is_extern: bool, code: []const u8) void { + if (code.len == 0) return; + f.appendBufAssumeCapacity(if (is_extern) "\nzig_extern " else "\nstatic "); + f.appendBufAssumeCapacity(code); + } + fn deinit(f: *Flush, gpa: Allocator) void { f.all_buffers.deinit(gpa); f.asm_buf.deinit(gpa); @@ -734,19 +765,20 @@ fn flushDeclBlock( zcu: *Zcu, mod: *Module, f: *Flush, - decl_block: *DeclBlock, + decl_block: *const DeclBlock, + exported_block: ?*const ExportedBlock, export_names: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, void), - extern_symbol_name: InternPool.OptionalNullTerminatedString, + extern_name: InternPool.OptionalNullTerminatedString, ) FlushDeclError!void { const gpa = self.base.comp.gpa; try self.flushLazyFns(zcu, mod, f, &decl_block.ctype_pool, decl_block.lazy_fns); try f.all_buffers.ensureUnusedCapacity(gpa, 1); - fwd_decl: { - if (extern_symbol_name.unwrap()) |name| { - if (export_names.contains(name)) break :fwd_decl; - } - f.appendBufAssumeCapacity(self.getString(decl_block.fwd_decl)); - } + // avoid emitting extern decls that are already exported + if (extern_name.unwrap()) |name| if (export_names.contains(name)) return; + f.appendBufAssumeCapacity(self.getString(if (exported_block) |exported| + exported.fwd_decl + else + decl_block.fwd_decl)); } pub fn flushEmitH(zcu: *Zcu) !void { @@ -798,8 +830,56 @@ pub fn updateExports( exported: Zcu.Exported, export_indices: []const u32, ) !void { - _ = self; - _ = zcu; - _ = exported; - _ = export_indices; + const gpa = self.base.comp.gpa; + const mod, const pass: codegen.DeclGen.Pass, const decl_block, const exported_block = switch (exported) { + .decl_index => |decl_index| .{ + zcu.namespacePtr(zcu.declPtr(decl_index).src_namespace).file_scope.mod, + .{ .decl = decl_index }, + self.decl_table.getPtr(decl_index).?, + (try self.exported_decls.getOrPut(gpa, decl_index)).value_ptr, + }, + .value => |value| .{ + zcu.root_mod, + .{ .anon = value }, + self.anon_decls.getPtr(value).?, + (try self.exported_values.getOrPut(gpa, value)).value_ptr, + }, + }; + const ctype_pool = &decl_block.ctype_pool; + const fwd_decl = &self.fwd_decl_buf; + fwd_decl.clearRetainingCapacity(); + var dg: codegen.DeclGen = .{ + .gpa = gpa, + .zcu = zcu, + .mod = mod, + .error_msg = null, + .pass = pass, + .is_naked_fn = false, + .fwd_decl = fwd_decl.toManaged(gpa), + .ctype_pool = decl_block.ctype_pool, + .scratch = .{}, + .anon_decl_deps = .{}, + .aligned_anon_decls = .{}, + }; + defer { + assert(dg.anon_decl_deps.count() == 0); + assert(dg.aligned_anon_decls.count() == 0); + fwd_decl.* = dg.fwd_decl.moveToUnmanaged(); + ctype_pool.* = dg.ctype_pool.move(); + ctype_pool.freeUnusedCapacity(gpa); + dg.scratch.deinit(gpa); + } + try codegen.genExports(&dg, exported, export_indices); + exported_block.* = .{ .fwd_decl = try self.addString(dg.fwd_decl.items) }; +} + +pub fn deleteExport( + self: *C, + exported: Zcu.Exported, + _: InternPool.NullTerminatedString, +) void { + switch (exported) { + .decl_index => |decl_index| _ = self.exported_decls.swapRemove(decl_index), + .value => |value| _ = self.exported_values.swapRemove(value), + } }