Skip to content

Commit

Permalink
JIT: Add GT_SWIFT_ERROR_RET to represent loading error register upon …
Browse files Browse the repository at this point in the history
…return (dotnet#100692)

Follow-up to dotnet#100429. If a method has a `SwiftError*` out parameter, a new phase -- `fgAddSwiftErrorReturns` -- converts all `GT_RETURN` nodes into `GT_SWIFT_ERROR_RET` nodes; this new node type is a binop that takes the error value as its first operand, and the normal return value (if there is one) as its second operand. The error value is loaded into the Swift error register upon returning.
  • Loading branch information
amanasifkhalid authored and matouskozak committed Apr 30, 2024
1 parent ae442e5 commit e2acd3d
Show file tree
Hide file tree
Showing 33 changed files with 437 additions and 161 deletions.
9 changes: 5 additions & 4 deletions src/coreclr/jit/assertionprop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4109,7 +4109,7 @@ GenTree* Compiler::optAssertionProp_ModDiv(ASSERT_VALARG_TP assertions, GenTreeO
}

//------------------------------------------------------------------------
// optAssertionProp_Return: Try and optimize a GT_RETURN via assertions.
// optAssertionProp_Return: Try and optimize a GT_RETURN/GT_SWIFT_ERROR_RET via assertions.
//
// Propagates ZEROOBJ for the return value.
//
Expand All @@ -4124,9 +4124,9 @@ GenTree* Compiler::optAssertionProp_ModDiv(ASSERT_VALARG_TP assertions, GenTreeO
// Notes:
// stmt may be nullptr during local assertion prop
//
GenTree* Compiler::optAssertionProp_Return(ASSERT_VALARG_TP assertions, GenTreeUnOp* ret, Statement* stmt)
GenTree* Compiler::optAssertionProp_Return(ASSERT_VALARG_TP assertions, GenTreeOp* ret, Statement* stmt)
{
GenTree* retValue = ret->gtGetOp1();
GenTree* retValue = ret->GetReturnValue();

// Only propagate zeroes that lowering can deal with.
if (!ret->TypeIs(TYP_VOID) && varTypeIsStruct(retValue) && !varTypeIsStruct(info.compRetNativeType))
Expand Down Expand Up @@ -5512,7 +5512,8 @@ GenTree* Compiler::optAssertionProp(ASSERT_VALARG_TP assertions, GenTree* tree,
return optAssertionProp_BlockStore(assertions, tree->AsBlk(), stmt);

case GT_RETURN:
return optAssertionProp_Return(assertions, tree->AsUnOp(), stmt);
case GT_SWIFT_ERROR_RET:
return optAssertionProp_Return(assertions, tree->AsOp(), stmt);

case GT_MOD:
case GT_DIV:
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -1309,6 +1309,10 @@ class CodeGen final : public CodeGenInterface

void genReturn(GenTree* treeNode);

#ifdef SWIFT_SUPPORT
void genSwiftErrorReturn(GenTree* treeNode);
#endif // SWIFT_SUPPORT

#ifdef TARGET_XARCH
void genStackPointerConstantAdjustment(ssize_t spDelta, bool trackSpAdjustments);
void genStackPointerConstantAdjustmentWithProbe(ssize_t spDelta, bool trackSpAdjustments);
Expand Down
6 changes: 3 additions & 3 deletions src/coreclr/jit/codegenarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2990,15 +2990,15 @@ void CodeGen::genCodeForStoreLclVar(GenTreeLclVar* lclNode)
// Note: treeNode's and op1's registers are already consumed.
//
// Arguments:
// treeNode - The GT_RETURN or GT_RETFILT tree node with non-struct and non-void type
// treeNode - The GT_RETURN/GT_RETFILT/GT_SWIFT_ERROR_RET tree node with non-struct and non-void type
//
// Return Value:
// None
//
void CodeGen::genSimpleReturn(GenTree* treeNode)
{
assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT);
GenTree* op1 = treeNode->gtGetOp1();
assert(treeNode->OperIs(GT_RETURN, GT_RETFILT, GT_SWIFT_ERROR_RET));
GenTree* op1 = treeNode->AsOp()->GetReturnValue();
var_types targetType = treeNode->TypeGet();

assert(targetType != TYP_STRUCT);
Expand Down
6 changes: 6 additions & 0 deletions src/coreclr/jit/codegenarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,12 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
genReturn(treeNode);
break;

#ifdef SWIFT_SUPPORT
case GT_SWIFT_ERROR_RET:
genSwiftErrorReturn(treeNode);
break;
#endif // SWIFT_SUPPORT

case GT_LEA:
// If we are here, it is the case where there is an LEA that cannot be folded into a parent instruction.
genLeaInstruction(treeNode->AsAddrMode());
Expand Down
56 changes: 33 additions & 23 deletions src/coreclr/jit/codegencommon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6869,8 +6869,8 @@ GenTreeIntCon CodeGen::intForm(var_types type, ssize_t value)
//
void CodeGen::genLongReturn(GenTree* treeNode)
{
assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT);
assert(treeNode->TypeGet() == TYP_LONG);
assert(treeNode->OperIs(GT_RETURN, GT_RETFILT));
assert(treeNode->TypeIs(TYP_LONG));
GenTree* op1 = treeNode->gtGetOp1();
var_types targetType = treeNode->TypeGet();

Expand All @@ -6894,16 +6894,16 @@ void CodeGen::genLongReturn(GenTree* treeNode)
// In case of LONG return on 32-bit, delegates to the genLongReturn method.
//
// Arguments:
// treeNode - The GT_RETURN or GT_RETFILT tree node.
// treeNode - The GT_RETURN/GT_RETFILT/GT_SWIFT_ERROR_RET tree node.
//
// Return Value:
// None
//
void CodeGen::genReturn(GenTree* treeNode)
{
assert(treeNode->OperIs(GT_RETURN, GT_RETFILT));
assert(treeNode->OperIs(GT_RETURN, GT_RETFILT, GT_SWIFT_ERROR_RET));

GenTree* op1 = treeNode->gtGetOp1();
GenTree* op1 = treeNode->AsOp()->GetReturnValue();
var_types targetType = treeNode->TypeGet();

// A void GT_RETFILT is the end of a finally. For non-void filter returns we need to load the result in the return
Expand Down Expand Up @@ -7005,7 +7005,7 @@ void CodeGen::genReturn(GenTree* treeNode)
//
// There should be a single GT_RETURN while generating profiler ELT callbacks.
//
if (treeNode->OperIs(GT_RETURN) && compiler->compIsProfilerHookNeeded())
if (treeNode->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET) && compiler->compIsProfilerHookNeeded())
{
// !! NOTE !!
// Since we are invalidating the assumption that we would slip into the epilog
Expand Down Expand Up @@ -7075,18 +7075,28 @@ void CodeGen::genReturn(GenTree* treeNode)

genStackPointerCheck(doStackPointerCheck, compiler->lvaReturnSpCheck);
#endif // defined(DEBUG) && defined(TARGET_XARCH)
}

#ifdef SWIFT_SUPPORT
// If this method has a SwiftError* out parameter, load the SwiftError pseudolocal value into the error register.
// TODO-CQ: Introduce GenTree node that models returning a normal and Swift error value.
if (compiler->lvaSwiftErrorArg != BAD_VAR_NUM)
{
assert(compiler->info.compCallConv == CorInfoCallConvExtension::Swift);
assert(compiler->lvaSwiftErrorLocal != BAD_VAR_NUM);
GetEmitter()->emitIns_R_S(ins_Load(TYP_I_IMPL), EA_PTRSIZE, REG_SWIFT_ERROR, compiler->lvaSwiftErrorLocal, 0);
}
#endif // SWIFT_SUPPORT
//------------------------------------------------------------------------
// genSwiftErrorReturn: Generates code for returning the normal return value,
// and loading the SwiftError pseudolocal value in the error register.
//
// Arguments:
// treeNode - The GT_SWIFT_ERROR_RET tree node.
//
// Return Value:
// None
//
void CodeGen::genSwiftErrorReturn(GenTree* treeNode)
{
assert(treeNode->OperIs(GT_SWIFT_ERROR_RET));
GenTree* swiftErrorNode = treeNode->gtGetOp1();
const regNumber errorSrcReg = genConsumeReg(swiftErrorNode);
inst_Mov(swiftErrorNode->TypeGet(), REG_SWIFT_ERROR, errorSrcReg, true, EA_PTRSIZE);
genReturn(treeNode);
}
#endif // SWIFT_SUPPORT

//------------------------------------------------------------------------
// isStructReturn: Returns whether the 'treeNode' is returning a struct.
Expand All @@ -7095,15 +7105,15 @@ void CodeGen::genReturn(GenTree* treeNode)
// treeNode - The tree node to evaluate whether is a struct return.
//
// Return Value:
// Returns true if the 'treeNode" is a GT_RETURN node of type struct.
// Returns true if the 'treeNode' is a GT_RETURN/GT_SWIFT_ERROR_RET node of type struct.
// Otherwise returns false.
//
bool CodeGen::isStructReturn(GenTree* treeNode)
{
// This method could be called for 'treeNode' of GT_RET_FILT or GT_RETURN.
// This method could be called for 'treeNode' of GT_RET_FILT/GT_RETURN/GT_SWIFT_ERROR_RET.
// For the GT_RET_FILT, the return is always a bool or a void, for the end of a finally block.
noway_assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT);
if (treeNode->OperGet() != GT_RETURN)
noway_assert(treeNode->OperIs(GT_RETURN, GT_RETFILT, GT_SWIFT_ERROR_RET));
if (!treeNode->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET))
{
return false;
}
Expand All @@ -7130,13 +7140,13 @@ bool CodeGen::isStructReturn(GenTree* treeNode)
//
void CodeGen::genStructReturn(GenTree* treeNode)
{
assert(treeNode->OperGet() == GT_RETURN);

genConsumeRegs(treeNode->gtGetOp1());
assert(treeNode->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET));

GenTree* op1 = treeNode->gtGetOp1();
GenTree* op1 = treeNode->AsOp()->GetReturnValue();
GenTree* actualOp1 = op1->gtSkipReloadOrCopy();

genConsumeRegs(op1);

ReturnTypeDesc retTypeDesc = compiler->compRetTypeDesc;
const unsigned regCount = retTypeDesc.GetReturnRegCount();
assert(regCount <= MAX_RET_REG_COUNT);
Expand Down
5 changes: 3 additions & 2 deletions src/coreclr/jit/codegenlinear.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,9 +498,10 @@ void CodeGen::genCodeForBBlist()
// as the determiner because something we are tracking as a byref
// might be used as a return value of a int function (which is legal)
GenTree* blockLastNode = block->lastNode();
if ((blockLastNode != nullptr) && (blockLastNode->gtOper == GT_RETURN) &&
if ((blockLastNode != nullptr) && (blockLastNode->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET)) &&
(varTypeIsGC(compiler->info.compRetType) ||
(blockLastNode->AsOp()->gtOp1 != nullptr && varTypeIsGC(blockLastNode->AsOp()->gtOp1->TypeGet()))))
(blockLastNode->AsOp()->GetReturnValue() != nullptr &&
varTypeIsGC(blockLastNode->AsOp()->GetReturnValue()->TypeGet()))))
{
nonVarPtrRegs &= ~RBM_INTRET;
}
Expand Down
10 changes: 9 additions & 1 deletion src/coreclr/jit/codegenxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1402,13 +1402,15 @@ void CodeGen::genSIMDSplitReturn(GenTree* src, ReturnTypeDesc* retTypeDesc)
//
// Arguments:
// treeNode - The GT_RETURN or GT_RETFILT tree node with float type.
// (We don't expect treeNode to be a GT_SWIFT_ERROR_RET node,
// as Swift interop isn't supported on x86.)
//
// Return Value:
// None
//
void CodeGen::genFloatReturn(GenTree* treeNode)
{
assert(treeNode->OperGet() == GT_RETURN || treeNode->OperGet() == GT_RETFILT);
assert(treeNode->OperIs(GT_RETURN, GT_RETFILT));
assert(varTypeIsFloating(treeNode));

GenTree* op1 = treeNode->gtGetOp1();
Expand Down Expand Up @@ -1966,6 +1968,12 @@ void CodeGen::genCodeForTreeNode(GenTree* treeNode)
genReturn(treeNode);
break;

#ifdef SWIFT_SUPPORT
case GT_SWIFT_ERROR_RET:
genSwiftErrorReturn(treeNode);
break;
#endif // SWIFT_SUPPORT

case GT_LEA:
// If we are here, it is the case where there is an LEA that cannot be folded into a parent instruction.
genLeaInstruction(treeNode->AsAddrMode());
Expand Down
6 changes: 6 additions & 0 deletions src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4722,6 +4722,12 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
//
DoPhase(this, PHASE_MORPH_ADD_INTERNAL, &Compiler::fgAddInternal);

#ifdef SWIFT_SUPPORT
// Transform GT_RETURN nodes into GT_SWIFT_ERROR_RET nodes if this method has Swift error handling
//
DoPhase(this, PHASE_SWIFT_ERROR_RET, &Compiler::fgAddSwiftErrorReturns);
#endif // SWIFT_SUPPORT

// Remove empty try regions
//
DoPhase(this, PHASE_EMPTY_TRY, &Compiler::fgRemoveEmptyTry);
Expand Down
12 changes: 10 additions & 2 deletions src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5285,6 +5285,10 @@ class Compiler

PhaseStatus fgAddInternal();

#ifdef SWIFT_SUPPORT
PhaseStatus fgAddSwiftErrorReturns();
#endif // SWIFT_SUPPORT

enum class FoldResult
{
FOLD_DID_NOTHING,
Expand Down Expand Up @@ -6529,7 +6533,7 @@ class Compiler
GenTree* fgOptimizeBitwiseAnd(GenTreeOp* andOp);
GenTree* fgOptimizeBitwiseXor(GenTreeOp* xorOp);
GenTree* fgPropagateCommaThrow(GenTree* parent, GenTreeOp* commaThrow, GenTreeFlags precedingSideEffects);
GenTree* fgMorphRetInd(GenTreeUnOp* tree);
GenTree* fgMorphRetInd(GenTreeOp* tree);
GenTree* fgMorphModToZero(GenTreeOp* tree);
GenTree* fgMorphModToSubMulDiv(GenTreeOp* tree);
GenTree* fgMorphUModToAndSub(GenTreeOp* tree);
Expand Down Expand Up @@ -7892,7 +7896,7 @@ class Compiler
GenTree* optAssertionProp_LocalStore(ASSERT_VALARG_TP assertions, GenTreeLclVarCommon* store, Statement* stmt);
GenTree* optAssertionProp_BlockStore(ASSERT_VALARG_TP assertions, GenTreeBlk* store, Statement* stmt);
GenTree* optAssertionProp_ModDiv(ASSERT_VALARG_TP assertions, GenTreeOp* tree, Statement* stmt);
GenTree* optAssertionProp_Return(ASSERT_VALARG_TP assertions, GenTreeUnOp* ret, Statement* stmt);
GenTree* optAssertionProp_Return(ASSERT_VALARG_TP assertions, GenTreeOp* ret, Statement* stmt);
GenTree* optAssertionProp_Ind(ASSERT_VALARG_TP assertions, GenTree* tree, Statement* stmt);
GenTree* optAssertionProp_Cast(ASSERT_VALARG_TP assertions, GenTreeCast* cast, Statement* stmt);
GenTree* optAssertionProp_Call(ASSERT_VALARG_TP assertions, GenTreeCall* call, Statement* stmt);
Expand Down Expand Up @@ -8420,6 +8424,10 @@ class Compiler
unsigned genReturnLocal; // Local number for the return value when applicable.
BasicBlock* genReturnBB; // jumped to when not optimizing for speed.

#ifdef SWIFT_SUPPORT
unsigned genReturnErrorLocal; // Local number for the Swift error value when applicable.
#endif // SWIFT_SUPPORT

// The following properties are part of CodeGenContext. Getters are provided here for
// convenience and backward compatibility, but the properties can only be set by invoking
// the setter on CodeGenContext directly.
Expand Down
1 change: 1 addition & 0 deletions src/coreclr/jit/compphases.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ CompPhaseNameMacro(PHASE_INCPROFILE, "Profile incorporation",
CompPhaseNameMacro(PHASE_MORPH_INIT, "Morph - Init", false, -1, false)
CompPhaseNameMacro(PHASE_MORPH_INLINE, "Morph - Inlining", false, -1, true)
CompPhaseNameMacro(PHASE_MORPH_ADD_INTERNAL, "Morph - Add internal blocks", false, -1, true)
CompPhaseNameMacro(PHASE_SWIFT_ERROR_RET, "Add Swift error returns", false, -1, true)
CompPhaseNameMacro(PHASE_ALLOCATE_OBJECTS, "Allocate Objects", false, -1, false)
CompPhaseNameMacro(PHASE_EMPTY_TRY, "Remove empty try", false, -1, false)
CompPhaseNameMacro(PHASE_EMPTY_FINALLY, "Remove empty finally", false, -1, false)
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/jit/decomposelongs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,8 @@ GenTree* DecomposeLongs::DecomposeNode(GenTree* tree)
break;

case GT_RETURN:
assert(tree->AsOp()->gtOp1->OperGet() == GT_LONG);
case GT_SWIFT_ERROR_RET:
assert(tree->AsOp()->GetReturnValue()->OperIs(GT_LONG));
break;

case GT_STOREIND:
Expand Down
4 changes: 4 additions & 0 deletions src/coreclr/jit/fgbasic.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ void Compiler::fgInit()
genReturnBB = nullptr;
genReturnLocal = BAD_VAR_NUM;

#ifdef SWIFT_SUPPORT
genReturnErrorLocal = BAD_VAR_NUM;
#endif // SWIFT_SUPPORT

/* We haven't reached the global morphing phase */
fgGlobalMorph = false;
fgGlobalMorphDone = false;
Expand Down
3 changes: 2 additions & 1 deletion src/coreclr/jit/fgstmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ void Compiler::fgInsertStmtNearEnd(BasicBlock* block, Statement* stmt)
}
else if (block->KindIs(BBJ_RETURN))
{
assert((lastStmt->GetRootNode()->OperIs(GT_RETURN, GT_JMP)) ||
assert((lastStmt->GetRootNode()->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET, GT_JMP)) ||
// BBJ_RETURN blocks in functions returning void do not get a GT_RETURN node if they
// have a .tail prefix (even if canTailCall returns false for these calls)
// code:Compiler::impImportBlockCode (search for the RET: label)
Expand Down Expand Up @@ -538,6 +538,7 @@ inline bool OperIsControlFlow(genTreeOps oper)

case GT_RETURN:
case GT_RETFILT:
case GT_SWIFT_ERROR_RET:
#if defined(FEATURE_EH_WINDOWS_X86)
case GT_END_LFIN:
#endif // FEATURE_EH_WINDOWS_X86
Expand Down
Loading

0 comments on commit e2acd3d

Please sign in to comment.