Skip to content

Implement @externWeak builtin #3971

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

Closed
wants to merge 1 commit into from
Closed
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
18 changes: 18 additions & 0 deletions src/all_types.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,7 @@ enum BuiltinFnId {
BuiltinFnIdFrameSize,
BuiltinFnIdAs,
BuiltinFnIdCall,
BuiltinFnIdExternWeak,
};

struct BuiltinFnEntry {
Expand Down Expand Up @@ -2660,6 +2661,8 @@ enum IrInstructionId {
IrInstructionIdSpillBegin,
IrInstructionIdSpillEnd,
IrInstructionIdVectorExtractElem,
IrInstructionIdExternWeakSrc,
IrInstructionIdExternWeakGen,
};

struct IrInstruction {
Expand Down Expand Up @@ -4046,6 +4049,21 @@ struct IrInstructionVectorExtractElem {
IrInstruction *index;
};

struct IrInstructionExternWeakSrc {
IrInstruction base;

IrInstruction *name;
IrInstruction *ptr_type;
ResultLoc *result_loc;
};

struct IrInstructionExternWeakGen {
IrInstruction base;

Buf *name;
IrInstruction *result_loc;
};

enum ResultLocId {
ResultLocIdInvalid,
ResultLocIdNone,
Expand Down
47 changes: 47 additions & 0 deletions src/codegen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6148,6 +6148,49 @@ static LLVMValueRef ir_render_vector_extract_elem(CodeGen *g, IrExecutable *exec
return LLVMBuildExtractElement(g->builder, vector, index, "");
}

static LLVMValueRef ir_render_extern_weak(CodeGen *g, IrExecutable *executable,
IrInstructionExternWeakGen *instruction)
{
ZigType *wanted_type = instruction->base.value->type;
assert(wanted_type->id == ZigTypeIdOptional);
ZigType *ptr_type = wanted_type->data.maybe.child_type;
assert(ptr_type->id == ZigTypeIdPointer);
ZigType *child_type = ptr_type->data.pointer.child_type;

const char *symbol_name = buf_ptr(instruction->name);
LLVMValueRef global_sym = LLVMGetNamedGlobal(g->module, symbol_name);
if (global_sym == nullptr) {
global_sym = LLVMAddGlobal(g->module, get_llvm_type(g, child_type), symbol_name);
LLVMSetLinkage(global_sym, LLVMExternalWeakLinkage);
LLVMSetGlobalConstant(global_sym, true);
} else if (LLVMGetLinkage(global_sym) != LLVMExternalWeakLinkage) {
zig_unreachable();
}

LLVMValueRef sym_ptr = LLVMBuildBitCast(g->builder, global_sym, get_llvm_type(g, ptr_type), "");

LLVMValueRef result_loc = instruction->result_loc ? ir_llvm_value(g, instruction->result_loc) : nullptr;

if (!handle_is_ptr(wanted_type)) {
if (result_loc != nullptr) {
gen_store_untyped(g, sym_ptr, result_loc, 0, false);
}
return sym_ptr;
}

if (result_loc != nullptr) {
LLVMValueRef usize_zero = LLVMConstNull(g->builtin_types.entry_usize->llvm_type);
LLVMValueRef nonnull_bit = LLVMBuildICmp(g->builder, LLVMIntNE, sym_ptr, usize_zero, "");

LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_child_index, "");
gen_assign_raw(g, val_ptr, ptr_type, sym_ptr);
LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_null_index, "");
gen_store_untyped(g, nonnull_bit, maybe_ptr, 0, false);
}

return result_loc;
}

static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
AstNode *source_node = instruction->source_node;
Scope *scope = instruction->scope;
Expand Down Expand Up @@ -6248,6 +6291,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
case IrInstructionIdSplatSrc:
case IrInstructionIdMergeErrSets:
case IrInstructionIdAsmSrc:
case IrInstructionIdExternWeakSrc:
zig_unreachable();

case IrInstructionIdDeclVarGen:
Expand Down Expand Up @@ -6416,6 +6460,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_splat(g, executable, (IrInstructionSplatGen *) instruction);
case IrInstructionIdVectorExtractElem:
return ir_render_vector_extract_elem(g, executable, (IrInstructionVectorExtractElem *) instruction);
case IrInstructionIdExternWeakGen:
return ir_render_extern_weak(g, executable, (IrInstructionExternWeakGen *) instruction);
}
zig_unreachable();
}
Expand Down Expand Up @@ -8230,6 +8276,7 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdFrameSize, "frameSize", 1);
create_builtin_fn(g, BuiltinFnIdAs, "as", 2);
create_builtin_fn(g, BuiltinFnIdCall, "call", 3);
create_builtin_fn(g, BuiltinFnIdExternWeak, "externWeak", 2);
}

