Skip to content

Commit 6a0e70b

Browse files
committed
Implement @externWeak builtin
Closes ziglang#1917
1 parent bc95c63 commit 6a0e70b

File tree

7 files changed

+225
-0
lines changed

7 files changed

+225
-0
lines changed

src/all_types.hpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1744,6 +1744,7 @@ enum BuiltinFnId {
17441744
BuiltinFnIdFrameSize,
17451745
BuiltinFnIdAs,
17461746
BuiltinFnIdCall,
1747+
BuiltinFnIdExternWeak,
17471748
};
17481749

17491750
struct BuiltinFnEntry {
@@ -2660,6 +2661,8 @@ enum IrInstructionId {
26602661
IrInstructionIdSpillBegin,
26612662
IrInstructionIdSpillEnd,
26622663
IrInstructionIdVectorExtractElem,
2664+
IrInstructionIdExternWeakSrc,
2665+
IrInstructionIdExternWeakGen,
26632666
};
26642667

26652668
struct IrInstruction {
@@ -4046,6 +4049,21 @@ struct IrInstructionVectorExtractElem {
40464049
IrInstruction *index;
40474050
};
40484051

4052+
struct IrInstructionExternWeakSrc {
4053+
IrInstruction base;
4054+
4055+
IrInstruction *name;
4056+
IrInstruction *ptr_type;
4057+
ResultLoc *result_loc;
4058+
};
4059+
4060+
struct IrInstructionExternWeakGen {
4061+
IrInstruction base;
4062+
4063+
Buf *name;
4064+
IrInstruction *result_loc;
4065+
};
4066+
40494067
enum ResultLocId {
40504068
ResultLocIdInvalid,
40514069
ResultLocIdNone,

src/codegen.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6148,6 +6148,49 @@ static LLVMValueRef ir_render_vector_extract_elem(CodeGen *g, IrExecutable *exec
61486148
return LLVMBuildExtractElement(g->builder, vector, index, "");
61496149
}
61506150

6151+
static LLVMValueRef ir_render_extern_weak(CodeGen *g, IrExecutable *executable,
6152+
IrInstructionExternWeakGen *instruction)
6153+
{
6154+
ZigType *wanted_type = instruction->base.value->type;
6155+
assert(wanted_type->id == ZigTypeIdOptional);
6156+
ZigType *ptr_type = wanted_type->data.maybe.child_type;
6157+
assert(ptr_type->id == ZigTypeIdPointer);
6158+
ZigType *child_type = ptr_type->data.pointer.child_type;
6159+
6160+
const char *symbol_name = buf_ptr(instruction->name);
6161+
LLVMValueRef global_sym = LLVMGetNamedGlobal(g->module, symbol_name);
6162+
if (global_sym == nullptr) {
6163+
global_sym = LLVMAddGlobal(g->module, get_llvm_type(g, child_type), symbol_name);
6164+
LLVMSetLinkage(global_sym, LLVMExternalWeakLinkage);
6165+
LLVMSetGlobalConstant(global_sym, true);
6166+
} else if (LLVMGetLinkage(global_sym) != LLVMExternalWeakLinkage) {
6167+
zig_unreachable();
6168+
}
6169+
6170+
LLVMValueRef sym_ptr = LLVMBuildBitCast(g->builder, global_sym, get_llvm_type(g, ptr_type), "");
6171+
6172+
LLVMValueRef result_loc = instruction->result_loc ? ir_llvm_value(g, instruction->result_loc) : nullptr;
6173+
6174+
if (!handle_is_ptr(wanted_type)) {
6175+
if (result_loc != nullptr) {
6176+
gen_store_untyped(g, sym_ptr, result_loc, 0, false);
6177+
}
6178+
return sym_ptr;
6179+
}
6180+
6181+
if (result_loc != nullptr) {
6182+
LLVMValueRef usize_zero = LLVMConstNull(g->builtin_types.entry_usize->llvm_type);
6183+
LLVMValueRef nonnull_bit = LLVMBuildICmp(g->builder, LLVMIntNE, sym_ptr, usize_zero, "");
6184+
6185+
LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_child_index, "");
6186+
gen_assign_raw(g, val_ptr, ptr_type, sym_ptr);
6187+
LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_null_index, "");
6188+
gen_store_untyped(g, nonnull_bit, maybe_ptr, 0, false);
6189+
}
6190+
6191+
return result_loc;
6192+
}
6193+
61516194
static void set_debug_location(CodeGen *g, IrInstruction *instruction) {
61526195
AstNode *source_node = instruction->source_node;
61536196
Scope *scope = instruction->scope;
@@ -6248,6 +6291,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
62486291
case IrInstructionIdSplatSrc:
62496292
case IrInstructionIdMergeErrSets:
62506293
case IrInstructionIdAsmSrc:
6294+
case IrInstructionIdExternWeakSrc:
62516295
zig_unreachable();
62526296

62536297
case IrInstructionIdDeclVarGen:
@@ -6416,6 +6460,8 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
64166460
return ir_render_splat(g, executable, (IrInstructionSplatGen *) instruction);
64176461
case IrInstructionIdVectorExtractElem:
64186462
return ir_render_vector_extract_elem(g, executable, (IrInstructionVectorExtractElem *) instruction);
6463+
case IrInstructionIdExternWeakGen:
6464+
return ir_render_extern_weak(g, executable, (IrInstructionExternWeakGen *) instruction);
64196465
}
64206466
zig_unreachable();
64216467
}
@@ -8230,6 +8276,7 @@ static void define_builtin_fns(CodeGen *g) {
82308276
create_builtin_fn(g, BuiltinFnIdFrameSize, "frameSize", 1);
82318277
create_builtin_fn(g, BuiltinFnIdAs, "as", 2);
82328278
create_builtin_fn(g, BuiltinFnIdCall, "call", 3);
8279+
create_builtin_fn(g, BuiltinFnIdExternWeak, "externWeak", 2);
82338280
}
82348281

