From ec7648702c1307ab4002a429996da7db023787dd Mon Sep 17 00:00:00 2001 From: Aman Khalid Date: Fri, 12 Apr 2024 19:26:00 +0000 Subject: [PATCH] JIT: Add GT_SWIFT_ERROR_RET to represent loading error register upon return (#100692) Follow-up to #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. --- src/coreclr/jit/assertionprop.cpp | 9 +-- src/coreclr/jit/codegen.h | 4 ++ src/coreclr/jit/codegenarm64.cpp | 6 +- src/coreclr/jit/codegenarmarch.cpp | 6 ++ src/coreclr/jit/codegencommon.cpp | 56 +++++++++------ src/coreclr/jit/codegenlinear.cpp | 5 +- src/coreclr/jit/codegenxarch.cpp | 10 ++- src/coreclr/jit/compiler.cpp | 6 ++ src/coreclr/jit/compiler.h | 12 +++- src/coreclr/jit/compphases.h | 1 + src/coreclr/jit/decomposelongs.cpp | 3 +- src/coreclr/jit/fgbasic.cpp | 4 ++ src/coreclr/jit/fgstmt.cpp | 3 +- src/coreclr/jit/flowgraph.cpp | 89 ++++++++++++++++++++++- src/coreclr/jit/forwardsub.cpp | 2 +- src/coreclr/jit/gentree.cpp | 4 ++ src/coreclr/jit/gentree.h | 31 ++++++++ src/coreclr/jit/gtlist.h | 4 +- src/coreclr/jit/ifconversion.cpp | 22 +++--- src/coreclr/jit/importer.cpp | 15 ---- src/coreclr/jit/lclvars.cpp | 1 - src/coreclr/jit/lir.cpp | 5 +- src/coreclr/jit/liveness.cpp | 1 + src/coreclr/jit/lower.cpp | 31 ++++---- src/coreclr/jit/lower.h | 2 +- src/coreclr/jit/lsra.cpp | 8 +-- src/coreclr/jit/lsraarm64.cpp | 12 +++- src/coreclr/jit/lsrabuild.cpp | 2 +- src/coreclr/jit/lsraxarch.cpp | 13 +++- src/coreclr/jit/morph.cpp | 112 ++++++++++++++++++++--------- src/coreclr/jit/optimizebools.cpp | 89 +++++++++++++++-------- src/coreclr/jit/promotion.cpp | 5 +- src/coreclr/jit/valuenum.cpp | 25 ++++++- 33 files changed, 437 insertions(+), 161 deletions(-) diff --git a/src/coreclr/jit/assertionprop.cpp b/src/coreclr/jit/assertionprop.cpp index cefb3838032959..d55e21b078e9e6 100644 --- a/src/coreclr/jit/assertionprop.cpp +++ b/src/coreclr/jit/assertionprop.cpp @@ -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. // @@ -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)) @@ -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: diff --git a/src/coreclr/jit/codegen.h b/src/coreclr/jit/codegen.h index b5b0d19402f0a3..3511935a062b0a 100644 --- a/src/coreclr/jit/codegen.h +++ b/src/coreclr/jit/codegen.h @@ -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); diff --git a/src/coreclr/jit/codegenarm64.cpp b/src/coreclr/jit/codegenarm64.cpp index cd1b1558d93e64..d4e8d58db741b7 100644 --- a/src/coreclr/jit/codegenarm64.cpp +++ b/src/coreclr/jit/codegenarm64.cpp @@ -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); diff --git a/src/coreclr/jit/codegenarmarch.cpp b/src/coreclr/jit/codegenarmarch.cpp index 041200a2abb343..d015332a76d8b7 100644 --- a/src/coreclr/jit/codegenarmarch.cpp +++ b/src/coreclr/jit/codegenarmarch.cpp @@ -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()); diff --git a/src/coreclr/jit/codegencommon.cpp b/src/coreclr/jit/codegencommon.cpp index 1edfb2ea124a72..9660b0c98be9d7 100644 --- a/src/coreclr/jit/codegencommon.cpp +++ b/src/coreclr/jit/codegencommon.cpp @@ -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(); @@ -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 @@ -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 @@ -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. @@ -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; } @@ -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); diff --git a/src/coreclr/jit/codegenlinear.cpp b/src/coreclr/jit/codegenlinear.cpp index 6a01b6bf3c050e..0b373a94fd3f80 100644 --- a/src/coreclr/jit/codegenlinear.cpp +++ b/src/coreclr/jit/codegenlinear.cpp @@ -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; } diff --git a/src/coreclr/jit/codegenxarch.cpp b/src/coreclr/jit/codegenxarch.cpp index 7c5b0563345a3c..4901ee9e3aa086 100644 --- a/src/coreclr/jit/codegenxarch.cpp +++ b/src/coreclr/jit/codegenxarch.cpp @@ -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(); @@ -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()); diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index f8dd928ec5a769..cd2dfd6b1846a3 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -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); diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index fbcb783ff11893..c280cdb025398f 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -5285,6 +5285,10 @@ class Compiler PhaseStatus fgAddInternal(); +#ifdef SWIFT_SUPPORT + PhaseStatus fgAddSwiftErrorReturns(); +#endif // SWIFT_SUPPORT + enum class FoldResult { FOLD_DID_NOTHING, @@ -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); @@ -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); @@ -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. diff --git a/src/coreclr/jit/compphases.h b/src/coreclr/jit/compphases.h index 4bd236ad7f1962..950313286d26fe 100644 --- a/src/coreclr/jit/compphases.h +++ b/src/coreclr/jit/compphases.h @@ -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) diff --git a/src/coreclr/jit/decomposelongs.cpp b/src/coreclr/jit/decomposelongs.cpp index ea87a996dbb1aa..84802400feeb0a 100644 --- a/src/coreclr/jit/decomposelongs.cpp +++ b/src/coreclr/jit/decomposelongs.cpp @@ -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: diff --git a/src/coreclr/jit/fgbasic.cpp b/src/coreclr/jit/fgbasic.cpp index 61ab0e9fe720c9..8947afbf93ff62 100644 --- a/src/coreclr/jit/fgbasic.cpp +++ b/src/coreclr/jit/fgbasic.cpp @@ -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; diff --git a/src/coreclr/jit/fgstmt.cpp b/src/coreclr/jit/fgstmt.cpp index 2189955a6f9785..946d3818d1c729 100644 --- a/src/coreclr/jit/fgstmt.cpp +++ b/src/coreclr/jit/fgstmt.cpp @@ -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) @@ -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 diff --git a/src/coreclr/jit/flowgraph.cpp b/src/coreclr/jit/flowgraph.cpp index 6f3a3af6f34eaf..6ab0977c55c252 100644 --- a/src/coreclr/jit/flowgraph.cpp +++ b/src/coreclr/jit/flowgraph.cpp @@ -1591,7 +1591,7 @@ GenTree* Compiler::fgCreateMonitorTree(unsigned lvaMonAcquired, unsigned lvaThis } #endif - if (block->KindIs(BBJ_RETURN) && block->lastStmt()->GetRootNode()->gtOper == GT_RETURN) + if (block->KindIs(BBJ_RETURN) && block->lastStmt()->GetRootNode()->OperIs(GT_RETURN)) { GenTreeUnOp* retNode = block->lastStmt()->GetRootNode()->AsUnOp(); GenTree* retExpr = retNode->gtOp1; @@ -2564,6 +2564,93 @@ PhaseStatus Compiler::fgAddInternal() return madeChanges ? PhaseStatus::MODIFIED_EVERYTHING : PhaseStatus::MODIFIED_NOTHING; } +#ifdef SWIFT_SUPPORT +//------------------------------------------------------------------------ +// fgAddSwiftErrorReturns: If this method uses Swift error handling, +// transform all GT_RETURN nodes into GT_SWIFT_ERROR_RET nodes +// to handle returning the error value alongside the normal return value. +// Also transform any GT_LCL_VAR uses of lvaSwiftErrorArg (the SwiftError* parameter) +// into GT_LCL_ADDR uses of lvaSwiftErrorLocal (the SwiftError pseudolocal). +// +// Returns: +// Suitable phase status. +// +PhaseStatus Compiler::fgAddSwiftErrorReturns() +{ + if (lvaSwiftErrorArg == BAD_VAR_NUM) + { + // No Swift error handling in this method + return PhaseStatus::MODIFIED_NOTHING; + } + + assert(lvaSwiftErrorLocal != BAD_VAR_NUM); + assert(info.compCallConv == CorInfoCallConvExtension::Swift); + + struct ReplaceSwiftErrorVisitor final : public GenTreeVisitor + { + enum + { + DoPreOrder = true, + DoLclVarsOnly = true, + }; + + ReplaceSwiftErrorVisitor(Compiler* comp) + : GenTreeVisitor(comp) + { + } + + fgWalkResult PreOrderVisit(GenTree** use, GenTree* user) + { + if ((*use)->AsLclVarCommon()->GetLclNum() == m_compiler->lvaSwiftErrorArg) + { + if (!(*use)->OperIs(GT_LCL_VAR)) + { + BADCODE("Found invalid use of SwiftError* parameter"); + } + + *use = m_compiler->gtNewLclVarAddrNode(m_compiler->lvaSwiftErrorLocal, genActualType(*use)); + } + + return fgWalkResult::WALK_CONTINUE; + } + }; + + ReplaceSwiftErrorVisitor visitor(this); + + for (BasicBlock* block : Blocks()) + { + for (Statement* const stmt : block->Statements()) + { + visitor.WalkTree(stmt->GetRootNodePointer(), nullptr); + } + + if (block->KindIs(BBJ_RETURN)) + { + GenTree* const ret = block->lastNode(); + assert(ret->OperIs(GT_RETURN)); + ret->SetOperRaw(GT_SWIFT_ERROR_RET); + ret->AsOp()->gtOp2 = ret->AsOp()->gtOp1; + + // If this is the merged return block, use the merged return error local as the error operand. + // Else, load the error value from the SwiftError pseudolocal (this will probably get promoted, anyway). + if (block == genReturnBB) + { + assert(genReturnErrorLocal == BAD_VAR_NUM); + genReturnErrorLocal = lvaGrabTemp(true DEBUGARG("Single return block SwiftError value")); + lvaGetDesc(genReturnErrorLocal)->lvType = TYP_I_IMPL; + ret->AsOp()->gtOp1 = gtNewLclvNode(genReturnErrorLocal, TYP_I_IMPL); + } + else + { + ret->AsOp()->gtOp1 = gtNewLclFldNode(lvaSwiftErrorLocal, TYP_I_IMPL, 0); + } + } + } + + return PhaseStatus::MODIFIED_EVERYTHING; +} +#endif // SWIFT_SUPPORT + /*****************************************************************************/ /*****************************************************************************/ diff --git a/src/coreclr/jit/forwardsub.cpp b/src/coreclr/jit/forwardsub.cpp index de4ac5fe8a4758..9d57fa3a4a6dff 100644 --- a/src/coreclr/jit/forwardsub.cpp +++ b/src/coreclr/jit/forwardsub.cpp @@ -798,7 +798,7 @@ bool Compiler::fgForwardSubStatement(Statement* stmt) // interaction between decomposition and RA. // if (compMethodReturnsMultiRegRetType() && (fsv.GetParentNode() != nullptr) && - fsv.GetParentNode()->OperIs(GT_RETURN)) + fsv.GetParentNode()->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET)) { #if defined(TARGET_X86) if (fwdSubNode->TypeGet() == TYP_LONG) diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index c9d258d82db82e..878c02ac1603df 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -9650,6 +9650,10 @@ GenTree* Compiler::gtCloneExpr(GenTree* tree) } break; + case GT_SWIFT_ERROR_RET: + copy = new (this, oper) GenTreeOp(oper, tree->TypeGet(), tree->gtGetOp1(), tree->gtGetOp2()); + break; + default: assert(!GenTree::IsExOp(tree->OperKind()) && tree->OperIsSimple()); // We're in the SimpleOp case, so it's always unary or binary. diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index c5ada2379c787f..c6369121809520 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -1738,6 +1738,9 @@ struct GenTree #endif // defined(TARGET_ARM64) return true; + + case GT_SWIFT_ERROR_RET: + return (gtType == TYP_VOID); default: return false; } @@ -3037,6 +3040,34 @@ struct GenTreeOp : public GenTreeUnOp // then sets the flag GTF_DIV_BY_CNS_OPT and GTF_DONT_CSE on the constant void CheckDivideByConstOptimized(Compiler* comp); + GenTree* GetReturnValue() const + { + assert(OperIs(GT_RETURN, GT_RETFILT, GT_SWIFT_ERROR_RET)); +#ifdef SWIFT_SUPPORT + if (OperIs(GT_SWIFT_ERROR_RET)) + { + return gtOp2; + } +#endif // SWIFT_SUPPORT + + return gtOp1; + } + + void SetReturnValue(GenTree* const retVal) + { + assert(OperIs(GT_RETURN, GT_RETFILT, GT_SWIFT_ERROR_RET)); +#ifdef SWIFT_SUPPORT + if (OperIs(GT_SWIFT_ERROR_RET)) + { + gtOp2 = retVal; + } + else +#endif // SWIFT_SUPPORT + { + gtOp1 = retVal; + } + } + #if !defined(TARGET_64BIT) || defined(TARGET_ARM64) bool IsValidLongMul(); #endif diff --git a/src/coreclr/jit/gtlist.h b/src/coreclr/jit/gtlist.h index 1d442f27673725..2554bb22ef7040 100644 --- a/src/coreclr/jit/gtlist.h +++ b/src/coreclr/jit/gtlist.h @@ -290,7 +290,9 @@ GTNODE(END_LFIN , GenTreeVal ,0,0,GTK_LEAF|GTK_NOVALUE) // End l // Swift interop-specific nodes: //----------------------------------------------------------------------------- -GTNODE(SWIFT_ERROR , GenTree ,0,0,GTK_LEAF) // Error register value post-Swift call +GTNODE(SWIFT_ERROR , GenTree ,0,0,GTK_LEAF) // Error register value post-Swift call +GTNODE(SWIFT_ERROR_RET , GenTreeOp ,0,1,GTK_BINOP|GTK_NOVALUE) // Returns normal return value, and SwiftError pseudolocal's value in REG_SWIFT_ERROR. + // op1 is the error value, and op2 is the return value (or null if the method returns void). //----------------------------------------------------------------------------- // Nodes used by Lower to generate a closer CPU representation of other nodes diff --git a/src/coreclr/jit/ifconversion.cpp b/src/coreclr/jit/ifconversion.cpp index 6d7ead881a91c5..6008bb432550b6 100644 --- a/src/coreclr/jit/ifconversion.cpp +++ b/src/coreclr/jit/ifconversion.cpp @@ -231,7 +231,7 @@ bool OptIfConversionDsc::IfConvertCheckStmts(BasicBlock* fromBlock, IfConvertOpe for (Statement* const stmt : block->Statements()) { GenTree* tree = stmt->GetRootNode(); - switch (tree->gtOper) + switch (tree->OperGet()) { case GT_STORE_LCL_VAR: { @@ -287,7 +287,8 @@ bool OptIfConversionDsc::IfConvertCheckStmts(BasicBlock* fromBlock, IfConvertOpe case GT_RETURN: { - GenTree* op1 = tree->gtGetOp1(); + // GT_SWIFT_ERROR_RET not supported + GenTree* const retVal = tree->gtGetOp1(); // Only allow RETURNs if else conversion is being used. if (!m_doElseConversion) @@ -296,7 +297,7 @@ bool OptIfConversionDsc::IfConvertCheckStmts(BasicBlock* fromBlock, IfConvertOpe } // Only one per operation per block can be conditionally executed. - if (found || op1 == nullptr) + if (found || retVal == nullptr) { return false; } @@ -317,7 +318,7 @@ bool OptIfConversionDsc::IfConvertCheckStmts(BasicBlock* fromBlock, IfConvertOpe #endif // Ensure it won't cause any additional side effects. - if ((op1->gtFlags & (GTF_SIDE_EFFECT | GTF_ORDER_SIDEEFF)) != 0) + if ((retVal->gtFlags & (GTF_SIDE_EFFECT | GTF_ORDER_SIDEEFF)) != 0) { return false; } @@ -326,7 +327,8 @@ bool OptIfConversionDsc::IfConvertCheckStmts(BasicBlock* fromBlock, IfConvertOpe // with the condition (for example, the condition could be an explicit bounds // check and the operand could read an array element). Disallow this except // for some common cases that we know are always side effect free. - if (((m_cond->gtFlags & GTF_ORDER_SIDEEFF) != 0) && !op1->IsInvariant() && !op1->OperIsLocal()) + if (((m_cond->gtFlags & GTF_ORDER_SIDEEFF) != 0) && !retVal->IsInvariant() && + !retVal->OperIsLocal()) { return false; } @@ -634,10 +636,10 @@ bool OptIfConversionDsc::optIfConvert() else { assert(m_mainOper == GT_RETURN); - thenCost = m_thenOperation.node->gtGetOp1()->GetCostEx(); + thenCost = m_thenOperation.node->AsOp()->GetReturnValue()->GetCostEx(); if (m_doElseConversion) { - elseCost = m_elseOperation.node->gtGetOp1()->GetCostEx(); + elseCost = m_elseOperation.node->AsOp()->GetReturnValue()->GetCostEx(); } } @@ -697,8 +699,8 @@ bool OptIfConversionDsc::optIfConvert() assert(m_doElseConversion); assert(m_thenOperation.node->TypeGet() == m_elseOperation.node->TypeGet()); - selectTrueInput = m_elseOperation.node->gtGetOp1(); - selectFalseInput = m_thenOperation.node->gtGetOp1(); + selectTrueInput = m_elseOperation.node->AsOp()->GetReturnValue(); + selectFalseInput = m_thenOperation.node->AsOp()->GetReturnValue(); selectType = genActualType(m_thenOperation.node); } @@ -714,7 +716,7 @@ bool OptIfConversionDsc::optIfConvert() } else { - m_thenOperation.node->AsOp()->gtOp1 = select; + m_thenOperation.node->AsOp()->SetReturnValue(select); } m_comp->gtSetEvalOrder(m_thenOperation.node); m_comp->fgSetStmtSeq(m_thenOperation.stmt); diff --git a/src/coreclr/jit/importer.cpp b/src/coreclr/jit/importer.cpp index 12a612fde1a66a..242162e77e50cb 100644 --- a/src/coreclr/jit/importer.cpp +++ b/src/coreclr/jit/importer.cpp @@ -10464,21 +10464,6 @@ void Compiler::impLoadArg(unsigned ilArgNum, IL_OFFSET offset) { lclNum = lvaArg0Var; } -#ifdef SWIFT_SUPPORT - else if (lclNum == lvaSwiftErrorArg) - { - // Convert any usages of the SwiftError pointer/ref parameter to pointers/refs to the SwiftError pseudolocal - // (set side effect flags so usages of references to pseudolocal aren't removed) - assert(info.compCallConv == CorInfoCallConvExtension::Swift); - assert(lvaSwiftErrorArg != BAD_VAR_NUM); - assert(lvaSwiftErrorLocal != BAD_VAR_NUM); - const var_types type = lvaGetDesc(lvaSwiftErrorArg)->TypeGet(); - GenTree* const swiftErrorLocalRef = gtNewLclVarAddrNode(lvaSwiftErrorLocal, type); - impPushOnStack(swiftErrorLocalRef, typeInfo(type)); - JITDUMP("\nCreated GT_LCL_ADDR of SwiftError pseudolocal\n"); - return; - } -#endif // SWIFT_SUPPORT impLoadVar(lclNum, offset); } diff --git a/src/coreclr/jit/lclvars.cpp b/src/coreclr/jit/lclvars.cpp index 774eee3fe3cb20..3f1031f6a2cccd 100644 --- a/src/coreclr/jit/lclvars.cpp +++ b/src/coreclr/jit/lclvars.cpp @@ -1466,7 +1466,6 @@ bool Compiler::lvaInitSpecialSwiftParam(CORINFO_ARG_LIST_HANDLE argHnd, // Instead, all usages of the SwiftError* parameter will be redirected to this pseudolocal. lvaSwiftErrorLocal = lvaGrabTempWithImplicitUse(false DEBUGARG("SwiftError pseudolocal")); lvaSetStruct(lvaSwiftErrorLocal, typeHnd, false); - lvaSetVarAddrExposed(lvaSwiftErrorLocal DEBUGARG(AddressExposedReason::ESCAPE_ADDRESS)); return true; } diff --git a/src/coreclr/jit/lir.cpp b/src/coreclr/jit/lir.cpp index d172cea22d369a..feabec03d02cad 100644 --- a/src/coreclr/jit/lir.cpp +++ b/src/coreclr/jit/lir.cpp @@ -1792,12 +1792,11 @@ void LIR::InsertBeforeTerminator(BasicBlock* block, LIR::Range&& range) break; case BBJ_SWITCH: - assert((insertionPoint->OperGet() == GT_SWITCH) || (insertionPoint->OperGet() == GT_SWITCH_TABLE)); + assert(insertionPoint->OperIs(GT_SWITCH, GT_SWITCH_TABLE)); break; case BBJ_RETURN: - assert((insertionPoint->OperGet() == GT_RETURN) || (insertionPoint->OperGet() == GT_JMP) || - (insertionPoint->OperGet() == GT_CALL)); + assert(insertionPoint->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET, GT_JMP, GT_CALL)); break; default: diff --git a/src/coreclr/jit/liveness.cpp b/src/coreclr/jit/liveness.cpp index c67e44a3de8b00..521e9891334339 100644 --- a/src/coreclr/jit/liveness.cpp +++ b/src/coreclr/jit/liveness.cpp @@ -1939,6 +1939,7 @@ void Compiler::fgComputeLifeLIR(VARSET_TP& life, BasicBlock* block, VARSET_VALAR case GT_PUTARG_STK: case GT_IL_OFFSET: case GT_KEEPALIVE: + case GT_SWIFT_ERROR_RET: // Never remove these nodes, as they are always side-effecting. // // NOTE: the only side-effect of some of these nodes (GT_CMP, GT_SUB_HI) is a write to the flags diff --git a/src/coreclr/jit/lower.cpp b/src/coreclr/jit/lower.cpp index 9790d67a93f5f6..cb8eb5d6a97a0d 100644 --- a/src/coreclr/jit/lower.cpp +++ b/src/coreclr/jit/lower.cpp @@ -520,8 +520,9 @@ GenTree* Lowering::LowerNode(GenTree* node) LowerJmpMethod(node); break; + case GT_SWIFT_ERROR_RET: case GT_RETURN: - LowerRet(node->AsUnOp()); + LowerRet(node->AsOp()); break; case GT_RETURNTRAP: @@ -4541,22 +4542,22 @@ void Lowering::LowerJmpMethod(GenTree* jmp) } } -// Lower GT_RETURN node to insert PInvoke method epilog if required. -void Lowering::LowerRet(GenTreeUnOp* ret) +// Lower GT_RETURN/GT_SWIFT_ERROR_RET node to insert PInvoke method epilog if required. +void Lowering::LowerRet(GenTreeOp* ret) { - assert(ret->OperGet() == GT_RETURN); + assert(ret->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET)); - JITDUMP("lowering GT_RETURN\n"); + JITDUMP("lowering return node\n"); DISPNODE(ret); JITDUMP("============\n"); - GenTree* retVal = ret->gtGetOp1(); + GenTree* retVal = ret->GetReturnValue(); // There are two kinds of retyping: // - A simple bitcast can be inserted when: // - We're returning a floating type as an integral type or vice-versa, or // - If we're returning a struct as a primitive type, we change the type of // 'retval' in 'LowerRetStructLclVar()' - bool needBitcast = (ret->TypeGet() != TYP_VOID) && !varTypeUsesSameRegType(ret, ret->gtGetOp1()); + bool needBitcast = (ret->TypeGet() != TYP_VOID) && !varTypeUsesSameRegType(ret, retVal); bool doPrimitiveBitcast = false; if (needBitcast) { @@ -4572,7 +4573,7 @@ void Lowering::LowerRet(GenTreeUnOp* ret) #endif GenTree* bitcast = comp->gtNewBitCastNode(ret->TypeGet(), retVal); - ret->gtOp1 = bitcast; + ret->SetReturnValue(bitcast); BlockRange().InsertBefore(ret, bitcast); ContainCheckBitCast(bitcast); } @@ -4871,7 +4872,7 @@ void Lowering::LowerRetStruct(GenTreeUnOp* ret) return; } - assert(ret->OperIs(GT_RETURN)); + assert(ret->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET)); assert(varTypeIsStruct(ret)); GenTree* retVal = ret->gtGetOp1(); @@ -4963,8 +4964,8 @@ void Lowering::LowerRetStruct(GenTreeUnOp* ret) void Lowering::LowerRetSingleRegStructLclVar(GenTreeUnOp* ret) { assert(!comp->compMethodReturnsMultiRegRetType()); - assert(ret->OperIs(GT_RETURN)); - GenTreeLclVarCommon* lclVar = ret->gtGetOp1()->AsLclVar(); + assert(ret->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET)); + GenTreeLclVarCommon* lclVar = ret->AsOp()->GetReturnValue()->AsLclVar(); assert(lclVar->OperIs(GT_LCL_VAR)); unsigned lclNum = lclVar->GetLclNum(); LclVarDsc* varDsc = comp->lvaGetDesc(lclNum); @@ -5012,7 +5013,7 @@ void Lowering::LowerRetSingleRegStructLclVar(GenTreeUnOp* ret) if (!varTypeUsesSameRegType(ret, lclVarType)) { GenTree* bitcast = comp->gtNewBitCastNode(ret->TypeGet(), ret->gtOp1); - ret->gtOp1 = bitcast; + ret->AsOp()->SetReturnValue(bitcast); BlockRange().InsertBefore(ret, bitcast); ContainCheckBitCast(bitcast); } @@ -8155,12 +8156,12 @@ void Lowering::ContainCheckLclHeap(GenTreeOp* node) // void Lowering::ContainCheckRet(GenTreeUnOp* ret) { - assert(ret->OperIs(GT_RETURN)); + assert(ret->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET)); #if !defined(TARGET_64BIT) if (ret->TypeGet() == TYP_LONG) { - GenTree* op1 = ret->gtGetOp1(); + GenTree* op1 = ret->AsOp()->GetReturnValue(); noway_assert(op1->OperGet() == GT_LONG); MakeSrcContained(ret, op1); } @@ -8168,7 +8169,7 @@ void Lowering::ContainCheckRet(GenTreeUnOp* ret) #if FEATURE_MULTIREG_RET if (ret->TypeIs(TYP_STRUCT)) { - GenTree* op1 = ret->gtGetOp1(); + GenTree* op1 = ret->AsOp()->GetReturnValue(); // op1 must be either a lclvar or a multi-reg returning call if (op1->OperGet() == GT_LCL_VAR) { diff --git a/src/coreclr/jit/lower.h b/src/coreclr/jit/lower.h index 0538c661e16260..055f425b664566 100644 --- a/src/coreclr/jit/lower.h +++ b/src/coreclr/jit/lower.h @@ -154,7 +154,7 @@ class Lowering final : public Phase bool TryLowerConditionToFlagsNode(GenTree* parent, GenTree* condition, GenCondition* code); GenTreeCC* LowerNodeCC(GenTree* node, GenCondition condition); void LowerJmpMethod(GenTree* jmp); - void LowerRet(GenTreeUnOp* ret); + void LowerRet(GenTreeOp* ret); void LowerStoreLocCommon(GenTreeLclVarCommon* lclVar); void LowerRetStruct(GenTreeUnOp* ret); void LowerRetSingleRegStructLclVar(GenTreeUnOp* ret); diff --git a/src/coreclr/jit/lsra.cpp b/src/coreclr/jit/lsra.cpp index f96a2e6a746a7a..ebcb21fff18bc3 100644 --- a/src/coreclr/jit/lsra.cpp +++ b/src/coreclr/jit/lsra.cpp @@ -8500,16 +8500,16 @@ void LinearScan::insertMove( noway_assert(!blockRange.IsEmpty()); GenTree* branch = lastNode; - assert(branch->OperIsConditionalJump() || branch->OperGet() == GT_SWITCH_TABLE || - branch->OperGet() == GT_SWITCH); + assert(branch->OperIsConditionalJump() || branch->OperIs(GT_SWITCH_TABLE, GT_SWITCH)); blockRange.InsertBefore(branch, std::move(treeRange)); } else { // These block kinds don't have a branch at the end. - assert((lastNode == nullptr) || (!lastNode->OperIsConditionalJump() && - !lastNode->OperIs(GT_SWITCH_TABLE, GT_SWITCH, GT_RETURN, GT_RETFILT))); + assert((lastNode == nullptr) || + (!lastNode->OperIsConditionalJump() && + !lastNode->OperIs(GT_SWITCH_TABLE, GT_SWITCH, GT_RETURN, GT_RETFILT, GT_SWIFT_ERROR_RET))); blockRange.InsertAtEnd(std::move(treeRange)); } } diff --git a/src/coreclr/jit/lsraarm64.cpp b/src/coreclr/jit/lsraarm64.cpp index 1096d7f11701c5..dfcebf4392c53c 100644 --- a/src/coreclr/jit/lsraarm64.cpp +++ b/src/coreclr/jit/lsraarm64.cpp @@ -582,7 +582,7 @@ int LinearScan::BuildNode(GenTree* tree) { assert(!tree->isContained()); int srcCount; - int dstCount = 0; + int dstCount; regMaskTP killMask = RBM_NONE; bool isLocalDefUse = false; @@ -741,6 +741,16 @@ int LinearScan::BuildNode(GenTree* tree) BuildDefsWithKills(tree, 0, RBM_NONE, killMask); break; +#ifdef SWIFT_SUPPORT + case GT_SWIFT_ERROR_RET: + BuildUse(tree->gtGetOp1(), RBM_SWIFT_ERROR); + // Plus one for error register + srcCount = BuildReturn(tree) + 1; + killMask = getKillSetForReturn(); + BuildDefsWithKills(tree, 0, RBM_NONE, killMask); + break; +#endif // SWIFT_SUPPORT + case GT_RETFILT: assert(dstCount == 0); if (tree->TypeGet() == TYP_VOID) diff --git a/src/coreclr/jit/lsrabuild.cpp b/src/coreclr/jit/lsrabuild.cpp index 15726e201d8a5e..d1825f44696611 100644 --- a/src/coreclr/jit/lsrabuild.cpp +++ b/src/coreclr/jit/lsrabuild.cpp @@ -4054,7 +4054,7 @@ int LinearScan::BuildSimple(GenTree* tree) // int LinearScan::BuildReturn(GenTree* tree) { - GenTree* op1 = tree->gtGetOp1(); + GenTree* op1 = tree->AsOp()->GetReturnValue(); #if !defined(TARGET_64BIT) if (tree->TypeGet() == TYP_LONG) diff --git a/src/coreclr/jit/lsraxarch.cpp b/src/coreclr/jit/lsraxarch.cpp index 7fe119ccfd165c..6abc86aab0ed5e 100644 --- a/src/coreclr/jit/lsraxarch.cpp +++ b/src/coreclr/jit/lsraxarch.cpp @@ -46,7 +46,7 @@ int LinearScan::BuildNode(GenTree* tree) { assert(!tree->isContained()); int srcCount; - int dstCount = 0; + int dstCount; regMaskTP killMask = RBM_NONE; bool isLocalDefUse = false; @@ -193,6 +193,16 @@ int LinearScan::BuildNode(GenTree* tree) BuildDefsWithKills(tree, 0, RBM_NONE, killMask); break; +#ifdef SWIFT_SUPPORT + case GT_SWIFT_ERROR_RET: + BuildUse(tree->gtGetOp1(), RBM_SWIFT_ERROR); + // Plus one for error register + srcCount = BuildReturn(tree) + 1; + killMask = getKillSetForReturn(); + BuildDefsWithKills(tree, 0, RBM_NONE, killMask); + break; +#endif // SWIFT_SUPPORT + case GT_RETFILT: assert(dstCount == 0); if (tree->TypeGet() == TYP_VOID) @@ -733,6 +743,7 @@ bool LinearScan::isRMWRegOper(GenTree* tree) #ifdef TARGET_X86 case GT_LONG: #endif + case GT_SWIFT_ERROR_RET: return false; case GT_ADD: diff --git a/src/coreclr/jit/morph.cpp b/src/coreclr/jit/morph.cpp index fe5cb92ebff176..fd06ede79cb946 100644 --- a/src/coreclr/jit/morph.cpp +++ b/src/coreclr/jit/morph.cpp @@ -8737,34 +8737,61 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA return fgMorphIntoHelperCall(tree, helper, true /* morphArgs */, op1, op2); case GT_RETURN: + case GT_SWIFT_ERROR_RET: + { + GenTree* retVal = tree->AsOp()->GetReturnValue(); + if (!tree->TypeIs(TYP_VOID)) { - if (op1->OperIs(GT_LCL_FLD)) + if (retVal->OperIs(GT_LCL_FLD)) { - op1 = fgMorphRetInd(tree->AsUnOp()); + retVal = fgMorphRetInd(tree->AsOp()); } - fgTryReplaceStructLocalWithField(op1); + fgTryReplaceStructLocalWithField(retVal); } // normalize small integer return values - if (fgGlobalMorph && varTypeIsSmall(info.compRetType) && (op1 != nullptr) && !op1->TypeIs(TYP_VOID) && - fgCastNeeded(op1, info.compRetType)) + if (fgGlobalMorph && varTypeIsSmall(info.compRetType) && (retVal != nullptr) && !retVal->TypeIs(TYP_VOID) && + fgCastNeeded(retVal, info.compRetType)) { +#ifdef SWIFT_SUPPORT + // Morph error operand if tree is a GT_SWIFT_ERROR_RET node + if (tree->OperIs(GT_SWIFT_ERROR_RET)) + { + GenTree* const errorVal = fgMorphTree(tree->gtGetOp1()); + tree->AsOp()->gtOp1 = errorVal; + + // Propagate side effect flags + tree->SetAllEffectsFlags(errorVal); + } +#endif // SWIFT_SUPPORT + // Small-typed return values are normalized by the callee - op1 = gtNewCastNode(TYP_INT, op1, false, info.compRetType); + retVal = gtNewCastNode(TYP_INT, retVal, false, info.compRetType); // Propagate GTF_COLON_COND - op1->gtFlags |= (tree->gtFlags & GTF_COLON_COND); + retVal->gtFlags |= (tree->gtFlags & GTF_COLON_COND); - tree->AsOp()->gtOp1 = fgMorphTree(op1); + retVal = fgMorphTree(retVal); + tree->AsOp()->SetReturnValue(retVal); // Propagate side effect flags - tree->SetAllEffectsFlags(tree->AsOp()->gtGetOp1()); + tree->SetAllEffectsFlags(retVal); return tree; } + + if (tree->OperIs(GT_RETURN)) + { + op1 = retVal; + } + else + { + op2 = retVal; + } break; + } case GT_EQ: case GT_NE: @@ -9631,15 +9658,18 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA break; case GT_RETURN: - - // Retry updating op1 to a field -- assertion - // prop done when morphing op1 changed the local. + case GT_SWIFT_ERROR_RET: + { + // Retry updating return operand to a field -- assertion + // prop done when morphing this operand changed the local. // - if (op1 != nullptr) + GenTree* const retVal = tree->AsOp()->GetReturnValue(); + if (retVal != nullptr) { - fgTryReplaceStructLocalWithField(op1); + fgTryReplaceStructLocalWithField(retVal); } break; + } default: break; @@ -9694,7 +9724,7 @@ GenTree* Compiler::fgMorphSmpOp(GenTree* tree, MorphAddrContext* mac, bool* optA // tree - tree to examine and possibly modify // // Notes: -// Currently only called when the tree parent is a GT_RETURN. +// Currently only called when the tree parent is a GT_RETURN/GT_SWIFT_ERROR_RET. // void Compiler::fgTryReplaceStructLocalWithField(GenTree* tree) { @@ -11538,16 +11568,16 @@ GenTree* Compiler::fgPropagateCommaThrow(GenTree* parent, GenTreeOp* commaThrow, // fgMorphRetInd: Try to get rid of extra local indirections in a return tree. // // Arguments: -// node - The return node that uses an local field. +// node - The return node that uses a local field. // // Return Value: -// the original op1 of the ret if there was no optimization or an optimized new op1. +// the original return operand if there was no optimization, or an optimized new return operand. // -GenTree* Compiler::fgMorphRetInd(GenTreeUnOp* ret) +GenTree* Compiler::fgMorphRetInd(GenTreeOp* ret) { - assert(ret->OperIs(GT_RETURN)); - assert(ret->gtGetOp1()->OperIs(GT_LCL_FLD)); - GenTreeLclFld* lclFld = ret->gtGetOp1()->AsLclFld(); + assert(ret->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET)); + assert(ret->GetReturnValue()->OperIs(GT_LCL_FLD)); + GenTreeLclFld* lclFld = ret->GetReturnValue()->AsLclFld(); unsigned lclNum = lclFld->GetLclNum(); if (fgGlobalMorph && varTypeIsStruct(lclFld) && !lvaIsImplicitByRefLocal(lclNum)) @@ -14071,7 +14101,7 @@ void Compiler::fgMergeBlockReturn(BasicBlock* block) Statement* lastStmt = block->lastStmt(); GenTree* ret = (lastStmt != nullptr) ? lastStmt->GetRootNode() : nullptr; - if ((ret != nullptr) && (ret->OperGet() == GT_RETURN) && ((ret->gtFlags & GTF_RET_MERGED) != 0)) + if ((ret != nullptr) && ret->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET) && ((ret->gtFlags & GTF_RET_MERGED) != 0)) { // This return was generated during epilog merging, so leave it alone } @@ -14092,27 +14122,40 @@ void Compiler::fgMergeBlockReturn(BasicBlock* block) fgReturnCount--; } +#ifdef SWIFT_SUPPORT + // If merging GT_SWIFT_ERROR_RET nodes, ensure the error operand is stored to the merged return error local, + // so the correct error value is retrieved in the merged return block. + if ((ret != nullptr) && ret->OperIs(GT_SWIFT_ERROR_RET)) + { + assert(genReturnErrorLocal != BAD_VAR_NUM); + const DebugInfo& di = lastStmt->GetDebugInfo(); + GenTree* swiftErrorStore = gtNewTempStore(genReturnErrorLocal, ret->gtGetOp1()); + Statement* const newStmt = gtNewStmt(swiftErrorStore, di); + fgInsertStmtBefore(block, lastStmt, newStmt); + } +#endif // SWIFT_SUPPORT + if (genReturnLocal != BAD_VAR_NUM) { - // replace the GT_RETURN node to be a STORE_LCL_VAR that stores the return value into genReturnLocal. + // replace the GT_RETURN/GT_SWIFT_ERROR_RET node to be a STORE_LCL_VAR that stores the return value into + // genReturnLocal. // Method must be returning a value other than TYP_VOID. noway_assert(compMethodHasRetVal()); - // This block must be ending with a GT_RETURN + // This block must be ending with a GT_RETURN/GT_SWIFT_ERROR_RET noway_assert(lastStmt != nullptr); noway_assert(lastStmt->GetNextStmt() == nullptr); noway_assert(ret != nullptr); - // GT_RETURN must have non-null operand as the method is returning the value assigned to + // Return node must have non-null operand as the method is returning the value assigned to // genReturnLocal - noway_assert(ret->OperGet() == GT_RETURN); - noway_assert(ret->gtGetOp1() != nullptr); + GenTree* const retVal = ret->AsOp()->GetReturnValue(); + noway_assert(retVal != nullptr); Statement* pAfterStatement = lastStmt; const DebugInfo& di = lastStmt->GetDebugInfo(); - GenTree* tree = - gtNewTempStore(genReturnLocal, ret->gtGetOp1(), CHECK_SPILL_NONE, &pAfterStatement, di, block); + GenTree* tree = gtNewTempStore(genReturnLocal, retVal, CHECK_SPILL_NONE, &pAfterStatement, di, block); if (tree->OperIsCopyBlkOp()) { tree = fgMorphCopyBlock(tree); @@ -14135,16 +14178,17 @@ void Compiler::fgMergeBlockReturn(BasicBlock* block) lastStmt = newStmt; } } - else if (ret != nullptr && ret->OperGet() == GT_RETURN) + else if ((ret != nullptr) && ret->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET)) { - // This block ends with a GT_RETURN + // This block ends with a GT_RETURN/GT_SWIFT_ERROR_RET noway_assert(lastStmt != nullptr); noway_assert(lastStmt->GetNextStmt() == nullptr); - // Must be a void GT_RETURN with null operand; delete it as this block branches to oneReturn - // block + // Must be a void return node with null operand; delete it as this block branches to + // oneReturn block + GenTree* const retVal = ret->AsOp()->GetReturnValue(); noway_assert(ret->TypeGet() == TYP_VOID); - noway_assert(ret->gtGetOp1() == nullptr); + noway_assert(retVal == nullptr); if (opts.compDbgCode && lastStmt->GetDebugInfo().IsValid()) { diff --git a/src/coreclr/jit/optimizebools.cpp b/src/coreclr/jit/optimizebools.cpp index 1e5d5a00b107c0..07da4731252ff6 100644 --- a/src/coreclr/jit/optimizebools.cpp +++ b/src/coreclr/jit/optimizebools.cpp @@ -18,15 +18,43 @@ XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX /*****************************************************************************/ //----------------------------------------------------------------------------- -// OptTestInfo: Member of OptBoolsDsc struct used to test if a GT_JTRUE or GT_RETURN node +// OptTestInfo: Member of OptBoolsDsc struct used to test if a GT_JTRUE or return node // is a boolean comparison // struct OptTestInfo { Statement* testStmt; // Last statement of the basic block - GenTree* testTree; // The root node of the testStmt (GT_JTRUE or GT_RETURN). + GenTree* testTree; // The root node of the testStmt (GT_JTRUE or GT_RETURN/GT_SWIFT_ERROR_RET). GenTree* compTree; // The compare node (i.e. GT_EQ or GT_NE node) of the testTree bool isBool; // If the compTree is boolean expression + + GenTree* GetTestOp() const + { + assert(testTree != nullptr); + + if (testTree->OperIs(GT_JTRUE)) + { + return testTree->gtGetOp1(); + } + + assert(testTree->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET)); + return testTree->AsOp()->GetReturnValue(); + } + + void SetTestOp(GenTree* const op) + { + assert(testTree != nullptr); + + if (testTree->OperIs(GT_JTRUE)) + { + testTree->AsOp()->gtOp1 = op; + } + else + { + assert(testTree->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET)); + testTree->AsOp()->SetReturnValue(op); + } + } }; //----------------------------------------------------------------------------- @@ -100,12 +128,12 @@ class OptBoolsDsc // For example, (x == 0 && y == 0 && z == 0) generates // B1: GT_JTRUE (BBJ_COND), jump to B4 // B2: GT_JTRUE (BBJ_COND), jump to B4 -// B3: GT_RETURN (BBJ_RETURN) -// B4: GT_RETURN (BBJ_RETURN) +// B3: GT_RETURN/GT_SWIFT_ERROR_RET (BBJ_RETURN) +// B4: GT_RETURN/GT_SWIFT_ERROR_RET (BBJ_RETURN) // and B1 and B2 are folded into B1: // B1: GT_JTRUE (BBJ_COND), jump to B4 -// B3: GT_RETURN (BBJ_RETURN) -// B4: GT_RETURN (BBJ_RETURN) +// B3: GT_RETURN/GT_SWIFT_ERROR_RET (BBJ_RETURN) +// B4: GT_RETURN/GT_SWIFT_ERROR_RET (BBJ_RETURN) // // Case 2: if B2->FalseTargetIs(B1->GetTarget()), it transforms // B1 : brtrue(t1, B3) @@ -234,8 +262,7 @@ bool OptBoolsDsc::optOptimizeBoolsCondBlock() cmpOp = GT_EQ; } else if (m_testInfo1.compTree->gtOper == GT_LT && m_testInfo2.compTree->gtOper == GT_LT && - (!m_testInfo1.testTree->AsOp()->gtOp1->IsUnsigned() && - !m_testInfo2.testTree->AsOp()->gtOp1->IsUnsigned())) + (!m_testInfo1.GetTestOp()->IsUnsigned() && !m_testInfo2.GetTestOp()->IsUnsigned())) { // t1:c1<0 t2:c2<0 ==> Branch to BX if either value < 0 // So we will branch to BX if (c1|c2)<0 @@ -297,8 +324,7 @@ bool OptBoolsDsc::optOptimizeBoolsCondBlock() cmpOp = GT_NE; } else if (m_testInfo1.compTree->gtOper == GT_LT && m_testInfo2.compTree->gtOper == GT_GE && - (!m_testInfo1.testTree->AsOp()->gtOp1->IsUnsigned() && - !m_testInfo2.testTree->AsOp()->gtOp1->IsUnsigned())) + (!m_testInfo1.GetTestOp()->IsUnsigned() && !m_testInfo2.GetTestOp()->IsUnsigned())) { // t1:c1<0 t2:c2>=0 ==> Branch to BX if both values >= 0 // So we will branch to BX if (c1|c2)>=0 @@ -1023,7 +1049,7 @@ bool OptBoolsDsc::optOptimizeCompareChainCondBlock() m_comp->gtNewOperNode(GT_NE, TYP_INT, chainedConditions, m_comp->gtNewZeroConNode(TYP_INT)); // Wire the chain into the second block - m_testInfo2.testTree->AsOp()->gtOp1 = testcondition; + m_testInfo2.SetTestOp(testcondition); m_testInfo2.testTree->AsOp()->gtFlags |= (testcondition->gtFlags & GTF_ALL_EFFECT); m_comp->gtSetEvalOrder(m_testInfo2.testTree); m_comp->fgSetStmtSeq(s2); @@ -1062,7 +1088,7 @@ bool OptBoolsDsc::optOptimizeCompareChainCondBlock() // // Notes: // This method checks if the second (and third block for cond/return/return case) contains only one statement, -// and checks if tree operators are of the right type, e.g, GT_JTRUE, GT_RETURN. +// and checks if tree operators are of the right type, e.g, GT_JTRUE, GT_RETURN, GT_SWIFT_ERROR_RET. // // On entry, m_b1, m_b2 are set and m_b3 is set for cond/return/return case. // If it passes all the conditions, m_testInfo1.testTree, m_testInfo2.testTree and m_t3 are set @@ -1091,7 +1117,7 @@ Statement* OptBoolsDsc::optOptimizeBoolsChkBlkCond() Statement* s1 = m_b1->lastStmt(); GenTree* testTree1 = s1->GetRootNode(); - assert(testTree1->gtOper == GT_JTRUE); + assert(testTree1->OperIs(GT_JTRUE)); // The second and the third block must contain a single statement @@ -1105,11 +1131,11 @@ Statement* OptBoolsDsc::optOptimizeBoolsChkBlkCond() if (!optReturnBlock) { - assert(testTree2->gtOper == GT_JTRUE); + assert(testTree2->OperIs(GT_JTRUE)); } else { - if (testTree2->gtOper != GT_RETURN) + if (!testTree2->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET)) { return nullptr; } @@ -1121,7 +1147,7 @@ Statement* OptBoolsDsc::optOptimizeBoolsChkBlkCond() } GenTree* testTree3 = s3->GetRootNode(); - if (testTree3->gtOper != GT_RETURN) + if (!testTree3->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET)) { return nullptr; } @@ -1132,12 +1158,13 @@ Statement* OptBoolsDsc::optOptimizeBoolsChkBlkCond() } // The third block is Return with "CNS_INT int 0/1" - if (testTree3->AsOp()->gtOp1->gtOper != GT_CNS_INT) + GenTree* const retVal = testTree3->AsOp()->GetReturnValue(); + if (!retVal->OperIs(GT_CNS_INT)) { return nullptr; } - if (testTree3->AsOp()->gtOp1->gtType != TYP_INT) + if (!retVal->TypeIs(TYP_INT)) { return nullptr; } @@ -1236,10 +1263,10 @@ void OptBoolsDsc::optOptimizeBoolsUpdateTrees() t1Comp->AsOp()->gtOp2->gtType = m_foldType; // Could have been varTypeIsGC() if (optReturnBlock) { - // Update tree when m_b1 is BBJ_COND and m_b2 and m_b3 are GT_RETURN (BBJ_RETURN) + // Update tree when m_b1 is BBJ_COND and m_b2 and m_b3 are GT_RETURN/GT_SWIFT_ERROR_RET (BBJ_RETURN) t1Comp->AsOp()->gtOp2->AsIntCon()->gtIconVal = 0; - m_testInfo1.testTree->gtOper = GT_RETURN; - m_testInfo1.testTree->gtType = m_testInfo2.testTree->gtType; + m_testInfo1.testTree->gtOper = m_testInfo2.testTree->OperGet(); + m_testInfo1.testTree->gtType = m_testInfo2.testTree->TypeGet(); // Update the return count of flow graph assert(m_comp->fgReturnCount >= 2); @@ -1388,10 +1415,10 @@ void OptBoolsDsc::optOptimizeBoolsUpdateTrees() // // For example, (x==0 && y==0) generates: // B1: GT_JTRUE (BBJ_COND), jumps to B3 -// B2: GT_RETURN (BBJ_RETURN) -// B3: GT_RETURN (BBJ_RETURN), +// B2: GT_RETURN/GT_SWIFT_ERROR (BBJ_RETURN) +// B3: GT_RETURN/GT_SWIFT_ERROR (BBJ_RETURN), // and it is folded into -// B1: GT_RETURN (BBJ_RETURN) +// B1: GT_RETURN/GT_SWIFT_ERROR (BBJ_RETURN) // bool OptBoolsDsc::optOptimizeBoolsReturnBlock(BasicBlock* b3) { @@ -1530,7 +1557,7 @@ bool OptBoolsDsc::optOptimizeBoolsReturnBlock(BasicBlock* b3) } else if ((m_testInfo1.compTree->gtOper == GT_LT && m_testInfo2.compTree->gtOper == GT_GE) && (it1val == 0 && it2val == 0 && it3val == 0) && - (!m_testInfo1.testTree->AsOp()->gtOp1->IsUnsigned() && !m_testInfo2.testTree->AsOp()->gtOp1->IsUnsigned())) + (!m_testInfo1.GetTestOp()->IsUnsigned() && !m_testInfo2.GetTestOp()->IsUnsigned())) { // Case: x >= 0 && y >= 0 // t1:c1<0 t2:c2>=0 t3:c3==0 @@ -1559,7 +1586,7 @@ bool OptBoolsDsc::optOptimizeBoolsReturnBlock(BasicBlock* b3) } else if ((m_testInfo1.compTree->gtOper == GT_LT && m_testInfo2.compTree->gtOper == GT_LT) && (it1val == 0 && it2val == 0 && it3val == 1) && - (!m_testInfo1.testTree->AsOp()->gtOp1->IsUnsigned() && !m_testInfo2.testTree->AsOp()->gtOp1->IsUnsigned())) + (!m_testInfo1.GetTestOp()->IsUnsigned() && !m_testInfo2.GetTestOp()->IsUnsigned())) { // Case: x < 0 || y < 0 // t1:c1<0 t2:c2<0 t3:c3==1 @@ -1676,7 +1703,7 @@ void OptBoolsDsc::optOptimizeBoolsGcStress() // On success, compTree is set to the compare node (i.e. GT_EQ or GT_NE or GT_LT or GT_GE) of the testTree. // isBool is set to true if the comparand (i.e., operand 1 of compTree is boolean. Otherwise, false. // -// Given a GT_JTRUE or GT_RETURN node, this method checks if it is a boolean comparison +// Given a GT_JTRUE or GT_RETURN/GT_SWIFT_ERROR_RET node, this method checks if it is a boolean comparison // of the form "if (boolVal ==/!=/>=/< 0/1)".This is translated into // a GT_EQ/GT_NE/GT_GE/GT_LT node with "opr1" being a boolean lclVar and "opr2" the const 0/1. // @@ -1687,8 +1714,8 @@ GenTree* OptBoolsDsc::optIsBoolComp(OptTestInfo* pOptTest) { pOptTest->isBool = false; - assert(pOptTest->testTree->gtOper == GT_JTRUE || pOptTest->testTree->gtOper == GT_RETURN); - GenTree* cond = pOptTest->testTree->AsOp()->gtOp1; + assert(pOptTest->testTree->OperIs(GT_JTRUE, GT_RETURN, GT_SWIFT_ERROR_RET)); + GenTree* cond = pOptTest->GetTestOp(); // The condition must be "!= 0" or "== 0" or >=0 or <= 0 or > 0 or < 0 if (!cond->OperIs(GT_EQ, GT_NE, GT_LT, GT_GT, GT_GE, GT_LE)) @@ -1749,13 +1776,13 @@ GenTree* OptBoolsDsc::optIsBoolComp(OptTestInfo* pOptTest) } //----------------------------------------------------------------------------- -// optOptimizeBools: Folds boolean conditionals for GT_JTRUE/GT_RETURN nodes +// optOptimizeBools: Folds boolean conditionals for GT_JTRUE/GT_RETURN/GT_SWIFT_ERROR_RET nodes // // Returns: // suitable phase status // // Notes: -// If the operand of GT_JTRUE/GT_RETURN node is GT_EQ/GT_NE/GT_GE/GT_LE/GT_GT/GT_LT of the form +// If the operand of GT_JTRUE/GT_RETURN/GT_SWIFT_ERROR_RET node is GT_EQ/GT_NE/GT_GE/GT_LE/GT_GT/GT_LT of the form // "if (boolVal ==/!=/>=/< 0/1)", the GT_EQ/GT_NE/GT_GE/GT_LE/GT_GT/GT_LT nodes are translated into a // GT_EQ/GT_NE/GT_GE/GT_LE/GT_GT/GT_LT node with // "op1" being a boolean GT_OR/GT_AND lclVar and diff --git a/src/coreclr/jit/promotion.cpp b/src/coreclr/jit/promotion.cpp index e02a5f0e06bab3..c22bf74e8309f6 100644 --- a/src/coreclr/jit/promotion.cpp +++ b/src/coreclr/jit/promotion.cpp @@ -1446,9 +1446,8 @@ class LocalsUseVisitor : public GenTreeVisitor flags |= AccessKindFlags::IsStoreSource; } - if (user->OperIs(GT_RETURN)) + if (user->OperIs(GT_RETURN, GT_SWIFT_ERROR_RET)) { - assert(user->gtGetOp1()->gtEffectiveVal() == lcl); flags |= AccessKindFlags::IsReturned; } #endif @@ -2546,7 +2545,7 @@ void ReplaceVisitor::ReplaceLocal(GenTree** use, GenTree* user) JITDUMP("Processing struct use [%06u] of V%02u.[%03u..%03u)\n", Compiler::dspTreeID(lcl), lclNum, offs, offs + lcl->GetLayout(m_compiler)->GetSize()); - assert(effectiveUser->OperIs(GT_CALL, GT_RETURN)); + assert(effectiveUser->OperIs(GT_CALL, GT_RETURN, GT_SWIFT_ERROR_RET)); unsigned size = lcl->GetLayout(m_compiler)->GetSize(); WriteBackBeforeUse(use, lclNum, lcl->GetLclOffs(), size); diff --git a/src/coreclr/jit/valuenum.cpp b/src/coreclr/jit/valuenum.cpp index a38a5bdede2bc9..1fd85a96840d9b 100644 --- a/src/coreclr/jit/valuenum.cpp +++ b/src/coreclr/jit/valuenum.cpp @@ -9571,7 +9571,8 @@ static genTreeOps genTreeOpsIllegalAsVNFunc[] = {GT_IND, // When we do heap memo GT_NOP, // These control-flow operations need no values. - GT_JTRUE, GT_RETURN, GT_SWITCH, GT_RETFILT, GT_CKFINITE}; + GT_JTRUE, GT_RETURN, GT_SWITCH, GT_RETFILT, GT_CKFINITE, + GT_SWIFT_ERROR_RET}; void ValueNumStore::ValidateValueNumStoreStatics() { @@ -11698,6 +11699,28 @@ void Compiler::fgValueNumberTree(GenTree* tree) } break; + // GT_SWIFT_ERROR_RET is similar to GT_RETURN, but it's a binary node, and its return value is op2. + case GT_SWIFT_ERROR_RET: + if (tree->gtGetOp2() != nullptr) + { + // We have a return value and an error value. + ValueNumPair vnp; + ValueNumPair op1Xvnp; + ValueNumPair op2Xvnp; + vnStore->VNPUnpackExc(tree->gtGetOp1()->gtVNPair, &vnp, &op1Xvnp); + vnStore->VNPUnpackExc(tree->gtGetOp2()->gtVNPair, &vnp, &op2Xvnp); + + const ValueNumPair excSetPair = vnStore->VNPExcSetUnion(op1Xvnp, op2Xvnp); + tree->gtVNPair = vnStore->VNPWithExc(vnStore->VNPForVoid(), excSetPair); + } + else + { + // We only have the error value. + tree->gtVNPair = vnStore->VNPWithExc(vnStore->VNPForVoid(), + vnStore->VNPExceptionSet(tree->gtGetOp1()->gtVNPair)); + } + break; + // BOX and CKFINITE are passthrough nodes (like NOP). We'll add the exception for the latter later. case GT_BOX: case GT_CKFINITE: