Skip to content

Commit 7dcda5b

Browse files
authored
Merge pull request #7182 from LemonBoy/externnnn
Initial implementation of @extern builtin
2 parents bf0cc32 + ccdaf94 commit 7dcda5b

File tree

6 files changed

+272
-5
lines changed

6 files changed

+272
-5
lines changed

lib/std/builtin.zig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -643,6 +643,15 @@ pub const ExportOptions = struct {
643643
section: ?[]const u8 = null,
644644
};
645645

646+
/// This data structure is used by the Zig language code generation and
647+
/// therefore must be kept in sync with the compiler implementation.
648+
pub const ExternOptions = struct {
649+
name: []const u8,
650+
library_name: ?[]const u8 = null,
651+
linkage: GlobalLinkage = .Strong,
652+
is_thread_local: bool = false,
653+
};
654+
646655
/// This function type is used by the Zig language code generation and
647656
/// therefore must be kept in sync with the compiler implementation.
648657
pub const TestFn = struct {

src/stage1/all_types.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1808,6 +1808,7 @@ enum BuiltinFnId {
18081808
BuiltinFnIdThis,
18091809
BuiltinFnIdSetAlignStack,
18101810
BuiltinFnIdExport,
1811+
BuiltinFnIdExtern,
18111812
BuiltinFnIdErrorReturnTrace,
18121813
BuiltinFnIdAtomicRmw,
18131814
BuiltinFnIdAtomicLoad,
@@ -2619,6 +2620,7 @@ enum IrInstSrcId {
26192620
IrInstSrcIdSetAlignStack,
26202621
IrInstSrcIdArgType,
26212622
IrInstSrcIdExport,
2623+
IrInstSrcIdExtern,
26222624
IrInstSrcIdErrorReturnTrace,
26232625
IrInstSrcIdErrorUnion,
26242626
IrInstSrcIdAtomicRmw,
@@ -2736,6 +2738,7 @@ enum IrInstGenId {
27362738
IrInstGenIdConst,
27372739
IrInstGenIdWasmMemorySize,
27382740
IrInstGenIdWasmMemoryGrow,
2741+
IrInstGenIdExtern,
27392742
};
27402743

27412744
// Common fields between IrInstSrc and IrInstGen. This allows future passes
@@ -4146,6 +4149,21 @@ struct IrInstSrcExport {
41464149
IrInstSrc *options;
41474150
};
41484151

4152+
struct IrInstSrcExtern {
4153+
IrInstSrc base;
4154+
4155+
IrInstSrc *type;
4156+
IrInstSrc *options;
4157+
};
4158+
4159+
struct IrInstGenExtern {
4160+
IrInstGen base;
4161+
4162+
Buf *name;
4163+
GlobalLinkageId linkage;
4164+
bool is_thread_local;
4165+
};
4166+
41494167
enum IrInstErrorReturnTraceOptional {
41504168
IrInstErrorReturnTraceNull,
41514169
IrInstErrorReturnTraceNonNull,

src/stage1/codegen.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6389,6 +6389,30 @@ static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutableGen *executable, IrI
63896389
return LLVMBuildTrunc(g->builder, shifted, get_llvm_type(g, expr_type), "");
63906390
}
63916391

6392+
static LLVMValueRef ir_render_extern(CodeGen *g, IrExecutableGen *executable,
6393+
IrInstGenExtern *instruction)
6394+
{
6395+
ZigType *expr_type = instruction->base.value->type;
6396+
assert(get_src_ptr_type(expr_type));
6397+
6398+
const char *symbol_name = buf_ptr(instruction->name);
6399+
const LLVMLinkage linkage = to_llvm_linkage(instruction->linkage, true);
6400+
6401+
LLVMValueRef global_value = LLVMGetNamedGlobal(g->module, symbol_name);
6402+
if (global_value == nullptr) {
6403+
global_value = LLVMAddGlobal(g->module, get_llvm_type(g, expr_type), symbol_name);
6404+
LLVMSetLinkage(global_value, linkage);
6405+
LLVMSetGlobalConstant(global_value, true);
6406+
if (instruction->is_thread_local)
6407+
LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel);
6408+
} else if (LLVMGetLinkage(global_value) != linkage) {
6409+
// XXX: Handle this case better!
6410+
zig_panic("duplicate extern symbol");
6411+
}
6412+
6413+
return LLVMBuildBitCast(g->builder, global_value, get_llvm_type(g, expr_type), "");
6414+
}
6415+
63926416
static LLVMValueRef ir_render_bit_reverse(CodeGen *g, IrExecutableGen *executable, IrInstGenBitReverse *instruction) {
63936417
LLVMValueRef op = ir_llvm_value(g, instruction->op);
63946418
ZigType *int_type = instruction->base.value->type;
@@ -6902,6 +6926,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutableGen *executabl
69026926
return ir_render_wasm_memory_size(g, executable, (IrInstGenWasmMemorySize *) instruction);
69036927
case IrInstGenIdWasmMemoryGrow:
69046928
return ir_render_wasm_memory_grow(g, executable, (IrInstGenWasmMemoryGrow *) instruction);
6929+
case IrInstGenIdExtern:
6930+
return ir_render_extern(g, executable, (IrInstGenExtern *) instruction);
69056931
}
69066932
zig_unreachable();
69076933
}
@@ -8800,6 +8826,7 @@ static void define_builtin_fns(CodeGen *g) {
88008826
create_builtin_fn(g, BuiltinFnIdAlignCast, "alignCast", 2);
88018827
create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1);
88028828
create_builtin_fn(g, BuiltinFnIdExport, "export", 2);
8829+
create_builtin_fn(g, BuiltinFnIdExtern, "extern", 2);
88038830
create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0);
88048831
create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5);
88058832
create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3);

