authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-10-26 20:32:16-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-10-26 20:32:16-07:00
log4bc88dd11641a664d80b00ad784bafc6da776697
tree0dfff49ae93d5826a0c10e3819a1225e4f992693
parentba9e38847a097777370b4721780d5dbefda1b12f

link: support exporting constant values without a Decl

The main motivating change here is to prevent the creation of a fake Decl object by the frontend in order to `@export()` a value. Instead, `link.updateDeclExports` is renamed to `link.updateExports` and accepts a tagged union which can be either a Decl.Index or a InternPool.Index.

13 files changed, 424 insertions(+), 226 deletions(-)

src/Module.zig+110-69
...@@ -70,6 +70,8 @@ local_zir_cache: Compilation.Directory,...@@ -70,6 +70,8 @@ local_zir_cache: Compilation.Directory,
70/// The Export memory is owned by the `export_owners` table; the slice itself70/// The Export memory is owned by the `export_owners` table; the slice itself
71/// is owned by this table. The slice is guaranteed to not be empty.71/// is owned by this table. The slice is guaranteed to not be empty.
72decl_exports: std.AutoArrayHashMapUnmanaged(Decl.Index, ArrayListUnmanaged(*Export)) = .{},72decl_exports: std.AutoArrayHashMapUnmanaged(Decl.Index, ArrayListUnmanaged(*Export)) = .{},
73/// Same as `decl_exports` but for exported constant values.
74value_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, ArrayListUnmanaged(*Export)) = .{},
73/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl75/// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl
74/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that76/// is modified. Note that the key of this table is not the Decl being exported, but the Decl that
75/// is performing the export of another Decl.77/// is performing the export of another Decl.
...@@ -244,6 +246,13 @@ pub const GlobalEmitH = struct {...@@ -244,6 +246,13 @@ pub const GlobalEmitH = struct {
244246
245pub const ErrorInt = u32;247pub const ErrorInt = u32;
246248
249pub const Exported = union(enum) {
250 /// The Decl being exported. Note this is *not* the Decl performing the export.
251 decl_index: Decl.Index,
252 /// Constant value being exported.
253 value: InternPool.Index,
254};
255
247pub const Export = struct {256pub const Export = struct {
248 opts: Options,257 opts: Options,
249 src: LazySrcLoc,258 src: LazySrcLoc,
...@@ -252,8 +261,7 @@ pub const Export = struct {...@@ -252,8 +261,7 @@ pub const Export = struct {
252 /// The Decl containing the export statement. Inline function calls261 /// The Decl containing the export statement. Inline function calls
253 /// may cause this to be different from the owner_decl.262 /// may cause this to be different from the owner_decl.
254 src_decl: Decl.Index,263 src_decl: Decl.Index,
255 /// The Decl being exported. Note this is *not* the Decl performing the export.264 exported: Exported,
256 exported_decl: Decl.Index,
257 status: enum {265 status: enum {
258 in_progress,266 in_progress,
259 failed,267 failed,
...@@ -2575,6 +2583,11 @@ pub fn deinit(mod: *Module) void {...@@ -2575,6 +2583,11 @@ pub fn deinit(mod: *Module) void {
2575 }2583 }
2576 mod.decl_exports.deinit(gpa);2584 mod.decl_exports.deinit(gpa);
25772585
2586 for (mod.value_exports.values()) |*export_list| {
2587 export_list.deinit(gpa);
2588 }
2589 mod.value_exports.deinit(gpa);
2590
2578 for (mod.export_owners.values()) |*value| {2591 for (mod.export_owners.values()) |*value| {
2579 freeExportList(gpa, value);2592 freeExportList(gpa, value);
2580 }2593 }
...@@ -4620,36 +4633,49 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) Allocator.Error!void...@@ -4620,36 +4633,49 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) Allocator.Error!void
4620 var export_owners = (mod.export_owners.fetchSwapRemove(decl_index) orelse return).value;4633 var export_owners = (mod.export_owners.fetchSwapRemove(decl_index) orelse return).value;
46214634
4622 for (export_owners.items) |exp| {4635 for (export_owners.items) |exp| {
4623 if (mod.decl_exports.getPtr(exp.exported_decl)) |value_ptr| {4636 switch (exp.exported) {
4624 // Remove exports with owner_decl matching the regenerating decl.4637 .decl_index => |exported_decl_index| {
4625 const list = value_ptr.items;4638 if (mod.decl_exports.getPtr(exported_decl_index)) |export_list| {
4626 var i: usize = 0;4639 // Remove exports with owner_decl matching the regenerating decl.
4627 var new_len = list.len;4640 const list = export_list.items;
4628 while (i < new_len) {4641 var i: usize = 0;
4629 if (list[i].owner_decl == decl_index) {4642 var new_len = list.len;
4630 mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]);4643 while (i < new_len) {
4631 new_len -= 1;4644 if (list[i].owner_decl == decl_index) {
4632 } else {4645 mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]);
4633 i += 1;4646 new_len -= 1;
4647 } else {
4648 i += 1;
4649 }
4650 }
4651 export_list.shrinkAndFree(mod.gpa, new_len);
4652 if (new_len == 0) {
4653 assert(mod.decl_exports.swapRemove(exported_decl_index));
4654 }
4634 }4655 }
4635 }4656 },
4636 value_ptr.shrinkAndFree(mod.gpa, new_len);4657 .value => |value| {
4637 if (new_len == 0) {4658 if (mod.value_exports.getPtr(value)) |export_list| {
4638 assert(mod.decl_exports.swapRemove(exp.exported_decl));4659 // Remove exports with owner_decl matching the regenerating decl.
4639 }4660 const list = export_list.items;
4640 }4661 var i: usize = 0;
4641 if (mod.comp.bin_file.cast(link.File.Elf)) |elf| {4662 var new_len = list.len;
4642 elf.deleteDeclExport(decl_index, exp.opts.name);4663 while (i < new_len) {
4643 }4664 if (list[i].owner_decl == decl_index) {
4644 if (mod.comp.bin_file.cast(link.File.MachO)) |macho| {4665 mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]);
4645 try macho.deleteDeclExport(decl_index, exp.opts.name);4666 new_len -= 1;
4646 }4667 } else {
4647 if (mod.comp.bin_file.cast(link.File.Wasm)) |wasm| {4668 i += 1;
4648 wasm.deleteDeclExport(decl_index);4669 }
4649 }4670 }
4650 if (mod.comp.bin_file.cast(link.File.Coff)) |coff| {4671 export_list.shrinkAndFree(mod.gpa, new_len);
4651 coff.deleteDeclExport(decl_index, exp.opts.name);4672 if (new_len == 0) {
4673 assert(mod.value_exports.swapRemove(value));
4674 }
4675 }
4676 },
4652 }4677 }
4678 try mod.comp.bin_file.deleteDeclExport(decl_index, exp.opts.name);
4653 if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| {4679 if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| {
4654 failed_kv.value.destroy(mod.gpa);4680 failed_kv.value.destroy(mod.gpa);
4655 }4681 }
...@@ -5503,48 +5529,63 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {...@@ -5503,48 +5529,63 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {
5503/// reporting compile errors. In this function we emit exported symbol collision5529/// reporting compile errors. In this function we emit exported symbol collision
5504/// errors and communicate exported symbols to the linker backend.5530/// errors and communicate exported symbols to the linker backend.
5505pub fn processExports(mod: *Module) !void {5531pub fn processExports(mod: *Module) !void {
5506 const gpa = mod.gpa;
5507 // Map symbol names to `Export` for name collision detection.5532 // Map symbol names to `Export` for name collision detection.
5508 var symbol_exports: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, *Export) = .{};5533 var symbol_exports: SymbolExports = .{};
5509 defer symbol_exports.deinit(gpa);5534 defer symbol_exports.deinit(mod.gpa);
55105535
5511 var it = mod.decl_exports.iterator();5536 for (mod.decl_exports.keys(), mod.decl_exports.values()) |exported_decl, exports_list| {
5512 while (it.next()) |entry| {5537 const exported: Exported = .{ .decl_index = exported_decl };
5513 const exported_decl = entry.key_ptr.*;5538 try processExportsInner(mod, &symbol_exports, exported, exports_list.items);
5514 const exports = entry.value_ptr.items;5539 }
5515 for (exports) |new_export| {5540
5516 const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name);5541 for (mod.value_exports.keys(), mod.value_exports.values()) |exported_value, exports_list| {
5517 if (gop.found_existing) {5542 const exported: Exported = .{ .value = exported_value };
5518 new_export.status = .failed_retryable;5543 try processExportsInner(mod, &symbol_exports, exported, exports_list.items);
5519 try mod.failed_exports.ensureUnusedCapacity(gpa, 1);5544 }
5520 const src_loc = new_export.getSrcLoc(mod);5545}
5521 const msg = try ErrorMsg.create(gpa, src_loc, "exported symbol collision: {}", .{5546
5522 new_export.opts.name.fmt(&mod.intern_pool),5547const SymbolExports = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, *Export);
5523 });5548
5524 errdefer msg.destroy(gpa);5549fn processExportsInner(
5525 const other_export = gop.value_ptr.*;5550 mod: *Module,
5526 const other_src_loc = other_export.getSrcLoc(mod);5551 symbol_exports: *SymbolExports,
5527 try mod.errNoteNonLazy(other_src_loc, msg, "other symbol here", .{});5552 exported: Exported,
5528 mod.failed_exports.putAssumeCapacityNoClobber(new_export, msg);5553 exports: []const *Export,
5529 new_export.status = .failed;5554) error{OutOfMemory}!void {
5530 } else {5555 const gpa = mod.gpa;
5531 gop.value_ptr.* = new_export;5556
5532 }5557 for (exports) |new_export| {
5558 const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name);
5559 if (gop.found_existing) {
5560 new_export.status = .failed_retryable;
5561 try mod.failed_exports.ensureUnusedCapacity(gpa, 1);
5562 const src_loc = new_export.getSrcLoc(mod);
5563 const msg = try ErrorMsg.create(gpa, src_loc, "exported symbol collision: {}", .{
5564 new_export.opts.name.fmt(&mod.intern_pool),
5565 });
5566 errdefer msg.destroy(gpa);
5567 const other_export = gop.value_ptr.*;
5568 const other_src_loc = other_export.getSrcLoc(mod);
5569 try mod.errNoteNonLazy(other_src_loc, msg, "other symbol here", .{});
5570 mod.failed_exports.putAssumeCapacityNoClobber(new_export, msg);
5571 new_export.status = .failed;
5572 } else {
5573 gop.value_ptr.* = new_export;
5533 }5574 }
5534 mod.comp.bin_file.updateDeclExports(mod, exported_decl, exports) catch |err| switch (err) {
5535 error.OutOfMemory => return error.OutOfMemory,
5536 else => {
5537 const new_export = exports[0];
5538 new_export.status = .failed_retryable;
5539 try mod.failed_exports.ensureUnusedCapacity(gpa, 1);
5540 const src_loc = new_export.getSrcLoc(mod);
5541 const msg = try ErrorMsg.create(gpa, src_loc, "unable to export: {s}", .{
5542 @errorName(err),
5543 });
5544 mod.failed_exports.putAssumeCapacityNoClobber(new_export, msg);
5545 },
5546 };
5547 }5575 }
5576 mod.comp.bin_file.updateExports(mod, exported, exports) catch |err| switch (err) {
5577 error.OutOfMemory => return error.OutOfMemory,
5578 else => {
5579 const new_export = exports[0];
5580 new_export.status = .failed_retryable;
5581 try mod.failed_exports.ensureUnusedCapacity(gpa, 1);
5582 const src_loc = new_export.getSrcLoc(mod);
5583 const msg = try ErrorMsg.create(gpa, src_loc, "unable to export: {s}", .{
5584 @errorName(err),
5585 });
5586 mod.failed_exports.putAssumeCapacityNoClobber(new_export, msg);
5587 },
5588 };
5548}5589}
55495590
5550pub fn populateTestFunctions(5591pub fn populateTestFunctions(
src/Sema.zig+47-31
...@@ -6026,6 +6026,7 @@ fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -6026,6 +6026,7 @@ fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
6026 const tracy = trace(@src());6026 const tracy = trace(@src());
6027 defer tracy.end();6027 defer tracy.end();
60286028
6029 const mod = sema.mod;
6029 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;6030 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
6030 const extra = sema.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data;6031 const extra = sema.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data;
6031 const src = inst_data.src();6032 const src = inst_data.src();
...@@ -6035,12 +6036,21 @@ fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -6035,12 +6036,21 @@ fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
6035 .needed_comptime_reason = "export target must be comptime-known",6036 .needed_comptime_reason = "export target must be comptime-known",
6036 });6037 });
6037 const options = try sema.resolveExportOptions(block, options_src, extra.options);6038 const options = try sema.resolveExportOptions(block, options_src, extra.options);
6038 const decl_index = if (operand.val.getFunction(sema.mod)) |function| function.owner_decl else blk: {6039 if (options.linkage == .Internal)
6039 var anon_decl = try block.startAnonDecl(); // TODO: export value without Decl6040 return;
6040 defer anon_decl.deinit();6041 if (operand.val.getFunction(mod)) |function| {
6041 break :blk try anon_decl.finish(operand.ty, operand.val, .none);6042 const decl_index = function.owner_decl;
6042 };6043 return sema.analyzeExport(block, src, options, decl_index);
6043 try sema.analyzeExport(block, src, options, decl_index);6044 }
6045
6046 try addExport(mod, .{
6047 .opts = options,
6048 .src = src,
6049 .owner_decl = sema.owner_decl_index,
6050 .src_decl = block.src_decl,
6051 .exported = .{ .value = operand.val.toIntern() },
6052 .status = .in_progress,
6053 });
6044}6054}
60456055
6046pub fn analyzeExport(6056pub fn analyzeExport(
...@@ -6050,12 +6060,11 @@ pub fn analyzeExport(...@@ -6050,12 +6060,11 @@ pub fn analyzeExport(
6050 options: Module.Export.Options,6060 options: Module.Export.Options,
6051 exported_decl_index: Decl.Index,6061 exported_decl_index: Decl.Index,
6052) !void {6062) !void {
6053 const Export = Module.Export;6063 const gpa = sema.gpa;
6054 const mod = sema.mod;6064 const mod = sema.mod;
60556065
6056 if (options.linkage == .Internal) {6066 if (options.linkage == .Internal)
6057 return;6067 return;
6058 }
60596068
6060 try mod.ensureDeclAnalyzed(exported_decl_index);6069 try mod.ensureDeclAnalyzed(exported_decl_index);
6061 const exported_decl = mod.declPtr(exported_decl_index);6070 const exported_decl = mod.declPtr(exported_decl_index);
...@@ -6063,7 +6072,7 @@ pub fn analyzeExport(...@@ -6063,7 +6072,7 @@ pub fn analyzeExport(
6063 if (!try sema.validateExternType(exported_decl.ty, .other)) {6072 if (!try sema.validateExternType(exported_decl.ty, .other)) {
6064 const msg = msg: {6073 const msg = msg: {
6065 const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(mod)});6074 const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(mod)});
6066 errdefer msg.destroy(sema.gpa);6075 errdefer msg.destroy(gpa);
60676076
6068 const src_decl = mod.declPtr(block.src_decl);6077 const src_decl = mod.declPtr(block.src_decl);
6069 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), exported_decl.ty, .other);6078 try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), exported_decl.ty, .other);
...@@ -6083,38 +6092,45 @@ pub fn analyzeExport(...@@ -6083,38 +6092,45 @@ pub fn analyzeExport(
6083 try mod.markDeclAlive(exported_decl);6092 try mod.markDeclAlive(exported_decl);
6084 try sema.maybeQueueFuncBodyAnalysis(exported_decl_index);6093 try sema.maybeQueueFuncBodyAnalysis(exported_decl_index);
60856094
6086 const gpa = sema.gpa;6095 try addExport(mod, .{
6096 .opts = options,
6097 .src = src,
6098 .owner_decl = sema.owner_decl_index,
6099 .src_decl = block.src_decl,
6100 .exported = .{ .decl_index = exported_decl_index },
6101 .status = .in_progress,
6102 });
6103}
6104
6105fn addExport(mod: *Module, export_init: Module.Export) error{OutOfMemory}!void {
6106 const gpa = mod.gpa;
60876107
6088 try mod.decl_exports.ensureUnusedCapacity(gpa, 1);6108 try mod.decl_exports.ensureUnusedCapacity(gpa, 1);
6109 try mod.value_exports.ensureUnusedCapacity(gpa, 1);
6089 try mod.export_owners.ensureUnusedCapacity(gpa, 1);6110 try mod.export_owners.ensureUnusedCapacity(gpa, 1);
60906111
6091 const new_export = try gpa.create(Export);6112 const new_export = try gpa.create(Module.Export);
6092 errdefer gpa.destroy(new_export);6113 errdefer gpa.destroy(new_export);
60936114
6094 new_export.* = .{6115 new_export.* = export_init;
6095 .opts = options,
6096 .src = src,
6097 .owner_decl = sema.owner_decl_index,
6098 .src_decl = block.src_decl,
6099 .exported_decl = exported_decl_index,
6100 .status = .in_progress,
6101 };
61026116
6103 // Add to export_owners table.6117 const eo_gop = mod.export_owners.getOrPutAssumeCapacity(export_init.owner_decl);
6104 const eo_gop = mod.export_owners.getOrPutAssumeCapacity(sema.owner_decl_index);6118 if (!eo_gop.found_existing) eo_gop.value_ptr.* = .{};
6105 if (!eo_gop.found_existing) {
6106 eo_gop.value_ptr.* = .{};
6107 }
6108 try eo_gop.value_ptr.append(gpa, new_export);6119 try eo_gop.value_ptr.append(gpa, new_export);
6109 errdefer _ = eo_gop.value_ptr.pop();6120 errdefer _ = eo_gop.value_ptr.pop();
61106121
6111 // Add to exported_decl table.6122 switch (export_init.exported) {
6112 const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl_index);6123 .decl_index => |decl_index| {
6113 if (!de_gop.found_existing) {6124 const de_gop = mod.decl_exports.getOrPutAssumeCapacity(decl_index);
6114 de_gop.value_ptr.* = .{};6125 if (!de_gop.found_existing) de_gop.value_ptr.* = .{};
6126 try de_gop.value_ptr.append(gpa, new_export);
6127 },
6128 .value => |value| {
6129 const ve_gop = mod.value_exports.getOrPutAssumeCapacity(value);
6130 if (!ve_gop.found_existing) ve_gop.value_ptr.* = .{};
6131 try ve_gop.value_ptr.append(gpa, new_export);
6132 },
6115 }6133 }
6116 try de_gop.value_ptr.append(gpa, new_export);
6117 errdefer _ = de_gop.value_ptr.pop();
6118}6134}
61196135
6120fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {6136fn zirSetAlignStack(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void {
src/codegen/llvm.zig+141-73
...@@ -1144,26 +1144,40 @@ pub const Object = struct {...@@ -1144,26 +1144,40 @@ pub const Object = struct {
11441144
1145 for (mod.decl_exports.keys(), mod.decl_exports.values()) |decl_index, export_list| {1145 for (mod.decl_exports.keys(), mod.decl_exports.values()) |decl_index, export_list| {
1146 const global = object.decl_map.get(decl_index) orelse continue;1146 const global = object.decl_map.get(decl_index) orelse continue;
1147 const global_base = global.toConst().getBase(&object.builder);1147 try resolveGlobalCollisions(object, global, export_list.items);
1148 for (export_list.items) |exp| {1148 }
1149 // Detect if the LLVM global has already been created as an extern. In such1149
1150 // case, we need to replace all uses of it with this exported global.1150 for (mod.value_exports.keys(), mod.value_exports.values()) |val, export_list| {
1151 const exp_name = object.builder.stringIfExists(mod.intern_pool.stringToSlice(exp.opts.name)) orelse continue;1151 const global = object.anon_decl_map.get(val) orelse continue;
11521152 try resolveGlobalCollisions(object, global, export_list.items);
1153 const other_global = object.builder.getGlobal(exp_name) orelse continue;1153 }
1154 if (other_global.toConst().getBase(&object.builder) == global_base) continue;1154 }
11551155
1156 try global.takeName(other_global, &object.builder);1156 fn resolveGlobalCollisions(
1157 try other_global.replace(global, &object.builder);1157 object: *Object,
1158 // Problem: now we need to replace in the decl_map that1158 global: Builder.Global.Index,
1159 // the extern decl index points to this new global. However we don't1159 export_list: []const *Module.Export,
1160 // know the decl index.1160 ) !void {
1161 // Even if we did, a future incremental update to the extern would then1161 const mod = object.module;
1162 // treat the LLVM global as an extern rather than an export, so it would1162 const global_base = global.toConst().getBase(&object.builder);
1163 // need a way to check that.1163 for (export_list) |exp| {
1164 // This is a TODO that needs to be solved when making1164 // Detect if the LLVM global has already been created as an extern. In such
1165 // the LLVM backend support incremental compilation.1165 // case, we need to replace all uses of it with this exported global.
1166 }1166 const exp_name = object.builder.stringIfExists(mod.intern_pool.stringToSlice(exp.opts.name)) orelse continue;
1167
1168 const other_global = object.builder.getGlobal(exp_name) orelse continue;
1169 if (other_global.toConst().getBase(&object.builder) == global_base) continue;
1170
1171 try global.takeName(other_global, &object.builder);
1172 try other_global.replace(global, &object.builder);
1173 // Problem: now we need to replace in the decl_map that
1174 // the extern decl index points to this new global. However we don't
1175 // know the decl index.
1176 // Even if we did, a future incremental update to the extern would then
1177 // treat the LLVM global as an extern rather than an export, so it would
1178 // need a way to check that.
1179 // This is a TODO that needs to be solved when making
1180 // the LLVM backend support incremental compilation.
1167 }1181 }
1168 }1182 }
11691183
...@@ -1642,7 +1656,7 @@ pub const Object = struct {...@@ -1642,7 +1656,7 @@ pub const Object = struct {
16421656
1643 try fg.wip.finish();1657 try fg.wip.finish();
16441658
1645 try o.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index));1659 try o.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
1646 }1660 }
16471661
1648 pub fn updateDecl(self: *Object, module: *Module, decl_index: Module.Decl.Index) !void {1662 pub fn updateDecl(self: *Object, module: *Module, decl_index: Module.Decl.Index) !void {
...@@ -1662,18 +1676,22 @@ pub const Object = struct {...@@ -1662,18 +1676,22 @@ pub const Object = struct {
1662 },1676 },
1663 else => |e| return e,1677 else => |e| return e,
1664 };1678 };
1665 try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));1679 try self.updateExports(module, .{ .decl_index = decl_index }, module.getDeclExports(decl_index));
1666 }1680 }
16671681
1668 pub fn updateDeclExports(1682 pub fn updateExports(
1669 self: *Object,1683 self: *Object,
1670 mod: *Module,1684 mod: *Module,
1671 decl_index: Module.Decl.Index,1685 exported: Module.Exported,
1672 exports: []const *Module.Export,1686 exports: []const *Module.Export,
1673 ) !void {1687 ) link.File.UpdateExportsError!void {
1688 const decl_index = switch (exported) {
1689 .decl_index => |i| i,
1690 .value => |val| return updateExportedValue(self, mod, val, exports),
1691 };
1674 const gpa = mod.gpa;1692 const gpa = mod.gpa;
1675 // If the module does not already have the function, we ignore this function call1693 // If the module does not already have the function, we ignore this function call
1676 // because we call `updateDeclExports` at the end of `updateFunc` and `updateDecl`.1694 // because we call `updateExports` at the end of `updateFunc` and `updateDecl`.
1677 const global_index = self.decl_map.get(decl_index) orelse return;1695 const global_index = self.decl_map.get(decl_index) orelse return;
1678 const decl = mod.declPtr(decl_index);1696 const decl = mod.declPtr(decl_index);
1679 if (decl.isExtern(mod)) {1697 if (decl.isExtern(mod)) {
...@@ -1733,8 +1751,7 @@ pub const Object = struct {...@@ -1733,8 +1751,7 @@ pub const Object = struct {
1733 mod.intern_pool.stringToSlice(exports[0].opts.name),1751 mod.intern_pool.stringToSlice(exports[0].opts.name),
1734 );1752 );
1735 try global_index.rename(main_exp_name, &self.builder);1753 try global_index.rename(main_exp_name, &self.builder);
1736 global_index.setUnnamedAddr(.default, &self.builder);1754
1737 if (mod.wantDllExports()) global_index.setDllStorageClass(.dllexport, &self.builder);
1738 if (self.di_map.get(decl)) |di_node| {1755 if (self.di_map.get(decl)) |di_node| {
1739 const main_exp_name_slice = main_exp_name.slice(&self.builder).?;1756 const main_exp_name_slice = main_exp_name.slice(&self.builder).?;
1740 if (try decl.isFunction(mod)) {1757 if (try decl.isFunction(mod)) {
...@@ -1755,55 +1772,12 @@ pub const Object = struct {...@@ -1755,55 +1772,12 @@ pub const Object = struct {
1755 di_global.replaceLinkageName(linkage_name);1772 di_global.replaceLinkageName(linkage_name);
1756 }1773 }
1757 }1774 }
1758 global_index.setLinkage(switch (exports[0].opts.linkage) {1775
1759 .Internal => unreachable,
1760 .Strong => .external,
1761 .Weak => .weak_odr,
1762 .LinkOnce => .linkonce_odr,
1763 }, &self.builder);
1764 global_index.setVisibility(switch (exports[0].opts.visibility) {
1765 .default => .default,
1766 .hidden => .hidden,
1767 .protected => .protected,
1768 }, &self.builder);
1769 if (mod.intern_pool.stringToSliceUnwrap(exports[0].opts.section)) |section|
1770 switch (global_index.ptrConst(&self.builder).kind) {
1771 inline .variable, .function => |impl_index| impl_index.setSection(
1772 try self.builder.string(section),
1773 &self.builder,
1774 ),
1775 .alias, .replaced => unreachable,
1776 };
1777 if (decl.val.getVariable(mod)) |decl_var| if (decl_var.is_threadlocal)1776 if (decl.val.getVariable(mod)) |decl_var| if (decl_var.is_threadlocal)
1778 global_index.ptrConst(&self.builder).kind1777 global_index.ptrConst(&self.builder).kind
1779 .variable.setThreadLocal(.generaldynamic, &self.builder);1778 .variable.setThreadLocal(.generaldynamic, &self.builder);
17801779
1781 // If a Decl is exported more than one time (which is rare),1780 return updateExportedGlobal(self, mod, global_index, exports);
1782 // we add aliases for all but the first export.
1783 // TODO LLVM C API does not support deleting aliases.
1784 // The planned solution to this is https://github.com/ziglang/zig/issues/13265
1785 // Until then we iterate over existing aliases and make them point
1786 // to the correct decl, or otherwise add a new alias. Old aliases are leaked.
1787 for (exports[1..]) |exp| {
1788 const exp_name = try self.builder.string(mod.intern_pool.stringToSlice(exp.opts.name));
1789 if (self.builder.getGlobal(exp_name)) |global| {
1790 switch (global.ptrConst(&self.builder).kind) {
1791 .alias => |alias| {
1792 alias.setAliasee(global_index.toConst(), &self.builder);
1793 continue;
1794 },
1795 .variable, .function => {},
1796 .replaced => unreachable,
1797 }
1798 }
1799 const alias_index = try self.builder.addAlias(
1800 .empty,
1801 global_index.typeOf(&self.builder),
1802 .default,
1803 global_index.toConst(),
1804 );
1805 try alias_index.rename(exp_name, &self.builder);
1806 }
1807 } else {1781 } else {
1808 const fqn = try self.builder.string(1782 const fqn = try self.builder.string(
1809 mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)),1783 mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)),
...@@ -1824,6 +1798,100 @@ pub const Object = struct {...@@ -1824,6 +1798,100 @@ pub const Object = struct {
1824 }1798 }
1825 }1799 }
18261800
1801 fn updateExportedValue(
1802 o: *Object,
1803 mod: *Module,
1804 exported_value: InternPool.Index,
1805 exports: []const *Module.Export,
1806 ) link.File.UpdateExportsError!void {
1807 const gpa = mod.gpa;
1808 const main_exp_name = try o.builder.string(
1809 mod.intern_pool.stringToSlice(exports[0].opts.name),
1810 );
1811 const global_index = i: {
1812 const gop = try o.anon_decl_map.getOrPut(gpa, exported_value);
1813 if (gop.found_existing) {
1814 const global_index = gop.value_ptr.*;
1815 try global_index.rename(main_exp_name, &o.builder);
1816 break :i global_index;
1817 }
1818 const llvm_addr_space = toLlvmAddressSpace(.generic, o.target);
1819 const variable_index = try o.builder.addVariable(
1820 main_exp_name,
1821 try o.lowerType(mod.intern_pool.typeOf(exported_value).toType()),
1822 llvm_addr_space,
1823 );
1824 const global_index = variable_index.ptrConst(&o.builder).global;
1825 gop.value_ptr.* = global_index;
1826 // This line invalidates `gop`.
1827 const init_val = o.lowerValue(exported_value) catch |err| switch (err) {
1828 error.OutOfMemory => return error.OutOfMemory,
1829 error.CodegenFail => return error.AnalysisFail,
1830 };
1831 try variable_index.setInitializer(init_val, &o.builder);
1832 break :i global_index;
1833 };
1834 return updateExportedGlobal(o, mod, global_index, exports);
1835 }
1836
1837 fn updateExportedGlobal(
1838 o: *Object,
1839 mod: *Module,
1840 global_index: Builder.Global.Index,
1841 exports: []const *Module.Export,
1842 ) link.File.UpdateExportsError!void {
1843 global_index.setUnnamedAddr(.default, &o.builder);
1844 if (mod.wantDllExports()) global_index.setDllStorageClass(.dllexport, &o.builder);
1845 global_index.setLinkage(switch (exports[0].opts.linkage) {
1846 .Internal => unreachable,
1847 .Strong => .external,
1848 .Weak => .weak_odr,
1849 .LinkOnce => .linkonce_odr,
1850 }, &o.builder);
1851 global_index.setVisibility(switch (exports[0].opts.visibility) {
1852 .default => .default,
1853 .hidden => .hidden,
1854 .protected => .protected,
1855 }, &o.builder);
1856 if (mod.intern_pool.stringToSliceUnwrap(exports[0].opts.section)) |section|
1857 switch (global_index.ptrConst(&o.builder).kind) {
1858 .variable => |impl_index| impl_index.setSection(
1859 try o.builder.string(section),
1860 &o.builder,
1861 ),
1862 .function => unreachable,
1863 .alias => unreachable,
1864 .replaced => unreachable,
1865 };
1866
1867 // If a Decl is exported more than one time (which is rare),
1868 // we add aliases for all but the first export.
1869 // TODO LLVM C API does not support deleting aliases.
1870 // The planned solution to this is https://github.com/ziglang/zig/issues/13265
1871 // Until then we iterate over existing aliases and make them point
1872 // to the correct decl, or otherwise add a new alias. Old aliases are leaked.
1873 for (exports[1..]) |exp| {
1874 const exp_name = try o.builder.string(mod.intern_pool.stringToSlice(exp.opts.name));
1875 if (o.builder.getGlobal(exp_name)) |global| {
1876 switch (global.ptrConst(&o.builder).kind) {
1877 .alias => |alias| {
1878 alias.setAliasee(global_index.toConst(), &o.builder);
1879 continue;
1880 },
1881 .variable, .function => {},
1882 .replaced => unreachable,
1883 }
1884 }
1885 const alias_index = try o.builder.addAlias(
1886 .empty,
1887 global_index.typeOf(&o.builder),
1888 .default,
1889 global_index.toConst(),
1890 );
1891 try alias_index.rename(exp_name, &o.builder);
1892 }
1893 }
1894
1827 pub fn freeDecl(self: *Object, decl_index: Module.Decl.Index) void {1895 pub fn freeDecl(self: *Object, decl_index: Module.Decl.Index) void {
1828 const global = self.decl_map.get(decl_index) orelse return;1896 const global = self.decl_map.get(decl_index) orelse return;
1829 global.delete(&self.builder);1897 global.delete(&self.builder);
src/link.zig+32-17
...@@ -587,7 +587,7 @@ pub const File = struct {...@@ -587,7 +587,7 @@ pub const File = struct {
587 }587 }
588 }588 }
589589
590 /// May be called before or after updateDeclExports for any given Decl.590 /// May be called before or after updateExports for any given Decl.
591 pub fn updateDecl(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void {591 pub fn updateDecl(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void {
592 const decl = module.declPtr(decl_index);592 const decl = module.declPtr(decl_index);
593 assert(decl.has_tv);593 assert(decl.has_tv);
...@@ -609,7 +609,7 @@ pub const File = struct {...@@ -609,7 +609,7 @@ pub const File = struct {
609 }609 }
610 }610 }
611611
612 /// May be called before or after updateDeclExports for any given Decl.612 /// May be called before or after updateExports for any given Decl.
613 pub fn updateFunc(base: *File, module: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) UpdateDeclError!void {613 pub fn updateFunc(base: *File, module: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) UpdateDeclError!void {
614 if (build_options.only_c) {614 if (build_options.only_c) {
615 assert(base.tag == .c);615 assert(base.tag == .c);
...@@ -882,33 +882,34 @@ pub const File = struct {...@@ -882,33 +882,34 @@ pub const File = struct {
882 }882 }
883 }883 }
884884
885 pub const UpdateDeclExportsError = error{885 pub const UpdateExportsError = error{
886 OutOfMemory,886 OutOfMemory,
887 AnalysisFail,887 AnalysisFail,
888 };888 };
889889
890 /// This is called for every exported thing. `exports` is almost always
891 /// a list of size 1, meaning that `exported` is exported once. However, it is possible
892 /// to export the same thing with multiple different symbol names (aliases).
890 /// May be called before or after updateDecl for any given Decl.893 /// May be called before or after updateDecl for any given Decl.
891 pub fn updateDeclExports(894 pub fn updateExports(
892 base: *File,895 base: *File,
893 module: *Module,896 module: *Module,
894 decl_index: Module.Decl.Index,897 exported: Module.Exported,
895 exports: []const *Module.Export,898 exports: []const *Module.Export,
896 ) UpdateDeclExportsError!void {899 ) UpdateExportsError!void {
897 const decl = module.declPtr(decl_index);
898 assert(decl.has_tv);
899 if (build_options.only_c) {900 if (build_options.only_c) {
900 assert(base.tag == .c);901 assert(base.tag == .c);
901 return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl_index, exports);902 return @fieldParentPtr(C, "base", base).updateExports(module, exported, exports);
902 }903 }
903 switch (base.tag) {904 switch (base.tag) {
904 .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl_index, exports),905 .coff => return @fieldParentPtr(Coff, "base", base).updateExports(module, exported, exports),
905 .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl_index, exports),906 .elf => return @fieldParentPtr(Elf, "base", base).updateExports(module, exported, exports),
906 .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl_index, exports),907 .macho => return @fieldParentPtr(MachO, "base", base).updateExports(module, exported, exports),
907 .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl_index, exports),908 .c => return @fieldParentPtr(C, "base", base).updateExports(module, exported, exports),
908 .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl_index, exports),909 .wasm => return @fieldParentPtr(Wasm, "base", base).updateExports(module, exported, exports),
909 .spirv => return @fieldParentPtr(SpirV, "base", base).updateDeclExports(module, decl_index, exports),910 .spirv => return @fieldParentPtr(SpirV, "base", base).updateExports(module, exported, exports),
910 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclExports(module, decl_index, exports),911 .plan9 => return @fieldParentPtr(Plan9, "base", base).updateExports(module, exported, exports),
911 .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDeclExports(module, decl_index, exports),912 .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateExports(module, exported, exports),
912 }913 }
913 }914 }
914915
...@@ -968,6 +969,20 @@ pub const File = struct {...@@ -968,6 +969,20 @@ pub const File = struct {
968 }969 }
969 }970 }
970971
972 pub fn deleteDeclExport(base: *File, decl_index: Module.Decl.Index, name: InternPool.NullTerminatedString) !void {
973 if (build_options.only_c) unreachable;
974 switch (base.tag) {
975 .coff => return @fieldParentPtr(Coff, "base", base).deleteDeclExport(decl_index, name),
976 .elf => return @fieldParentPtr(Elf, "base", base).deleteDeclExport(decl_index, name),
977 .macho => return @fieldParentPtr(MachO, "base", base).deleteDeclExport(decl_index, name),
978 .plan9 => {},
979 .c => {},
980 .wasm => return @fieldParentPtr(Wasm, "base", base).deleteDeclExport(decl_index),
981 .spirv => {},
982 .nvptx => {},
983 }
984 }
985
971 /// This function is called by the frontend before flush(). It communicates that986 /// This function is called by the frontend before flush(). It communicates that
972 /// `options.bin_file.emit` directory needs to be renamed from987 /// `options.bin_file.emit` directory needs to be renamed from
973 /// `[zig-cache]/tmp/[random]` to `[zig-cache]/o/[digest]`.988 /// `[zig-cache]/tmp/[random]` to `[zig-cache]/o/[digest]`.
src/link/C.zig+3-3
...@@ -753,14 +753,14 @@ pub fn flushEmitH(module: *Module) !void {...@@ -753,14 +753,14 @@ pub fn flushEmitH(module: *Module) !void {
753 try file.pwritevAll(all_buffers.items, 0);753 try file.pwritevAll(all_buffers.items, 0);
754}754}
755755
756pub fn updateDeclExports(756pub fn updateExports(
757 self: *C,757 self: *C,
758 module: *Module,758 module: *Module,
759 decl_index: Module.Decl.Index,759 exported: Module.Exported,
760 exports: []const *Module.Export,760 exports: []const *Module.Export,
761) !void {761) !void {
762 _ = exports;762 _ = exports;
763 _ = decl_index;763 _ = exported;
764 _ = module;764 _ = module;
765 _ = self;765 _ = self;
766}766}
src/link/Coff.zig+18-7
...@@ -1075,7 +1075,7 @@ pub fn updateFunc(self: *Coff, mod: *Module, func_index: InternPool.Index, air:...@@ -1075,7 +1075,7 @@ pub fn updateFunc(self: *Coff, mod: *Module, func_index: InternPool.Index, air:
10751075
1076 // Since we updated the vaddr and the size, each corresponding export1076 // Since we updated the vaddr and the size, each corresponding export
1077 // symbol also needs to be updated.1077 // symbol also needs to be updated.
1078 return self.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index));1078 return self.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
1079}1079}
10801080
1081pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {1081pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {
...@@ -1195,7 +1195,7 @@ pub fn updateDecl(...@@ -1195,7 +1195,7 @@ pub fn updateDecl(
11951195
1196 // Since we updated the vaddr and the size, each corresponding export1196 // Since we updated the vaddr and the size, each corresponding export
1197 // symbol also needs to be updated.1197 // symbol also needs to be updated.
1198 return self.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index));1198 return self.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
1199}1199}
12001200
1201fn updateLazySymbolAtom(1201fn updateLazySymbolAtom(
...@@ -1409,12 +1409,12 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {...@@ -1409,12 +1409,12 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
1409 }1409 }
1410}1410}
14111411
1412pub fn updateDeclExports(1412pub fn updateExports(
1413 self: *Coff,1413 self: *Coff,
1414 mod: *Module,1414 mod: *Module,
1415 decl_index: Module.Decl.Index,1415 exported: Module.Exported,
1416 exports: []const *Module.Export,1416 exports: []const *Module.Export,
1417) link.File.UpdateDeclExportsError!void {1417) link.File.UpdateExportsError!void {
1418 if (build_options.skip_non_native and builtin.object_format != .coff) {1418 if (build_options.skip_non_native and builtin.object_format != .coff) {
1419 @panic("Attempted to compile for object format that was disabled by build configuration");1419 @panic("Attempted to compile for object format that was disabled by build configuration");
1420 }1420 }
...@@ -1425,7 +1425,11 @@ pub fn updateDeclExports(...@@ -1425,7 +1425,11 @@ pub fn updateDeclExports(
1425 // Even in the case of LLVM, we need to notice certain exported symbols in order to1425 // Even in the case of LLVM, we need to notice certain exported symbols in order to
1426 // detect the default subsystem.1426 // detect the default subsystem.
1427 for (exports) |exp| {1427 for (exports) |exp| {
1428 const exported_decl = mod.declPtr(exp.exported_decl);1428 const exported_decl_index = switch (exp.exported) {
1429 .decl_index => |i| i,
1430 .value => continue,
1431 };
1432 const exported_decl = mod.declPtr(exported_decl_index);
1429 if (exported_decl.getOwnedFunction(mod) == null) continue;1433 if (exported_decl.getOwnedFunction(mod) == null) continue;
1430 const winapi_cc = switch (self.base.options.target.cpu.arch) {1434 const winapi_cc = switch (self.base.options.target.cpu.arch) {
1431 .x86 => std.builtin.CallingConvention.Stdcall,1435 .x86 => std.builtin.CallingConvention.Stdcall,
...@@ -1452,12 +1456,19 @@ pub fn updateDeclExports(...@@ -1452,12 +1456,19 @@ pub fn updateDeclExports(
1452 }1456 }
1453 }1457 }
14541458
1455 if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(mod, decl_index, exports);1459 if (self.llvm_object) |llvm_object| return llvm_object.updateExports(mod, exported, exports);
14561460
1457 if (self.base.options.emit == null) return;1461 if (self.base.options.emit == null) return;
14581462
1459 const gpa = self.base.allocator;1463 const gpa = self.base.allocator;
14601464
1465 const decl_index = switch (exported) {
1466 .decl_index => |i| i,
1467 .value => |val| {
1468 _ = val;
1469 @panic("TODO: implement COFF linker code for exporting a constant value");
1470 },
1471 };
1461 const decl = mod.declPtr(decl_index);1472 const decl = mod.declPtr(decl_index);
1462 const atom_index = try self.getOrCreateAtomForDecl(decl_index);1473 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
1463 const atom = self.getAtom(atom_index);1474 const atom = self.getAtom(atom_index);
src/link/Elf.zig+13-6
...@@ -3306,7 +3306,7 @@ pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: A...@@ -3306,7 +3306,7 @@ pub fn updateFunc(self: *Elf, mod: *Module, func_index: InternPool.Index, air: A
33063306
3307 // Since we updated the vaddr and the size, each corresponding export3307 // Since we updated the vaddr and the size, each corresponding export
3308 // symbol also needs to be updated.3308 // symbol also needs to be updated.
3309 return self.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index));3309 return self.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
3310}3310}
33113311
3312pub fn updateDecl(3312pub fn updateDecl(
...@@ -3388,7 +3388,7 @@ pub fn updateDecl(...@@ -3388,7 +3388,7 @@ pub fn updateDecl(
33883388
3389 // Since we updated the vaddr and the size, each corresponding export3389 // Since we updated the vaddr and the size, each corresponding export
3390 // symbol also needs to be updated.3390 // symbol also needs to be updated.
3391 return self.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index));3391 return self.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
3392}3392}
33933393
3394fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.Index) !void {3394fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.Index) !void {
...@@ -3555,16 +3555,16 @@ fn lowerConst(...@@ -3555,16 +3555,16 @@ fn lowerConst(
3555 return .{ .ok = sym_index };3555 return .{ .ok = sym_index };
3556}3556}
35573557
3558pub fn updateDeclExports(3558pub fn updateExports(
3559 self: *Elf,3559 self: *Elf,
3560 mod: *Module,3560 mod: *Module,
3561 decl_index: Module.Decl.Index,3561 exported: Module.Exported,
3562 exports: []const *Module.Export,3562 exports: []const *Module.Export,
3563) link.File.UpdateDeclExportsError!void {3563) link.File.UpdateExportsError!void {
3564 if (build_options.skip_non_native and builtin.object_format != .elf) {3564 if (build_options.skip_non_native and builtin.object_format != .elf) {
3565 @panic("Attempted to compile for object format that was disabled by build configuration");3565 @panic("Attempted to compile for object format that was disabled by build configuration");
3566 }3566 }
3567 if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(mod, decl_index, exports);3567 if (self.llvm_object) |llvm_object| return llvm_object.updateExports(mod, exported, exports);
35683568
3569 if (self.base.options.emit == null) return;3569 if (self.base.options.emit == null) return;
35703570
...@@ -3573,6 +3573,13 @@ pub fn updateDeclExports(...@@ -3573,6 +3573,13 @@ pub fn updateDeclExports(
35733573
3574 const gpa = self.base.allocator;3574 const gpa = self.base.allocator;
35753575
3576 const decl_index = switch (exported) {
3577 .decl_index => |i| i,
3578 .value => |val| {
3579 _ = val;
3580 @panic("TODO: implement ELF linker code for exporting a constant value");
3581 },
3582 };
3576 const zig_module = self.file(self.zig_module_index.?).?.zig_module;3583 const zig_module = self.file(self.zig_module_index.?).?.zig_module;
3577 const decl = mod.declPtr(decl_index);3584 const decl = mod.declPtr(decl_index);
3578 const decl_sym_index = try self.getOrCreateMetadataForDecl(decl_index);3585 const decl_sym_index = try self.getOrCreateMetadataForDecl(decl_index);
src/link/MachO.zig+15-8
...@@ -1670,7 +1670,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {...@@ -1670,7 +1670,7 @@ fn resolveGlobalSymbol(self: *MachO, current: SymbolWithLoc) !void {
1670 const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext());1670 const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext());
16711671
1672 if (sym_is_strong and global_is_strong) {1672 if (sym_is_strong and global_is_strong) {
1673 // TODO redo this logic with corresponding logic in updateDeclExports to avoid this1673 // TODO redo this logic with corresponding logic in updateExports to avoid this
1674 // ugly check.1674 // ugly check.
1675 if (self.mode == .zld) {1675 if (self.mode == .zld) {
1676 try self.reportSymbolCollision(global, current);1676 try self.reportSymbolCollision(global, current);
...@@ -2180,7 +2180,7 @@ pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air:...@@ -2180,7 +2180,7 @@ pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air:
21802180
2181 // Since we updated the vaddr and the size, each corresponding export symbol also2181 // Since we updated the vaddr and the size, each corresponding export symbol also
2182 // needs to be updated.2182 // needs to be updated.
2183 try self.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index));2183 try self.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
2184}2184}
21852185
2186pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {2186pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {
...@@ -2340,7 +2340,7 @@ pub fn updateDecl(self: *MachO, mod: *Module, decl_index: Module.Decl.Index) !vo...@@ -2340,7 +2340,7 @@ pub fn updateDecl(self: *MachO, mod: *Module, decl_index: Module.Decl.Index) !vo
23402340
2341 // Since we updated the vaddr and the size, each corresponding export symbol also2341 // Since we updated the vaddr and the size, each corresponding export symbol also
2342 // needs to be updated.2342 // needs to be updated.
2343 try self.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index));2343 try self.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
2344}2344}
23452345
2346fn updateLazySymbolAtom(2346fn updateLazySymbolAtom(
...@@ -2529,7 +2529,7 @@ fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: Module.D...@@ -2529,7 +2529,7 @@ fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: Module.D
2529 );2529 );
2530 }2530 }
25312531
2532 try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));2532 try self.updateExports(module, .{ .decl_index = decl_index }, module.getDeclExports(decl_index));
25332533
2534 // 2. Create a TLV descriptor.2534 // 2. Create a TLV descriptor.
2535 const init_atom_sym_loc = init_atom.getSymbolWithLoc();2535 const init_atom_sym_loc = init_atom.getSymbolWithLoc();
...@@ -2670,17 +2670,17 @@ pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl_index: Module.De...@@ -2670,17 +2670,17 @@ pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl_index: Module.De
2670 }2670 }
2671}2671}
26722672
2673pub fn updateDeclExports(2673pub fn updateExports(
2674 self: *MachO,2674 self: *MachO,
2675 mod: *Module,2675 mod: *Module,
2676 decl_index: Module.Decl.Index,2676 exported: Module.Exported,
2677 exports: []const *Module.Export,2677 exports: []const *Module.Export,
2678) File.UpdateDeclExportsError!void {2678) File.UpdateExportsError!void {
2679 if (build_options.skip_non_native and builtin.object_format != .macho) {2679 if (build_options.skip_non_native and builtin.object_format != .macho) {
2680 @panic("Attempted to compile for object format that was disabled by build configuration");2680 @panic("Attempted to compile for object format that was disabled by build configuration");
2681 }2681 }
2682 if (self.llvm_object) |llvm_object|2682 if (self.llvm_object) |llvm_object|
2683 return llvm_object.updateDeclExports(mod, decl_index, exports);2683 return llvm_object.updateExports(mod, exported, exports);
26842684
2685 if (self.base.options.emit == null) return;2685 if (self.base.options.emit == null) return;
26862686
...@@ -2689,6 +2689,13 @@ pub fn updateDeclExports(...@@ -2689,6 +2689,13 @@ pub fn updateDeclExports(
26892689
2690 const gpa = self.base.allocator;2690 const gpa = self.base.allocator;
26912691
2692 const decl_index = switch (exported) {
2693 .decl_index => |i| i,
2694 .value => |val| {
2695 _ = val;
2696 @panic("TODO: implement MachO linker code for exporting a constant value");
2697 },
2698 };
2692 const decl = mod.declPtr(decl_index);2699 const decl = mod.declPtr(decl_index);
2693 const atom_index = try self.getOrCreateAtomForDecl(decl_index);2700 const atom_index = try self.getOrCreateAtomForDecl(decl_index);
2694 const atom = self.getAtom(atom_index);2701 const atom = self.getAtom(atom_index);
src/link/NvPtx.zig+3-3
...@@ -74,16 +74,16 @@ pub fn updateDecl(self: *NvPtx, module: *Module, decl_index: Module.Decl.Index)...@@ -74,16 +74,16 @@ pub fn updateDecl(self: *NvPtx, module: *Module, decl_index: Module.Decl.Index)
74 return self.llvm_object.updateDecl(module, decl_index);74 return self.llvm_object.updateDecl(module, decl_index);
75}75}
7676
77pub fn updateDeclExports(77pub fn updateExports(
78 self: *NvPtx,78 self: *NvPtx,
79 module: *Module,79 module: *Module,
80 decl_index: Module.Decl.Index,80 exported: Module.Exported,
81 exports: []const *Module.Export,81 exports: []const *Module.Export,
82) !void {82) !void {
83 if (build_options.skip_non_native and builtin.object_format != .nvptx) {83 if (build_options.skip_non_native and builtin.object_format != .nvptx) {
84 @panic("Attempted to compile for object format that was disabled by build configuration");84 @panic("Attempted to compile for object format that was disabled by build configuration");
85 }85 }
86 return self.llvm_object.updateDeclExports(module, decl_index, exports);86 return self.llvm_object.updateExports(module, exported, exports);
87}87}
8888
89pub fn freeDecl(self: *NvPtx, decl_index: Module.Decl.Index) void {89pub fn freeDecl(self: *NvPtx, decl_index: Module.Decl.Index) void {
src/link/Plan9.zig+6-3
...@@ -1116,13 +1116,16 @@ pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !Atom.Index {...@@ -1116,13 +1116,16 @@ pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !Atom.Index {
1116 return atom_idx;1116 return atom_idx;
1117}1117}
11181118
1119pub fn updateDeclExports(1119pub fn updateExports(
1120 self: *Plan9,1120 self: *Plan9,
1121 module: *Module,1121 module: *Module,
1122 decl_index: Module.Decl.Index,1122 exported: Module.Exported,
1123 exports: []const *Module.Export,1123 exports: []const *Module.Export,
1124) !void {1124) !void {
1125 _ = try self.seeDecl(decl_index);1125 switch (exported) {
1126 .value => @panic("TODO: plan9 updateExports handling values"),
1127 .decl_index => |decl_index| _ = try self.seeDecl(decl_index),
1128 }
1126 // we do all the things in flush1129 // we do all the things in flush
1127 _ = module;1130 _ = module;
1128 _ = exports;1131 _ = exports;
src/link/SpirV.zig+9-2
...@@ -120,12 +120,19 @@ pub fn updateDecl(self: *SpirV, module: *Module, decl_index: Module.Decl.Index)...@@ -120,12 +120,19 @@ pub fn updateDecl(self: *SpirV, module: *Module, decl_index: Module.Decl.Index)
120 try self.object.updateDecl(module, decl_index);120 try self.object.updateDecl(module, decl_index);
121}121}
122122
123pub fn updateDeclExports(123pub fn updateExports(
124 self: *SpirV,124 self: *SpirV,
125 mod: *Module,125 mod: *Module,
126 decl_index: Module.Decl.Index,126 exported: Module.Exported,
127 exports: []const *Module.Export,127 exports: []const *Module.Export,
128) !void {128) !void {
129 const decl_index = switch (exported) {
130 .decl_index => |i| i,
131 .value => |val| {
132 _ = val;
133 @panic("TODO: implement SpirV linker code for exporting a constant value");
134 },
135 };
129 const decl = mod.declPtr(decl_index);136 const decl = mod.declPtr(decl_index);
130 if (decl.val.isFuncBody(mod) and decl.ty.fnCallingConvention(mod) == .Kernel) {137 if (decl.val.isFuncBody(mod) and decl.ty.fnCallingConvention(mod) == .Kernel) {
131 const spv_decl_index = try self.object.resolveDecl(mod, decl_index);138 const spv_decl_index = try self.object.resolveDecl(mod, decl_index);
src/link/Wasm.zig+23-4
...@@ -1786,19 +1786,26 @@ pub fn deleteDeclExport(wasm: *Wasm, decl_index: Module.Decl.Index) void {...@@ -1786,19 +1786,26 @@ pub fn deleteDeclExport(wasm: *Wasm, decl_index: Module.Decl.Index) void {
1786 }1786 }
1787}1787}
17881788
1789pub fn updateDeclExports(1789pub fn updateExports(
1790 wasm: *Wasm,1790 wasm: *Wasm,
1791 mod: *Module,1791 mod: *Module,
1792 decl_index: Module.Decl.Index,1792 exported: Module.Exported,
1793 exports: []const *Module.Export,1793 exports: []const *Module.Export,
1794) !void {1794) !void {
1795 if (build_options.skip_non_native and builtin.object_format != .wasm) {1795 if (build_options.skip_non_native and builtin.object_format != .wasm) {
1796 @panic("Attempted to compile for object format that was disabled by build configuration");1796 @panic("Attempted to compile for object format that was disabled by build configuration");
1797 }1797 }
1798 if (wasm.llvm_object) |llvm_object| return llvm_object.updateDeclExports(mod, decl_index, exports);1798 if (wasm.llvm_object) |llvm_object| return llvm_object.updateExports(mod, exported, exports);
17991799
1800 if (wasm.base.options.emit == null) return;1800 if (wasm.base.options.emit == null) return;
18011801
1802 const decl_index = switch (exported) {
1803 .decl_index => |i| i,
1804 .value => |val| {
1805 _ = val;
1806 @panic("TODO: implement Wasm linker code for exporting a constant value");
1807 },
1808 };
1802 const decl = mod.declPtr(decl_index);1809 const decl = mod.declPtr(decl_index);
1803 const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);1810 const atom_index = try wasm.getOrCreateAtomForDecl(decl_index);
1804 const atom = wasm.getAtom(atom_index);1811 const atom = wasm.getAtom(atom_index);
...@@ -1816,7 +1823,19 @@ pub fn updateDeclExports(...@@ -1816,7 +1823,19 @@ pub fn updateDeclExports(
1816 continue;1823 continue;
1817 }1824 }
18181825
1819 const exported_atom_index = try wasm.getOrCreateAtomForDecl(exp.exported_decl);1826 const exported_decl_index = switch (exp.exported) {
1827 .value => {
1828 try mod.failed_exports.putNoClobber(gpa, exp, try Module.ErrorMsg.create(
1829 gpa,
1830 decl.srcLoc(mod),
1831 "Unimplemented: exporting a named constant value",
1832 .{},
1833 ));
1834 continue;
1835 },
1836 .decl_index => |i| i,
1837 };
1838 const exported_atom_index = try wasm.getOrCreateAtomForDecl(exported_decl_index);
1820 const exported_atom = wasm.getAtom(exported_atom_index);1839 const exported_atom = wasm.getAtom(exported_atom_index);
1821 const export_name = try wasm.string_table.put(wasm.base.allocator, mod.intern_pool.stringToSlice(exp.opts.name));1840 const export_name = try wasm.string_table.put(wasm.base.allocator, mod.intern_pool.stringToSlice(exp.opts.name));
1822 const sym_loc = exported_atom.symbolLoc();1841 const sym_loc = exported_atom.symbolLoc();
test/behavior/export.zig+4
...@@ -72,6 +72,8 @@ test "exporting using field access" {...@@ -72,6 +72,8 @@ test "exporting using field access" {
72}72}
7373
74test "exporting comptime-known value" {74test "exporting comptime-known value" {
75 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
76
75 const x: u32 = 10;77 const x: u32 = 10;
76 @export(x, .{ .name = "exporting_comptime_known_value_foo" });78 @export(x, .{ .name = "exporting_comptime_known_value_foo" });
77 const S = struct {79 const S = struct {
...@@ -81,6 +83,8 @@ test "exporting comptime-known value" {...@@ -81,6 +83,8 @@ test "exporting comptime-known value" {
81}83}
8284
83test "exporting comptime var" {85test "exporting comptime var" {
86 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
87
84 comptime var x: u32 = 5;88 comptime var x: u32 = 5;
85 @export(x, .{ .name = "exporting_comptime_var_foo" });89 @export(x, .{ .name = "exporting_comptime_var_foo" });
86 x = 7; // modifying this now shouldn't change anything90 x = 7; // modifying this now shouldn't change anything