diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp index cde9ed0b2..4af0fed0f 100644 --- a/src/llvm_abi.cpp +++ b/src/llvm_abi.cpp @@ -22,8 +22,7 @@ struct lbArgType { // it drops padding, and the dense type it produces puts the surviving members at different offsets // than they really have, eg `struct #min_field_align(16){i8, f32}` flattens to `{i8, float}`, moving // the float from offset 16 to offset 4. These give the real byte offset of each `cast_type` element. - i64 *coerce_offsets; - isize coerce_offset_count; + Slice coerce_offsets; }; @@ -31,14 +30,13 @@ gb_internal i64 lb_sizeof(LLVMTypeRef type); gb_internal i64 lb_alignof(LLVMTypeRef type); gb_internal lbArgType lb_arg_type_direct(LLVMTypeRef type, LLVMTypeRef cast_type, LLVMTypeRef pad_type, LLVMAttributeRef attr) { - return lbArgType{lbArg_Direct, type, cast_type, pad_type, attr, nullptr, 0, false}; + return lbArgType{lbArg_Direct, type, cast_type, pad_type, attr, {}, false}; } // Same as above, except coercion reads each element of `cast_type` from its real offset in `type` // instead of reinterpreting the bits from offset zero. See `coerce_offsets`. -gb_internal lbArgType lb_arg_type_direct_fields(LLVMTypeRef type, LLVMTypeRef cast_type, i64 *offsets, isize count) { +gb_internal lbArgType lb_arg_type_direct_fields(LLVMTypeRef type, LLVMTypeRef cast_type, Slice offsets) { lbArgType arg = lb_arg_type_direct(type, cast_type, nullptr, nullptr); arg.coerce_offsets = offsets; - arg.coerce_offset_count = count; return arg; } gb_internal lbArgType lb_arg_type_direct(LLVMTypeRef type) { @@ -46,7 +44,7 @@ gb_internal lbArgType lb_arg_type_direct(LLVMTypeRef type) { } gb_internal lbArgType lb_arg_type_indirect(LLVMTypeRef type, LLVMAttributeRef attr) { - return lbArgType{lbArg_Indirect, type, nullptr, nullptr, attr, nullptr, 0, false}; + return lbArgType{lbArg_Indirect, type, nullptr, nullptr, attr, {}, false}; } gb_internal lbArgType lb_arg_type_indirect_byval(LLVMContextRef c, LLVMTypeRef type, Type *source_type = nullptr) { @@ -65,7 +63,7 @@ gb_internal lbArgType lb_arg_type_indirect_byval(LLVMContextRef c, LLVMTypeRef t } gb_internal lbArgType lb_arg_type_ignore(LLVMTypeRef type) { - return lbArgType{lbArg_Ignore, type, nullptr, nullptr, nullptr, nullptr, 0, false}; + return lbArgType{lbArg_Ignore, type, nullptr, nullptr, nullptr, {}, false}; } struct lbFunctionType { @@ -2498,8 +2496,7 @@ namespace lbAbiRiscv64 { LLVMTypeRef fp_type = type; LLVMTypeKind fp_kind = kind; i64 fp_size = size; - i64 *fp_offsets = nullptr; - isize fp_offset_count = 0; + Slice fp_offsets = {}; if (kind == LLVMStructTypeKind) { Array fields = array_make(temporary_allocator(), 0, LLVMCountStructElementTypes(type)); flatten(m, &fields, type, false); @@ -2507,11 +2504,7 @@ namespace lbAbiRiscv64 { auto offsets = array_make(temporary_allocator(), 0, fields.count); flatten_offsets(m, &offsets, type, 0); if (flatten_moved_a_member(fields, offsets)) { - fp_offsets = gb_alloc_array(permanent_allocator(), i64, offsets.count); - for_array(i, offsets) { - fp_offsets[i] = offsets[i]; - } - fp_offset_count = offsets.count; + fp_offsets = slice_clone_from_array(permanent_allocator(), offsets); } if (fields.count == 1) { @@ -2531,8 +2524,8 @@ namespace lbAbiRiscv64 { if (fp_type != orig_type) { // A struct that flattened to a single float has to be coerced to that float; // handing back the original sends an over-aligned one to integer registers. - if (fp_offset_count > 0) { - return lb_arg_type_direct_fields(orig_type, fp_type, fp_offsets, fp_offset_count); + if (fp_offsets.count > 0) { + return lb_arg_type_direct_fields(orig_type, fp_type, fp_offsets); } return lb_arg_type_direct(orig_type, fp_type, nullptr, nullptr); } @@ -2549,8 +2542,8 @@ namespace lbAbiRiscv64 { if (is_float(ty1) && is_float(ty2) && ty1s <= flen && ty2s <= flen && *fprs_left >= 2) { *fprs_left -= 2; - if (fp_offset_count > 0) { - return lb_arg_type_direct_fields(orig_type, fp_type, fp_offsets, fp_offset_count); + if (fp_offsets.count > 0) { + return lb_arg_type_direct_fields(orig_type, fp_type, fp_offsets); } return lb_arg_type_direct(orig_type, fp_type, nullptr, nullptr); } @@ -2558,8 +2551,8 @@ namespace lbAbiRiscv64 { if (is_float(ty1) && is_int_member(ty2) && ty1s <= flen && ty2s <= xlen && *fprs_left >= 1 && *gprs_left >= 1) { *fprs_left -= 1; *gprs_left -= 1; - if (fp_offset_count > 0) { - return lb_arg_type_direct_fields(orig_type, fp_type, fp_offsets, fp_offset_count); + if (fp_offsets.count > 0) { + return lb_arg_type_direct_fields(orig_type, fp_type, fp_offsets); } return lb_arg_type_direct(orig_type, fp_type, nullptr, nullptr); } @@ -2567,8 +2560,8 @@ namespace lbAbiRiscv64 { if (is_int_member(ty1) && is_float(ty2) && ty1s <= xlen && ty2s <= flen && *gprs_left >= 1 && *fprs_left >= 1) { *fprs_left -= 1; *gprs_left -= 1; - if (fp_offset_count > 0) { - return lb_arg_type_direct_fields(orig_type, fp_type, fp_offsets, fp_offset_count); + if (fp_offsets.count > 0) { + return lb_arg_type_direct_fields(orig_type, fp_type, fp_offsets); } return lb_arg_type_direct(orig_type, fp_type, nullptr, nullptr); } diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index b91a9dda0..404620926 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -684,7 +684,7 @@ gb_internal void lb_begin_procedure_body(lbProcedure *p) { if (e->token.string.len != 0 && !is_blank_ident(e->token.string)) { LLVMTypeRef param_type = lb_type(p->module, e->type); LLVMValueRef original_value = LLVMGetParam(p->value, param_offset+llvm_param_index); - LLVMValueRef value = arg_type->coerce_offset_count > 0 + LLVMValueRef value = arg_type->coerce_offsets.count > 0 ? lb_coerce_fields_store(p, original_value, e->type, arg_type) : OdinLLVMBuildTransmute(p, original_value, param_type); @@ -939,14 +939,14 @@ gb_internal LLVMValueRef lb_coerce_fields_load(lbProcedure *p, lbValue x, lbArgT lbValue base = lb_address_from_load_or_generate_local(p, x); if (LLVMGetTypeKind(arg->cast_type) != LLVMStructTypeKind) { - GB_ASSERT(arg->coerce_offset_count == 1); + GB_ASSERT(arg->coerce_offsets.count == 1); LLVMValueRef index = LLVMConstInt(i64t, cast(unsigned long long)arg->coerce_offsets[0], false); LLVMValueRef ptr = LLVMBuildInBoundsGEP2(p->builder, i8, base.value, &index, 1, ""); return LLVMBuildLoad2(p->builder, arg->cast_type, ptr, ""); } unsigned count = LLVMCountStructElementTypes(arg->cast_type); - GB_ASSERT(cast(isize)count == arg->coerce_offset_count); + GB_ASSERT(cast(isize)count == arg->coerce_offsets.count); LLVMValueRef result = LLVMGetUndef(arg->cast_type); for (unsigned i = 0; i < count; i += 1) { LLVMTypeRef elem_type = LLVMStructGetTypeAtIndex(arg->cast_type, i); @@ -971,7 +971,7 @@ gb_internal LLVMValueRef lb_coerce_fields_store(lbProcedure *p, LLVMValueRef coe if (is_struct) { count = LLVMCountStructElementTypes(arg->cast_type); } - GB_ASSERT(cast(isize)count == arg->coerce_offset_count); + GB_ASSERT(cast(isize)count == arg->coerce_offsets.count); for (unsigned i = 0; i < count; i += 1) { LLVMValueRef elem = is_struct ? LLVMBuildExtractValue(p->builder, coerced, i, "") : coerced; LLVMValueRef index = LLVMConstInt(i64t, cast(unsigned long long)arg->coerce_offsets[i], false); @@ -1255,7 +1255,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array c if (!abi_type) { abi_type = arg->type; } - if (arg->coerce_offset_count > 0) { + if (arg->coerce_offsets.count > 0) { x.value = lb_coerce_fields_load(p, x, arg); array_add(&processed_args, x); } else if (xt == abi_type) { @@ -1328,7 +1328,7 @@ gb_internal lbValue lb_emit_call(lbProcedure *p, lbValue value, Array c result = lb_emit_load(p, return_ptr); } else if (rt != nullptr) { result = lb_emit_call_internal(p, value, {}, processed_args, rt, context_ptr, inlining, tailing); - if (ft->ret.coerce_offset_count > 0) { + if (ft->ret.coerce_offsets.count > 0) { result.value = lb_coerce_fields_store(p, result.value, rt, &ft->ret); } else { if (ft->ret.cast_type) { diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index 9fbb2d15d..5cdfb21ce 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -1,6 +1,8 @@ #define LB_ENABLE_BASIC_RVO true #define LB_ENABLE_ADVANCED_RVO build_context.enable_rvo +gb_internal LLVMValueRef lb_coerce_fields_load(lbProcedure *p, lbValue x, lbArgType const *arg); + // NOTE(bill): @RVO Check if a call expression returns by sret with a return type matching dst_type. // Returns the callee's function type if eligible for copy elision, nullptr otherwise. gb_internal lbFunctionType *lb_call_sret_eligible(lbProcedure *p, Ast *call_expr, Type *dst_type) { @@ -2587,7 +2589,9 @@ gb_internal void lb_build_return_stmt_internal(lbProcedure *p, lbValue res, Toke ret_type = cast_type; } - if (LLVMGetTypeKind(ret_type) == LLVMStructTypeKind) { + if (ft->ret.coerce_offsets.count > 0) { + ret_val = lb_coerce_fields_load(p, res, &ft->ret); + } else if (LLVMGetTypeKind(ret_type) == LLVMStructTypeKind) { LLVMTypeRef src_type = LLVMTypeOf(ret_val); if (p->temp_callee_return_struct_memory == nullptr) { diff --git a/tests/abi/gen.odin b/tests/abi/gen.odin index a793faf30..6303ac09c 100644 --- a/tests/abi/gen.odin +++ b/tests/abi/gen.odin @@ -1441,6 +1441,11 @@ emit_main :: proc() -> string { w(&sb, "%s\tif %s_arg(s, 7) != 7 { return 1 }\n", ind, t.name) w(&sb, "%s\tif %s_chk(s) != 0 { return 1 }\n", ind, t.name) w(&sb, "%s\tif %s_ex(1,2,3,4,5,6,7, 1,2,3,4,5,6,7,8, s, 7) != 7 { return 1 }\n", ind, t.name) + w(&sb, "%s\tif %s_ex2(1,2,3,4,5,6,7,8,9, 1,2,3,4,5,6,7,8,9, s, 7) != 7 { return 1 }\n", ind, t.name) + w(&sb, "%s\tif %s_two(s, s, 7) != 7 { return 1 }\n", ind, t.name) + w(&sb, "%s\tif %s_can(1,2,3,4,5,6,7, 1,2,3,4,5,6,7,8, 0x1111111111111111, s, 0x2222222222222222, 7) != 7 { return 1 }\n", ind, t.name) + w(&sb, "%s\tif %s_can2(1,2,3,4,5,6,7,8,9,10,11, 0x1111111111111111, s, 0x2222222222222222, 7) != 7 { return 1 }\n", ind, t.name) + w(&sb, "%s\tif %s_back() != 0 { return 1 }\n", ind, t.name) w(&sb, "%s\tr := %s_ret()\n", ind, t.name) getters := odin_getters(t, "r") if len(getters) == 0 {