From d1855a0e931706d84a81c0213994067bd9f2fc7f Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 6 Sep 2018 12:24:12 +0900 Subject: [PATCH 01/20] std/crypto/x25519.zig: fix signature for `createPublicKey`; --- std/crypto/x25519.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/crypto/x25519.zig b/std/crypto/x25519.zig index 16ec09f66e8b771c1aeb0689f01cee5c3da64299..9a9df30962c0d19aef0d211e85d460f7a1756a1d 100644 --- a/std/crypto/x25519.zig +++ b/std/crypto/x25519.zig @@ -114,7 +114,7 @@ pub const X25519 = struct { return !zerocmp(u8, out); } - pub fn createPublicKey(public_key: []const u8, private_key: []const u8) bool { + pub fn createPublicKey(public_key: [] u8, private_key: []const u8) bool { var base_point = []u8{9} ++ []u8{0} ** 31; return create(public_key, private_key, base_point); } -- 2.54.0 From 15d30b967af3c4bd4d22a9e746c14b52b7974ba4 Mon Sep 17 00:00:00 2001 From: kristopher tate Date: Thu, 6 Sep 2018 12:24:53 +0900 Subject: [PATCH 02/20] std/crypto/x25519.zig: add test for `createPublicKey`; --- std/crypto/x25519.zig | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/std/crypto/x25519.zig b/std/crypto/x25519.zig index 9a9df30962c0d19aef0d211e85d460f7a1756a1d..41b2ff09599f8dd4580cdc6f4dbd0fa5ef18355e 100644 --- a/std/crypto/x25519.zig +++ b/std/crypto/x25519.zig @@ -4,6 +4,7 @@ const std = @import("../index.zig"); const builtin = @import("builtin"); +const fmt = std.fmt; const Endian = builtin.Endian; const readInt = std.mem.readInt; @@ -573,6 +574,16 @@ const Fe = struct { } }; +test "x25519 public key calculation from secret key" { + var sk: [32]u8 = undefined; + var pk_expected: [32]u8 = undefined; + var pk_calculated: [32]u8 = undefined; + try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166"); + try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50"); + std.debug.assert(X25519.createPublicKey(pk_calculated[0..], sk)); + std.debug.assert(std.mem.eql(u8, pk_calculated, pk_expected)); +} + test "x25519 rfc7748 vector1" { const secret_key = "\xa5\x46\xe3\x6b\xf0\x52\x7c\x9d\x3b\x16\x15\x4b\x82\x46\x5e\xdd\x62\x14\x4c\x0a\xc1\xfc\x5a\x18\x50\x6a\x22\x44\xba\x44\x9a\xc4"; const public_key = "\xe6\xdb\x68\x67\x58\x30\x30\xdb\x35\x94\xc1\xa4\x24\xb1\x5f\x7c\x72\x66\x24\xec\x26\xb3\x35\x3b\x10\xa9\x03\xa6\xd0\xab\x1c\x4c"; -- 2.54.0 From a375bd0d9f5c7773598a8ea31af8c963d6a79706 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 6 Sep 2018 11:58:58 -0400 Subject: [PATCH 03/20] stage1: compile error instead of incorrect code for unimplemented C ABI See #1411 See #1481 --- src/codegen.cpp | 90 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 62 insertions(+), 28 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 0300ccca993947b210ffedb3d1a6ca669616c755..20d700fbbddce7a321077c84cfcec2d71bdd1eac 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3111,6 +3111,51 @@ static void set_call_instr_sret(CodeGen *g, LLVMValueRef call_instr) { LLVMAddCallSiteAttribute(call_instr, 1, sret_attr); } +ATTRIBUTE_NORETURN +static void report_errors_and_exit(CodeGen *g) { + assert(g->errors.length != 0); + for (size_t i = 0; i < g->errors.length; i += 1) { + ErrorMsg *err = g->errors.at(i); + print_err_msg(err, g->err_color); + } + exit(1); +} + +static void report_errors_and_maybe_exit(CodeGen *g) { + if (g->errors.length != 0) { + report_errors_and_exit(g); + } +} + +ATTRIBUTE_NORETURN +static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { + ErrorMsg *msg = add_node_error(g, source_node, + buf_sprintf("TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481")); + add_error_note(g, msg, source_node, + buf_sprintf("pointers, integers, floats, bools, and enums work on all targets")); + report_errors_and_exit(g); +} + +static void gen_c_abi_param(CodeGen *g, ZigList *gen_param_values, LLVMValueRef val, + ZigType *ty, AstNode *source_node) +{ + if (ty->id == ZigTypeIdInt || + ty->id == ZigTypeIdFloat || + ty->id == ZigTypeIdBool || + ty->id == ZigTypeIdEnum || + get_codegen_ptr_type(ty) != nullptr) + { + gen_param_values->append(val); + return; + } + + if (g->zig_target.arch.arch == ZigLLVM_x86_64) { + give_up_with_c_abi_error(g, source_node); + } else { + give_up_with_c_abi_error(g, source_node); + } +} + static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCall *instruction) { LLVMValueRef fn_val; ZigType *fn_type; @@ -3128,29 +3173,25 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr ZigType *src_return_type = fn_type_id->return_type; bool ret_has_bits = type_has_bits(src_return_type); + CallingConvention cc = fn_type->data.fn.fn_type_id.cc; + bool is_c_abi = cc == CallingConventionC; + bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type) && - calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc); + calling_convention_does_first_arg_return(cc); bool prefix_arg_err_ret_stack = get_prefix_arg_err_ret_stack(g, fn_type_id); - // +2 for the async args - size_t actual_param_count = instruction->arg_count + (first_arg_ret ? 1 : 0) + (prefix_arg_err_ret_stack ? 1 : 0) + 2; bool is_var_args = fn_type_id->is_var_args; - LLVMValueRef *gen_param_values = allocate(actual_param_count); - size_t gen_param_index = 0; + ZigList gen_param_values = {}; if (first_arg_ret) { - gen_param_values[gen_param_index] = instruction->tmp_ptr; - gen_param_index += 1; + gen_param_values.append(instruction->tmp_ptr); } if (prefix_arg_err_ret_stack) { - gen_param_values[gen_param_index] = get_cur_err_ret_trace_val(g, instruction->base.scope); - gen_param_index += 1; + gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.scope)); } if (instruction->is_async) { - gen_param_values[gen_param_index] = ir_llvm_value(g, instruction->async_allocator); - gen_param_index += 1; + gen_param_values.append(ir_llvm_value(g, instruction->async_allocator)); LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_err_index, ""); - gen_param_values[gen_param_index] = err_val_ptr; - gen_param_index += 1; + gen_param_values.append(err_val_ptr); } for (size_t call_i = 0; call_i < instruction->arg_count; call_i += 1) { IrInstruction *param_instruction = instruction->args[call_i]; @@ -3158,8 +3199,11 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr if (is_var_args || type_has_bits(param_type)) { LLVMValueRef param_value = ir_llvm_value(g, param_instruction); assert(param_value); - gen_param_values[gen_param_index] = param_value; - gen_param_index += 1; + if (is_c_abi) { + gen_c_abi_param(g, &gen_param_values, param_value, param_type, param_instruction->source_node); + } else { + gen_param_values.append(param_value); + } } } @@ -3176,12 +3220,12 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr break; } - LLVMCallConv llvm_cc = get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc); + LLVMCallConv llvm_cc = get_llvm_cc(g, cc); LLVMValueRef result; if (instruction->new_stack == nullptr) { result = ZigLLVMBuildCall(g->builder, fn_val, - gen_param_values, (unsigned)gen_param_index, llvm_cc, fn_inline, ""); + gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, fn_inline, ""); } else { LLVMValueRef stacksave_fn_val = get_stacksave_fn_val(g); LLVMValueRef stackrestore_fn_val = get_stackrestore_fn_val(g); @@ -3190,7 +3234,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr LLVMValueRef old_stack_ref = LLVMBuildCall(g->builder, stacksave_fn_val, nullptr, 0, ""); gen_set_stack_pointer(g, new_stack_addr); result = ZigLLVMBuildCall(g->builder, fn_val, - gen_param_values, (unsigned)gen_param_index, llvm_cc, fn_inline, ""); + gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, fn_inline, ""); LLVMBuildCall(g->builder, stackrestore_fn_val, &old_stack_ref, 1, ""); } @@ -5735,16 +5779,6 @@ static void ensure_cache_dir(CodeGen *g) { } } -static void report_errors_and_maybe_exit(CodeGen *g) { - if (g->errors.length != 0) { - for (size_t i = 0; i < g->errors.length; i += 1) { - ErrorMsg *err = g->errors.at(i); - print_err_msg(err, g->err_color); - } - exit(1); - } -} - static void validate_inline_fns(CodeGen *g) { for (size_t i = 0; i < g->inline_fns.length; i += 1) { ZigFn *fn_entry = g->inline_fns.at(i); -- 2.54.0 From a9a925e500c66b8eb57d619b9b6828c70a13d4c5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 6 Sep 2018 16:29:35 -0400 Subject: [PATCH 04/20] add C ABI tests --- src/analyze.cpp | 122 +++++++++++++----- src/analyze.hpp | 2 + src/codegen.cpp | 242 +++++++++++++++++++++++++++++++++--- std/build.zig | 27 ++++ test/build_examples.zig | 6 + test/gen_h.zig | 28 ++++- test/stage1/c_abi/build.zig | 17 +++ test/stage1/c_abi/cfuncs.c | 64 ++++++++++ test/stage1/c_abi/main.zig | 58 +++++++++ 9 files changed, 515 insertions(+), 51 deletions(-) create mode 100644 test/stage1/c_abi/build.zig create mode 100644 test/stage1/c_abi/cfuncs.c create mode 100644 test/stage1/c_abi/main.zig diff --git a/src/analyze.cpp b/src/analyze.cpp index aa4fda76242e59a99a9e8f63ff2be6967bb5f529..7014c0ea9d95bf71251e76cf8c7acfbb66e9a007 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1061,6 +1061,77 @@ ZigType *get_ptr_to_stack_trace_type(CodeGen *g) { return g->ptr_to_stack_trace_type; } +bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { + size_t ty_size = type_size(g, ty); + if (ty_size > g->pointer_size_bytes) + return false; + return (ty->id == ZigTypeIdInt || + ty->id == ZigTypeIdFloat || + ty->id == ZigTypeIdBool || + ty->id == ZigTypeIdEnum || + get_codegen_ptr_type(ty) != nullptr); +} + +// If you edit this function you have to edit the corresponding code: +// codegen.cpp:gen_c_abi_param +// analyze.cpp:gen_c_abi_param_type +// codegen.cpp:gen_c_abi_param_var +// codegen.cpp:gen_c_abi_param_var_init +static void gen_c_abi_param_type(CodeGen *g, ZigList *gen_param_types, + ZigList *param_di_types, ZigType *ty) +{ + if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || + ty->id == ZigTypeIdInt // TODO investigate if we need to change this + ) { + gen_param_types->append(ty->type_ref); + param_di_types->append(ty->di_type); + return; + } + + // Arrays are just pointers + if (ty->id == ZigTypeIdArray) { + ZigType *gen_type = get_pointer_to_type(g, ty, true); + gen_param_types->append(gen_type->type_ref); + param_di_types->append(gen_type->di_type); + return; + } + + if (g->zig_target.arch.arch == ZigLLVM_x86_64) { + size_t ty_size = type_size(g, ty); + if (ty->id == ZigTypeIdStruct || ty->id == ZigTypeIdUnion) { + // "If the size of an object is larger than four eightbytes, or it contains unaligned + // fields, it has class MEMORY" + if (ty_size > 32) { + ZigType *gen_type = get_pointer_to_type(g, ty, true); + gen_param_types->append(gen_type->type_ref); + param_di_types->append(gen_type->di_type); + return; + } + } + if (ty->id == ZigTypeIdStruct) { + // "If the size of the aggregate exceeds a single eightbyte, each is classified + // separately. Each eightbyte gets initialized to class NO_CLASS." + if (ty_size <= 8) { + bool contains_int = false; + for (size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { + if (type_is_c_abi_int(g, ty->data.structure.fields[i].type_entry)) { + contains_int = true; + break; + } + } + if (contains_int) { + ZigType *gen_type = get_int_type(g, false, ty_size * 8); + gen_param_types->append(gen_type->type_ref); + param_di_types->append(gen_type->di_type); + return; + } + } + } + } + + // allow codegen code to report a compile error +} + ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { Error err; auto table_entry = g->fn_type_table.maybe_get(fn_type_id); @@ -1119,18 +1190,18 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) && handle_is_ptr(fn_type_id->return_type); bool is_async = fn_type_id->cc == CallingConventionAsync; + bool is_c_abi = fn_type_id->cc == CallingConventionC; bool prefix_arg_error_return_trace = g->have_err_ret_tracing && fn_type_can_fail(fn_type_id); // +1 for maybe making the first argument the return value // +1 for maybe first argument the error return trace // +2 for maybe arguments async allocator and error code pointer - LLVMTypeRef *gen_param_types = allocate(4 + fn_type_id->param_count); + ZigList gen_param_types = {}; // +1 because 0 is the return type and // +1 for maybe making first arg ret val and // +1 for maybe first argument the error return trace // +2 for maybe arguments async allocator and error code pointer - ZigLLVMDIType **param_di_types = allocate(5 + fn_type_id->param_count); - param_di_types[0] = fn_type_id->return_type->di_type; - size_t gen_param_index = 0; + ZigList param_di_types = {}; + param_di_types.append(fn_type_id->return_type->di_type); ZigType *gen_return_type; if (is_async) { gen_return_type = get_pointer_to_type(g, g->builtin_types.entry_u8, false); @@ -1138,10 +1209,8 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { gen_return_type = g->builtin_types.entry_void; } else if (first_arg_return) { ZigType *gen_type = get_pointer_to_type(g, fn_type_id->return_type, false); - gen_param_types[gen_param_index] = gen_type->type_ref; - gen_param_index += 1; - // after the gen_param_index += 1 because 0 is the return type - param_di_types[gen_param_index] = gen_type->di_type; + gen_param_types.append(gen_type->type_ref); + param_di_types.append(gen_type->di_type); gen_return_type = g->builtin_types.entry_void; } else { gen_return_type = fn_type_id->return_type; @@ -1150,28 +1219,22 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { if (prefix_arg_error_return_trace) { ZigType *gen_type = get_ptr_to_stack_trace_type(g); - gen_param_types[gen_param_index] = gen_type->type_ref; - gen_param_index += 1; - // after the gen_param_index += 1 because 0 is the return type - param_di_types[gen_param_index] = gen_type->di_type; + gen_param_types.append(gen_type->type_ref); + param_di_types.append(gen_type->di_type); } if (is_async) { { // async allocator param ZigType *gen_type = fn_type_id->async_allocator_type; - gen_param_types[gen_param_index] = gen_type->type_ref; - gen_param_index += 1; - // after the gen_param_index += 1 because 0 is the return type - param_di_types[gen_param_index] = gen_type->di_type; + gen_param_types.append(gen_type->type_ref); + param_di_types.append(gen_type->di_type); } { // error code pointer ZigType *gen_type = get_pointer_to_type(g, g->builtin_types.entry_global_error_set, false); - gen_param_types[gen_param_index] = gen_type->type_ref; - gen_param_index += 1; - // after the gen_param_index += 1 because 0 is the return type - param_di_types[gen_param_index] = gen_type->di_type; + gen_param_types.append(gen_type->type_ref); + param_di_types.append(gen_type->di_type); } } @@ -1187,7 +1250,9 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { if ((err = ensure_complete_type(g, type_entry))) return g->builtin_types.entry_invalid; - if (type_has_bits(type_entry)) { + if (is_c_abi) { + gen_c_abi_param_type(g, &gen_param_types, ¶m_di_types, type_entry); + } else if (type_has_bits(type_entry)) { ZigType *gen_type; if (handle_is_ptr(type_entry)) { gen_type = get_pointer_to_type(g, type_entry, true); @@ -1195,23 +1260,20 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { } else { gen_type = type_entry; } - gen_param_types[gen_param_index] = gen_type->type_ref; - gen_param_info->gen_index = gen_param_index; + gen_param_info->gen_index = gen_param_types.length; gen_param_info->type = gen_type; + gen_param_types.append(gen_type->type_ref); - gen_param_index += 1; - - // after the gen_param_index += 1 because 0 is the return type - param_di_types[gen_param_index] = gen_type->di_type; + param_di_types.append(gen_type->di_type); } } - fn_type->data.fn.gen_param_count = gen_param_index; + fn_type->data.fn.gen_param_count = gen_param_types.length; fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref, - gen_param_types, (unsigned int)gen_param_index, fn_type_id->is_var_args); + gen_param_types.items, (unsigned int)gen_param_types.length, fn_type_id->is_var_args); fn_type->type_ref = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0); - fn_type->di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types, (int)(gen_param_index + 1), 0); + fn_type->di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0); } g->fn_type_table.put(&fn_type->data.fn.fn_type_id, fn_type); diff --git a/src/analyze.hpp b/src/analyze.hpp index 41cc50916eac81e690343b6dbf960335f9b796ec..f13c528c5a94bd097495d0ba8b7e35670f355101 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -210,4 +210,6 @@ ZigType *get_primitive_type(CodeGen *g, Buf *name); bool calling_convention_allows_zig_types(CallingConvention cc); const char *calling_convention_name(CallingConvention cc); +bool type_is_c_abi_int(CodeGen *g, ZigType *ty); + #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 20d700fbbddce7a321077c84cfcec2d71bdd1eac..f35f146bb47aac04df652444f686fbe8f982b4b1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3136,24 +3136,226 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { report_errors_and_exit(g); } +static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { + assert(alignment > 0); + LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name); + LLVMSetAlignment(result, alignment); + return result; +} + +// If you edit this function you have to edit the corresponding code: +// codegen.cpp:gen_c_abi_param +// analyze.cpp:gen_c_abi_param_type +// codegen.cpp:gen_c_abi_param_var +// codegen.cpp:gen_c_abi_param_var_init static void gen_c_abi_param(CodeGen *g, ZigList *gen_param_values, LLVMValueRef val, ZigType *ty, AstNode *source_node) { - if (ty->id == ZigTypeIdInt || - ty->id == ZigTypeIdFloat || - ty->id == ZigTypeIdBool || - ty->id == ZigTypeIdEnum || - get_codegen_ptr_type(ty) != nullptr) - { + if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || + ty->id == ZigTypeIdInt // TODO investigate if we need to change this + ) { gen_param_values->append(val); return; } + // Arrays are just pointers + if (ty->id == ZigTypeIdArray) { + assert(handle_is_ptr(ty)); + gen_param_values->append(val); + return; + } + + if (g->zig_target.arch.arch == ZigLLVM_x86_64) { + // This code all assumes that val is a pointer. + assert(handle_is_ptr(ty)); + + size_t ty_size = type_size(g, ty); + if (ty->id == ZigTypeIdStruct || ty->id == ZigTypeIdUnion) { + // "If the size of an object is larger than four eightbytes, or it contains unaligned + // fields, it has class MEMORY" + if (ty_size > 32) { + gen_param_values->append(val); + return; + } + } + if (ty->id == ZigTypeIdStruct) { + // "If the size of the aggregate exceeds a single eightbyte, each is classified + // separately. Each eightbyte gets initialized to class NO_CLASS." + if (ty_size <= 8) { + bool contains_int = false; + for (size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { + if (type_is_c_abi_int(g, ty->data.structure.fields[i].type_entry)) { + contains_int = true; + break; + } + } + if (contains_int) { + LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); + LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, val, ptr_to_int_type_ref, ""); + LLVMValueRef loaded = LLVMBuildLoad(g->builder, bitcasted, ""); + gen_param_values->append(loaded); + return; + } + } + } + } + + give_up_with_c_abi_error(g, source_node); +} + +// If you edit this function you have to edit the corresponding code: +// codegen.cpp:gen_c_abi_param +// analyze.cpp:gen_c_abi_param_type +// codegen.cpp:gen_c_abi_param_var +// codegen.cpp:gen_c_abi_param_var_init +static void gen_c_abi_param_var(CodeGen *g, ImportTableEntry *import, LLVMValueRef llvm_fn, ZigFn *fn, + ZigVar *var, unsigned *arg_index) +{ + ZigType *ty = var->value->type; + + ZigType *dest_ty = nullptr; + unsigned di_arg_index; + + if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || + ty->id == ZigTypeIdInt // TODO investigate if we need to change this + ) { + var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes); + di_arg_index = *arg_index; + *arg_index += 1; + dest_ty = ty; + goto ok; + } + + // Arrays are just pointers + if (ty->id == ZigTypeIdArray) { + di_arg_index = *arg_index; + var->value_ref = LLVMGetParam(llvm_fn, *arg_index); + dest_ty = get_pointer_to_type(g, ty, false); + *arg_index += 1; + goto ok; + } + if (g->zig_target.arch.arch == ZigLLVM_x86_64) { - give_up_with_c_abi_error(g, source_node); - } else { - give_up_with_c_abi_error(g, source_node); + assert(handle_is_ptr(ty)); + size_t ty_size = type_size(g, ty); + + if (ty->id == ZigTypeIdStruct || ty->id == ZigTypeIdUnion) { + // "If the size of an object is larger than four eightbytes, or it contains unaligned + // fields, it has class MEMORY" + if (ty_size > 32) { + di_arg_index = *arg_index; + var->value_ref = LLVMGetParam(llvm_fn, *arg_index); + dest_ty = get_pointer_to_type(g, ty, false); + *arg_index += 1; + goto ok; + } + } + if (ty->id == ZigTypeIdStruct) { + // "If the size of the aggregate exceeds a single eightbyte, each is classified + // separately. Each eightbyte gets initialized to class NO_CLASS." + if (ty_size <= 8) { + bool contains_int = false; + for (size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { + if (type_is_c_abi_int(g, ty->data.structure.fields[i].type_entry)) { + contains_int = true; + break; + } + } + if (contains_int) { + var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes); + *arg_index += 1; + goto ok; + } + } + } } + + give_up_with_c_abi_error(g, fn->proto_node); + +ok: + if (dest_ty != nullptr && var->decl_node) { + // arg index + 1 because the 0 index is return value + var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), + buf_ptr(&var->name), import->di_file, + (unsigned)(var->decl_node->line + 1), + dest_ty->di_type, !g->strip_debug_symbols, 0, di_arg_index + 1); + } +} + +// If you edit this function you have to edit the corresponding code: +// codegen.cpp:gen_c_abi_param +// analyze.cpp:gen_c_abi_param_type +// codegen.cpp:gen_c_abi_param_var +// codegen.cpp:gen_c_abi_param_var_init +static void gen_c_abi_param_var_init(CodeGen *g, ImportTableEntry *import, LLVMValueRef llvm_fn, ZigFn *fn, + ZigVar *var, unsigned *arg_index) +{ + ZigType *ty = var->value->type; + + if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || + ty->id == ZigTypeIdInt // TODO investigate if we need to change this + ) { + clear_debug_source_node(g); + gen_store_untyped(g, LLVMGetParam(llvm_fn, *arg_index), var->value_ref, var->align_bytes, false); + if (var->decl_node) { + gen_var_debug_decl(g, var); + } + *arg_index += 1; + return; + } + + // Arrays are just pointers + if (ty->id == ZigTypeIdArray) { + if (var->decl_node) { + gen_var_debug_decl(g, var); + } + *arg_index += 1; + return; + } + + if (g->zig_target.arch.arch == ZigLLVM_x86_64) { + assert(handle_is_ptr(ty)); + size_t ty_size = type_size(g, ty); + + if (ty->id == ZigTypeIdStruct || ty->id == ZigTypeIdUnion) { + // "If the size of an object is larger than four eightbytes, or it contains unaligned + // fields, it has class MEMORY" + if (ty_size > 32) { + if (var->decl_node) { + gen_var_debug_decl(g, var); + } + *arg_index += 1; + return; + } + } + if (ty->id == ZigTypeIdStruct) { + // "If the size of the aggregate exceeds a single eightbyte, each is classified + // separately. Each eightbyte gets initialized to class NO_CLASS." + if (ty_size <= 8) { + bool contains_int = false; + for (size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { + if (type_is_c_abi_int(g, ty->data.structure.fields[i].type_entry)) { + contains_int = true; + break; + } + } + if (contains_int) { + clear_debug_source_node(g); + LLVMValueRef arg = LLVMGetParam(llvm_fn, *arg_index); + LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); + LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); + gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); + if (var->decl_node) { + gen_var_debug_decl(g, var); + } + *arg_index += 1; + return; + } + } + } + } + + give_up_with_c_abi_error(g, fn->proto_node); } static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCall *instruction) { @@ -5765,13 +5967,6 @@ static void gen_global_var(CodeGen *g, ZigVar *var, LLVMValueRef init_val, // TODO ^^ make an actual global variable } -static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { - assert(alignment > 0); - LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name); - LLVMSetAlignment(result, alignment); - return result; -} - static void ensure_cache_dir(CodeGen *g) { int err; if ((err = os_make_path(&g->cache_dir))) { @@ -5899,6 +6094,8 @@ static void do_code_gen(CodeGen *g) { // Generate function definitions. for (size_t fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) { ZigFn *fn_table_entry = g->fn_defs.at(fn_i); + CallingConvention cc = fn_table_entry->type_entry->data.fn.fn_type_id.cc; + bool is_c_abi = cc == CallingConventionC; LLVMValueRef fn = fn_llvm_value(g, fn_table_entry); g->cur_fn = fn_table_entry; @@ -5922,7 +6119,7 @@ static void do_code_gen(CodeGen *g) { } // error return tracing setup - bool is_async = fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync; + bool is_async = cc == CallingConventionAsync; bool have_err_ret_trace_stack = g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn && !is_async && !have_err_ret_trace_arg; LLVMValueRef err_ret_array_val = nullptr; if (have_err_ret_trace_stack) { @@ -5982,6 +6179,7 @@ static void do_code_gen(CodeGen *g) { ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); // create debug variable declarations for variables and allocate all local variables + unsigned c_abi_arg_index = 0; for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) { ZigVar *var = fn_table_entry->variable_list.at(var_i); @@ -6000,6 +6198,8 @@ static void do_code_gen(CodeGen *g) { buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1), var->value->type->di_type, !g->strip_debug_symbols, 0); + } else if (is_c_abi) { + gen_c_abi_param_var(g, import, fn, fn_table_entry, var, &c_abi_arg_index); } else { assert(var->gen_arg_index != SIZE_MAX); ZigType *gen_type; @@ -6056,7 +6256,14 @@ static void do_code_gen(CodeGen *g) { // create debug variable declarations for parameters // rely on the first variables in the variable_list being parameters. size_t next_var_i = 0; + unsigned c_abi_arg_init_index = 0; for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { + if (is_c_abi) { + ZigVar *var = fn_table_entry->variable_list.at(param_i); + gen_c_abi_param_var_init(g, import, fn, fn_table_entry, var, &c_abi_arg_init_index); + continue; + } + FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i]; if (info->gen_index == SIZE_MAX) continue; @@ -6078,6 +6285,7 @@ static void do_code_gen(CodeGen *g) { gen_var_debug_decl(g, variable); } } + assert(c_abi_arg_index == c_abi_arg_init_index); ir_render(g, fn_table_entry); diff --git a/std/build.zig b/std/build.zig index 08bb5635d9f57e8fa0b4c9b8f59303c7a9f48121..4e323eaf7be1e9803a879daa21ba1e63b46297b1 100644 --- a/std/build.zig +++ b/std/build.zig @@ -48,6 +48,12 @@ pub const Builder = struct { cache_root: []const u8, release_mode: ?builtin.Mode, + pub const CStd = enum { + C89, + C99, + C11, + }; + const UserInputOptionsMap = HashMap([]const u8, UserInputOption, mem.hash_slice_u8, mem.eql_slice_u8); const AvailableOptionsMap = HashMap([]const u8, AvailableOption, mem.hash_slice_u8, mem.eql_slice_u8); @@ -817,6 +823,7 @@ pub const LibExeObjStep = struct { frameworks: BufSet, verbose_link: bool, no_rosegment: bool, + c_std: Builder.CStd, // zig only stuff root_src: ?[]const u8, @@ -918,6 +925,7 @@ pub const LibExeObjStep = struct { .object_src = undefined, .disable_libc = true, .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable, + .c_std = Builder.CStd.C99, }; self.computeOutFileNames(); return self; @@ -952,6 +960,7 @@ pub const LibExeObjStep = struct { .disable_libc = false, .is_zig = false, .linker_script = null, + .c_std = Builder.CStd.C99, .root_src = undefined, .verbose_link = false, @@ -1392,6 +1401,13 @@ pub const LibExeObjStep = struct { const is_darwin = self.target.isDarwin(); + const c_std_arg = switch (self.c_std) { + Builder.CStd.C89 => "-std=c89", + Builder.CStd.C99 => "-std=c99", + Builder.CStd.C11 => "-std=c11", + }; + try cc_args.append(c_std_arg); + switch (self.kind) { Kind.Obj => { cc_args.append("-c") catch unreachable; @@ -1678,6 +1694,17 @@ pub const TestStep = struct { self.filter = text; } + pub fn addObject(self: *TestStep, obj: *LibExeObjStep) void { + assert(obj.kind == LibExeObjStep.Kind.Obj); + + self.step.dependOn(&obj.step); + + self.object_files.append(obj.getOutputPath()) catch unreachable; + + // TODO should be some kind of isolated directory that only has this header in it + self.include_dirs.append(self.builder.cache_root) catch unreachable; + } + pub fn addObjectFile(self: *TestStep, path: []const u8) void { self.object_files.append(path) catch unreachable; } diff --git a/test/build_examples.zig b/test/build_examples.zig index 79192c3e9aacd872cab3768af5a66affa651df65..96112fe6875b9bd56dcc72a8521d465d441cf7ab 100644 --- a/test/build_examples.zig +++ b/test/build_examples.zig @@ -28,4 +28,10 @@ pub fn addCases(cases: *tests.BuildExamplesContext) void { // TODO figure out how to make this work on darwin - probably libSystem has dlopen/dlsym in it cases.addBuildFile("test/standalone/load_dynamic_library/build.zig"); } + + if (!is_windows // TODO support compiling C files on windows with zig build system + and builtin.arch == builtin.Arch.x86_64 // TODO add C ABI support for other architectures + ) { + cases.addBuildFile("test/stage1/c_abi/build.zig"); + } } diff --git a/test/gen_h.zig b/test/gen_h.zig index b3aaa263d6d8d3b517ed2422fe129b81862473e9..11fb55344f52d7d529686855a84dcee2d4948177 100644 --- a/test/gen_h.zig +++ b/test/gen_h.zig @@ -20,6 +20,9 @@ pub fn addCases(cases: *tests.GenHContext) void { \\ A: i32, \\ B: f32, \\ C: bool, + \\ D: u64, + \\ E: u64, + \\ F: u64, \\}; \\export fn entry(foo: Foo) void { } , @@ -27,6 +30,9 @@ pub fn addCases(cases: *tests.GenHContext) void { \\ int32_t A; \\ float B; \\ bool C; + \\ uint64_t D; + \\ uint64_t E; + \\ uint64_t F; \\}; \\ \\TEST_EXPORT void entry(struct Foo foo); @@ -34,17 +40,34 @@ pub fn addCases(cases: *tests.GenHContext) void { ); cases.add("declare union", + \\const Big = extern struct { + \\ A: u64, + \\ B: u64, + \\ C: u64, + \\ D: u64, + \\ E: u64, + \\}; \\const Foo = extern union { \\ A: i32, \\ B: f32, \\ C: bool, + \\ D: Big, \\}; - \\export fn entry(foo: Foo) void { } + \\export fn entry(foo: Foo) void {} , + \\struct Big { + \\ uint64_t A; + \\ uint64_t B; + \\ uint64_t C; + \\ uint64_t D; + \\ uint64_t E; + \\}; + \\ \\union Foo { \\ int32_t A; \\ float B; \\ bool C; + \\ struct Big D; \\}; \\ \\TEST_EXPORT void entry(union Foo foo); @@ -85,7 +108,6 @@ pub fn addCases(cases: *tests.GenHContext) void { \\export fn a(s: *S) u8 { \\ return s.a; \\} - , \\struct S; \\TEST_EXPORT uint8_t a(struct S * s); @@ -101,7 +123,6 @@ pub fn addCases(cases: *tests.GenHContext) void { \\export fn a(s: *U) u8 { \\ return s.A; \\} - , \\union U; \\TEST_EXPORT uint8_t a(union U * s); @@ -117,7 +138,6 @@ pub fn addCases(cases: *tests.GenHContext) void { \\export fn a(s: *E) u8 { \\ return @enumToInt(s.*); \\} - , \\enum E; \\TEST_EXPORT uint8_t a(enum E * s); diff --git a/test/stage1/c_abi/build.zig b/test/stage1/c_abi/build.zig new file mode 100644 index 0000000000000000000000000000000000000000..02db8f7904bc52843382493e127ff2e7f10d9b89 --- /dev/null +++ b/test/stage1/c_abi/build.zig @@ -0,0 +1,17 @@ +const Builder = @import("std").build.Builder; + +pub fn build(b: *Builder) void { + const rel_opts = b.standardReleaseOptions(); + + const c_obj = b.addCObject("cfuncs", "cfuncs.c"); + c_obj.setBuildMode(rel_opts); + + const main = b.addTest("main.zig"); + main.setBuildMode(rel_opts); + main.addObject(c_obj); + + const test_step = b.step("test", "Test the program"); + test_step.dependOn(&main.step); + + b.default_step.dependOn(test_step); +} diff --git a/test/stage1/c_abi/cfuncs.c b/test/stage1/c_abi/cfuncs.c new file mode 100644 index 0000000000000000000000000000000000000000..aef4618747af86d9f8da408cb664b1ab822602b4 --- /dev/null +++ b/test/stage1/c_abi/cfuncs.c @@ -0,0 +1,64 @@ +#include +#include +#include + +void zig_panic(); + +static void assert_or_panic(bool ok) { + if (!ok) { + zig_panic(); + } +} + +void zig_u8(uint8_t); +void zig_u16(uint16_t); +void zig_u32(uint32_t); +void zig_u64(uint64_t); +void zig_i8(int8_t); +void zig_i16(int16_t); +void zig_i32(int32_t); +void zig_i64(int64_t); + +void run_c_tests(void) { + zig_u8(0xff); + zig_u16(0xfffe); + zig_u32(0xfffffffd); + zig_u64(0xfffffffffffffffc); + + zig_i8(-1); + zig_i16(-2); + zig_i32(-3); + zig_i64(-4); +} + +void c_u8(uint8_t x) { + assert_or_panic(x == 0xff); +} + +void c_u16(uint16_t x) { + assert_or_panic(x == 0xfffe); +} + +void c_u32(uint32_t x) { + assert_or_panic(x == 0xfffffffd); +} + +void c_u64(uint64_t x) { + assert_or_panic(x == 0xfffffffffffffffcULL); +} + +void c_i8(int8_t x) { + assert_or_panic(x == -1); +} + +void c_i16(int16_t x) { + assert_or_panic(x == -2); +} + +void c_i32(int32_t x) { + assert_or_panic(x == -3); +} + +void c_i64(int64_t x) { + assert_or_panic(x == -4); +} diff --git a/test/stage1/c_abi/main.zig b/test/stage1/c_abi/main.zig new file mode 100644 index 0000000000000000000000000000000000000000..c409def6c754be3c8e0c89a2df4ec0f0c4b5cecc --- /dev/null +++ b/test/stage1/c_abi/main.zig @@ -0,0 +1,58 @@ +const std = @import("std"); +const assertOrPanic = std.debug.assertOrPanic; + +extern fn run_c_tests() void; + +export fn zig_panic() noreturn { + @panic("zig_panic called from C"); +} + +test "C importing Zig ABI Tests" { + run_c_tests(); +} + +extern fn c_u8(u8) void; +extern fn c_u16(u16) void; +extern fn c_u32(u32) void; +extern fn c_u64(u64) void; +extern fn c_i8(i8) void; +extern fn c_i16(i16) void; +extern fn c_i32(i32) void; +extern fn c_i64(i64) void; + +test "C ABI integers" { + c_u8(0xff); + c_u16(0xfffe); + c_u32(0xfffffffd); + c_u64(0xfffffffffffffffc); + + c_i8(-1); + c_i16(-2); + c_i32(-3); + c_i64(-4); +} + +export fn zig_u8(x: u8) void { + assertOrPanic(x == 0xff); +} +export fn zig_u16(x: u16) void { + assertOrPanic(x == 0xfffe); +} +export fn zig_u32(x: u32) void { + assertOrPanic(x == 0xfffffffd); +} +export fn zig_u64(x: u64) void { + assertOrPanic(x == 0xfffffffffffffffc); +} +export fn zig_i8(x: i8) void { + assertOrPanic(x == -1); +} +export fn zig_i16(x: i16) void { + assertOrPanic(x == -2); +} +export fn zig_i32(x: i32) void { + assertOrPanic(x == -3); +} +export fn zig_i64(x: i64) void { + assertOrPanic(x == -4); +} -- 2.54.0 From be6cccb3a51512dea011326d9ad30ad495e7c716 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 11:52:57 -0400 Subject: [PATCH 05/20] stage1: c abi for big struct works --- src/all_types.hpp | 24 +++ src/analyze.cpp | 1 - src/codegen.cpp | 357 +++++++++++++++++++++++-------------- src/codegen.hpp | 1 + test/stage1/c_abi/cfuncs.c | 71 ++++++++ test/stage1/c_abi/main.zig | 74 ++++++++ 6 files changed, 396 insertions(+), 132 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index dbaa3b5467e4343c719d149b96e8b0a17abd8088..a89a94c0a026f01e7b27fc8963a7177a11ee74b8 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -3284,4 +3284,28 @@ enum FloatMode { FloatModeStrict, }; +enum FnWalkId { + FnWalkIdAttrs, + FnWalkIdCall, +}; + +struct FnWalkAttrs { + ZigFn *fn; + unsigned gen_i; +}; + +struct FnWalkCall { + ZigList *gen_param_values; + IrInstructionCall *inst; + bool is_var_args; +}; + +struct FnWalk { + FnWalkId id; + union { + FnWalkAttrs attrs; + FnWalkCall call; + } data; +}; + #endif diff --git a/src/analyze.cpp b/src/analyze.cpp index 7014c0ea9d95bf71251e76cf8c7acfbb66e9a007..a658c469ac59d4fc68f814520773ed567319a943 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1073,7 +1073,6 @@ bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { } // If you edit this function you have to edit the corresponding code: -// codegen.cpp:gen_c_abi_param // analyze.cpp:gen_c_abi_param_type // codegen.cpp:gen_c_abi_param_var // codegen.cpp:gen_c_abi_param_var_init diff --git a/src/codegen.cpp b/src/codegen.cpp index f35f146bb47aac04df652444f686fbe8f982b4b1..469d62f3a2e53d29ff5d491a6bad9caeac68301b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -466,7 +466,8 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { } bool external_linkage = linkage != GlobalLinkageIdInternal; - if (fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionStdcall && external_linkage && + CallingConvention cc = fn_table_entry->type_entry->data.fn.fn_type_id.cc; + if (cc == CallingConventionStdcall && external_linkage && g->zig_target.arch.arch == ZigLLVM_x86) { // prevent llvm name mangling @@ -510,17 +511,17 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { break; } - if (fn_type->data.fn.fn_type_id.cc == CallingConventionNaked) { + if (cc == CallingConventionNaked) { addLLVMFnAttr(fn_table_entry->llvm_value, "naked"); } else { LLVMSetFunctionCallConv(fn_table_entry->llvm_value, get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc)); } - if (fn_type->data.fn.fn_type_id.cc == CallingConventionAsync) { + if (cc == CallingConventionAsync) { addLLVMFnAttr(fn_table_entry->llvm_value, "optnone"); addLLVMFnAttr(fn_table_entry->llvm_value, "noinline"); } - bool want_cold = fn_table_entry->is_cold || fn_type->data.fn.fn_type_id.cc == CallingConventionCold; + bool want_cold = fn_table_entry->is_cold || cc == CallingConventionCold; if (want_cold) { ZigLLVMAddFunctionAttrCold(fn_table_entry->llvm_value); } @@ -576,37 +577,16 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { // nothing to do } else if (type_is_codegen_pointer(return_type)) { addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull"); - } else if (handle_is_ptr(return_type) && - calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc)) - { + } else if (handle_is_ptr(return_type) && calling_convention_does_first_arg_return(cc)) { addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull"); } - // set parameter attributes - for (size_t param_i = 0; param_i < fn_type->data.fn.fn_type_id.param_count; param_i += 1) { - FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i]; - size_t gen_index = gen_info->gen_index; - bool is_byval = gen_info->is_byval; - - if (gen_index == SIZE_MAX) { - continue; - } - - FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[param_i]; - - ZigType *param_type = gen_info->type; - if (param_info->is_noalias) { - addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "noalias"); - } - if ((param_type->id == ZigTypeIdPointer && param_type->data.pointer.is_const) || is_byval) { - addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "readonly"); - } - if (param_type->id == ZigTypeIdPointer) { - addLLVMArgAttr(fn_table_entry->llvm_value, (unsigned)gen_index, "nonnull"); - } - } + FnWalk fn_walk = {}; + fn_walk.id = FnWalkIdAttrs; + fn_walk.data.attrs.fn = fn_table_entry; + walk_function_params(g, fn_type, &fn_walk); uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); if (err_ret_trace_arg_index != UINT32_MAX) { @@ -1888,6 +1868,216 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) { return instruction->llvm_value; } +ATTRIBUTE_NORETURN +static void report_errors_and_exit(CodeGen *g) { + assert(g->errors.length != 0); + for (size_t i = 0; i < g->errors.length; i += 1) { + ErrorMsg *err = g->errors.at(i); + print_err_msg(err, g->err_color); + } + exit(1); +} + +static void report_errors_and_maybe_exit(CodeGen *g) { + if (g->errors.length != 0) { + report_errors_and_exit(g); + } +} + +ATTRIBUTE_NORETURN +static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { + ErrorMsg *msg = add_node_error(g, source_node, + buf_sprintf("TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481")); + add_error_note(g, msg, source_node, + buf_sprintf("pointers, integers, floats, bools, and enums work on all targets")); + report_errors_and_exit(g); +} + +static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk, size_t src_i) { + // Initialized from the type for some walks, but because of C var args, + // initialized based on callsite instructions for that one. + FnTypeParamInfo *param_info = nullptr; + ZigType *ty; + AstNode *source_node = nullptr; + LLVMValueRef val; + LLVMValueRef llvm_fn; + switch (fn_walk->id) { + case FnWalkIdAttrs: + if (src_i >= fn_type->data.fn.fn_type_id.param_count) + return false; + param_info = &fn_type->data.fn.fn_type_id.param_info[src_i]; + ty = param_info->type; + source_node = fn_walk->data.attrs.fn->proto_node; + llvm_fn = fn_walk->data.attrs.fn->llvm_value; + break; + case FnWalkIdCall: { + if (src_i >= fn_walk->data.call.inst->arg_count) + return false; + IrInstruction *arg = fn_walk->data.call.inst->args[src_i]; + ty = arg->value.type; + source_node = arg->source_node; + val = ir_llvm_value(g, arg); + break; + } + } + if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || + ty->id == ZigTypeIdInt // TODO investigate if we need to change this + ) { + switch (fn_walk->id) { + case FnWalkIdAttrs: { + ZigType *ptr_type = get_codegen_ptr_type(ty); + if (ptr_type != nullptr) { + if (ty->id != ZigTypeIdOptional) { + addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); + } + if (ptr_type->data.pointer.is_const) { + addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "readonly"); + } + if (param_info->is_noalias) { + addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "noalias"); + } + } + fn_walk->data.attrs.gen_i += 1; + break; + } + case FnWalkIdCall: + fn_walk->data.call.gen_param_values->append(val); + break; + } + return true; + } + + // Arrays are just pointers + if (ty->id == ZigTypeIdArray) { + assert(handle_is_ptr(ty)); + switch (fn_walk->id) { + case FnWalkIdAttrs: + addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); + fn_walk->data.attrs.gen_i += 1; + break; + case FnWalkIdCall: + fn_walk->data.call.gen_param_values->append(val); + break; + } + return true; + } + + if (g->zig_target.arch.arch == ZigLLVM_x86_64) { + size_t ty_size = type_size(g, ty); + if (ty->id == ZigTypeIdStruct || ty->id == ZigTypeIdUnion) { + assert(handle_is_ptr(ty)); + + // "If the size of an object is larger than four eightbytes, or it contains unaligned + // fields, it has class MEMORY" + if (ty_size > 32) { + switch (fn_walk->id) { + case FnWalkIdAttrs: + addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "byval"); + addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); + fn_walk->data.attrs.gen_i += 1; + break; + case FnWalkIdCall: + fn_walk->data.call.gen_param_values->append(val); + break; + } + return true; + } + } + if (ty->id == ZigTypeIdStruct) { + assert(handle_is_ptr(ty)); + // "If the size of the aggregate exceeds a single eightbyte, each is classified + // separately. Each eightbyte gets initialized to class NO_CLASS." + if (ty_size <= 8) { + bool contains_int = false; + for (size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { + if (type_is_c_abi_int(g, ty->data.structure.fields[i].type_entry)) { + contains_int = true; + break; + } + } + if (contains_int) { + switch (fn_walk->id) { + case FnWalkIdAttrs: + fn_walk->data.attrs.gen_i += 1; + break; + case FnWalkIdCall: { + LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); + LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, val, ptr_to_int_type_ref, ""); + LLVMValueRef loaded = LLVMBuildLoad(g->builder, bitcasted, ""); + fn_walk->data.call.gen_param_values->append(loaded); + break; + } + } + return true; + } + } + } + } + if (source_node != nullptr) { + give_up_with_c_abi_error(g, source_node); + } + // otherwise allow codegen code to report a compile error + return false; +} + +void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { + CallingConvention cc = fn_type->data.fn.fn_type_id.cc; + if (cc == CallingConventionC) { + size_t src_i = 0; + for (;;) { + if (!iter_function_params_c_abi(g, fn_type, fn_walk, src_i)) + break; + src_i += 1; + } + return; + } + if (fn_walk->id == FnWalkIdCall) { + IrInstructionCall *instruction = fn_walk->data.call.inst; + bool is_var_args = fn_walk->data.call.is_var_args; + for (size_t call_i = 0; call_i < instruction->arg_count; call_i += 1) { + IrInstruction *param_instruction = instruction->args[call_i]; + ZigType *param_type = param_instruction->value.type; + if (is_var_args || type_has_bits(param_type)) { + LLVMValueRef param_value = ir_llvm_value(g, param_instruction); + assert(param_value); + fn_walk->data.call.gen_param_values->append(param_value); + } + } + return; + } + for (size_t param_i = 0; param_i < fn_type->data.fn.fn_type_id.param_count; param_i += 1) { + FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i]; + size_t gen_index = gen_info->gen_index; + + if (gen_index == SIZE_MAX) { + continue; + } + + switch (fn_walk->id) { + case FnWalkIdAttrs: { + LLVMValueRef llvm_fn = fn_walk->data.attrs.fn->llvm_value; + bool is_byval = gen_info->is_byval; + FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[param_i]; + + ZigType *param_type = gen_info->type; + if (param_info->is_noalias) { + addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "noalias"); + } + if ((param_type->id == ZigTypeIdPointer && param_type->data.pointer.is_const) || is_byval) { + addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "readonly"); + } + if (param_type->id == ZigTypeIdPointer) { + addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "nonnull"); + } + break; + } + case FnWalkIdCall: + // handled before for loop + zig_unreachable(); + } + } +} + static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *executable, IrInstructionSaveErrRetAddr *save_err_ret_addr_instruction) { @@ -3111,31 +3301,6 @@ static void set_call_instr_sret(CodeGen *g, LLVMValueRef call_instr) { LLVMAddCallSiteAttribute(call_instr, 1, sret_attr); } -ATTRIBUTE_NORETURN -static void report_errors_and_exit(CodeGen *g) { - assert(g->errors.length != 0); - for (size_t i = 0; i < g->errors.length; i += 1) { - ErrorMsg *err = g->errors.at(i); - print_err_msg(err, g->err_color); - } - exit(1); -} - -static void report_errors_and_maybe_exit(CodeGen *g) { - if (g->errors.length != 0) { - report_errors_and_exit(g); - } -} - -ATTRIBUTE_NORETURN -static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { - ErrorMsg *msg = add_node_error(g, source_node, - buf_sprintf("TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481")); - add_error_note(g, msg, source_node, - buf_sprintf("pointers, integers, floats, bools, and enums work on all targets")); - report_errors_and_exit(g); -} - static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { assert(alignment > 0); LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name); @@ -3144,67 +3309,6 @@ static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *na } // If you edit this function you have to edit the corresponding code: -// codegen.cpp:gen_c_abi_param -// analyze.cpp:gen_c_abi_param_type -// codegen.cpp:gen_c_abi_param_var -// codegen.cpp:gen_c_abi_param_var_init -static void gen_c_abi_param(CodeGen *g, ZigList *gen_param_values, LLVMValueRef val, - ZigType *ty, AstNode *source_node) -{ - if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || - ty->id == ZigTypeIdInt // TODO investigate if we need to change this - ) { - gen_param_values->append(val); - return; - } - - // Arrays are just pointers - if (ty->id == ZigTypeIdArray) { - assert(handle_is_ptr(ty)); - gen_param_values->append(val); - return; - } - - if (g->zig_target.arch.arch == ZigLLVM_x86_64) { - // This code all assumes that val is a pointer. - assert(handle_is_ptr(ty)); - - size_t ty_size = type_size(g, ty); - if (ty->id == ZigTypeIdStruct || ty->id == ZigTypeIdUnion) { - // "If the size of an object is larger than four eightbytes, or it contains unaligned - // fields, it has class MEMORY" - if (ty_size > 32) { - gen_param_values->append(val); - return; - } - } - if (ty->id == ZigTypeIdStruct) { - // "If the size of the aggregate exceeds a single eightbyte, each is classified - // separately. Each eightbyte gets initialized to class NO_CLASS." - if (ty_size <= 8) { - bool contains_int = false; - for (size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { - if (type_is_c_abi_int(g, ty->data.structure.fields[i].type_entry)) { - contains_int = true; - break; - } - } - if (contains_int) { - LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); - LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, val, ptr_to_int_type_ref, ""); - LLVMValueRef loaded = LLVMBuildLoad(g->builder, bitcasted, ""); - gen_param_values->append(loaded); - return; - } - } - } - } - - give_up_with_c_abi_error(g, source_node); -} - -// If you edit this function you have to edit the corresponding code: -// codegen.cpp:gen_c_abi_param // analyze.cpp:gen_c_abi_param_type // codegen.cpp:gen_c_abi_param_var // codegen.cpp:gen_c_abi_param_var_init @@ -3283,7 +3387,6 @@ ok: } // If you edit this function you have to edit the corresponding code: -// codegen.cpp:gen_c_abi_param // analyze.cpp:gen_c_abi_param_type // codegen.cpp:gen_c_abi_param_var // codegen.cpp:gen_c_abi_param_var_init @@ -3376,7 +3479,6 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr bool ret_has_bits = type_has_bits(src_return_type); CallingConvention cc = fn_type->data.fn.fn_type_id.cc; - bool is_c_abi = cc == CallingConventionC; bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type) && calling_convention_does_first_arg_return(cc); @@ -3395,19 +3497,12 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, err_union_err_index, ""); gen_param_values.append(err_val_ptr); } - for (size_t call_i = 0; call_i < instruction->arg_count; call_i += 1) { - IrInstruction *param_instruction = instruction->args[call_i]; - ZigType *param_type = param_instruction->value.type; - if (is_var_args || type_has_bits(param_type)) { - LLVMValueRef param_value = ir_llvm_value(g, param_instruction); - assert(param_value); - if (is_c_abi) { - gen_c_abi_param(g, &gen_param_values, param_value, param_type, param_instruction->source_node); - } else { - gen_param_values.append(param_value); - } - } - } + FnWalk fn_walk = {}; + fn_walk.id = FnWalkIdCall; + fn_walk.data.call.inst = instruction; + fn_walk.data.call.is_var_args = is_var_args; + fn_walk.data.call.gen_param_values = &gen_param_values; + walk_function_params(g, fn_type, &fn_walk); ZigLLVM_FnInline fn_inline; switch (instruction->fn_inline) { diff --git a/src/codegen.hpp b/src/codegen.hpp index 6297c4611b1a5189b6f5c9d88a531ceb80832928..785ba9f05d60ac45e9283fa16abbd9b3eded2b43 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -61,5 +61,6 @@ void codegen_translate_c(CodeGen *g, Buf *path); Buf *codegen_generate_builtin_source(CodeGen *g); +void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk); #endif diff --git a/test/stage1/c_abi/cfuncs.c b/test/stage1/c_abi/cfuncs.c index aef4618747af86d9f8da408cb664b1ab822602b4..bfdaa006ef9ae9e38ca758b82e8ce3d9bc33d11b 100644 --- a/test/stage1/c_abi/cfuncs.c +++ b/test/stage1/c_abi/cfuncs.c @@ -19,6 +19,29 @@ void zig_i16(int16_t); void zig_i32(int32_t); void zig_i64(int64_t); +void zig_f32(float); +void zig_f64(double); + +void zig_ptr(void *); + +void zig_bool(bool); + +void zig_array(uint8_t[10]); + +static uint8_t array[10] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}; + +struct BigStruct { + uint64_t a; + uint64_t b; + uint64_t c; + uint64_t d; + uint8_t e; +}; + +void zig_big_struct(struct BigStruct); + +static struct BigStruct s = {1, 2, 3, 4, 5}; + void run_c_tests(void) { zig_u8(0xff); zig_u16(0xfffe); @@ -29,6 +52,17 @@ void run_c_tests(void) { zig_i16(-2); zig_i32(-3); zig_i64(-4); + + zig_f32(12.34f); + zig_f64(56.78); + + zig_ptr((void*)0xdeadbeefL); + + zig_bool(true); + + zig_array(array); + + zig_big_struct(s); } void c_u8(uint8_t x) { @@ -62,3 +96,40 @@ void c_i32(int32_t x) { void c_i64(int64_t x) { assert_or_panic(x == -4); } + +void c_f32(float x) { + assert_or_panic(x == 12.34f); +} + +void c_f64(double x) { + assert_or_panic(x == 56.78); +} + +void c_ptr(void *x) { + assert_or_panic(x == (void*)0xdeadbeefL); +} + +void c_bool(bool x) { + assert_or_panic(x); +} + +void c_array(uint8_t x[10]) { + assert_or_panic(x[0] == '1'); + assert_or_panic(x[1] == '2'); + assert_or_panic(x[2] == '3'); + assert_or_panic(x[3] == '4'); + assert_or_panic(x[4] == '5'); + assert_or_panic(x[5] == '6'); + assert_or_panic(x[6] == '7'); + assert_or_panic(x[7] == '8'); + assert_or_panic(x[8] == '9'); + assert_or_panic(x[9] == '0'); +} + +void c_big_struct(struct BigStruct x) { + assert_or_panic(x.a == 1); + assert_or_panic(x.b == 2); + assert_or_panic(x.c == 3); + assert_or_panic(x.d == 4); + assert_or_panic(x.e == 5); +} diff --git a/test/stage1/c_abi/main.zig b/test/stage1/c_abi/main.zig index c409def6c754be3c8e0c89a2df4ec0f0c4b5cecc..5f5e0b0ca653f1cd0992f676aca3f89002cac2a2 100644 --- a/test/stage1/c_abi/main.zig +++ b/test/stage1/c_abi/main.zig @@ -56,3 +56,77 @@ export fn zig_i32(x: i32) void { export fn zig_i64(x: i64) void { assertOrPanic(x == -4); } + +extern fn c_f32(f32) void; +extern fn c_f64(f64) void; + +test "C ABI floats" { + c_f32(12.34); + c_f64(56.78); +} + +export fn zig_f32(x: f32) void { + assertOrPanic(x == 12.34); +} +export fn zig_f64(x: f64) void { + assertOrPanic(x == 56.78); +} + +extern fn c_ptr(*c_void) void; + +test "C ABI pointer" { + c_ptr(@intToPtr(*c_void, 0xdeadbeef)); +} + +export fn zig_ptr(x: *c_void) void { + assertOrPanic(@ptrToInt(x) == 0xdeadbeef); +} + +extern fn c_bool(bool) void; + +test "C ABI bool" { + c_bool(true); +} + +export fn zig_bool(x: bool) void { + assertOrPanic(x); +} + +extern fn c_array([10]u8) void; + +test "C ABI array" { + var array: [10]u8 = "1234567890"; + c_array(array); +} + +export fn zig_array(x: [10]u8) void { + assertOrPanic(std.mem.eql(u8, x, "1234567890")); +} + +const BigStruct = extern struct { + a: u64, + b: u64, + c: u64, + d: u64, + e: u8, +}; +extern fn c_big_struct(BigStruct) void; + +test "C ABI big struct" { + var s = BigStruct{ + .a = 1, + .b = 2, + .c = 3, + .d = 4, + .e = 5, + }; + c_big_struct(s); +} + +export fn zig_big_struct(x: BigStruct) void { + assertOrPanic(x.a == 1); + assertOrPanic(x.b == 2); + assertOrPanic(x.c == 3); + assertOrPanic(x.d == 4); + assertOrPanic(x.e == 5); +} -- 2.54.0 From 04d7b565f78895d96e5a43e8b1f4873ea5f529cf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 12:23:50 -0400 Subject: [PATCH 06/20] stage1: refactor fn type analysis to use C ABI walk fn --- src/all_types.hpp | 7 ++++ src/analyze.cpp | 85 +++++++---------------------------------------- src/analyze.hpp | 3 +- src/codegen.cpp | 43 ++++++++++++++++++++++++ src/codegen.hpp | 2 -- 5 files changed, 64 insertions(+), 76 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index a89a94c0a026f01e7b27fc8963a7177a11ee74b8..5ce8e88669d546ea495b695ef1f7d15cb931a311 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -3287,6 +3287,7 @@ enum FloatMode { enum FnWalkId { FnWalkIdAttrs, FnWalkIdCall, + FnWalkIdTypes, }; struct FnWalkAttrs { @@ -3300,11 +3301,17 @@ struct FnWalkCall { bool is_var_args; }; +struct FnWalkTypes { + ZigList *param_di_types; + ZigList *gen_param_types; +}; + struct FnWalk { FnWalkId id; union { FnWalkAttrs attrs; FnWalkCall call; + FnWalkTypes types; } data; }; diff --git a/src/analyze.cpp b/src/analyze.cpp index a658c469ac59d4fc68f814520773ed567319a943..0fe83e0d9624f4c0ab7e86db5d06dae204322b29 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1061,76 +1061,6 @@ ZigType *get_ptr_to_stack_trace_type(CodeGen *g) { return g->ptr_to_stack_trace_type; } -bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { - size_t ty_size = type_size(g, ty); - if (ty_size > g->pointer_size_bytes) - return false; - return (ty->id == ZigTypeIdInt || - ty->id == ZigTypeIdFloat || - ty->id == ZigTypeIdBool || - ty->id == ZigTypeIdEnum || - get_codegen_ptr_type(ty) != nullptr); -} - -// If you edit this function you have to edit the corresponding code: -// analyze.cpp:gen_c_abi_param_type -// codegen.cpp:gen_c_abi_param_var -// codegen.cpp:gen_c_abi_param_var_init -static void gen_c_abi_param_type(CodeGen *g, ZigList *gen_param_types, - ZigList *param_di_types, ZigType *ty) -{ - if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || - ty->id == ZigTypeIdInt // TODO investigate if we need to change this - ) { - gen_param_types->append(ty->type_ref); - param_di_types->append(ty->di_type); - return; - } - - // Arrays are just pointers - if (ty->id == ZigTypeIdArray) { - ZigType *gen_type = get_pointer_to_type(g, ty, true); - gen_param_types->append(gen_type->type_ref); - param_di_types->append(gen_type->di_type); - return; - } - - if (g->zig_target.arch.arch == ZigLLVM_x86_64) { - size_t ty_size = type_size(g, ty); - if (ty->id == ZigTypeIdStruct || ty->id == ZigTypeIdUnion) { - // "If the size of an object is larger than four eightbytes, or it contains unaligned - // fields, it has class MEMORY" - if (ty_size > 32) { - ZigType *gen_type = get_pointer_to_type(g, ty, true); - gen_param_types->append(gen_type->type_ref); - param_di_types->append(gen_type->di_type); - return; - } - } - if (ty->id == ZigTypeIdStruct) { - // "If the size of the aggregate exceeds a single eightbyte, each is classified - // separately. Each eightbyte gets initialized to class NO_CLASS." - if (ty_size <= 8) { - bool contains_int = false; - for (size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { - if (type_is_c_abi_int(g, ty->data.structure.fields[i].type_entry)) { - contains_int = true; - break; - } - } - if (contains_int) { - ZigType *gen_type = get_int_type(g, false, ty_size * 8); - gen_param_types->append(gen_type->type_ref); - param_di_types->append(gen_type->di_type); - return; - } - } - } - } - - // allow codegen code to report a compile error -} - ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { Error err; auto table_entry = g->fn_type_table.maybe_get(fn_type_id); @@ -1249,9 +1179,10 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { if ((err = ensure_complete_type(g, type_entry))) return g->builtin_types.entry_invalid; - if (is_c_abi) { - gen_c_abi_param_type(g, &gen_param_types, ¶m_di_types, type_entry); - } else if (type_has_bits(type_entry)) { + if (is_c_abi) + continue; + + if (type_has_bits(type_entry)) { ZigType *gen_type; if (handle_is_ptr(type_entry)) { gen_type = get_pointer_to_type(g, type_entry, true); @@ -1267,6 +1198,14 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { } } + if (is_c_abi) { + FnWalk fn_walk = {}; + fn_walk.id = FnWalkIdTypes; + fn_walk.data.types.param_di_types = ¶m_di_types; + fn_walk.data.types.gen_param_types = &gen_param_types; + walk_function_params(g, fn_type, &fn_walk); + } + fn_type->data.fn.gen_param_count = gen_param_types.length; fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref, diff --git a/src/analyze.hpp b/src/analyze.hpp index f13c528c5a94bd097495d0ba8b7e35670f355101..98e4412594468fcc8480cf46b802c43e465d73b3 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -210,6 +210,7 @@ ZigType *get_primitive_type(CodeGen *g, Buf *name); bool calling_convention_allows_zig_types(CallingConvention cc); const char *calling_convention_name(CallingConvention cc); -bool type_is_c_abi_int(CodeGen *g, ZigType *ty); +void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk); + #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 469d62f3a2e53d29ff5d491a6bad9caeac68301b..e96445ebe31d56261bbf6a2b4c68a1a71e05ec0b 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1893,6 +1893,17 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { report_errors_and_exit(g); } +static bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { + size_t ty_size = type_size(g, ty); + if (ty_size > g->pointer_size_bytes) + return false; + return (ty->id == ZigTypeIdInt || + ty->id == ZigTypeIdFloat || + ty->id == ZigTypeIdBool || + ty->id == ZigTypeIdEnum || + get_codegen_ptr_type(ty) != nullptr); +} + static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk, size_t src_i) { // Initialized from the type for some walks, but because of C var args, // initialized based on callsite instructions for that one. @@ -1919,7 +1930,14 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ val = ir_llvm_value(g, arg); break; } + case FnWalkIdTypes: + if (src_i >= fn_type->data.fn.fn_type_id.param_count) + return false; + param_info = &fn_type->data.fn.fn_type_id.param_info[src_i]; + ty = param_info->type; + break; } + if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || ty->id == ZigTypeIdInt // TODO investigate if we need to change this ) { @@ -1943,6 +1961,10 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ case FnWalkIdCall: fn_walk->data.call.gen_param_values->append(val); break; + case FnWalkIdTypes: + fn_walk->data.types.gen_param_types->append(ty->type_ref); + fn_walk->data.types.param_di_types->append(ty->di_type); + break; } return true; } @@ -1958,6 +1980,12 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ case FnWalkIdCall: fn_walk->data.call.gen_param_values->append(val); break; + case FnWalkIdTypes: { + ZigType *gen_type = get_pointer_to_type(g, ty, true); + fn_walk->data.types.gen_param_types->append(gen_type->type_ref); + fn_walk->data.types.param_di_types->append(gen_type->di_type); + break; + } } return true; } @@ -1979,6 +2007,12 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ case FnWalkIdCall: fn_walk->data.call.gen_param_values->append(val); break; + case FnWalkIdTypes: { + ZigType *gen_type = get_pointer_to_type(g, ty, true); + fn_walk->data.types.gen_param_types->append(gen_type->type_ref); + fn_walk->data.types.param_di_types->append(gen_type->di_type); + break; + } } return true; } @@ -2007,6 +2041,12 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ fn_walk->data.call.gen_param_values->append(loaded); break; } + case FnWalkIdTypes: { + ZigType *gen_type = get_int_type(g, false, ty_size * 8); + fn_walk->data.types.gen_param_types->append(gen_type->type_ref); + fn_walk->data.types.param_di_types->append(gen_type->di_type); + break; + } } return true; } @@ -2074,6 +2114,9 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { case FnWalkIdCall: // handled before for loop zig_unreachable(); + case FnWalkIdTypes: + // Not called for non-c-abi + zig_unreachable(); } } } diff --git a/src/codegen.hpp b/src/codegen.hpp index 785ba9f05d60ac45e9283fa16abbd9b3eded2b43..b1a4dbf6e20a09261fa91765829caae80d5e7eb9 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -61,6 +61,4 @@ void codegen_translate_c(CodeGen *g, Buf *path); Buf *codegen_generate_builtin_source(CodeGen *g); -void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk); - #endif -- 2.54.0 From c528c0090089041e63f9bdbb52f951e53ce94631 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 12:59:59 -0400 Subject: [PATCH 07/20] stage1: refactor param vars for C ABI --- src/all_types.hpp | 10 +++ src/codegen.cpp | 155 ++++++++++++++++++++-------------------------- 2 files changed, 76 insertions(+), 89 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 5ce8e88669d546ea495b695ef1f7d15cb931a311..649250bf3e442669200a5ce9ed86bf5d5f51ef00 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -3288,6 +3288,7 @@ enum FnWalkId { FnWalkIdAttrs, FnWalkIdCall, FnWalkIdTypes, + FnWalkIdVars, }; struct FnWalkAttrs { @@ -3306,12 +3307,21 @@ struct FnWalkTypes { ZigList *gen_param_types; }; +struct FnWalkVars { + ImportTableEntry *import; + LLVMValueRef llvm_fn; + ZigFn *fn; + ZigVar *var; + unsigned gen_i; +}; + struct FnWalk { FnWalkId id; union { FnWalkAttrs attrs; FnWalkCall call; FnWalkTypes types; + FnWalkVars vars; } data; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index e96445ebe31d56261bbf6a2b4c68a1a71e05ec0b..19d2781ac0d1c22418dbd11fef3bbc8f4753efd8 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1904,14 +1904,24 @@ static bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { get_codegen_ptr_type(ty) != nullptr); } +static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { + assert(alignment > 0); + LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name); + LLVMSetAlignment(result, alignment); + return result; +} + static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk, size_t src_i) { // Initialized from the type for some walks, but because of C var args, // initialized based on callsite instructions for that one. FnTypeParamInfo *param_info = nullptr; ZigType *ty; + ZigType *dest_ty = nullptr; AstNode *source_node = nullptr; LLVMValueRef val; LLVMValueRef llvm_fn; + unsigned di_arg_index; + ZigVar *var; switch (fn_walk->id) { case FnWalkIdAttrs: if (src_i >= fn_type->data.fn.fn_type_id.param_count) @@ -1936,6 +1946,14 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ param_info = &fn_type->data.fn.fn_type_id.param_info[src_i]; ty = param_info->type; break; + case FnWalkIdVars: + assert(src_i < fn_type->data.fn.fn_type_id.param_count); + param_info = &fn_type->data.fn.fn_type_id.param_info[src_i]; + ty = param_info->type; + var = fn_walk->data.vars.var; + source_node = var->decl_node; + llvm_fn = fn_walk->data.vars.llvm_fn; + break; } if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || @@ -1965,6 +1983,13 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ fn_walk->data.types.gen_param_types->append(ty->type_ref); fn_walk->data.types.param_di_types->append(ty->di_type); break; + case FnWalkIdVars: { + var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes); + di_arg_index = fn_walk->data.vars.gen_i; + fn_walk->data.vars.gen_i += 1; + dest_ty = ty; + goto var_ok; + } } return true; } @@ -1986,6 +2011,13 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ fn_walk->data.types.param_di_types->append(gen_type->di_type); break; } + case FnWalkIdVars: { + var->value_ref = LLVMGetParam(llvm_fn, fn_walk->data.vars.gen_i); + di_arg_index = fn_walk->data.vars.gen_i; + dest_ty = get_pointer_to_type(g, ty, false); + fn_walk->data.vars.gen_i += 1; + goto var_ok; + } } return true; } @@ -2013,6 +2045,13 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ fn_walk->data.types.param_di_types->append(gen_type->di_type); break; } + case FnWalkIdVars: { + di_arg_index = fn_walk->data.vars.gen_i; + var->value_ref = LLVMGetParam(llvm_fn, fn_walk->data.vars.gen_i); + dest_ty = get_pointer_to_type(g, ty, false); + fn_walk->data.vars.gen_i += 1; + goto var_ok; + } } return true; } @@ -2047,6 +2086,12 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ fn_walk->data.types.param_di_types->append(gen_type->di_type); break; } + case FnWalkIdVars: { + di_arg_index = fn_walk->data.vars.gen_i; + var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes); + fn_walk->data.vars.gen_i += 1; + goto var_ok; + } } return true; } @@ -2058,6 +2103,16 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ } // otherwise allow codegen code to report a compile error return false; + +var_ok: + if (dest_ty != nullptr && var->decl_node) { + // arg index + 1 because the 0 index is return value + var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), + buf_ptr(&var->name), fn_walk->data.vars.import->di_file, + (unsigned)(var->decl_node->line + 1), + dest_ty->di_type, !g->strip_debug_symbols, 0, di_arg_index + 1); + } + return true; } void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { @@ -2117,6 +2172,9 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { case FnWalkIdTypes: // Not called for non-c-abi zig_unreachable(); + case FnWalkIdVars: + // iter_function_params_c_abi is called directly for this one + zig_unreachable(); } } } @@ -3344,93 +3402,7 @@ static void set_call_instr_sret(CodeGen *g, LLVMValueRef call_instr) { LLVMAddCallSiteAttribute(call_instr, 1, sret_attr); } -static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { - assert(alignment > 0); - LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name); - LLVMSetAlignment(result, alignment); - return result; -} - // If you edit this function you have to edit the corresponding code: -// analyze.cpp:gen_c_abi_param_type -// codegen.cpp:gen_c_abi_param_var -// codegen.cpp:gen_c_abi_param_var_init -static void gen_c_abi_param_var(CodeGen *g, ImportTableEntry *import, LLVMValueRef llvm_fn, ZigFn *fn, - ZigVar *var, unsigned *arg_index) -{ - ZigType *ty = var->value->type; - - ZigType *dest_ty = nullptr; - unsigned di_arg_index; - - if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || - ty->id == ZigTypeIdInt // TODO investigate if we need to change this - ) { - var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes); - di_arg_index = *arg_index; - *arg_index += 1; - dest_ty = ty; - goto ok; - } - - // Arrays are just pointers - if (ty->id == ZigTypeIdArray) { - di_arg_index = *arg_index; - var->value_ref = LLVMGetParam(llvm_fn, *arg_index); - dest_ty = get_pointer_to_type(g, ty, false); - *arg_index += 1; - goto ok; - } - - if (g->zig_target.arch.arch == ZigLLVM_x86_64) { - assert(handle_is_ptr(ty)); - size_t ty_size = type_size(g, ty); - - if (ty->id == ZigTypeIdStruct || ty->id == ZigTypeIdUnion) { - // "If the size of an object is larger than four eightbytes, or it contains unaligned - // fields, it has class MEMORY" - if (ty_size > 32) { - di_arg_index = *arg_index; - var->value_ref = LLVMGetParam(llvm_fn, *arg_index); - dest_ty = get_pointer_to_type(g, ty, false); - *arg_index += 1; - goto ok; - } - } - if (ty->id == ZigTypeIdStruct) { - // "If the size of the aggregate exceeds a single eightbyte, each is classified - // separately. Each eightbyte gets initialized to class NO_CLASS." - if (ty_size <= 8) { - bool contains_int = false; - for (size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { - if (type_is_c_abi_int(g, ty->data.structure.fields[i].type_entry)) { - contains_int = true; - break; - } - } - if (contains_int) { - var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes); - *arg_index += 1; - goto ok; - } - } - } - } - - give_up_with_c_abi_error(g, fn->proto_node); - -ok: - if (dest_ty != nullptr && var->decl_node) { - // arg index + 1 because the 0 index is return value - var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), - buf_ptr(&var->name), import->di_file, - (unsigned)(var->decl_node->line + 1), - dest_ty->di_type, !g->strip_debug_symbols, 0, di_arg_index + 1); - } -} - -// If you edit this function you have to edit the corresponding code: -// analyze.cpp:gen_c_abi_param_type // codegen.cpp:gen_c_abi_param_var // codegen.cpp:gen_c_abi_param_var_init static void gen_c_abi_param_var_init(CodeGen *g, ImportTableEntry *import, LLVMValueRef llvm_fn, ZigFn *fn, @@ -6317,7 +6289,12 @@ static void do_code_gen(CodeGen *g) { ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); // create debug variable declarations for variables and allocate all local variables - unsigned c_abi_arg_index = 0; + FnWalk fn_walk = {}; + fn_walk.id = FnWalkIdVars; + fn_walk.data.vars.import = import; + fn_walk.data.vars.fn = fn_table_entry; + fn_walk.data.vars.llvm_fn = fn; + fn_walk.data.vars.gen_i = 0; for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) { ZigVar *var = fn_table_entry->variable_list.at(var_i); @@ -6337,7 +6314,8 @@ static void do_code_gen(CodeGen *g) { var->value->type->di_type, !g->strip_debug_symbols, 0); } else if (is_c_abi) { - gen_c_abi_param_var(g, import, fn, fn_table_entry, var, &c_abi_arg_index); + fn_walk.data.vars.var = var; + iter_function_params_c_abi(g, fn_table_entry->type_entry, &fn_walk, var->src_arg_index); } else { assert(var->gen_arg_index != SIZE_MAX); ZigType *gen_type; @@ -6423,7 +6401,6 @@ static void do_code_gen(CodeGen *g) { gen_var_debug_decl(g, variable); } } - assert(c_abi_arg_index == c_abi_arg_init_index); ir_render(g, fn_table_entry); -- 2.54.0 From 421ca1523feec17db75e7c53b05e5abe4c88d58f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 13:24:41 -0400 Subject: [PATCH 08/20] stage1: refactor variable inits to use c abi fn walk --- src/all_types.hpp | 8 ++ src/codegen.cpp | 190 +++++++++++++++++++--------------------------- 2 files changed, 84 insertions(+), 114 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 649250bf3e442669200a5ce9ed86bf5d5f51ef00..b80fe1705333cb85037db679027492b1c9d1b123 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -3289,6 +3289,7 @@ enum FnWalkId { FnWalkIdCall, FnWalkIdTypes, FnWalkIdVars, + FnWalkIdInits, }; struct FnWalkAttrs { @@ -3315,6 +3316,12 @@ struct FnWalkVars { unsigned gen_i; }; +struct FnWalkInits { + LLVMValueRef llvm_fn; + ZigFn *fn; + unsigned gen_i; +}; + struct FnWalk { FnWalkId id; union { @@ -3322,6 +3329,7 @@ struct FnWalk { FnWalkCall call; FnWalkTypes types; FnWalkVars vars; + FnWalkInits inits; } data; }; diff --git a/src/codegen.cpp b/src/codegen.cpp index 19d2781ac0d1c22418dbd11fef3bbc8f4753efd8..d4de5935293759d2c381d4802209624d152563ff 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1954,6 +1954,15 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ source_node = var->decl_node; llvm_fn = fn_walk->data.vars.llvm_fn; break; + case FnWalkIdInits: + if (src_i >= fn_type->data.fn.fn_type_id.param_count) + return false; + param_info = &fn_type->data.fn.fn_type_id.param_info[src_i]; + ty = param_info->type; + var = fn_walk->data.inits.fn->variable_list.at(src_i); + source_node = fn_walk->data.inits.fn->proto_node; + llvm_fn = fn_walk->data.inits.llvm_fn; + break; } if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || @@ -1990,6 +1999,14 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ dest_ty = ty; goto var_ok; } + case FnWalkIdInits: + clear_debug_source_node(g); + gen_store_untyped(g, LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i), var->value_ref, var->align_bytes, false); + if (var->decl_node) { + gen_var_debug_decl(g, var); + } + fn_walk->data.inits.gen_i += 1; + break; } return true; } @@ -2018,6 +2035,12 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ fn_walk->data.vars.gen_i += 1; goto var_ok; } + case FnWalkIdInits: + if (var->decl_node) { + gen_var_debug_decl(g, var); + } + fn_walk->data.inits.gen_i += 1; + break; } return true; } @@ -2052,6 +2075,12 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ fn_walk->data.vars.gen_i += 1; goto var_ok; } + case FnWalkIdInits: + if (var->decl_node) { + gen_var_debug_decl(g, var); + } + fn_walk->data.inits.gen_i += 1; + break; } return true; } @@ -2092,6 +2121,18 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ fn_walk->data.vars.gen_i += 1; goto var_ok; } + case FnWalkIdInits: { + clear_debug_source_node(g); + LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i += 1); + LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); + LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); + gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); + if (var->decl_node) { + gen_var_debug_decl(g, var); + } + fn_walk->data.inits.gen_i += 1; + break; + } } return true; } @@ -2140,6 +2181,7 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { } return; } + size_t next_var_i = 0; for (size_t param_i = 0; param_i < fn_type->data.fn.fn_type_id.param_count; param_i += 1) { FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i]; size_t gen_index = gen_info->gen_index; @@ -2166,6 +2208,27 @@ void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { } break; } + case FnWalkIdInits: { + ZigFn *fn_table_entry = fn_walk->data.inits.fn; + LLVMValueRef llvm_fn = fn_table_entry->llvm_value; + ZigVar *variable = fn_table_entry->variable_list.at(next_var_i); + assert(variable->src_arg_index != SIZE_MAX); + next_var_i += 1; + + assert(variable); + assert(variable->value_ref); + + if (!handle_is_ptr(variable->value->type)) { + clear_debug_source_node(g); + gen_store_untyped(g, LLVMGetParam(llvm_fn, (unsigned)variable->gen_arg_index), variable->value_ref, + variable->align_bytes, false); + } + + if (variable->decl_node) { + gen_var_debug_decl(g, variable); + } + break; + } case FnWalkIdCall: // handled before for loop zig_unreachable(); @@ -3402,80 +3465,6 @@ static void set_call_instr_sret(CodeGen *g, LLVMValueRef call_instr) { LLVMAddCallSiteAttribute(call_instr, 1, sret_attr); } -// If you edit this function you have to edit the corresponding code: -// codegen.cpp:gen_c_abi_param_var -// codegen.cpp:gen_c_abi_param_var_init -static void gen_c_abi_param_var_init(CodeGen *g, ImportTableEntry *import, LLVMValueRef llvm_fn, ZigFn *fn, - ZigVar *var, unsigned *arg_index) -{ - ZigType *ty = var->value->type; - - if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || - ty->id == ZigTypeIdInt // TODO investigate if we need to change this - ) { - clear_debug_source_node(g); - gen_store_untyped(g, LLVMGetParam(llvm_fn, *arg_index), var->value_ref, var->align_bytes, false); - if (var->decl_node) { - gen_var_debug_decl(g, var); - } - *arg_index += 1; - return; - } - - // Arrays are just pointers - if (ty->id == ZigTypeIdArray) { - if (var->decl_node) { - gen_var_debug_decl(g, var); - } - *arg_index += 1; - return; - } - - if (g->zig_target.arch.arch == ZigLLVM_x86_64) { - assert(handle_is_ptr(ty)); - size_t ty_size = type_size(g, ty); - - if (ty->id == ZigTypeIdStruct || ty->id == ZigTypeIdUnion) { - // "If the size of an object is larger than four eightbytes, or it contains unaligned - // fields, it has class MEMORY" - if (ty_size > 32) { - if (var->decl_node) { - gen_var_debug_decl(g, var); - } - *arg_index += 1; - return; - } - } - if (ty->id == ZigTypeIdStruct) { - // "If the size of the aggregate exceeds a single eightbyte, each is classified - // separately. Each eightbyte gets initialized to class NO_CLASS." - if (ty_size <= 8) { - bool contains_int = false; - for (size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { - if (type_is_c_abi_int(g, ty->data.structure.fields[i].type_entry)) { - contains_int = true; - break; - } - } - if (contains_int) { - clear_debug_source_node(g); - LLVMValueRef arg = LLVMGetParam(llvm_fn, *arg_index); - LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); - LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); - gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); - if (var->decl_node) { - gen_var_debug_decl(g, var); - } - *arg_index += 1; - return; - } - } - } - } - - give_up_with_c_abi_error(g, fn->proto_node); -} - static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCall *instruction) { LLVMValueRef fn_val; ZigType *fn_type; @@ -6289,12 +6278,12 @@ static void do_code_gen(CodeGen *g) { ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); // create debug variable declarations for variables and allocate all local variables - FnWalk fn_walk = {}; - fn_walk.id = FnWalkIdVars; - fn_walk.data.vars.import = import; - fn_walk.data.vars.fn = fn_table_entry; - fn_walk.data.vars.llvm_fn = fn; - fn_walk.data.vars.gen_i = 0; + FnWalk fn_walk_var = {}; + fn_walk_var.id = FnWalkIdVars; + fn_walk_var.data.vars.import = import; + fn_walk_var.data.vars.fn = fn_table_entry; + fn_walk_var.data.vars.llvm_fn = fn; + fn_walk_var.data.vars.gen_i = 0; for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) { ZigVar *var = fn_table_entry->variable_list.at(var_i); @@ -6314,8 +6303,8 @@ static void do_code_gen(CodeGen *g) { var->value->type->di_type, !g->strip_debug_symbols, 0); } else if (is_c_abi) { - fn_walk.data.vars.var = var; - iter_function_params_c_abi(g, fn_table_entry->type_entry, &fn_walk, var->src_arg_index); + fn_walk_var.data.vars.var = var; + iter_function_params_c_abi(g, fn_table_entry->type_entry, &fn_walk_var, var->src_arg_index); } else { assert(var->gen_arg_index != SIZE_MAX); ZigType *gen_type; @@ -6367,40 +6356,13 @@ static void do_code_gen(CodeGen *g) { gen_store(g, LLVMConstInt(usize->type_ref, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false)); } - FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id; - // create debug variable declarations for parameters // rely on the first variables in the variable_list being parameters. - size_t next_var_i = 0; - unsigned c_abi_arg_init_index = 0; - for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { - if (is_c_abi) { - ZigVar *var = fn_table_entry->variable_list.at(param_i); - gen_c_abi_param_var_init(g, import, fn, fn_table_entry, var, &c_abi_arg_init_index); - continue; - } - - FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i]; - if (info->gen_index == SIZE_MAX) - continue; - - ZigVar *variable = fn_table_entry->variable_list.at(next_var_i); - assert(variable->src_arg_index != SIZE_MAX); - next_var_i += 1; - - assert(variable); - assert(variable->value_ref); - - if (!handle_is_ptr(variable->value->type)) { - clear_debug_source_node(g); - gen_store_untyped(g, LLVMGetParam(fn, (unsigned)variable->gen_arg_index), variable->value_ref, - variable->align_bytes, false); - } - - if (variable->decl_node) { - gen_var_debug_decl(g, variable); - } - } + FnWalk fn_walk_init = {}; + fn_walk_init.id = FnWalkIdInits; + fn_walk_init.data.inits.fn = fn_table_entry; + fn_walk_init.data.inits.llvm_fn = fn; + walk_function_params(g, fn_table_entry->type_entry, &fn_walk_init); ir_render(g, fn_table_entry); -- 2.54.0 From 743b2e4afc72f436a73977f896b32e6041785795 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 13:51:11 -0400 Subject: [PATCH 09/20] add C ABI test for big unions --- src/codegen.cpp | 4 ++- test/stage1/c_abi/cfuncs.c | 42 ++++++++++++++++++++++++++++--- test/stage1/c_abi/main.zig | 51 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 92 insertions(+), 5 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index d4de5935293759d2c381d4802209624d152563ff..8acc7e970266b77d02396725a25afd6f7a9695be 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1838,6 +1838,7 @@ static LLVMValueRef gen_assign_raw(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_ty } static void gen_var_debug_decl(CodeGen *g, ZigVar *var) { + assert(var->di_loc_var != nullptr); AstNode *source_node = var->decl_node; ZigLLVMDILocation *debug_loc = ZigLLVMGetDebugLoc((unsigned)source_node->line + 1, (unsigned)source_node->column + 1, get_di_scope(g, var->parent_scope)); @@ -2119,11 +2120,12 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ di_arg_index = fn_walk->data.vars.gen_i; var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes); fn_walk->data.vars.gen_i += 1; + dest_ty = ty; goto var_ok; } case FnWalkIdInits: { clear_debug_source_node(g); - LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i += 1); + LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i); LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); diff --git a/test/stage1/c_abi/cfuncs.c b/test/stage1/c_abi/cfuncs.c index bfdaa006ef9ae9e38ca758b82e8ce3d9bc33d11b..393c3a4f5afbd27e0f868672f38bebe285b1eb57 100644 --- a/test/stage1/c_abi/cfuncs.c +++ b/test/stage1/c_abi/cfuncs.c @@ -28,8 +28,6 @@ void zig_bool(bool); void zig_array(uint8_t[10]); -static uint8_t array[10] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}; - struct BigStruct { uint64_t a; uint64_t b; @@ -40,7 +38,19 @@ struct BigStruct { void zig_big_struct(struct BigStruct); -static struct BigStruct s = {1, 2, 3, 4, 5}; +union BigUnion { + struct BigStruct a; +}; + +void zig_big_union(union BigUnion); + +struct SmallStructInts { + uint8_t a; + uint8_t b; + uint8_t c; + uint8_t d; +}; +void zig_small_struct_ints(struct SmallStructInts); void run_c_tests(void) { zig_u8(0xff); @@ -60,9 +70,19 @@ void run_c_tests(void) { zig_bool(true); + // TODO making this non-static crashes for some reason + static uint8_t array[10] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}; zig_array(array); - zig_big_struct(s); + { + struct BigStruct s = {1, 2, 3, 4, 5}; + zig_big_struct(s); + } + + { + struct SmallStructInts s = {1, 2, 3, 4}; + zig_small_struct_ints(s); + } } void c_u8(uint8_t x) { @@ -133,3 +153,17 @@ void c_big_struct(struct BigStruct x) { assert_or_panic(x.d == 4); assert_or_panic(x.e == 5); } + +void c_big_union(union BigUnion x) { + assert_or_panic(x.a.a == 1); + assert_or_panic(x.a.b == 2); + assert_or_panic(x.a.c == 3); + assert_or_panic(x.a.d == 4); +} + +void c_small_struct_ints(struct SmallStructInts x) { + assert_or_panic(x.a == 1); + assert_or_panic(x.b == 2); + assert_or_panic(x.c == 3); + assert_or_panic(x.d == 4); +} diff --git a/test/stage1/c_abi/main.zig b/test/stage1/c_abi/main.zig index 5f5e0b0ca653f1cd0992f676aca3f89002cac2a2..ef425ceb67858735a03015d9c90177f55ae91cd2 100644 --- a/test/stage1/c_abi/main.zig +++ b/test/stage1/c_abi/main.zig @@ -130,3 +130,54 @@ export fn zig_big_struct(x: BigStruct) void { assertOrPanic(x.d == 4); assertOrPanic(x.e == 5); } + +const BigUnion = extern union { + a: BigStruct, +}; +extern fn c_big_union(BigUnion) void; + +test "C ABI big union" { + var x = BigUnion{ + .a = BigStruct{ + .a = 1, + .b = 2, + .c = 3, + .d = 4, + .e = 5, + }, + }; + c_big_union(x); +} + +export fn zig_big_union(x: BigUnion) void { + assertOrPanic(x.a.a == 1); + assertOrPanic(x.a.b == 2); + assertOrPanic(x.a.c == 3); + assertOrPanic(x.a.d == 4); + assertOrPanic(x.a.e == 5); +} + +const SmallStructInts = extern struct { + a: u8, + b: u8, + c: u8, + d: u8, +}; +extern fn c_small_struct_ints(SmallStructInts) void; + +test "C ABI small struct of ints" { + var s = SmallStructInts{ + .a = 1, + .b = 2, + .c = 3, + .d = 4, + }; + c_small_struct_ints(s); +} + +export fn zig_small_struct_ints(x: SmallStructInts) void { + assertOrPanic(x.a == 1); + assertOrPanic(x.b == 2); + assertOrPanic(x.c == 3); + assertOrPanic(x.d == 4); +} -- 2.54.0 From b18af37c578b118a245c911508d73ed23c303ee0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 15:17:24 -0400 Subject: [PATCH 10/20] fix crash when var init has compile error and then the var is referenced closes #1483 --- src/ir.cpp | 3 +-- test/compile_errors.zig | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/ir.cpp b/src/ir.cpp index 0269c29b1aa1459ce03d972aa8981109cea3ee81..30f40b3a1e3f529875c319b47d0e920f4269c3e1 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -13107,8 +13107,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction, assert(ira->codegen->errors.length != 0); return ira->codegen->invalid_instruction; } - assert(var->value->type); - if (type_is_invalid(var->value->type)) + if (var->value->type == nullptr || type_is_invalid(var->value->type)) return ira->codegen->invalid_instruction; bool comptime_var_mem = ir_get_var_is_comptime(var); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index faf17d2c059e55a07ada18db95d18846206db1e5..185ca62973e2e3800b0d5f1a0f19601931436047 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,24 @@ const tests = @import("tests.zig"); pub fn addCases(cases: *tests.CompileErrorContext) void { + cases.add( + "variable initialization compile error then referenced", + \\fn Undeclared() type { + \\ return T; + \\} + \\fn Gen() type { + \\ const X = Undeclared(); + \\ return struct { + \\ x: X, + \\ }; + \\} + \\export fn entry() void { + \\ const S = Gen(); + \\} + , + ".tmp_source.zig:2:12: error: use of undeclared identifier 'T'", + ); + cases.add( "refer to the type of a generic function", \\export fn entry() void { -- 2.54.0 From 85534a26c6ddea6277e79b12b759188689f3da43 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 18:34:19 -0400 Subject: [PATCH 11/20] stage1: function to classify x86_64 abi types --- src/codegen.cpp | 241 ++++++++++++++++++++++++++++++------------------ 1 file changed, 150 insertions(+), 91 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index 8acc7e970266b77d02396725a25afd6f7a9695be..a38cc869dd641948917e0cdc523516b8e4dda2b8 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1894,6 +1894,91 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { report_errors_and_exit(g); } +static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { + assert(alignment > 0); + LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name); + LLVMSetAlignment(result, alignment); + return result; +} + +enum X64CABIClass { + X64CABIClass_Unknown, + X64CABIClass_MEMORY, + X64CABIClass_INTEGER, + X64CABIClass_SSE, +}; + +static X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) { + size_t ty_size = type_size(g, ty); + if (get_codegen_ptr_type(ty) != nullptr) + return X64CABIClass_INTEGER; + switch (ty->id) { + case ZigTypeIdEnum: + case ZigTypeIdInt: + case ZigTypeIdBool: + return X64CABIClass_INTEGER; + case ZigTypeIdFloat: + return X64CABIClass_SSE; + case ZigTypeIdStruct: { + // "If the size of an object is larger than four eightbytes, or it contains unaligned + // fields, it has class MEMORY" + if (ty_size > 32) + return X64CABIClass_MEMORY; + if (ty->data.structure.layout != ContainerLayoutExtern) { + // TODO determine whether packed structs have any unaligned fields + return X64CABIClass_Unknown; + } + // "If the size of the aggregate exceeds two eightbytes and the first eight- + // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument + // is passed in memory." + if (ty_size > 16) { + // Zig doesn't support vectors and large fp registers yet, so this will always + // be memory. + return X64CABIClass_MEMORY; + } + X64CABIClass working_class = X64CABIClass_Unknown; + for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) { + X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields->type_entry); + if (field_class == X64CABIClass_Unknown) + return X64CABIClass_Unknown; + if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) { + working_class = field_class; + } + } + return working_class; + } + case ZigTypeIdUnion: { + // "If the size of an object is larger than four eightbytes, or it contains unaligned + // fields, it has class MEMORY" + if (ty_size > 32) + return X64CABIClass_MEMORY; + if (ty->data.unionation.layout != ContainerLayoutExtern) + return X64CABIClass_MEMORY; + // "If the size of the aggregate exceeds two eightbytes and the first eight- + // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument + // is passed in memory." + if (ty_size > 16) { + // Zig doesn't support vectors and large fp registers yet, so this will always + // be memory. + return X64CABIClass_MEMORY; + } + X64CABIClass working_class = X64CABIClass_Unknown; + for (uint32_t i = 0; i < ty->data.unionation.src_field_count; i += 1) { + X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.unionation.fields->type_entry); + if (field_class == X64CABIClass_Unknown) + return X64CABIClass_Unknown; + if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) { + working_class = field_class; + } + } + return working_class; + } + default: + return X64CABIClass_Unknown; + } +} + +// NOTE this does not depend on x86_64 static bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { size_t ty_size = type_size(g, ty); if (ty_size > g->pointer_size_bytes) @@ -1905,13 +1990,6 @@ static bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { get_codegen_ptr_type(ty) != nullptr); } -static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { - assert(alignment > 0); - LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name); - LLVMSetAlignment(result, alignment); - return result; -} - static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk, size_t src_i) { // Initialized from the type for some walks, but because of C var args, // initialized based on callsite instructions for that one. @@ -2047,98 +2125,79 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ } if (g->zig_target.arch.arch == ZigLLVM_x86_64) { + X64CABIClass abi_class = type_c_abi_x86_64_class(g, ty); size_t ty_size = type_size(g, ty); - if (ty->id == ZigTypeIdStruct || ty->id == ZigTypeIdUnion) { + if (abi_class == X64CABIClass_MEMORY) { assert(handle_is_ptr(ty)); - - // "If the size of an object is larger than four eightbytes, or it contains unaligned - // fields, it has class MEMORY" - if (ty_size > 32) { - switch (fn_walk->id) { - case FnWalkIdAttrs: - addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "byval"); - addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); - fn_walk->data.attrs.gen_i += 1; - break; - case FnWalkIdCall: - fn_walk->data.call.gen_param_values->append(val); - break; - case FnWalkIdTypes: { - ZigType *gen_type = get_pointer_to_type(g, ty, true); - fn_walk->data.types.gen_param_types->append(gen_type->type_ref); - fn_walk->data.types.param_di_types->append(gen_type->di_type); - break; - } - case FnWalkIdVars: { - di_arg_index = fn_walk->data.vars.gen_i; - var->value_ref = LLVMGetParam(llvm_fn, fn_walk->data.vars.gen_i); - dest_ty = get_pointer_to_type(g, ty, false); - fn_walk->data.vars.gen_i += 1; - goto var_ok; - } - case FnWalkIdInits: - if (var->decl_node) { - gen_var_debug_decl(g, var); - } - fn_walk->data.inits.gen_i += 1; - break; + switch (fn_walk->id) { + case FnWalkIdAttrs: + addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "byval"); + addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); + fn_walk->data.attrs.gen_i += 1; + break; + case FnWalkIdCall: + fn_walk->data.call.gen_param_values->append(val); + break; + case FnWalkIdTypes: { + ZigType *gen_type = get_pointer_to_type(g, ty, true); + fn_walk->data.types.gen_param_types->append(gen_type->type_ref); + fn_walk->data.types.param_di_types->append(gen_type->di_type); + break; } - return true; - } - } - if (ty->id == ZigTypeIdStruct) { - assert(handle_is_ptr(ty)); - // "If the size of the aggregate exceeds a single eightbyte, each is classified - // separately. Each eightbyte gets initialized to class NO_CLASS." - if (ty_size <= 8) { - bool contains_int = false; - for (size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { - if (type_is_c_abi_int(g, ty->data.structure.fields[i].type_entry)) { - contains_int = true; - break; + case FnWalkIdVars: { + di_arg_index = fn_walk->data.vars.gen_i; + var->value_ref = LLVMGetParam(llvm_fn, fn_walk->data.vars.gen_i); + dest_ty = get_pointer_to_type(g, ty, false); + fn_walk->data.vars.gen_i += 1; + goto var_ok; + } + case FnWalkIdInits: + if (var->decl_node) { + gen_var_debug_decl(g, var); } + fn_walk->data.inits.gen_i += 1; + break; + } + return true; + } else if (abi_class == X64CABIClass_INTEGER && ty_size <= 8) { + switch (fn_walk->id) { + case FnWalkIdAttrs: + fn_walk->data.attrs.gen_i += 1; + break; + case FnWalkIdCall: { + LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); + LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, val, ptr_to_int_type_ref, ""); + LLVMValueRef loaded = LLVMBuildLoad(g->builder, bitcasted, ""); + fn_walk->data.call.gen_param_values->append(loaded); + break; + } + case FnWalkIdTypes: { + ZigType *gen_type = get_int_type(g, false, ty_size * 8); + fn_walk->data.types.gen_param_types->append(gen_type->type_ref); + fn_walk->data.types.param_di_types->append(gen_type->di_type); + break; + } + case FnWalkIdVars: { + di_arg_index = fn_walk->data.vars.gen_i; + var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes); + fn_walk->data.vars.gen_i += 1; + dest_ty = ty; + goto var_ok; } - if (contains_int) { - switch (fn_walk->id) { - case FnWalkIdAttrs: - fn_walk->data.attrs.gen_i += 1; - break; - case FnWalkIdCall: { - LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); - LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, val, ptr_to_int_type_ref, ""); - LLVMValueRef loaded = LLVMBuildLoad(g->builder, bitcasted, ""); - fn_walk->data.call.gen_param_values->append(loaded); - break; - } - case FnWalkIdTypes: { - ZigType *gen_type = get_int_type(g, false, ty_size * 8); - fn_walk->data.types.gen_param_types->append(gen_type->type_ref); - fn_walk->data.types.param_di_types->append(gen_type->di_type); - break; - } - case FnWalkIdVars: { - di_arg_index = fn_walk->data.vars.gen_i; - var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes); - fn_walk->data.vars.gen_i += 1; - dest_ty = ty; - goto var_ok; - } - case FnWalkIdInits: { - clear_debug_source_node(g); - LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i); - LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); - LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); - gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); - if (var->decl_node) { - gen_var_debug_decl(g, var); - } - fn_walk->data.inits.gen_i += 1; - break; - } + case FnWalkIdInits: { + clear_debug_source_node(g); + LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i); + LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); + LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); + gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); + if (var->decl_node) { + gen_var_debug_decl(g, var); } - return true; + fn_walk->data.inits.gen_i += 1; + break; } } + return true; } } if (source_node != nullptr) { -- 2.54.0 From 9017efee220950b11070242415b6dc456d4442df Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 18:47:57 -0400 Subject: [PATCH 12/20] C ABI: support medium size structs & unions for x86_64 params See #1481 --- src/codegen.cpp | 12 +++++++++--- test/stage1/c_abi/cfuncs.c | 18 ++++++++++++++++++ test/stage1/c_abi/main.zig | 22 ++++++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/src/codegen.cpp b/src/codegen.cpp index a38cc869dd641948917e0cdc523516b8e4dda2b8..4be08b03e7c8cfd9d679161d7c6812d4d244b951 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -354,8 +354,12 @@ static void addLLVMFnAttrInt(LLVMValueRef fn_val, const char *attr_name, uint64_ return addLLVMAttrInt(fn_val, -1, attr_name, attr_val); } -static void addLLVMArgAttr(LLVMValueRef arg_val, unsigned param_index, const char *attr_name) { - return addLLVMAttr(arg_val, param_index + 1, attr_name); +static void addLLVMArgAttr(LLVMValueRef fn_val, unsigned param_index, const char *attr_name) { + return addLLVMAttr(fn_val, param_index + 1, attr_name); +} + +static void addLLVMArgAttrInt(LLVMValueRef fn_val, unsigned param_index, const char *attr_name, uint64_t attr_val) { + return addLLVMAttrInt(fn_val, param_index + 1, attr_name, attr_val); } static bool is_symbol_available(CodeGen *g, Buf *name) { @@ -2096,6 +2100,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ switch (fn_walk->id) { case FnWalkIdAttrs: addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); + addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty)); fn_walk->data.attrs.gen_i += 1; break; case FnWalkIdCall: @@ -2132,6 +2137,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ switch (fn_walk->id) { case FnWalkIdAttrs: addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "byval"); + addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty)); addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); fn_walk->data.attrs.gen_i += 1; break; @@ -2159,7 +2165,7 @@ static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_ break; } return true; - } else if (abi_class == X64CABIClass_INTEGER && ty_size <= 8) { + } else if (abi_class == X64CABIClass_INTEGER) { switch (fn_walk->id) { case FnWalkIdAttrs: fn_walk->data.attrs.gen_i += 1; diff --git a/test/stage1/c_abi/cfuncs.c b/test/stage1/c_abi/cfuncs.c index 393c3a4f5afbd27e0f868672f38bebe285b1eb57..ff249d2a594782f895ec5a211e940ac061d71ca8 100644 --- a/test/stage1/c_abi/cfuncs.c +++ b/test/stage1/c_abi/cfuncs.c @@ -52,6 +52,13 @@ struct SmallStructInts { }; void zig_small_struct_ints(struct SmallStructInts); +struct SplitStructInts { + uint64_t a; + uint8_t b; + uint32_t c; +}; +void zig_split_struct_ints(struct SplitStructInts); + void run_c_tests(void) { zig_u8(0xff); zig_u16(0xfffe); @@ -83,6 +90,11 @@ void run_c_tests(void) { struct SmallStructInts s = {1, 2, 3, 4}; zig_small_struct_ints(s); } + + { + struct SplitStructInts s = {1234, 100, 1337}; + zig_split_struct_ints(s); + } } void c_u8(uint8_t x) { @@ -167,3 +179,9 @@ void c_small_struct_ints(struct SmallStructInts x) { assert_or_panic(x.c == 3); assert_or_panic(x.d == 4); } + +void c_split_struct_ints(struct SplitStructInts x) { + assert_or_panic(x.a == 1234); + assert_or_panic(x.b == 100); + assert_or_panic(x.c == 1337); +} diff --git a/test/stage1/c_abi/main.zig b/test/stage1/c_abi/main.zig index ef425ceb67858735a03015d9c90177f55ae91cd2..e1356f50ef36a35814b98e365d16825ccb488e7b 100644 --- a/test/stage1/c_abi/main.zig +++ b/test/stage1/c_abi/main.zig @@ -181,3 +181,25 @@ export fn zig_small_struct_ints(x: SmallStructInts) void { assertOrPanic(x.c == 3); assertOrPanic(x.d == 4); } + +const SplitStructInt = extern struct { + a: u64, + b: u8, + c: u32, +}; +extern fn c_split_struct_ints(SplitStructInt) void; + +test "C ABI split struct of ints" { + var s = SplitStructInt{ + .a = 1234, + .b = 100, + .c = 1337, + }; + c_split_struct_ints(s); +} + +export fn zig_split_struct_ints(x: SplitStructInt) void { + assertOrPanic(x.a == 1234); + assertOrPanic(x.b == 100); + assertOrPanic(x.c == 1337); +} -- 2.54.0 From 9c169f3cf763c58f91b485f907f80cd8d08c4e12 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 20:09:33 -0400 Subject: [PATCH 13/20] C ABI: support returning large structs on x86_64 also panic instead of emitting bad code for returning small structs See #1481 --- src/all_types.hpp | 7 ++ src/analyze.cpp | 109 +++++++++++++++++++++++++++++-- src/analyze.hpp | 5 +- src/codegen.cpp | 126 +++++++----------------------------- test/behavior.zig | 1 - test/cases/bugs/1230.zig | 14 ---- test/stage1/c_abi/build.zig | 1 + test/stage1/c_abi/cfuncs.c | 25 ++++++- test/stage1/c_abi/main.zig | 34 ++++++++++ 9 files changed, 193 insertions(+), 129 deletions(-) delete mode 100644 test/cases/bugs/1230.zig diff --git a/src/all_types.hpp b/src/all_types.hpp index b80fe1705333cb85037db679027492b1c9d1b123..6adb53b774bc7d361a08501e15784093b8b5ba69 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -40,6 +40,13 @@ struct Tld; struct TldExport; struct IrAnalyze; +enum X64CABIClass { + X64CABIClass_Unknown, + X64CABIClass_MEMORY, + X64CABIClass_INTEGER, + X64CABIClass_SSE, +}; + struct IrExecutable { ZigList basic_block_list; Buf *name; diff --git a/src/analyze.cpp b/src/analyze.cpp index 0fe83e0d9624f4c0ab7e86db5d06dae204322b29..1c49f696bf3a1a39bbaf44d5e23d03648f152656 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1009,10 +1009,6 @@ ZigType *get_bound_fn_type(CodeGen *g, ZigFn *fn_entry) { return bound_fn_type; } -bool calling_convention_does_first_arg_return(CallingConvention cc) { - return cc == CallingConventionUnspecified; -} - const char *calling_convention_name(CallingConvention cc) { switch (cc) { case CallingConventionUnspecified: return "undefined"; @@ -1061,6 +1057,26 @@ ZigType *get_ptr_to_stack_trace_type(CodeGen *g) { return g->ptr_to_stack_trace_type; } +bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) { + if (fn_type_id->cc == CallingConventionUnspecified) { + return handle_is_ptr(fn_type_id->return_type); + } + if (fn_type_id->cc != CallingConventionC) { + return false; + } + if (type_is_c_abi_int(g, fn_type_id->return_type)) { + return false; + } + if (g->zig_target.arch.arch == ZigLLVM_x86_64) { + X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type); + if (abi_class == X64CABIClass_MEMORY) { + return true; + } + zig_panic("TODO implement C ABI for x86_64 return types. '%s'", buf_ptr(&fn_type_id->return_type->name)); + } + zig_panic("TODO implement C ABI for this architecture"); +} + ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { Error err; auto table_entry = g->fn_type_table.maybe_get(fn_type_id); @@ -1116,8 +1132,7 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { // next, loop over the parameters again and compute debug information // and codegen information if (!skip_debug_info) { - bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) && - handle_is_ptr(fn_type_id->return_type); + bool first_arg_return = want_first_arg_sret(g, fn_type_id); bool is_async = fn_type_id->cc == CallingConventionAsync; bool is_c_abi = fn_type_id->cc == CallingConventionC; bool prefix_arg_error_return_trace = g->have_err_ret_tracing && fn_type_can_fail(fn_type_id); @@ -6367,3 +6382,85 @@ not_integer: } return nullptr; } + +X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) { + size_t ty_size = type_size(g, ty); + if (get_codegen_ptr_type(ty) != nullptr) + return X64CABIClass_INTEGER; + switch (ty->id) { + case ZigTypeIdEnum: + case ZigTypeIdInt: + case ZigTypeIdBool: + return X64CABIClass_INTEGER; + case ZigTypeIdFloat: + return X64CABIClass_SSE; + case ZigTypeIdStruct: { + // "If the size of an object is larger than four eightbytes, or it contains unaligned + // fields, it has class MEMORY" + if (ty_size > 32) + return X64CABIClass_MEMORY; + if (ty->data.structure.layout != ContainerLayoutExtern) { + // TODO determine whether packed structs have any unaligned fields + return X64CABIClass_Unknown; + } + // "If the size of the aggregate exceeds two eightbytes and the first eight- + // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument + // is passed in memory." + if (ty_size > 16) { + // Zig doesn't support vectors and large fp registers yet, so this will always + // be memory. + return X64CABIClass_MEMORY; + } + X64CABIClass working_class = X64CABIClass_Unknown; + for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) { + X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields->type_entry); + if (field_class == X64CABIClass_Unknown) + return X64CABIClass_Unknown; + if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) { + working_class = field_class; + } + } + return working_class; + } + case ZigTypeIdUnion: { + // "If the size of an object is larger than four eightbytes, or it contains unaligned + // fields, it has class MEMORY" + if (ty_size > 32) + return X64CABIClass_MEMORY; + if (ty->data.unionation.layout != ContainerLayoutExtern) + return X64CABIClass_MEMORY; + // "If the size of the aggregate exceeds two eightbytes and the first eight- + // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument + // is passed in memory." + if (ty_size > 16) { + // Zig doesn't support vectors and large fp registers yet, so this will always + // be memory. + return X64CABIClass_MEMORY; + } + X64CABIClass working_class = X64CABIClass_Unknown; + for (uint32_t i = 0; i < ty->data.unionation.src_field_count; i += 1) { + X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.unionation.fields->type_entry); + if (field_class == X64CABIClass_Unknown) + return X64CABIClass_Unknown; + if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) { + working_class = field_class; + } + } + return working_class; + } + default: + return X64CABIClass_Unknown; + } +} + +// NOTE this does not depend on x86_64 +bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { + return (ty->id == ZigTypeIdInt || + ty->id == ZigTypeIdFloat || + ty->id == ZigTypeIdBool || + ty->id == ZigTypeIdEnum || + ty->id == ZigTypeIdVoid || + ty->id == ZigTypeIdUnreachable || + get_codegen_ptr_type(ty) != nullptr); +} + diff --git a/src/analyze.hpp b/src/analyze.hpp index 98e4412594468fcc8480cf46b802c43e465d73b3..cd3f52d9c5fa50a590f549cf7f920f8c07cdd76e 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -182,7 +182,6 @@ size_t type_id_index(ZigType *entry); ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id); Result type_is_copyable(CodeGen *g, ZigType *type_entry); LinkLib *create_link_lib(Buf *name); -bool calling_convention_does_first_arg_return(CallingConvention cc); LinkLib *add_link_lib(CodeGen *codegen, Buf *lib); uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry); @@ -211,6 +210,8 @@ bool calling_convention_allows_zig_types(CallingConvention cc); const char *calling_convention_name(CallingConvention cc); void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk); - +X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty); +bool type_is_c_abi_int(CodeGen *g, ZigType *ty); +bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id); #endif diff --git a/src/codegen.cpp b/src/codegen.cpp index 4be08b03e7c8cfd9d679161d7c6812d4d244b951..71caf18e286e7e63bec8ef2a55d60c4ec3f5e928 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -577,19 +577,25 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) { // use the ABI alignment, which is fine. } + unsigned init_gen_i = 0; if (!type_has_bits(return_type)) { // nothing to do } else if (type_is_codegen_pointer(return_type)) { addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull"); - } else if (handle_is_ptr(return_type) && calling_convention_does_first_arg_return(cc)) { + } else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) { addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret"); addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull"); + if (cc == CallingConventionC) { + addLLVMArgAttr(fn_table_entry->llvm_value, 0, "noalias"); + } + init_gen_i = 1; } // set parameter attributes FnWalk fn_walk = {}; fn_walk.id = FnWalkIdAttrs; fn_walk.data.attrs.fn = fn_table_entry; + fn_walk.data.attrs.gen_i = init_gen_i; walk_function_params(g, fn_type, &fn_walk); uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); @@ -1905,95 +1911,6 @@ static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *na return result; } -enum X64CABIClass { - X64CABIClass_Unknown, - X64CABIClass_MEMORY, - X64CABIClass_INTEGER, - X64CABIClass_SSE, -}; - -static X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) { - size_t ty_size = type_size(g, ty); - if (get_codegen_ptr_type(ty) != nullptr) - return X64CABIClass_INTEGER; - switch (ty->id) { - case ZigTypeIdEnum: - case ZigTypeIdInt: - case ZigTypeIdBool: - return X64CABIClass_INTEGER; - case ZigTypeIdFloat: - return X64CABIClass_SSE; - case ZigTypeIdStruct: { - // "If the size of an object is larger than four eightbytes, or it contains unaligned - // fields, it has class MEMORY" - if (ty_size > 32) - return X64CABIClass_MEMORY; - if (ty->data.structure.layout != ContainerLayoutExtern) { - // TODO determine whether packed structs have any unaligned fields - return X64CABIClass_Unknown; - } - // "If the size of the aggregate exceeds two eightbytes and the first eight- - // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument - // is passed in memory." - if (ty_size > 16) { - // Zig doesn't support vectors and large fp registers yet, so this will always - // be memory. - return X64CABIClass_MEMORY; - } - X64CABIClass working_class = X64CABIClass_Unknown; - for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) { - X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields->type_entry); - if (field_class == X64CABIClass_Unknown) - return X64CABIClass_Unknown; - if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) { - working_class = field_class; - } - } - return working_class; - } - case ZigTypeIdUnion: { - // "If the size of an object is larger than four eightbytes, or it contains unaligned - // fields, it has class MEMORY" - if (ty_size > 32) - return X64CABIClass_MEMORY; - if (ty->data.unionation.layout != ContainerLayoutExtern) - return X64CABIClass_MEMORY; - // "If the size of the aggregate exceeds two eightbytes and the first eight- - // byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument - // is passed in memory." - if (ty_size > 16) { - // Zig doesn't support vectors and large fp registers yet, so this will always - // be memory. - return X64CABIClass_MEMORY; - } - X64CABIClass working_class = X64CABIClass_Unknown; - for (uint32_t i = 0; i < ty->data.unionation.src_field_count; i += 1) { - X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.unionation.fields->type_entry); - if (field_class == X64CABIClass_Unknown) - return X64CABIClass_Unknown; - if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) { - working_class = field_class; - } - } - return working_class; - } - default: - return X64CABIClass_Unknown; - } -} - -// NOTE this does not depend on x86_64 -static bool type_is_c_abi_int(CodeGen *g, ZigType *ty) { - size_t ty_size = type_size(g, ty); - if (ty_size > g->pointer_size_bytes) - return false; - return (ty->id == ZigTypeIdInt || - ty->id == ZigTypeIdFloat || - ty->id == ZigTypeIdBool || - ty->id == ZigTypeIdEnum || - get_codegen_ptr_type(ty) != nullptr); -} - static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk, size_t src_i) { // Initialized from the type for some walks, but because of C var args, // initialized based on callsite instructions for that one. @@ -2327,15 +2244,13 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns LLVMValueRef value = ir_llvm_value(g, return_instruction->value); ZigType *return_type = return_instruction->value->value.type; - if (handle_is_ptr(return_type)) { - if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) { - assert(g->cur_ret_ptr); - gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value); - LLVMBuildRetVoid(g->builder); - } else { - LLVMValueRef by_val_value = gen_load_untyped(g, value, 0, false, ""); - LLVMBuildRet(g->builder, by_val_value); - } + if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) { + assert(g->cur_ret_ptr); + gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value); + LLVMBuildRetVoid(g->builder); + } else if (handle_is_ptr(return_type)) { + LLVMValueRef by_val_value = gen_load_untyped(g, value, 0, false, ""); + LLVMBuildRet(g->builder, by_val_value); } else { LLVMBuildRet(g->builder, value); } @@ -3551,8 +3466,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr CallingConvention cc = fn_type->data.fn.fn_type_id.cc; - bool first_arg_ret = ret_has_bits && handle_is_ptr(src_return_type) && - calling_convention_does_first_arg_return(cc); + bool first_arg_ret = ret_has_bits && want_first_arg_sret(g, fn_type_id); bool prefix_arg_err_ret_stack = get_prefix_arg_err_ret_stack(g, fn_type_id); bool is_var_args = fn_type_id->is_var_args; ZigList gen_param_values = {}; @@ -6260,13 +6174,14 @@ static void do_code_gen(CodeGen *g) { // Generate function definitions. for (size_t fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) { ZigFn *fn_table_entry = g->fn_defs.at(fn_i); - CallingConvention cc = fn_table_entry->type_entry->data.fn.fn_type_id.cc; + FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id; + CallingConvention cc = fn_type_id->cc; bool is_c_abi = cc == CallingConventionC; LLVMValueRef fn = fn_llvm_value(g, fn_table_entry); g->cur_fn = fn_table_entry; g->cur_fn_val = fn; - ZigType *return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type; + ZigType *return_type = fn_type_id->return_type; if (handle_is_ptr(return_type)) { g->cur_ret_ptr = LLVMGetParam(fn, 0); } else { @@ -6344,13 +6259,15 @@ static void do_code_gen(CodeGen *g) { ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); + unsigned gen_i_init = want_first_arg_sret(g, fn_type_id) ? 1 : 0; + // create debug variable declarations for variables and allocate all local variables FnWalk fn_walk_var = {}; fn_walk_var.id = FnWalkIdVars; fn_walk_var.data.vars.import = import; fn_walk_var.data.vars.fn = fn_table_entry; fn_walk_var.data.vars.llvm_fn = fn; - fn_walk_var.data.vars.gen_i = 0; + fn_walk_var.data.vars.gen_i = gen_i_init; for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) { ZigVar *var = fn_table_entry->variable_list.at(var_i); @@ -6429,6 +6346,7 @@ static void do_code_gen(CodeGen *g) { fn_walk_init.id = FnWalkIdInits; fn_walk_init.data.inits.fn = fn_table_entry; fn_walk_init.data.inits.llvm_fn = fn; + fn_walk_init.data.inits.gen_i = gen_i_init; walk_function_params(g, fn_table_entry->type_entry, &fn_walk_init); ir_render(g, fn_table_entry); diff --git a/test/behavior.zig b/test/behavior.zig index 6b35b0c030834e435d6bc91d8bb14bcedf2fbd14..25997a2a4b24119ea108bbf856fbfc9ef989ede2 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -9,7 +9,6 @@ comptime { _ = @import("cases/bitcast.zig"); _ = @import("cases/bool.zig"); _ = @import("cases/bugs/1111.zig"); - _ = @import("cases/bugs/1230.zig"); _ = @import("cases/bugs/1277.zig"); _ = @import("cases/bugs/1421.zig"); _ = @import("cases/bugs/394.zig"); diff --git a/test/cases/bugs/1230.zig b/test/cases/bugs/1230.zig deleted file mode 100644 index b782a77f0b3a9c44a46079351f235f71460e8cc5..0000000000000000000000000000000000000000 --- a/test/cases/bugs/1230.zig +++ /dev/null @@ -1,14 +0,0 @@ -const assert = @import("std").debug.assert; - -const S = extern struct { - x: i32, -}; - -extern fn ret_struct() S { - return S{ .x = 42 }; -} - -test "extern return small struct (bug 1230)" { - const s = ret_struct(); - assert(s.x == 42); -} diff --git a/test/stage1/c_abi/build.zig b/test/stage1/c_abi/build.zig index 02db8f7904bc52843382493e127ff2e7f10d9b89..e0a108aeb5e472395ca2c8404184452ff8dc6265 100644 --- a/test/stage1/c_abi/build.zig +++ b/test/stage1/c_abi/build.zig @@ -5,6 +5,7 @@ pub fn build(b: *Builder) void { const c_obj = b.addCObject("cfuncs", "cfuncs.c"); c_obj.setBuildMode(rel_opts); + c_obj.setNoStdLib(true); const main = b.addTest("main.zig"); main.setBuildMode(rel_opts); diff --git a/test/stage1/c_abi/cfuncs.c b/test/stage1/c_abi/cfuncs.c index ff249d2a594782f895ec5a211e940ac061d71ca8..102cd466b19f468ca357547cf9bf5975446d2ec1 100644 --- a/test/stage1/c_abi/cfuncs.c +++ b/test/stage1/c_abi/cfuncs.c @@ -59,6 +59,8 @@ struct SplitStructInts { }; void zig_split_struct_ints(struct SplitStructInts); +struct BigStruct zig_big_struct_both(struct BigStruct); + void run_c_tests(void) { zig_u8(0xff); zig_u16(0xfffe); @@ -77,8 +79,7 @@ void run_c_tests(void) { zig_bool(true); - // TODO making this non-static crashes for some reason - static uint8_t array[10] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}; + uint8_t array[10] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'}; zig_array(array); { @@ -95,6 +96,16 @@ void run_c_tests(void) { struct SplitStructInts s = {1234, 100, 1337}; zig_split_struct_ints(s); } + + { + struct BigStruct s = {30, 31, 32, 33, 34}; + struct BigStruct res = zig_big_struct_both(s); + assert_or_panic(res.a == 20); + assert_or_panic(res.b == 21); + assert_or_panic(res.c == 22); + assert_or_panic(res.d == 23); + assert_or_panic(res.e == 24); + } } void c_u8(uint8_t x) { @@ -185,3 +196,13 @@ void c_split_struct_ints(struct SplitStructInts x) { assert_or_panic(x.b == 100); assert_or_panic(x.c == 1337); } + +struct BigStruct c_big_struct_both(struct BigStruct x) { + assert_or_panic(x.a == 1); + assert_or_panic(x.b == 2); + assert_or_panic(x.c == 3); + assert_or_panic(x.d == 4); + assert_or_panic(x.e == 5); + struct BigStruct y = {10, 11, 12, 13, 14}; + return y; +} diff --git a/test/stage1/c_abi/main.zig b/test/stage1/c_abi/main.zig index e1356f50ef36a35814b98e365d16825ccb488e7b..196311a2836b208d6beb25b57ee95569e8348ae7 100644 --- a/test/stage1/c_abi/main.zig +++ b/test/stage1/c_abi/main.zig @@ -203,3 +203,37 @@ export fn zig_split_struct_ints(x: SplitStructInt) void { assertOrPanic(x.b == 100); assertOrPanic(x.c == 1337); } + +extern fn c_big_struct_both(BigStruct) BigStruct; + +test "C ABI sret and byval together" { + var s = BigStruct{ + .a = 1, + .b = 2, + .c = 3, + .d = 4, + .e = 5, + }; + var y = c_big_struct_both(s); + assertOrPanic(y.a == 10); + assertOrPanic(y.b == 11); + assertOrPanic(y.c == 12); + assertOrPanic(y.d == 13); + assertOrPanic(y.e == 14); +} + +export fn zig_big_struct_both(x: BigStruct) BigStruct { + assertOrPanic(x.a == 30); + assertOrPanic(x.b == 31); + assertOrPanic(x.c == 32); + assertOrPanic(x.d == 33); + assertOrPanic(x.e == 34); + var s = BigStruct{ + .a = 20, + .b = 21, + .c = 22, + .d = 23, + .e = 24, + }; + return s; +} -- 2.54.0 From 200589868992e4d482c646ad8d60a738fefe6302 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 20:19:55 -0400 Subject: [PATCH 14/20] improve panic message from previous commit --- src/analyze.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 1c49f696bf3a1a39bbaf44d5e23d03648f152656..a20c2091c94a43c7e5036f2d5760345be0a9fe41 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1072,9 +1072,10 @@ bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) { if (abi_class == X64CABIClass_MEMORY) { return true; } - zig_panic("TODO implement C ABI for x86_64 return types. '%s'", buf_ptr(&fn_type_id->return_type->name)); + zig_panic("TODO implement C ABI for x86_64 return types. type '%s'\nSee https://github.com/ziglang/zig/issues/1481", + buf_ptr(&fn_type_id->return_type->name)); } - zig_panic("TODO implement C ABI for this architecture"); + zig_panic("TODO implement C ABI for this architecture. See https://github.com/ziglang/zig/issues/1481"); } ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { -- 2.54.0 From d80a5c9a79908a211258da6deba1e5a8a81098dd Mon Sep 17 00:00:00 2001 From: Sahnvour Date: Sat, 8 Sep 2018 18:43:18 +0200 Subject: [PATCH 15/20] Fixes a path corruption when compiling on windows. --- src/os.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/os.cpp b/src/os.cpp index 0e62d84a48ca27ecf4ee0450242a032fce9b7b05..31ed703e62fba3379e1d13d50f8bcb7cb8ef2891 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -489,7 +489,7 @@ static Buf os_path_resolve_windows(Buf **paths_ptr, size_t paths_len) { } // determine which disk designator we will result with, if any - char result_drive_buf[2] = {'_', ':'}; + char result_drive_buf[3] = {'_', ':', '\0'}; // 0 needed for strlen later Slice result_disk_designator = str(""); WindowsPathKind have_drive_kind = WindowsPathKindNone; bool have_abs_path = false; -- 2.54.0 From 7c9f7b72c59e7c6de38038f512ae332fc164e8d7 Mon Sep 17 00:00:00 2001 From: Bas van den Berg Date: Sun, 9 Sep 2018 12:54:00 +0200 Subject: [PATCH 16/20] Add capacity and appendAssumeCapacity to ArrayList --- std/array_list.zig | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/std/array_list.zig b/std/array_list.zig index 298026d11c150d4179364c30c3e376538e732927..dda6f176eb4d96051faf7412d0fa69ef0a604b7d 100644 --- a/std/array_list.zig +++ b/std/array_list.zig @@ -62,6 +62,10 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { return self.len; } + pub fn capacity(self: Self) usize { + return self.items.len; + } + /// ArrayList takes ownership of the passed in slice. The slice must have been /// allocated with `allocator`. /// Deinitialize with `deinit` or use `toOwnedSlice`. @@ -102,6 +106,11 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { new_item_ptr.* = item; } + pub fn appendAssumeCapacity(self: *Self, item: T) void { + const new_item_ptr = self.addOneAssumeCapacity(); + new_item_ptr.* = item; + } + /// Removes the element at the specified index and returns it. /// The empty slot is filled from the end of the list. pub fn swapRemove(self: *Self, i: usize) T { @@ -138,7 +147,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { } pub fn ensureCapacity(self: *Self, new_capacity: usize) !void { - var better_capacity = self.items.len; + var better_capacity = self.capacity(); if (better_capacity >= new_capacity) return; while (true) { better_capacity += better_capacity / 2 + 8; @@ -150,8 +159,13 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { pub fn addOne(self: *Self) !*T { const new_length = self.len + 1; try self.ensureCapacity(new_length); + return self.addOneAssumeCapacity(); + } + + pub fn addOneAssumeCapacity(self: *Self) *T { + assert(self.count() < self.capacity()); const result = &self.items[self.len]; - self.len = new_length; + self.len += 1; return result; } @@ -191,6 +205,17 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type { }; } +test "std.ArrayList.init" { + var bytes: [1024]u8 = undefined; + const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; + + var list = ArrayList(i32).init(allocator); + defer list.deinit(); + + assert(list.count() == 0); + assert(list.capacity() == 0); +} + test "std.ArrayList.basic" { var bytes: [1024]u8 = undefined; const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator; -- 2.54.0 From 52f4e934a963a5497bd5dd9834e6c419cd171ae4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 10 Sep 2018 22:44:27 -0400 Subject: [PATCH 17/20] fix llvm assertion and missing compile error --- src/analyze.cpp | 3 +++ src/ir.cpp | 8 +++++++- test/compile_errors.zig | 1 + 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index a20c2091c94a43c7e5036f2d5760345be0a9fe41..e0539df10bbb560fade8a028d48262a473b9f965 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1224,6 +1224,9 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { fn_type->data.fn.gen_param_count = gen_param_types.length; + for (size_t i = 0; i < gen_param_types.length; i += 1) { + assert(gen_param_types.items[i] != nullptr); + } fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref, gen_param_types.items, (unsigned int)gen_param_types.length, fn_type_id->is_var_args); fn_type->type_ref = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0); diff --git a/src/ir.cpp b/src/ir.cpp index 30f40b3a1e3f529875c319b47d0e920f4269c3e1..77b34e7558f66841177d6f5dfb903d1cc577bafc 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -19643,7 +19643,7 @@ static ZigType *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnP return ira->codegen->builtin_types.entry_invalid; if (type_requires_comptime(param_type)) { if (!calling_convention_allows_zig_types(fn_type_id.cc)) { - ir_add_error(ira, &instruction->base, + ir_add_error(ira, param_type_value, buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'", buf_ptr(¶m_type->name), calling_convention_name(fn_type_id.cc))); return ira->codegen->builtin_types.entry_invalid; @@ -19654,6 +19654,12 @@ static ZigType *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnP out_val->data.x_type = get_generic_fn_type(ira->codegen, &fn_type_id); return ira->codegen->builtin_types.entry_type; } + if (!type_has_bits(param_type) && !calling_convention_allows_zig_types(fn_type_id.cc)) { + ir_add_error(ira, param_type_value, + buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'", + buf_ptr(¶m_type->name), calling_convention_name(fn_type_id.cc))); + return ira->codegen->builtin_types.entry_invalid; + } param_info->type = param_type; } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 185ca62973e2e3800b0d5f1a0f19601931436047..dda45a5897f0e8764fa44cc765279815b970f681 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1291,6 +1291,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ \\extern fn bar(x: *void) void { } , + ".tmp_source.zig:1:30: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'ccc'", ".tmp_source.zig:7:18: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'ccc'", ); -- 2.54.0 From c4f96ea745ae2aa56ec5cb2c871e5b3d86b04c8f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 11 Sep 2018 11:52:16 -0400 Subject: [PATCH 18/20] disable stage2 tests on all targets See #1364 --- build.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/build.zig b/build.zig index 6584e5ab1fc220efd6325768e294d642fe4e37b0..d4a159e5e6eb9781b231dcb2efb146e714deaacc 100644 --- a/build.zig +++ b/build.zig @@ -76,7 +76,11 @@ pub fn build(b: *Builder) !void { const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests"); test_stage2_step.dependOn(&test_stage2.step); - test_step.dependOn(test_stage2_step); + + // TODO see https://github.com/ziglang/zig/issues/1364 + if (false) { + test_step.dependOn(test_stage2_step); + } const all_modes = []builtin.Mode{ builtin.Mode.Debug, -- 2.54.0 From dd1338b0e6280b10b9b62ca73bf9ece34bd8524e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 11 Sep 2018 12:57:53 -0400 Subject: [PATCH 19/20] fix incorrect union const value generation closes #1381 The union was generated as a 3 byte struct when it needed to be 4 bytes so that the packed struct bitcast could work correctly. Now it recognizes this situation and adds padding bytes to become the correct size so that it can fit into an array. --- src/codegen.cpp | 15 ++++++++++++--- test/behavior.zig | 1 + test/cases/bugs/1381.zig | 21 +++++++++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 test/cases/bugs/1381.zig diff --git a/src/codegen.cpp b/src/codegen.cpp index 71caf18e286e7e63bec8ef2a55d60c4ec3f5e928..2548b3fa8c9e2651b3ddc618a464eb7bf67ae21e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3218,7 +3218,8 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable, assert(var->value->type == init_value->value.type); ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false, PtrLenSingle, var->align_bytes, 0, 0); - gen_assign_raw(g, var->value_ref, var_ptr_type, ir_llvm_value(g, init_value)); + LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value); + gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val); } else { bool want_safe = ir_want_runtime_safety(g, &decl_var_instruction->base); if (want_safe) { @@ -5863,12 +5864,20 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c LLVMValueRef tag_value = bigint_to_llvm_const(type_entry->data.unionation.tag_type->type_ref, &const_val->data.x_union.tag); - LLVMValueRef fields[2]; + LLVMValueRef fields[3]; fields[type_entry->data.unionation.gen_union_index] = union_value_ref; fields[type_entry->data.unionation.gen_tag_index] = tag_value; if (make_unnamed_struct) { - return LLVMConstStruct(fields, 2, false); + LLVMValueRef result = LLVMConstStruct(fields, 2, false); + size_t expected_sz = LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref); + size_t actual_sz = LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(result)); + if (actual_sz < expected_sz) { + unsigned pad_sz = expected_sz - actual_sz; + fields[2] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_sz)); + result = LLVMConstStruct(fields, 3, false); + } + return result; } else { return LLVMConstNamedStruct(type_entry->type_ref, fields, 2); } diff --git a/test/behavior.zig b/test/behavior.zig index 25997a2a4b24119ea108bbf856fbfc9ef989ede2..cae8c4b0f35a5e5d2fdfaed74a4f1b12bf36cfcf 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -10,6 +10,7 @@ comptime { _ = @import("cases/bool.zig"); _ = @import("cases/bugs/1111.zig"); _ = @import("cases/bugs/1277.zig"); + _ = @import("cases/bugs/1381.zig"); _ = @import("cases/bugs/1421.zig"); _ = @import("cases/bugs/394.zig"); _ = @import("cases/bugs/655.zig"); diff --git a/test/cases/bugs/1381.zig b/test/cases/bugs/1381.zig new file mode 100644 index 0000000000000000000000000000000000000000..2d452da15610e28f13c2a5d9811791435f0b757b --- /dev/null +++ b/test/cases/bugs/1381.zig @@ -0,0 +1,21 @@ +const std = @import("std"); + +const B = union(enum) { + D: u8, + E: u16, +}; + +const A = union(enum) { + B: B, + C: u8, +}; + +test "union that needs padding bytes inside an array" { + var as = []A{ + A{ .B = B{ .D = 1 } }, + A{ .B = B{ .D = 1 } }, + }; + + const a = as[0].B; + std.debug.assertOrPanic(a.D == 1); +} -- 2.54.0 From 7dd3c3814de0caf808bc112aa07044cdd8bba135 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 11 Sep 2018 15:16:50 -0400 Subject: [PATCH 20/20] fix incorrect error union const value generation closes #1442 zig needed to insert explicit padding into this structure before it got bitcasted. --- src/analyze.cpp | 15 +++++++++++++-- src/codegen.cpp | 38 +++++++++++++++++++++++++++++--------- std/os/index.zig | 36 +++++++++++++++++++----------------- test/behavior.zig | 1 + test/cases/bugs/1442.zig | 11 +++++++++++ 5 files changed, 73 insertions(+), 28 deletions(-) create mode 100644 test/cases/bugs/1442.zig diff --git a/src/analyze.cpp b/src/analyze.cpp index e0539df10bbb560fade8a028d48262a473b9f965..07c7b94e2528f5725c6543e4397bdf10fa14cc6e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -5882,12 +5882,23 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { } case ZigTypeIdErrorUnion: { - buf_appendf(buf, "(error union %s constant)", buf_ptr(&type_entry->name)); + buf_appendf(buf, "%s(", buf_ptr(&type_entry->name)); + if (const_val->data.x_err_union.err == nullptr) { + render_const_value(g, buf, const_val->data.x_err_union.payload); + } else { + buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->data.error_union.err_set_type->name), + buf_ptr(&const_val->data.x_err_union.err->name)); + } + buf_appendf(buf, ")"); return; } case ZigTypeIdUnion: { - buf_appendf(buf, "(union %s constant)", buf_ptr(&type_entry->name)); + uint64_t tag = bigint_as_unsigned(&const_val->data.x_union.tag); + TypeUnionField *field = &type_entry->data.unionation.fields[tag]; + buf_appendf(buf, "%s { .%s = ", buf_ptr(&type_entry->name), buf_ptr(field->name)); + render_const_value(g, buf, const_val->data.x_union.payload); + buf_append_str(buf, "}"); return; } case ZigTypeIdErrorSet: diff --git a/src/codegen.cpp b/src/codegen.cpp index 2548b3fa8c9e2651b3ddc618a464eb7bf67ae21e..5b6c53e8f83ed31d310d6267947224aead86d8ea 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -5870,13 +5870,17 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c if (make_unnamed_struct) { LLVMValueRef result = LLVMConstStruct(fields, 2, false); - size_t expected_sz = LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref); - size_t actual_sz = LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(result)); - if (actual_sz < expected_sz) { - unsigned pad_sz = expected_sz - actual_sz; + uint64_t last_field_offset = LLVMOffsetOfElement(g->target_data_ref, LLVMTypeOf(result), 1); + uint64_t end_offset = last_field_offset + + LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(fields[1])); + uint64_t expected_sz = LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref); + unsigned pad_sz = expected_sz - end_offset; + if (pad_sz != 0) { fields[2] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_sz)); result = LLVMConstStruct(fields, 3, false); } + uint64_t actual_sz = LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(result)); + assert(actual_sz == expected_sz); return result; } else { return LLVMConstNamedStruct(type_entry->type_ref, fields, 2); @@ -5917,13 +5921,29 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c err_payload_value = gen_const_val(g, payload_val, ""); make_unnamed_struct = is_llvm_value_unnamed_type(payload_val->type, err_payload_value); } - LLVMValueRef fields[] = { - err_tag_value, - err_payload_value, - }; if (make_unnamed_struct) { - return LLVMConstStruct(fields, 2, false); + uint64_t payload_off = LLVMOffsetOfElement(g->target_data_ref, type_entry->type_ref, 1); + uint64_t err_sz = LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(err_tag_value)); + unsigned pad_sz = payload_off - err_sz; + if (pad_sz == 0) { + LLVMValueRef fields[] = { + err_tag_value, + err_payload_value, + }; + return LLVMConstStruct(fields, 2, false); + } else { + LLVMValueRef fields[] = { + err_tag_value, + LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_sz)), + err_payload_value, + }; + return LLVMConstStruct(fields, 3, false); + } } else { + LLVMValueRef fields[] = { + err_tag_value, + err_payload_value, + }; return LLVMConstNamedStruct(type_entry->type_ref, fields, 2); } } diff --git a/std/os/index.zig b/std/os/index.zig index 3ab73a79ac120cbd79d8b675f99e22603dca39a9..8e92fc23c5471b93ee6690561e9f99972f26abd4 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -343,23 +343,25 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void { const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len)); const rc = posix.write(fd, bytes.ptr + index, amt_to_write); const write_err = posix.getErrno(rc); - if (write_err > 0) { - return switch (write_err) { - posix.EINTR => continue, - posix.EINVAL, posix.EFAULT => unreachable, - posix.EAGAIN => PosixWriteError.WouldBlock, - posix.EBADF => PosixWriteError.FileClosed, - posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired, - posix.EDQUOT => PosixWriteError.DiskQuota, - posix.EFBIG => PosixWriteError.FileTooBig, - posix.EIO => PosixWriteError.InputOutput, - posix.ENOSPC => PosixWriteError.NoSpaceLeft, - posix.EPERM => PosixWriteError.AccessDenied, - posix.EPIPE => PosixWriteError.BrokenPipe, - else => unexpectedErrorPosix(write_err), - }; + switch (write_err) { + 0 => { + index += rc; + continue; + }, + posix.EINTR => continue, + posix.EINVAL => unreachable, + posix.EFAULT => unreachable, + posix.EAGAIN => return PosixWriteError.WouldBlock, + posix.EBADF => return PosixWriteError.FileClosed, + posix.EDESTADDRREQ => return PosixWriteError.DestinationAddressRequired, + posix.EDQUOT => return PosixWriteError.DiskQuota, + posix.EFBIG => return PosixWriteError.FileTooBig, + posix.EIO => return PosixWriteError.InputOutput, + posix.ENOSPC => return PosixWriteError.NoSpaceLeft, + posix.EPERM => return PosixWriteError.AccessDenied, + posix.EPIPE => return PosixWriteError.BrokenPipe, + else => return unexpectedErrorPosix(write_err), } - index += rc; } } @@ -1614,7 +1616,7 @@ pub const Dir = struct { return null; } const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr); - if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{'.', '.'})) + if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{ '.', '.' })) continue; // Trust that Windows gives us valid UTF-16LE const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable; diff --git a/test/behavior.zig b/test/behavior.zig index cae8c4b0f35a5e5d2fdfaed74a4f1b12bf36cfcf..869024f2967fb1e34f102493c547b17954054571 100644 --- a/test/behavior.zig +++ b/test/behavior.zig @@ -12,6 +12,7 @@ comptime { _ = @import("cases/bugs/1277.zig"); _ = @import("cases/bugs/1381.zig"); _ = @import("cases/bugs/1421.zig"); + _ = @import("cases/bugs/1442.zig"); _ = @import("cases/bugs/394.zig"); _ = @import("cases/bugs/655.zig"); _ = @import("cases/bugs/656.zig"); diff --git a/test/cases/bugs/1442.zig b/test/cases/bugs/1442.zig new file mode 100644 index 0000000000000000000000000000000000000000..d7057d9ed19309850cf7f29851363fb505046841 --- /dev/null +++ b/test/cases/bugs/1442.zig @@ -0,0 +1,11 @@ +const std = @import("std"); + +const Union = union(enum) { + Text: []const u8, + Color: u32, +}; + +test "const error union field alignment" { + var union_or_err: error!Union = Union{ .Color = 1234 }; + std.debug.assertOrPanic((union_or_err catch unreachable).Color == 1234); +} -- 2.54.0