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