From f4e01f5416342985768ddb3b1f5fb98fa322ef6b Mon Sep 17 00:00:00 2001 From: imhameed Date: Wed, 2 Jun 2021 07:43:49 -0700 Subject: [PATCH] Consolidate setret handling for diverging functions (#53535) Some functions never associate the mini IR source registers used with their `setret` instructions with LLVM values because they unconditionally throw. `System.Runtime.Intrinsics.X86.Aes:Decrypt` is an example of such a function; when compiled for arm64, it never returns a value and unconditionally throws a `PlatformNotSupportedException`. We already had support for handling this for some, but not all, return value passing conventions. Deduplicate this support and apply it uniformly to all return value passing conventions that expect a populated mini IR source register. Make `LLVMArgNone` specifically mean "no return value"/"`void` return type". Split from https://github.com/dotnet/runtime/pull/53132. Partially fixes FullAOT compilation of System.Private.CoreLib.dll on arm64. --- src/mono/mono/mini/mini-arm.c | 5 +- src/mono/mono/mini/mini-arm64.c | 3 + src/mono/mono/mini/mini-llvm.c | 108 +++++++++++++++++--------------- 3 files changed, 64 insertions(+), 52 deletions(-) diff --git a/src/mono/mono/mini/mini-arm.c b/src/mono/mono/mini/mini-arm.c index b08a5bcb649d2..1f4ec81d761f4 100644 --- a/src/mono/mono/mini/mini-arm.c +++ b/src/mono/mono/mini/mini-arm.c @@ -2311,10 +2311,13 @@ mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig) * in 1 or 2 integer registers. */ switch (cinfo->ret.storage) { - case RegTypeGeneral: case RegTypeNone: + linfo->ret.storage = LLVMArgNone; + break; + case RegTypeGeneral: case RegTypeFP: case RegTypeIRegPair: + linfo->ret.storage = LLVMArgNormal; break; case RegTypeStructByAddr: if (sig->pinvoke) { diff --git a/src/mono/mono/mini/mini-arm64.c b/src/mono/mono/mini/mini-arm64.c index b46541b01dae4..a868facb7ff48 100644 --- a/src/mono/mono/mini/mini-arm64.c +++ b/src/mono/mono/mini/mini-arm64.c @@ -2451,7 +2451,10 @@ mono_arch_get_llvm_call_info (MonoCompile *cfg, MonoMethodSignature *sig) case ArgInIReg: case ArgInFReg: case ArgInFRegR4: + linfo->ret.storage = LLVMArgNormal; + break; case ArgNone: + linfo->ret.storage = LLVMArgNone; break; case ArgVtypeByRef: linfo->ret.storage = LLVMArgVtypeByRef; diff --git a/src/mono/mono/mini/mini-llvm.c b/src/mono/mono/mini/mini-llvm.c index ffe0a83578fa3..4a4308dd555c3 100644 --- a/src/mono/mono/mini/mini-llvm.c +++ b/src/mono/mono/mini/mini-llvm.c @@ -5660,46 +5660,65 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb) case OP_SETRET: switch (linfo->ret.storage) { - case LLVMArgVtypeInReg: { + case LLVMArgNormal: + case LLVMArgVtypeInReg: + case LLVMArgVtypeAsScalar: { LLVMTypeRef ret_type = LLVMGetReturnType (LLVMGetElementType (LLVMTypeOf (method))); LLVMValueRef retval = LLVMGetUndef (ret_type); - if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type_internal (sig->ret))) { - /* The return type is an LLVM aggregate type, so a bare bitcast cannot be used to do this conversion. */ - int width = mono_type_size (sig->ret, NULL); - int elems = width / TARGET_SIZEOF_VOID_P; - /* The return value might not be set if there is a throw */ - LLVMValueRef val = lhs ? LLVMBuildBitCast (builder, lhs, LLVMVectorType (IntPtrType (), elems), "") : LLVMConstNull (LLVMVectorType (IntPtrType (), elems)); - for (int i = 0; i < elems; ++i) { - LLVMValueRef element = LLVMBuildExtractElement (builder, val, const_int32 (i), ""); - retval = LLVMBuildInsertValue (builder, retval, element, i, "setret_simd_vtype_in_reg"); - } - } else { - LLVMValueRef addr = LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (ret_type, 0), ""); - for (int i = 0; i < 2; ++i) { - if (linfo->ret.pair_storage [i] == LLVMArgInIReg) { - LLVMValueRef indexes [2], part_addr; - - indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); - indexes [1] = LLVMConstInt (LLVMInt32Type (), i, FALSE); - part_addr = LLVMBuildGEP (builder, addr, indexes, 2, ""); - - retval = LLVMBuildInsertValue (builder, retval, LLVMBuildLoad (builder, part_addr, ""), i, ""); - } else { - g_assert (linfo->ret.pair_storage [i] == LLVMArgNone); + gboolean src_in_reg = FALSE; + gboolean is_simd = MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type_internal (sig->ret)); + switch (linfo->ret.storage) { + case LLVMArgNormal: src_in_reg = TRUE; break; + case LLVMArgVtypeInReg: case LLVMArgVtypeAsScalar: src_in_reg = is_simd; break; + } + if (src_in_reg && (!lhs || ctx->is_dead [ins->sreg1])) { + /* + * The method did not set its return value, probably because it + * ends with a throw. + */ + LLVMBuildRet (builder, retval); + break; + } + switch (linfo->ret.storage) { + case LLVMArgNormal: + retval = convert (ctx, lhs, type_to_llvm_type (ctx, sig->ret)); + break; + case LLVMArgVtypeInReg: + if (is_simd) { + /* The return type is an LLVM aggregate type, so a bare bitcast cannot be used to do this conversion. */ + int width = mono_type_size (sig->ret, NULL); + int elems = width / TARGET_SIZEOF_VOID_P; + /* The return value might not be set if there is a throw */ + LLVMValueRef val = LLVMBuildBitCast (builder, lhs, LLVMVectorType (IntPtrType (), elems), ""); + for (int i = 0; i < elems; ++i) { + LLVMValueRef element = LLVMBuildExtractElement (builder, val, const_int32 (i), ""); + retval = LLVMBuildInsertValue (builder, retval, element, i, "setret_simd_vtype_in_reg"); + } + } else { + LLVMValueRef addr = LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (ret_type, 0), ""); + for (int i = 0; i < 2; ++i) { + if (linfo->ret.pair_storage [i] == LLVMArgInIReg) { + LLVMValueRef indexes [2], part_addr; + + indexes [0] = LLVMConstInt (LLVMInt32Type (), 0, FALSE); + indexes [1] = LLVMConstInt (LLVMInt32Type (), i, FALSE); + part_addr = LLVMBuildGEP (builder, addr, indexes, 2, ""); + + retval = LLVMBuildInsertValue (builder, retval, LLVMBuildLoad (builder, part_addr, ""), i, ""); + } else { + g_assert (linfo->ret.pair_storage [i] == LLVMArgNone); + } } } - } - LLVMBuildRet (builder, retval); - break; - } - case LLVMArgVtypeAsScalar: { - LLVMTypeRef ret_type = LLVMGetReturnType (LLVMGetElementType (LLVMTypeOf (method))); - LLVMValueRef retval = NULL; - if (MONO_CLASS_IS_SIMD (ctx->cfg, mono_class_from_mono_type_internal (sig->ret))) - retval = LLVMBuildBitCast (builder, values [ins->sreg1], ret_type, "setret_simd_vtype_as_scalar"); - else { - g_assert (addresses [ins->sreg1]); - retval = LLVMBuildLoad (builder, LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (ret_type, 0), ""), ""); + break; + case LLVMArgVtypeAsScalar: + if (is_simd) { + retval = LLVMBuildBitCast (builder, values [ins->sreg1], ret_type, "setret_simd_vtype_as_scalar"); + } else { + g_assert (addresses [ins->sreg1]); + retval = LLVMBuildLoad (builder, LLVMBuildBitCast (builder, addresses [ins->sreg1], LLVMPointerType (ret_type, 0), ""), ""); + } + break; } LLVMBuildRet (builder, retval); break; @@ -5745,26 +5764,13 @@ process_bb (EmitContext *ctx, MonoBasicBlock *bb) break; } case LLVMArgNone: - case LLVMArgNormal: { - if (!lhs || ctx->is_dead [ins->sreg1]) { - /* - * The method did not set its return value, probably because it - * ends with a throw. - */ - if (cfg->vret_addr) - LLVMBuildRetVoid (builder); - else - LLVMBuildRet (builder, LLVMConstNull (type_to_llvm_type (ctx, sig->ret))); - } else { - LLVMBuildRet (builder, convert (ctx, lhs, type_to_llvm_type (ctx, sig->ret))); - } - has_terminator = TRUE; + LLVMBuildRetVoid (builder); break; - } default: g_assert_not_reached (); break; } + has_terminator = TRUE; break; case OP_ICOMPARE: case OP_FCOMPARE: