From 253ecd5c11747f49575b8425a506c2fecfe26ee2 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Mon, 16 Apr 2018 03:26:10 +0300 Subject: Added ReleaseSmall mode --- src/zig_llvm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/zig_llvm.cpp') diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index b4eef13cc1..ef0e9f24a8 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -81,7 +81,7 @@ static const bool assertions_on = false; #endif bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref, - const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug) + const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug, bool is_small) { std::error_code EC; raw_fd_ostream dest(filename, EC, sys::fs::F_None); @@ -100,7 +100,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM return true; } PMBuilder->OptLevel = target_machine->getOptLevel(); - PMBuilder->SizeLevel = 0; + PMBuilder->SizeLevel = is_small ? 1 : 0; PMBuilder->DisableTailCalls = is_debug; PMBuilder->DisableUnitAtATime = is_debug; -- cgit v1.2.3 From 1c85050dad6a7e1d486606205ca2eb2cd7028ef5 Mon Sep 17 00:00:00 2001 From: Alexandros Naskos Date: Mon, 16 Apr 2018 03:54:40 +0300 Subject: Set SizeLevel to 2 in ReleaseSmall mode --- src/zig_llvm.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/zig_llvm.cpp') diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index ef0e9f24a8..a56378ab3e 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -100,7 +100,7 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM return true; } PMBuilder->OptLevel = target_machine->getOptLevel(); - PMBuilder->SizeLevel = is_small ? 1 : 0; + PMBuilder->SizeLevel = is_small ? 2 : 0; PMBuilder->DisableTailCalls = is_debug; PMBuilder->DisableUnitAtATime = is_debug; -- cgit v1.2.3 From f1f998e07124f141312289ff82e0ad8d99af1cf7 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 18 Apr 2018 12:16:42 -0400 Subject: improve cmpxchg * remove @cmpxchg, add @cmpxchgWeak and @cmpxchgStrong - See explanations in the langref. * add operand type as first parameter * return type is ?T where T is the operand type closes #461 --- doc/langref.html.in | 54 ++++++++++++++++++++++++++++--- src/all_types.hpp | 9 +++++- src/codegen.cpp | 21 ++++++++++-- src/ir.cpp | 85 ++++++++++++++++++++++++++----------------------- src/zig_llvm.cpp | 8 +++-- src/zig_llvm.h | 2 +- test/cases/atomics.zig | 16 ++++++++-- test/compile_errors.zig | 16 +++++----- 8 files changed, 148 insertions(+), 63 deletions(-) (limited to 'src/zig_llvm.cpp') diff --git a/doc/langref.html.in b/doc/langref.html.in index 5cbec218a9..a5d31aada4 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -4065,16 +4065,60 @@ comptime {

