Skip to content

Commit

Permalink
Consolidate setret handling for diverging functions (#53535)
Browse files Browse the repository at this point in the history
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 #53132.

Partially fixes FullAOT compilation of System.Private.CoreLib.dll on arm64.
  • Loading branch information
imhameed committed Jun 2, 2021
1 parent 0e557e8 commit f4e01f5
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 52 deletions.
5 changes: 4 additions & 1 deletion src/mono/mono/mini/mini-arm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
3 changes: 3 additions & 0 deletions src/mono/mono/mini/mini-arm64.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
108 changes: 57 additions & 51 deletions src/mono/mono/mini/mini-llvm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit f4e01f5

Please sign in to comment.