mirror of
https://github.com/ziglang/zig.git
synced 2026-02-21 16:54:52 +00:00
Merge remote-tracking branch 'origin/master' into stage1-caching
This commit is contained in:
commit
4af844732a
@ -76,7 +76,11 @@ pub fn build(b: *Builder) !void {
|
||||
|
||||
const test_stage2_step = b.step("test-stage2", "Run the stage2 compiler tests");
|
||||
test_stage2_step.dependOn(&test_stage2.step);
|
||||
test_step.dependOn(test_stage2_step);
|
||||
|
||||
// TODO see https://github.com/ziglang/zig/issues/1364
|
||||
if (false) {
|
||||
test_step.dependOn(test_stage2_step);
|
||||
}
|
||||
|
||||
const all_modes = []builtin.Mode{
|
||||
builtin.Mode.Debug,
|
||||
|
||||
@ -41,6 +41,13 @@ struct Tld;
|
||||
struct TldExport;
|
||||
struct IrAnalyze;
|
||||
|
||||
enum X64CABIClass {
|
||||
X64CABIClass_Unknown,
|
||||
X64CABIClass_MEMORY,
|
||||
X64CABIClass_INTEGER,
|
||||
X64CABIClass_SSE,
|
||||
};
|
||||
|
||||
struct IrExecutable {
|
||||
ZigList<IrBasicBlock *> basic_block_list;
|
||||
Buf *name;
|
||||
@ -3282,4 +3289,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
|
||||
|
||||
185
src/analyze.cpp
185
src/analyze.cpp
@ -1009,10 +1009,6 @@ ZigType *get_bound_fn_type(CodeGen *g, ZigFn *fn_entry) {
|
||||
return bound_fn_type;
|
||||
}
|
||||
|
||||
bool calling_convention_does_first_arg_return(CallingConvention cc) {
|
||||
return cc == CallingConventionUnspecified;
|
||||
}
|
||||
|
||||
const char *calling_convention_name(CallingConvention cc) {
|
||||
switch (cc) {
|
||||
case CallingConventionUnspecified: return "undefined";
|
||||
@ -1061,6 +1057,27 @@ ZigType *get_ptr_to_stack_trace_type(CodeGen *g) {
|
||||
return g->ptr_to_stack_trace_type;
|
||||
}
|
||||
|
||||
bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id) {
|
||||
if (fn_type_id->cc == CallingConventionUnspecified) {
|
||||
return handle_is_ptr(fn_type_id->return_type);
|
||||
}
|
||||
if (fn_type_id->cc != CallingConventionC) {
|
||||
return false;
|
||||
}
|
||||
if (type_is_c_abi_int(g, fn_type_id->return_type)) {
|
||||
return false;
|
||||
}
|
||||
if (g->zig_target.arch.arch == ZigLLVM_x86_64) {
|
||||
X64CABIClass abi_class = type_c_abi_x86_64_class(g, fn_type_id->return_type);
|
||||
if (abi_class == X64CABIClass_MEMORY) {
|
||||
return true;
|
||||
}
|
||||
zig_panic("TODO implement C ABI for x86_64 return types. type '%s'\nSee https://github.com/ziglang/zig/issues/1481",
|
||||
buf_ptr(&fn_type_id->return_type->name));
|
||||
}
|
||||
zig_panic("TODO implement C ABI for this architecture. See https://github.com/ziglang/zig/issues/1481");
|
||||
}
|
||||
|
||||
ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
|
||||
Error err;
|
||||
auto table_entry = g->fn_type_table.maybe_get(fn_type_id);
|
||||
@ -1116,21 +1133,20 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
|
||||
// next, loop over the parameters again and compute debug information
|
||||
// and codegen information
|
||||
if (!skip_debug_info) {
|
||||
bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) &&
|
||||
handle_is_ptr(fn_type_id->return_type);
|
||||
bool first_arg_return = want_first_arg_sret(g, fn_type_id);
|
||||
bool is_async = fn_type_id->cc == CallingConventionAsync;
|
||||
bool is_c_abi = fn_type_id->cc == CallingConventionC;
|
||||
bool prefix_arg_error_return_trace = g->have_err_ret_tracing && fn_type_can_fail(fn_type_id);
|
||||
// +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 +1154,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 +1164,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 +1195,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 +1206,31 @@ 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 = ¶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;
|
||||
|
||||
for (size_t i = 0; i < gen_param_types.length; i += 1) {
|
||||
assert(gen_param_types.items[i] != nullptr);
|
||||
}
|
||||
fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref,
|
||||
gen_param_types, (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);
|
||||
@ -5863,12 +5882,23 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) {
|
||||
}
|
||||
case ZigTypeIdErrorUnion:
|
||||
{
|
||||
buf_appendf(buf, "(error union %s constant)", buf_ptr(&type_entry->name));
|
||||
buf_appendf(buf, "%s(", buf_ptr(&type_entry->name));
|
||||
if (const_val->data.x_err_union.err == nullptr) {
|
||||
render_const_value(g, buf, const_val->data.x_err_union.payload);
|
||||
} else {
|
||||
buf_appendf(buf, "%s.%s", buf_ptr(&type_entry->data.error_union.err_set_type->name),
|
||||
buf_ptr(&const_val->data.x_err_union.err->name));
|
||||
}
|
||||
buf_appendf(buf, ")");
|
||||
return;
|
||||
}
|
||||
case ZigTypeIdUnion:
|
||||
{
|
||||
buf_appendf(buf, "(union %s constant)", buf_ptr(&type_entry->name));
|
||||
uint64_t tag = bigint_as_unsigned(&const_val->data.x_union.tag);
|
||||
TypeUnionField *field = &type_entry->data.unionation.fields[tag];
|
||||
buf_appendf(buf, "%s { .%s = ", buf_ptr(&type_entry->name), buf_ptr(field->name));
|
||||
render_const_value(g, buf, const_val->data.x_union.payload);
|
||||
buf_append_str(buf, "}");
|
||||
return;
|
||||
}
|
||||
case ZigTypeIdErrorSet:
|
||||
@ -6375,3 +6405,84 @@ Error file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents) {
|
||||
return os_fetch_file_path(resolved_path, contents, false);
|
||||
}
|
||||
}
|
||||
|
||||
X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty) {
|
||||
size_t ty_size = type_size(g, ty);
|
||||
if (get_codegen_ptr_type(ty) != nullptr)
|
||||
return X64CABIClass_INTEGER;
|
||||
switch (ty->id) {
|
||||
case ZigTypeIdEnum:
|
||||
case ZigTypeIdInt:
|
||||
case ZigTypeIdBool:
|
||||
return X64CABIClass_INTEGER;
|
||||
case ZigTypeIdFloat:
|
||||
return X64CABIClass_SSE;
|
||||
case ZigTypeIdStruct: {
|
||||
// "If the size of an object is larger than four eightbytes, or it contains unaligned
|
||||
// fields, it has class MEMORY"
|
||||
if (ty_size > 32)
|
||||
return X64CABIClass_MEMORY;
|
||||
if (ty->data.structure.layout != ContainerLayoutExtern) {
|
||||
// TODO determine whether packed structs have any unaligned fields
|
||||
return X64CABIClass_Unknown;
|
||||
}
|
||||
// "If the size of the aggregate exceeds two eightbytes and the first eight-
|
||||
// byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument
|
||||
// is passed in memory."
|
||||
if (ty_size > 16) {
|
||||
// Zig doesn't support vectors and large fp registers yet, so this will always
|
||||
// be memory.
|
||||
return X64CABIClass_MEMORY;
|
||||
}
|
||||
X64CABIClass working_class = X64CABIClass_Unknown;
|
||||
for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) {
|
||||
X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.structure.fields->type_entry);
|
||||
if (field_class == X64CABIClass_Unknown)
|
||||
return X64CABIClass_Unknown;
|
||||
if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) {
|
||||
working_class = field_class;
|
||||
}
|
||||
}
|
||||
return working_class;
|
||||
}
|
||||
case ZigTypeIdUnion: {
|
||||
// "If the size of an object is larger than four eightbytes, or it contains unaligned
|
||||
// fields, it has class MEMORY"
|
||||
if (ty_size > 32)
|
||||
return X64CABIClass_MEMORY;
|
||||
if (ty->data.unionation.layout != ContainerLayoutExtern)
|
||||
return X64CABIClass_MEMORY;
|
||||
// "If the size of the aggregate exceeds two eightbytes and the first eight-
|
||||
// byte isn’t SSE or any other eightbyte isn’t SSEUP, the whole argument
|
||||
// is passed in memory."
|
||||
if (ty_size > 16) {
|
||||
// Zig doesn't support vectors and large fp registers yet, so this will always
|
||||
// be memory.
|
||||
return X64CABIClass_MEMORY;
|
||||
}
|
||||
X64CABIClass working_class = X64CABIClass_Unknown;
|
||||
for (uint32_t i = 0; i < ty->data.unionation.src_field_count; i += 1) {
|
||||
X64CABIClass field_class = type_c_abi_x86_64_class(g, ty->data.unionation.fields->type_entry);
|
||||
if (field_class == X64CABIClass_Unknown)
|
||||
return X64CABIClass_Unknown;
|
||||
if (i == 0 || field_class == X64CABIClass_MEMORY || working_class == X64CABIClass_SSE) {
|
||||
working_class = field_class;
|
||||
}
|
||||
}
|
||||
return working_class;
|
||||
}
|
||||
default:
|
||||
return X64CABIClass_Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE this does not depend on x86_64
|
||||
bool type_is_c_abi_int(CodeGen *g, ZigType *ty) {
|
||||
return (ty->id == ZigTypeIdInt ||
|
||||
ty->id == ZigTypeIdFloat ||
|
||||
ty->id == ZigTypeIdBool ||
|
||||
ty->id == ZigTypeIdEnum ||
|
||||
ty->id == ZigTypeIdVoid ||
|
||||
ty->id == ZigTypeIdUnreachable ||
|
||||
get_codegen_ptr_type(ty) != nullptr);
|
||||
}
|
||||
|
||||
@ -182,7 +182,6 @@ size_t type_id_index(ZigType *entry);
|
||||
ZigType *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id);
|
||||
Result<bool> type_is_copyable(CodeGen *g, ZigType *type_entry);
|
||||
LinkLib *create_link_lib(Buf *name);
|
||||
bool calling_convention_does_first_arg_return(CallingConvention cc);
|
||||
LinkLib *add_link_lib(CodeGen *codegen, Buf *lib);
|
||||
|
||||
uint32_t get_abi_alignment(CodeGen *g, ZigType *type_entry);
|
||||
@ -210,7 +209,11 @@ ZigType *get_primitive_type(CodeGen *g, Buf *name);
|
||||
bool calling_convention_allows_zig_types(CallingConvention cc);
|
||||
const char *calling_convention_name(CallingConvention cc);
|
||||
|
||||
|
||||
Error ATTRIBUTE_MUST_USE file_fetch(CodeGen *g, Buf *resolved_path, Buf *contents);
|
||||
|
||||
void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk);
|
||||
X64CABIClass type_c_abi_x86_64_class(CodeGen *g, ZigType *ty);
|
||||
bool type_is_c_abi_int(CodeGen *g, ZigType *ty);
|
||||
bool want_first_arg_sret(CodeGen *g, FnTypeId *fn_type_id);
|
||||
|
||||
#endif
|
||||
|
||||
573
src/codegen.cpp
573
src/codegen.cpp
@ -350,8 +350,12 @@ static void addLLVMFnAttrInt(LLVMValueRef fn_val, const char *attr_name, uint64_
|
||||
return addLLVMAttrInt(fn_val, -1, attr_name, attr_val);
|
||||
}
|
||||
|
||||
static void addLLVMArgAttr(LLVMValueRef arg_val, unsigned param_index, const char *attr_name) {
|
||||
return addLLVMAttr(arg_val, param_index + 1, attr_name);
|
||||
static void addLLVMArgAttr(LLVMValueRef fn_val, unsigned param_index, const char *attr_name) {
|
||||
return addLLVMAttr(fn_val, param_index + 1, attr_name);
|
||||
}
|
||||
|
||||
static void addLLVMArgAttrInt(LLVMValueRef fn_val, unsigned param_index, const char *attr_name, uint64_t attr_val) {
|
||||
return addLLVMAttrInt(fn_val, param_index + 1, attr_name, attr_val);
|
||||
}
|
||||
|
||||
static bool is_symbol_available(CodeGen *g, Buf *name) {
|
||||
@ -462,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
|
||||
@ -506,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);
|
||||
}
|
||||
@ -568,41 +573,26 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn_table_entry) {
|
||||
// use the ABI alignment, which is fine.
|
||||
}
|
||||
|
||||
unsigned init_gen_i = 0;
|
||||
if (!type_has_bits(return_type)) {
|
||||
// nothing to do
|
||||
} else if (type_is_codegen_pointer(return_type)) {
|
||||
addLLVMAttr(fn_table_entry->llvm_value, 0, "nonnull");
|
||||
} else if (handle_is_ptr(return_type) &&
|
||||
calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc))
|
||||
{
|
||||
} else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) {
|
||||
addLLVMArgAttr(fn_table_entry->llvm_value, 0, "sret");
|
||||
addLLVMArgAttr(fn_table_entry->llvm_value, 0, "nonnull");
|
||||
if (cc == CallingConventionC) {
|
||||
addLLVMArgAttr(fn_table_entry->llvm_value, 0, "noalias");
|
||||
}
|
||||
init_gen_i = 1;
|
||||
}
|
||||
|
||||
|
||||
// set parameter attributes
|
||||
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;
|
||||
fn_walk.data.attrs.gen_i = init_gen_i;
|
||||
walk_function_params(g, fn_type, &fn_walk);
|
||||
|
||||
uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry);
|
||||
if (err_ret_trace_arg_index != UINT32_MAX) {
|
||||
@ -1854,6 +1844,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));
|
||||
@ -1884,6 +1875,353 @@ 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 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");
|
||||
addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty));
|
||||
fn_walk->data.attrs.gen_i += 1;
|
||||
break;
|
||||
case FnWalkIdCall:
|
||||
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) {
|
||||
X64CABIClass abi_class = type_c_abi_x86_64_class(g, ty);
|
||||
size_t ty_size = type_size(g, ty);
|
||||
if (abi_class == X64CABIClass_MEMORY) {
|
||||
assert(handle_is_ptr(ty));
|
||||
switch (fn_walk->id) {
|
||||
case FnWalkIdAttrs:
|
||||
addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "byval");
|
||||
addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty));
|
||||
addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull");
|
||||
fn_walk->data.attrs.gen_i += 1;
|
||||
break;
|
||||
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;
|
||||
} else if (abi_class == X64CABIClass_INTEGER) {
|
||||
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)
|
||||
{
|
||||
@ -1902,15 +2240,13 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
|
||||
LLVMValueRef value = ir_llvm_value(g, return_instruction->value);
|
||||
ZigType *return_type = return_instruction->value->value.type;
|
||||
|
||||
if (handle_is_ptr(return_type)) {
|
||||
if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) {
|
||||
assert(g->cur_ret_ptr);
|
||||
gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value);
|
||||
LLVMBuildRetVoid(g->builder);
|
||||
} else {
|
||||
LLVMValueRef by_val_value = gen_load_untyped(g, value, 0, false, "");
|
||||
LLVMBuildRet(g->builder, by_val_value);
|
||||
}
|
||||
if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) {
|
||||
assert(g->cur_ret_ptr);
|
||||
gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value);
|
||||
LLVMBuildRetVoid(g->builder);
|
||||
} else if (handle_is_ptr(return_type)) {
|
||||
LLVMValueRef by_val_value = gen_load_untyped(g, value, 0, false, "");
|
||||
LLVMBuildRet(g->builder, by_val_value);
|
||||
} else {
|
||||
LLVMBuildRet(g->builder, value);
|
||||
}
|
||||
@ -2878,7 +3214,8 @@ static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutable *executable,
|
||||
assert(var->value->type == init_value->value.type);
|
||||
ZigType *var_ptr_type = get_pointer_to_type_extra(g, var->value->type, false, false,
|
||||
PtrLenSingle, var->align_bytes, 0, 0);
|
||||
gen_assign_raw(g, var->value_ref, var_ptr_type, ir_llvm_value(g, init_value));
|
||||
LLVMValueRef llvm_init_val = ir_llvm_value(g, init_value);
|
||||
gen_assign_raw(g, var->value_ref, var_ptr_type, llvm_init_val);
|
||||
} else {
|
||||
bool want_safe = ir_want_runtime_safety(g, &decl_var_instruction->base);
|
||||
if (want_safe) {
|
||||
@ -3124,40 +3461,30 @@ 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);
|
||||
|
||||
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);
|
||||
CallingConvention cc = fn_type->data.fn.fn_type_id.cc;
|
||||
|
||||
bool first_arg_ret = ret_has_bits && want_first_arg_sret(g, fn_type_id);
|
||||
bool prefix_arg_err_ret_stack = get_prefix_arg_err_ret_stack(g, fn_type_id);
|
||||
// +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) {
|
||||
@ -3172,12 +3499,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);
|
||||
@ -3186,7 +3513,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, "");
|
||||
}
|
||||
|
||||
@ -5533,12 +5860,24 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
|
||||
LLVMValueRef tag_value = bigint_to_llvm_const(type_entry->data.unionation.tag_type->type_ref,
|
||||
&const_val->data.x_union.tag);
|
||||
|
||||
LLVMValueRef fields[2];
|
||||
LLVMValueRef fields[3];
|
||||
fields[type_entry->data.unionation.gen_union_index] = union_value_ref;
|
||||
fields[type_entry->data.unionation.gen_tag_index] = tag_value;
|
||||
|
||||
if (make_unnamed_struct) {
|
||||
return LLVMConstStruct(fields, 2, false);
|
||||
LLVMValueRef result = LLVMConstStruct(fields, 2, false);
|
||||
uint64_t last_field_offset = LLVMOffsetOfElement(g->target_data_ref, LLVMTypeOf(result), 1);
|
||||
uint64_t end_offset = last_field_offset +
|
||||
LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(fields[1]));
|
||||
uint64_t expected_sz = LLVMStoreSizeOfType(g->target_data_ref, type_entry->type_ref);
|
||||
unsigned pad_sz = expected_sz - end_offset;
|
||||
if (pad_sz != 0) {
|
||||
fields[2] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_sz));
|
||||
result = LLVMConstStruct(fields, 3, false);
|
||||
}
|
||||
uint64_t actual_sz = LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(result));
|
||||
assert(actual_sz == expected_sz);
|
||||
return result;
|
||||
} else {
|
||||
return LLVMConstNamedStruct(type_entry->type_ref, fields, 2);
|
||||
}
|
||||
@ -5578,13 +5917,29 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c
|
||||
err_payload_value = gen_const_val(g, payload_val, "");
|
||||
make_unnamed_struct = is_llvm_value_unnamed_type(payload_val->type, err_payload_value);
|
||||
}
|
||||
LLVMValueRef fields[] = {
|
||||
err_tag_value,
|
||||
err_payload_value,
|
||||
};
|
||||
if (make_unnamed_struct) {
|
||||
return LLVMConstStruct(fields, 2, false);
|
||||
uint64_t payload_off = LLVMOffsetOfElement(g->target_data_ref, type_entry->type_ref, 1);
|
||||
uint64_t err_sz = LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(err_tag_value));
|
||||
unsigned pad_sz = payload_off - err_sz;
|
||||
if (pad_sz == 0) {
|
||||
LLVMValueRef fields[] = {
|
||||
err_tag_value,
|
||||
err_payload_value,
|
||||
};
|
||||
return LLVMConstStruct(fields, 2, false);
|
||||
} else {
|
||||
LLVMValueRef fields[] = {
|
||||
err_tag_value,
|
||||
LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_sz)),
|
||||
err_payload_value,
|
||||
};
|
||||
return LLVMConstStruct(fields, 3, false);
|
||||
}
|
||||
} else {
|
||||
LLVMValueRef fields[] = {
|
||||
err_tag_value,
|
||||
err_payload_value,
|
||||
};
|
||||
return LLVMConstNamedStruct(type_entry->type_ref, fields, 2);
|
||||
}
|
||||
}
|
||||
@ -5717,23 +6072,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 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);
|
||||
@ -5852,11 +6190,14 @@ static void do_code_gen(CodeGen *g) {
|
||||
// Generate function definitions.
|
||||
for (size_t fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) {
|
||||
ZigFn *fn_table_entry = g->fn_defs.at(fn_i);
|
||||
FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id;
|
||||
CallingConvention cc = fn_type_id->cc;
|
||||
bool is_c_abi = cc == CallingConventionC;
|
||||
|
||||
LLVMValueRef fn = fn_llvm_value(g, fn_table_entry);
|
||||
g->cur_fn = fn_table_entry;
|
||||
g->cur_fn_val = fn;
|
||||
ZigType *return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type;
|
||||
ZigType *return_type = fn_type_id->return_type;
|
||||
if (handle_is_ptr(return_type)) {
|
||||
g->cur_ret_ptr = LLVMGetParam(fn, 0);
|
||||
} else {
|
||||
@ -5875,7 +6216,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) {
|
||||
@ -5934,7 +6275,15 @@ static void do_code_gen(CodeGen *g) {
|
||||
|
||||
ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base);
|
||||
|
||||
unsigned gen_i_init = want_first_arg_sret(g, fn_type_id) ? 1 : 0;
|
||||
|
||||
// create debug variable declarations for variables and allocate all local variables
|
||||
FnWalk fn_walk_var = {};
|
||||
fn_walk_var.id = FnWalkIdVars;
|
||||
fn_walk_var.data.vars.import = import;
|
||||
fn_walk_var.data.vars.fn = fn_table_entry;
|
||||
fn_walk_var.data.vars.llvm_fn = fn;
|
||||
fn_walk_var.data.vars.gen_i = gen_i_init;
|
||||
for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) {
|
||||
ZigVar *var = fn_table_entry->variable_list.at(var_i);
|
||||
|
||||
@ -5953,6 +6302,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;
|
||||
@ -6004,33 +6356,14 @@ 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;
|
||||
fn_walk_init.data.inits.gen_i = gen_i_init;
|
||||
walk_function_params(g, fn_table_entry->type_entry, &fn_walk_init);
|
||||
|
||||
ir_render(g, fn_table_entry);
|
||||
|
||||
|
||||
@ -61,5 +61,4 @@ void codegen_translate_c(CodeGen *g, Buf *path);
|
||||
|
||||
Buf *codegen_generate_builtin_source(CodeGen *g);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
11
src/ir.cpp
11
src/ir.cpp
@ -13107,8 +13107,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,
|
||||
assert(ira->codegen->errors.length != 0);
|
||||
return ira->codegen->invalid_instruction;
|
||||
}
|
||||
assert(var->value->type);
|
||||
if (type_is_invalid(var->value->type))
|
||||
if (var->value->type == nullptr || type_is_invalid(var->value->type))
|
||||
return ira->codegen->invalid_instruction;
|
||||
|
||||
bool comptime_var_mem = ir_get_var_is_comptime(var);
|
||||
@ -19643,7 +19642,7 @@ static ZigType *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnP
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
if (type_requires_comptime(param_type)) {
|
||||
if (!calling_convention_allows_zig_types(fn_type_id.cc)) {
|
||||
ir_add_error(ira, &instruction->base,
|
||||
ir_add_error(ira, param_type_value,
|
||||
buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'",
|
||||
buf_ptr(¶m_type->name), calling_convention_name(fn_type_id.cc)));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
@ -19654,6 +19653,12 @@ static ZigType *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstructionFnP
|
||||
out_val->data.x_type = get_generic_fn_type(ira->codegen, &fn_type_id);
|
||||
return ira->codegen->builtin_types.entry_type;
|
||||
}
|
||||
if (!type_has_bits(param_type) && !calling_convention_allows_zig_types(fn_type_id.cc)) {
|
||||
ir_add_error(ira, param_type_value,
|
||||
buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'",
|
||||
buf_ptr(¶m_type->name), calling_convention_name(fn_type_id.cc)));
|
||||
return ira->codegen->builtin_types.entry_invalid;
|
||||
}
|
||||
param_info->type = param_type;
|
||||
}
|
||||
|
||||
|
||||
@ -445,7 +445,7 @@ static Buf os_path_resolve_windows(Buf **paths_ptr, size_t paths_len) {
|
||||
}
|
||||
|
||||
// determine which disk designator we will result with, if any
|
||||
char result_drive_buf[2] = {'_', ':'};
|
||||
char result_drive_buf[3] = {'_', ':', '\0'}; // 0 needed for strlen later
|
||||
Slice<uint8_t> result_disk_designator = str("");
|
||||
WindowsPathKind have_drive_kind = WindowsPathKindNone;
|
||||
bool have_abs_path = false;
|
||||
|
||||
@ -62,6 +62,10 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
|
||||
return self.len;
|
||||
}
|
||||
|
||||
pub fn capacity(self: Self) usize {
|
||||
return self.items.len;
|
||||
}
|
||||
|
||||
/// ArrayList takes ownership of the passed in slice. The slice must have been
|
||||
/// allocated with `allocator`.
|
||||
/// Deinitialize with `deinit` or use `toOwnedSlice`.
|
||||
@ -102,6 +106,11 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
|
||||
new_item_ptr.* = item;
|
||||
}
|
||||
|
||||
pub fn appendAssumeCapacity(self: *Self, item: T) void {
|
||||
const new_item_ptr = self.addOneAssumeCapacity();
|
||||
new_item_ptr.* = item;
|
||||
}
|
||||
|
||||
/// Removes the element at the specified index and returns it.
|
||||
/// The empty slot is filled from the end of the list.
|
||||
pub fn swapRemove(self: *Self, i: usize) T {
|
||||
@ -138,7 +147,7 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
|
||||
}
|
||||
|
||||
pub fn ensureCapacity(self: *Self, new_capacity: usize) !void {
|
||||
var better_capacity = self.items.len;
|
||||
var better_capacity = self.capacity();
|
||||
if (better_capacity >= new_capacity) return;
|
||||
while (true) {
|
||||
better_capacity += better_capacity / 2 + 8;
|
||||
@ -150,8 +159,13 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
|
||||
pub fn addOne(self: *Self) !*T {
|
||||
const new_length = self.len + 1;
|
||||
try self.ensureCapacity(new_length);
|
||||
return self.addOneAssumeCapacity();
|
||||
}
|
||||
|
||||
pub fn addOneAssumeCapacity(self: *Self) *T {
|
||||
assert(self.count() < self.capacity());
|
||||
const result = &self.items[self.len];
|
||||
self.len = new_length;
|
||||
self.len += 1;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -191,6 +205,17 @@ pub fn AlignedArrayList(comptime T: type, comptime A: u29) type {
|
||||
};
|
||||
}
|
||||
|
||||
test "std.ArrayList.init" {
|
||||
var bytes: [1024]u8 = undefined;
|
||||
const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
|
||||
|
||||
var list = ArrayList(i32).init(allocator);
|
||||
defer list.deinit();
|
||||
|
||||
assert(list.count() == 0);
|
||||
assert(list.capacity() == 0);
|
||||
}
|
||||
|
||||
test "std.ArrayList.basic" {
|
||||
var bytes: [1024]u8 = undefined;
|
||||
const allocator = &std.heap.FixedBufferAllocator.init(bytes[0..]).allocator;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
const std = @import("../index.zig");
|
||||
const builtin = @import("builtin");
|
||||
const fmt = std.fmt;
|
||||
|
||||
const Endian = builtin.Endian;
|
||||
const readInt = std.mem.readInt;
|
||||
@ -114,7 +115,7 @@ pub const X25519 = struct {
|
||||
return !zerocmp(u8, out);
|
||||
}
|
||||
|
||||
pub fn createPublicKey(public_key: []const u8, private_key: []const u8) bool {
|
||||
pub fn createPublicKey(public_key: [] u8, private_key: []const u8) bool {
|
||||
var base_point = []u8{9} ++ []u8{0} ** 31;
|
||||
return create(public_key, private_key, base_point);
|
||||
}
|
||||
@ -573,6 +574,16 @@ const Fe = struct {
|
||||
}
|
||||
};
|
||||
|
||||
test "x25519 public key calculation from secret key" {
|
||||
var sk: [32]u8 = undefined;
|
||||
var pk_expected: [32]u8 = undefined;
|
||||
var pk_calculated: [32]u8 = undefined;
|
||||
try fmt.hexToBytes(sk[0..], "8052030376d47112be7f73ed7a019293dd12ad910b654455798b4667d73de166");
|
||||
try fmt.hexToBytes(pk_expected[0..], "f1814f0e8ff1043d8a44d25babff3cedcae6c22c3edaa48f857ae70de2baae50");
|
||||
std.debug.assert(X25519.createPublicKey(pk_calculated[0..], sk));
|
||||
std.debug.assert(std.mem.eql(u8, pk_calculated, pk_expected));
|
||||
}
|
||||
|
||||
test "x25519 rfc7748 vector1" {
|
||||
const secret_key = "\xa5\x46\xe3\x6b\xf0\x52\x7c\x9d\x3b\x16\x15\x4b\x82\x46\x5e\xdd\x62\x14\x4c\x0a\xc1\xfc\x5a\x18\x50\x6a\x22\x44\xba\x44\x9a\xc4";
|
||||
const public_key = "\xe6\xdb\x68\x67\x58\x30\x30\xdb\x35\x94\xc1\xa4\x24\xb1\x5f\x7c\x72\x66\x24\xec\x26\xb3\x35\x3b\x10\xa9\x03\xa6\xd0\xab\x1c\x4c";
|
||||
|
||||
@ -343,23 +343,25 @@ pub fn posixWrite(fd: i32, bytes: []const u8) !void {
|
||||
const amt_to_write = math.min(bytes.len - index, usize(max_bytes_len));
|
||||
const rc = posix.write(fd, bytes.ptr + index, amt_to_write);
|
||||
const write_err = posix.getErrno(rc);
|
||||
if (write_err > 0) {
|
||||
return switch (write_err) {
|
||||
posix.EINTR => continue,
|
||||
posix.EINVAL, posix.EFAULT => unreachable,
|
||||
posix.EAGAIN => PosixWriteError.WouldBlock,
|
||||
posix.EBADF => PosixWriteError.FileClosed,
|
||||
posix.EDESTADDRREQ => PosixWriteError.DestinationAddressRequired,
|
||||
posix.EDQUOT => PosixWriteError.DiskQuota,
|
||||
posix.EFBIG => PosixWriteError.FileTooBig,
|
||||
posix.EIO => PosixWriteError.InputOutput,
|
||||
posix.ENOSPC => PosixWriteError.NoSpaceLeft,
|
||||
posix.EPERM => PosixWriteError.AccessDenied,
|
||||
posix.EPIPE => PosixWriteError.BrokenPipe,
|
||||
else => unexpectedErrorPosix(write_err),
|
||||
};
|
||||
switch (write_err) {
|
||||
0 => {
|
||||
index += rc;
|
||||
continue;
|
||||
},
|
||||
posix.EINTR => continue,
|
||||
posix.EINVAL => unreachable,
|
||||
posix.EFAULT => unreachable,
|
||||
posix.EAGAIN => return PosixWriteError.WouldBlock,
|
||||
posix.EBADF => return PosixWriteError.FileClosed,
|
||||
posix.EDESTADDRREQ => return PosixWriteError.DestinationAddressRequired,
|
||||
posix.EDQUOT => return PosixWriteError.DiskQuota,
|
||||
posix.EFBIG => return PosixWriteError.FileTooBig,
|
||||
posix.EIO => return PosixWriteError.InputOutput,
|
||||
posix.ENOSPC => return PosixWriteError.NoSpaceLeft,
|
||||
posix.EPERM => return PosixWriteError.AccessDenied,
|
||||
posix.EPIPE => return PosixWriteError.BrokenPipe,
|
||||
else => return unexpectedErrorPosix(write_err),
|
||||
}
|
||||
index += rc;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1614,7 +1616,7 @@ pub const Dir = struct {
|
||||
return null;
|
||||
}
|
||||
const name_utf16le = mem.toSlice(u16, self.handle.find_file_data.cFileName[0..].ptr);
|
||||
if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{'.', '.'}))
|
||||
if (mem.eql(u16, name_utf16le, []u16{'.'}) or mem.eql(u16, name_utf16le, []u16{ '.', '.' }))
|
||||
continue;
|
||||
// Trust that Windows gives us valid UTF-16LE
|
||||
const name_utf8_len = std.unicode.utf16leToUtf8(self.handle.name_data[0..], name_utf16le) catch unreachable;
|
||||
|
||||
@ -9,9 +9,10 @@ comptime {
|
||||
_ = @import("cases/bitcast.zig");
|
||||
_ = @import("cases/bool.zig");
|
||||
_ = @import("cases/bugs/1111.zig");
|
||||
_ = @import("cases/bugs/1230.zig");
|
||||
_ = @import("cases/bugs/1277.zig");
|
||||
_ = @import("cases/bugs/1381.zig");
|
||||
_ = @import("cases/bugs/1421.zig");
|
||||
_ = @import("cases/bugs/1442.zig");
|
||||
_ = @import("cases/bugs/394.zig");
|
||||
_ = @import("cases/bugs/655.zig");
|
||||
_ = @import("cases/bugs/656.zig");
|
||||
|
||||
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,14 +0,0 @@
|
||||
const assert = @import("std").debug.assert;
|
||||
|
||||
const S = extern struct {
|
||||
x: i32,
|
||||
};
|
||||
|
||||
extern fn ret_struct() S {
|
||||
return S{ .x = 42 };
|
||||
}
|
||||
|
||||
test "extern return small struct (bug 1230)" {
|
||||
const s = ret_struct();
|
||||
assert(s.x == 42);
|
||||
}
|
||||
21
test/cases/bugs/1381.zig
Normal file
21
test/cases/bugs/1381.zig
Normal file
@ -0,0 +1,21 @@
|
||||
const std = @import("std");
|
||||
|
||||
const B = union(enum) {
|
||||
D: u8,
|
||||
E: u16,
|
||||
};
|
||||
|
||||
const A = union(enum) {
|
||||
B: B,
|
||||
C: u8,
|
||||
};
|
||||
|
||||
test "union that needs padding bytes inside an array" {
|
||||
var as = []A{
|
||||
A{ .B = B{ .D = 1 } },
|
||||
A{ .B = B{ .D = 1 } },
|
||||
};
|
||||
|
||||
const a = as[0].B;
|
||||
std.debug.assertOrPanic(a.D == 1);
|
||||
}
|
||||
11
test/cases/bugs/1442.zig
Normal file
11
test/cases/bugs/1442.zig
Normal file
@ -0,0 +1,11 @@
|
||||
const std = @import("std");
|
||||
|
||||
const Union = union(enum) {
|
||||
Text: []const u8,
|
||||
Color: u32,
|
||||
};
|
||||
|
||||
test "const error union field alignment" {
|
||||
var union_or_err: error!Union = Union{ .Color = 1234 };
|
||||
std.debug.assertOrPanic((union_or_err catch unreachable).Color == 1234);
|
||||
}
|
||||
@ -1,6 +1,24 @@
|
||||
const tests = @import("tests.zig");
|
||||
|
||||
pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
cases.add(
|
||||
"variable initialization compile error then referenced",
|
||||
\\fn Undeclared() type {
|
||||
\\ return T;
|
||||
\\}
|
||||
\\fn Gen() type {
|
||||
\\ const X = Undeclared();
|
||||
\\ return struct {
|
||||
\\ x: X,
|
||||
\\ };
|
||||
\\}
|
||||
\\export fn entry() void {
|
||||
\\ const S = Gen();
|
||||
\\}
|
||||
,
|
||||
".tmp_source.zig:2:12: error: use of undeclared identifier 'T'",
|
||||
);
|
||||
|
||||
cases.add(
|
||||
"refer to the type of a generic function",
|
||||
\\export fn entry() void {
|
||||
@ -1273,6 +1291,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void {
|
||||
\\
|
||||
\\extern fn bar(x: *void) void { }
|
||||
,
|
||||
".tmp_source.zig:1:30: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'ccc'",
|
||||
".tmp_source.zig:7:18: error: parameter of type '*void' has 0 bits; not allowed in function with calling convention 'ccc'",
|
||||
);
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
18
test/stage1/c_abi/build.zig
Normal file
18
test/stage1/c_abi/build.zig
Normal file
@ -0,0 +1,18 @@
|
||||
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);
|
||||
c_obj.setNoStdLib(true);
|
||||
|
||||
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);
|
||||
}
|
||||
208
test/stage1/c_abi/cfuncs.c
Normal file
208
test/stage1/c_abi/cfuncs.c
Normal file
@ -0,0 +1,208 @@
|
||||
#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);
|
||||
|
||||
struct SplitStructInts {
|
||||
uint64_t a;
|
||||
uint8_t b;
|
||||
uint32_t c;
|
||||
};
|
||||
void zig_split_struct_ints(struct SplitStructInts);
|
||||
|
||||
struct BigStruct zig_big_struct_both(struct BigStruct);
|
||||
|
||||
void run_c_tests(void) {
|
||||
zig_u8(0xff);
|
||||
zig_u16(0xfffe);
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
{
|
||||
struct SplitStructInts s = {1234, 100, 1337};
|
||||
zig_split_struct_ints(s);
|
||||
}
|
||||
|
||||
{
|
||||
struct BigStruct s = {30, 31, 32, 33, 34};
|
||||
struct BigStruct res = zig_big_struct_both(s);
|
||||
assert_or_panic(res.a == 20);
|
||||
assert_or_panic(res.b == 21);
|
||||
assert_or_panic(res.c == 22);
|
||||
assert_or_panic(res.d == 23);
|
||||
assert_or_panic(res.e == 24);
|
||||
}
|
||||
}
|
||||
|
||||
void c_u8(uint8_t x) {
|
||||
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);
|
||||
}
|
||||
|
||||
void c_split_struct_ints(struct SplitStructInts x) {
|
||||
assert_or_panic(x.a == 1234);
|
||||
assert_or_panic(x.b == 100);
|
||||
assert_or_panic(x.c == 1337);
|
||||
}
|
||||
|
||||
struct BigStruct c_big_struct_both(struct BigStruct x) {
|
||||
assert_or_panic(x.a == 1);
|
||||
assert_or_panic(x.b == 2);
|
||||
assert_or_panic(x.c == 3);
|
||||
assert_or_panic(x.d == 4);
|
||||
assert_or_panic(x.e == 5);
|
||||
struct BigStruct y = {10, 11, 12, 13, 14};
|
||||
return y;
|
||||
}
|
||||
239
test/stage1/c_abi/main.zig
Normal file
239
test/stage1/c_abi/main.zig
Normal file
@ -0,0 +1,239 @@
|
||||
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);
|
||||
}
|
||||
|
||||
const SplitStructInt = extern struct {
|
||||
a: u64,
|
||||
b: u8,
|
||||
c: u32,
|
||||
};
|
||||
extern fn c_split_struct_ints(SplitStructInt) void;
|
||||
|
||||
test "C ABI split struct of ints" {
|
||||
var s = SplitStructInt{
|
||||
.a = 1234,
|
||||
.b = 100,
|
||||
.c = 1337,
|
||||
};
|
||||
c_split_struct_ints(s);
|
||||
}
|
||||
|
||||
export fn zig_split_struct_ints(x: SplitStructInt) void {
|
||||
assertOrPanic(x.a == 1234);
|
||||
assertOrPanic(x.b == 100);
|
||||
assertOrPanic(x.c == 1337);
|
||||
}
|
||||
|
||||
extern fn c_big_struct_both(BigStruct) BigStruct;
|
||||
|
||||
test "C ABI sret and byval together" {
|
||||
var s = BigStruct{
|
||||
.a = 1,
|
||||
.b = 2,
|
||||
.c = 3,
|
||||
.d = 4,
|
||||
.e = 5,
|
||||
};
|
||||
var y = c_big_struct_both(s);
|
||||
assertOrPanic(y.a == 10);
|
||||
assertOrPanic(y.b == 11);
|
||||
assertOrPanic(y.c == 12);
|
||||
assertOrPanic(y.d == 13);
|
||||
assertOrPanic(y.e == 14);
|
||||
}
|
||||
|
||||
export fn zig_big_struct_both(x: BigStruct) BigStruct {
|
||||
assertOrPanic(x.a == 30);
|
||||
assertOrPanic(x.b == 31);
|
||||
assertOrPanic(x.c == 32);
|
||||
assertOrPanic(x.d == 33);
|
||||
assertOrPanic(x.e == 34);
|
||||
var s = BigStruct{
|
||||
.a = 20,
|
||||
.b = 21,
|
||||
.c = 22,
|
||||
.d = 23,
|
||||
.e = 24,
|
||||
};
|
||||
return s;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user