| author | |
| committer | |
| log | 1c85b0acbb5148dec7ff671e767354f3df5c6b83 |
| tree | 29edfb52b0885c647ad4f72a12e13683d513e81b |
| parent | 772636ed0d4a2051955197999c9ddf2906384806 |
| parent | 4bc88dd11641a664d80b00ad784bafc6da776697 |
| signature |
link: support exporting constant values without a Decl13 files changed, 425 insertions(+), 233 deletions(-)
src/Module.zig+110-69| ... | ... | @@ -70,6 +70,8 @@ local_zir_cache: Compilation.Directory, |
| 70 | 70 | /// The Export memory is owned by the `export_owners` table; the slice itself |
| 71 | 71 | /// is owned by this table. The slice is guaranteed to not be empty. |
| 72 | 72 | decl_exports: std.AutoArrayHashMapUnmanaged(Decl.Index, ArrayListUnmanaged(*Export)) = .{}, |
| 73 | /// Same as `decl_exports` but for exported constant values. | |
| 74 | value_exports: std.AutoArrayHashMapUnmanaged(InternPool.Index, ArrayListUnmanaged(*Export)) = .{}, | |
| 73 | 75 | /// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl |
| 74 | 76 | /// is modified. Note that the key of this table is not the Decl being exported, but the Decl that |
| 75 | 77 | /// is performing the export of another Decl. |
| ... | ... | @@ -244,6 +246,13 @@ pub const GlobalEmitH = struct { |
| 244 | 246 | |
| 245 | 247 | pub const ErrorInt = u32; |
| 246 | 248 | |
| 249 | pub 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 | ||
| 247 | 256 | pub const Export = struct { |
| 248 | 257 | opts: Options, |
| 249 | 258 | src: LazySrcLoc, |
| ... | ... | @@ -252,8 +261,7 @@ pub const Export = struct { |
| 252 | 261 | /// The Decl containing the export statement. Inline function calls |
| 253 | 262 | /// may cause this to be different from the owner_decl. |
| 254 | 263 | src_decl: Decl.Index, |
| 255 | /// The Decl being exported. Note this is *not* the Decl performing the export. | |
| 256 | exported_decl: Decl.Index, | |
| 264 | exported: Exported, | |
| 257 | 265 | status: enum { |
| 258 | 266 | in_progress, |
| 259 | 267 | failed, |
| ... | ... | @@ -2575,6 +2583,11 @@ pub fn deinit(mod: *Module) void { |
| 2575 | 2583 | } |
| 2576 | 2584 | mod.decl_exports.deinit(gpa); |
| 2577 | 2585 | |
| 2586 | for (mod.value_exports.values()) |*export_list| { | |
| 2587 | export_list.deinit(gpa); | |
| 2588 | } | |
| 2589 | mod.value_exports.deinit(gpa); | |
| 2590 | ||
| 2578 | 2591 | for (mod.export_owners.values()) |*value| { |
| 2579 | 2592 | freeExportList(gpa, value); |
| 2580 | 2593 | } |
| ... | ... | @@ -4620,36 +4633,49 @@ fn deleteDeclExports(mod: *Module, decl_index: Decl.Index) Allocator.Error!void |
| 4620 | 4633 | var export_owners = (mod.export_owners.fetchSwapRemove(decl_index) orelse return).value; |
| 4621 | 4634 | |
| 4622 | 4635 | for (export_owners.items) |exp| { |
| 4623 | if (mod.decl_exports.getPtr(exp.exported_decl)) |value_ptr| { | |
| 4624 | // Remove exports with owner_decl matching the regenerating decl. | |
| 4625 | const list = value_ptr.items; | |
| 4626 | var i: usize = 0; | |
| 4627 | var new_len = list.len; | |
| 4628 | while (i < new_len) { | |
| 4629 | if (list[i].owner_decl == decl_index) { | |
| 4630 | mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]); | |
| 4631 | new_len -= 1; | |
| 4632 | } else { | |
| 4633 | i += 1; | |
| 4636 | switch (exp.exported) { | |
| 4637 | .decl_index => |exported_decl_index| { | |
| 4638 | if (mod.decl_exports.getPtr(exported_decl_index)) |export_list| { | |
| 4639 | // Remove exports with owner_decl matching the regenerating decl. | |
| 4640 | const list = export_list.items; | |
| 4641 | var i: usize = 0; | |
| 4642 | var new_len = list.len; | |
| 4643 | while (i < new_len) { | |
| 4644 | if (list[i].owner_decl == decl_index) { | |
| 4645 | mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]); | |
| 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 | } | |
| 4636 | value_ptr.shrinkAndFree(mod.gpa, new_len); | |
| 4637 | if (new_len == 0) { | |
| 4638 | assert(mod.decl_exports.swapRemove(exp.exported_decl)); | |
| 4639 | } | |
| 4640 | } | |
| 4641 | if (mod.comp.bin_file.cast(link.File.Elf)) |elf| { | |
| 4642 | elf.deleteDeclExport(decl_index, exp.opts.name); | |
| 4643 | } | |
| 4644 | if (mod.comp.bin_file.cast(link.File.MachO)) |macho| { | |
| 4645 | try macho.deleteDeclExport(decl_index, exp.opts.name); | |
| 4646 | } | |
| 4647 | if (mod.comp.bin_file.cast(link.File.Wasm)) |wasm| { | |
| 4648 | wasm.deleteDeclExport(decl_index); | |
| 4649 | } | |
| 4650 | if (mod.comp.bin_file.cast(link.File.Coff)) |coff| { | |
| 4651 | coff.deleteDeclExport(decl_index, exp.opts.name); | |
| 4656 | }, | |
| 4657 | .value => |value| { | |
| 4658 | if (mod.value_exports.getPtr(value)) |export_list| { | |
| 4659 | // Remove exports with owner_decl matching the regenerating decl. | |
| 4660 | const list = export_list.items; | |
| 4661 | var i: usize = 0; | |
| 4662 | var new_len = list.len; | |
| 4663 | while (i < new_len) { | |
| 4664 | if (list[i].owner_decl == decl_index) { | |
| 4665 | mem.copyBackwards(*Export, list[i..], list[i + 1 .. new_len]); | |
| 4666 | new_len -= 1; | |
| 4667 | } else { | |
| 4668 | i += 1; | |
| 4669 | } | |
| 4670 | } | |
| 4671 | export_list.shrinkAndFree(mod.gpa, new_len); | |
| 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 | 4679 | if (mod.failed_exports.fetchSwapRemove(exp)) |failed_kv| { |
| 4654 | 4680 | failed_kv.value.destroy(mod.gpa); |
| 4655 | 4681 | } |
| ... | ... | @@ -5503,48 +5529,63 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { |
| 5503 | 5529 | /// reporting compile errors. In this function we emit exported symbol collision |
| 5504 | 5530 | /// errors and communicate exported symbols to the linker backend. |
| 5505 | 5531 | pub fn processExports(mod: *Module) !void { |
| 5506 | const gpa = mod.gpa; | |
| 5507 | 5532 | // Map symbol names to `Export` for name collision detection. |
| 5508 | var symbol_exports: std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, *Export) = .{}; | |
| 5509 | defer symbol_exports.deinit(gpa); | |
| 5510 | ||
| 5511 | var it = mod.decl_exports.iterator(); | |
| 5512 | while (it.next()) |entry| { | |
| 5513 | const exported_decl = entry.key_ptr.*; | |
| 5514 | const exports = entry.value_ptr.items; | |
| 5515 | for (exports) |new_export| { | |
| 5516 | const gop = try symbol_exports.getOrPut(gpa, new_export.opts.name); | |
| 5517 | if (gop.found_existing) { | |
| 5518 | new_export.status = .failed_retryable; | |
| 5519 | try mod.failed_exports.ensureUnusedCapacity(gpa, 1); | |
| 5520 | const src_loc = new_export.getSrcLoc(mod); | |
| 5521 | const msg = try ErrorMsg.create(gpa, src_loc, "exported symbol collision: {}", .{ | |
| 5522 | new_export.opts.name.fmt(&mod.intern_pool), | |
| 5523 | }); | |
| 5524 | errdefer msg.destroy(gpa); | |
| 5525 | const other_export = gop.value_ptr.*; | |
| 5526 | const other_src_loc = other_export.getSrcLoc(mod); | |
| 5527 | try mod.errNoteNonLazy(other_src_loc, msg, "other symbol here", .{}); | |
| 5528 | mod.failed_exports.putAssumeCapacityNoClobber(new_export, msg); | |
| 5529 | new_export.status = .failed; | |
| 5530 | } else { | |
| 5531 | gop.value_ptr.* = new_export; | |
| 5532 | } | |
| 5533 | var symbol_exports: SymbolExports = .{}; | |
| 5534 | defer symbol_exports.deinit(mod.gpa); | |
| 5535 | ||
| 5536 | for (mod.decl_exports.keys(), mod.decl_exports.values()) |exported_decl, exports_list| { | |
| 5537 | const exported: Exported = .{ .decl_index = exported_decl }; | |
| 5538 | try processExportsInner(mod, &symbol_exports, exported, exports_list.items); | |
| 5539 | } | |
| 5540 | ||
| 5541 | for (mod.value_exports.keys(), mod.value_exports.values()) |exported_value, exports_list| { | |
| 5542 | const exported: Exported = .{ .value = exported_value }; | |
| 5543 | try processExportsInner(mod, &symbol_exports, exported, exports_list.items); | |
| 5544 | } | |
| 5545 | } | |
| 5546 | ||
| 5547 | const SymbolExports = std.AutoArrayHashMapUnmanaged(InternPool.NullTerminatedString, *Export); | |
| 5548 | ||
| 5549 | fn processExportsInner( | |
| 5550 | mod: *Module, | |
| 5551 | symbol_exports: *SymbolExports, | |
| 5552 | exported: Exported, | |
| 5553 | exports: []const *Export, | |
| 5554 | ) error{OutOfMemory}!void { | |
| 5555 | const gpa = mod.gpa; | |
| 5556 | ||
| 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 | } |
| 5549 | 5590 | |
| 5550 | 5591 | pub fn populateTestFunctions( |
src/Sema.zig+48-38| ... | ... | @@ -6026,6 +6026,7 @@ fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 6026 | 6026 | const tracy = trace(@src()); |
| 6027 | 6027 | defer tracy.end(); |
| 6028 | 6028 | |
| 6029 | const mod = sema.mod; | |
| 6029 | 6030 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 6030 | 6031 | const extra = sema.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data; |
| 6031 | 6032 | const src = inst_data.src(); |
| ... | ... | @@ -6034,19 +6035,22 @@ fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 6034 | 6035 | const operand = try sema.resolveInstConst(block, operand_src, extra.operand, .{ |
| 6035 | 6036 | .needed_comptime_reason = "export target must be comptime-known", |
| 6036 | 6037 | }); |
| 6037 | const options = sema.resolveExportOptions(block, .unneeded, extra.options) catch |err| switch (err) { | |
| 6038 | error.NeededSourceLocation => { | |
| 6039 | _ = try sema.resolveExportOptions(block, options_src, extra.options); | |
| 6040 | unreachable; | |
| 6041 | }, | |
| 6042 | else => |e| return e, | |
| 6043 | }; | |
| 6044 | const decl_index = if (operand.val.getFunction(sema.mod)) |function| function.owner_decl else blk: { | |
| 6045 | var anon_decl = try block.startAnonDecl(); // TODO: export value without Decl | |
| 6046 | defer anon_decl.deinit(); | |
| 6047 | break :blk try anon_decl.finish(operand.ty, operand.val, .none); | |
| 6048 | }; | |
| 6049 | try sema.analyzeExport(block, src, options, decl_index); | |
| 6038 | const options = try sema.resolveExportOptions(block, options_src, extra.options); | |
| 6039 | if (options.linkage == .Internal) | |
| 6040 | return; | |
| 6041 | if (operand.val.getFunction(mod)) |function| { | |
| 6042 | const decl_index = function.owner_decl; | |
| 6043 | return 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 | }); | |
| 6050 | 6054 | } |
| 6051 | 6055 | |
| 6052 | 6056 | pub fn analyzeExport( |
| ... | ... | @@ -6056,12 +6060,11 @@ pub fn analyzeExport( |
| 6056 | 6060 | options: Module.Export.Options, |
| 6057 | 6061 | exported_decl_index: Decl.Index, |
| 6058 | 6062 | ) !void { |
| 6059 | const Export = Module.Export; | |
| 6063 | const gpa = sema.gpa; | |
| 6060 | 6064 | const mod = sema.mod; |
| 6061 | 6065 | |
| 6062 | if (options.linkage == .Internal) { | |
| 6066 | if (options.linkage == .Internal) | |
| 6063 | 6067 | return; |
| 6064 | } | |
| 6065 | 6068 | |
| 6066 | 6069 | try mod.ensureDeclAnalyzed(exported_decl_index); |
| 6067 | 6070 | const exported_decl = mod.declPtr(exported_decl_index); |
| ... | ... | @@ -6069,7 +6072,7 @@ pub fn analyzeExport( |
| 6069 | 6072 | if (!try sema.validateExternType(exported_decl.ty, .other)) { |
| 6070 | 6073 | const msg = msg: { |
| 6071 | 6074 | const msg = try sema.errMsg(block, src, "unable to export type '{}'", .{exported_decl.ty.fmt(mod)}); |
| 6072 | errdefer msg.destroy(sema.gpa); | |
| 6075 | errdefer msg.destroy(gpa); | |
| 6073 | 6076 | |
| 6074 | 6077 | const src_decl = mod.declPtr(block.src_decl); |
| 6075 | 6078 | try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), exported_decl.ty, .other); |
| ... | ... | @@ -6089,38 +6092,45 @@ pub fn analyzeExport( |
| 6089 | 6092 | try mod.markDeclAlive(exported_decl); |
| 6090 | 6093 | try sema.maybeQueueFuncBodyAnalysis(exported_decl_index); |
| 6091 | 6094 | |
| 6092 | 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 | ||
| 6105 | fn addExport(mod: *Module, export_init: Module.Export) error{OutOfMemory}!void { | |
| 6106 | const gpa = mod.gpa; | |
| 6093 | 6107 | |
| 6094 | 6108 | try mod.decl_exports.ensureUnusedCapacity(gpa, 1); |
| 6109 | try mod.value_exports.ensureUnusedCapacity(gpa, 1); | |
| 6095 | 6110 | try mod.export_owners.ensureUnusedCapacity(gpa, 1); |
| 6096 | 6111 | |
| 6097 | const new_export = try gpa.create(Export); | |
| 6112 | const new_export = try gpa.create(Module.Export); | |
| 6098 | 6113 | errdefer gpa.destroy(new_export); |
| 6099 | 6114 | |
| 6100 | new_export.* = .{ | |
| 6101 | .opts = options, | |
| 6102 | .src = src, | |
| 6103 | .owner_decl = sema.owner_decl_index, | |
| 6104 | .src_decl = block.src_decl, | |
| 6105 | .exported_decl = exported_decl_index, | |
| 6106 | .status = .in_progress, | |
| 6107 | }; | |
| 6115 | new_export.* = export_init; | |
| 6108 | 6116 | |
| 6109 | // Add to export_owners table. | |
| 6110 | const eo_gop = mod.export_owners.getOrPutAssumeCapacity(sema.owner_decl_index); | |
| 6111 | if (!eo_gop.found_existing) { | |
| 6112 | eo_gop.value_ptr.* = .{}; | |
| 6113 | } | |
| 6117 | const eo_gop = mod.export_owners.getOrPutAssumeCapacity(export_init.owner_decl); | |
| 6118 | if (!eo_gop.found_existing) eo_gop.value_ptr.* = .{}; | |
| 6114 | 6119 | try eo_gop.value_ptr.append(gpa, new_export); |
| 6115 | 6120 | errdefer _ = eo_gop.value_ptr.pop(); |
| 6116 | 6121 | |
| 6117 | // Add to exported_decl table. | |
| 6118 | const de_gop = mod.decl_exports.getOrPutAssumeCapacity(exported_decl_index); | |
| 6119 | if (!de_gop.found_existing) { | |
| 6120 | de_gop.value_ptr.* = .{}; | |
| 6122 | switch (export_init.exported) { | |
| 6123 | .decl_index => |decl_index| { | |
| 6124 | const de_gop = mod.decl_exports.getOrPutAssumeCapacity(decl_index); | |
| 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 | }, | |
| 6121 | 6133 | } |
| 6122 | try de_gop.value_ptr.append(gpa, new_export); | |
| 6123 | errdefer _ = de_gop.value_ptr.pop(); | |
| 6124 | 6134 | } |
| 6125 | 6135 | |
| 6126 | 6136 | fn 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 | 1144 | |
| 1145 | 1145 | for (mod.decl_exports.keys(), mod.decl_exports.values()) |decl_index, export_list| { |
| 1146 | 1146 | const global = object.decl_map.get(decl_index) orelse continue; |
| 1147 | const global_base = global.toConst().getBase(&object.builder); | |
| 1148 | for (export_list.items) |exp| { | |
| 1149 | // Detect if the LLVM global has already been created as an extern. In such | |
| 1150 | // case, we need to replace all uses of it with this exported global. | |
| 1151 | const exp_name = object.builder.stringIfExists(mod.intern_pool.stringToSlice(exp.opts.name)) orelse continue; | |
| 1152 | ||
| 1153 | const other_global = object.builder.getGlobal(exp_name) orelse continue; | |
| 1154 | if (other_global.toConst().getBase(&object.builder) == global_base) continue; | |
| 1155 | ||
| 1156 | try global.takeName(other_global, &object.builder); | |
| 1157 | try other_global.replace(global, &object.builder); | |
| 1158 | // Problem: now we need to replace in the decl_map that | |
| 1159 | // the extern decl index points to this new global. However we don't | |
| 1160 | // know the decl index. | |
| 1161 | // Even if we did, a future incremental update to the extern would then | |
| 1162 | // treat the LLVM global as an extern rather than an export, so it would | |
| 1163 | // need a way to check that. | |
| 1164 | // This is a TODO that needs to be solved when making | |
| 1165 | // the LLVM backend support incremental compilation. | |
| 1166 | } | |
| 1147 | try resolveGlobalCollisions(object, global, export_list.items); | |
| 1148 | } | |
| 1149 | ||
| 1150 | for (mod.value_exports.keys(), mod.value_exports.values()) |val, export_list| { | |
| 1151 | const global = object.anon_decl_map.get(val) orelse continue; | |
| 1152 | try resolveGlobalCollisions(object, global, export_list.items); | |
| 1153 | } | |
| 1154 | } | |
| 1155 | ||
| 1156 | fn resolveGlobalCollisions( | |
| 1157 | object: *Object, | |
| 1158 | global: Builder.Global.Index, | |
| 1159 | export_list: []const *Module.Export, | |
| 1160 | ) !void { | |
| 1161 | const mod = object.module; | |
| 1162 | const global_base = global.toConst().getBase(&object.builder); | |
| 1163 | for (export_list) |exp| { | |
| 1164 | // Detect if the LLVM global has already been created as an extern. In such | |
| 1165 | // case, we need to replace all uses of it with this exported global. | |
| 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 | } |
| 1169 | 1183 | |
| ... | ... | @@ -1642,7 +1656,7 @@ pub const Object = struct { |
| 1642 | 1656 | |
| 1643 | 1657 | try fg.wip.finish(); |
| 1644 | 1658 | |
| 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 | } |
| 1647 | 1661 | |
| 1648 | 1662 | pub fn updateDecl(self: *Object, module: *Module, decl_index: Module.Decl.Index) !void { |
| ... | ... | @@ -1662,18 +1676,22 @@ pub const Object = struct { |
| 1662 | 1676 | }, |
| 1663 | 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 | } |
| 1667 | 1681 | |
| 1668 | pub fn updateDeclExports( | |
| 1682 | pub fn updateExports( | |
| 1669 | 1683 | self: *Object, |
| 1670 | 1684 | mod: *Module, |
| 1671 | decl_index: Module.Decl.Index, | |
| 1685 | exported: Module.Exported, | |
| 1672 | 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 | 1692 | const gpa = mod.gpa; |
| 1675 | 1693 | // 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 | 1695 | const global_index = self.decl_map.get(decl_index) orelse return; |
| 1678 | 1696 | const decl = mod.declPtr(decl_index); |
| 1679 | 1697 | if (decl.isExtern(mod)) { |
| ... | ... | @@ -1733,8 +1751,7 @@ pub const Object = struct { |
| 1733 | 1751 | mod.intern_pool.stringToSlice(exports[0].opts.name), |
| 1734 | 1752 | ); |
| 1735 | 1753 | try global_index.rename(main_exp_name, &self.builder); |
| 1736 | global_index.setUnnamedAddr(.default, &self.builder); | |
| 1737 | if (mod.wantDllExports()) global_index.setDllStorageClass(.dllexport, &self.builder); | |
| 1754 | ||
| 1738 | 1755 | if (self.di_map.get(decl)) |di_node| { |
| 1739 | 1756 | const main_exp_name_slice = main_exp_name.slice(&self.builder).?; |
| 1740 | 1757 | if (try decl.isFunction(mod)) { |
| ... | ... | @@ -1755,55 +1772,12 @@ pub const Object = struct { |
| 1755 | 1772 | di_global.replaceLinkageName(linkage_name); |
| 1756 | 1773 | } |
| 1757 | 1774 | } |
| 1758 | global_index.setLinkage(switch (exports[0].opts.linkage) { | |
| 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 | }; | |
| 1775 | ||
| 1777 | 1776 | if (decl.val.getVariable(mod)) |decl_var| if (decl_var.is_threadlocal) |
| 1778 | 1777 | global_index.ptrConst(&self.builder).kind |
| 1779 | 1778 | .variable.setThreadLocal(.generaldynamic, &self.builder); |
| 1780 | 1779 | |
| 1781 | // If a Decl is exported more than one time (which is rare), | |
| 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 | } | |
| 1780 | return updateExportedGlobal(self, mod, global_index, exports); | |
| 1807 | 1781 | } else { |
| 1808 | 1782 | const fqn = try self.builder.string( |
| 1809 | 1783 | mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)), |
| ... | ... | @@ -1824,6 +1798,100 @@ pub const Object = struct { |
| 1824 | 1798 | } |
| 1825 | 1799 | } |
| 1826 | 1800 | |
| 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 | 1895 | pub fn freeDecl(self: *Object, decl_index: Module.Decl.Index) void { |
| 1828 | 1896 | const global = self.decl_map.get(decl_index) orelse return; |
| 1829 | 1897 | global.delete(&self.builder); |
src/link.zig+32-17| ... | ... | @@ -587,7 +587,7 @@ pub const File = struct { |
| 587 | 587 | } |
| 588 | 588 | } |
| 589 | 589 | |
| 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 | 591 | pub fn updateDecl(base: *File, module: *Module, decl_index: Module.Decl.Index) UpdateDeclError!void { |
| 592 | 592 | const decl = module.declPtr(decl_index); |
| 593 | 593 | assert(decl.has_tv); |
| ... | ... | @@ -609,7 +609,7 @@ pub const File = struct { |
| 609 | 609 | } |
| 610 | 610 | } |
| 611 | 611 | |
| 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 | 613 | pub fn updateFunc(base: *File, module: *Module, func_index: InternPool.Index, air: Air, liveness: Liveness) UpdateDeclError!void { |
| 614 | 614 | if (build_options.only_c) { |
| 615 | 615 | assert(base.tag == .c); |
| ... | ... | @@ -882,33 +882,34 @@ pub const File = struct { |
| 882 | 882 | } |
| 883 | 883 | } |
| 884 | 884 | |
| 885 | pub const UpdateDeclExportsError = error{ | |
| 885 | pub const UpdateExportsError = error{ | |
| 886 | 886 | OutOfMemory, |
| 887 | 887 | AnalysisFail, |
| 888 | 888 | }; |
| 889 | 889 | |
| 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 | 893 | /// May be called before or after updateDecl for any given Decl. |
| 891 | pub fn updateDeclExports( | |
| 894 | pub fn updateExports( | |
| 892 | 895 | base: *File, |
| 893 | 896 | module: *Module, |
| 894 | decl_index: Module.Decl.Index, | |
| 897 | exported: Module.Exported, | |
| 895 | 898 | exports: []const *Module.Export, |
| 896 | ) UpdateDeclExportsError!void { | |
| 897 | const decl = module.declPtr(decl_index); | |
| 898 | assert(decl.has_tv); | |
| 899 | ) UpdateExportsError!void { | |
| 899 | 900 | if (build_options.only_c) { |
| 900 | 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 | 904 | switch (base.tag) { |
| 904 | .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl_index, exports), | |
| 905 | .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl_index, exports), | |
| 906 | .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl_index, exports), | |
| 907 | .c => return @fieldParentPtr(C, "base", base).updateDeclExports(module, decl_index, exports), | |
| 908 | .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl_index, exports), | |
| 909 | .spirv => return @fieldParentPtr(SpirV, "base", base).updateDeclExports(module, decl_index, exports), | |
| 910 | .plan9 => return @fieldParentPtr(Plan9, "base", base).updateDeclExports(module, decl_index, exports), | |
| 911 | .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateDeclExports(module, decl_index, exports), | |
| 905 | .coff => return @fieldParentPtr(Coff, "base", base).updateExports(module, exported, exports), | |
| 906 | .elf => return @fieldParentPtr(Elf, "base", base).updateExports(module, exported, exports), | |
| 907 | .macho => return @fieldParentPtr(MachO, "base", base).updateExports(module, exported, exports), | |
| 908 | .c => return @fieldParentPtr(C, "base", base).updateExports(module, exported, exports), | |
| 909 | .wasm => return @fieldParentPtr(Wasm, "base", base).updateExports(module, exported, exports), | |
| 910 | .spirv => return @fieldParentPtr(SpirV, "base", base).updateExports(module, exported, exports), | |
| 911 | .plan9 => return @fieldParentPtr(Plan9, "base", base).updateExports(module, exported, exports), | |
| 912 | .nvptx => return @fieldParentPtr(NvPtx, "base", base).updateExports(module, exported, exports), | |
| 912 | 913 | } |
| 913 | 914 | } |
| 914 | 915 | |
| ... | ... | @@ -968,6 +969,20 @@ pub const File = struct { |
| 968 | 969 | } |
| 969 | 970 | } |
| 970 | 971 | |
| 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 | 986 | /// This function is called by the frontend before flush(). It communicates that |
| 972 | 987 | /// `options.bin_file.emit` directory needs to be renamed from |
| 973 | 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 | 753 | try file.pwritevAll(all_buffers.items, 0); |
| 754 | 754 | } |
| 755 | 755 | |
| 756 | pub fn updateDeclExports( | |
| 756 | pub fn updateExports( | |
| 757 | 757 | self: *C, |
| 758 | 758 | module: *Module, |
| 759 | decl_index: Module.Decl.Index, | |
| 759 | exported: Module.Exported, | |
| 760 | 760 | exports: []const *Module.Export, |
| 761 | 761 | ) !void { |
| 762 | 762 | _ = exports; |
| 763 | _ = decl_index; | |
| 763 | _ = exported; | |
| 764 | 764 | _ = module; |
| 765 | 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 | 1075 | |
| 1076 | 1076 | // Since we updated the vaddr and the size, each corresponding export |
| 1077 | 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 | } |
| 1080 | 1080 | |
| 1081 | 1081 | pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 { |
| ... | ... | @@ -1195,7 +1195,7 @@ pub fn updateDecl( |
| 1195 | 1195 | |
| 1196 | 1196 | // Since we updated the vaddr and the size, each corresponding export |
| 1197 | 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 | } |
| 1200 | 1200 | |
| 1201 | 1201 | fn updateLazySymbolAtom( |
| ... | ... | @@ -1409,12 +1409,12 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void { |
| 1409 | 1409 | } |
| 1410 | 1410 | } |
| 1411 | 1411 | |
| 1412 | pub fn updateDeclExports( | |
| 1412 | pub fn updateExports( | |
| 1413 | 1413 | self: *Coff, |
| 1414 | 1414 | mod: *Module, |
| 1415 | decl_index: Module.Decl.Index, | |
| 1415 | exported: Module.Exported, | |
| 1416 | 1416 | exports: []const *Module.Export, |
| 1417 | ) link.File.UpdateDeclExportsError!void { | |
| 1417 | ) link.File.UpdateExportsError!void { | |
| 1418 | 1418 | if (build_options.skip_non_native and builtin.object_format != .coff) { |
| 1419 | 1419 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 1420 | 1420 | } |
| ... | ... | @@ -1425,7 +1425,11 @@ pub fn updateDeclExports( |
| 1425 | 1425 | // Even in the case of LLVM, we need to notice certain exported symbols in order to |
| 1426 | 1426 | // detect the default subsystem. |
| 1427 | 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 | 1433 | if (exported_decl.getOwnedFunction(mod) == null) continue; |
| 1430 | 1434 | const winapi_cc = switch (self.base.options.target.cpu.arch) { |
| 1431 | 1435 | .x86 => std.builtin.CallingConvention.Stdcall, |
| ... | ... | @@ -1452,12 +1456,19 @@ pub fn updateDeclExports( |
| 1452 | 1456 | } |
| 1453 | 1457 | } |
| 1454 | 1458 | |
| 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); | |
| 1456 | 1460 | |
| 1457 | 1461 | if (self.base.options.emit == null) return; |
| 1458 | 1462 | |
| 1459 | 1463 | const gpa = self.base.allocator; |
| 1460 | 1464 | |
| 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 | 1472 | const decl = mod.declPtr(decl_index); |
| 1462 | 1473 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); |
| 1463 | 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 | 3306 | |
| 3307 | 3307 | // Since we updated the vaddr and the size, each corresponding export |
| 3308 | 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 | } |
| 3311 | 3311 | |
| 3312 | 3312 | pub fn updateDecl( |
| ... | ... | @@ -3388,7 +3388,7 @@ pub fn updateDecl( |
| 3388 | 3388 | |
| 3389 | 3389 | // Since we updated the vaddr and the size, each corresponding export |
| 3390 | 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 | } |
| 3393 | 3393 | |
| 3394 | 3394 | fn updateLazySymbol(self: *Elf, sym: link.File.LazySymbol, symbol_index: Symbol.Index) !void { |
| ... | ... | @@ -3555,16 +3555,16 @@ fn lowerConst( |
| 3555 | 3555 | return .{ .ok = sym_index }; |
| 3556 | 3556 | } |
| 3557 | 3557 | |
| 3558 | pub fn updateDeclExports( | |
| 3558 | pub fn updateExports( | |
| 3559 | 3559 | self: *Elf, |
| 3560 | 3560 | mod: *Module, |
| 3561 | decl_index: Module.Decl.Index, | |
| 3561 | exported: Module.Exported, | |
| 3562 | 3562 | exports: []const *Module.Export, |
| 3563 | ) link.File.UpdateDeclExportsError!void { | |
| 3563 | ) link.File.UpdateExportsError!void { | |
| 3564 | 3564 | if (build_options.skip_non_native and builtin.object_format != .elf) { |
| 3565 | 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); | |
| 3568 | 3568 | |
| 3569 | 3569 | if (self.base.options.emit == null) return; |
| 3570 | 3570 | |
| ... | ... | @@ -3573,6 +3573,13 @@ pub fn updateDeclExports( |
| 3573 | 3573 | |
| 3574 | 3574 | const gpa = self.base.allocator; |
| 3575 | 3575 | |
| 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 | 3583 | const zig_module = self.file(self.zig_module_index.?).?.zig_module; |
| 3577 | 3584 | const decl = mod.declPtr(decl_index); |
| 3578 | 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 | 1670 | const global_is_weak = global_sym.sect() and (global_sym.weakDef() or global_sym.pext()); |
| 1671 | 1671 | |
| 1672 | 1672 | if (sym_is_strong and global_is_strong) { |
| 1673 | // TODO redo this logic with corresponding logic in updateDeclExports to avoid this | |
| 1673 | // TODO redo this logic with corresponding logic in updateExports to avoid this | |
| 1674 | 1674 | // ugly check. |
| 1675 | 1675 | if (self.mode == .zld) { |
| 1676 | 1676 | try self.reportSymbolCollision(global, current); |
| ... | ... | @@ -2180,7 +2180,7 @@ pub fn updateFunc(self: *MachO, mod: *Module, func_index: InternPool.Index, air: |
| 2180 | 2180 | |
| 2181 | 2181 | // Since we updated the vaddr and the size, each corresponding export symbol also |
| 2182 | 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 | } |
| 2185 | 2185 | |
| 2186 | 2186 | pub 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 | 2340 | |
| 2341 | 2341 | // Since we updated the vaddr and the size, each corresponding export symbol also |
| 2342 | 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 | } |
| 2345 | 2345 | |
| 2346 | 2346 | fn updateLazySymbolAtom( |
| ... | ... | @@ -2529,7 +2529,7 @@ fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: Module.D |
| 2529 | 2529 | ); |
| 2530 | 2530 | } |
| 2531 | 2531 | |
| 2532 | try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index)); | |
| 2532 | try self.updateExports(module, .{ .decl_index = decl_index }, module.getDeclExports(decl_index)); | |
| 2533 | 2533 | |
| 2534 | 2534 | // 2. Create a TLV descriptor. |
| 2535 | 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 | 2670 | } |
| 2671 | 2671 | } |
| 2672 | 2672 | |
| 2673 | pub fn updateDeclExports( | |
| 2673 | pub fn updateExports( | |
| 2674 | 2674 | self: *MachO, |
| 2675 | 2675 | mod: *Module, |
| 2676 | decl_index: Module.Decl.Index, | |
| 2676 | exported: Module.Exported, | |
| 2677 | 2677 | exports: []const *Module.Export, |
| 2678 | ) File.UpdateDeclExportsError!void { | |
| 2678 | ) File.UpdateExportsError!void { | |
| 2679 | 2679 | if (build_options.skip_non_native and builtin.object_format != .macho) { |
| 2680 | 2680 | @panic("Attempted to compile for object format that was disabled by build configuration"); |
| 2681 | 2681 | } |
| 2682 | 2682 | if (self.llvm_object) |llvm_object| |
| 2683 | return llvm_object.updateDeclExports(mod, decl_index, exports); | |
| 2683 | return llvm_object.updateExports(mod, exported, exports); | |
| 2684 | 2684 | |
| 2685 | 2685 | if (self.base.options.emit == null) return; |
| 2686 | 2686 | |
| ... | ... | @@ -2689,6 +2689,13 @@ pub fn updateDeclExports( |
| 2689 | 2689 | |
| 2690 | 2690 | const gpa = self.base.allocator; |
| 2691 | 2691 | |
| 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 | 2699 | const decl = mod.declPtr(decl_index); |
| 2693 | 2700 | const atom_index = try self.getOrCreateAtomForDecl(decl_index); |
| 2694 | 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 | 74 | return self.llvm_object.updateDecl(module, decl_index); |
| 75 | 75 | } |
| 76 | 76 | |
| 77 | pub fn updateDeclExports( | |
| 77 | pub fn updateExports( | |
| 78 | 78 | self: *NvPtx, |
| 79 | 79 | module: *Module, |
| 80 | decl_index: Module.Decl.Index, | |
| 80 | exported: Module.Exported, | |
| 81 | 81 | exports: []const *Module.Export, |
| 82 | 82 | ) !void { |
| 83 | 83 | if (build_options.skip_non_native and builtin.object_format != .nvptx) { |
| 84 | 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 | } |
| 88 | 88 | |
| 89 | 89 | pub 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 | 1116 | return atom_idx; |
| 1117 | 1117 | } |
| 1118 | 1118 | |
| 1119 | pub fn updateDeclExports( | |
| 1119 | pub fn updateExports( | |
| 1120 | 1120 | self: *Plan9, |
| 1121 | 1121 | module: *Module, |
| 1122 | decl_index: Module.Decl.Index, | |
| 1122 | exported: Module.Exported, | |
| 1123 | 1123 | exports: []const *Module.Export, |
| 1124 | 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 | 1129 | // we do all the things in flush |
| 1127 | 1130 | _ = module; |
| 1128 | 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 | 120 | try self.object.updateDecl(module, decl_index); |
| 121 | 121 | } |
| 122 | 122 | |
| 123 | pub fn updateDeclExports( | |
| 123 | pub fn updateExports( | |
| 124 | 124 | self: *SpirV, |
| 125 | 125 | mod: *Module, |
| 126 | decl_index: Module.Decl.Index, | |
| 126 | exported: Module.Exported, | |
| 127 | 127 | exports: []const *Module.Export, |
| 128 | 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 | 136 | const decl = mod.declPtr(decl_index); |
| 130 | 137 | if (decl.val.isFuncBody(mod) and decl.ty.fnCallingConvention(mod) == .Kernel) { |
| 131 | 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 | 1786 | } |
| 1787 | 1787 | } |
| 1788 | 1788 | |
| 1789 | pub fn updateDeclExports( | |
| 1789 | pub fn updateExports( | |
| 1790 | 1790 | wasm: *Wasm, |
| 1791 | 1791 | mod: *Module, |
| 1792 | decl_index: Module.Decl.Index, | |
| 1792 | exported: Module.Exported, | |
| 1793 | 1793 | exports: []const *Module.Export, |
| 1794 | 1794 | ) !void { |
| 1795 | 1795 | if (build_options.skip_non_native and builtin.object_format != .wasm) { |
| 1796 | 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); | |
| 1799 | 1799 | |
| 1800 | 1800 | if (wasm.base.options.emit == null) return; |
| 1801 | 1801 | |
| 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 | 1809 | const decl = mod.declPtr(decl_index); |
| 1803 | 1810 | const atom_index = try wasm.getOrCreateAtomForDecl(decl_index); |
| 1804 | 1811 | const atom = wasm.getAtom(atom_index); |
| ... | ... | @@ -1816,7 +1823,19 @@ pub fn updateDeclExports( |
| 1816 | 1823 | continue; |
| 1817 | 1824 | } |
| 1818 | 1825 | |
| 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 | 1839 | const exported_atom = wasm.getAtom(exported_atom_index); |
| 1821 | 1840 | const export_name = try wasm.string_table.put(wasm.base.allocator, mod.intern_pool.stringToSlice(exp.opts.name)); |
| 1822 | 1841 | const sym_loc = exported_atom.symbolLoc(); |
test/behavior/export.zig+4| ... | ... | @@ -72,6 +72,8 @@ test "exporting using field access" { |
| 72 | 72 | } |
| 73 | 73 | |
| 74 | 74 | test "exporting comptime-known value" { |
| 75 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 76 | ||
| 75 | 77 | const x: u32 = 10; |
| 76 | 78 | @export(x, .{ .name = "exporting_comptime_known_value_foo" }); |
| 77 | 79 | const S = struct { |
| ... | ... | @@ -81,6 +83,8 @@ test "exporting comptime-known value" { |
| 81 | 83 | } |
| 82 | 84 | |
| 83 | 85 | test "exporting comptime var" { |
| 86 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 87 | ||
| 84 | 88 | comptime var x: u32 = 5; |
| 85 | 89 | @export(x, .{ .name = "exporting_comptime_var_foo" }); |
| 86 | 90 | x = 7; // modifying this now shouldn't change anything |