82358282
static const char *bool_to_str(bool b) {

src/ir.cpp

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,10 @@ static void destroy_instruction(IrInstruction *inst) {
613613
return destroy(reinterpret_cast<IrInstructionSpillEnd *>(inst), name);
614614
case IrInstructionIdVectorExtractElem:
615615
return destroy(reinterpret_cast<IrInstructionVectorExtractElem *>(inst), name);
616+
case IrInstructionIdExternWeakSrc:
617+
return destroy(reinterpret_cast<IrInstructionExternWeakSrc *>(inst), name);
618+
case IrInstructionIdExternWeakGen:
619+
return destroy(reinterpret_cast<IrInstructionExternWeakGen *>(inst), name);
616620
}
617621
zig_unreachable();
618622
}
@@ -891,6 +895,14 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVarSrc *) {
891895
return IrInstructionIdDeclVarSrc;
892896
}
893897

898+
static constexpr IrInstructionId ir_instruction_id(IrInstructionExternWeakSrc *) {
899+
return IrInstructionIdExternWeakSrc;
900+
}
901+
902+
static constexpr IrInstructionId ir_instruction_id(IrInstructionExternWeakGen *) {
903+
return IrInstructionIdExternWeakGen;
904+
}
905+
894906
static constexpr IrInstructionId ir_instruction_id(IrInstructionDeclVarGen *) {
895907
return IrInstructionIdDeclVarGen;
896908
}
@@ -1603,6 +1615,32 @@ static T *ir_build_instruction(IrBuilder *irb, Scope *scope, AstNode *source_nod
16031615
return special_instruction;
16041616
}
16051617

1618+
static IrInstruction *ir_build_extern_weak_src(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *name,
1619+
IrInstruction *ptr_type, ResultLoc *result_loc)
1620+
{
1621+
IrInstructionExternWeakSrc *instruction = ir_build_instruction<IrInstructionExternWeakSrc>(irb, scope, source_node);
1622+
instruction->name = name;
1623+
instruction->ptr_type = ptr_type;
1624+
instruction->result_loc = result_loc;
1625+
1626+
ir_ref_instruction(name, irb->current_basic_block);
1627+
ir_ref_instruction(ptr_type, irb->current_basic_block);
1628+
1629+
return &instruction->base;
1630+
}
1631+
1632+
static IrInstruction *ir_build_extern_weak_gen(IrBuilder *irb, Scope *scope, AstNode *source_node, Buf *name,
1633+
IrInstruction *result_loc)
1634+
{
1635+
IrInstructionExternWeakGen *instruction = ir_build_instruction<IrInstructionExternWeakGen>(irb, scope, source_node);
1636+
instruction->name = name;
1637+
instruction->result_loc = result_loc;
1638+
1639+
ir_ref_instruction(result_loc, irb->current_basic_block);
1640+
1641+
return &instruction->base;
1642+
}
1643+
16061644
static IrInstruction *ir_build_cast(IrBuilder *irb, Scope *scope, AstNode *source_node, ZigType *dest_type,
16071645
IrInstruction *value, CastOp cast_op)
16081646
{
@@ -6461,6 +6499,21 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
64616499
IrInstruction *has_decl = ir_build_has_decl(irb, scope, node, arg0_value, arg1_value);
64626500
return ir_lval_wrap(irb, scope, has_decl, lval, result_loc);
64636501
}
6502+
case BuiltinFnIdExternWeak:
6503+
{
6504+
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
6505+
IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
6506+
if (arg0_value == irb->codegen->invalid_instruction)
6507+
return arg0_value;
6508+
6509+
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
6510+
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
6511+
if (arg1_value == irb->codegen->invalid_instruction)
6512+
return arg1_value;
6513+
6514+
IrInstruction *extern_weak = ir_build_extern_weak_src(irb, scope, node, arg0_value, arg1_value, result_loc);
6515+
return ir_lval_wrap(irb, scope, extern_weak, lval, result_loc);
6516+
}
64646517
case BuiltinFnIdUnionInit:
64656518
{
64666519
AstNode *union_type_node = node->data.fn_call_expr.params.at(0);
@@ -26692,6 +26745,44 @@ static IrInstruction *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruct
2669226745
instruction->safety_check_on);
2669326746
}
2669426747

