Merge branch 'c-abi'

closes #1411
closes #1264
This commit is contained in:
Andrew Kelley 2018-09-07 13:56:54 -04:00
commit 7505529e44
No known key found for this signature in database
GPG Key ID: 4E7CD66038A4D47C
11 changed files with 927 additions and 133 deletions

View File

@ -3284,4 +3284,53 @@ enum FloatMode {
FloatModeStrict,
};
enum FnWalkId {
FnWalkIdAttrs,
FnWalkIdCall,
FnWalkIdTypes,
FnWalkIdVars,
FnWalkIdInits,
};
struct FnWalkAttrs {
ZigFn *fn;
unsigned gen_i;
};
struct FnWalkCall {
ZigList<LLVMValueRef> *gen_param_values;
IrInstructionCall *inst;
bool is_var_args;
};
struct FnWalkTypes {
ZigList<ZigLLVMDIType *> *param_di_types;
ZigList<LLVMTypeRef> *gen_param_types;
};
struct FnWalkVars {
ImportTableEntry *import;
LLVMValueRef llvm_fn;
ZigFn *fn;
ZigVar *var;
unsigned gen_i;
};
struct FnWalkInits {
LLVMValueRef llvm_fn;
ZigFn *fn;
unsigned gen_i;
};
struct FnWalk {
FnWalkId id;
union {
FnWalkAttrs attrs;
FnWalkCall call;
FnWalkTypes types;
FnWalkVars vars;
FnWalkInits inits;
} data;
};
#endif

View File

@ -1119,18 +1119,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<LLVMTypeRef>(4 + fn_type_id->param_count);
ZigList<LLVMTypeRef> 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<ZigLLVMDIType*>(5 + fn_type_id->param_count);
param_di_types[0] = fn_type_id->return_type->di_type;
size_t gen_param_index = 0;
ZigList<ZigLLVMDIType *> 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 +1138,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 +1148,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,6 +1179,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 (is_c_abi)
continue;
if (type_has_bits(type_entry)) {
ZigType *gen_type;
if (handle_is_ptr(type_entry)) {
@ -1195,23 +1190,28 @@ 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;
if (is_c_abi) {
FnWalk fn_walk = {};
fn_walk.id = FnWalkIdTypes;
fn_walk.data.types.param_di_types = &param_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,
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);

View File

@ -210,4 +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);
void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk);
#endif

View File

@ -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) {
@ -1858,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));
@ -1888,6 +1869,381 @@ 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 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 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)
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;
}
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;
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;
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 ||
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;
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;
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;
}
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;
}
// 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;
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: {
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;
}
case FnWalkIdInits:
if (var->decl_node) {
gen_var_debug_decl(g, var);
}
fn_walk->data.inits.gen_i += 1;
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;
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;
}
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;
}
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;
}
}
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;
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) {
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;
}
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;
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 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();
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();
}
}
}
static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutable *executable,
IrInstructionSaveErrRetAddr *save_err_ret_addr_instruction)
{
@ -3128,40 +3484,31 @@ 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 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<LLVMValueRef>(actual_param_count);
size_t gen_param_index = 0;
ZigList<LLVMValueRef> 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;
}
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);
gen_param_values[gen_param_index] = param_value;
gen_param_index += 1;
}
gen_param_values.append(err_val_ptr);
}
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) {
@ -3176,12 +3523,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 +3537,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, "");
}
@ -5721,13 +6068,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))) {
@ -5735,16 +6075,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);
@ -5865,6 +6195,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;
@ -5888,7 +6220,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) {
@ -5948,6 +6280,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_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);
@ -5966,6 +6304,9 @@ 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) {
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;
@ -6017,33 +6358,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;
for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) {
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);

View File

@ -61,5 +61,4 @@ void codegen_translate_c(CodeGen *g, Buf *path);
Buf *codegen_generate_builtin_source(CodeGen *g);
#endif

View File

@ -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;
}

View File

@ -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");
}
}

View File

@ -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);

View File

@ -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);
}

169
test/stage1/c_abi/cfuncs.c Normal file
View File

@ -0,0 +1,169 @@
#include <inttypes.h>
#include <stdlib.h>
#include <stdbool.h>
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 zig_f32(float);
void zig_f64(double);
void zig_ptr(void *);
void zig_bool(bool);
void zig_array(uint8_t[10]);
struct BigStruct {
uint64_t a;
uint64_t b;
uint64_t c;
uint64_t d;
uint8_t e;
};
void zig_big_struct(struct BigStruct);
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);
zig_u16(0xfffe);
zig_u32(0xfffffffd);
zig_u64(0xfffffffffffffffc);
zig_i8(-1);
zig_i16(-2);
zig_i32(-3);
zig_i64(-4);
zig_f32(12.34f);
zig_f64(56.78);
zig_ptr((void*)0xdeadbeefL);
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);
{
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) {
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);
}
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);
}
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);
}

183
test/stage1/c_abi/main.zig Normal file
View File

@ -0,0 +1,183 @@
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);
}
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);
}
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);
}