static const char *bool_to_str(bool b) {
Expand Down
96 changes: 96 additions & 0 deletions src/ir.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -613,6 +613,10 @@ static void destroy_instruction(IrInstruction *inst) {
return destroy(reinterpret_cast<IrInstructionSpillEnd *>(inst), name);
case IrInstructionIdVectorExtractElem:
return destroy(reinterpret_cast<IrInstructionVectorExtractElem *>(inst), name);
case IrInstructionIdExternWeakSrc:
return destroy(reinterpret_cast<IrInstructionExternWeakSrc *>(inst), name);
case IrInstructionIdExternWeakGen:
return destroy(reinterpret_cast<IrInstructionExternWeakGen *>(inst), name);
}
zig_unreachable();
}
Expand Down Expand Up @@ -891,6 +895,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVarSrc *) {
return IrInstructionIdDeclVarSrc;
}

static constexpr IrInstructionId ir_instruction_id(IrInstructionExternWeakSrc *) {
return IrInstructionIdExternWeakSrc;
}

static constexpr IrInstructionId ir_instruction_id(IrInstructionExternWeakGen *) {
return IrInstructionIdExternWeakGen;
}

static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVarGen *) {
return IrInstructionIdDeclVarGen;
}
Expand Down Expand Up @@ -1603,6 +1615,32 @@ static T *ir_build_instruction(IrBuilder *irb, Scope *scope, AstNode *source_nod
return special_instruction;
}

static IrInstruction *ir_build_extern_weak_src(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name,
IrInstruction *ptr_type, ResultLoc *result_loc)
{
IrInstructionExternWeakSrc *instruction = ir_build_instruction<IrInstructionExternWeakSrc>(irb, scope, source_node);
instruction->name = name;
instruction->ptr_type = ptr_type;
instruction->result_loc = result_loc;

ir_ref_instruction(name, irb->current_basic_block);
ir_ref_instruction(ptr_type, irb->current_basic_block);

return &instruction->base;
}

static IrInstruction *ir_build_extern_weak_gen(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *name,
IrInstruction *result_loc)
{
IrInstructionExternWeakGen *instruction = ir_build_instruction<IrInstructionExternWeakGen>(irb, scope, source_node);
instruction->name = name;
instruction->result_loc = result_loc;

ir_ref_instruction(result_loc, irb->current_basic_block);

return &instruction->base;
}

static IrInstruction *ir_build_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigType *dest_type,
IrInstruction *value, CastOp cast_op)
{
Expand Down Expand Up @@ -6461,6 +6499,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
IrInstruction *has_decl = ir_build_has_decl(irb, scope, node, arg0_value, arg1_value);
return ir_lval_wrap(irb, scope, has_decl, lval, result_loc);
}
case BuiltinFnIdExternWeak:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
if (arg0_value == irb->codegen->invalid_instruction)
return arg0_value;

AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;

IrInstruction *extern_weak = ir_build_extern_weak_src(irb, scope, node, arg0_value, arg1_value, result_loc);
return ir_lval_wrap(irb, scope, extern_weak, lval, result_loc);
}
case BuiltinFnIdUnionInit:
{
AstNode *union_type_node = node->data.fn_call_expr.params.at(0);
Expand Down Expand Up @@ -26692,6 +26745,44 @@ static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruct
instruction->safety_check_on);
}

static IrInstruction *ir_analyze_instruction_extern_weak_src(IrAnalyze *ira, IrInstructionExternWeakSrc *instruction) {
IrInstruction *name_value = instruction->name->child;
Buf *symbol_name = ir_resolve_str(ira, name_value);
if (symbol_name == nullptr)
return ira->codegen->invalid_instruction;

if (buf_len(symbol_name) == 0) {
ir_add_error_node(ira, instruction->name->source_node,
buf_sprintf("symbol name cannot be empty"));
return ira->codegen->invalid_instruction;
}

IrInstruction *ptr_type_value = instruction->ptr_type->child;
ZigType *ptr_type = ir_resolve_type(ira, ptr_type_value);
if (type_is_invalid(ptr_type))
return ira->codegen->invalid_instruction;

if (ptr_type->id != ZigTypeIdPointer) {
ir_add_error_node(ira, instruction->ptr_type->source_node,
buf_sprintf("a pointer type is required, got '%s'", buf_ptr(&ptr_type->name)));
return ira->codegen->invalid_instruction;
}

ZigType *opt_ptr_type = get_optional_type(ira->codegen, ptr_type);

IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
opt_ptr_type, nullptr, true, false, true);
if (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc)) {
return result_loc;
}

IrInstruction *result = ir_build_extern_weak_gen(&ira->new_irb, instruction->base.scope,
instruction->base.source_node, symbol_name, result_loc);
result->value->type = opt_ptr_type;

return result;
}