26748+
static IrInstruction *ir_analyze_instruction_extern_weak_src(IrAnalyze *ira, IrInstructionExternWeakSrc *instruction) {
26749+
IrInstruction *name_value = instruction->name->child;
26750+
Buf *symbol_name = ir_resolve_str(ira, name_value);
26751+
if (symbol_name == nullptr)
26752+
return ira->codegen->invalid_instruction;
26753+
26754+
if (buf_len(symbol_name) == 0) {
26755+
ir_add_error_node(ira, instruction->name->source_node,
26756+
buf_sprintf("symbol name cannot be empty"));
26757+
return ira->codegen->invalid_instruction;
26758+
}
26759+
26760+
IrInstruction *ptr_type_value = instruction->ptr_type->child;
26761+
ZigType *ptr_type = ir_resolve_type(ira, ptr_type_value);
26762+
if (type_is_invalid(ptr_type))
26763+
return ira->codegen->invalid_instruction;
26764+
26765+
if (ptr_type->id != ZigTypeIdPointer) {
26766+
ir_add_error_node(ira, instruction->ptr_type->source_node,
26767+
buf_sprintf("a pointer type is required, got '%s'", buf_ptr(&ptr_type->name)));
26768+
return ira->codegen->invalid_instruction;
26769+
}
26770+
26771+
ZigType *opt_ptr_type = get_optional_type(ira->codegen, ptr_type);
26772+
26773+
IrInstruction *result_loc = ir_resolve_result(ira, &instruction->base, instruction->result_loc,
26774+
opt_ptr_type, nullptr, true, false, true);
26775+
if (type_is_invalid(result_loc->value->type) || instr_is_unreachable(result_loc)) {
26776+
return result_loc;
26777+
}
26778+
26779+
IrInstruction *result = ir_build_extern_weak_gen(&ira->new_irb, instruction->base.scope,
26780+
instruction->base.source_node, symbol_name, result_loc);
26781+
result->value->type = opt_ptr_type;
26782+
26783+
return result;
26784+
}
26785+
2669526786
static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ZigValue *val, size_t len) {
2669626787
size_t buf_i = 0;
2669726788
// TODO optimize the buf case
@@ -28397,6 +28488,7 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction
2839728488
case IrInstructionIdVectorExtractElem:
2839828489
case IrInstructionIdVectorStoreElem:
2839928490
case IrInstructionIdAsmGen:
28491+
case IrInstructionIdExternWeakGen:
2840028492
zig_unreachable();
2840128493

2840228494
case IrInstructionIdReturn:
@@ -28583,6 +28675,8 @@ static IrInstruction *ir_analyze_instruction_base(IrAnalyze *ira, IrInstruction
2858328675
return ir_analyze_instruction_panic(ira, (IrInstructionPanic *)instruction);
2858428676
case IrInstructionIdPtrCastSrc:
2858528677
return ir_analyze_instruction_ptr_cast(ira, (IrInstructionPtrCastSrc *)instruction);
28678+
case IrInstructionIdExternWeakSrc:
28679+
return ir_analyze_instruction_extern_weak_src(ira, (IrInstructionExternWeakSrc *)instruction);
2858628680
case IrInstructionIdIntToPtr:
2858728681
return ir_analyze_instruction_int_to_ptr(ira, (IrInstructionIntToPtr *)instruction);
2858828682
case IrInstructionIdPtrToInt:
@@ -28875,6 +28969,8 @@ bool ir_has_side_effects(IrInstruction *instruction) {
2887528969
case IrInstructionIdAwaitSrc:
2887628970
case IrInstructionIdAwaitGen:
2887728971
case IrInstructionIdSpillBegin:
28972+
case IrInstructionIdExternWeakSrc:
28973+
case IrInstructionIdExternWeakGen:
2887828974
return true;
2887928975

2888028976
case IrInstructionIdPhi:

src/ir_print.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -380,6 +380,10 @@ const char* ir_instruction_type_str(IrInstructionId id) {
380380
return "SpillEnd";
381381
case IrInstructionIdVectorExtractElem:
382382
return "VectorExtractElem";
383+
case IrInstructionIdExternWeakSrc:
384+
return "ExternWeakSrc";
385+
case IrInstructionIdExternWeakGen:
386+
return "ExternWeakGen";
383387
}
384388
zig_unreachable();
385389
}
@@ -2100,6 +2104,22 @@ static void ir_print_await_gen(IrPrint *irp, IrInstructionAwaitGen *instruction)
21002104
fprintf(irp->f, ")");
21012105
}
21022106

2107+
static void ir_print_extern_weak_src(IrPrint *irp, IrInstructionExternWeakSrc *instruction) {
2108+
fprintf(irp->f, "@externWeak(");
2109+
ir_print_other_instruction(irp, instruction->name);
2110+
fprintf(irp->f, ",");
2111+
ir_print_other_instruction(irp, instruction->ptr_type);
2112+
fprintf(irp->f, ",");
2113+
ir_print_result_loc(irp, instruction->result_loc);
2114+
fprintf(irp->f, ")");
2115+
}
2116+
2117+
static void ir_print_extern_weak_gen(IrPrint *irp, IrInstructionExternWeakGen *instruction) {
2118+
fprintf(irp->f, "@externWeak(\"%s\",", buf_ptr(instruction->name));
2119+
ir_print_other_instruction(irp, instruction->result_loc);
2120+
fprintf(irp->f, ")");
2121+
}
2122+
21032123
static void ir_print_spill_begin(IrPrint *irp, IrInstructionSpillBegin *instruction) {
21042124
fprintf(irp->f, "@spillBegin(");
21052125
ir_print_other_instruction(irp, instruction->operand);
@@ -2632,6 +2652,12 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction, bool
26322652
case IrInstructionIdVectorExtractElem:
26332653
ir_print_vector_extract_elem(irp, (IrInstructionVectorExtractElem *)instruction);
26342654
break;
2655+
case IrInstructionIdExternWeakSrc:
2656+
ir_print_extern_weak_src(irp, (IrInstructionExternWeakSrc *)instruction);
2657+
break;
2658+
case IrInstructionIdExternWeakGen:
2659+
ir_print_extern_weak_gen(irp, (IrInstructionExternWeakGen *)instruction);
2660+
break;
26352661
}
26362662
fprintf(irp->f, "\n");
26372663
}

