Skip to content

Initial implementation of @extern builtin #7182

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 21, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lib/std/builtin.zig
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,15 @@ pub const ExportOptions = struct {
section: ?[]const u8 = null,
};

/// This data structure is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
pub const ExternOptions = struct {
name: []const u8,
library_name: ?[]const u8 = null,
linkage: GlobalLinkage = .Strong,
is_thread_local: bool = false,
};

/// This function type is used by the Zig language code generation and
/// therefore must be kept in sync with the compiler implementation.
pub const TestFn = struct {
Expand Down
18 changes: 18 additions & 0 deletions src/stage1/all_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1808,6 +1808,7 @@ enum BuiltinFnId {
BuiltinFnIdThis,
BuiltinFnIdSetAlignStack,
BuiltinFnIdExport,
BuiltinFnIdExtern,
BuiltinFnIdErrorReturnTrace,
BuiltinFnIdAtomicRmw,
BuiltinFnIdAtomicLoad,
Expand Down Expand Up @@ -2619,6 +2620,7 @@ enum IrInstSrcId {
IrInstSrcIdSetAlignStack,
IrInstSrcIdArgType,
IrInstSrcIdExport,
IrInstSrcIdExtern,
IrInstSrcIdErrorReturnTrace,
IrInstSrcIdErrorUnion,
IrInstSrcIdAtomicRmw,
Expand Down Expand Up @@ -2736,6 +2738,7 @@ enum IrInstGenId {
IrInstGenIdConst,
IrInstGenIdWasmMemorySize,
IrInstGenIdWasmMemoryGrow,
IrInstGenIdExtern,
};

// Common fields between IrInstSrc and IrInstGen. This allows future passes
Expand Down Expand Up @@ -4146,6 +4149,21 @@ struct IrInstSrcExport {
IrInstSrc *options;
};

struct IrInstSrcExtern {
IrInstSrc base;

IrInstSrc *type;
IrInstSrc *options;
};

struct IrInstGenExtern {
IrInstGen base;

Buf *name;
GlobalLinkageId linkage;
bool is_thread_local;
};

enum IrInstErrorReturnTraceOptional {
IrInstErrorReturnTraceNull,
IrInstErrorReturnTraceNonNull,
Expand Down
27 changes: 27 additions & 0 deletions src/stage1/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6389,6 +6389,30 @@ static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutableGen *executable, IrI
return LLVMBuildTrunc(g->builder, shifted, get_llvm_type(g, expr_type), "");
}

static LLVMValueRef ir_render_extern(CodeGen *g, IrExecutableGen *executable,
IrInstGenExtern *instruction)
{
ZigType *expr_type = instruction->base.value->type;
assert(get_src_ptr_type(expr_type));

const char *symbol_name = buf_ptr(instruction->name);
const LLVMLinkage linkage = to_llvm_linkage(instruction->linkage, true);

LLVMValueRef global_value = LLVMGetNamedGlobal(g->module, symbol_name);
if (global_value == nullptr) {
global_value = LLVMAddGlobal(g->module, get_llvm_type(g, expr_type), symbol_name);
LLVMSetLinkage(global_value, linkage);
LLVMSetGlobalConstant(global_value, true);
if (instruction->is_thread_local)
LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel);
} else if (LLVMGetLinkage(global_value) != linkage) {
// XXX: Handle this case better!
zig_panic("duplicate extern symbol");
}

return LLVMBuildBitCast(g->builder, global_value, get_llvm_type(g, expr_type), "");
}

static LLVMValueRef ir_render_bit_reverse(CodeGen *g, IrExecutableGen *executable, IrInstGenBitReverse *instruction) {
LLVMValueRef op = ir_llvm_value(g, instruction->op);
ZigType *int_type = instruction->base.value->type;
Expand Down Expand Up @@ -6902,6 +6926,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutableGen *executabl
return ir_render_wasm_memory_size(g, executable, (IrInstGenWasmMemorySize *) instruction);
case IrInstGenIdWasmMemoryGrow:
return ir_render_wasm_memory_grow(g, executable, (IrInstGenWasmMemoryGrow *) instruction);
case IrInstGenIdExtern:
return ir_render_extern(g, executable, (IrInstGenExtern *) instruction);
}
zig_unreachable();
}
Expand Down Expand Up @@ -8800,6 +8826,7 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdAlignCast, "alignCast", 2);
create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1);
create_builtin_fn(g, BuiltinFnIdExport, "export", 2);
create_builtin_fn(g, BuiltinFnIdExtern, "extern", 2);
create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0);
create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5);
create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3);
Expand Down
187 changes: 187 additions & 0 deletions src/stage1/ir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,8 @@ static void destroy_instruction_src(IrInstSrc *inst) {
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcTagType *>(inst));
case IrInstSrcIdExport:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExport *>(inst));
case IrInstSrcIdExtern:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcExtern *>(inst));
case IrInstSrcIdErrorReturnTrace:
return heap::c_allocator.destroy(reinterpret_cast<IrInstSrcErrorReturnTrace *>(inst));
case IrInstSrcIdErrorUnion:
Expand Down Expand Up @@ -755,6 +757,8 @@ void destroy_instruction_gen(IrInstGen *inst) {
return heap::c_allocator.destroy(reinterpret_cast<IrInstGenWasmMemorySize *>(inst));
case IrInstGenIdWasmMemoryGrow:
return heap::c_allocator.destroy(reinterpret_cast<IrInstGenWasmMemoryGrow *>(inst));
case IrInstGenIdExtern:
return heap::c_allocator.destroy(reinterpret_cast<IrInstGenExtern *>(inst));
}
zig_unreachable();
}
Expand Down Expand Up @@ -1555,6 +1559,10 @@ static constexpr IrInstSrcId ir_inst_id(IrInstSrcExport *) {
return IrInstSrcIdExport;
}