static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ZigValue *val, size_t len) {
size_t buf_i = 0;
// TODO optimize the buf case
Expand Down Expand Up @@ -28397,6 +28488,7 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction
case IrInstructionIdVectorExtractElem:
case IrInstructionIdVectorStoreElem:
case IrInstructionIdAsmGen:
case IrInstructionIdExternWeakGen:
zig_unreachable();

case IrInstructionIdReturn:
Expand Down Expand Up @@ -28583,6 +28675,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction
return ir_analyze_instruction_panic(ira, (IrInstructionPanic *)instruction);
case IrInstructionIdPtrCastSrc:
return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCastSrc *)instruction);
case IrInstructionIdExternWeakSrc:
return ir_analyze_instruction_extern_weak_src(ira, (IrInstructionExternWeakSrc *)instruction);
case IrInstructionIdIntToPtr:
return ir_analyze_instruction_int_to_ptr(ira, (IrInstructionIntToPtr *)instruction);
case IrInstructionIdPtrToInt:
Expand Down Expand Up @@ -28875,6 +28969,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdAwaitSrc:
case IrInstructionIdAwaitGen:
case IrInstructionIdSpillBegin:
case IrInstructionIdExternWeakSrc:
case IrInstructionIdExternWeakGen:
return true;

case IrInstructionIdPhi:
Expand Down
26 changes: 26 additions & 0 deletions src/ir_print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,10 @@ const char* ir_instruction_type_str(IrInstructionId id) {
return "SpillEnd";
case IrInstructionIdVectorExtractElem:
return "VectorExtractElem";
case IrInstructionIdExternWeakSrc:
return "ExternWeakSrc";
case IrInstructionIdExternWeakGen:
return "ExternWeakGen";
}
zig_unreachable();
}
Expand Down Expand Up @@ -2100,6 +2104,22 @@ static void ir_print_await_gen(IrPrint *irp, IrInstructionAwaitGen *instruction)
fprintf(irp->f, ")");
}

static void ir_print_extern_weak_src(IrPrint *irp, IrInstructionExternWeakSrc *instruction) {
fprintf(irp->f, "@externWeak(");
ir_print_other_instruction(irp, instruction->name);
fprintf(irp->f, ",");
ir_print_other_instruction(irp, instruction->ptr_type);
fprintf(irp->f, ",");
ir_print_result_loc(irp, instruction->result_loc);
fprintf(irp->f, ")");
}

static void ir_print_extern_weak_gen(IrPrint *irp, IrInstructionExternWeakGen *instruction) {
fprintf(irp->f, "@externWeak(\"%s\",", buf_ptr(instruction->name));
ir_print_other_instruction(irp, instruction->result_loc);
fprintf(irp->f, ")");
}

static void ir_print_spill_begin(IrPrint *irp, IrInstructionSpillBegin *instruction) {
fprintf(irp->f, "@spillBegin(");
ir_print_other_instruction(irp, instruction->operand);
Expand Down Expand Up @@ -2632,6 +2652,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool
case IrInstructionIdVectorExtractElem:
ir_print_vector_extract_elem(irp, (IrInstructionVectorExtractElem *)instruction);
break;
case IrInstructionIdExternWeakSrc:
ir_print_extern_weak_src(irp, (IrInstructionExternWeakSrc *)instruction);
break;
case IrInstructionIdExternWeakGen:
ir_print_extern_weak_gen(irp, (IrInstructionExternWeakGen *)instruction);
break;
}
fprintf(irp->f, "\n");
}
Expand Down
16 changes: 16 additions & 0 deletions test/compile_errors.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,22 @@ const tests = @import("tests.zig");
const builtin = @import("builtin");

pub fn addCases(cases: *tests.CompileErrorContext) void {
cases.add("externWeak with empty symbol name",
\\pub fn main() void {
\\ const p = @externWeak("", *u8);
\\}
, &[_][]const u8{
"tmp.zig:2:27: error: symbol name cannot be empty",
});

cases.add("externWeak with non-pointer type",
\\pub fn main() void {
\\ const p = @externWeak("foo", u8);
\\}
, &[_][]const u8{
"tmp.zig:2:34: error: a pointer type is required, got 'u8'",
});

cases.add("slice sentinel mismatch",
\\fn foo() [:0]u8 {
\\ var x: []u8 = undefined;
Expand Down
1 change: 1 addition & 0 deletions test/stage1/behavior.zig
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,5 @@ comptime {
_ = @import("behavior/void.zig");
_ = @import("behavior/while.zig");
_ = @import("behavior/widening.zig");
_ = @import("behavior/externweak.zig");
}
21 changes: 21 additions & 0 deletions test/stage1/behavior/externweak.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const builtin = @import("builtin");
const expect = @import("std").testing.expect;
const expectEqual = @import("std").testing.expectEqual;

test "externWeak" {
if (builtin.os != .linux) return error.SkipZigTest;

const p1 = @externWeak("__ehdr_start", *u8);
expectEqual(?*u8, @TypeOf(p1));

const p2 = @externWeak("__ehdr_start", *u8);
expect(p1 == p2);

// Different kind of optional layout
const p3 = @externWeak("__ehdr_start", *allowzero u8);
expect((p1 == null and p3 == null) or (p1.? == p3.?));

// Destination result_loc is null
_ = @externWeak("__ehdr_start", *u8);
_ = @externWeak("__ehdr_start", *allowzero u8);
}