src/stage1/ir.cpp

Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {
519519
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTagType *>(inst));
520520
case IrInstSrcIdExport:
521521
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExport *>(inst));
522+
case IrInstSrcIdExtern:
523+
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExtern *>(inst));
522524
case IrInstSrcIdErrorReturnTrace:
523525
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrorReturnTrace *>(inst));
524526
case IrInstSrcIdErrorUnion:
@@ -755,6 +757,8 @@ void destroy_instruction_gen(IrInstGen *inst) {
755757
return heap::c_allocator.destroy(reinterpret_cast<IrInstGenWasmMemorySize *>(inst));
756758
case IrInstGenIdWasmMemoryGrow:
757759
return heap::c_allocator.destroy(reinterpret_cast<IrInstGenWasmMemoryGrow *>(inst));
760+
case IrInstGenIdExtern:
761+
return heap::c_allocator.destroy(reinterpret_cast<IrInstGenExtern *>(inst));
758762
}
759763
zig_unreachable();
760764
}
@@ -1555,6 +1559,10 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcExport *) {
15551559
return IrInstSrcIdExport;
15561560
}
15571561

1562+
static constexpr IrInstSrcId ir_inst_id(IrInstSrcExtern *) {
1563+
return IrInstSrcIdExtern;
1564+
}
1565+
15581566
static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrorReturnTrace *) {
15591567
return IrInstSrcIdErrorReturnTrace;
15601568
}
@@ -1999,6 +2007,10 @@ static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemoryGrow *) {
19992007
return IrInstGenIdWasmMemoryGrow;
20002008
}
20012009

2010+
static constexpr IrInstGenId ir_inst_id(IrInstGenExtern *) {
2011+
return IrInstGenIdExtern;
2012+
}
2013+
20022014
template<typename T>
20032015
static T *ir_create_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
20042016
T *special_instruction = heap::c_allocator.create<T>();
@@ -2807,6 +2819,33 @@ static IrInstSrc *ir_build_export(IrBuilderSrc *irb, Scope *scope, AstNode *sour
28072819
return &export_instruction->base;
28082820
}
28092821

