authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-09-06 16:29:35-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-09-06 16:29:35-04:00
loga9a925e500c66b8eb57d619b9b6828c70a13d4c5
treeb118496e115a04c5b49fa77bf3478d2821d82024
parenta375bd0d9f5c7773598a8ea31af8c963d6a79706
signaturelock-open Commit is signed but in an unrecognized format.

add C ABI tests


9 files changed, 515 insertions(+), 51 deletions(-)

src/analyze.cpp+92-30
......@@ -1061,6 +1061,77 @@ ZigType *get_ptr_to_stack_trace_type(CodeGen *g) {
10611061 return g->ptr_to_stack_trace_type;
10621062}
10631063
1064bool type_is_c_abi_int(CodeGen *g, ZigType *ty) {
1065 size_t ty_size = type_size(g, ty);
1066 if (ty_size > g->pointer_size_bytes)
1067 return false;
1068 return (ty->id == ZigTypeIdInt ||
1069 ty->id == ZigTypeIdFloat ||
1070 ty->id == ZigTypeIdBool ||
1071 ty->id == ZigTypeIdEnum ||
1072 get_codegen_ptr_type(ty) != nullptr);
1073}
1074
1075// If you edit this function you have to edit the corresponding code:
1076// codegen.cpp:gen_c_abi_param
1077// analyze.cpp:gen_c_abi_param_type
1078// codegen.cpp:gen_c_abi_param_var
1079// codegen.cpp:gen_c_abi_param_var_init
1080static void gen_c_abi_param_type(CodeGen *g, ZigList<LLVMTypeRef> *gen_param_types,
1081 ZigList<ZigLLVMDIType *> *param_di_types, ZigType *ty)
1082{
1083 if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat ||
1084 ty->id == ZigTypeIdInt // TODO investigate if we need to change this
1085 ) {
1086 gen_param_types->append(ty->type_ref);
1087 param_di_types->append(ty->di_type);
1088 return;
1089 }
1090
1091 // Arrays are just pointers
1092 if (ty->id == ZigTypeIdArray) {
1093 ZigType *gen_type = get_pointer_to_type(g, ty, true);
1094 gen_param_types->append(gen_type->type_ref);
1095 param_di_types->append(gen_type->di_type);
1096 return;
1097 }
1098
1099 if (g->zig_target.arch.arch == ZigLLVM_x86_64) {
1100 size_t ty_size = type_size(g, ty);
1101 if (ty->id == ZigTypeIdStruct || ty->id == ZigTypeIdUnion) {
1102 // "If the size of an object is larger than four eightbytes, or it contains unaligned
1103 // fields, it has class MEMORY"
1104 if (ty_size > 32) {
1105 ZigType *gen_type = get_pointer_to_type(g, ty, true);
1106 gen_param_types->append(gen_type->type_ref);
1107 param_di_types->append(gen_type->di_type);
1108 return;
1109 }
1110 }
1111 if (ty->id == ZigTypeIdStruct) {
1112 // "If the size of the aggregate exceeds a single eightbyte, each is classified
1113 // separately. Each eightbyte gets initialized to class NO_CLASS."
1114 if (ty_size <= 8) {
1115 bool contains_int = false;
1116 for (size_t i = 0; i < ty->data.structure.src_field_count; i += 1) {
1117 if (type_is_c_abi_int(g, ty->data.structure.fields[i].type_entry)) {
1118 contains_int = true;
1119 break;
1120 }
1121 }
1122 if (contains_int) {
1123 ZigType *gen_type = get_int_type(g, false, ty_size * 8);
1124 gen_param_types->append(gen_type->type_ref);
1125 param_di_types->append(gen_type->di_type);
1126 return;
1127 }
1128 }
1129 }
1130 }
1131
1132 // allow codegen code to report a compile error
1133}
1134
10641135ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
10651136 Error err;
10661137 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) {
11191190 bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) &&
11201191 handle_is_ptr(fn_type_id->return_type);
11211192 bool is_async = fn_type_id->cc == CallingConventionAsync;
1193 bool is_c_abi = fn_type_id->cc == CallingConventionC;
11221194 bool prefix_arg_error_return_trace = g->have_err_ret_tracing && fn_type_can_fail(fn_type_id);
11231195 // +1 for maybe making the first argument the return value
11241196 // +1 for maybe first argument the error return trace
11251197 // +2 for maybe arguments async allocator and error code pointer
1126 LLVMTypeRef *gen_param_types = allocate<LLVMTypeRef>(4 + fn_type_id->param_count);
1198 ZigList<LLVMTypeRef> gen_param_types = {};
11271199 // +1 because 0 is the return type and
11281200 // +1 for maybe making first arg ret val and
11291201 // +1 for maybe first argument the error return trace
11301202 // +2 for maybe arguments async allocator and error code pointer
1131 ZigLLVMDIType **param_di_types = allocate<ZigLLVMDIType*>(5 + fn_type_id->param_count);
1132 param_di_types[0] = fn_type_id->return_type->di_type;
1133 size_t gen_param_index = 0;
1203 ZigList<ZigLLVMDIType *> param_di_types = {};
1204 param_di_types.append(fn_type_id->return_type->di_type);
11341205 ZigType *gen_return_type;
11351206 if (is_async) {
11361207 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) {
11381209 gen_return_type = g->builtin_types.entry_void;
11391210 } else if (first_arg_return) {
11401211 ZigType *gen_type = get_pointer_to_type(g, fn_type_id->return_type, false);
1141 gen_param_types[gen_param_index] = gen_type->type_ref;
1142 gen_param_index += 1;
1143 // after the gen_param_index += 1 because 0 is the return type
1144 param_di_types[gen_param_index] = gen_type->di_type;
1212 gen_param_types.append(gen_type->type_ref);
1213 param_di_types.append(gen_type->di_type);
11451214 gen_return_type = g->builtin_types.entry_void;
11461215 } else {
11471216 gen_return_type = fn_type_id->return_type;
......@@ -1150,28 +1219,22 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
11501219
11511220 if (prefix_arg_error_return_trace) {
11521221 ZigType *gen_type = get_ptr_to_stack_trace_type(g);
1153 gen_param_types[gen_param_index] = gen_type->type_ref;
1154 gen_param_index += 1;
1155 // after the gen_param_index += 1 because 0 is the return type
1156 param_di_types[gen_param_index] = gen_type->di_type;
1222 gen_param_types.append(gen_type->type_ref);
1223 param_di_types.append(gen_type->di_type);
11571224 }
11581225 if (is_async) {
11591226 {
11601227 // async allocator param
11611228 ZigType *gen_type = fn_type_id->async_allocator_type;
1162 gen_param_types[gen_param_index] = gen_type->type_ref;
1163 gen_param_index += 1;
1164 // after the gen_param_index += 1 because 0 is the return type
1165 param_di_types[gen_param_index] = gen_type->di_type;
1229 gen_param_types.append(gen_type->type_ref);
1230 param_di_types.append(gen_type->di_type);
11661231 }
11671232
11681233 {
11691234 // error code pointer
11701235 ZigType *gen_type = get_pointer_to_type(g, g->builtin_types.entry_global_error_set, false);
1171 gen_param_types[gen_param_index] = gen_type->type_ref;
1172 gen_param_index += 1;
1173 // after the gen_param_index += 1 because 0 is the return type
1174 param_di_types[gen_param_index] = gen_type->di_type;
1236 gen_param_types.append(gen_type->type_ref);
1237 param_di_types.append(gen_type->di_type);
11751238 }
11761239 }
11771240
......@@ -1187,7 +1250,9 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
11871250 if ((err = ensure_complete_type(g, type_entry)))
11881251 return g->builtin_types.entry_invalid;
11891252
1190 if (type_has_bits(type_entry)) {
1253 if (is_c_abi) {
1254 gen_c_abi_param_type(g, &gen_param_types, &param_di_types, type_entry);
1255 } else if (type_has_bits(type_entry)) {
11911256 ZigType *gen_type;
11921257 if (handle_is_ptr(type_entry)) {
11931258 gen_type = get_pointer_to_type(g, type_entry, true);
......@@ -1195,23 +1260,20 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) {
11951260 } else {
11961261 gen_type = type_entry;
11971262 }
1198 gen_param_types[gen_param_index] = gen_type->type_ref;
1199 gen_param_info->gen_index = gen_param_index;
1263 gen_param_info->gen_index = gen_param_types.length;
12001264 gen_param_info->type = gen_type;
1265 gen_param_types.append(gen_type->type_ref);
12011266
1202 gen_param_index += 1;
1203
1204 // after the gen_param_index += 1 because 0 is the return type
1205 param_di_types[gen_param_index] = gen_type->di_type;
1267 param_di_types.append(gen_type->di_type);
12061268 }
12071269 }
12081270
1209 fn_type->data.fn.gen_param_count = gen_param_index;
1271 fn_type->data.fn.gen_param_count = gen_param_types.length;
12101272
12111273 fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref,
1212 gen_param_types, (unsigned int)gen_param_index, fn_type_id->is_var_args);
1274 gen_param_types.items, (unsigned int)gen_param_types.length, fn_type_id->is_var_args);
12131275 fn_type->type_ref = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0);
1214 fn_type->di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types, (int)(gen_param_index + 1), 0);
1276 fn_type->di_type = ZigLLVMCreateSubroutineType(g->dbuilder, param_di_types.items, (int)param_di_types.length, 0);
12151277 }
12161278
12171279 g->fn_type_table.put(&fn_type->data.fn.fn_type_id, fn_type);
src/analyze.hpp+2
......@@ -210,4 +210,6 @@ ZigType *get_primitive_type(CodeGen *g, Buf *name);
210210bool calling_convention_allows_zig_types(CallingConvention cc);
211211const char *calling_convention_name(CallingConvention cc);
212212
213bool type_is_c_abi_int(CodeGen *g, ZigType *ty);
214
213215#endif
src/codegen.cpp+225-17
......@@ -3136,24 +3136,226 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) {
31363136 report_errors_and_exit(g);
31373137}
31383138
3139static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) {
3140 assert(alignment > 0);
3141 LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name);
3142 LLVMSetAlignment(result, alignment);
3143 return result;
3144}
3145
3146// If you edit this function you have to edit the corresponding code:
3147// codegen.cpp:gen_c_abi_param
3148// analyze.cpp:gen_c_abi_param_type
3149// codegen.cpp:gen_c_abi_param_var
3150// codegen.cpp:gen_c_abi_param_var_init
31393151static void gen_c_abi_param(CodeGen *g, ZigList<LLVMValueRef> *gen_param_values, LLVMValueRef val,
31403152 ZigType *ty, AstNode *source_node)
31413153{
3142 if (ty->id == ZigTypeIdInt ||
3143 ty->id == ZigTypeIdFloat ||
3144 ty->id == ZigTypeIdBool ||
3145 ty->id == ZigTypeIdEnum ||
3146 get_codegen_ptr_type(ty) != nullptr)
3147 {
3154 if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat ||
3155 ty->id == ZigTypeIdInt // TODO investigate if we need to change this
3156 ) {
3157 gen_param_values->append(val);
3158 return;
3159 }
3160
3161 // Arrays are just pointers
3162 if (ty->id == ZigTypeIdArray) {
3163 assert(handle_is_ptr(ty));
31483164 gen_param_values->append(val);
31493165 return;
31503166 }
31513167
31523168 if (g->zig_target.arch.arch == ZigLLVM_x86_64) {
3153 give_up_with_c_abi_error(g, source_node);
3154 } else {
3155 give_up_with_c_abi_error(g, source_node);
3169 // This code all assumes that val is a pointer.
3170 assert(handle_is_ptr(ty));
3171
3172 size_t ty_size = type_size(g, ty);
3173 if (ty->id == ZigTypeIdStruct || ty->id == ZigTypeIdUnion) {
3174 // "If the size of an object is larger than four eightbytes, or it contains unaligned
3175 // fields, it has class MEMORY"
3176 if (ty_size > 32) {
3177 gen_param_values->append(val);
3178 return;
3179 }
3180 }
3181 if (ty->id == ZigTypeIdStruct) {
3182 // "If the size of the aggregate exceeds a single eightbyte, each is classified
3183 // separately. Each eightbyte gets initialized to class NO_CLASS."
3184 if (ty_size <= 8) {
3185 bool contains_int = false;
3186 for (size_t i = 0; i < ty->data.structure.src_field_count; i += 1) {
3187 if (type_is_c_abi_int(g, ty->data.structure.fields[i].type_entry)) {
3188 contains_int = true;
3189 break;
3190 }
3191 }
3192 if (contains_int) {
3193 LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0);
3194 LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, val, ptr_to_int_type_ref, "");
3195 LLVMValueRef loaded = LLVMBuildLoad(g->builder, bitcasted, "");
3196 gen_param_values->append(loaded);
3197 return;
3198 }
3199 }
3200 }
3201 }
3202
3203 give_up_with_c_abi_error(g, source_node);
3204}
3205
3206// If you edit this function you have to edit the corresponding code:
3207// codegen.cpp:gen_c_abi_param
3208// analyze.cpp:gen_c_abi_param_type
3209// codegen.cpp:gen_c_abi_param_var
3210// codegen.cpp:gen_c_abi_param_var_init
3211static void gen_c_abi_param_var(CodeGen *g, ImportTableEntry *import, LLVMValueRef llvm_fn, ZigFn *fn,
3212 ZigVar *var, unsigned *arg_index)
3213{
3214 ZigType *ty = var->value->type;
3215
3216 ZigType *dest_ty = nullptr;
3217 unsigned di_arg_index;
3218
3219 if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat ||
3220 ty->id == ZigTypeIdInt // TODO investigate if we need to change this
3221 ) {
3222 var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes);
3223 di_arg_index = *arg_index;
3224 *arg_index += 1;
3225 dest_ty = ty;
3226 goto ok;
3227 }
3228
3229 // Arrays are just pointers
3230 if (ty->id == ZigTypeIdArray) {
3231 di_arg_index = *arg_index;
3232 var->value_ref = LLVMGetParam(llvm_fn, *arg_index);
3233 dest_ty = get_pointer_to_type(g, ty, false);
3234 *arg_index += 1;
3235 goto ok;
3236 }
3237
3238 if (g->zig_target.arch.arch == ZigLLVM_x86_64) {
3239 assert(handle_is_ptr(ty));
3240 size_t ty_size = type_size(g, ty);
3241
3242 if (ty->id == ZigTypeIdStruct || ty->id == ZigTypeIdUnion) {
3243 // "If the size of an object is larger than four eightbytes, or it contains unaligned
3244 // fields, it has class MEMORY"
3245 if (ty_size > 32) {
3246 di_arg_index = *arg_index;
3247 var->value_ref = LLVMGetParam(llvm_fn, *arg_index);
3248 dest_ty = get_pointer_to_type(g, ty, false);
3249 *arg_index += 1;
3250 goto ok;
3251 }
3252 }
3253 if (ty->id == ZigTypeIdStruct) {
3254 // "If the size of the aggregate exceeds a single eightbyte, each is classified
3255 // separately. Each eightbyte gets initialized to class NO_CLASS."
3256 if (ty_size <= 8) {
3257 bool contains_int = false;
3258 for (size_t i = 0; i < ty->data.structure.src_field_count; i += 1) {
3259 if (type_is_c_abi_int(g, ty->data.structure.fields[i].type_entry)) {
3260 contains_int = true;
3261 break;
3262 }
3263 }
3264 if (contains_int) {
3265 var->value_ref = build_alloca(g, ty, buf_ptr(&var->name), var->align_bytes);
3266 *arg_index += 1;
3267 goto ok;
3268 }
3269 }
3270 }
3271 }
3272
3273 give_up_with_c_abi_error(g, fn->proto_node);
3274
3275ok:
3276 if (dest_ty != nullptr && var->decl_node) {
3277 // arg index + 1 because the 0 index is return value
3278 var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope),
3279 buf_ptr(&var->name), import->di_file,
3280 (unsigned)(var->decl_node->line + 1),
3281 dest_ty->di_type, !g->strip_debug_symbols, 0, di_arg_index + 1);
3282 }
3283}
3284
3285// If you edit this function you have to edit the corresponding code:
3286// codegen.cpp:gen_c_abi_param
3287// analyze.cpp:gen_c_abi_param_type
3288// codegen.cpp:gen_c_abi_param_var
3289// codegen.cpp:gen_c_abi_param_var_init
3290static void gen_c_abi_param_var_init(CodeGen *g, ImportTableEntry *import, LLVMValueRef llvm_fn, ZigFn *fn,
3291 ZigVar *var, unsigned *arg_index)
3292{
3293 ZigType *ty = var->value->type;
3294
3295 if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat ||
3296 ty->id == ZigTypeIdInt // TODO investigate if we need to change this
3297 ) {
3298 clear_debug_source_node(g);
3299 gen_store_untyped(g, LLVMGetParam(llvm_fn, *arg_index), var->value_ref, var->align_bytes, false);
3300 if (var->decl_node) {
3301 gen_var_debug_decl(g, var);
3302 }
3303 *arg_index += 1;
3304 return;
3305 }
3306
3307 // Arrays are just pointers
3308 if (ty->id == ZigTypeIdArray) {
3309 if (var->decl_node) {
3310 gen_var_debug_decl(g, var);
3311 }
3312 *arg_index += 1;
3313 return;
3314 }
3315
3316 if (g->zig_target.arch.arch == ZigLLVM_x86_64) {
3317 assert(handle_is_ptr(ty));
3318 size_t ty_size = type_size(g, ty);
3319
3320 if (ty->id == ZigTypeIdStruct || ty->id == ZigTypeIdUnion) {
3321 // "If the size of an object is larger than four eightbytes, or it contains unaligned
3322 // fields, it has class MEMORY"
3323 if (ty_size > 32) {
3324 if (var->decl_node) {
3325 gen_var_debug_decl(g, var);
3326 }
3327 *arg_index += 1;
3328 return;
3329 }
3330 }
3331 if (ty->id == ZigTypeIdStruct) {
3332 // "If the size of the aggregate exceeds a single eightbyte, each is classified
3333 // separately. Each eightbyte gets initialized to class NO_CLASS."
3334 if (ty_size <= 8) {
3335 bool contains_int = false;
3336 for (size_t i = 0; i < ty->data.structure.src_field_count; i += 1) {
3337 if (type_is_c_abi_int(g, ty->data.structure.fields[i].type_entry)) {
3338 contains_int = true;
3339 break;
3340 }
3341 }
3342 if (contains_int) {
3343 clear_debug_source_node(g);
3344 LLVMValueRef arg = LLVMGetParam(llvm_fn, *arg_index);
3345 LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0);
3346 LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, "");
3347 gen_store_untyped(g, arg, bitcasted, var->align_bytes, false);
3348 if (var->decl_node) {
3349 gen_var_debug_decl(g, var);
3350 }
3351 *arg_index += 1;
3352 return;
3353 }
3354 }
3355 }
31563356 }
3357
3358 give_up_with_c_abi_error(g, fn->proto_node);
31573359}
31583360
31593361static 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,
57655967 // TODO ^^ make an actual global variable
57665968}
57675969
5768static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) {
5769 assert(alignment > 0);
5770 LLVMValueRef result = LLVMBuildAlloca(g->builder, type_entry->type_ref, name);
5771 LLVMSetAlignment(result, alignment);
5772 return result;
5773}
5774
57755970static void ensure_cache_dir(CodeGen *g) {
57765971 int err;
57775972 if ((err = os_make_path(&g->cache_dir))) {
......@@ -5899,6 +6094,8 @@ static void do_code_gen(CodeGen *g) {
58996094 // Generate function definitions.
59006095 for (size_t fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) {
59016096 ZigFn *fn_table_entry = g->fn_defs.at(fn_i);
6097 CallingConvention cc = fn_table_entry->type_entry->data.fn.fn_type_id.cc;
6098 bool is_c_abi = cc == CallingConventionC;
59026099
59036100 LLVMValueRef fn = fn_llvm_value(g, fn_table_entry);
59046101 g->cur_fn = fn_table_entry;
......@@ -5922,7 +6119,7 @@ static void do_code_gen(CodeGen *g) {
59226119 }
59236120
59246121 // error return tracing setup
5925 bool is_async = fn_table_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync;
6122 bool is_async = cc == CallingConventionAsync;
59266123 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;
59276124 LLVMValueRef err_ret_array_val = nullptr;
59286125 if (have_err_ret_trace_stack) {
......@@ -5982,6 +6179,7 @@ static void do_code_gen(CodeGen *g) {
59826179 ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base);
59836180
59846181 // create debug variable declarations for variables and allocate all local variables
6182 unsigned c_abi_arg_index = 0;
59856183 for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) {
59866184 ZigVar *var = fn_table_entry->variable_list.at(var_i);
59876185
......@@ -6000,6 +6198,8 @@ static void do_code_gen(CodeGen *g) {
60006198 buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1),
60016199 var->value->type->di_type, !g->strip_debug_symbols, 0);
60026200
6201 } else if (is_c_abi) {
6202 gen_c_abi_param_var(g, import, fn, fn_table_entry, var, &c_abi_arg_index);
60036203 } else {
60046204 assert(var->gen_arg_index != SIZE_MAX);
60056205 ZigType *gen_type;
......@@ -6056,7 +6256,14 @@ static void do_code_gen(CodeGen *g) {
60566256 // create debug variable declarations for parameters
60576257 // rely on the first variables in the variable_list being parameters.
60586258 size_t next_var_i = 0;
6259 unsigned c_abi_arg_init_index = 0;
60596260 for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) {
6261 if (is_c_abi) {
6262 ZigVar *var = fn_table_entry->variable_list.at(param_i);
6263 gen_c_abi_param_var_init(g, import, fn, fn_table_entry, var, &c_abi_arg_init_index);
6264 continue;
6265 }
6266
60606267 FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i];
60616268 if (info->gen_index == SIZE_MAX)
60626269 continue;
......@@ -6078,6 +6285,7 @@ static void do_code_gen(CodeGen *g) {
60786285 gen_var_debug_decl(g, variable);
60796286 }
60806287 }
6288 assert(c_abi_arg_index == c_abi_arg_init_index);
60816289
60826290 ir_render(g, fn_table_entry);
60836291
std/build.zig+27
......@@ -48,6 +48,12 @@ pub const Builder = struct {
4848 cache_root: []const u8,
4949 release_mode: ?builtin.Mode,
5050
51 pub const CStd = enum {
52 C89,
53 C99,
54 C11,
55 };
56
5157 const UserInputOptionsMap = HashMap([]const u8, UserInputOption, mem.hash_slice_u8, mem.eql_slice_u8);
5258 const AvailableOptionsMap = HashMap([]const u8, AvailableOption, mem.hash_slice_u8, mem.eql_slice_u8);
5359
......@@ -817,6 +823,7 @@ pub const LibExeObjStep = struct {
817823 frameworks: BufSet,
818824 verbose_link: bool,
819825 no_rosegment: bool,
826 c_std: Builder.CStd,
820827
821828 // zig only stuff
822829 root_src: ?[]const u8,
......@@ -918,6 +925,7 @@ pub const LibExeObjStep = struct {
918925 .object_src = undefined,
919926 .disable_libc = true,
920927 .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable,
928 .c_std = Builder.CStd.C99,
921929 };
922930 self.computeOutFileNames();
923931 return self;
......@@ -952,6 +960,7 @@ pub const LibExeObjStep = struct {
952960 .disable_libc = false,
953961 .is_zig = false,
954962 .linker_script = null,
963 .c_std = Builder.CStd.C99,
955964
956965 .root_src = undefined,
957966 .verbose_link = false,
......@@ -1392,6 +1401,13 @@ pub const LibExeObjStep = struct {
13921401
13931402 const is_darwin = self.target.isDarwin();
13941403
1404 const c_std_arg = switch (self.c_std) {
1405 Builder.CStd.C89 => "-std=c89",
1406 Builder.CStd.C99 => "-std=c99",
1407 Builder.CStd.C11 => "-std=c11",
1408 };
1409 try cc_args.append(c_std_arg);
1410
13951411 switch (self.kind) {
13961412 Kind.Obj => {
13971413 cc_args.append("-c") catch unreachable;
......@@ -1678,6 +1694,17 @@ pub const TestStep = struct {
16781694 self.filter = text;
16791695 }
16801696
1697 pub fn addObject(self: *TestStep, obj: *LibExeObjStep) void {
1698 assert(obj.kind == LibExeObjStep.Kind.Obj);
1699
1700 self.step.dependOn(&obj.step);
1701
1702 self.object_files.append(obj.getOutputPath()) catch unreachable;
1703
1704 // TODO should be some kind of isolated directory that only has this header in it
1705 self.include_dirs.append(self.builder.cache_root) catch unreachable;
1706 }
1707
16811708 pub fn addObjectFile(self: *TestStep, path: []const u8) void {
16821709 self.object_files.append(path) catch unreachable;
16831710 }
test/build_examples.zig+6
......@@ -28,4 +28,10 @@ pub fn addCases(cases: *tests.BuildExamplesContext) void {
2828 // TODO figure out how to make this work on darwin - probably libSystem has dlopen/dlsym in it
2929 cases.addBuildFile("test/standalone/load_dynamic_library/build.zig");
3030 }
31
32 if (!is_windows // TODO support compiling C files on windows with zig build system
33 and builtin.arch == builtin.Arch.x86_64 // TODO add C ABI support for other architectures
34 ) {
35 cases.addBuildFile("test/stage1/c_abi/build.zig");
36 }
3137}
test/gen_h.zig+24-4
......@@ -20,6 +20,9 @@ pub fn addCases(cases: *tests.GenHContext) void {
2020 \\ A: i32,
2121 \\ B: f32,
2222 \\ C: bool,
23 \\ D: u64,
24 \\ E: u64,
25 \\ F: u64,
2326 \\};
2427 \\export fn entry(foo: Foo) void { }
2528 ,
......@@ -27,6 +30,9 @@ pub fn addCases(cases: *tests.GenHContext) void {
2730 \\ int32_t A;
2831 \\ float B;
2932 \\ bool C;
33 \\ uint64_t D;
34 \\ uint64_t E;
35 \\ uint64_t F;
3036 \\};
3137 \\
3238 \\TEST_EXPORT void entry(struct Foo foo);
......@@ -34,17 +40,34 @@ pub fn addCases(cases: *tests.GenHContext) void {
3440 );
3541
3642 cases.add("declare union",
43 \\const Big = extern struct {
44 \\ A: u64,
45 \\ B: u64,
46 \\ C: u64,
47 \\ D: u64,
48 \\ E: u64,
49 \\};
3750 \\const Foo = extern union {
3851 \\ A: i32,
3952 \\ B: f32,
4053 \\ C: bool,
54 \\ D: Big,
4155 \\};
42 \\export fn entry(foo: Foo) void { }
56 \\export fn entry(foo: Foo) void {}
4357 ,
58 \\struct Big {
59 \\ uint64_t A;
60 \\ uint64_t B;
61 \\ uint64_t C;
62 \\ uint64_t D;
63 \\ uint64_t E;
64 \\};
65 \\
4466 \\union Foo {
4567 \\ int32_t A;
4668 \\ float B;
4769 \\ bool C;
70 \\ struct Big D;
4871 \\};
4972 \\
5073 \\TEST_EXPORT void entry(union Foo foo);
......@@ -85,7 +108,6 @@ pub fn addCases(cases: *tests.GenHContext) void {
85108 \\export fn a(s: *S) u8 {
86109 \\ return s.a;
87110 \\}
88
89111 ,
90112 \\struct S;
91113 \\TEST_EXPORT uint8_t a(struct S * s);
......@@ -101,7 +123,6 @@ pub fn addCases(cases: *tests.GenHContext) void {
101123 \\export fn a(s: *U) u8 {
102124 \\ return s.A;
103125 \\}
104
105126 ,
106127 \\union U;
107128 \\TEST_EXPORT uint8_t a(union U * s);
......@@ -117,7 +138,6 @@ pub fn addCases(cases: *tests.GenHContext) void {
117138 \\export fn a(s: *E) u8 {
118139 \\ return @enumToInt(s.*);
119140 \\}
120
121141 ,
122142 \\enum E;
123143 \\TEST_EXPORT uint8_t a(enum E * s);
test/stage1/c_abi/build.zig created+17
......@@ -0,0 +1,17 @@
1const Builder = @import("std").build.Builder;
2
3pub fn build(b: *Builder) void {
4 const rel_opts = b.standardReleaseOptions();
5
6 const c_obj = b.addCObject("cfuncs", "cfuncs.c");
7 c_obj.setBuildMode(rel_opts);
8
9 const main = b.addTest("main.zig");
10 main.setBuildMode(rel_opts);
11 main.addObject(c_obj);
12
13 const test_step = b.step("test", "Test the program");
14 test_step.dependOn(&main.step);
15
16 b.default_step.dependOn(test_step);
17}
test/stage1/c_abi/cfuncs.c created+64
......@@ -0,0 +1,64 @@
1#include <inttypes.h>
2#include <stdlib.h>
3#include <stdbool.h>
4
5void zig_panic();
6
7static void assert_or_panic(bool ok) {
8 if (!ok) {
9 zig_panic();
10 }
11}
12
13void zig_u8(uint8_t);
14void zig_u16(uint16_t);
15void zig_u32(uint32_t);
16void zig_u64(uint64_t);
17void zig_i8(int8_t);
18void zig_i16(int16_t);
19void zig_i32(int32_t);
20void zig_i64(int64_t);
21
22void run_c_tests(void) {
23 zig_u8(0xff);
24 zig_u16(0xfffe);
25 zig_u32(0xfffffffd);
26 zig_u64(0xfffffffffffffffc);
27
28 zig_i8(-1);
29 zig_i16(-2);
30 zig_i32(-3);
31 zig_i64(-4);
32}
33
34void c_u8(uint8_t x) {
35 assert_or_panic(x == 0xff);
36}
37
38void c_u16(uint16_t x) {
39 assert_or_panic(x == 0xfffe);
40}
41
42void c_u32(uint32_t x) {
43 assert_or_panic(x == 0xfffffffd);
44}
45
46void c_u64(uint64_t x) {
47 assert_or_panic(x == 0xfffffffffffffffcULL);
48}
49
50void c_i8(int8_t x) {
51 assert_or_panic(x == -1);
52}
53
54void c_i16(int16_t x) {
55 assert_or_panic(x == -2);
56}
57
58void c_i32(int32_t x) {
59 assert_or_panic(x == -3);
60}
61
62void c_i64(int64_t x) {
63 assert_or_panic(x == -4);
64}
test/stage1/c_abi/main.zig created+58
......@@ -0,0 +1,58 @@
1const std = @import("std");
2const assertOrPanic = std.debug.assertOrPanic;
3
4extern fn run_c_tests() void;
5
6export fn zig_panic() noreturn {
7 @panic("zig_panic called from C");
8}
9
10test "C importing Zig ABI Tests" {
11 run_c_tests();
12}
13
14extern fn c_u8(u8) void;
15extern fn c_u16(u16) void;
16extern fn c_u32(u32) void;
17extern fn c_u64(u64) void;
18extern fn c_i8(i8) void;
19extern fn c_i16(i16) void;
20extern fn c_i32(i32) void;
21extern fn c_i64(i64) void;
22
23test "C ABI integers" {
24 c_u8(0xff);
25 c_u16(0xfffe);
26 c_u32(0xfffffffd);
27 c_u64(0xfffffffffffffffc);
28
29 c_i8(-1);
30 c_i16(-2);
31 c_i32(-3);
32 c_i64(-4);
33}
34
35export fn zig_u8(x: u8) void {
36 assertOrPanic(x == 0xff);
37}
38export fn zig_u16(x: u16) void {
39 assertOrPanic(x == 0xfffe);
40}
41export fn zig_u32(x: u32) void {
42 assertOrPanic(x == 0xfffffffd);
43}
44export fn zig_u64(x: u64) void {
45 assertOrPanic(x == 0xfffffffffffffffc);
46}
47export fn zig_i8(x: i8) void {
48 assertOrPanic(x == -1);
49}
50export fn zig_i16(x: i16) void {
51 assertOrPanic(x == -2);
52}
53export fn zig_i32(x: i32) void {
54 assertOrPanic(x == -3);
55}
56export fn zig_i64(x: i64) void {
57 assertOrPanic(x == -4);
58}