static constexpr IrInstSrcId ir_inst_id(IrInstSrcExtern *) {
return IrInstSrcIdExtern;
}

static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrorReturnTrace *) {
return IrInstSrcIdErrorReturnTrace;
}
Expand Down Expand Up @@ -1999,6 +2007,10 @@ static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemoryGrow *) {
return IrInstGenIdWasmMemoryGrow;
}

static constexpr IrInstGenId ir_inst_id(IrInstGenExtern *) {
return IrInstGenIdExtern;
}

template<typename T>
static T *ir_create_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) {
T *special_instruction = heap::c_allocator.create<T>();
Expand Down Expand Up @@ -2807,6 +2819,33 @@ static IrInstSrc *ir_build_export(IrBuilderSrc *irb, Scope *scope, AstNode *sour
return &export_instruction->base;
}

static IrInstSrc *ir_build_extern(IrBuilderSrc *irb, Scope *scope, AstNode *source_node,
IrInstSrc *type, IrInstSrc *options)
{
IrInstSrcExtern *extern_instruction = ir_build_instruction<IrInstSrcExtern>(
irb, scope, source_node);
extern_instruction->type = type;
extern_instruction->options = options;

ir_ref_instruction(type, irb->current_basic_block);
ir_ref_instruction(options, irb->current_basic_block);

return &extern_instruction->base;
}

static IrInstGen *ir_build_extern_gen(IrAnalyze *ira, IrInst *source_instr, Buf *name,
GlobalLinkageId linkage, bool is_thread_local, ZigType *expr_type)
{
IrInstGenExtern *instruction = ir_build_inst_gen<IrInstGenExtern>(&ira->new_irb,
source_instr->scope, source_instr->source_node);
instruction->base.value->type = expr_type;
instruction->name = name;
instruction->linkage = linkage;
instruction->is_thread_local = is_thread_local;

return &instruction->base;
}

static IrInstSrc *ir_build_load_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *ptr) {
IrInstSrcLoadPtr *instruction = ir_build_instruction<IrInstSrcLoadPtr>(irb, scope, source_node);
instruction->ptr = ptr;
Expand Down Expand Up @@ -7376,6 +7415,30 @@ static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNod
IrInstSrc *ir_export = ir_build_export(irb, scope, node, target_value, casted_options_value);
return ir_lval_wrap(irb, scope, ir_export, lval, result_loc);
}
case BuiltinFnIdExtern:
{
// Cast the options parameter to the options type
ZigType *options_type = get_builtin_type(irb->codegen, "ExternOptions");
IrInstSrc *options_type_inst = ir_build_const_type(irb, scope, node, options_type);
ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc());