2822+
static IrInstSrc *ir_build_extern(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
2823+
IrInstSrc *type, IrInstSrc *options)
2824+
{
2825+
IrInstSrcExtern *extern_instruction = ir_build_instruction<IrInstSrcExtern>(
2826+
irb, scope, source_node);
2827+
extern_instruction->type = type;
2828+
extern_instruction->options = options;
2829+
2830+
ir_ref_instruction(type, irb->current_basic_block);
2831+
ir_ref_instruction(options, irb->current_basic_block);
2832+
2833+
return &extern_instruction->base;
2834+
}
2835+
2836+
static IrInstGen *ir_build_extern_gen(IrAnalyze *ira, IrInst *source_instr, Buf *name,
2837+
GlobalLinkageId linkage, bool is_thread_local, ZigType *expr_type)
2838+
{
2839+
IrInstGenExtern *instruction = ir_build_inst_gen<IrInstGenExtern>(&ira->new_irb,
2840+
source_instr->scope, source_instr->source_node);
2841+
instruction->base.value->type = expr_type;
2842+
instruction->name = name;
2843+
instruction->linkage = linkage;
2844+
instruction->is_thread_local = is_thread_local;
2845+
2846+
return &instruction->base;
2847+
}
2848+
28102849
static IrInstSrc *ir_build_load_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *ptr) {
28112850
IrInstSrcLoadPtr *instruction = ir_build_instruction<IrInstSrcLoadPtr>(irb, scope, source_node);
28122851
instruction->ptr = ptr;
@@ -7376,6 +7415,30 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod
73767415
IrInstSrc *ir_export = ir_build_export(irb, scope, node, target_value, casted_options_value);
73777416
return ir_lval_wrap(irb, scope, ir_export, lval, result_loc);
73787417
}
7418+
case BuiltinFnIdExtern:
7419+
{
7420+
// Cast the options parameter to the options type
7421+
ZigType *options_type = get_builtin_type(irb->codegen, "ExternOptions");
7422+
IrInstSrc *options_type_inst = ir_build_const_type(irb, scope, node, options_type);
7423+
ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc());
7424+
7425+
AstNode *type_node = node->data.fn_call_expr.params.at(0);
7426+
IrInstSrc *type_value = ir_gen_node(irb, type_node, scope);
7427+
if (type_value == irb->codegen->invalid_inst_src)
7428+
return type_value;
7429+
7430+
AstNode *options_node = node->data.fn_call_expr.params.at(1);
7431+
IrInstSrc *options_value = ir_gen_node_extra(irb, options_node,
7432+
scope, LValNone, &result_loc_cast->base);
7433+
if (options_value == irb->codegen->invalid_inst_src)
7434+
return options_value;
7435+
7436+
IrInstSrc *casted_options_value = ir_build_implicit_cast(
7437+
irb, scope, options_node, options_value, result_loc_cast);
7438+
7439+
IrInstSrc *ir_extern = ir_build_extern(irb, scope, node, type_value, casted_options_value);
7440+
return ir_lval_wrap(irb, scope, ir_extern, lval, result_loc);
7441+
}
73797442
case BuiltinFnIdErrorReturnTrace:
73807443
{
73817444
IrInstSrc *error_return_trace = ir_build_error_return_trace_src(irb, scope, node,
@@ -19166,6 +19229,126 @@ static IrInstGen *ir_analyze_instruction_export(IrAnalyze *ira, IrInstSrcExport
1916619229
return ir_const_void(ira, &instruction->base.base);
1916719230
}
1916819231

19232+
static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node);
19233+
19234+
static IrInstGen *ir_analyze_instruction_extern(IrAnalyze *ira, IrInstSrcExtern *instruction) {
19235+
IrInstGen *type_inst = instruction->type->child;
19236+
if (type_is_invalid(type_inst->value->type))
19237+
return ira->codegen->invalid_inst_gen;
19238+
19239+
IrInstGen *options = instruction->options->child;
19240+
if (type_is_invalid(options->value->type))
19241+
return ira->codegen->invalid_inst_gen;
19242+
19243+
ZigType *options_type = options->value->type;
19244+
assert(options_type->id == ZigTypeIdStruct);
19245+
19246+
TypeStructField *name_field = find_struct_type_field(options_type, buf_create_from_str("name"));
19247+
ir_assert(name_field != nullptr, &instruction->base.base);
19248+
IrInstGen *name_inst = ir_analyze_struct_value_field_value(ira, &instruction->base.base, options, name_field);
19249+
if (type_is_invalid(name_inst->value->type))
19250+
return ira->codegen->invalid_inst_gen;
19251+
19252+
TypeStructField *linkage_field = find_struct_type_field(options_type, buf_create_from_str("linkage"));
19253+
ir_assert(linkage_field != nullptr, &instruction->base.base);
19254+
IrInstGen *linkage_inst = ir_analyze_struct_value_field_value(ira, &instruction->base.base, options, linkage_field);
19255+
if (type_is_invalid(linkage_inst->value->type))
19256+
return ira->codegen->invalid_inst_gen;
19257+
19258+
TypeStructField *is_thread_local_field = find_struct_type_field(options_type, buf_create_from_str("is_thread_local"));
19259+
ir_assert(is_thread_local_field != nullptr, &instruction->base.base);
19260+
IrInstGen *is_thread_local_inst = ir_analyze_struct_value_field_value(ira, &instruction->base.base, options, is_thread_local_field);
19261+
if (type_is_invalid(is_thread_local_inst->value->type))
19262+
return ira->codegen->invalid_inst_gen;
19263+
19264+
TypeStructField *library_name_field = find_struct_type_field(options_type, buf_create_from_str("library_name"));
19265+
ir_assert(library_name_field != nullptr, &instruction->base.base);
19266+
IrInstGen *library_name_inst = ir_analyze_struct_value_field_value(ira, &instruction->base.base, options, library_name_field);
19267+
if (type_is_invalid(library_name_inst->value->type))
19268+
return ira->codegen->invalid_inst_gen;
19269+
19270+
// The `library_name` field is optional, we have to unwrap it first
19271+
IrInstGen *non_null_check = ir_analyze_test_non_null(ira, &instruction->base.base, library_name_inst);
19272+
bool is_non_null;
19273+
if (!ir_resolve_bool(ira, non_null_check, &is_non_null))
19274+
return ira->codegen->invalid_inst_gen;
19275+
19276+
IrInstGen *library_name_val_inst = nullptr;
19277+
if (is_non_null) {
19278+
library_name_val_inst = ir_analyze_optional_value_payload_value(ira, &instruction->base.base, library_name_inst, false);
19279+
if (type_is_invalid(library_name_val_inst->value->type))
19280+
return ira->codegen->invalid_inst_gen;
19281+
}
19282+
19283+
// Resolve all the comptime values
19284+
ZigType *value_type = ir_resolve_type(ira, type_inst);
19285+
if (type_is_invalid(value_type))
19286+
return ira->codegen->invalid_inst_gen;
19287+
19288+
if (get_src_ptr_type(value_type) == nullptr) {
19289+
ir_add_error(ira, &name_inst->base,
19290+
buf_sprintf("expected (optional) pointer type or function"));
19291+
return ira->codegen->invalid_inst_gen;
19292+
}
19293+
19294+
Buf *symbol_name = ir_resolve_str(ira, name_inst);
19295+
if (!symbol_name)
19296+
return ira->codegen->invalid_inst_gen;
19297+
19298+
if (buf_len(symbol_name) == 0) {
19299+
ir_add_error(ira, &name_inst->base,
19300+
buf_sprintf("extern symbol name cannot be empty"));
19301+
return ira->codegen->invalid_inst_gen;
19302+
}
19303+
19304+
Buf *library_name = nullptr;
19305+
if (library_name_val_inst) {
19306+
library_name = ir_resolve_str(ira, library_name_val_inst);
19307+
if (!library_name)
19308+
return ira->codegen->invalid_inst_gen;
19309+
19310+
if (buf_len(library_name) == 0) {
19311+
ir_add_error(ira, &library_name_inst->base,
19312+
buf_sprintf("library name name cannot be empty"));
19313+
return ira->codegen->invalid_inst_gen;
19314+
}
19315+
19316+
add_link_lib_symbol(ira, library_name, symbol_name, instruction->base.base.source_node);
19317+
19318+
buf_destroy(library_name);
19319+
}
19320+
19321+
GlobalLinkageId global_linkage_id;
19322+
if (!ir_resolve_global_linkage(ira, linkage_inst, &global_linkage_id))
19323+
return ira->codegen->invalid_inst_gen;
19324+
19325+
bool is_thread_local;
19326+
if (!ir_resolve_bool(ira, is_thread_local_inst, &is_thread_local))
19327+
return ira->codegen->invalid_inst_gen;
19328+
19329+
ZigType *expr_type = value_type;
19330+
if (global_linkage_id == GlobalLinkageIdWeak && value_type->id != ZigTypeIdOptional)
19331+
expr_type = get_optional_type(ira->codegen, expr_type);
19332+
19333+
// Create a bogus Tld object to keep track of the extern symbol.
19334+
// XXX: Find a better way to do this (in stage2).
19335+
TldFn *tld_fn = heap::c_allocator.create<TldFn>();
19336+
tld_fn->base.id = TldIdFn;
19337+
tld_fn->base.source_node = instruction->base.base.source_node;
19338+
19339+
auto entry = ira->codegen->external_symbol_names.put_unique(symbol_name, &tld_fn->base);
19340+
if (entry) {
19341+
AstNode *other_extern_node = entry->value->source_node;
19342+
ErrorMsg *msg = ir_add_error(ira, &instruction->base.base,
19343+
buf_sprintf("extern symbol collision: '%s'", buf_ptr(symbol_name)));
19344+
add_error_note(ira->codegen, msg, other_extern_node, buf_sprintf("other symbol is here"));
19345+
return ira->codegen->invalid_inst_gen;
19346+
}
19347+
19348+
return ir_build_extern_gen(ira, &instruction->base.base, symbol_name, global_linkage_id,
19349+
is_thread_local, expr_type);
19350+
}
19351+
1916919352
static bool exec_has_err_ret_trace(CodeGen *g, IrExecutableSrc *exec) {
1917019353
ZigFn *fn_entry = exec_fn_entry(exec);
1917119354
return fn_entry != nullptr && fn_entry->calls_or_awaits_errorable_fn && g->have_err_ret_tracing;
@@ -32112,6 +32295,8 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
3211232295
return ir_analyze_instruction_tag_type(ira, (IrInstSrcTagType *)instruction);
3211332296
case IrInstSrcIdExport:
3211432297
return ir_analyze_instruction_export(ira, (IrInstSrcExport *)instruction);
32298+
case IrInstSrcIdExtern:
32299+
return ir_analyze_instruction_extern(ira, (IrInstSrcExtern *)instruction);
3211532300
case IrInstSrcIdErrorReturnTrace:
3211632301
return ir_analyze_instruction_error_return_trace(ira, (IrInstSrcErrorReturnTrace *)instruction);
3211732302
case IrInstSrcIdErrorUnion:
@@ -32347,6 +32532,7 @@ bool ir_inst_gen_has_side_effects(IrInstGen *instruction) {
3234732532
case IrInstGenIdAwait:
3234832533
case IrInstGenIdSpillBegin:
3234932534
case IrInstGenIdWasmMemoryGrow:
32535+
case IrInstGenIdExtern:
3235032536
return true;
3235132537

3235232538
case IrInstGenIdPhi:
@@ -32467,6 +32653,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {
3246732653
case IrInstSrcIdPtrType:
3246832654
case IrInstSrcIdSetAlignStack:
3246932655
case IrInstSrcIdExport:
32656+
case IrInstSrcIdExtern:
3247032657
case IrInstSrcIdSaveErrRetAddr:
3247132658
case IrInstSrcIdAddImplicitReturnType:
3247232659
case IrInstSrcIdAtomicRmw:

0 commit comments

Comments
 (0)