diff --git a/src/compiler/c_abi_internal.h b/src/compiler/c_abi_internal.h index e3ea16ffd..8baf59bfa 100644 --- a/src/compiler/c_abi_internal.h +++ b/src/compiler/c_abi_internal.h @@ -19,10 +19,11 @@ ABIArgInfo *abi_arg_new_direct_by_reg(bool by_reg); ABIArgInfo *abi_arg_new_expand(void); ABIArgInfo *abi_arg_new_direct_int_ext(Type *type_to_extend); ABIArgInfo *abi_arg_new_direct_int_ext_by_reg(Type *int_to_extend, bool by_reg); +ABIArgInfo *abi_arg_new_direct_coerce_int_ext_by_reg(Type *int_to_extend, bool by_reg); +ABIArgInfo *abi_arg_new_direct_coerce_int_ext(Type *int_to_extend); ABIArgInfo *abi_arg_new_direct_coerce_bits(BitSize bits); ABIArgInfo *abi_arg_new_direct_coerce_type(Type *type); -ABIArgInfo *abi_arg_new_direct_coerce_array_type(Type *type, int8_t elements); -ABIArgInfo *abi_arg_new_direct_coerce_to_struct_with_elements(Type *type, int8_t elements); +ABIArgInfo *abi_arg_new_direct_struct_expand(Type *type, int8_t elements); ABIArgInfo *abi_arg_new_direct_coerce(AbiType type); ABIArgInfo *abi_arg_new_expand_coerce(AbiType target_type, unsigned offset); ABIArgInfo *abi_arg_new_expand_coerce_pair(AbiType first_element, unsigned initial_offset, AbiType second_element, unsigned padding, bool is_packed); diff --git a/src/compiler/codegen_internal.h b/src/compiler/codegen_internal.h index 8f35eddca..6643dd032 100644 --- a/src/compiler/codegen_internal.h +++ b/src/compiler/codegen_internal.h @@ -32,10 +32,6 @@ static inline bool abi_type_is_valid(AbiType type) return type.int_bits_plus_1 != 0; } -static inline bool abi_info_should_flatten(ABIArgInfo *info) -{ - return info->kind == ABI_ARG_DIRECT_COERCE && info->direct_coerce.elements > 1U && !info->direct_coerce.prevent_flatten; -} static inline bool abi_type_is_promotable_integer_or_bool(AbiType type) { diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 57f4639b6..7ff878d15 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -1480,6 +1480,7 @@ typedef enum ABI_ARG_DIRECT, ABI_ARG_DIRECT_PAIR, ABI_ARG_DIRECT_COERCE, + ABI_ARG_DIRECT_SPLIT_STRUCT, ABI_ARG_EXPAND_COERCE, ABI_ARG_INDIRECT, ABI_ARG_EXPAND, @@ -1532,16 +1533,12 @@ typedef struct ABIArgInfo_ AbiType lo; AbiType hi; } coerce_expand; + AbiType direct_coerce_type; struct { - AbiType partial_type; - }; - struct - { - AbiType type; - uint8_t elements : 7; - bool prevent_flatten : 1; - } direct_coerce; + Type *type; + uint8_t elements; + } direct_struct_expand; struct { // We may request a certain alignment of the parameters. @@ -1581,7 +1578,7 @@ extern TypeInfo *poisoned_type_info; extern Type *type_bool, *type_void, *type_voidptr; -extern Type *type_half, *type_float, *type_double, *type_quad; +extern Type *type_half, *type_float, *type_double, *type_f128; extern Type *type_ichar, *type_short, *type_int, *type_long, *type_isize; extern Type *type_char, *type_ushort, *type_uint, *type_ulong, *type_usize; extern Type *type_iptr, *type_uptr, *type_iptrdiff, *type_uptrdiff; diff --git a/src/compiler/llvm_codegen_c_abi.c b/src/compiler/llvm_codegen_c_abi.c index 69a96fa8c..814466a77 100644 --- a/src/compiler/llvm_codegen_c_abi.c +++ b/src/compiler/llvm_codegen_c_abi.c @@ -49,6 +49,7 @@ bool abi_arg_is_indirect(ABIArgInfo *info) case ABI_ARG_IGNORE: case ABI_ARG_DIRECT: case ABI_ARG_DIRECT_COERCE: + case ABI_ARG_DIRECT_SPLIT_STRUCT: case ABI_ARG_EXPAND: case ABI_ARG_DIRECT_PAIR: case ABI_ARG_EXPAND_COERCE: @@ -96,6 +97,25 @@ ABIArgInfo *abi_arg_new_direct_int_ext(Type *int_to_extend) return abi_arg_new_direct_int_ext_by_reg(int_to_extend, false); } +ABIArgInfo *abi_arg_new_direct_coerce_int_ext(Type *int_to_extend) +{ + return abi_arg_new_direct_coerce_int_ext_by_reg(int_to_extend, false); +} + +ABIArgInfo *abi_arg_new_direct_coerce_int_ext_by_reg(Type *int_to_extend, bool by_reg) +{ + ABIArgInfo *info = abi_arg_new_direct_coerce_type(int_to_extend); + if (type_is_signed(int_to_extend)) + { + info->attributes.signext = true; + } + else + { + info->attributes.zeroext = true; + } + info->attributes.by_reg = by_reg; + return info; +} ABIArgInfo *abi_arg_new_direct_int_ext_by_reg(Type *int_to_extend, bool by_reg) { @@ -166,9 +186,9 @@ ABIArgInfo *abi_arg_new_expand_coerce_pair(AbiType first_element, unsigned initi ABIArgInfo *abi_arg_new_direct_coerce_bits(BitSize bits) { + assert(bits >= 8); ABIArgInfo *info = abi_arg_new(ABI_ARG_DIRECT_COERCE); - abi_type_set_int_bits(&info->direct_coerce.type, bits); - info->direct_coerce.elements = 0; + abi_type_set_int_bits(&info->direct_coerce_type, bits); return info; } @@ -176,30 +196,23 @@ ABIArgInfo *abi_arg_new_direct_coerce(AbiType type) { assert(abi_type_is_valid(type)); ABIArgInfo *info = abi_arg_new(ABI_ARG_DIRECT_COERCE); - info->direct_coerce.type = type; - info->direct_coerce.elements = 0; + info->direct_coerce_type = type; return info; } ABIArgInfo *abi_arg_new_direct_coerce_type(Type *type) { + assert(type); ABIArgInfo *info = abi_arg_new(ABI_ARG_DIRECT_COERCE); - abi_type_set_type(&info->direct_coerce.type, type); - info->direct_coerce.elements = 0; + abi_type_set_type(&info->direct_coerce_type, type); return info; } -ABIArgInfo *abi_arg_new_direct_coerce_to_struct_with_elements(Type *type, int8_t elements) +ABIArgInfo *abi_arg_new_direct_struct_expand(Type *type, int8_t elements) { - TODO -} - -ABIArgInfo *abi_arg_new_direct_coerce_array_type(Type *type, int8_t elements) -{ - assert(elements > 0); - ABIArgInfo *info = abi_arg_new(ABI_ARG_DIRECT_COERCE); - abi_type_set_type(&info->direct_coerce.type, type); - info->direct_coerce.elements = elements; + ABIArgInfo *info = abi_arg_new(ABI_ARG_DIRECT_SPLIT_STRUCT); + info->direct_struct_expand.type = type; + info->direct_struct_expand.elements = elements; return info; } diff --git a/src/compiler/llvm_codegen_c_abi_riscv.c b/src/compiler/llvm_codegen_c_abi_riscv.c index ff8ebf3de..9dae3601e 100644 --- a/src/compiler/llvm_codegen_c_abi_riscv.c +++ b/src/compiler/llvm_codegen_c_abi_riscv.c @@ -207,7 +207,11 @@ static ABIArgInfo *riscv_classify_argument_type(Type *type, bool is_fixed, unsig // stack. if (size < xlen && type_is_integer(type) && !must_use_stack) { - return abi_arg_new_expand_padded(type_int_unsigned_by_bitsize(xlen * 8)); + if (xlen == 8 || type_is_promotable_integer(type)) + { + return abi_arg_new_direct_int_ext(type); + } + return abi_arg_new_direct(); } if (size > 16 || (size > 8 && !platform_target.int128)) { @@ -231,7 +235,7 @@ static ABIArgInfo *riscv_classify_argument_type(Type *type, bool is_fixed, unsig return abi_arg_new_direct_coerce_type(type_int_unsigned_by_bitsize(xlen * 16)); } Type *ret_type = type_int_unsigned_by_bitsize(xlen * 8); - return abi_arg_new_direct_coerce_array_type(ret_type, 2); + return abi_arg_new_direct_coerce_type(type_get_array(ret_type, 2)); } return abi_arg_new_indirect_not_by_val(type); } @@ -255,7 +259,7 @@ void c_abi_func_create_riscv(FunctionPrototype *prototype) unsigned fpr = 8; Type *ret_type = type_lowering(prototype->abi_ret_type); - ABIArgInfo *ret_abi = prototype->ret_abi_info = riscv_classify_return(prototype->abi_ret_type); + ABIArgInfo *ret_abi = prototype->ret_abi_info = riscv_classify_return(ret_type); // IsRetIndirect is true if classifyArgumentType indicated the value should // be passed indirect, or if the type size is a scalar greater than 2*XLen @@ -279,8 +283,8 @@ void c_abi_func_create_riscv(FunctionPrototype *prototype) // If we have a failable, then the return type is a parameter. if (prototype->ret_by_ref) { - prototype->ret_by_ref = riscv_classify_argument_type(type_get_ptr(prototype->ret_by_ref_type), - true, &arg_gprs_left, &arg_fprs_left); + prototype->ret_by_ref_abi_info = riscv_classify_argument_type(type_get_ptr(prototype->ret_by_ref_type), + true, &arg_gprs_left, &arg_fprs_left); } Type **params = prototype->params; diff --git a/src/compiler/llvm_codegen_c_abi_x64.c b/src/compiler/llvm_codegen_c_abi_x64.c index cbf634e80..dc97e49f8 100644 --- a/src/compiler/llvm_codegen_c_abi_x64.c +++ b/src/compiler/llvm_codegen_c_abi_x64.c @@ -691,7 +691,7 @@ ABIArgInfo *x64_classify_return(Type *return_type) result_type = x64_get_int_type_at_offset(return_type, 0, return_type, 0); if (hi_class == CLASS_NO_CLASS && abi_type_is_promotable_integer_or_bool(result_type)) { - return abi_arg_new_direct_int_ext(return_type); + return abi_arg_new_direct_coerce_int_ext(return_type); } break; case CLASS_SSE: @@ -776,10 +776,10 @@ static ABIArgInfo *x64_classify_argument_type(Type *type, unsigned free_int_regs case CLASS_INTEGER: needed_registers->int_registers++; result_type = x64_get_int_type_at_offset(type, 0, type, 0); - if (hi_class == CLASS_NO_CLASS && abi_type_is_promotable_integer_or_bool(result_type)) + if (hi_class == CLASS_NO_CLASS && type_is_promotable_integer(type)) { assert(abi_type_is_type(result_type)); - return abi_arg_new_direct_int_ext(result_type.type); + return abi_arg_new_direct_coerce_int_ext(result_type.type); } break; case CLASS_SSE: diff --git a/src/compiler/llvm_codegen_c_abi_x86.c b/src/compiler/llvm_codegen_c_abi_x86.c index 985768e5e..115a51e48 100644 --- a/src/compiler/llvm_codegen_c_abi_x86.c +++ b/src/compiler/llvm_codegen_c_abi_x86.c @@ -494,7 +494,7 @@ static inline ABIArgInfo *x86_classify_aggregate(CallABI call, Regs *regs, Type ABIArgInfo *info; if (size_in_regs > 1) { - info = abi_arg_new_direct_coerce_to_struct_with_elements(type_uint, (int8_t)size_in_regs); + info = abi_arg_new_direct_struct_expand(type_uint, (int8_t)size_in_regs); } else { diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 4f8f2a0b6..d788bfbdb 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -4131,25 +4131,16 @@ void llvm_emit_parameter(GenContext *c, LLVMValueRef **args, ABIArgInfo *info, B case ABI_ARG_DIRECT: vec_add(*args, llvm_load_value_store(c, be_value)); return; - case ABI_ARG_DIRECT_COERCE: + case ABI_ARG_DIRECT_SPLIT_STRUCT: { LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info); - if (!coerce_type || coerce_type == llvm_get_type(c, type)) - { - vec_add(*args, llvm_load_value_store(c, be_value)); - return; - } - if (!abi_info_should_flatten(info)) - { - vec_add(*args, llvm_emit_coerce(c, coerce_type, be_value, type)); - return; - } + assert(coerce_type && coerce_type != llvm_get_type(c, type)); AlignSize target_alignment = llvm_abi_alignment(c, coerce_type); AlignSize alignment; LLVMValueRef cast = llvm_emit_coerce_alignment(c, be_value, coerce_type, target_alignment, &alignment); - LLVMTypeRef element = llvm_abi_type(c, info->direct_coerce.type); - for (unsigned idx = 0; idx < info->direct_coerce.elements; idx++) + LLVMTypeRef element = llvm_get_type(c, info->direct_struct_expand.type); + for (unsigned idx = 0; idx < info->direct_struct_expand.elements; idx++) { AlignSize load_align; LLVMValueRef element_ptr = llvm_emit_struct_gep_raw(c, cast, coerce_type, idx, alignment, &load_align); @@ -4157,6 +4148,17 @@ void llvm_emit_parameter(GenContext *c, LLVMValueRef **args, ABIArgInfo *info, B } return; } + case ABI_ARG_DIRECT_COERCE: + { + LLVMTypeRef coerce_type = llvm_abi_type(c, info->direct_coerce_type); + if (coerce_type == llvm_get_type(c, type)) + { + vec_add(*args, llvm_load_value_store(c, be_value)); + return; + } + vec_add(*args, llvm_emit_coerce(c, coerce_type, be_value, type)); + return; + } case ABI_ARG_DIRECT_PAIR: { llvm_value_addr(c, be_value); @@ -4461,6 +4463,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) vec_add(values, result_value->value); break; case ABI_ARG_EXPAND: + case ABI_ARG_DIRECT_SPLIT_STRUCT: UNREACHABLE case ABI_ARG_DIRECT_PAIR: case ABI_ARG_IGNORE: @@ -4609,6 +4612,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) switch (ret_info->kind) { case ABI_ARG_EXPAND: + case ABI_ARG_DIRECT_SPLIT_STRUCT: UNREACHABLE case ABI_ARG_IGNORE: // 12. Basically void returns or empty structs. @@ -4698,7 +4702,7 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) // 16. A direct coerce, this is basically "call result" bitcast return type. // 16a. Get the type of the return. - LLVMTypeRef coerce = llvm_get_coerce_type(c, ret_info); + LLVMTypeRef coerce = llvm_abi_type(c, ret_info->direct_coerce_type); // 16b. If we don't have any coerce type, or the actual LLVM types are the same, we're done. if (!coerce || coerce == llvm_get_type(c, call_return_type)) @@ -4708,7 +4712,6 @@ void llvm_emit_call_expr(GenContext *c, BEValue *result_value, Expr *expr) break; } // 16c. We use a normal bitcast coerce. - assert(!abi_info_should_flatten(ret_info) && "Did not expect flattening on return types."); llvm_emit_convert_value_from_coerced(c, result_value, coerce, call_value, call_return_type); break; } diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index cf2f0ba74..7663670de 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -186,33 +186,20 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, ABIAr llvm_emit_and_set_decl_alloca(c, decl); llvm_store_decl_raw(c, decl, llvm_get_next_param(c, index)); return; - case ABI_ARG_DIRECT_COERCE: + case ABI_ARG_DIRECT_SPLIT_STRUCT: { LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info); - if (!coerce_type || coerce_type == llvm_get_type(c, decl->type)) - { - goto DIRECT_FROM_COERCE; - } llvm_emit_and_set_decl_alloca(c, decl); - // If we're not flattening, we simply do a store. - if (!abi_info_should_flatten(info)) - { - LLVMValueRef param = llvm_get_next_param(c, index); - // Store it with the alignment of the decl. - llvm_emit_coerce_store(c, decl->backend_ref, decl->alignment, coerce_type, param, llvm_get_type(c, decl->type)); - return; - } - // In this case we've been flattening the parameter into multiple registers. - LLVMTypeRef element_type = llvm_abi_type(c, info->direct_coerce.type); + LLVMTypeRef element_type = llvm_get_type(c, info->direct_struct_expand.type); // Cast to the coerce type. LLVMValueRef cast = LLVMBuildBitCast(c->builder, decl->backend_ref, LLVMPointerType(coerce_type, 0), "coerce"); AlignSize decl_alignment = decl->alignment; // Store each expanded parameter. - for (unsigned idx = 0; idx < info->direct_coerce.elements; idx++) + for (unsigned idx = 0; idx < info->direct_struct_expand.elements; idx++) { AlignSize align; LLVMValueRef element_ptr = llvm_emit_struct_gep_raw(c, cast, coerce_type, idx, decl_alignment, &align); @@ -221,6 +208,20 @@ static inline void llvm_process_parameter_value(GenContext *c, Decl *decl, ABIAr } return; } + case ABI_ARG_DIRECT_COERCE: + { + LLVMTypeRef coerce_type = llvm_abi_type(c, info->direct_coerce_type); + if (coerce_type == llvm_get_type(c, decl->type)) + { + goto DIRECT_FROM_COERCE; + } + llvm_emit_and_set_decl_alloca(c, decl); + + LLVMValueRef param = llvm_get_next_param(c, index); + // Store it with the alignment of the decl. + llvm_emit_coerce_store(c, decl->backend_ref, decl->alignment, coerce_type, param, llvm_get_type(c, decl->type)); + return; + } case ABI_ARG_EXPAND: { llvm_emit_and_set_decl_alloca(c, decl); @@ -296,6 +297,7 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl case ABI_ARG_IGNORE: llvm_emit_return_value(c, NULL); return; + case ABI_ARG_DIRECT_SPLIT_STRUCT: case ABI_ARG_EXPAND: // Expands to multiple slots - // Not applicable to return values. @@ -340,20 +342,21 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl break; } case ABI_ARG_DIRECT: +DIRECT_RETURN: // The normal return llvm_emit_return_value(c, llvm_load_value_store(c, return_value)); return; case ABI_ARG_DIRECT_PAIR: - case ABI_ARG_DIRECT_COERCE: { LLVMTypeRef coerce_type = llvm_get_coerce_type(c, info); - if (!coerce_type || coerce_type == llvm_get_type(c, call_return_type)) - { - // The normal return - llvm_emit_return_value(c, llvm_load_value_store(c, return_value)); - return; - } - assert(!abi_info_should_flatten(info)); + if (coerce_type == llvm_get_type(c, call_return_type)) goto DIRECT_RETURN; + llvm_emit_return_value(c, llvm_emit_coerce(c, coerce_type, return_value, call_return_type)); + return; + } + case ABI_ARG_DIRECT_COERCE: + { + LLVMTypeRef coerce_type = llvm_abi_type(c, info->direct_coerce_type); + if (coerce_type == llvm_get_type(c, call_return_type)) goto DIRECT_RETURN; llvm_emit_return_value(c, llvm_emit_coerce(c, coerce_type, return_value, call_return_type)); return; } @@ -491,7 +494,8 @@ void llvm_emit_function_body(GenContext *context, Decl *decl) static void llvm_emit_param_attributes(GenContext *c, LLVMValueRef function, ABIArgInfo *info, bool is_return, int index, int last_index) { assert(last_index == index || info->kind == ABI_ARG_DIRECT_PAIR || info->kind == ABI_ARG_IGNORE - || info->kind == ABI_ARG_EXPAND || info->kind == ABI_ARG_DIRECT || info->kind == ABI_ARG_DIRECT_COERCE); + || info->kind == ABI_ARG_EXPAND || info->kind == ABI_ARG_DIRECT || info->kind == ABI_ARG_DIRECT_COERCE + || info->kind == ABI_ARG_DIRECT_SPLIT_STRUCT); if (info->attributes.zeroext) { @@ -513,6 +517,7 @@ static void llvm_emit_param_attributes(GenContext *c, LLVMValueRef function, ABI { case ABI_ARG_EXPAND: case ABI_ARG_IGNORE: + case ABI_ARG_DIRECT_SPLIT_STRUCT: case ABI_ARG_DIRECT_COERCE: case ABI_ARG_DIRECT_PAIR: case ABI_ARG_DIRECT: diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index cdb04bd29..e5b34bcb5 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -188,24 +188,22 @@ static inline void add_func_type_param(GenContext *context, Type *param_type, AB case ABI_ARG_DIRECT: vec_add(*params, llvm_get_type(context, param_type)); break; + case ABI_ARG_DIRECT_SPLIT_STRUCT: + { + // Normal direct. + LLVMTypeRef coerce_type = llvm_get_type(context, arg_info->direct_struct_expand.type); + for (unsigned idx = 0; idx < arg_info->direct_struct_expand.elements; idx++) + { + vec_add(*params, coerce_type); + } + break; + } case ABI_ARG_DIRECT_COERCE: { // Normal direct. - if (!abi_type_is_valid(arg_info->direct_coerce.type)) - { - vec_add(*params, llvm_get_type(context, param_type)); - break; - } - LLVMTypeRef coerce_type = llvm_abi_type(context, arg_info->direct_coerce.type); - if (!abi_info_should_flatten(arg_info)) - { - vec_add(*params, coerce_type); - break; - } - for (unsigned idx = 0; idx < arg_info->direct_coerce.elements; idx++) - { + assert(abi_type_is_valid(arg_info->direct_coerce_type)); + LLVMTypeRef coerce_type = llvm_abi_type(context, arg_info->direct_coerce_type); vec_add(*params, coerce_type); - } break; } case ABI_ARG_DIRECT_PAIR: @@ -262,9 +260,10 @@ LLVMTypeRef llvm_func_type(GenContext *context, FunctionPrototype *prototype) case ABI_ARG_DIRECT: return_type = llvm_get_type(context, call_return_type); break; + case ABI_ARG_DIRECT_SPLIT_STRUCT: + UNREACHABLE case ABI_ARG_DIRECT_COERCE: - assert(!abi_info_should_flatten(ret_arg_info)); - return_type = llvm_get_coerce_type(context, ret_arg_info); + return_type = llvm_abi_type(context, ret_arg_info->direct_coerce_type); if (!return_type) return_type = llvm_get_type(context, call_return_type); break; } @@ -376,50 +375,54 @@ LLVMTypeRef llvm_get_type(GenContext *c, Type *any_type) LLVMTypeRef llvm_get_coerce_type(GenContext *c, ABIArgInfo *arg_info) { - if (arg_info->kind == ABI_ARG_EXPAND_COERCE) + switch (arg_info->kind) { - unsigned element_index = 0; - LLVMTypeRef elements[4]; - // Add optional padding to make the data appear at the correct offset. - if (arg_info->coerce_expand.offset_lo) + case ABI_ARG_EXPAND_COERCE: { - elements[element_index++] = llvm_const_padding_type(c, arg_info->coerce_expand.offset_lo); + unsigned element_index = 0; + LLVMTypeRef elements[4]; + // Add optional padding to make the data appear at the correct offset. + if (arg_info->coerce_expand.offset_lo) + { + elements[element_index++] = llvm_const_padding_type(c, arg_info->coerce_expand.offset_lo); + } + elements[element_index++] = llvm_abi_type(c, arg_info->coerce_expand.lo); + // Add optional padding to make the high field appear at the correct off. + if (arg_info->coerce_expand.padding_hi) + { + elements[element_index++] = LLVMArrayType(llvm_get_type(c, type_char), arg_info->coerce_expand.padding_hi); + } + // Check if there is a top type as well. + if (abi_type_is_valid(arg_info->coerce_expand.hi)) + { + elements[element_index++] = llvm_abi_type(c, arg_info->coerce_expand.hi); + } + return LLVMStructType(elements, element_index, arg_info->coerce_expand.packed); } - elements[element_index++] = llvm_abi_type(c, arg_info->coerce_expand.lo); - // Add optional padding to make the high field appear at the correct off. - if (arg_info->coerce_expand.padding_hi) + case ABI_ARG_DIRECT_SPLIT_STRUCT: { - elements[element_index++] = LLVMArrayType(llvm_get_type(c, type_char), arg_info->coerce_expand.padding_hi); + LLVMTypeRef coerce_type = llvm_get_type(c, arg_info->direct_struct_expand.type); + assert(arg_info->direct_struct_expand.elements > 1U); + LLVMTypeRef *refs = MALLOC(sizeof(LLVMValueRef) * arg_info->direct_struct_expand.elements); + for (unsigned i = 0; i < arg_info->direct_struct_expand.elements; i++) + { + refs[i] = coerce_type; + } + return LLVMStructTypeInContext(c->context, refs, arg_info->direct_struct_expand.elements, false); } - // Check if there is a top type as well. - if (abi_type_is_valid(arg_info->coerce_expand.hi)) + case ABI_ARG_DIRECT_PAIR: { - elements[element_index++] = llvm_abi_type(c, arg_info->coerce_expand.hi); + LLVMTypeRef lo = llvm_abi_type(c, arg_info->direct_pair.lo); + LLVMTypeRef hi = llvm_abi_type(c, arg_info->direct_pair.hi); + return llvm_get_twostruct(c, lo, hi); } - return LLVMStructType(elements, element_index, arg_info->coerce_expand.packed); + case ABI_ARG_IGNORE: + case ABI_ARG_DIRECT: + case ABI_ARG_DIRECT_COERCE: + case ABI_ARG_INDIRECT: + case ABI_ARG_EXPAND: + UNREACHABLE } - - if (arg_info->kind == ABI_ARG_DIRECT_COERCE) - { - assert(abi_type_is_valid(arg_info->direct_coerce.type)); - if (!abi_type_is_valid(arg_info->direct_coerce.type)) return NULL; - LLVMTypeRef coerce_type = llvm_abi_type(c, arg_info->direct_coerce.type); - if (arg_info->direct_coerce.elements < 2U) return coerce_type; - LLVMTypeRef *refs = MALLOC(sizeof(LLVMValueRef) * arg_info->direct_coerce.elements); - for (unsigned i = 0; i < arg_info->direct_coerce.elements; i++) - { - refs[i] = coerce_type; - } - return LLVMStructTypeInContext(c->context, refs, arg_info->direct_coerce.elements, false); - } - if (arg_info->kind == ABI_ARG_DIRECT_PAIR) - { - LLVMTypeRef lo = llvm_abi_type(c, arg_info->direct_pair.lo); - LLVMTypeRef hi = llvm_abi_type(c, arg_info->direct_pair.hi); - return llvm_get_twostruct(c, lo, hi); - } - - assert(arg_info->kind != ABI_ARG_DIRECT); UNREACHABLE } diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 5a955f19e..79da5f3d9 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -1337,7 +1337,7 @@ static Expr *parse_double(ParseContext *context, Expr *left) switch (number->const_expr.fxx.type) { case TYPE_F128: - number->type = type_quad; + number->type = type_f128; break; case TYPE_F64: number->type = type_double; diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 4840db858..1104c6d05 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -1118,6 +1118,7 @@ bool parse_parameters(ParseContext *context, Visibility visibility, Decl ***para ASSIGN_EXPR_ELSE(param->var.init_expr, parse_initializer(context), false); } } + if (!parse_attributes(context, ¶m->attributes)) return false; var_arg_found |= ellipsis; param->var.vararg = ellipsis; vec_add(params, param); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 62963f893..8c0ec88d9 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -6,7 +6,7 @@ static bool sema_analyse_struct_union(SemaContext *context, Decl *decl); - +static bool sema_analyse_attributes_for_var(SemaContext *context, Decl *decl); static bool sema_check_section(SemaContext *context, Decl *decl, Attr *attr) { const char *section_string = attr->expr->const_expr.string.chars; @@ -616,6 +616,8 @@ static inline bool sema_analyse_function_param(SemaContext *context, Decl *param SEMA_ERROR(param, "Only regular parameters are allowed for functions."); return false; } + + if (!sema_analyse_attributes_for_var(context, param)) return false; if (!param->var.type_info) { SEMA_ERROR(param, "Only typed parameters are allowed for functions."); @@ -1677,7 +1679,7 @@ static inline bool sema_analyse_macro(SemaContext *context, Decl *decl) } -bool sema_analyse_attributes_for_var(SemaContext *context, Decl *decl) +static bool sema_analyse_attributes_for_var(SemaContext *context, Decl *decl) { AttributeDomain domain; switch (decl->var.kind) diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 32e96cbd8..82a9ff5d8 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -1047,7 +1047,7 @@ static inline bool sema_expr_analyse_binary_subexpr(SemaContext *context, Expr * return (int)sema_analyse_expr(context, left) & (int)sema_analyse_expr(context, right); } -static inline bool sema_expr_analyse_binary_arithmetic_subexpr(SemaContext *context, Expr *expr, const char *error) +static inline bool sema_expr_analyse_binary_arithmetic_subexpr(SemaContext *context, Expr *expr, const char *error, bool bool_is_allowed) { Expr *left = expr->binary_expr.left; Expr *right = expr->binary_expr.right; @@ -1060,6 +1060,7 @@ static inline bool sema_expr_analyse_binary_arithmetic_subexpr(SemaContext *cont Type *left_type = type_no_fail(left->type)->canonical; Type *right_type = type_no_fail(right->type)->canonical; + if (bool_is_allowed && left_type == type_bool && right_type == type_bool) return true; // 2. Perform promotion to a common type. return binary_arithmetic_promotion(context, left, right, left_type, right_type, expr, error); } @@ -4754,7 +4755,7 @@ static bool sema_expr_analyse_mult(SemaContext *context, Expr *expr, Expr *left, { // 1. Analyse the sub expressions and promote to a common type - if (!sema_expr_analyse_binary_arithmetic_subexpr(context, expr, "It is not possible to multiply %s by %s.")) return false; + if (!sema_expr_analyse_binary_arithmetic_subexpr(context, expr, "It is not possible to multiply %s by %s.", false)) return false; // 2. Handle constant folding. @@ -4787,7 +4788,7 @@ static bool sema_expr_analyse_mult(SemaContext *context, Expr *expr, Expr *left, static bool sema_expr_analyse_div(SemaContext *context, Expr *expr, Expr *left, Expr *right) { // 1. Analyse sub expressions and promote to a common type - if (!sema_expr_analyse_binary_arithmetic_subexpr(context, expr, "Cannot divide %s by %s.")) return false; + if (!sema_expr_analyse_binary_arithmetic_subexpr(context, expr, "Cannot divide %s by %s.", false)) return false; // 2. Check for a constant 0 on the rhs. if (IS_CONST(right)) @@ -4839,7 +4840,7 @@ static bool sema_expr_analyse_div(SemaContext *context, Expr *expr, Expr *left, static bool sema_expr_analyse_mod(SemaContext *context, Expr *expr, Expr *left, Expr *right) { // 1. Analyse both sides and promote to a common type - if (!sema_expr_analyse_binary_arithmetic_subexpr(context, expr, NULL)) return false; + if (!sema_expr_analyse_binary_arithmetic_subexpr(context, expr, NULL, false)) return false; // 3. a % 0 is not valid, so detect it. if (IS_CONST(right) && int_is_zero(right->const_expr.ixx)) @@ -4866,11 +4867,13 @@ static bool sema_expr_analyse_mod(SemaContext *context, Expr *expr, Expr *left, */ static bool sema_expr_analyse_bit(SemaContext *context, Expr *expr, Expr *left, Expr *right) { - // 1. Convert to common type if possible. - if (!sema_expr_analyse_binary_arithmetic_subexpr(context, expr, NULL)) return false; - // 2. Check that both are integers. - if (!both_any_integer_or_integer_vector(left, right)) + // 1. Convert to common type if possible. + if (!sema_expr_analyse_binary_arithmetic_subexpr(context, expr, NULL, true)) return false; + + // 2. Check that both are integers or bools. + bool is_bool = left->type->canonical == type_bool; + if (!is_bool && !both_any_integer_or_integer_vector(left, right)) { return sema_type_error_on_binop(expr); } @@ -4880,19 +4883,39 @@ static bool sema_expr_analyse_bit(SemaContext *context, Expr *expr, Expr *left, { BinaryOp op = expr->binary_expr.operator; expr_replace(expr, left); - switch (op) + if (is_bool) { - case BINARYOP_BIT_AND: - expr->const_expr.ixx = int_and(left->const_expr.ixx, right->const_expr.ixx); - break; - case BINARYOP_BIT_XOR: - expr->const_expr.ixx = int_xor(left->const_expr.ixx, right->const_expr.ixx); - break; - case BINARYOP_BIT_OR: - expr->const_expr.ixx = int_or(left->const_expr.ixx, right->const_expr.ixx); - break; - default: - UNREACHABLE; + switch (op) + { + case BINARYOP_BIT_AND: + expr->const_expr.b = left->const_expr.b & right->const_expr.b; + break; + case BINARYOP_BIT_XOR: + expr->const_expr.b = left->const_expr.b ^ right->const_expr.b; + break; + case BINARYOP_BIT_OR: + expr->const_expr.b = left->const_expr.b | right->const_expr.b; + break; + default: + UNREACHABLE; + } + } + else + { + switch (op) + { + case BINARYOP_BIT_AND: + expr->const_expr.ixx = int_and(left->const_expr.ixx, right->const_expr.ixx); + break; + case BINARYOP_BIT_XOR: + expr->const_expr.ixx = int_xor(left->const_expr.ixx, right->const_expr.ixx); + break; + case BINARYOP_BIT_OR: + expr->const_expr.ixx = int_or(left->const_expr.ixx, right->const_expr.ixx); + break; + default: + UNREACHABLE; + } } } diff --git a/src/compiler/target.c b/src/compiler/target.c index 104126f78..1bcfde626 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -469,7 +469,7 @@ static char *arch_to_target_triple[ARCH_OS_TARGET_LAST + 1] = { [AARCH64_LINUX] = "aarch64-unknown-linux-gnu", [AARCH64_DARWIN] = "aarch64-apple-darwin", [RISCV32_LINUX] = "riscv32-unknown-linux", - [RISCV64_LINUX] = "riscv32-unknown-linux", + [RISCV64_LINUX] = "riscv64-unknown-linux", [WASM32] = "wasm32-unknown-unknown", [WASM64] = "wasm64-unknown-unknown", }; @@ -817,6 +817,8 @@ static unsigned os_target_supports_int128(OsType os, ArchType arch) { switch (arch) { + case ARCH_TYPE_RISCV64: + return true; case ARCH_TYPE_AARCH64: return true; case ARCH_TYPE_PPC: @@ -1328,10 +1330,10 @@ void target_setup(BuildTarget *target) break; case ARCH_TYPE_RISCV64: case ARCH_TYPE_RISCV32: - platform_target.riscv.xlen = 0; // pointer width - platform_target.riscv.flen = 32; // ends with f / d (64) + platform_target.riscv.xlen = arch_pointer_bit_width(platform_target.os, platform_target.arch) / 8; // pointer width + platform_target.riscv.flen = 0; // ends with f / d (64) platform_target.abi = ABI_RISCV; - TODO + break; case ARCH_TYPE_X86: target_setup_x86_abi(target); break; diff --git a/src/compiler/types.c b/src/compiler/types.c index 5fe42b9e2..14647e377 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -21,7 +21,7 @@ Type *type_voidptr = &t.voidstar; Type *type_half = &t.f16; Type *type_float = &t.f32; Type *type_double = &t.f64; -Type *type_quad = &t.f128; +Type *type_f128 = &t.f128; Type *type_typeid = &t.typeid; Type *type_any = &t.any; Type *type_typeinfo = &t.typeinfo; @@ -1306,6 +1306,8 @@ Type *type_from_token(TokenType type) return type_double; case TOKEN_FLOAT: return type_float; + case TOKEN_FLOAT128: + return type_f128; case TOKEN_INT128: return type_i128; case TOKEN_ICHAR: diff --git a/src/version.h b/src/version.h index 89634e7d8..11b9f7836 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "PRE.13" \ No newline at end of file +#define COMPILER_VERSION "PRE.14" \ No newline at end of file diff --git a/test/test_suite/abi/aarch64_args.c3t b/test/test_suite/abi/aarch64_args.c3t new file mode 100644 index 000000000..663d2eec5 --- /dev/null +++ b/test/test_suite/abi/aarch64_args.c3t @@ -0,0 +1,34 @@ +// #target: aarch64-darwin +module test; + +struct Large { + void*[8] pointers; +} + +extern fn void pass_large(Large); + +fn void example() { + Large l = {}; + pass_large(l); + pass_large(l); +} + +/* #expect: test.ll + +define void @test.example() #0 { +entry: + %l = alloca %Large, align 8 + %indirectarg = alloca %Large, align 8 + %indirectarg1 = alloca %Large, align 8 + %0 = bitcast %Large* %l to i8* + call void @llvm.memset.p0i8.i64(i8* align 8 %0, i8 0, i64 64, i1 false) + %1 = bitcast %Large* %indirectarg to i8* + %2 = bitcast %Large* %l to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %1, i8* align 8 %2, i32 64, i1 false) + call void @pass_large(%Large* align 8 %indirectarg) + %3 = bitcast %Large* %indirectarg1 to i8* + %4 = bitcast %Large* %l to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %3, i8* align 8 %4, i32 64, i1 false) + call void @pass_large(%Large* align 8 %indirectarg1) + ret void +} \ No newline at end of file diff --git a/test/test_suite/abi/riscv64-lp64-abi.c3t b/test/test_suite/abi/riscv64-lp64-abi.c3t new file mode 100644 index 000000000..8b976715d --- /dev/null +++ b/test/test_suite/abi/riscv64-lp64-abi.c3t @@ -0,0 +1,56 @@ +// #target: riscv64-linux +module test; + +struct Large { + long a, b, c, d; +} + +define V32i8 = char[<32>]; + +fn int f_scalar_stack_1(int a, int128 b, float c, float128 d, V32i8 e, + char f, char g, char h) { + return g + h; +} + +fn Large f_scalar_stack_2(double a, int128 b, float128 c, V32i8 d, + char e, ichar f, char g) { + return Large {(long)(a), e, f, g}; +} + +/* #expect: test.ll + +define signext i32 @test.f_scalar_stack_1(i32 signext %0, i128 %1, float %2, fp128 %3, <32 x i8>* align 32 %4, i8 zeroext %5, i8 %6, i8 %7) #0 { +entry: + %e = alloca <32 x i8>, align 32 + %8 = bitcast <32 x i8>* %e to i8* + %9 = bitcast <32 x i8>* %4 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 32 %8, i8* align 32 %9, i32 32, i1 false) + %uisiext = zext i8 %6 to i32 + %uisiext1 = zext i8 %7 to i32 + %add = add i32 %uisiext, %uisiext1 + ret i32 %add +} +define void @test.f_scalar_stack_2(%Large* noalias sret(%Large) align 8 %0, double %1, i128 %2, fp128 %3, <32 x i8>* align 32 %4, i8 zeroext %5, i8 %6, i8 %7) #0 { +entry: + %d = alloca <32 x i8>, align 32 + %literal = alloca %Large, align 8 + %8 = bitcast <32 x i8>* %d to i8* + %9 = bitcast <32 x i8>* %4 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 32 %8, i8* align 32 %9, i32 32, i1 false) + %10 = getelementptr inbounds %Large, %Large* %literal, i32 0, i32 0 + %fpui = fptoui double %1 to i64 + store i64 %fpui, i64* %10, align 8 + %11 = getelementptr inbounds %Large, %Large* %literal, i32 0, i32 1 + %uisiext = zext i8 %5 to i64 + store i64 %uisiext, i64* %11, align 8 + %12 = getelementptr inbounds %Large, %Large* %literal, i32 0, i32 2 + %sisiext = sext i8 %6 to i64 + store i64 %sisiext, i64* %12, align 8 + %13 = getelementptr inbounds %Large, %Large* %literal, i32 0, i32 3 + %uisiext1 = zext i8 %7 to i64 + store i64 %uisiext1, i64* %13, align 8 + %14 = bitcast %Large* %0 to i8* + %15 = bitcast %Large* %literal to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %14, i8* align 8 %15, i32 32, i1 false) + ret void +} diff --git a/test/test_suite/clang/2002-01_02.c3t b/test/test_suite/clang/2002-01_02.c3t new file mode 100644 index 000000000..134cb61e5 --- /dev/null +++ b/test/test_suite/clang/2002-01_02.c3t @@ -0,0 +1,369 @@ +// #target: x64-darwin +module test; + +struct Foo { int x, y; } +Foo[10] array; + +fn void foo() {} + +fn void *dlclose(void*); + +fn void ap_os_dso_unload(void *handle) +{ + dlclose(handle); + return; +} + +extern fn void foo2(int, double, float); + +fn void bar(int x) { + foo2(x, x ? 1.0 : 12.5, 1.0f); +} + +extern fn int tolower(int); + +fn char *rangematch(char *pattern, int teste, int c) { + + if ((c <= teste) | (tolower(c) <= tolower((char)(teste)))) return (char*)(0); + + return pattern; +} + +struct Foostruct { + short x; +} + +extern fn int foo3(Foostruct ic); + +fn void teste2() { + Foostruct xqic; + foo3(xqic); +} + +struct Vertex +{ + float a, b, c; +} +struct Edge_rec +{ + Vertex *v; + Edge_rec *next; + int wasseen; + int more_data; +} + +define QuadEdge = Edge_rec*; + +struct EdgePair { + QuadEdge left, right; +} + +struct EdgeStack { + int ptr; + QuadEdge *elts; + int stack_size; +} + +fn int do_merge(QuadEdge ldo, QuadEdge rdo) { + int lvalid; + QuadEdge basel; + QuadEdge rcand; + while (1) { + if (!lvalid) { + return (int)((iptr)(basel.next)); + } + } + return 1; +} + +fn int test(int x) { + return x; +} + +fn void abc(int *x); +fn int def(int y, int z) { + abc(&z); + return y; +} + +struct Test { short x, x2; int y, z; } + +fn int testing(Test *a) { + return a.x2+a.y; +} + +fn int test2(int x, Test a, int y) { + return x+y+a.x2+a.y; +} +fn int test3(Test a, Test b) { + return a.x2+a.y+b.y+b.z; +} + +fn Test test4(Test a) { + return a; +} + +fn int test6() { + int[200] b; + return b[4]; +} + +struct STest2 { int x; short[4] y; double z; } + +fn STest2 test7(STest2 x) { + return x; +} + +extern fn int printf(char * format, ...); + +fn int main2() { + long v = 123455678902i64; + printf("%lld\n", v); + return 1; +} + +double afoo = 17; +double abar = 12.0; +float axx = 12.0f; + +char*[*] procnames = { + "EXIT" +}; + +void *[*] data = { &afoo, &abar, &axx }; + +/* #expect: test.ll + +%Foo = type { i32, i32 } +%Foostruct = type { i16 } +%Edge_rec = type { %Vertex*, %Edge_rec*, i32, i32 } +%Vertex = type { float, float, float } +%Test = type { i16, i16, i32, i32 } +%STest2 = type { i32, [4 x i16], double } + +@test.array = local_unnamed_addr global [10 x %Foo] zeroinitializer, align 16 +@test.afoo = global double 1.700000e+01, align 8 +@test.abar = global double 1.200000e+01, align 8 +@test.axx = global float 1.200000e+01, align 4 +@.str = private unnamed_addr constant [5 x i8] c"EXIT\00", align 1 +@test.procnames = local_unnamed_addr global [1 x i8*] [i8* getelementptr inbounds ([5 x i8], [5 x i8]* @.str, i32 0, i32 0)], align 8 +@test.data = local_unnamed_addr global [3 x i8*] [i8* bitcast (double* @test.afoo to i8*), i8* bitcast (double* @test.abar to i8*), i8* bitcast (float* @test.axx to i8*)], align 16 +@.str.6 = private unnamed_addr constant [6 x i8] c"%lld\0A\00", align 1 + +define void @test.foo() #0 { +entry: + ret void +} + +declare i8* @test.dlclose(i8*) #0 + +define void @test.ap_os_dso_unload(i8* %0) #0 { +entry: + %1 = call i8* @test.dlclose(i8* %0) + ret void +} + +declare void @foo2(i32, double, float) #0 + +define void @test.bar(i32 %0) #0 { +entry: + %intbool = icmp ne i32 %0, 0 + br i1 %intbool, label %cond.lhs, label %cond.rhs + +cond.lhs: ; preds = %entry + br label %cond.phi + +cond.rhs: ; preds = %entry + br label %cond.phi + +cond.phi: ; preds = %cond.rhs, %cond.lhs + %val = phi double [ 1.000000e+00, %cond.lhs ], [ 1.250000e+01, %cond.rhs ] + call void @foo2(i32 %0, double %val, float 1.000000e+00) + ret void +} + +declare i32 @tolower(i32) #0 + +define i8* @test.rangematch(i8* %0, i32 %1, i32 %2) #0 { +entry: + %le = icmp sle i32 %2, %1 + %3 = call i32 @tolower(i32 %2) + %siuitrunc = trunc i32 %1 to i8 + %uisiext = zext i8 %siuitrunc to i32 + %4 = call i32 @tolower(i32 %uisiext) + %le1 = icmp sle i32 %3, %4 + %or = or i1 %le, %le1 + br i1 %or, label %if.then, label %if.exit + +if.then: ; preds = %entry + ret i8* null + +if.exit: ; preds = %entry + ret i8* %0 +} + +declare i32 @foo3(i16) #0 + +define void @test.teste2() #0 { +entry: + %xqic = alloca %Foostruct, align 2 + %0 = bitcast %Foostruct* %xqic to i16* + store i16 0, i16* %0, align 2 + %1 = getelementptr inbounds %Foostruct, %Foostruct* %xqic, i32 0, i32 0 + %2 = load i16, i16* %1, align 2 + %3 = call i32 @foo3(i16 %2) + ret void +} + +define i32 @test.do_merge(%Edge_rec* %0, %Edge_rec* %1) #0 { +entry: + %lvalid = alloca i32, align 4 + %basel = alloca %Edge_rec*, align 8 + %rcand = alloca %Edge_rec*, align 8 + store i32 0, i32* %lvalid, align 4 + store %Edge_rec* null, %Edge_rec** %basel, align 8 + store %Edge_rec* null, %Edge_rec** %rcand, align 8 + br label %loop.body + +loop.body: ; preds = %if.exit, %entry + %2 = load i32, i32* %lvalid, align 4 + %not = icmp eq i32 %2, 0 + br i1 %not, label %if.then, label %if.exit + +if.then: ; preds = %loop.body + %3 = load %Edge_rec*, %Edge_rec** %basel, align 8 + %4 = getelementptr inbounds %Edge_rec, %Edge_rec* %3, i32 0, i32 1 + %5 = load %Edge_rec*, %Edge_rec** %4, align 8 + %ptrxi = ptrtoint %Edge_rec* %5 to i64 + %sisitrunc = trunc i64 %ptrxi to i32 + ret i32 %sisitrunc + +if.exit: ; preds = %loop.body + br label %loop.body + +loop.exit: ; No predecessors! + ret i32 1 +} + +define i32 @test.test(i32 %0) #0 { +entry: + ret i32 %0 +} + +declare void @test.abc(i32*) #0 + +define i32 @test.def(i32 %0, i32 %1) #0 { +entry: + %z = alloca i32, align 4 + store i32 %1, i32* %z, align 4 + call void @test.abc(i32* %z) + ret i32 %0 +} + +define i32 @test.testing(%Test* %0) #0 { +entry: + %1 = getelementptr inbounds %Test, %Test* %0, i32 0, i32 1 + %2 = load i16, i16* %1, align 2 + %sisiext = sext i16 %2 to i32 + %3 = getelementptr inbounds %Test, %Test* %0, i32 0, i32 2 + %4 = load i32, i32* %3, align 4 + %add = add i32 %sisiext, %4 + ret i32 %add +} + +define i32 @test.test2(i32 %0, i64 %1, i32 %2, i32 %3) #0 { +entry: + %4 = alloca { i64, i32 }, align 8 + %a = bitcast { i64, i32 }* %4 to %Test* + %5 = getelementptr inbounds { i64, i32 }, { i64, i32 }* %4, i32 0, i32 0 + store i64 %1, i64* %5, align 8 + %6 = getelementptr inbounds { i64, i32 }, { i64, i32 }* %4, i32 0, i32 1 + store i32 %2, i32* %6, align 8 + %add = add i32 %0, %3 + %7 = getelementptr inbounds %Test, %Test* %a, i32 0, i32 1 + %8 = load i16, i16* %7, align 2 + %sisiext = sext i16 %8 to i32 + %add1 = add i32 %add, %sisiext + %9 = getelementptr inbounds %Test, %Test* %a, i32 0, i32 2 + %10 = load i32, i32* %9, align 4 + %add2 = add i32 %add1, %10 + ret i32 %add2 +} + +define i32 @test.test3(i64 %0, i32 %1, i64 %2, i32 %3) #0 { +entry: + %4 = alloca { i64, i32 }, align 8 + %5 = alloca { i64, i32 }, align 8 + %a = bitcast { i64, i32 }* %4 to %Test* + %6 = getelementptr inbounds { i64, i32 }, { i64, i32 }* %4, i32 0, i32 0 + store i64 %0, i64* %6, align 8 + %7 = getelementptr inbounds { i64, i32 }, { i64, i32 }* %4, i32 0, i32 1 + store i32 %1, i32* %7, align 8 + %b = bitcast { i64, i32 }* %5 to %Test* + %8 = getelementptr inbounds { i64, i32 }, { i64, i32 }* %5, i32 0, i32 0 + store i64 %2, i64* %8, align 8 + %9 = getelementptr inbounds { i64, i32 }, { i64, i32 }* %5, i32 0, i32 1 + store i32 %3, i32* %9, align 8 + %10 = getelementptr inbounds %Test, %Test* %a, i32 0, i32 1 + %11 = load i16, i16* %10, align 2 + %sisiext = sext i16 %11 to i32 + %12 = getelementptr inbounds %Test, %Test* %a, i32 0, i32 2 + %13 = load i32, i32* %12, align 4 + %add = add i32 %sisiext, %13 + %14 = getelementptr inbounds %Test, %Test* %b, i32 0, i32 2 + %15 = load i32, i32* %14, align 4 + %add1 = add i32 %add, %15 + %16 = getelementptr inbounds %Test, %Test* %b, i32 0, i32 3 + %17 = load i32, i32* %16, align 8 + %add2 = add i32 %add1, %17 + ret i32 %add2 +} + +define { i64, i32 } @test.test4(i64 %0, i32 %1) #0 { +entry: + %2 = alloca { i64, i32 }, align 8 + %tempcoerce = alloca { i64, i32 }, align 8 + %a = bitcast { i64, i32 }* %2 to %Test* + %3 = getelementptr inbounds { i64, i32 }, { i64, i32 }* %2, i32 0, i32 0 + store i64 %0, i64* %3, align 8 + %4 = getelementptr inbounds { i64, i32 }, { i64, i32 }* %2, i32 0, i32 1 + store i32 %1, i32* %4, align 8 + %5 = bitcast { i64, i32 }* %tempcoerce to i8* + %6 = bitcast %Test* %a to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %5, i8* align 8 %6, i32 12, i1 false) + %7 = load { i64, i32 }, { i64, i32 }* %tempcoerce, align 8 + ret { i64, i32 } %7 +} + +define i32 @test.test6() #0 { +entry: + %b = alloca [200 x i32], align 16 + %0 = bitcast [200 x i32]* %b to i8* + call void @llvm.memset.p0i8.i64(i8* align 16 %0, i8 0, i64 800, i1 false) + %1 = getelementptr inbounds [200 x i32], [200 x i32]* %b, i64 0, i64 4 + %2 = load i32, i32* %1, align 4 + ret i32 %2 +} + +define void @test.test7(%STest2* noalias sret(%STest2) align 8 %0, %STest2* byval(%STest2) align 8 %1) #0 { +entry: + %x = alloca %STest2, align 8 + %2 = bitcast %STest2* %x to i8* + %3 = bitcast %STest2* %1 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %2, i8* align 8 %3, i32 24, i1 false) + %4 = bitcast %STest2* %0 to i8* + %5 = bitcast %STest2* %x to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %4, i8* align 8 %5, i32 24, i1 false) + ret void +} + +declare i32 @printf(i8*, ...) #0 + +define i32 @test.main2() #0 { +entry: + %v = alloca i64, align 8 + store i64 123455678902, i64* %v, align 8 + %0 = load i64, i64* %v, align 8 + %1 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([6 x i8], [6 x i8]* @.str.6, i32 0, i32 0), i64 %0) + ret i32 1 +}