{#header_close#} - {#header_open|@cmpxchg#} -
@cmpxchg(ptr: &T, cmp: T, new: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> bool
+ {#header_open|@cmpxchgStrong#} +
@cmpxchgStrong(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> ?T

- This function performs an atomic compare exchange operation. + This function performs a strong atomic compare exchange operation. It's the equivalent of this code, + except atomic: +

+ {#code_begin|syntax#} +fn cmpxchgStrongButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T { + const old_value = *ptr; + if (old_value == expected_value) { + *ptr = new_value; + return null; + } else { + return old_value; + } +} + {#code_end#} +

+ If you are using cmpxchg in a loop, {#link|@cmpxchgWeak#} is the better choice, because it can be implemented + more efficiently in machine instructions.

AtomicOrder can be found with @import("builtin").AtomicOrder.

@typeOf(ptr).alignment must be >= @sizeOf(T).

- {#see_also|Compile Variables#} + {#see_also|Compile Variables|cmpxchgWeak#} + {#header_close#} + {#header_open|@cmpxchgWeak#} +
@cmpxchgWeak(comptime T: type, ptr: &T, expected_value: T, new_value: T, success_order: AtomicOrder, fail_order: AtomicOrder) -> ?T
+

+ This function performs a weak atomic compare exchange operation. It's the equivalent of this code, + except atomic: +

+ {#code_begin|syntax#} +fn cmpxchgWeakButNotAtomic(comptime T: type, ptr: &T, expected_value: T, new_value: T) ?T { + const old_value = *ptr; + if (old_value == expected_value and usuallyTrueButSometimesFalse()) { + *ptr = new_value; + return null; + } else { + return old_value; + } +} + {#code_end#} +

+ If you are using cmpxchg in a loop, the sporadic failure will be no problem, and cmpxchgWeak + is the better choice, because it can be implemented more efficiently in machine instructions. + However if you need a stronger guarantee, use {#link|@cmpxchgStrong#}. +

+

+ AtomicOrder can be found with @import("builtin").AtomicOrder. +

+

@typeOf(ptr).alignment must be >= @sizeOf(T).

+ {#see_also|Compile Variables|cmpxchgStrong#} {#header_close#} {#header_open|@compileError#}
@compileError(comptime msg: []u8)
@@ -6020,7 +6064,7 @@ hljs.registerLanguage("zig", function(t) { a = t.IR + "\\s*\\(", c = { keyword: "const align var extern stdcallcc nakedcc volatile export pub noalias inline struct packed enum union break return try catch test continue unreachable comptime and or asm defer errdefer if else switch while for fn use bool f32 f64 void type noreturn error i8 u8 i16 u16 i32 u32 i64 u64 isize usize i8w u8w i16w i32w u32w i64w u64w isizew usizew c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong", - built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchg fence divExact truncate atomicRmw sqrt", + built_in: "atomicLoad breakpoint returnAddress frameAddress fieldParentPtr setFloatMode IntType OpaqueType compileError compileLog setCold setRuntimeSafety setEvalBranchQuota offsetOf memcpy inlineCall setGlobalLinkage setGlobalSection divTrunc divFloor enumTagName intToPtr ptrToInt panic canImplicitCast ptrCast bitCast rem mod memset sizeOf alignOf alignCast maxValue minValue memberCount memberName memberType typeOf addWithOverflow subWithOverflow mulWithOverflow shlWithOverflow shlExact shrExact cInclude cDefine cUndef ctz clz import cImport errorName embedFile cmpxchgStrong cmpxchgWeak fence divExact truncate atomicRmw sqrt", literal: "true false null undefined" }, n = [e, t.CLCM, t.CBCM, s, r]; diff --git a/src/all_types.hpp b/src/all_types.hpp index 7ef7c10393..5a3590dd4d 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1310,7 +1310,8 @@ enum BuiltinFnId { BuiltinFnIdReturnAddress, BuiltinFnIdFrameAddress, BuiltinFnIdEmbedFile, - BuiltinFnIdCmpExchange, + BuiltinFnIdCmpxchgWeak, + BuiltinFnIdCmpxchgStrong, BuiltinFnIdFence, BuiltinFnIdDivExact, BuiltinFnIdDivTrunc, @@ -2528,6 +2529,7 @@ struct IrInstructionEmbedFile { struct IrInstructionCmpxchg { IrInstruction base; + IrInstruction *type_value; IrInstruction *ptr; IrInstruction *cmp_value; IrInstruction *new_value; @@ -2535,8 +2537,13 @@ struct IrInstructionCmpxchg { IrInstruction *failure_order_value; // if this instruction gets to runtime then we know these values: + TypeTableEntry *type; AtomicOrder success_order; AtomicOrder failure_order; + + bool is_weak; + + LLVMValueRef tmp_ptr; }; struct IrInstructionFence { diff --git a/src/codegen.cpp b/src/codegen.cpp index 0279771be7..a7d373e9d0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3558,9 +3558,20 @@ static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutable *executable, IrIn LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order); LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val, - success_order, failure_order); + success_order, failure_order, instruction->is_weak); - return LLVMBuildExtractValue(g->builder, result_val, 1, ""); + assert(instruction->tmp_ptr != nullptr); + assert(type_has_bits(instruction->type)); + + LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); + LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, ""); + gen_assign_raw(g, val_ptr, get_pointer_to_type(g, instruction->type, false), payload_val); + + LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); + LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, ""); + LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, ""); + gen_store_untyped(g, nonnull_bit, maybe_ptr, 0, false); + return instruction->tmp_ptr; } static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutable *executable, IrInstructionFence *instruction) { @@ -5588,6 +5599,9 @@ static void do_code_gen(CodeGen *g) { } else if (instruction->id == IrInstructionIdErrWrapCode) { IrInstructionErrWrapCode *err_wrap_code_instruction = (IrInstructionErrWrapCode *)instruction; slot = &err_wrap_code_instruction->tmp_ptr; + } else if (instruction->id == IrInstructionIdCmpxchg) { + IrInstructionCmpxchg *cmpxchg_instruction = (IrInstructionCmpxchg *)instruction; + slot = &cmpxchg_instruction->tmp_ptr; } else { zig_unreachable(); } @@ -6115,7 +6129,8 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1); create_builtin_fn(g, BuiltinFnIdCanImplicitCast, "canImplicitCast", 2); create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1); - create_builtin_fn(g, BuiltinFnIdCmpExchange, "cmpxchg", 5); + create_builtin_fn(g, BuiltinFnIdCmpxchgWeak, "cmpxchgWeak", 6); + create_builtin_fn(g, BuiltinFnIdCmpxchgStrong, "cmpxchgStrong", 6); create_builtin_fn(g, BuiltinFnIdFence, "fence", 1); create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2); create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); diff --git a/src/ir.cpp b/src/ir.cpp index d43efe0190..89193a4c27 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -110,6 +110,7 @@ static IrInstruction *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_ IrInstruction *source_instr, IrInstruction *container_ptr, TypeTableEntry *container_type); static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, VariableTableEntry *var, bool is_const_ptr, bool is_volatile_ptr); +static TypeTableEntry *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstruction *op); ConstExprValue *const_ptr_pointee(CodeGen *g, ConstExprValue *const_val) { assert(const_val->type->id == TypeTableEntryIdPointer); @@ -1832,38 +1833,34 @@ static IrInstruction *ir_build_embed_file(IrBuilder *irb, Scope *scope, AstNode return &instruction->base; } -static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *ptr, - IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value, - AtomicOrder success_order, AtomicOrder failure_order) +static IrInstruction *ir_build_cmpxchg(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *type_value, + IrInstruction *ptr, IrInstruction *cmp_value, IrInstruction *new_value, + IrInstruction *success_order_value, IrInstruction *failure_order_value, + bool is_weak, + TypeTableEntry *type, AtomicOrder success_order, AtomicOrder failure_order) { IrInstructionCmpxchg *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type_value = type_value; instruction->ptr = ptr; instruction->cmp_value = cmp_value; instruction->new_value = new_value; instruction->success_order_value = success_order_value; instruction->failure_order_value = failure_order_value; + instruction->is_weak = is_weak; + instruction->type = type; instruction->success_order = success_order; instruction->failure_order = failure_order; + if (type_value != nullptr) ir_ref_instruction(type_value, irb->current_basic_block); ir_ref_instruction(ptr, irb->current_basic_block); ir_ref_instruction(cmp_value, irb->current_basic_block); ir_ref_instruction(new_value, irb->current_basic_block); - ir_ref_instruction(success_order_value, irb->current_basic_block); - ir_ref_instruction(failure_order_value, irb->current_basic_block); + if (type_value != nullptr) ir_ref_instruction(success_order_value, irb->current_basic_block); + if (type_value != nullptr) ir_ref_instruction(failure_order_value, irb->current_basic_block); return &instruction->base; } -static IrInstruction *ir_build_cmpxchg_from(IrBuilder *irb, IrInstruction *old_instruction, IrInstruction *ptr, - IrInstruction *cmp_value, IrInstruction *new_value, IrInstruction *success_order_value, IrInstruction *failure_order_value, - AtomicOrder success_order, AtomicOrder failure_order) -{ - IrInstruction *new_instruction = ir_build_cmpxchg(irb, old_instruction->scope, old_instruction->source_node, - ptr, cmp_value, new_value, success_order_value, failure_order_value, success_order, failure_order); - ir_link_new_instruction(new_instruction, old_instruction); - return new_instruction; -} - static IrInstruction *ir_build_fence(IrBuilder *irb, Scope *scope, AstNode *source_node, IrInstruction *order_value, AtomicOrder order) { IrInstructionFence *instruction = ir_build_instruction(irb, scope, source_node); instruction->order_value = order_value; @@ -3771,7 +3768,8 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo return ir_build_embed_file(irb, scope, node, arg0_value); } - case BuiltinFnIdCmpExchange: + case BuiltinFnIdCmpxchgWeak: + case BuiltinFnIdCmpxchgStrong: { AstNode *arg0_node = node->data.fn_call_expr.params.at(0); IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope); @@ -3798,9 +3796,14 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo if (arg4_value == irb->codegen->invalid_instruction) return arg4_value; + AstNode *arg5_node = node->data.fn_call_expr.params.at(5); + IrInstruction *arg5_value = ir_gen_node(irb, arg5_node, scope); + if (arg5_value == irb->codegen->invalid_instruction) + return arg5_value; + return ir_build_cmpxchg(irb, scope, node, arg0_value, arg1_value, - arg2_value, arg3_value, arg4_value, - AtomicOrderUnordered, AtomicOrderUnordered); + arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak), + nullptr, AtomicOrderUnordered, AtomicOrderUnordered); } case BuiltinFnIdFence: { @@ -15730,10 +15733,20 @@ static TypeTableEntry *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstr } static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstructionCmpxchg *instruction) { + TypeTableEntry *operand_type = ir_resolve_atomic_operand_type(ira, instruction->type_value->other); + if (type_is_invalid(operand_type)) + return ira->codegen->builtin_types.entry_invalid; + IrInstruction *ptr = instruction->ptr->other; if (type_is_invalid(ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; + // TODO let this be volatile + TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); + IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr, ptr_type); + if (type_is_invalid(casted_ptr->value.type)) + return ira->codegen->builtin_types.entry_invalid; + IrInstruction *cmp_value = instruction->cmp_value->other; if (type_is_invalid(cmp_value->value.type)) return ira->codegen->builtin_types.entry_invalid; @@ -15758,28 +15771,11 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order)) return ira->codegen->builtin_types.entry_invalid; - if (ptr->value.type->id != TypeTableEntryIdPointer) { - ir_add_error(ira, instruction->ptr, - buf_sprintf("expected pointer argument, found '%s'", buf_ptr(&ptr->value.type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - - TypeTableEntry *child_type = ptr->value.type->data.pointer.child_type; - - uint32_t align_bytes = ptr->value.type->data.pointer.alignment; - uint64_t size_bytes = type_size(ira->codegen, child_type); - if (align_bytes < size_bytes) { - ir_add_error(ira, instruction->ptr, - buf_sprintf("expected pointer alignment of at least %" ZIG_PRI_u64 ", found %" PRIu32, - size_bytes, align_bytes)); - return ira->codegen->builtin_types.entry_invalid; - } - - IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, child_type); + IrInstruction *casted_cmp_value = ir_implicit_cast(ira, cmp_value, operand_type); if (type_is_invalid(casted_cmp_value->value.type)) return ira->codegen->builtin_types.entry_invalid; - IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, child_type); + IrInstruction *casted_new_value = ir_implicit_cast(ira, new_value, operand_type); if (type_is_invalid(casted_new_value->value.type)) return ira->codegen->builtin_types.entry_invalid; @@ -15804,9 +15800,17 @@ static TypeTableEntry *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstruct return ira->codegen->builtin_types.entry_invalid; } - ir_build_cmpxchg_from(&ira->new_irb, &instruction->base, ptr, casted_cmp_value, casted_new_value, - success_order_value, failure_order_value, success_order, failure_order); - return ira->codegen->builtin_types.entry_bool; + if (instr_is_comptime(casted_ptr) && instr_is_comptime(casted_cmp_value) && instr_is_comptime(casted_new_value)) { + zig_panic("TODO compile-time execution of cmpxchg"); + } + + IrInstruction *result = ir_build_cmpxchg(&ira->new_irb, instruction->base.scope, instruction->base.source_node, + nullptr, casted_ptr, casted_cmp_value, casted_new_value, nullptr, nullptr, instruction->is_weak, + operand_type, success_order, failure_order); + result->value.type = get_maybe_type(ira->codegen, operand_type); + ir_link_new_instruction(result, &instruction->base); + ir_add_alloca(ira, result, result->value.type); + return result->value.type; } static TypeTableEntry *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstructionFence *instruction) { @@ -17981,6 +17985,7 @@ static TypeTableEntry *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstr if (type_is_invalid(ptr_inst->value.type)) return ira->codegen->builtin_types.entry_invalid; + // TODO let this be volatile TypeTableEntry *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); IrInstruction *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); if (type_is_invalid(casted_ptr->value.type)) diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index a56378ab3e..5905fa8167 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -765,10 +765,12 @@ static AtomicOrdering mapFromLLVMOrdering(LLVMAtomicOrdering Ordering) { LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp, LLVMValueRef new_val, LLVMAtomicOrdering success_ordering, - LLVMAtomicOrdering failure_ordering) + LLVMAtomicOrdering failure_ordering, bool is_weak) { - return wrap(unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp), unwrap(new_val), - mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering))); + AtomicCmpXchgInst *inst = unwrap(builder)->CreateAtomicCmpXchg(unwrap(ptr), unwrap(cmp), + unwrap(new_val), mapFromLLVMOrdering(success_ordering), mapFromLLVMOrdering(failure_ordering)); + inst->setWeak(is_weak); + return wrap(inst); } LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, diff --git a/src/zig_llvm.h b/src/zig_llvm.h index 0d267b1014..d34300b8ae 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -66,7 +66,7 @@ ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LL ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp, LLVMValueRef new_val, LLVMAtomicOrdering success_ordering, - LLVMAtomicOrdering failure_ordering); + LLVMAtomicOrdering failure_ordering, bool is_weak); ZIG_EXTERN_C LLVMValueRef ZigLLVMBuildNSWShl(LLVMBuilderRef builder, LLVMValueRef LHS, LLVMValueRef RHS, const char *name); diff --git a/test/cases/atomics.zig b/test/cases/atomics.zig index 323906e4a4..4cadabb728 100644 --- a/test/cases/atomics.zig +++ b/test/cases/atomics.zig @@ -1,12 +1,24 @@ -const assert = @import("std").debug.assert; +const std = @import("std"); +const assert = std.debug.assert; const builtin = @import("builtin"); const AtomicRmwOp = builtin.AtomicRmwOp; const AtomicOrder = builtin.AtomicOrder; test "cmpxchg" { var x: i32 = 1234; - while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {} + if (@cmpxchgWeak(i32, &x, 99, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assert(x1 == 1234); + } else { + @panic("cmpxchg should have failed"); + } + + while (@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) |x1| { + assert(x1 == 1234); + } assert(x == 5678); + + assert(@cmpxchgStrong(i32, &x, 5678, 42, AtomicOrder.SeqCst, AtomicOrder.SeqCst) == null); + assert(x == 42); } test "fence" { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index b22816a9a8..926e997c6e 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1394,17 +1394,17 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\const AtomicOrder = @import("builtin").AtomicOrder; \\export fn f() void { \\ var x: i32 = 1234; - \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {} + \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Monotonic, AtomicOrder.SeqCst)) {} \\} - , ".tmp_source.zig:4:72: error: failure atomic ordering must be no stricter than success"); + , ".tmp_source.zig:4:81: error: failure atomic ordering must be no stricter than success"); cases.add("atomic orderings of cmpxchg - success Monotonic or stricter", \\const AtomicOrder = @import("builtin").AtomicOrder; \\export fn f() void { \\ var x: i32 = 1234; - \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {} + \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.Unordered, AtomicOrder.Unordered)) {} \\} - , ".tmp_source.zig:4:49: error: success atomic ordering must be Monotonic or stricter"); + , ".tmp_source.zig:4:58: error: success atomic ordering must be Monotonic or stricter"); cases.add("negation overflow in function evaluation", \\const y = neg(-128); @@ -2460,11 +2460,11 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\const AtomicOrder = @import("builtin").AtomicOrder; \\export fn entry() bool { \\ var x: i32 align(1) = 1234; - \\ while (!@cmpxchg(&x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {} + \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, AtomicOrder.SeqCst, AtomicOrder.SeqCst)) {} \\ return x == 5678; \\} , - ".tmp_source.zig:4:23: error: expected pointer alignment of at least 4, found 1"); + ".tmp_source.zig:4:32: error: expected type '&i32', found '&align(1) i32'"); cases.add("wrong size to an array literal", \\comptime { @@ -2534,10 +2534,10 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { cases.add("wrong types given to atomic order args in cmpxchg", \\export fn entry() void { \\ var x: i32 = 1234; - \\ while (!@cmpxchg(&x, 1234, 5678, u32(1234), u32(1234))) {} + \\ while (!@cmpxchgWeak(i32, &x, 1234, 5678, u32(1234), u32(1234))) {} \\} , - ".tmp_source.zig:3:41: error: expected type 'AtomicOrder', found 'u32'"); + ".tmp_source.zig:3:50: error: expected type 'AtomicOrder', found 'u32'"); cases.add("wrong types given to @export", \\extern fn entry() void { } -- cgit v1.2.3 From d464b2532200de3778ac7362e701791a11150d55 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Sun, 10 Jun 2018 04:39:22 +0200 Subject: support `--target-arch wasm32` (#1094) Add wasm32 support to the build-obj, build-exe and build-lib commands of the stage 1 compiler. Wasm64 should work transparently once it's supported in upstream LLVM. To export a function: // lib.zig - for exposition, not necessary for this example pub use @import("add.zig"); // add.zig export fn add(a: i32, b: i32) i32 { return a + b; } To import a function: // cube.zig extern fn square(x: i32) i32; export fn cube(x: i32) i32 { return x * square(x); } --- build.zig | 1 + src/link.cpp | 15 ++++++++++++++- src/target.cpp | 7 +++++-- src/zig_llvm.cpp | 2 +- 4 files changed, 21 insertions(+), 4 deletions(-) (limited to 'src/zig_llvm.cpp') diff --git a/build.zig b/build.zig index 109a799ac9..08a47570ef 100644 --- a/build.zig +++ b/build.zig @@ -63,6 +63,7 @@ pub fn build(b: *Builder) !void { exe.addObjectFile(lib); } } else { + addCppLib(b, exe, cmake_binary_dir, "embedded_lld_wasm"); addCppLib(b, exe, cmake_binary_dir, "embedded_lld_elf"); addCppLib(b, exe, cmake_binary_dir, "embedded_lld_coff"); addCppLib(b, exe, cmake_binary_dir, "embedded_lld_lib"); diff --git a/src/link.cpp b/src/link.cpp index d454d77aae..d2925cb5a8 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -391,6 +391,19 @@ static void construct_linker_job_elf(LinkJob *lj) { } } +static void construct_linker_job_wasm(LinkJob *lj) { + CodeGen *g = lj->codegen; + + lj->args.append("--relocatable"); // So lld doesn't look for _start. + lj->args.append("-o"); + lj->args.append(buf_ptr(&lj->out_file)); + + // .o files + for (size_t i = 0; i < g->link_objects.length; i += 1) { + lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); + } +} + //static bool is_target_cyg_mingw(const ZigTarget *target) { // return (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_Cygnus) || // (target->os == ZigLLVM_Win32 && target->env_type == ZigLLVM_GNU); @@ -924,7 +937,7 @@ static void construct_linker_job(LinkJob *lj) { case ZigLLVM_MachO: return construct_linker_job_macho(lj); case ZigLLVM_Wasm: - zig_panic("TODO link wasm"); + return construct_linker_job_wasm(lj); } } diff --git a/src/target.cpp b/src/target.cpp index c53ed74d14..bd4aa4d4c2 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -597,12 +597,15 @@ void resolve_target_object_format(ZigTarget *target) { case ZigLLVM_tce: case ZigLLVM_tcele: case ZigLLVM_thumbeb: - case ZigLLVM_wasm32: - case ZigLLVM_wasm64: case ZigLLVM_xcore: target->oformat= ZigLLVM_ELF; return; + case ZigLLVM_wasm32: + case ZigLLVM_wasm64: + target->oformat = ZigLLVM_Wasm; + return; + case ZigLLVM_ppc: case ZigLLVM_ppc64: if (is_os_darwin(target)) { diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 5905fa8167..24f2a8a343 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -838,7 +838,7 @@ bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_ return lld::mach_o::link(array_ref_args, diag); case ZigLLVM_Wasm: - assert(false); // TODO ZigLLDLink for Wasm + return lld::wasm::link(array_ref_args, false, diag); } assert(false); // unreachable abort(); -- cgit v1.2.3 From 97bfeac13f89e1b5a22fcd7d4705341b4c3e1950 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 16 Jul 2018 20:52:50 -0400 Subject: self-hosted: create tmp dir for .o files and emit .o file for fn --- CMakeLists.txt | 1 + src-self-hosted/codegen.zig | 86 ++++++++++- src-self-hosted/compilation.zig | 335 ++++++++++++++++++++++++++++++++-------- src-self-hosted/ir.zig | 10 +- src-self-hosted/llvm.zig | 75 ++++++++- src-self-hosted/main.zig | 8 +- src-self-hosted/package.zig | 29 ++++ src-self-hosted/scope.zig | 32 ++-- src-self-hosted/target.zig | 116 ++++++++++---- src-self-hosted/test.zig | 3 +- src-self-hosted/type.zig | 58 +++---- src-self-hosted/value.zig | 41 ++++- src/zig_llvm.cpp | 5 + src/zig_llvm.h | 3 +- std/atomic/int.zig | 4 + std/buffer.zig | 13 ++ std/dwarf.zig | 37 +++++ std/event/future.zig | 39 ++++- std/index.zig | 3 + std/lazy_init.zig | 85 ++++++++++ 20 files changed, 808 insertions(+), 175 deletions(-) create mode 100644 src-self-hosted/package.zig create mode 100644 std/lazy_init.zig (limited to 'src/zig_llvm.cpp') diff --git a/CMakeLists.txt b/CMakeLists.txt index e606855555..0e7c1df350 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -479,6 +479,7 @@ set(ZIG_STD_FILES "index.zig" "io.zig" "json.zig" + "lazy_init.zig" "linked_list.zig" "macho.zig" "math/acos.zig" diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 698f1e5b45..28ba2a1564 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -1,19 +1,22 @@ const std = @import("std"); +const builtin = @import("builtin"); const Compilation = @import("compilation.zig").Compilation; -// we go through llvm instead of c for 2 reasons: -// 1. to avoid accidentally calling the non-thread-safe functions -// 2. patch up some of the types to remove nullability const llvm = @import("llvm.zig"); +const c = @import("c.zig"); const ir = @import("ir.zig"); const Value = @import("value.zig").Value; const Type = @import("type.zig").Type; const event = std.event; const assert = std.debug.assert; +const DW = std.dwarf; pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) !void { fn_val.base.ref(); defer fn_val.base.deref(comp); - defer code.destroy(comp.a()); + defer code.destroy(comp.gpa()); + + var output_path = try await (async comp.createRandomOutputPath(comp.target.oFileExt()) catch unreachable); + errdefer output_path.deinit(); const llvm_handle = try comp.event_loop_local.getAnyLlvmContext(); defer llvm_handle.release(comp.event_loop_local); @@ -23,13 +26,56 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) const module = llvm.ModuleCreateWithNameInContext(comp.name.ptr(), context) orelse return error.OutOfMemory; defer llvm.DisposeModule(module); + llvm.SetTarget(module, comp.llvm_triple.ptr()); + llvm.SetDataLayout(module, comp.target_layout_str); + + if (comp.target.getObjectFormat() == builtin.ObjectFormat.coff) { + llvm.AddModuleCodeViewFlag(module); + } else { + llvm.AddModuleDebugInfoFlag(module); + } + const builder = llvm.CreateBuilderInContext(context) orelse return error.OutOfMemory; defer llvm.DisposeBuilder(builder); + const dibuilder = llvm.CreateDIBuilder(module, true) orelse return error.OutOfMemory; + defer llvm.DisposeDIBuilder(dibuilder); + + // Don't use ZIG_VERSION_STRING here. LLVM misparses it when it includes + // the git revision. + const producer = try std.Buffer.allocPrint( + &code.arena.allocator, + "zig {}.{}.{}", + u32(c.ZIG_VERSION_MAJOR), + u32(c.ZIG_VERSION_MINOR), + u32(c.ZIG_VERSION_PATCH), + ); + const flags = c""; + const runtime_version = 0; + const compile_unit_file = llvm.CreateFile( + dibuilder, + comp.name.ptr(), + comp.root_package.root_src_dir.ptr(), + ) orelse return error.OutOfMemory; + const is_optimized = comp.build_mode != builtin.Mode.Debug; + const compile_unit = llvm.CreateCompileUnit( + dibuilder, + DW.LANG_C99, + compile_unit_file, + producer.ptr(), + is_optimized, + flags, + runtime_version, + c"", + 0, + !comp.strip, + ) orelse return error.OutOfMemory; + var ofile = ObjectFile{ .comp = comp, .module = module, .builder = builder, + .dibuilder = dibuilder, .context = context, .lock = event.Lock.init(comp.loop), }; @@ -41,8 +87,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) // LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm)); //} - // TODO - //ZigLLVMDIBuilderFinalize(g->dbuilder); + llvm.DIBuilderFinalize(dibuilder); if (comp.verbose_llvm_ir) { llvm.DumpModule(ofile.module); @@ -53,17 +98,42 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code) var error_ptr: ?[*]u8 = null; _ = llvm.VerifyModule(ofile.module, llvm.AbortProcessAction, &error_ptr); } + + assert(comp.emit_file_type == Compilation.Emit.Binary); // TODO support other types + + const is_small = comp.build_mode == builtin.Mode.ReleaseSmall; + const is_debug = comp.build_mode == builtin.Mode.Debug; + + var err_msg: [*]u8 = undefined; + // TODO integrate this with evented I/O + if (llvm.TargetMachineEmitToFile( + comp.target_machine, + module, + output_path.ptr(), + llvm.EmitBinary, + &err_msg, + is_debug, + is_small, + )) { + if (std.debug.runtime_safety) { + std.debug.panic("unable to write object file {}: {s}\n", output_path.toSliceConst(), err_msg); + } + return error.WritingObjectFileFailed; + } + //validate_inline_fns(g); TODO + fn_val.containing_object = output_path; } pub const ObjectFile = struct { comp: *Compilation, module: llvm.ModuleRef, builder: llvm.BuilderRef, + dibuilder: *llvm.DIBuilder, context: llvm.ContextRef, lock: event.Lock, - fn a(self: *ObjectFile) *std.mem.Allocator { - return self.comp.a(); + fn gpa(self: *ObjectFile) *std.mem.Allocator { + return self.comp.gpa(); } }; diff --git a/src-self-hosted/compilation.zig b/src-self-hosted/compilation.zig index 1dbbf21206..d5380a0644 100644 --- a/src-self-hosted/compilation.zig +++ b/src-self-hosted/compilation.zig @@ -26,16 +26,31 @@ const Value = @import("value.zig").Value; const Type = Value.Type; const Span = errmsg.Span; const codegen = @import("codegen.zig"); +const Package = @import("package.zig").Package; /// Data that is local to the event loop. pub const EventLoopLocal = struct { loop: *event.Loop, llvm_handle_pool: std.atomic.Stack(llvm.ContextRef), - fn init(loop: *event.Loop) EventLoopLocal { + /// TODO pool these so that it doesn't have to lock + prng: event.Locked(std.rand.DefaultPrng), + + var lazy_init_targets = std.lazyInit(void); + + fn init(loop: *event.Loop) !EventLoopLocal { + lazy_init_targets.get() orelse { + Target.initializeAll(); + lazy_init_targets.resolve(); + }; + + var seed_bytes: [@sizeOf(u64)]u8 = undefined; + try std.os.getRandomBytes(seed_bytes[0..]); + const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big); return EventLoopLocal{ .loop = loop, .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(), + .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)), }; } @@ -76,10 +91,16 @@ pub const Compilation = struct { event_loop_local: *EventLoopLocal, loop: *event.Loop, name: Buffer, + llvm_triple: Buffer, root_src_path: ?[]const u8, target: Target, + llvm_target: llvm.TargetRef, build_mode: builtin.Mode, zig_lib_dir: []const u8, + zig_std_dir: []const u8, + + /// lazily created when we need it + tmp_dir: event.Future(BuildError![]u8), version_major: u32, version_minor: u32, @@ -106,8 +127,16 @@ pub const Compilation = struct { lib_dirs: []const []const u8, rpath_list: []const []const u8, assembly_files: []const []const u8, + + /// paths that are explicitly provided by the user to link against link_objects: []const []const u8, + /// functions that have their own objects that we need to link + /// it uses an optional pointer so that tombstone removals are possible + fn_link_set: event.Locked(FnLinkSet), + + pub const FnLinkSet = std.LinkedList(?*Value.Fn); + windows_subsystem_windows: bool, windows_subsystem_console: bool, @@ -141,7 +170,7 @@ pub const Compilation = struct { /// Before code generation starts, must wait on this group to make sure /// the build is complete. - build_group: event.Group(BuildError!void), + prelink_group: event.Group(BuildError!void), compile_errors: event.Locked(CompileErrList), @@ -155,6 +184,16 @@ pub const Compilation = struct { false_value: *Value.Bool, noreturn_value: *Value.NoReturn, + target_machine: llvm.TargetMachineRef, + target_data_ref: llvm.TargetDataRef, + target_layout_str: [*]u8, + + /// for allocating things which have the same lifetime as this Compilation + arena_allocator: std.heap.ArenaAllocator, + + root_package: *Package, + std_package: *Package, + const CompileErrList = std.ArrayList(*errmsg.Msg); // TODO handle some of these earlier and report them in a way other than error codes @@ -195,6 +234,9 @@ pub const Compilation = struct { BufferTooSmall, Unimplemented, // TODO remove this one SemanticAnalysisFailed, // TODO remove this one + ReadOnlyFileSystem, + LinkQuotaExceeded, + EnvironmentVariableNotFound, }; pub const Event = union(enum) { @@ -234,31 +276,31 @@ pub const Compilation = struct { event_loop_local: *EventLoopLocal, name: []const u8, root_src_path: ?[]const u8, - target: *const Target, + target: Target, kind: Kind, build_mode: builtin.Mode, + is_static: bool, zig_lib_dir: []const u8, cache_dir: []const u8, ) !*Compilation { const loop = event_loop_local.loop; - - var name_buffer = try Buffer.init(loop.allocator, name); - errdefer name_buffer.deinit(); - - const events = try event.Channel(Event).create(loop, 0); - errdefer events.destroy(); - - const comp = try loop.allocator.create(Compilation{ + const comp = try event_loop_local.loop.allocator.create(Compilation{ .loop = loop, + .arena_allocator = std.heap.ArenaAllocator.init(loop.allocator), .event_loop_local = event_loop_local, - .events = events, - .name = name_buffer, + .events = undefined, .root_src_path = root_src_path, - .target = target.*, + .target = target, + .llvm_target = undefined, .kind = kind, .build_mode = build_mode, .zig_lib_dir = zig_lib_dir, + .zig_std_dir = undefined, .cache_dir = cache_dir, + .tmp_dir = event.Future(BuildError![]u8).init(loop), + + .name = undefined, + .llvm_triple = undefined, .version_major = 0, .version_minor = 0, @@ -283,7 +325,7 @@ pub const Compilation = struct { .is_test = false, .each_lib_rpath = false, .strip = false, - .is_static = false, + .is_static = is_static, .linker_rdynamic = false, .clang_argv = [][]const u8{}, .llvm_argv = [][]const u8{}, @@ -291,9 +333,10 @@ pub const Compilation = struct { .rpath_list = [][]const u8{}, .assembly_files = [][]const u8{}, .link_objects = [][]const u8{}, + .fn_link_set = event.Locked(FnLinkSet).init(loop, FnLinkSet.init()), .windows_subsystem_windows = false, .windows_subsystem_console = false, - .link_libs_list = ArrayList(*LinkLib).init(loop.allocator), + .link_libs_list = undefined, .libc_link_lib = null, .err_color = errmsg.Color.Auto, .darwin_frameworks = [][]const u8{}, @@ -303,7 +346,7 @@ pub const Compilation = struct { .emit_file_type = Emit.Binary, .link_out_file = null, .exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)), - .build_group = event.Group(BuildError!void).init(loop), + .prelink_group = event.Group(BuildError!void).init(loop), .compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)), .meta_type = undefined, @@ -314,13 +357,82 @@ pub const Compilation = struct { .false_value = undefined, .noreturn_type = undefined, .noreturn_value = undefined, + + .target_machine = undefined, + .target_data_ref = undefined, + .target_layout_str = undefined, + + .root_package = undefined, + .std_package = undefined, }); + errdefer { + comp.arena_allocator.deinit(); + comp.loop.allocator.destroy(comp); + } + + comp.name = try Buffer.init(comp.arena(), name); + comp.llvm_triple = try target.getTriple(comp.arena()); + comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple); + comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena()); + comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std"); + + const opt_level = switch (build_mode) { + builtin.Mode.Debug => llvm.CodeGenLevelNone, + else => llvm.CodeGenLevelAggressive, + }; + + const reloc_mode = if (is_static) llvm.RelocStatic else llvm.RelocPIC; + + // LLVM creates invalid binaries on Windows sometimes. + // See https://github.com/ziglang/zig/issues/508 + // As a workaround we do not use target native features on Windows. + var target_specific_cpu_args: ?[*]u8 = null; + var target_specific_cpu_features: ?[*]u8 = null; + errdefer llvm.DisposeMessage(target_specific_cpu_args); + errdefer llvm.DisposeMessage(target_specific_cpu_features); + if (target == Target.Native and !target.isWindows()) { + target_specific_cpu_args = llvm.GetHostCPUName() orelse return error.OutOfMemory; + target_specific_cpu_features = llvm.GetNativeFeatures() orelse return error.OutOfMemory; + } + + comp.target_machine = llvm.CreateTargetMachine( + comp.llvm_target, + comp.llvm_triple.ptr(), + target_specific_cpu_args orelse c"", + target_specific_cpu_features orelse c"", + opt_level, + reloc_mode, + llvm.CodeModelDefault, + ) orelse return error.OutOfMemory; + errdefer llvm.DisposeTargetMachine(comp.target_machine); + + comp.target_data_ref = llvm.CreateTargetDataLayout(comp.target_machine) orelse return error.OutOfMemory; + errdefer llvm.DisposeTargetData(comp.target_data_ref); + + comp.target_layout_str = llvm.CopyStringRepOfTargetData(comp.target_data_ref) orelse return error.OutOfMemory; + errdefer llvm.DisposeMessage(comp.target_layout_str); + + comp.events = try event.Channel(Event).create(comp.loop, 0); + errdefer comp.events.destroy(); + + if (root_src_path) |root_src| { + const dirname = std.os.path.dirname(root_src) orelse "."; + const basename = std.os.path.basename(root_src); + + comp.root_package = try Package.create(comp.arena(), dirname, basename); + comp.std_package = try Package.create(comp.arena(), comp.zig_std_dir, "index.zig"); + try comp.root_package.add("std", comp.std_package); + } else { + comp.root_package = try Package.create(comp.arena(), ".", ""); + } + try comp.initTypes(); + return comp; } fn initTypes(comp: *Compilation) !void { - comp.meta_type = try comp.a().create(Type.MetaType{ + comp.meta_type = try comp.gpa().create(Type.MetaType{ .base = Type{ .base = Value{ .id = Value.Id.Type, @@ -333,9 +445,9 @@ pub const Compilation = struct { }); comp.meta_type.value = &comp.meta_type.base; comp.meta_type.base.base.typeof = &comp.meta_type.base; - errdefer comp.a().destroy(comp.meta_type); + errdefer comp.gpa().destroy(comp.meta_type); - comp.void_type = try comp.a().create(Type.Void{ + comp.void_type = try comp.gpa().create(Type.Void{ .base = Type{ .base = Value{ .id = Value.Id.Type, @@ -345,9 +457,9 @@ pub const Compilation = struct { .id = builtin.TypeId.Void, }, }); - errdefer comp.a().destroy(comp.void_type); + errdefer comp.gpa().destroy(comp.void_type); - comp.noreturn_type = try comp.a().create(Type.NoReturn{ + comp.noreturn_type = try comp.gpa().create(Type.NoReturn{ .base = Type{ .base = Value{ .id = Value.Id.Type, @@ -357,9 +469,9 @@ pub const Compilation = struct { .id = builtin.TypeId.NoReturn, }, }); - errdefer comp.a().destroy(comp.noreturn_type); + errdefer comp.gpa().destroy(comp.noreturn_type); - comp.bool_type = try comp.a().create(Type.Bool{ + comp.bool_type = try comp.gpa().create(Type.Bool{ .base = Type{ .base = Value{ .id = Value.Id.Type, @@ -369,18 +481,18 @@ pub const Compilation = struct { .id = builtin.TypeId.Bool, }, }); - errdefer comp.a().destroy(comp.bool_type); + errdefer comp.gpa().destroy(comp.bool_type); - comp.void_value = try comp.a().create(Value.Void{ + comp.void_value = try comp.gpa().create(Value.Void{ .base = Value{ .id = Value.Id.Void, .typeof = &Type.Void.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, }); - errdefer comp.a().destroy(comp.void_value); + errdefer comp.gpa().destroy(comp.void_value); - comp.true_value = try comp.a().create(Value.Bool{ + comp.true_value = try comp.gpa().create(Value.Bool{ .base = Value{ .id = Value.Id.Bool, .typeof = &Type.Bool.get(comp).base, @@ -388,9 +500,9 @@ pub const Compilation = struct { }, .x = true, }); - errdefer comp.a().destroy(comp.true_value); + errdefer comp.gpa().destroy(comp.true_value); - comp.false_value = try comp.a().create(Value.Bool{ + comp.false_value = try comp.gpa().create(Value.Bool{ .base = Value{ .id = Value.Id.Bool, .typeof = &Type.Bool.get(comp).base, @@ -398,19 +510,23 @@ pub const Compilation = struct { }, .x = false, }); - errdefer comp.a().destroy(comp.false_value); + errdefer comp.gpa().destroy(comp.false_value); - comp.noreturn_value = try comp.a().create(Value.NoReturn{ + comp.noreturn_value = try comp.gpa().create(Value.NoReturn{ .base = Value{ .id = Value.Id.NoReturn, .typeof = &Type.NoReturn.get(comp).base, .ref_count = std.atomic.Int(usize).init(1), }, }); - errdefer comp.a().destroy(comp.noreturn_value); + errdefer comp.gpa().destroy(comp.noreturn_value); } pub fn destroy(self: *Compilation) void { + if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| { + os.deleteTree(self.arena(), tmp_dir) catch {}; + } else |_| {}; + self.noreturn_value.base.deref(self); self.void_value.base.deref(self); self.false_value.base.deref(self); @@ -420,14 +536,18 @@ pub const Compilation = struct { self.meta_type.base.base.deref(self); self.events.destroy(); - self.name.deinit(); - self.a().destroy(self); + llvm.DisposeMessage(self.target_layout_str); + llvm.DisposeTargetData(self.target_data_ref); + llvm.DisposeTargetMachine(self.target_machine); + + self.arena_allocator.deinit(); + self.gpa().destroy(self); } pub fn build(self: *Compilation) !void { if (self.llvm_argv.len != 0) { - var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.a(), [][]const []const u8{ + var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{ [][]const u8{"zig (LLVM option parsing)"}, self.llvm_argv, }); @@ -436,7 +556,7 @@ pub const Compilation = struct { c.ZigLLVMParseCommandLineOptions(self.llvm_argv.len + 1, c_compatible_args.ptr); } - _ = try async self.buildAsync(); + _ = try async self.buildAsync(); } async fn buildAsync(self: *Compilation) void { @@ -464,7 +584,7 @@ pub const Compilation = struct { } } else |err| { // if there's an error then the compile errors have dangling references - self.a().free(compile_errors); + self.gpa().free(compile_errors); await (async self.events.put(Event{ .Error = err }) catch unreachable); } @@ -477,26 +597,26 @@ pub const Compilation = struct { async fn addRootSrc(self: *Compilation) !void { const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path"); // TODO async/await os.path.real - const root_src_real_path = os.path.real(self.a(), root_src_path) catch |err| { + const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| { try printError("unable to get real path '{}': {}", root_src_path, err); return err; }; - errdefer self.a().free(root_src_real_path); + errdefer self.gpa().free(root_src_real_path); // TODO async/await readFileAlloc() - const source_code = io.readFileAlloc(self.a(), root_src_real_path) catch |err| { + const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| { try printError("unable to open '{}': {}", root_src_real_path, err); return err; }; - errdefer self.a().free(source_code); + errdefer self.gpa().free(source_code); - const parsed_file = try self.a().create(ParsedFile{ + const parsed_file = try self.gpa().create(ParsedFile{ .tree = undefined, .realpath = root_src_real_path, }); - errdefer self.a().destroy(parsed_file); + errdefer self.gpa().destroy(parsed_file); - parsed_file.tree = try std.zig.parse(self.a(), source_code); + parsed_file.tree = try std.zig.parse(self.gpa(), source_code); errdefer parsed_file.tree.deinit(); const tree = &parsed_file.tree; @@ -525,7 +645,7 @@ pub const Compilation = struct { continue; }; - const fn_decl = try self.a().create(Decl.Fn{ + const fn_decl = try self.gpa().create(Decl.Fn{ .base = Decl{ .id = Decl.Id.Fn, .name = name, @@ -538,7 +658,7 @@ pub const Compilation = struct { .value = Decl.Fn.Val{ .Unresolved = {} }, .fn_proto = fn_proto, }); - errdefer self.a().destroy(fn_decl); + errdefer self.gpa().destroy(fn_decl); try decl_group.call(addTopLevelDecl, self, &fn_decl.base); }, @@ -547,15 +667,15 @@ pub const Compilation = struct { } } try await (async decl_group.wait() catch unreachable); - try await (async self.build_group.wait() catch unreachable); + try await (async self.prelink_group.wait() catch unreachable); } async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void { const is_export = decl.isExported(&decl.parsed_file.tree); if (is_export) { - try self.build_group.call(verifyUniqueSymbol, self, decl); - try self.build_group.call(resolveDecl, self, decl); + try self.prelink_group.call(verifyUniqueSymbol, self, decl); + try self.prelink_group.call(resolveDecl, self, decl); } } @@ -563,7 +683,7 @@ pub const Compilation = struct { const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args); errdefer self.loop.allocator.free(text); - try self.build_group.call(addCompileErrorAsync, self, parsed_file, span, text); + try self.prelink_group.call(addCompileErrorAsync, self, parsed_file, span, text); } async fn addCompileErrorAsync( @@ -625,11 +745,11 @@ pub const Compilation = struct { } } - const link_lib = try self.a().create(LinkLib{ + const link_lib = try self.gpa().create(LinkLib{ .name = name, .path = null, .provided_explicitly = provided_explicitly, - .symbols = ArrayList([]u8).init(self.a()), + .symbols = ArrayList([]u8).init(self.gpa()), }); try self.link_libs_list.append(link_lib); if (is_libc) { @@ -638,9 +758,71 @@ pub const Compilation = struct { return link_lib; } - fn a(self: Compilation) *mem.Allocator { + /// General Purpose Allocator. Must free when done. + fn gpa(self: Compilation) *mem.Allocator { return self.loop.allocator; } + + /// Arena Allocator. Automatically freed when the Compilation is destroyed. + fn arena(self: *Compilation) *mem.Allocator { + return &self.arena_allocator.allocator; + } + + /// If the temporary directory for this compilation has not been created, it creates it. + /// Then it creates a random file name in that dir and returns it. + pub async fn createRandomOutputPath(self: *Compilation, suffix: []const u8) !Buffer { + const tmp_dir = try await (async self.getTmpDir() catch unreachable); + const file_prefix = await (async self.getRandomFileName() catch unreachable); + + const file_name = try std.fmt.allocPrint(self.gpa(), "{}{}", file_prefix[0..], suffix); + defer self.gpa().free(file_name); + + const full_path = try os.path.join(self.gpa(), tmp_dir, file_name[0..]); + errdefer self.gpa().free(full_path); + + return Buffer.fromOwnedSlice(self.gpa(), full_path); + } + + /// If the temporary directory for this Compilation has not been created, creates it. + /// Then returns it. The directory is unique to this Compilation and cleaned up when + /// the Compilation deinitializes. + async fn getTmpDir(self: *Compilation) ![]const u8 { + if (await (async self.tmp_dir.start() catch unreachable)) |ptr| return ptr.*; + self.tmp_dir.data = await (async self.getTmpDirImpl() catch unreachable); + self.tmp_dir.resolve(); + return self.tmp_dir.data; + } + + async fn getTmpDirImpl(self: *Compilation) ![]u8 { + const comp_dir_name = await (async self.getRandomFileName() catch unreachable); + const zig_dir_path = try getZigDir(self.gpa()); + defer self.gpa().free(zig_dir_path); + + const tmp_dir = try os.path.join(self.arena(), zig_dir_path, comp_dir_name[0..]); + try os.makePath(self.gpa(), tmp_dir); + return tmp_dir; + } + + async fn getRandomFileName(self: *Compilation) [12]u8 { + // here we replace the standard +/ with -_ so that it can be used in a file name + const b64_fs_encoder = std.base64.Base64Encoder.init( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", + std.base64.standard_pad_char, + ); + + var rand_bytes: [9]u8 = undefined; + + { + const held = await (async self.event_loop_local.prng.acquire() catch unreachable); + defer held.release(); + + held.value.random.bytes(rand_bytes[0..]); + } + + var result: [12]u8 = undefined; + b64_fs_encoder.encode(result[0..], rand_bytes); + return result; + } }; fn printError(comptime format: []const u8, args: ...) !void { @@ -662,13 +844,11 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib /// This declaration has been blessed as going into the final code generation. pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void { - if (@atomicRmw(u8, &decl.resolution_in_progress, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst) == 0) { - decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable); - decl.resolution.resolve(); - return decl.resolution.data; - } else { - return (await (async decl.resolution.get() catch unreachable)).*; - } + if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*; + + decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable); + decl.resolution.resolve(); + return decl.resolution.data; } /// The function that actually does the generation. @@ -698,7 +878,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { const fn_type = try Type.Fn.create(comp, return_type, params, is_var_args); defer fn_type.base.base.deref(comp); - var symbol_name = try std.Buffer.init(comp.a(), fn_decl.base.name); + var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name); errdefer symbol_name.deinit(); const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name); @@ -719,7 +899,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { error.SemanticAnalysisFailed => return {}, else => return err, }; - defer unanalyzed_code.destroy(comp.a()); + defer unanalyzed_code.destroy(comp.gpa()); if (comp.verbose_ir) { std.debug.warn("unanalyzed:\n"); @@ -738,7 +918,7 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { error.SemanticAnalysisFailed => return {}, else => return err, }; - errdefer analyzed_code.destroy(comp.a()); + errdefer analyzed_code.destroy(comp.gpa()); if (comp.verbose_ir) { std.debug.warn("analyzed:\n"); @@ -747,5 +927,30 @@ async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void { // Kick off rendering to LLVM module, but it doesn't block the fn decl // analysis from being complete. - try comp.build_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); + try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code); + try comp.prelink_group.call(addFnToLinkSet, comp, fn_val); +} + +async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void { + fn_val.base.ref(); + defer fn_val.base.deref(comp); + + fn_val.link_set_node.data = fn_val; + + const held = await (async comp.fn_link_set.acquire() catch unreachable); + defer held.release(); + + held.value.append(fn_val.link_set_node); +} + +fn getZigDir(allocator: *mem.Allocator) ![]u8 { + const home_dir = try getHomeDir(allocator); + defer allocator.free(home_dir); + + return os.path.join(allocator, home_dir, ".zig"); +} + +/// TODO move to zig std lib, and make it work for other OSes +fn getHomeDir(allocator: *mem.Allocator) ![]u8 { + return os.getEnvVarOwned(allocator, "HOME"); } diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 0e0a4f9bf3..c1f9c97001 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -453,7 +453,7 @@ pub const Code = struct { arena: std.heap.ArenaAllocator, return_type: ?*Type, - /// allocator is comp.a() + /// allocator is comp.gpa() pub fn destroy(self: *Code, allocator: *Allocator) void { self.arena.deinit(); allocator.destroy(self); @@ -483,13 +483,13 @@ pub const Builder = struct { pub const Error = Analyze.Error; pub fn init(comp: *Compilation, parsed_file: *ParsedFile) !Builder { - const code = try comp.a().create(Code{ + const code = try comp.gpa().create(Code{ .basic_block_list = undefined, - .arena = std.heap.ArenaAllocator.init(comp.a()), + .arena = std.heap.ArenaAllocator.init(comp.gpa()), .return_type = null, }); code.basic_block_list = std.ArrayList(*BasicBlock).init(&code.arena.allocator); - errdefer code.destroy(comp.a()); + errdefer code.destroy(comp.gpa()); return Builder{ .comp = comp, @@ -502,7 +502,7 @@ pub const Builder = struct { } pub fn abort(self: *Builder) void { - self.code.destroy(self.comp.a()); + self.code.destroy(self.comp.gpa()); } /// Call code.destroy() when done diff --git a/src-self-hosted/llvm.zig b/src-self-hosted/llvm.zig index 13480dc2c6..b196656367 100644 --- a/src-self-hosted/llvm.zig +++ b/src-self-hosted/llvm.zig @@ -2,6 +2,12 @@ const builtin = @import("builtin"); const c = @import("c.zig"); const assert = @import("std").debug.assert; +// we wrap the c module for 3 reasons: +// 1. to avoid accidentally calling the non-thread-safe functions +// 2. patch up some of the types to remove nullability +// 3. some functions have been augmented by zig_llvm.cpp to be more powerful, +// such as ZigLLVMTargetMachineEmitToFile + pub const AttributeIndex = c_uint; pub const Bool = c_int; @@ -12,25 +18,51 @@ pub const ValueRef = removeNullability(c.LLVMValueRef); pub const TypeRef = removeNullability(c.LLVMTypeRef); pub const BasicBlockRef = removeNullability(c.LLVMBasicBlockRef); pub const AttributeRef = removeNullability(c.LLVMAttributeRef); +pub const TargetRef = removeNullability(c.LLVMTargetRef); +pub const TargetMachineRef = removeNullability(c.LLVMTargetMachineRef); +pub const TargetDataRef = removeNullability(c.LLVMTargetDataRef); +pub const DIBuilder = c.ZigLLVMDIBuilder; pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex; pub const AddFunction = c.LLVMAddFunction; +pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag; +pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag; pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation; +pub const ConstAllOnes = c.LLVMConstAllOnes; pub const ConstInt = c.LLVMConstInt; +pub const ConstNull = c.LLVMConstNull; pub const ConstStringInContext = c.LLVMConstStringInContext; pub const ConstStructInContext = c.LLVMConstStructInContext; +pub const CopyStringRepOfTargetData = c.LLVMCopyStringRepOfTargetData; pub const CreateBuilderInContext = c.LLVMCreateBuilderInContext; +pub const CreateCompileUnit = c.ZigLLVMCreateCompileUnit; +pub const CreateDIBuilder = c.ZigLLVMCreateDIBuilder; pub const CreateEnumAttribute = c.LLVMCreateEnumAttribute; +pub const CreateFile = c.ZigLLVMCreateFile; pub const CreateStringAttribute = c.LLVMCreateStringAttribute; +pub const CreateTargetDataLayout = c.LLVMCreateTargetDataLayout; +pub const CreateTargetMachine = c.LLVMCreateTargetMachine; +pub const DIBuilderFinalize = c.ZigLLVMDIBuilderFinalize; pub const DisposeBuilder = c.LLVMDisposeBuilder; +pub const DisposeDIBuilder = c.ZigLLVMDisposeDIBuilder; +pub const DisposeMessage = c.LLVMDisposeMessage; pub const DisposeModule = c.LLVMDisposeModule; +pub const DisposeTargetData = c.LLVMDisposeTargetData; +pub const DisposeTargetMachine = c.LLVMDisposeTargetMachine; pub const DoubleTypeInContext = c.LLVMDoubleTypeInContext; pub const DumpModule = c.LLVMDumpModule; pub const FP128TypeInContext = c.LLVMFP128TypeInContext; pub const FloatTypeInContext = c.LLVMFloatTypeInContext; pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName; +pub const GetHostCPUName = c.ZigLLVMGetHostCPUName; pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext; +pub const GetNativeFeatures = c.ZigLLVMGetNativeFeatures; pub const HalfTypeInContext = c.LLVMHalfTypeInContext; +pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers; +pub const InitializeAllAsmPrinters = c.LLVMInitializeAllAsmPrinters; +pub const InitializeAllTargetInfos = c.LLVMInitializeAllTargetInfos; +pub const InitializeAllTargetMCs = c.LLVMInitializeAllTargetMCs; +pub const InitializeAllTargets = c.LLVMInitializeAllTargets; pub const InsertBasicBlockInContext = c.LLVMInsertBasicBlockInContext; pub const Int128TypeInContext = c.LLVMInt128TypeInContext; pub const Int16TypeInContext = c.LLVMInt16TypeInContext; @@ -47,13 +79,16 @@ pub const MDStringInContext = c.LLVMMDStringInContext; pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext; pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext; pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext; +pub const SetDataLayout = c.LLVMSetDataLayout; +pub const SetTarget = c.LLVMSetTarget; pub const StructTypeInContext = c.LLVMStructTypeInContext; pub const TokenTypeInContext = c.LLVMTokenTypeInContext; pub const VoidTypeInContext = c.LLVMVoidTypeInContext; pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext; pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext; -pub const ConstAllOnes = c.LLVMConstAllOnes; -pub const ConstNull = c.LLVMConstNull; + +pub const GetTargetFromTriple = LLVMGetTargetFromTriple; +extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: *TargetRef, ErrorMessage: ?*[*]u8) Bool; pub const VerifyModule = LLVMVerifyModule; extern fn LLVMVerifyModule(M: ModuleRef, Action: VerifierFailureAction, OutMessage: *?[*]u8) Bool; @@ -83,6 +118,31 @@ pub const PrintMessageAction = VerifierFailureAction.LLVMPrintMessageAction; pub const ReturnStatusAction = VerifierFailureAction.LLVMReturnStatusAction; pub const VerifierFailureAction = c.LLVMVerifierFailureAction; +pub const CodeGenLevelNone = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelNone; +pub const CodeGenLevelLess = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelLess; +pub const CodeGenLevelDefault = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelDefault; +pub const CodeGenLevelAggressive = c.LLVMCodeGenOptLevel.LLVMCodeGenLevelAggressive; +pub const CodeGenOptLevel = c.LLVMCodeGenOptLevel; + +pub const RelocDefault = c.LLVMRelocMode.LLVMRelocDefault; +pub const RelocStatic = c.LLVMRelocMode.LLVMRelocStatic; +pub const RelocPIC = c.LLVMRelocMode.LLVMRelocPIC; +pub const RelocDynamicNoPic = c.LLVMRelocMode.LLVMRelocDynamicNoPic; +pub const RelocMode = c.LLVMRelocMode; + +pub const CodeModelDefault = c.LLVMCodeModel.LLVMCodeModelDefault; +pub const CodeModelJITDefault = c.LLVMCodeModel.LLVMCodeModelJITDefault; +pub const CodeModelSmall = c.LLVMCodeModel.LLVMCodeModelSmall; +pub const CodeModelKernel = c.LLVMCodeModel.LLVMCodeModelKernel; +pub const CodeModelMedium = c.LLVMCodeModel.LLVMCodeModelMedium; +pub const CodeModelLarge = c.LLVMCodeModel.LLVMCodeModelLarge; +pub const CodeModel = c.LLVMCodeModel; + +pub const EmitAssembly = EmitOutputType.ZigLLVM_EmitAssembly; +pub const EmitBinary = EmitOutputType.ZigLLVM_EmitBinary; +pub const EmitLLVMIr = EmitOutputType.ZigLLVM_EmitLLVMIr; +pub const EmitOutputType = c.ZigLLVM_EmitOutputType; + fn removeNullability(comptime T: type) type { comptime assert(@typeId(T) == builtin.TypeId.Optional); return T.Child; @@ -90,3 +150,14 @@ fn removeNullability(comptime T: type) type { pub const BuildRet = LLVMBuildRet; extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ValueRef; + +pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile; +extern fn ZigLLVMTargetMachineEmitToFile( + targ_machine_ref: TargetMachineRef, + module_ref: ModuleRef, + filename: [*]const u8, + output_type: EmitOutputType, + error_message: *[*]u8, + is_debug: bool, + is_small: bool, +) bool; diff --git a/src-self-hosted/main.zig b/src-self-hosted/main.zig index c9478954c5..8b668e35bd 100644 --- a/src-self-hosted/main.zig +++ b/src-self-hosted/main.zig @@ -363,6 +363,8 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co } }; + const is_static = flags.present("static"); + const assembly_files = flags.many("assembly"); const link_objects = flags.many("object"); if (root_source_file == null and link_objects.len == 0 and assembly_files.len == 0) { @@ -389,7 +391,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co try loop.initMultiThreaded(allocator); defer loop.deinit(); - var event_loop_local = EventLoopLocal.init(&loop); + var event_loop_local = try EventLoopLocal.init(&loop); defer event_loop_local.deinit(); var comp = try Compilation.create( @@ -399,6 +401,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co Target.Native, out_type, build_mode, + is_static, zig_lib_dir, full_cache_dir, ); @@ -426,7 +429,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co comp.clang_argv = clang_argv_buf.toSliceConst(); comp.strip = flags.present("strip"); - comp.is_static = flags.present("static"); if (flags.single("libc-lib-dir")) |libc_lib_dir| { comp.libc_lib_dir = libc_lib_dir; @@ -481,9 +483,9 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co } comp.emit_file_type = emit_type; - comp.link_objects = link_objects; comp.assembly_files = assembly_files; comp.link_out_file = flags.single("out-file"); + comp.link_objects = link_objects; try comp.build(); const process_build_events_handle = try async processBuildEvents(comp, color); diff --git a/src-self-hosted/package.zig b/src-self-hosted/package.zig new file mode 100644 index 0000000000..720b279651 --- /dev/null +++ b/src-self-hosted/package.zig @@ -0,0 +1,29 @@ +const std = @import("std"); +const mem = std.mem; +const assert = std.debug.assert; +const Buffer = std.Buffer; + +pub const Package = struct { + root_src_dir: Buffer, + root_src_path: Buffer, + + /// relative to root_src_dir + table: Table, + + pub const Table = std.HashMap([]const u8, *Package, mem.hash_slice_u8, mem.eql_slice_u8); + + /// makes internal copies of root_src_dir and root_src_path + /// allocator should be an arena allocator because Package never frees anything + pub fn create(allocator: *mem.Allocator, root_src_dir: []const u8, root_src_path: []const u8) !*Package { + return allocator.create(Package{ + .root_src_dir = try Buffer.init(allocator, root_src_dir), + .root_src_path = try Buffer.init(allocator, root_src_path), + .table = Table.init(allocator), + }); + } + + pub fn add(self: *Package, name: []const u8, package: *Package) !void { + const entry = try self.table.put(try mem.dupe(self.table.allocator, u8, name), package); + assert(entry == null); + } +}; diff --git a/src-self-hosted/scope.zig b/src-self-hosted/scope.zig index 4326617fa0..1c519d6c08 100644 --- a/src-self-hosted/scope.zig +++ b/src-self-hosted/scope.zig @@ -64,7 +64,7 @@ pub const Scope = struct { /// Creates a Decls scope with 1 reference pub fn create(comp: *Compilation, parent: ?*Scope) !*Decls { - const self = try comp.a().create(Decls{ + const self = try comp.gpa().create(Decls{ .base = Scope{ .id = Id.Decls, .parent = parent, @@ -72,9 +72,9 @@ pub const Scope = struct { }, .table = undefined, }); - errdefer comp.a().destroy(self); + errdefer comp.gpa().destroy(self); - self.table = Decl.Table.init(comp.a()); + self.table = Decl.Table.init(comp.gpa()); errdefer self.table.deinit(); if (parent) |p| p.ref(); @@ -126,7 +126,7 @@ pub const Scope = struct { /// Creates a Block scope with 1 reference pub fn create(comp: *Compilation, parent: ?*Scope) !*Block { - const self = try comp.a().create(Block{ + const self = try comp.gpa().create(Block{ .base = Scope{ .id = Id.Block, .parent = parent, @@ -138,14 +138,14 @@ pub const Scope = struct { .is_comptime = undefined, .safety = Safety.Auto, }); - errdefer comp.a().destroy(self); + errdefer comp.gpa().destroy(self); if (parent) |p| p.ref(); return self; } pub fn destroy(self: *Block, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -158,7 +158,7 @@ pub const Scope = struct { /// Creates a FnDef scope with 1 reference /// Must set the fn_val later pub fn create(comp: *Compilation, parent: ?*Scope) !*FnDef { - const self = try comp.a().create(FnDef{ + const self = try comp.gpa().create(FnDef{ .base = Scope{ .id = Id.FnDef, .parent = parent, @@ -173,7 +173,7 @@ pub const Scope = struct { } pub fn destroy(self: *FnDef, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -182,7 +182,7 @@ pub const Scope = struct { /// Creates a CompTime scope with 1 reference pub fn create(comp: *Compilation, parent: ?*Scope) !*CompTime { - const self = try comp.a().create(CompTime{ + const self = try comp.gpa().create(CompTime{ .base = Scope{ .id = Id.CompTime, .parent = parent, @@ -195,7 +195,7 @@ pub const Scope = struct { } pub fn destroy(self: *CompTime, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -216,7 +216,7 @@ pub const Scope = struct { kind: Kind, defer_expr_scope: *DeferExpr, ) !*Defer { - const self = try comp.a().create(Defer{ + const self = try comp.gpa().create(Defer{ .base = Scope{ .id = Id.Defer, .parent = parent, @@ -225,7 +225,7 @@ pub const Scope = struct { .defer_expr_scope = defer_expr_scope, .kind = kind, }); - errdefer comp.a().destroy(self); + errdefer comp.gpa().destroy(self); defer_expr_scope.base.ref(); @@ -235,7 +235,7 @@ pub const Scope = struct { pub fn destroy(self: *Defer, comp: *Compilation) void { self.defer_expr_scope.base.deref(comp); - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -245,7 +245,7 @@ pub const Scope = struct { /// Creates a DeferExpr scope with 1 reference pub fn create(comp: *Compilation, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr { - const self = try comp.a().create(DeferExpr{ + const self = try comp.gpa().create(DeferExpr{ .base = Scope{ .id = Id.DeferExpr, .parent = parent, @@ -253,14 +253,14 @@ pub const Scope = struct { }, .expr_node = expr_node, }); - errdefer comp.a().destroy(self); + errdefer comp.gpa().destroy(self); if (parent) |p| p.ref(); return self; } pub fn destroy(self: *DeferExpr, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; }; diff --git a/src-self-hosted/target.zig b/src-self-hosted/target.zig index 724d99ea23..db673e421a 100644 --- a/src-self-hosted/target.zig +++ b/src-self-hosted/target.zig @@ -1,60 +1,118 @@ +const std = @import("std"); const builtin = @import("builtin"); -const c = @import("c.zig"); - -pub const CrossTarget = struct { - arch: builtin.Arch, - os: builtin.Os, - environ: builtin.Environ, -}; +const llvm = @import("llvm.zig"); pub const Target = union(enum) { Native, - Cross: CrossTarget, + Cross: Cross, - pub fn oFileExt(self: *const Target) []const u8 { - const environ = switch (self.*) { - Target.Native => builtin.environ, - Target.Cross => |t| t.environ, - }; - return switch (environ) { - builtin.Environ.msvc => ".obj", + pub const Cross = struct { + arch: builtin.Arch, + os: builtin.Os, + environ: builtin.Environ, + object_format: builtin.ObjectFormat, + }; + + pub fn oFileExt(self: Target) []const u8 { + return switch (self.getObjectFormat()) { + builtin.ObjectFormat.coff => ".obj", else => ".o", }; } - pub fn exeFileExt(self: *const Target) []const u8 { + pub fn exeFileExt(self: Target) []const u8 { return switch (self.getOs()) { builtin.Os.windows => ".exe", else => "", }; } - pub fn getOs(self: *const Target) builtin.Os { - return switch (self.*) { + pub fn getOs(self: Target) builtin.Os { + return switch (self) { Target.Native => builtin.os, - Target.Cross => |t| t.os, + @TagType(Target).Cross => |t| t.os, + }; + } + + pub fn getArch(self: Target) builtin.Arch { + return switch (self) { + Target.Native => builtin.arch, + @TagType(Target).Cross => |t| t.arch, + }; + } + + pub fn getEnviron(self: Target) builtin.Environ { + return switch (self) { + Target.Native => builtin.environ, + @TagType(Target).Cross => |t| t.environ, + }; + } + + pub fn getObjectFormat(self: Target) builtin.ObjectFormat { + return switch (self) { + Target.Native => builtin.object_format, + @TagType(Target).Cross => |t| t.object_format, }; } - pub fn isDarwin(self: *const Target) bool { + pub fn isWasm(self: Target) bool { + return switch (self.getArch()) { + builtin.Arch.wasm32, builtin.Arch.wasm64 => true, + else => false, + }; + } + + pub fn isDarwin(self: Target) bool { return switch (self.getOs()) { builtin.Os.ios, builtin.Os.macosx => true, else => false, }; } - pub fn isWindows(self: *const Target) bool { + pub fn isWindows(self: Target) bool { return switch (self.getOs()) { builtin.Os.windows => true, else => false, }; } -}; -pub fn initializeAll() void { - c.LLVMInitializeAllTargets(); - c.LLVMInitializeAllTargetInfos(); - c.LLVMInitializeAllTargetMCs(); - c.LLVMInitializeAllAsmPrinters(); - c.LLVMInitializeAllAsmParsers(); -} + pub fn initializeAll() void { + llvm.InitializeAllTargets(); + llvm.InitializeAllTargetInfos(); + llvm.InitializeAllTargetMCs(); + llvm.InitializeAllAsmPrinters(); + llvm.InitializeAllAsmParsers(); + } + + pub fn getTriple(self: Target, allocator: *std.mem.Allocator) !std.Buffer { + var result = try std.Buffer.initSize(allocator, 0); + errdefer result.deinit(); + + // LLVM WebAssembly output support requires the target to be activated at + // build type with -DCMAKE_LLVM_EXPIERMENTAL_TARGETS_TO_BUILD=WebAssembly. + // + // LLVM determines the output format based on the environment suffix, + // defaulting to an object based on the architecture. The default format in + // LLVM 6 sets the wasm arch output incorrectly to ELF. We need to + // explicitly set this ourself in order for it to work. + // + // This is fixed in LLVM 7 and you will be able to get wasm output by + // using the target triple `wasm32-unknown-unknown-unknown`. + const env_name = if (self.isWasm()) "wasm" else @tagName(self.getEnviron()); + + var out = &std.io.BufferOutStream.init(&result).stream; + try out.print("{}-unknown-{}-{}", @tagName(self.getArch()), @tagName(self.getOs()), env_name); + + return result; + } + + pub fn llvmTargetFromTriple(triple: std.Buffer) !llvm.TargetRef { + var result: llvm.TargetRef = undefined; + var err_msg: [*]u8 = undefined; + if (llvm.GetTargetFromTriple(triple.ptr(), &result, &err_msg) != 0) { + std.debug.warn("triple: {s} error: {s}\n", triple.ptr(), err_msg); + return error.UnsupportedTarget; + } + return result; + } +}; diff --git a/src-self-hosted/test.zig b/src-self-hosted/test.zig index 3edb267ca9..45e5362124 100644 --- a/src-self-hosted/test.zig +++ b/src-self-hosted/test.zig @@ -46,7 +46,7 @@ pub const TestContext = struct { try self.loop.initMultiThreaded(allocator); errdefer self.loop.deinit(); - self.event_loop_local = EventLoopLocal.init(&self.loop); + self.event_loop_local = try EventLoopLocal.init(&self.loop); errdefer self.event_loop_local.deinit(); self.group = std.event.Group(error!void).init(&self.loop); @@ -107,6 +107,7 @@ pub const TestContext = struct { Target.Native, Compilation.Kind.Obj, builtin.Mode.Debug, + true, // is_static self.zig_lib_dir, self.zig_cache_dir, ); diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 670547cce2..bb1fb9bb01 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -160,7 +160,7 @@ pub const Type = struct { decls: *Scope.Decls, pub fn destroy(self: *Struct, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Struct, ofile: *ObjectFile) llvm.TypeRef { @@ -180,7 +180,7 @@ pub const Type = struct { }; pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn { - const result = try comp.a().create(Fn{ + const result = try comp.gpa().create(Fn{ .base = Type{ .base = Value{ .id = Value.Id.Type, @@ -193,7 +193,7 @@ pub const Type = struct { .params = params, .is_var_args = is_var_args, }); - errdefer comp.a().destroy(result); + errdefer comp.gpa().destroy(result); result.return_type.base.ref(); for (result.params) |param| { @@ -207,7 +207,7 @@ pub const Type = struct { for (self.params) |param| { param.typeof.base.deref(comp); } - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Fn, ofile: *ObjectFile) !llvm.TypeRef { @@ -215,8 +215,8 @@ pub const Type = struct { Type.Id.Void => llvm.VoidTypeInContext(ofile.context) orelse return error.OutOfMemory, else => try self.return_type.getLlvmType(ofile), }; - const llvm_param_types = try ofile.a().alloc(llvm.TypeRef, self.params.len); - defer ofile.a().free(llvm_param_types); + const llvm_param_types = try ofile.gpa().alloc(llvm.TypeRef, self.params.len); + defer ofile.gpa().free(llvm_param_types); for (llvm_param_types) |*llvm_param_type, i| { llvm_param_type.* = try self.params[i].typeof.getLlvmType(ofile); } @@ -241,7 +241,7 @@ pub const Type = struct { } pub fn destroy(self: *MetaType, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -255,7 +255,7 @@ pub const Type = struct { } pub fn destroy(self: *Void, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -269,7 +269,7 @@ pub const Type = struct { } pub fn destroy(self: *Bool, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Bool, ofile: *ObjectFile) llvm.TypeRef { @@ -287,7 +287,7 @@ pub const Type = struct { } pub fn destroy(self: *NoReturn, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -295,7 +295,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Int, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Int, ofile: *ObjectFile) llvm.TypeRef { @@ -307,7 +307,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Float, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Float, ofile: *ObjectFile) llvm.TypeRef { @@ -332,7 +332,7 @@ pub const Type = struct { pub const Size = builtin.TypeInfo.Pointer.Size; pub fn destroy(self: *Pointer, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn get( @@ -355,7 +355,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Array, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Array, ofile: *ObjectFile) llvm.TypeRef { @@ -367,7 +367,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *ComptimeFloat, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -375,7 +375,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *ComptimeInt, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -383,7 +383,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Undefined, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -391,7 +391,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Null, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -399,7 +399,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Optional, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Optional, ofile: *ObjectFile) llvm.TypeRef { @@ -411,7 +411,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *ErrorUnion, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *ErrorUnion, ofile: *ObjectFile) llvm.TypeRef { @@ -423,7 +423,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *ErrorSet, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *ErrorSet, ofile: *ObjectFile) llvm.TypeRef { @@ -435,7 +435,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Enum, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Enum, ofile: *ObjectFile) llvm.TypeRef { @@ -447,7 +447,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Union, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Union, ofile: *ObjectFile) llvm.TypeRef { @@ -459,7 +459,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Namespace, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -467,7 +467,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Block, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -475,7 +475,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *BoundFn, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *BoundFn, ofile: *ObjectFile) llvm.TypeRef { @@ -487,7 +487,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *ArgTuple, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -495,7 +495,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Opaque, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Opaque, ofile: *ObjectFile) llvm.TypeRef { @@ -507,7 +507,7 @@ pub const Type = struct { base: Type, pub fn destroy(self: *Promise, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmType(self: *Promise, ofile: *ObjectFile) llvm.TypeRef { diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index e3b91d2807..be19c6bccf 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -4,6 +4,7 @@ const Scope = @import("scope.zig").Scope; const Compilation = @import("compilation.zig").Compilation; const ObjectFile = @import("codegen.zig").ObjectFile; const llvm = @import("llvm.zig"); +const Buffer = std.Buffer; /// Values are ref-counted, heap-allocated, and copy-on-write /// If there is only 1 ref then write need not copy @@ -68,7 +69,7 @@ pub const Value = struct { /// The main external name that is used in the .o file. /// TODO https://github.com/ziglang/zig/issues/265 - symbol_name: std.Buffer, + symbol_name: Buffer, /// parent should be the top level decls or container decls fndef_scope: *Scope.FnDef, @@ -79,10 +80,22 @@ pub const Value = struct { /// parent is child_scope block_scope: *Scope.Block, + /// Path to the object file that contains this function + containing_object: Buffer, + + link_set_node: *std.LinkedList(?*Value.Fn).Node, + /// Creates a Fn value with 1 ref /// Takes ownership of symbol_name - pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: std.Buffer) !*Fn { - const self = try comp.a().create(Fn{ + pub fn create(comp: *Compilation, fn_type: *Type.Fn, fndef_scope: *Scope.FnDef, symbol_name: Buffer) !*Fn { + const link_set_node = try comp.gpa().create(Compilation.FnLinkSet.Node{ + .data = null, + .next = undefined, + .prev = undefined, + }); + errdefer comp.gpa().destroy(link_set_node); + + const self = try comp.gpa().create(Fn{ .base = Value{ .id = Value.Id.Fn, .typeof = &fn_type.base, @@ -92,6 +105,8 @@ pub const Value = struct { .child_scope = &fndef_scope.base, .block_scope = undefined, .symbol_name = symbol_name, + .containing_object = Buffer.initNull(comp.gpa()), + .link_set_node = link_set_node, }); fn_type.base.base.ref(); fndef_scope.fn_val = self; @@ -100,9 +115,19 @@ pub const Value = struct { } pub fn destroy(self: *Fn, comp: *Compilation) void { + // remove with a tombstone so that we do not have to grab a lock + if (self.link_set_node.data != null) { + // it's now the job of the link step to find this tombstone and + // deallocate it. + self.link_set_node.data = null; + } else { + comp.gpa().destroy(self.link_set_node); + } + + self.containing_object.deinit(); self.fndef_scope.base.deref(comp); self.symbol_name.deinit(); - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -115,7 +140,7 @@ pub const Value = struct { } pub fn destroy(self: *Void, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -134,7 +159,7 @@ pub const Value = struct { } pub fn destroy(self: *Bool, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } pub fn getLlvmConst(self: *Bool, ofile: *ObjectFile) ?llvm.ValueRef { @@ -156,7 +181,7 @@ pub const Value = struct { } pub fn destroy(self: *NoReturn, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; @@ -170,7 +195,7 @@ pub const Value = struct { }; pub fn destroy(self: *Ptr, comp: *Compilation) void { - comp.a().destroy(self); + comp.gpa().destroy(self); } }; }; diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 24f2a8a343..a43d2d182c 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -440,6 +440,11 @@ ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unreso return reinterpret_cast(di_builder); } +void ZigLLVMDisposeDIBuilder(ZigLLVMDIBuilder *dbuilder) { + DIBuilder *di_builder = reinterpret_cast(dbuilder); + delete di_builder; +} + void ZigLLVMSetCurrentDebugLocation(LLVMBuilderRef builder, int line, int column, ZigLLVMDIScope *scope) { unwrap(builder)->SetCurrentDebugLocation(DebugLoc::get( line, column, reinterpret_cast(scope))); diff --git a/src/zig_llvm.h b/src/zig_llvm.h index d34300b8ae..6f25df8674 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -39,7 +39,7 @@ struct ZigLLVMInsertionPoint; ZIG_EXTERN_C void ZigLLVMInitializeLoopStrengthReducePass(LLVMPassRegistryRef R); ZIG_EXTERN_C void ZigLLVMInitializeLowerIntrinsicsPass(LLVMPassRegistryRef R); -/// Caller must free memory. +/// Caller must free memory with LLVMDisposeMessage ZIG_EXTERN_C char *ZigLLVMGetHostCPUName(void); ZIG_EXTERN_C char *ZigLLVMGetNativeFeatures(void); @@ -139,6 +139,7 @@ ZIG_EXTERN_C unsigned ZigLLVMTag_DW_enumeration_type(void); ZIG_EXTERN_C unsigned ZigLLVMTag_DW_union_type(void); ZIG_EXTERN_C struct ZigLLVMDIBuilder *ZigLLVMCreateDIBuilder(LLVMModuleRef module, bool allow_unresolved); +ZIG_EXTERN_C void ZigLLVMDisposeDIBuilder(struct ZigLLVMDIBuilder *dbuilder); ZIG_EXTERN_C void ZigLLVMAddModuleDebugInfoFlag(LLVMModuleRef module); ZIG_EXTERN_C void ZigLLVMAddModuleCodeViewFlag(LLVMModuleRef module); diff --git a/std/atomic/int.zig b/std/atomic/int.zig index d51454c673..4103d52719 100644 --- a/std/atomic/int.zig +++ b/std/atomic/int.zig @@ -25,5 +25,9 @@ pub fn Int(comptime T: type) type { pub fn get(self: *Self) T { return @atomicLoad(T, &self.unprotected_value, AtomicOrder.SeqCst); } + + pub fn xchg(self: *Self, new_value: T) T { + return @atomicRmw(T, &self.unprotected_value, builtin.AtomicRmwOp.Xchg, new_value, AtomicOrder.SeqCst); + } }; } diff --git a/std/buffer.zig b/std/buffer.zig index aff7fa86ef..3b58002aba 100644 --- a/std/buffer.zig +++ b/std/buffer.zig @@ -54,6 +54,19 @@ pub const Buffer = struct { return result; } + pub fn allocPrint(allocator: *Allocator, comptime format: []const u8, args: ...) !Buffer { + const countSize = struct { + fn countSize(size: *usize, bytes: []const u8) (error{}!void) { + size.* += bytes.len; + } + }.countSize; + var size: usize = 0; + std.fmt.format(&size, error{}, countSize, format, args) catch |err| switch (err) {}; + var self = try Buffer.initSize(allocator, size); + assert((std.fmt.bufPrint(self.list.items, format, args) catch unreachable).len == size); + return self; + } + pub fn deinit(self: *Buffer) void { self.list.deinit(); } diff --git a/std/dwarf.zig b/std/dwarf.zig index 76ed122447..2cf8ed953e 100644 --- a/std/dwarf.zig +++ b/std/dwarf.zig @@ -639,3 +639,40 @@ pub const LNE_define_file = 0x03; pub const LNE_set_discriminator = 0x04; pub const LNE_lo_user = 0x80; pub const LNE_hi_user = 0xff; + +pub const LANG_C89 = 0x0001; +pub const LANG_C = 0x0002; +pub const LANG_Ada83 = 0x0003; +pub const LANG_C_plus_plus = 0x0004; +pub const LANG_Cobol74 = 0x0005; +pub const LANG_Cobol85 = 0x0006; +pub const LANG_Fortran77 = 0x0007; +pub const LANG_Fortran90 = 0x0008; +pub const LANG_Pascal83 = 0x0009; +pub const LANG_Modula2 = 0x000a; +pub const LANG_Java = 0x000b; +pub const LANG_C99 = 0x000c; +pub const LANG_Ada95 = 0x000d; +pub const LANG_Fortran95 = 0x000e; +pub const LANG_PLI = 0x000f; +pub const LANG_ObjC = 0x0010; +pub const LANG_ObjC_plus_plus = 0x0011; +pub const LANG_UPC = 0x0012; +pub const LANG_D = 0x0013; +pub const LANG_Python = 0x0014; +pub const LANG_Go = 0x0016; +pub const LANG_C_plus_plus_11 = 0x001a; +pub const LANG_Rust = 0x001c; +pub const LANG_C11 = 0x001d; +pub const LANG_C_plus_plus_14 = 0x0021; +pub const LANG_Fortran03 = 0x0022; +pub const LANG_Fortran08 = 0x0023; +pub const LANG_lo_user = 0x8000; +pub const LANG_hi_user = 0xffff; +pub const LANG_Mips_Assembler = 0x8001; +pub const LANG_Upc = 0x8765; +pub const LANG_HP_Bliss = 0x8003; +pub const LANG_HP_Basic91 = 0x8004; +pub const LANG_HP_Pascal91 = 0x8005; +pub const LANG_HP_IMacro = 0x8006; +pub const LANG_HP_Assembler = 0x8007; diff --git a/std/event/future.zig b/std/event/future.zig index 0f27b4131b..f5d14d1ca6 100644 --- a/std/event/future.zig +++ b/std/event/future.zig @@ -6,15 +6,20 @@ const AtomicOrder = builtin.AtomicOrder; const Lock = std.event.Lock; const Loop = std.event.Loop; -/// This is a value that starts out unavailable, until a value is put(). +/// This is a value that starts out unavailable, until resolve() is called /// While it is unavailable, coroutines suspend when they try to get() it, -/// and then are resumed when the value is put(). -/// At this point the value remains forever available, and another put() is not allowed. +/// and then are resumed when resolve() is called. +/// At this point the value remains forever available, and another resolve() is not allowed. pub fn Future(comptime T: type) type { return struct { lock: Lock, data: T, - available: u8, // TODO make this a bool + + /// TODO make this an enum + /// 0 - not started + /// 1 - started + /// 2 - finished + available: u8, const Self = this; const Queue = std.atomic.Queue(promise); @@ -31,7 +36,7 @@ pub fn Future(comptime T: type) type { /// available. /// Thread-safe. pub async fn get(self: *Self) *T { - if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) { + if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) { return &self.data; } const held = await (async self.lock.acquire() catch unreachable); @@ -43,18 +48,36 @@ pub fn Future(comptime T: type) type { /// Gets the data without waiting for it. If it's available, a pointer is /// returned. Otherwise, null is returned. pub fn getOrNull(self: *Self) ?*T { - if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 1) { + if (@atomicLoad(u8, &self.available, AtomicOrder.SeqCst) == 2) { return &self.data; } else { return null; } } + /// If someone else has started working on the data, wait for them to complete + /// and return a pointer to the data. Otherwise, return null, and the caller + /// should start working on the data. + /// It's not required to call start() before resolve() but it can be useful since + /// this method is thread-safe. + pub async fn start(self: *Self) ?*T { + const state = @cmpxchgStrong(u8, &self.available, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null; + switch (state) { + 1 => { + const held = await (async self.lock.acquire() catch unreachable); + held.release(); + return &self.data; + }, + 2 => return &self.data, + else => unreachable, + } + } + /// Make the data become available. May be called only once. /// Before calling this, modify the `data` property. pub fn resolve(self: *Self) void { - const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 1, AtomicOrder.SeqCst); - assert(prev == 0); // put() called twice + const prev = @atomicRmw(u8, &self.available, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst); + assert(prev == 0 or prev == 1); // resolve() called twice Lock.Held.release(Lock.Held{ .lock = &self.lock }); } }; diff --git a/std/index.zig b/std/index.zig index 3b523f519f..2f4cfb7553 100644 --- a/std/index.zig +++ b/std/index.zig @@ -36,6 +36,8 @@ pub const sort = @import("sort.zig"); pub const unicode = @import("unicode.zig"); pub const zig = @import("zig/index.zig"); +pub const lazyInit = @import("lazy_init.zig").lazyInit; + test "std" { // run tests from these _ = @import("atomic/index.zig"); @@ -71,4 +73,5 @@ test "std" { _ = @import("sort.zig"); _ = @import("unicode.zig"); _ = @import("zig/index.zig"); + _ = @import("lazy_init.zig"); } diff --git a/std/lazy_init.zig b/std/lazy_init.zig new file mode 100644 index 0000000000..c46c067810 --- /dev/null +++ b/std/lazy_init.zig @@ -0,0 +1,85 @@ +const std = @import("index.zig"); +const builtin = @import("builtin"); +const assert = std.debug.assert; +const AtomicRmwOp = builtin.AtomicRmwOp; +const AtomicOrder = builtin.AtomicOrder; + +/// Thread-safe initialization of global data. +/// TODO use a mutex instead of a spinlock +pub fn lazyInit(comptime T: type) LazyInit(T) { + return LazyInit(T){ + .data = undefined, + .state = 0, + }; +} + +fn LazyInit(comptime T: type) type { + return struct { + state: u8, // TODO make this an enum + data: Data, + + const Self = this; + + // TODO this isn't working for void, investigate and then remove this special case + const Data = if (@sizeOf(T) == 0) u8 else T; + const Ptr = if (T == void) void else *T; + + /// Returns a usable pointer to the initialized data, + /// or returns null, indicating that the caller should + /// perform the initialization and then call resolve(). + pub fn get(self: *Self) ?Ptr { + while (true) { + var state = @cmpxchgWeak(u8, &self.state, 0, 1, AtomicOrder.SeqCst, AtomicOrder.SeqCst) orelse return null; + switch (state) { + 0 => continue, + 1 => { + // TODO mutex instead of a spinlock + continue; + }, + 2 => { + if (@sizeOf(T) == 0) { + return T(undefined); + } else { + return &self.data; + } + }, + else => unreachable, + } + } + } + + pub fn resolve(self: *Self) void { + const prev = @atomicRmw(u8, &self.state, AtomicRmwOp.Xchg, 2, AtomicOrder.SeqCst); + assert(prev == 1); // resolve() called twice + } + }; +} + +var global_number = lazyInit(i32); + +test "std.lazyInit" { + if (global_number.get()) |_| @panic("bad") else { + global_number.data = 1234; + global_number.resolve(); + } + if (global_number.get()) |x| { + assert(x.* == 1234); + } else { + @panic("bad"); + } + if (global_number.get()) |x| { + assert(x.* == 1234); + } else { + @panic("bad"); + } +} + +var global_void = lazyInit(void); + +test "std.lazyInit(void)" { + if (global_void.get()) |_| @panic("bad") else { + global_void.resolve(); + } + assert(global_void.get() != null); + assert(global_void.get() != null); +} -- cgit v1.2.3