From a375bd0d9f5c7773598a8ea31af8c963d6a79706 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 6 Sep 2018 11:58:58 -0400 Subject: 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(-) (limited to 'src/codegen.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index 0300ccca99..20d700fbbd 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); -- cgit v1.2.3 From a9a925e500c66b8eb57d619b9b6828c70a13d4c5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 6 Sep 2018 16:29:35 -0400 Subject: 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 (limited to 'src/codegen.cpp') diff --git a/src/analyze.cpp b/src/analyze.cpp index aa4fda7624..7014c0ea9d 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 41cc50916e..f13c528c5a 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 20d700fbbd..f35f146bb4 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) { - give_up_with_c_abi_error(g, source_node); - } else { - give_up_with_c_abi_error(g, source_node); + // 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) { + 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 08bb5635d9..4e323eaf7b 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 79192c3e9a..96112fe687 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 b3aaa263d6..11fb55344f 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 0000000000..02db8f7904 --- /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 0000000000..aef4618747 --- /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 0000000000..c409def6c7 --- /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); +} -- cgit v1.2.3 From be6cccb3a51512dea011326d9ad30ad495e7c716 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 11:52:57 -0400 Subject: 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(-) (limited to 'src/codegen.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index dbaa3b5467..a89a94c0a0 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 7014c0ea9d..a658c469ac 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 f35f146bb4..469d62f3a2 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 6297c4611b..785ba9f05d 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 aef4618747..bfdaa006ef 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 c409def6c7..5f5e0b0ca6 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); +} -- cgit v1.2.3 From 04d7b565f78895d96e5a43e8b1f4873ea5f529cf Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 12:23:50 -0400 Subject: 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(-) (limited to 'src/codegen.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index a89a94c0a0..5ce8e88669 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 a658c469ac..0fe83e0d96 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 f13c528c5a..98e4412594 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 469d62f3a2..e96445ebe3 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 785ba9f05d..b1a4dbf6e2 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 -- cgit v1.2.3 From c528c0090089041e63f9bdbb52f951e53ce94631 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 12:59:59 -0400 Subject: stage1: refactor param vars for C ABI --- src/all_types.hpp | 10 ++++ src/codegen.cpp | 155 +++++++++++++++++++++++------------------------------- 2 files changed, 76 insertions(+), 89 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index 5ce8e88669..649250bf3e 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 e96445ebe3..19d2781ac0 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); -- cgit v1.2.3 From 421ca1523feec17db75e7c53b05e5abe4c88d58f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 13:24:41 -0400 Subject: 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(-) (limited to 'src/codegen.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index 649250bf3e..b80fe17053 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 19d2781ac0..d4de593529 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); -- cgit v1.2.3 From 743b2e4afc72f436a73977f896b32e6041785795 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 13:51:11 -0400 Subject: 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(-) (limited to 'src/codegen.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index d4de593529..8acc7e9702 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 bfdaa006ef..393c3a4f5a 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 5f5e0b0ca6..ef425ceb67 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); +} -- cgit v1.2.3 From 85534a26c6ddea6277e79b12b759188689f3da43 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 18:34:19 -0400 Subject: stage1: function to classify x86_64 abi types --- src/codegen.cpp | 241 +++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 150 insertions(+), 91 deletions(-) (limited to 'src/codegen.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index 8acc7e9702..a38cc869dd 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; } - 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 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); } - return true; + fn_walk->data.inits.gen_i += 1; + break; } } + return true; } } if (source_node != nullptr) { -- cgit v1.2.3 From 9017efee220950b11070242415b6dc456d4442df Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 18:47:57 -0400 Subject: 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(-) (limited to 'src/codegen.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index a38cc869dd..4be08b03e7 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 393c3a4f5a..ff249d2a59 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 ef425ceb67..e1356f50ef 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); +} -- cgit v1.2.3 From 9c169f3cf763c58f91b485f907f80cd8d08c4e12 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 7 Sep 2018 20:09:33 -0400 Subject: 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 (limited to 'src/codegen.cpp') diff --git a/src/all_types.hpp b/src/all_types.hpp index b80fe17053..6adb53b774 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 0fe83e0d96..1c49f696bf 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 98e4412594..cd3f52d9c5 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 4be08b03e7..71caf18e28 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 6b35b0c030..25997a2a4b 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 b782a77f0b..0000000000 --- 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 02db8f7904..e0a108aeb5 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 ff249d2a59..102cd466b1 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 e1356f50ef..196311a283 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; +} -- cgit v1.2.3 From dd1338b0e6280b10b9b62ca73bf9ece34bd8524e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 11 Sep 2018 12:57:53 -0400 Subject: 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 (limited to 'src/codegen.cpp') diff --git a/src/codegen.cpp b/src/codegen.cpp index 71caf18e28..2548b3fa8c 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 25997a2a4b..cae8c4b0f3 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 0000000000..2d452da156 --- /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); +} -- cgit v1.2.3 From 7dd3c3814de0caf808bc112aa07044cdd8bba135 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 11 Sep 2018 15:16:50 -0400 Subject: 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 (limited to 'src/codegen.cpp') diff --git a/src/analyze.cpp b/src/analyze.cpp index e0539df10b..07c7b94e25 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 2548b3fa8c..5b6c53e8f8 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 3ab73a79ac..8e92fc23c5 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 cae8c4b0f3..869024f296 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 0000000000..d7057d9ed1 --- /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); +} -- cgit v1.2.3