AstNode *type_node = node->data.fn_call_expr.params.at(0);
IrInstSrc *type_value = ir_gen_node(irb, type_node, scope);
if (type_value == irb->codegen->invalid_inst_src)
return type_value;

AstNode *options_node = node->data.fn_call_expr.params.at(1);
IrInstSrc *options_value = ir_gen_node_extra(irb, options_node,
scope, LValNone, &result_loc_cast->base);
if (options_value == irb->codegen->invalid_inst_src)
return options_value;

IrInstSrc *casted_options_value = ir_build_implicit_cast(
irb, scope, options_node, options_value, result_loc_cast);

IrInstSrc *ir_extern = ir_build_extern(irb, scope, node, type_value, casted_options_value);
return ir_lval_wrap(irb, scope, ir_extern, lval, result_loc);
}
case BuiltinFnIdErrorReturnTrace:
{
IrInstSrc *error_return_trace = ir_build_error_return_trace_src(irb, scope, node,
Expand Down Expand Up @@ -19166,6 +19229,126 @@ static IrInstGen *ir_analyze_instruction_export(IrAnalyze *ira, IrInstSrcExport
return ir_const_void(ira, &instruction->base.base);
}

static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node);

static IrInstGen *ir_analyze_instruction_extern(IrAnalyze *ira, IrInstSrcExtern *instruction) {
IrInstGen *type_inst = instruction->type->child;
if (type_is_invalid(type_inst->value->type))
return ira->codegen->invalid_inst_gen;

IrInstGen *options = instruction->options->child;
if (type_is_invalid(options->value->type))
return ira->codegen->invalid_inst_gen;

ZigType *options_type = options->value->type;
assert(options_type->id == ZigTypeIdStruct);

TypeStructField *name_field = find_struct_type_field(options_type, buf_create_from_str("name"));
ir_assert(name_field != nullptr, &instruction->base.base);
IrInstGen *name_inst = ir_analyze_struct_value_field_value(ira, &instruction->base.base, options, name_field);
if (type_is_invalid(name_inst->value->type))
return ira->codegen->invalid_inst_gen;

TypeStructField *linkage_field = find_struct_type_field(options_type, buf_create_from_str("linkage"));
ir_assert(linkage_field != nullptr, &instruction->base.base);
IrInstGen *linkage_inst = ir_analyze_struct_value_field_value(ira, &instruction->base.base, options, linkage_field);
if (type_is_invalid(linkage_inst->value->type))
return ira->codegen->invalid_inst_gen;

TypeStructField *is_thread_local_field = find_struct_type_field(options_type, buf_create_from_str("is_thread_local"));
ir_assert(is_thread_local_field != nullptr, &instruction->base.base);
IrInstGen *is_thread_local_inst = ir_analyze_struct_value_field_value(ira, &instruction->base.base, options, is_thread_local_field);
if (type_is_invalid(is_thread_local_inst->value->type))
return ira->codegen->invalid_inst_gen;

TypeStructField *library_name_field = find_struct_type_field(options_type, buf_create_from_str("library_name"));
ir_assert(library_name_field != nullptr, &instruction->base.base);
IrInstGen *library_name_inst = ir_analyze_struct_value_field_value(ira, &instruction->base.base, options, library_name_field);
if (type_is_invalid(library_name_inst->value->type))
return ira->codegen->invalid_inst_gen;

// The `library_name` field is optional, we have to unwrap it first
IrInstGen *non_null_check = ir_analyze_test_non_null(ira, &instruction->base.base, library_name_inst);
bool is_non_null;
if (!ir_resolve_bool(ira, non_null_check, &is_non_null))
return ira->codegen->invalid_inst_gen;

IrInstGen *library_name_val_inst = nullptr;
if (is_non_null) {
library_name_val_inst = ir_analyze_optional_value_payload_value(ira, &instruction->base.base, library_name_inst, false);
if (type_is_invalid(library_name_val_inst->value->type))
return ira->codegen->invalid_inst_gen;
}

// Resolve all the comptime values
ZigType *value_type = ir_resolve_type(ira, type_inst);
if (type_is_invalid(value_type))
return ira->codegen->invalid_inst_gen;

if (get_src_ptr_type(value_type) == nullptr) {
ir_add_error(ira, &name_inst->base,
buf_sprintf("expected (optional) pointer type or function"));
return ira->codegen->invalid_inst_gen;
}

Buf *symbol_name = ir_resolve_str(ira, name_inst);
if (!symbol_name)
return ira->codegen->invalid_inst_gen;

if (buf_len(symbol_name) == 0) {
ir_add_error(ira, &name_inst->base,
buf_sprintf("extern symbol name cannot be empty"));
return ira->codegen->invalid_inst_gen;
}

Buf *library_name = nullptr;
if (library_name_val_inst) {
library_name = ir_resolve_str(ira, library_name_val_inst);
if (!library_name)
return ira->codegen->invalid_inst_gen;

if (buf_len(library_name) == 0) {
ir_add_error(ira, &library_name_inst->base,
buf_sprintf("library name name cannot be empty"));
return ira->codegen->invalid_inst_gen;
}

add_link_lib_symbol(ira, library_name, symbol_name, instruction->base.base.source_node);

buf_destroy(library_name);
}

GlobalLinkageId global_linkage_id;
if (!ir_resolve_global_linkage(ira, linkage_inst, &global_linkage_id))
return ira->codegen->invalid_inst_gen;

bool is_thread_local;
if (!ir_resolve_bool(ira, is_thread_local_inst, &is_thread_local))
return ira->codegen->invalid_inst_gen;

ZigType *expr_type = value_type;
if (global_linkage_id == GlobalLinkageIdWeak && value_type->id != ZigTypeIdOptional)
expr_type = get_optional_type(ira->codegen, expr_type);

// Create a bogus Tld object to keep track of the extern symbol.
// XXX: Find a better way to do this (in stage2).
TldFn *tld_fn = heap::c_allocator.create<TldFn>();
tld_fn->base.id = TldIdFn;
tld_fn->base.source_node = instruction->base.base.source_node;

auto entry = ira->codegen->external_symbol_names.put_unique(symbol_name, &tld_fn->base);
if (entry) {
AstNode *other_extern_node = entry->value->source_node;
ErrorMsg *msg = ir_add_error(ira, &instruction->base.base,
buf_sprintf("extern symbol collision: '%s'", buf_ptr(symbol_name)));
add_error_note(ira->codegen, msg, other_extern_node, buf_sprintf("other symbol is here"));
return ira->codegen->invalid_inst_gen;
}

return ir_build_extern_gen(ira, &instruction->base.base, symbol_name, global_linkage_id,
is_thread_local, expr_type);
}

static bool exec_has_err_ret_trace(CodeGen *g, IrExecutableSrc *exec) {
ZigFn *fn_entry = exec_fn_entry(exec);
return fn_entry != nullptr && fn_entry->calls_or_awaits_errorable_fn && g->have_err_ret_tracing;
Expand Down Expand Up @@ -32110,6 +32293,8 @@ static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruc
return ir_analyze_instruction_tag_type(ira, (IrInstSrcTagType *)instruction);
case IrInstSrcIdExport:
return ir_analyze_instruction_export(ira, (IrInstSrcExport *)instruction);
case IrInstSrcIdExtern:
return ir_analyze_instruction_extern(ira, (IrInstSrcExtern *)instruction);
case IrInstSrcIdErrorReturnTrace:
return ir_analyze_instruction_error_return_trace(ira, (IrInstSrcErrorReturnTrace *)instruction);
case IrInstSrcIdErrorUnion:
Expand Down Expand Up @@ -32345,6 +32530,7 @@ bool ir_inst_gen_has_side_effects(IrInstGen *instruction) {
case IrInstGenIdAwait:
case IrInstGenIdSpillBegin:
case IrInstGenIdWasmMemoryGrow:
case IrInstGenIdExtern:
return true;

case IrInstGenIdPhi:
Expand Down Expand Up @@ -32465,6 +32651,7 @@ bool ir_inst_src_has_side_effects(IrInstSrc *instruction) {
case IrInstSrcIdPtrType:
case IrInstSrcIdSetAlignStack:
case IrInstSrcIdExport:
case IrInstSrcIdExtern:
case IrInstSrcIdSaveErrRetAddr:
case IrInstSrcIdAddImplicitReturnType:
case IrInstSrcIdAtomicRmw:
Expand Down
Loading