test/compile_errors.zig

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,22 @@ const tests = @import("tests.zig");
22
const builtin = @import("builtin");
33

44
pub fn addCases(cases: *tests.CompileErrorContext) void {
5+
cases.add("externWeak with empty symbol name",
6+
\\pub fn main() void {
7+
\\ const p = @externWeak("", *u8);
8+
\\}
9+
, &[_][]const u8{
10+
"tmp.zig:2:27: error: symbol name cannot be empty",
11+
});
12+
13+
cases.add("externWeak with non-pointer type",
14+
\\pub fn main() void {
15+
\\ const p = @externWeak("foo", u8);
16+
\\}
17+
, &[_][]const u8{
18+
"tmp.zig:2:34: error: a pointer type is required, got 'u8'",
19+
});
20+
521
cases.add("slice sentinel mismatch",
622
\\fn foo() [:0]u8 {
723
\\ var x: []u8 = undefined;

test/stage1/behavior.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,4 +116,5 @@ comptime {
116116
_ = @import("behavior/void.zig");
117117
_ = @import("behavior/while.zig");
118118
_ = @import("behavior/widening.zig");
119+
_ = @import("behavior/externweak.zig");
119120
}

test/stage1/behavior/externweak.zig

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const builtin = @import("builtin");
2+
const expect = @import("std").testing.expect;
3+
const expectEqual = @import("std").testing.expectEqual;
4+
5+
test "externWeak" {
6+
if (builtin.os != .linux) return error.SkipZigTest;
7+
8+
const p1 = @externWeak("__ehdr_start", *u8);
9+
expectEqual(?*u8, @TypeOf(p1));
10+
11+
const p2 = @externWeak("__ehdr_start", *u8);
12+
expect(p1 == p2);
13+
14+
// Different kind of optional layout
15+
const p3 = @externWeak("__ehdr_start", *allowzero u8);
16+
expect((p1 == null and p3 == null) or (p1.? == p3.?));
17+
18+
// Destination result_loc is null
19+
_ = @externWeak("__ehdr_start", *u8);
20+
_ = @externWeak("__ehdr_start", *allowzero u8);
21+
}

0 commit comments

Comments
 (0)