add C ABI tests
This commit is contained in:
parent
a375bd0d9f
commit
a9a925e500
122
src/analyze.cpp
122
src/analyze.cpp
@ -1061,6 +1061,77 @@ ZigType *get_ptr_to_stack_trace_type(CodeGen *g) {
|
|||||||
return g->ptr_to_stack_trace_type;
|
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<LLVMTypeRef> *gen_param_types,
|
||||||
|
ZigList<ZigLLVMDIType *> *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) {
|
ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
|
||||||
Error err;
|
Error err;
|
||||||
auto table_entry = g->fn_type_table.maybe_get(fn_type_id);
|
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) &&
|
bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) &&
|
||||||
handle_is_ptr(fn_type_id->return_type);
|
handle_is_ptr(fn_type_id->return_type);
|
||||||
bool is_async = fn_type_id->cc == CallingConventionAsync;
|
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);
|
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 making the first argument the return value
|
||||||
// +1 for maybe first argument the error return trace
|
// +1 for maybe first argument the error return trace
|
||||||
// +2 for maybe arguments async allocator and error code pointer
|
// +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 because 0 is the return type and
|
||||||
// +1 for maybe making first arg ret val and
|
// +1 for maybe making first arg ret val and
|
||||||
// +1 for maybe first argument the error return trace
|
// +1 for maybe first argument the error return trace
|
||||||
// +2 for maybe arguments async allocator and error code pointer
|
// +2 for maybe arguments async allocator and error code pointer
|
||||||
ZigLLVMDIType **param_di_types = allocate<ZigLLVMDIType*>(5 + fn_type_id->param_count);
|
ZigList<ZigLLVMDIType *> param_di_types = {};
|
||||||
param_di_types[0] = fn_type_id->return_type->di_type;
|
param_di_types.append(fn_type_id->return_type->di_type);
|
||||||
size_t gen_param_index = 0;
|
|
||||||
ZigType *gen_return_type;
|
ZigType *gen_return_type;
|
||||||
if (is_async) {
|
if (is_async) {
|
||||||
gen_return_type = get_pointer_to_type(g, g->builtin_types.entry_u8, false);
|
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;
|
gen_return_type = g->builtin_types.entry_void;
|
||||||
} else if (first_arg_return) {
|
} else if (first_arg_return) {
|
||||||
ZigType *gen_type = get_pointer_to_type(g, fn_type_id->return_type, false);
|
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_types.append(gen_type->type_ref);
|
||||||
gen_param_index += 1;
|
param_di_types.append(gen_type->di_type);
|
||||||
// after the gen_param_index += 1 because 0 is the return type
|
|
||||||
param_di_types[gen_param_index] = gen_type->di_type;
|
|
||||||
gen_return_type = g->builtin_types.entry_void;
|
gen_return_type = g->builtin_types.entry_void;
|
||||||
} else {
|
} else {
|
||||||
gen_return_type = fn_type_id->return_type;
|
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) {
|
if (prefix_arg_error_return_trace) {
|
||||||
ZigType *gen_type = get_ptr_to_stack_trace_type(g);
|
ZigType *gen_type = get_ptr_to_stack_trace_type(g);
|
||||||
gen_param_types[gen_param_index] = gen_type->type_ref;
|
gen_param_types.append(gen_type->type_ref);
|
||||||
gen_param_index += 1;
|
param_di_types.append(gen_type->di_type);
|
||||||
// after the gen_param_index += 1 because 0 is the return type
|
|
||||||
param_di_types[gen_param_index] = gen_type->di_type;
|
|
||||||
}
|
}
|
||||||
if (is_async) {
|
if (is_async) {
|
||||||
{
|
{
|
||||||
// async allocator param
|
// async allocator param
|
||||||
ZigType *gen_type = fn_type_id->async_allocator_type;
|
ZigType *gen_type = fn_type_id->async_allocator_type;
|
||||||
gen_param_types[gen_param_index] = gen_type->type_ref;
|
gen_param_types.append(gen_type->type_ref);
|
||||||
gen_param_index += 1;
|
param_di_types.append(gen_type->di_type);
|
||||||
// after the gen_param_index += 1 because 0 is the return type
|
|
||||||
param_di_types[gen_param_index] = gen_type->di_type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
// error code pointer
|
// error code pointer
|
||||||
ZigType *gen_type = get_pointer_to_type(g, g->builtin_types.entry_global_error_set, false);
|
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_types.append(gen_type->type_ref);
|
||||||
gen_param_index += 1;
|
param_di_types.append(gen_type->di_type);
|
||||||
// after the gen_param_index += 1 because 0 is the return type
|
|
||||||
param_di_types[gen_param_index] = 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)))
|
if ((err = ensure_complete_type(g, type_entry)))
|
||||||
return g->builtin_types.entry_invalid;
|
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;
|
ZigType *gen_type;
|
||||||
if (handle_is_ptr(type_entry)) {
|
if (handle_is_ptr(type_entry)) {
|
||||||
gen_type = get_pointer_to_type(g, type_entry, true);
|
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 {
|
} else {
|
||||||
gen_type = type_entry;
|
gen_type = type_entry;
|
||||||
}
|
}
|
||||||
gen_param_types[gen_param_index] = gen_type->type_ref;
|
gen_param_info->gen_index = gen_param_types.length;
|
||||||
gen_param_info->gen_index = gen_param_index;
|
|
||||||
gen_param_info->type = gen_type;
|
gen_param_info->type = gen_type;
|
||||||
|
gen_param_types.append(gen_type->type_ref);
|
||||||
|
|
||||||
gen_param_index += 1;
|
param_di_types.append(gen_type->di_type);
|
||||||
|
|
||||||
// after the gen_param_index += 1 because 0 is the return type
|
|
||||||
param_di_types[gen_param_index] = 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,
|
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->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);
|
g->fn_type_table.put(&fn_type->data.fn.fn_type_id, fn_type);
|
||||||
|
@ -210,4 +210,6 @@ ZigType *get_primitive_type(CodeGen *g, Buf *name);
|
|||||||
bool calling_convention_allows_zig_types(CallingConvention cc);
|
bool calling_convention_allows_zig_types(CallingConvention cc);
|
||||||
const char *calling_convention_name(CallingConvention cc);
|
const char *calling_convention_name(CallingConvention cc);
|
||||||
|
|
||||||
|
bool type_is_c_abi_int(CodeGen *g, ZigType *ty);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
242
src/codegen.cpp
242
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);
|
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<LLVMValueRef> *gen_param_values, LLVMValueRef val,
|
static void gen_c_abi_param(CodeGen *g, ZigList<LLVMValueRef> *gen_param_values, LLVMValueRef val,
|
||||||
ZigType *ty, AstNode *source_node)
|
ZigType *ty, AstNode *source_node)
|
||||||
{
|
{
|
||||||
if (ty->id == ZigTypeIdInt ||
|
if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat ||
|
||||||
ty->id == ZigTypeIdFloat ||
|
ty->id == ZigTypeIdInt // TODO investigate if we need to change this
|
||||||
ty->id == ZigTypeIdBool ||
|
) {
|
||||||
ty->id == ZigTypeIdEnum ||
|
gen_param_values->append(val);
|
||||||
get_codegen_ptr_type(ty) != nullptr)
|
return;
|
||||||
{
|
}
|
||||||
|
|
||||||
|
// Arrays are just pointers
|
||||||
|
if (ty->id == ZigTypeIdArray) {
|
||||||
|
assert(handle_is_ptr(ty));
|
||||||
gen_param_values->append(val);
|
gen_param_values->append(val);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g->zig_target.arch.arch == ZigLLVM_x86_64) {
|
if (g->zig_target.arch.arch == ZigLLVM_x86_64) {
|
||||||
give_up_with_c_abi_error(g, source_node);
|
// This code all assumes that val is a pointer.
|
||||||
} else {
|
assert(handle_is_ptr(ty));
|
||||||
give_up_with_c_abi_error(g, source_node);
|
|
||||||
|
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) {
|
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
|
// 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) {
|
static void ensure_cache_dir(CodeGen *g) {
|
||||||
int err;
|
int err;
|
||||||
if ((err = os_make_path(&g->cache_dir))) {
|
if ((err = os_make_path(&g->cache_dir))) {
|
||||||
@ -5899,6 +6094,8 @@ static void do_code_gen(CodeGen *g) {
|
|||||||
// Generate function definitions.
|
// Generate function definitions.
|
||||||
for (size_t fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) {
|
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);
|
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);
|
LLVMValueRef fn = fn_llvm_value(g, fn_table_entry);
|
||||||
g->cur_fn = fn_table_entry;
|
g->cur_fn = fn_table_entry;
|
||||||
@ -5922,7 +6119,7 @@ static void do_code_gen(CodeGen *g) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// error return tracing setup
|
// 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;
|
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;
|
LLVMValueRef err_ret_array_val = nullptr;
|
||||||
if (have_err_ret_trace_stack) {
|
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);
|
ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base);
|
||||||
|
|
||||||
// create debug variable declarations for variables and allocate all local variables
|
// 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) {
|
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);
|
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),
|
buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1),
|
||||||
var->value->type->di_type, !g->strip_debug_symbols, 0);
|
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 {
|
} else {
|
||||||
assert(var->gen_arg_index != SIZE_MAX);
|
assert(var->gen_arg_index != SIZE_MAX);
|
||||||
ZigType *gen_type;
|
ZigType *gen_type;
|
||||||
@ -6056,7 +6256,14 @@ static void do_code_gen(CodeGen *g) {
|
|||||||
// create debug variable declarations for parameters
|
// create debug variable declarations for parameters
|
||||||
// rely on the first variables in the variable_list being parameters.
|
// rely on the first variables in the variable_list being parameters.
|
||||||
size_t next_var_i = 0;
|
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) {
|
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];
|
FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i];
|
||||||
if (info->gen_index == SIZE_MAX)
|
if (info->gen_index == SIZE_MAX)
|
||||||
continue;
|
continue;
|
||||||
@ -6078,6 +6285,7 @@ static void do_code_gen(CodeGen *g) {
|
|||||||
gen_var_debug_decl(g, variable);
|
gen_var_debug_decl(g, variable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
assert(c_abi_arg_index == c_abi_arg_init_index);
|
||||||
|
|
||||||
ir_render(g, fn_table_entry);
|
ir_render(g, fn_table_entry);
|
||||||
|
|
||||||
|
@ -48,6 +48,12 @@ pub const Builder = struct {
|
|||||||
cache_root: []const u8,
|
cache_root: []const u8,
|
||||||
release_mode: ?builtin.Mode,
|
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 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);
|
const AvailableOptionsMap = HashMap([]const u8, AvailableOption, mem.hash_slice_u8, mem.eql_slice_u8);
|
||||||
|
|
||||||
@ -817,6 +823,7 @@ pub const LibExeObjStep = struct {
|
|||||||
frameworks: BufSet,
|
frameworks: BufSet,
|
||||||
verbose_link: bool,
|
verbose_link: bool,
|
||||||
no_rosegment: bool,
|
no_rosegment: bool,
|
||||||
|
c_std: Builder.CStd,
|
||||||
|
|
||||||
// zig only stuff
|
// zig only stuff
|
||||||
root_src: ?[]const u8,
|
root_src: ?[]const u8,
|
||||||
@ -918,6 +925,7 @@ pub const LibExeObjStep = struct {
|
|||||||
.object_src = undefined,
|
.object_src = undefined,
|
||||||
.disable_libc = true,
|
.disable_libc = true,
|
||||||
.build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable,
|
.build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable,
|
||||||
|
.c_std = Builder.CStd.C99,
|
||||||
};
|
};
|
||||||
self.computeOutFileNames();
|
self.computeOutFileNames();
|
||||||
return self;
|
return self;
|
||||||
@ -952,6 +960,7 @@ pub const LibExeObjStep = struct {
|
|||||||
.disable_libc = false,
|
.disable_libc = false,
|
||||||
.is_zig = false,
|
.is_zig = false,
|
||||||
.linker_script = null,
|
.linker_script = null,
|
||||||
|
.c_std = Builder.CStd.C99,
|
||||||
|
|
||||||
.root_src = undefined,
|
.root_src = undefined,
|
||||||
.verbose_link = false,
|
.verbose_link = false,
|
||||||
@ -1392,6 +1401,13 @@ pub const LibExeObjStep = struct {
|
|||||||
|
|
||||||
const is_darwin = self.target.isDarwin();
|
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) {
|
switch (self.kind) {
|
||||||
Kind.Obj => {
|
Kind.Obj => {
|
||||||
cc_args.append("-c") catch unreachable;
|
cc_args.append("-c") catch unreachable;
|
||||||
@ -1678,6 +1694,17 @@ pub const TestStep = struct {
|
|||||||
self.filter = text;
|
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 {
|
pub fn addObjectFile(self: *TestStep, path: []const u8) void {
|
||||||
self.object_files.append(path) catch unreachable;
|
self.object_files.append(path) catch unreachable;
|
||||||
}
|
}
|
||||||
|
@ -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
|
// 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");
|
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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,9 @@ pub fn addCases(cases: *tests.GenHContext) void {
|
|||||||
\\ A: i32,
|
\\ A: i32,
|
||||||
\\ B: f32,
|
\\ B: f32,
|
||||||
\\ C: bool,
|
\\ C: bool,
|
||||||
|
\\ D: u64,
|
||||||
|
\\ E: u64,
|
||||||
|
\\ F: u64,
|
||||||
\\};
|
\\};
|
||||||
\\export fn entry(foo: Foo) void { }
|
\\export fn entry(foo: Foo) void { }
|
||||||
,
|
,
|
||||||
@ -27,6 +30,9 @@ pub fn addCases(cases: *tests.GenHContext) void {
|
|||||||
\\ int32_t A;
|
\\ int32_t A;
|
||||||
\\ float B;
|
\\ float B;
|
||||||
\\ bool C;
|
\\ bool C;
|
||||||
|
\\ uint64_t D;
|
||||||
|
\\ uint64_t E;
|
||||||
|
\\ uint64_t F;
|
||||||
\\};
|
\\};
|
||||||
\\
|
\\
|
||||||
\\TEST_EXPORT void entry(struct Foo foo);
|
\\TEST_EXPORT void entry(struct Foo foo);
|
||||||
@ -34,17 +40,34 @@ pub fn addCases(cases: *tests.GenHContext) void {
|
|||||||
);
|
);
|
||||||
|
|
||||||
cases.add("declare union",
|
cases.add("declare union",
|
||||||
|
\\const Big = extern struct {
|
||||||
|
\\ A: u64,
|
||||||
|
\\ B: u64,
|
||||||
|
\\ C: u64,
|
||||||
|
\\ D: u64,
|
||||||
|
\\ E: u64,
|
||||||
|
\\};
|
||||||
\\const Foo = extern union {
|
\\const Foo = extern union {
|
||||||
\\ A: i32,
|
\\ A: i32,
|
||||||
\\ B: f32,
|
\\ B: f32,
|
||||||
\\ C: bool,
|
\\ 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 {
|
\\union Foo {
|
||||||
\\ int32_t A;
|
\\ int32_t A;
|
||||||
\\ float B;
|
\\ float B;
|
||||||
\\ bool C;
|
\\ bool C;
|
||||||
|
\\ struct Big D;
|
||||||
\\};
|
\\};
|
||||||
\\
|
\\
|
||||||
\\TEST_EXPORT void entry(union Foo foo);
|
\\TEST_EXPORT void entry(union Foo foo);
|
||||||
@ -85,7 +108,6 @@ pub fn addCases(cases: *tests.GenHContext) void {
|
|||||||
\\export fn a(s: *S) u8 {
|
\\export fn a(s: *S) u8 {
|
||||||
\\ return s.a;
|
\\ return s.a;
|
||||||
\\}
|
\\}
|
||||||
|
|
||||||
,
|
,
|
||||||
\\struct S;
|
\\struct S;
|
||||||
\\TEST_EXPORT uint8_t a(struct S * 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 {
|
\\export fn a(s: *U) u8 {
|
||||||
\\ return s.A;
|
\\ return s.A;
|
||||||
\\}
|
\\}
|
||||||
|
|
||||||
,
|
,
|
||||||
\\union U;
|
\\union U;
|
||||||
\\TEST_EXPORT uint8_t a(union U * s);
|
\\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 {
|
\\export fn a(s: *E) u8 {
|
||||||
\\ return @enumToInt(s.*);
|
\\ return @enumToInt(s.*);
|
||||||
\\}
|
\\}
|
||||||
|
|
||||||
,
|
,
|
||||||
\\enum E;
|
\\enum E;
|
||||||
\\TEST_EXPORT uint8_t a(enum E * s);
|
\\TEST_EXPORT uint8_t a(enum E * s);
|
||||||
|
17
test/stage1/c_abi/build.zig
Normal file
17
test/stage1/c_abi/build.zig
Normal 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);
|
||||||
|
}
|
64
test/stage1/c_abi/cfuncs.c
Normal file
64
test/stage1/c_abi/cfuncs.c
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
#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 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);
|
||||||
|
}
|
58
test/stage1/c_abi/main.zig
Normal file
58
test/stage1/c_abi/main.zig
Normal file
@ -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);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user