diff --git a/src/all_types.hpp b/src/all_types.hpp index ea46ab81a6dd..9a260df20f3a 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1744,6 +1744,7 @@ enum BuiltinFnId { BuiltinFnIdFrameSize, BuiltinFnIdAs, BuiltinFnIdCall, + BuiltinFnIdExternWeak, }; struct BuiltinFnEntry { @@ -2660,6 +2661,8 @@ enum IrInstructionId { IrInstructionIdSpillBegin, IrInstructionIdSpillEnd, IrInstructionIdVectorExtractElem, + IrInstructionIdExternWeakSrc, + IrInstructionIdExternWeakGen, }; struct IrInstruction { @@ -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, diff --git a/src/codegen.cpp b/src/codegen.cpp index 1455b4b74317..0a16a72d1941 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -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; @@ -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: @@ -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(); } @@ -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) { diff --git a/src/ir.cpp b/src/ir.cpp index d60fe9ea7037..d768a4b5d566 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -613,6 +613,10 @@ static void destroy_instruction(IrInstruction *inst) { return destroy(reinterpret_cast(inst), name); case IrInstructionIdVectorExtractElem: return destroy(reinterpret_cast(inst), name); + case IrInstructionIdExternWeakSrc: + return destroy(reinterpret_cast(inst), name); + case IrInstructionIdExternWeakGen: + return destroy(reinterpret_cast(inst), name); } zig_unreachable(); } @@ -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; } @@ -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(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(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) { @@ -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); @@ -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 @@ -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: @@ -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: @@ -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: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index 06dbe0f2b5ab..3f97d314b90d 100644 --- a/src/ir_print.cpp +++ b/src/ir_print.cpp @@ -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(); } @@ -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); @@ -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"); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 450f91b9be5d..30bbc7ea2f65 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -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; diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index ea8720d98ca6..c4c0ad063b83 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -116,4 +116,5 @@ comptime { _ = @import("behavior/void.zig"); _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); + _ = @import("behavior/externweak.zig"); } diff --git a/test/stage1/behavior/externweak.zig b/test/stage1/behavior/externweak.zig new file mode 100644 index 000000000000..e26d43bdb8a8 --- /dev/null +++ b/test/stage1/behavior/externweak.zig @@ -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); +}