aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/llvm.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-10-27 15:20:13 -0400
committerGitHub <noreply@github.com>2023-10-27 15:20:13 -0400
commit1c85b0acbb5148dec7ff671e767354f3df5c6b83 (patch)
tree29edfb52b0885c647ad4f72a12e13683d513e81b /src/codegen/llvm.zig
parent772636ed0d4a2051955197999c9ddf2906384806 (diff)
parent4bc88dd11641a664d80b00ad784bafc6da776697 (diff)
downloadzig-1c85b0acbb5148dec7ff671e767354f3df5c6b83.tar.gz
zig-1c85b0acbb5148dec7ff671e767354f3df5c6b83.zip
Merge pull request #17735 from ziglang/export-anon
link: support exporting constant values without a Decl
Diffstat (limited to 'src/codegen/llvm.zig')
-rw-r--r--src/codegen/llvm.zig214
1 files changed, 141 insertions, 73 deletions
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index b202320194..9edc456b35 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -1144,26 +1144,40 @@ pub const Object = struct {
for (mod.decl_exports.keys(), mod.decl_exports.values()) |decl_index, export_list| {
const global = object.decl_map.get(decl_index) orelse continue;
- const global_base = global.toConst().getBase(&object.builder);
- for (export_list.items) |exp| {
- // Detect if the LLVM global has already been created as an extern. In such
- // case, we need to replace all uses of it with this exported global.
- const exp_name = object.builder.stringIfExists(mod.intern_pool.stringToSlice(exp.opts.name)) orelse continue;
-
- const other_global = object.builder.getGlobal(exp_name) orelse continue;
- if (other_global.toConst().getBase(&object.builder) == global_base) continue;
-
- try global.takeName(other_global, &object.builder);
- try other_global.replace(global, &object.builder);
- // Problem: now we need to replace in the decl_map that
- // the extern decl index points to this new global. However we don't
- // know the decl index.
- // Even if we did, a future incremental update to the extern would then
- // treat the LLVM global as an extern rather than an export, so it would
- // need a way to check that.
- // This is a TODO that needs to be solved when making
- // the LLVM backend support incremental compilation.
- }
+ try resolveGlobalCollisions(object, global, export_list.items);
+ }
+
+ for (mod.value_exports.keys(), mod.value_exports.values()) |val, export_list| {
+ const global = object.anon_decl_map.get(val) orelse continue;
+ try resolveGlobalCollisions(object, global, export_list.items);
+ }
+ }
+
+ fn resolveGlobalCollisions(
+ object: *Object,
+ global: Builder.Global.Index,
+ export_list: []const *Module.Export,
+ ) !void {
+ const mod = object.module;
+ const global_base = global.toConst().getBase(&object.builder);
+ for (export_list) |exp| {
+ // Detect if the LLVM global has already been created as an extern. In such
+ // case, we need to replace all uses of it with this exported global.
+ const exp_name = object.builder.stringIfExists(mod.intern_pool.stringToSlice(exp.opts.name)) orelse continue;
+
+ const other_global = object.builder.getGlobal(exp_name) orelse continue;
+ if (other_global.toConst().getBase(&object.builder) == global_base) continue;
+
+ try global.takeName(other_global, &object.builder);
+ try other_global.replace(global, &object.builder);
+ // Problem: now we need to replace in the decl_map that
+ // the extern decl index points to this new global. However we don't
+ // know the decl index.
+ // Even if we did, a future incremental update to the extern would then
+ // treat the LLVM global as an extern rather than an export, so it would
+ // need a way to check that.
+ // This is a TODO that needs to be solved when making
+ // the LLVM backend support incremental compilation.
}
}
@@ -1642,7 +1656,7 @@ pub const Object = struct {
try fg.wip.finish();
- try o.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index));
+ try o.updateExports(mod, .{ .decl_index = decl_index }, mod.getDeclExports(decl_index));
}
pub fn updateDecl(self: *Object, module: *Module, decl_index: Module.Decl.Index) !void {
@@ -1662,18 +1676,22 @@ pub const Object = struct {
},
else => |e| return e,
};
- try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
+ try self.updateExports(module, .{ .decl_index = decl_index }, module.getDeclExports(decl_index));
}
- pub fn updateDeclExports(
+ pub fn updateExports(
self: *Object,
mod: *Module,
- decl_index: Module.Decl.Index,
+ exported: Module.Exported,
exports: []const *Module.Export,
- ) !void {
+ ) link.File.UpdateExportsError!void {
+ const decl_index = switch (exported) {
+ .decl_index => |i| i,
+ .value => |val| return updateExportedValue(self, mod, val, exports),
+ };
const gpa = mod.gpa;
// If the module does not already have the function, we ignore this function call
- // because we call `updateDeclExports` at the end of `updateFunc` and `updateDecl`.
+ // because we call `updateExports` at the end of `updateFunc` and `updateDecl`.
const global_index = self.decl_map.get(decl_index) orelse return;
const decl = mod.declPtr(decl_index);
if (decl.isExtern(mod)) {
@@ -1733,8 +1751,7 @@ pub const Object = struct {
mod.intern_pool.stringToSlice(exports[0].opts.name),
);
try global_index.rename(main_exp_name, &self.builder);
- global_index.setUnnamedAddr(.default, &self.builder);
- if (mod.wantDllExports()) global_index.setDllStorageClass(.dllexport, &self.builder);
+
if (self.di_map.get(decl)) |di_node| {
const main_exp_name_slice = main_exp_name.slice(&self.builder).?;
if (try decl.isFunction(mod)) {
@@ -1755,55 +1772,12 @@ pub const Object = struct {
di_global.replaceLinkageName(linkage_name);
}
}
- global_index.setLinkage(switch (exports[0].opts.linkage) {
- .Internal => unreachable,
- .Strong => .external,
- .Weak => .weak_odr,
- .LinkOnce => .linkonce_odr,
- }, &self.builder);
- global_index.setVisibility(switch (exports[0].opts.visibility) {
- .default => .default,
- .hidden => .hidden,
- .protected => .protected,
- }, &self.builder);
- if (mod.intern_pool.stringToSliceUnwrap(exports[0].opts.section)) |section|
- switch (global_index.ptrConst(&self.builder).kind) {
- inline .variable, .function => |impl_index| impl_index.setSection(
- try self.builder.string(section),
- &self.builder,
- ),
- .alias, .replaced => unreachable,
- };
+
if (decl.val.getVariable(mod)) |decl_var| if (decl_var.is_threadlocal)
global_index.ptrConst(&self.builder).kind
.variable.setThreadLocal(.generaldynamic, &self.builder);
- // If a Decl is exported more than one time (which is rare),
- // we add aliases for all but the first export.
- // TODO LLVM C API does not support deleting aliases.
- // The planned solution to this is https://github.com/ziglang/zig/issues/13265
- // Until then we iterate over existing aliases and make them point
- // to the correct decl, or otherwise add a new alias. Old aliases are leaked.
- for (exports[1..]) |exp| {
- const exp_name = try self.builder.string(mod.intern_pool.stringToSlice(exp.opts.name));
- if (self.builder.getGlobal(exp_name)) |global| {
- switch (global.ptrConst(&self.builder).kind) {
- .alias => |alias| {
- alias.setAliasee(global_index.toConst(), &self.builder);
- continue;
- },
- .variable, .function => {},
- .replaced => unreachable,
- }
- }
- const alias_index = try self.builder.addAlias(
- .empty,
- global_index.typeOf(&self.builder),
- .default,
- global_index.toConst(),
- );
- try alias_index.rename(exp_name, &self.builder);
- }
+ return updateExportedGlobal(self, mod, global_index, exports);
} else {
const fqn = try self.builder.string(
mod.intern_pool.stringToSlice(try decl.getFullyQualifiedName(mod)),
@@ -1824,6 +1798,100 @@ pub const Object = struct {
}
}
+ fn updateExportedValue(
+ o: *Object,
+ mod: *Module,
+ exported_value: InternPool.Index,
+ exports: []const *Module.Export,
+ ) link.File.UpdateExportsError!void {
+ const gpa = mod.gpa;
+ const main_exp_name = try o.builder.string(
+ mod.intern_pool.stringToSlice(exports[0].opts.name),
+ );
+ const global_index = i: {
+ const gop = try o.anon_decl_map.getOrPut(gpa, exported_value);
+ if (gop.found_existing) {
+ const global_index = gop.value_ptr.*;
+ try global_index.rename(main_exp_name, &o.builder);
+ break :i global_index;
+ }
+ const llvm_addr_space = toLlvmAddressSpace(.generic, o.target);
+ const variable_index = try o.builder.addVariable(
+ main_exp_name,
+ try o.lowerType(mod.intern_pool.typeOf(exported_value).toType()),
+ llvm_addr_space,
+ );
+ const global_index = variable_index.ptrConst(&o.builder).global;
+ gop.value_ptr.* = global_index;
+ // This line invalidates `gop`.
+ const init_val = o.lowerValue(exported_value) catch |err| switch (err) {
+ error.OutOfMemory => return error.OutOfMemory,
+ error.CodegenFail => return error.AnalysisFail,
+ };
+ try variable_index.setInitializer(init_val, &o.builder);
+ break :i global_index;
+ };
+ return updateExportedGlobal(o, mod, global_index, exports);
+ }
+
+ fn updateExportedGlobal(
+ o: *Object,
+ mod: *Module,
+ global_index: Builder.Global.Index,
+ exports: []const *Module.Export,
+ ) link.File.UpdateExportsError!void {
+ global_index.setUnnamedAddr(.default, &o.builder);
+ if (mod.wantDllExports()) global_index.setDllStorageClass(.dllexport, &o.builder);
+ global_index.setLinkage(switch (exports[0].opts.linkage) {
+ .Internal => unreachable,
+ .Strong => .external,
+ .Weak => .weak_odr,
+ .LinkOnce => .linkonce_odr,
+ }, &o.builder);
+ global_index.setVisibility(switch (exports[0].opts.visibility) {
+ .default => .default,
+ .hidden => .hidden,
+ .protected => .protected,
+ }, &o.builder);
+ if (mod.intern_pool.stringToSliceUnwrap(exports[0].opts.section)) |section|
+ switch (global_index.ptrConst(&o.builder).kind) {
+ .variable => |impl_index| impl_index.setSection(
+ try o.builder.string(section),
+ &o.builder,
+ ),
+ .function => unreachable,
+ .alias => unreachable,
+ .replaced => unreachable,
+ };
+
+ // If a Decl is exported more than one time (which is rare),
+ // we add aliases for all but the first export.
+ // TODO LLVM C API does not support deleting aliases.
+ // The planned solution to this is https://github.com/ziglang/zig/issues/13265
+ // Until then we iterate over existing aliases and make them point
+ // to the correct decl, or otherwise add a new alias. Old aliases are leaked.
+ for (exports[1..]) |exp| {
+ const exp_name = try o.builder.string(mod.intern_pool.stringToSlice(exp.opts.name));
+ if (o.builder.getGlobal(exp_name)) |global| {
+ switch (global.ptrConst(&o.builder).kind) {
+ .alias => |alias| {
+ alias.setAliasee(global_index.toConst(), &o.builder);
+ continue;
+ },
+ .variable, .function => {},
+ .replaced => unreachable,
+ }
+ }
+ const alias_index = try o.builder.addAlias(
+ .empty,
+ global_index.typeOf(&o.builder),
+ .default,
+ global_index.toConst(),
+ );
+ try alias_index.rename(exp_name, &o.builder);
+ }
+ }
+
pub fn freeDecl(self: *Object, decl_index: Module.Decl.Index) void {
const global = self.decl_map.get(decl_index) orelse return;
global.delete(&self.builder);