| author | |
| committer | |
| log | a9a925e500c66b8eb57d619b9b6828c70a13d4c5 |
| tree | b118496e115a04c5b49fa77bf3478d2821d82024 |
| parent | a375bd0d9f5c7773598a8ea31af8c963d6a79706 |
| signature |
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) { | ... | @@ -1061,6 +1061,77 @@ ZigType *get_ptr_to_stack_trace_type(CodeGen *g) { |
| 1061 | return g->ptr_to_stack_trace_type; | 1061 | return g->ptr_to_stack_trace_type; |
| 1062 | } | 1062 | } |
| 1063 | 1063 | ||
| 1064 | bool 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 | ||
| 1080 | static 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 | |||
| 1064 | ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { | 1135 | ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 1065 | Error err; | 1136 | Error err; |
| 1066 | auto table_entry = g->fn_type_table.maybe_get(fn_type_id); | 1137 | 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) { | ... | @@ -1119,18 +1190,18 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 1119 | bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) && | 1190 | bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) && |
| 1120 | handle_is_ptr(fn_type_id->return_type); | 1191 | handle_is_ptr(fn_type_id->return_type); |
| 1121 | bool is_async = fn_type_id->cc == CallingConventionAsync; | 1192 | bool is_async = fn_type_id->cc == CallingConventionAsync; |
| 1193 | bool is_c_abi = fn_type_id->cc == CallingConventionC; | ||
| 1122 | bool prefix_arg_error_return_trace = g->have_err_ret_tracing && fn_type_can_fail(fn_type_id); | 1194 | bool prefix_arg_error_return_trace = g->have_err_ret_tracing && fn_type_can_fail(fn_type_id); |
| 1123 | // +1 for maybe making the first argument the return value | 1195 | // +1 for maybe making the first argument the return value |
| 1124 | // +1 for maybe first argument the error return trace | 1196 | // +1 for maybe first argument the error return trace |
| 1125 | // +2 for maybe arguments async allocator and error code pointer | 1197 | // +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 = {}; |
| 1127 | // +1 because 0 is the return type and | 1199 | // +1 because 0 is the return type and |
| 1128 | // +1 for maybe making first arg ret val and | 1200 | // +1 for maybe making first arg ret val and |
| 1129 | // +1 for maybe first argument the error return trace | 1201 | // +1 for maybe first argument the error return trace |
| 1130 | // +2 for maybe arguments async allocator and error code pointer | 1202 | // +2 for maybe arguments async allocator and error code pointer |
| 1131 | ZigLLVMDIType **param_di_types = allocate<ZigLLVMDIType*>(5 + fn_type_id->param_count); | 1203 | ZigList<ZigLLVMDIType *> param_di_types = {}; |
| 1132 | param_di_types[0] = fn_type_id->return_type->di_type; | 1204 | param_di_types.append(fn_type_id->return_type->di_type); |
| 1133 | size_t gen_param_index = 0; | ||
| 1134 | ZigType *gen_return_type; | 1205 | ZigType *gen_return_type; |
| 1135 | if (is_async) { | 1206 | if (is_async) { |
| 1136 | gen_return_type = get_pointer_to_type(g, g->builtin_types.entry_u8, false); | 1207 | 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) { | ... | @@ -1138,10 +1209,8 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 1138 | gen_return_type = g->builtin_types.entry_void; | 1209 | gen_return_type = g->builtin_types.entry_void; |
| 1139 | } else if (first_arg_return) { | 1210 | } else if (first_arg_return) { |
| 1140 | ZigType *gen_type = get_pointer_to_type(g, fn_type_id->return_type, false); | 1211 | 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; | 1212 | gen_param_types.append(gen_type->type_ref); |
| 1142 | gen_param_index += 1; | 1213 | param_di_types.append(gen_type->di_type); |
| 1143 | // after the gen_param_index += 1 because 0 is the return type | ||
| 1144 | param_di_types[gen_param_index] = gen_type->di_type; | ||
| 1145 | gen_return_type = g->builtin_types.entry_void; | 1214 | gen_return_type = g->builtin_types.entry_void; |
| 1146 | } else { | 1215 | } else { |
| 1147 | gen_return_type = fn_type_id->return_type; | 1216 | gen_return_type = fn_type_id->return_type; |
| ... | @@ -1150,28 +1219,22 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { | ... | @@ -1150,28 +1219,22 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 1150 | 1219 | ||
| 1151 | if (prefix_arg_error_return_trace) { | 1220 | if (prefix_arg_error_return_trace) { |
| 1152 | ZigType *gen_type = get_ptr_to_stack_trace_type(g); | 1221 | ZigType *gen_type = get_ptr_to_stack_trace_type(g); |
| 1153 | gen_param_types[gen_param_index] = gen_type->type_ref; | 1222 | gen_param_types.append(gen_type->type_ref); |
| 1154 | gen_param_index += 1; | 1223 | param_di_types.append(gen_type->di_type); |
| 1155 | // after the gen_param_index += 1 because 0 is the return type | ||
| 1156 | param_di_types[gen_param_index] = gen_type->di_type; | ||
| 1157 | } | 1224 | } |
| 1158 | if (is_async) { | 1225 | if (is_async) { |
| 1159 | { | 1226 | { |
| 1160 | // async allocator param | 1227 | // async allocator param |
| 1161 | ZigType *gen_type = fn_type_id->async_allocator_type; | 1228 | ZigType *gen_type = fn_type_id->async_allocator_type; |
| 1162 | gen_param_types[gen_param_index] = gen_type->type_ref; | 1229 | gen_param_types.append(gen_type->type_ref); |
| 1163 | gen_param_index += 1; | 1230 | param_di_types.append(gen_type->di_type); |
| 1164 | // after the gen_param_index += 1 because 0 is the return type | ||
| 1165 | param_di_types[gen_param_index] = gen_type->di_type; | ||
| 1166 | } | 1231 | } |
| 1167 | 1232 | ||
| 1168 | { | 1233 | { |
| 1169 | // error code pointer | 1234 | // error code pointer |
| 1170 | ZigType *gen_type = get_pointer_to_type(g, g->builtin_types.entry_global_error_set, false); | 1235 | 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; | 1236 | gen_param_types.append(gen_type->type_ref); |
| 1172 | gen_param_index += 1; | 1237 | param_di_types.append(gen_type->di_type); |
| 1173 | // after the gen_param_index += 1 because 0 is the return type | ||
| 1174 | param_di_types[gen_param_index] = gen_type->di_type; | ||
| 1175 | } | 1238 | } |
| 1176 | } | 1239 | } |
| 1177 | 1240 | ||
| ... | @@ -1187,7 +1250,9 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { | ... | @@ -1187,7 +1250,9 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 1187 | if ((err = ensure_complete_type(g, type_entry))) | 1250 | if ((err = ensure_complete_type(g, type_entry))) |
| 1188 | return g->builtin_types.entry_invalid; | 1251 | return g->builtin_types.entry_invalid; |
| 1189 | 1252 | ||
| 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)) { | ||
| 1191 | ZigType *gen_type; | 1256 | ZigType *gen_type; |
| 1192 | if (handle_is_ptr(type_entry)) { | 1257 | if (handle_is_ptr(type_entry)) { |
| 1193 | gen_type = get_pointer_to_type(g, type_entry, true); | 1258 | gen_type = get_pointer_to_type(g, type_entry, true); |
| ... | @@ -1195,23 +1260,20 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { | ... | @@ -1195,23 +1260,20 @@ ZigType *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { |
| 1195 | } else { | 1260 | } else { |
| 1196 | gen_type = type_entry; | 1261 | gen_type = type_entry; |
| 1197 | } | 1262 | } |
| 1198 | gen_param_types[gen_param_index] = gen_type->type_ref; | 1263 | gen_param_info->gen_index = gen_param_types.length; |
| 1199 | gen_param_info->gen_index = gen_param_index; | ||
| 1200 | gen_param_info->type = gen_type; | 1264 | gen_param_info->type = gen_type; |
| 1265 | gen_param_types.append(gen_type->type_ref); | ||
| 1201 | 1266 | ||
| 1202 | gen_param_index += 1; | 1267 | param_di_types.append(gen_type->di_type); |
| 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; | ||
| 1206 | } | 1268 | } |
| 1207 | } | 1269 | } |
| 1208 | 1270 | ||
| 1209 | fn_type->data.fn.gen_param_count = gen_param_index; | 1271 | fn_type->data.fn.gen_param_count = gen_param_types.length; |
| 1210 | 1272 | ||
| 1211 | fn_type->data.fn.raw_type_ref = LLVMFunctionType(gen_return_type->type_ref, | 1273 | 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); |
| 1213 | fn_type->type_ref = LLVMPointerType(fn_type->data.fn.raw_type_ref, 0); | 1275 | 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); |
| 1215 | } | 1277 | } |
| 1216 | 1278 | ||
| 1217 | g->fn_type_table.put(&fn_type->data.fn.fn_type_id, fn_type); | 1279 | 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); | ... | @@ -210,4 +210,6 @@ ZigType *get_primitive_type(CodeGen *g, Buf *name); |
| 210 | bool calling_convention_allows_zig_types(CallingConvention cc); | 210 | bool calling_convention_allows_zig_types(CallingConvention cc); |
| 211 | const char *calling_convention_name(CallingConvention cc); | 211 | const char *calling_convention_name(CallingConvention cc); |
| 212 | 212 | ||
| 213 | bool type_is_c_abi_int(CodeGen *g, ZigType *ty); | ||
| 214 | |||
| 213 | #endif | 215 | #endif |
src/codegen.cpp+225-17| ... | @@ -3136,24 +3136,226 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { | ... | @@ -3136,24 +3136,226 @@ static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { |
| 3136 | report_errors_and_exit(g); | 3136 | report_errors_and_exit(g); |
| 3137 | } | 3137 | } |
| 3138 | 3138 | ||
| 3139 | static 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 | ||
| 3139 | static void gen_c_abi_param(CodeGen *g, ZigList<LLVMValueRef> *gen_param_values, LLVMValueRef val, | 3151 | static void gen_c_abi_param(CodeGen *g, ZigList<LLVMValueRef> *gen_param_values, LLVMValueRef val, |
| 3140 | ZigType *ty, AstNode *source_node) | 3152 | ZigType *ty, AstNode *source_node) |
| 3141 | { | 3153 | { |
| 3142 | if (ty->id == ZigTypeIdInt || | 3154 | if (type_is_c_abi_int(g, ty) || ty->id == ZigTypeIdFloat || |
| 3143 | ty->id == ZigTypeIdFloat || | 3155 | ty->id == ZigTypeIdInt // TODO investigate if we need to change this |
| 3144 | ty->id == ZigTypeIdBool || | 3156 | ) { |
| 3145 | ty->id == ZigTypeIdEnum || | 3157 | gen_param_values->append(val); |
| 3146 | get_codegen_ptr_type(ty) != nullptr) | 3158 | return; |
| 3147 | { | 3159 | } |
| 3160 | |||
| 3161 | // Arrays are just pointers | ||
| 3162 | if (ty->id == ZigTypeIdArray) { | ||
| 3163 | assert(handle_is_ptr(ty)); | ||
| 3148 | gen_param_values->append(val); | 3164 | gen_param_values->append(val); |
| 3149 | return; | 3165 | return; |
| 3150 | } | 3166 | } |
| 3151 | 3167 | ||
| 3152 | if (g->zig_target.arch.arch == ZigLLVM_x86_64) { | 3168 | if (g->zig_target.arch.arch == ZigLLVM_x86_64) { |
| 3153 | give_up_with_c_abi_error(g, source_node); | 3169 | // This code all assumes that val is a pointer. |
| 3154 | } else { | 3170 | assert(handle_is_ptr(ty)); |
| 3155 | give_up_with_c_abi_error(g, source_node); | 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 | ||
| 3211 | static 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 | |||
| 3275 | ok: | ||
| 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 | ||
| 3290 | static 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 | } | ||
| 3156 | } | 3356 | } |
| 3357 | |||
| 3358 | give_up_with_c_abi_error(g, fn->proto_node); | ||
| 3157 | } | 3359 | } |
| 3158 | 3360 | ||
| 3159 | static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCall *instruction) { | 3361 | static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstructionCall *instruction) { |
| ... | @@ -5765,13 +5967,6 @@ static void gen_global_var(CodeGen *g, ZigVar *var, LLVMValueRef init_val, | ... | @@ -5765,13 +5967,6 @@ static void gen_global_var(CodeGen *g, ZigVar *var, LLVMValueRef init_val, |
| 5765 | // TODO ^^ make an actual global variable | 5967 | // TODO ^^ make an actual global variable |
| 5766 | } | 5968 | } |
| 5767 | 5969 | ||
| 5768 | static 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 | |||
| 5775 | static void ensure_cache_dir(CodeGen *g) { | 5970 | static void ensure_cache_dir(CodeGen *g) { |
| 5776 | int err; | 5971 | int err; |
| 5777 | if ((err = os_make_path(&g->cache_dir))) { | 5972 | if ((err = os_make_path(&g->cache_dir))) { |
| ... | @@ -5899,6 +6094,8 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -5899,6 +6094,8 @@ static void do_code_gen(CodeGen *g) { |
| 5899 | // Generate function definitions. | 6094 | // Generate function definitions. |
| 5900 | for (size_t fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) { | 6095 | for (size_t fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) { |
| 5901 | ZigFn *fn_table_entry = g->fn_defs.at(fn_i); | 6096 | 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; | ||
| 5902 | 6099 | ||
| 5903 | LLVMValueRef fn = fn_llvm_value(g, fn_table_entry); | 6100 | LLVMValueRef fn = fn_llvm_value(g, fn_table_entry); |
| 5904 | g->cur_fn = fn_table_entry; | 6101 | g->cur_fn = fn_table_entry; |
| ... | @@ -5922,7 +6119,7 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -5922,7 +6119,7 @@ static void do_code_gen(CodeGen *g) { |
| 5922 | } | 6119 | } |
| 5923 | 6120 | ||
| 5924 | // error return tracing setup | 6121 | // 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; |
| 5926 | 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; | 6123 | 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; |
| 5927 | LLVMValueRef err_ret_array_val = nullptr; | 6124 | LLVMValueRef err_ret_array_val = nullptr; |
| 5928 | if (have_err_ret_trace_stack) { | 6125 | if (have_err_ret_trace_stack) { |
| ... | @@ -5982,6 +6179,7 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -5982,6 +6179,7 @@ static void do_code_gen(CodeGen *g) { |
| 5982 | ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); | 6179 | ImportTableEntry *import = get_scope_import(&fn_table_entry->fndef_scope->base); |
| 5983 | 6180 | ||
| 5984 | // create debug variable declarations for variables and allocate all local variables | 6181 | // create debug variable declarations for variables and allocate all local variables |
| 6182 | unsigned c_abi_arg_index = 0; | ||
| 5985 | for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) { | 6183 | for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) { |
| 5986 | ZigVar *var = fn_table_entry->variable_list.at(var_i); | 6184 | ZigVar *var = fn_table_entry->variable_list.at(var_i); |
| 5987 | 6185 | ||
| ... | @@ -6000,6 +6198,8 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -6000,6 +6198,8 @@ static void do_code_gen(CodeGen *g) { |
| 6000 | buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1), | 6198 | buf_ptr(&var->name), import->di_file, (unsigned)(var->decl_node->line + 1), |
| 6001 | var->value->type->di_type, !g->strip_debug_symbols, 0); | 6199 | var->value->type->di_type, !g->strip_debug_symbols, 0); |
| 6002 | 6200 | ||
| 6201 | } else if (is_c_abi) { | ||
| 6202 | gen_c_abi_param_var(g, import, fn, fn_table_entry, var, &c_abi_arg_index); | ||
| 6003 | } else { | 6203 | } else { |
| 6004 | assert(var->gen_arg_index != SIZE_MAX); | 6204 | assert(var->gen_arg_index != SIZE_MAX); |
| 6005 | ZigType *gen_type; | 6205 | ZigType *gen_type; |
| ... | @@ -6056,7 +6256,14 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -6056,7 +6256,14 @@ static void do_code_gen(CodeGen *g) { |
| 6056 | // create debug variable declarations for parameters | 6256 | // create debug variable declarations for parameters |
| 6057 | // rely on the first variables in the variable_list being parameters. | 6257 | // rely on the first variables in the variable_list being parameters. |
| 6058 | size_t next_var_i = 0; | 6258 | size_t next_var_i = 0; |
| 6259 | unsigned c_abi_arg_init_index = 0; | ||
| 6059 | for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { | 6260 | 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 | |||
| 6060 | FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i]; | 6267 | FnGenParamInfo *info = &fn_table_entry->type_entry->data.fn.gen_param_info[param_i]; |
| 6061 | if (info->gen_index == SIZE_MAX) | 6268 | if (info->gen_index == SIZE_MAX) |
| 6062 | continue; | 6269 | continue; |
| ... | @@ -6078,6 +6285,7 @@ static void do_code_gen(CodeGen *g) { | ... | @@ -6078,6 +6285,7 @@ static void do_code_gen(CodeGen *g) { |
| 6078 | gen_var_debug_decl(g, variable); | 6285 | gen_var_debug_decl(g, variable); |
| 6079 | } | 6286 | } |
| 6080 | } | 6287 | } |
| 6288 | assert(c_abi_arg_index == c_abi_arg_init_index); | ||
| 6081 | 6289 | ||
| 6082 | ir_render(g, fn_table_entry); | 6290 | ir_render(g, fn_table_entry); |
| 6083 | 6291 |
std/build.zig+27| ... | @@ -48,6 +48,12 @@ pub const Builder = struct { | ... | @@ -48,6 +48,12 @@ pub const Builder = struct { |
| 48 | cache_root: []const u8, | 48 | cache_root: []const u8, |
| 49 | release_mode: ?builtin.Mode, | 49 | release_mode: ?builtin.Mode, |
| 50 | 50 | ||
| 51 | pub const CStd = enum { | ||
| 52 | C89, | ||
| 53 | C99, | ||
| 54 | C11, | ||
| 55 | }; | ||
| 56 | |||
| 51 | const UserInputOptionsMap = HashMap([]const u8, UserInputOption, mem.hash_slice_u8, mem.eql_slice_u8); | 57 | const UserInputOptionsMap = HashMap([]const u8, UserInputOption, mem.hash_slice_u8, mem.eql_slice_u8); |
| 52 | const AvailableOptionsMap = HashMap([]const u8, AvailableOption, mem.hash_slice_u8, mem.eql_slice_u8); | 58 | const AvailableOptionsMap = HashMap([]const u8, AvailableOption, mem.hash_slice_u8, mem.eql_slice_u8); |
| 53 | 59 | ||
| ... | @@ -817,6 +823,7 @@ pub const LibExeObjStep = struct { | ... | @@ -817,6 +823,7 @@ pub const LibExeObjStep = struct { |
| 817 | frameworks: BufSet, | 823 | frameworks: BufSet, |
| 818 | verbose_link: bool, | 824 | verbose_link: bool, |
| 819 | no_rosegment: bool, | 825 | no_rosegment: bool, |
| 826 | c_std: Builder.CStd, | ||
| 820 | 827 | ||
| 821 | // zig only stuff | 828 | // zig only stuff |
| 822 | root_src: ?[]const u8, | 829 | root_src: ?[]const u8, |
| ... | @@ -918,6 +925,7 @@ pub const LibExeObjStep = struct { | ... | @@ -918,6 +925,7 @@ pub const LibExeObjStep = struct { |
| 918 | .object_src = undefined, | 925 | .object_src = undefined, |
| 919 | .disable_libc = true, | 926 | .disable_libc = true, |
| 920 | .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable, | 927 | .build_options_contents = std.Buffer.initSize(builder.allocator, 0) catch unreachable, |
| 928 | .c_std = Builder.CStd.C99, | ||
| 921 | }; | 929 | }; |
| 922 | self.computeOutFileNames(); | 930 | self.computeOutFileNames(); |
| 923 | return self; | 931 | return self; |
| ... | @@ -952,6 +960,7 @@ pub const LibExeObjStep = struct { | ... | @@ -952,6 +960,7 @@ pub const LibExeObjStep = struct { |
| 952 | .disable_libc = false, | 960 | .disable_libc = false, |
| 953 | .is_zig = false, | 961 | .is_zig = false, |
| 954 | .linker_script = null, | 962 | .linker_script = null, |
| 963 | .c_std = Builder.CStd.C99, | ||
| 955 | 964 | ||
| 956 | .root_src = undefined, | 965 | .root_src = undefined, |
| 957 | .verbose_link = false, | 966 | .verbose_link = false, |
| ... | @@ -1392,6 +1401,13 @@ pub const LibExeObjStep = struct { | ... | @@ -1392,6 +1401,13 @@ pub const LibExeObjStep = struct { |
| 1392 | 1401 | ||
| 1393 | const is_darwin = self.target.isDarwin(); | 1402 | const is_darwin = self.target.isDarwin(); |
| 1394 | 1403 | ||
| 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 | |||
| 1395 | switch (self.kind) { | 1411 | switch (self.kind) { |
| 1396 | Kind.Obj => { | 1412 | Kind.Obj => { |
| 1397 | cc_args.append("-c") catch unreachable; | 1413 | cc_args.append("-c") catch unreachable; |
| ... | @@ -1678,6 +1694,17 @@ pub const TestStep = struct { | ... | @@ -1678,6 +1694,17 @@ pub const TestStep = struct { |
| 1678 | self.filter = text; | 1694 | self.filter = text; |
| 1679 | } | 1695 | } |
| 1680 | 1696 | ||
| 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 | |||
| 1681 | pub fn addObjectFile(self: *TestStep, path: []const u8) void { | 1708 | pub fn addObjectFile(self: *TestStep, path: []const u8) void { |
| 1682 | self.object_files.append(path) catch unreachable; | 1709 | self.object_files.append(path) catch unreachable; |
| 1683 | } | 1710 | } |
test/build_examples.zig+6| ... | @@ -28,4 +28,10 @@ pub fn addCases(cases: *tests.BuildExamplesContext) void { | ... | @@ -28,4 +28,10 @@ pub fn addCases(cases: *tests.BuildExamplesContext) void { |
| 28 | // TODO figure out how to make this work on darwin - probably libSystem has dlopen/dlsym in it | 28 | // TODO figure out how to make this work on darwin - probably libSystem has dlopen/dlsym in it |
| 29 | cases.addBuildFile("test/standalone/load_dynamic_library/build.zig"); | 29 | cases.addBuildFile("test/standalone/load_dynamic_library/build.zig"); |
| 30 | } | 30 | } |
| 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 | } | ||
| 31 | } | 37 | } |
test/gen_h.zig+24-4| ... | @@ -20,6 +20,9 @@ pub fn addCases(cases: *tests.GenHContext) void { | ... | @@ -20,6 +20,9 @@ pub fn addCases(cases: *tests.GenHContext) void { |
| 20 | \\ A: i32, | 20 | \\ A: i32, |
| 21 | \\ B: f32, | 21 | \\ B: f32, |
| 22 | \\ C: bool, | 22 | \\ C: bool, |
| 23 | \\ D: u64, | ||
| 24 | \\ E: u64, | ||
| 25 | \\ F: u64, | ||
| 23 | \\}; | 26 | \\}; |
| 24 | \\export fn entry(foo: Foo) void { } | 27 | \\export fn entry(foo: Foo) void { } |
| 25 | , | 28 | , |
| ... | @@ -27,6 +30,9 @@ pub fn addCases(cases: *tests.GenHContext) void { | ... | @@ -27,6 +30,9 @@ pub fn addCases(cases: *tests.GenHContext) void { |
| 27 | \\ int32_t A; | 30 | \\ int32_t A; |
| 28 | \\ float B; | 31 | \\ float B; |
| 29 | \\ bool C; | 32 | \\ bool C; |
| 33 | \\ uint64_t D; | ||
| 34 | \\ uint64_t E; | ||
| 35 | \\ uint64_t F; | ||
| 30 | \\}; | 36 | \\}; |
| 31 | \\ | 37 | \\ |
| 32 | \\TEST_EXPORT void entry(struct Foo foo); | 38 | \\TEST_EXPORT void entry(struct Foo foo); |
| ... | @@ -34,17 +40,34 @@ pub fn addCases(cases: *tests.GenHContext) void { | ... | @@ -34,17 +40,34 @@ pub fn addCases(cases: *tests.GenHContext) void { |
| 34 | ); | 40 | ); |
| 35 | 41 | ||
| 36 | cases.add("declare union", | 42 | 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 | \\}; | ||
| 37 | \\const Foo = extern union { | 50 | \\const Foo = extern union { |
| 38 | \\ A: i32, | 51 | \\ A: i32, |
| 39 | \\ B: f32, | 52 | \\ B: f32, |
| 40 | \\ C: bool, | 53 | \\ C: bool, |
| 54 | \\ D: Big, | ||
| 41 | \\}; | 55 | \\}; |
| 42 | \\export fn entry(foo: Foo) void { } | 56 | \\export fn entry(foo: Foo) void {} |
| 43 | , | 57 | , |
| 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 | \\ | ||
| 44 | \\union Foo { | 66 | \\union Foo { |
| 45 | \\ int32_t A; | 67 | \\ int32_t A; |
| 46 | \\ float B; | 68 | \\ float B; |
| 47 | \\ bool C; | 69 | \\ bool C; |
| 70 | \\ struct Big D; | ||
| 48 | \\}; | 71 | \\}; |
| 49 | \\ | 72 | \\ |
| 50 | \\TEST_EXPORT void entry(union Foo foo); | 73 | \\TEST_EXPORT void entry(union Foo foo); |
| ... | @@ -85,7 +108,6 @@ pub fn addCases(cases: *tests.GenHContext) void { | ... | @@ -85,7 +108,6 @@ pub fn addCases(cases: *tests.GenHContext) void { |
| 85 | \\export fn a(s: *S) u8 { | 108 | \\export fn a(s: *S) u8 { |
| 86 | \\ return s.a; | 109 | \\ return s.a; |
| 87 | \\} | 110 | \\} |
| 88 | |||
| 89 | , | 111 | , |
| 90 | \\struct S; | 112 | \\struct S; |
| 91 | \\TEST_EXPORT uint8_t a(struct S * s); | 113 | \\TEST_EXPORT uint8_t a(struct S * s); |
| ... | @@ -101,7 +123,6 @@ pub fn addCases(cases: *tests.GenHContext) void { | ... | @@ -101,7 +123,6 @@ pub fn addCases(cases: *tests.GenHContext) void { |
| 101 | \\export fn a(s: *U) u8 { | 123 | \\export fn a(s: *U) u8 { |
| 102 | \\ return s.A; | 124 | \\ return s.A; |
| 103 | \\} | 125 | \\} |
| 104 | |||
| 105 | , | 126 | , |
| 106 | \\union U; | 127 | \\union U; |
| 107 | \\TEST_EXPORT uint8_t a(union U * s); | 128 | \\TEST_EXPORT uint8_t a(union U * s); |
| ... | @@ -117,7 +138,6 @@ pub fn addCases(cases: *tests.GenHContext) void { | ... | @@ -117,7 +138,6 @@ pub fn addCases(cases: *tests.GenHContext) void { |
| 117 | \\export fn a(s: *E) u8 { | 138 | \\export fn a(s: *E) u8 { |
| 118 | \\ return @enumToInt(s.*); | 139 | \\ return @enumToInt(s.*); |
| 119 | \\} | 140 | \\} |
| 120 | |||
| 121 | , | 141 | , |
| 122 | \\enum E; | 142 | \\enum E; |
| 123 | \\TEST_EXPORT uint8_t a(enum E * s); | 143 | \\TEST_EXPORT uint8_t a(enum E * s); |
test/stage1/c_abi/build.zig created+17| ... | @@ -0,0 +1,17 @@ | ||
| 1 | const Builder = @import("std").build.Builder; | ||
| 2 | |||
| 3 | pub 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 | |||
| 5 | void zig_panic(); | ||
| 6 | |||
| 7 | static void assert_or_panic(bool ok) { | ||
| 8 | if (!ok) { | ||
| 9 | zig_panic(); | ||
| 10 | } | ||
| 11 | } | ||
| 12 | |||
| 13 | void zig_u8(uint8_t); | ||
| 14 | void zig_u16(uint16_t); | ||
| 15 | void zig_u32(uint32_t); | ||
| 16 | void zig_u64(uint64_t); | ||
| 17 | void zig_i8(int8_t); | ||
| 18 | void zig_i16(int16_t); | ||
| 19 | void zig_i32(int32_t); | ||
| 20 | void zig_i64(int64_t); | ||
| 21 | |||
| 22 | void 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 | |||
| 34 | void c_u8(uint8_t x) { | ||
| 35 | assert_or_panic(x == 0xff); | ||
| 36 | } | ||
| 37 | |||
| 38 | void c_u16(uint16_t x) { | ||
| 39 | assert_or_panic(x == 0xfffe); | ||
| 40 | } | ||
| 41 | |||
| 42 | void c_u32(uint32_t x) { | ||
| 43 | assert_or_panic(x == 0xfffffffd); | ||
| 44 | } | ||
| 45 | |||
| 46 | void c_u64(uint64_t x) { | ||
| 47 | assert_or_panic(x == 0xfffffffffffffffcULL); | ||
| 48 | } | ||
| 49 | |||
| 50 | void c_i8(int8_t x) { | ||
| 51 | assert_or_panic(x == -1); | ||
| 52 | } | ||
| 53 | |||
| 54 | void c_i16(int16_t x) { | ||
| 55 | assert_or_panic(x == -2); | ||
| 56 | } | ||
| 57 | |||
| 58 | void c_i32(int32_t x) { | ||
| 59 | assert_or_panic(x == -3); | ||
| 60 | } | ||
| 61 | |||
| 62 | void c_i64(int64_t x) { | ||
| 63 | assert_or_panic(x == -4); | ||
| 64 | } | ||
test/stage1/c_abi/main.zig created+58| ... | @@ -0,0 +1,58 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const assertOrPanic = std.debug.assertOrPanic; | ||
| 3 | |||
| 4 | extern fn run_c_tests() void; | ||
| 5 | |||
| 6 | export fn zig_panic() noreturn { | ||
| 7 | @panic("zig_panic called from C"); | ||
| 8 | } | ||
| 9 | |||
| 10 | test "C importing Zig ABI Tests" { | ||
| 11 | run_c_tests(); | ||
| 12 | } | ||
| 13 | |||
| 14 | extern fn c_u8(u8) void; | ||
| 15 | extern fn c_u16(u16) void; | ||
| 16 | extern fn c_u32(u32) void; | ||
| 17 | extern fn c_u64(u64) void; | ||
| 18 | extern fn c_i8(i8) void; | ||
| 19 | extern fn c_i16(i16) void; | ||
| 20 | extern fn c_i32(i32) void; | ||
| 21 | extern fn c_i64(i64) void; | ||
| 22 | |||
| 23 | test "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 | |||
| 35 | export fn zig_u8(x: u8) void { | ||
| 36 | assertOrPanic(x == 0xff); | ||
| 37 | } | ||
| 38 | export fn zig_u16(x: u16) void { | ||
| 39 | assertOrPanic(x == 0xfffe); | ||
| 40 | } | ||
| 41 | export fn zig_u32(x: u32) void { | ||
| 42 | assertOrPanic(x == 0xfffffffd); | ||
| 43 | } | ||
| 44 | export fn zig_u64(x: u64) void { | ||
| 45 | assertOrPanic(x == 0xfffffffffffffffc); | ||
| 46 | } | ||
| 47 | export fn zig_i8(x: i8) void { | ||
| 48 | assertOrPanic(x == -1); | ||
| 49 | } | ||
| 50 | export fn zig_i16(x: i16) void { | ||
| 51 | assertOrPanic(x == -2); | ||
| 52 | } | ||
| 53 | export fn zig_i32(x: i32) void { | ||
| 54 | assertOrPanic(x == -3); | ||
| 55 | } | ||
| 56 | export fn zig_i64(x: i64) void { | ||
| 57 | assertOrPanic(x == -4); | ||
| 58 | } | ||