Skip to content

Commit

Permalink
Fold const expression for HW Intrinsic imm const arg during importation
Browse files Browse the repository at this point in the history
Fixes #19888

This enables expansin of imm HW Intrinsics which otherwise would be emitted
with jump tables. Significantly improves user experience and performance
  • Loading branch information
4creators committed Sep 11, 2018
1 parent d1ce100 commit e898a55
Show file tree
Hide file tree
Showing 2 changed files with 134 additions and 3 deletions.
1 change: 1 addition & 0 deletions src/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -3279,6 +3279,7 @@ class Compiler
GenTree* getArgForHWIntrinsic(var_types argType, CORINFO_CLASS_HANDLE argClass);
GenTree* impNonConstFallback(NamedIntrinsic intrinsic, var_types simdType, var_types baseType);
GenTree* addRangeCheckIfNeeded(NamedIntrinsic intrinsic, GenTree* lastOp, bool mustExpand);
GenTree* tryFoldExprConst(GenTree* tree, bool* isConstExpr);
bool hwIntrinsicSignatureTypeSupported(var_types retType, CORINFO_SIG_INFO* sig, NamedIntrinsic intrinsic);
#endif // _TARGET_XARCH_
#ifdef _TARGET_ARM64_
Expand Down
136 changes: 133 additions & 3 deletions src/jit/hwintrinsicxarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -690,6 +690,128 @@ static bool impIsTableDrivenHWIntrinsic(NamedIntrinsic intrinsicId, HWIntrinsicC
HWIntrinsicInfo::RequiresCodegen(intrinsicId) && !HWIntrinsicInfo::HasSpecialImport(intrinsicId);
}

//----------------------------------------------------------------------------
// tryFoldExprConst Method tries to fold passed const expression.
// It is possible to call it during import phase as it does
// not need any additional processing of passed expressions.
//
// Arguments:
// tree - tree representing expression to undergo constant folding
// isConstExprPtr - parameter which allows to return info if processed
// expression is constant
//
// Return value:
// returns tree folded to constant node if successful otherwise tree
// remains unchanged
//
GenTree* Compiler::tryFoldExprConst(GenTree* tree, bool* isConstExprPtr)
{
assert(tree != nullptr);

// Do not fold const expressions for debug code
if (opts.compDbgCode)
{
return tree;
}

genTreeOps oper = tree->OperGet();
unsigned kind = tree->OperKind();

assert(kind & (GTK_UNOP | GTK_BINOP));

switch (tree->gtOper)
{
case GT_RETFILT:
case GT_RETURN:
case GT_IND:
return tree;
default:
break;
}

GenTree* op1 = tree->gtOp.gtOp1;
GenTree* oldTree = tree;
GenTree* oldOp1 = op1;

if ((kind & GTK_UNOP) && op1)
{

unsigned op1Kind = op1->OperKind();
genTreeOps op1Oper = op1->OperGet();

if ((op1Kind & GTK_CONST))
{
tree = gtFoldExprConst(tree);
if (tree->IsConstInitVal())
{
*isConstExprPtr = true;
}
else
{
*isConstExprPtr = false;
}

return tree;
}
else
{
op1 = tryFoldExprConst(op1, isConstExprPtr);
if (*isConstExprPtr)
{
tree = gtFoldExprConst(tree);
*isConstExprPtr = true;
return tree;
}
}
}
else if ((kind & GTK_BINOP) && op1 && tree->gtOp.gtOp2)
{
GenTree* op2 = tree->gtOp.gtOp2;
GenTree* op2Old = op2;
unsigned op1Kind = op1->OperKind();
unsigned op2Kind = op2->OperKind();
genTreeOps op1Oper = op1->OperGet();
genTreeOps op2Oper = op2->OperGet();

if ((op1Kind & GTK_CONST) && (op2Kind & GTK_CONST))
{
tree = gtFoldExprConst(tree);
*isConstExprPtr = true;
return tree;
}
else
{
bool isOp1Const = false;
if (!(op1Kind & GTK_CONST))
{
op1 = tryFoldExprConst(op1, &isOp1Const);
}
else
{
isOp1Const = true;
}

bool isOp2Const = false;
if (!(op2Kind & GTK_CONST))
{
op2 = tryFoldExprConst(op2, &isOp2Const);
}
else
{
isOp2Const = true;
}

if (isOp1Const && isOp2Const)
{
tree = gtFoldExprConst(tree);
*isConstExprPtr = true;
return tree;
}
}
}
return tree;
}

//------------------------------------------------------------------------
// impHWIntrinsic: dispatch hardware intrinsics to their own implementation
//
Expand Down Expand Up @@ -740,19 +862,27 @@ GenTree* Compiler::impHWIntrinsic(NamedIntrinsic intrinsic,
// Avoid checking stacktop for 0-op intrinsics
if (sig->numArgs > 0 && HWIntrinsicInfo::isImmOp(intrinsic, impStackTop().val))
{
GenTree* lastOp = impStackTop().val;
GenTree* lastOp = impStackTop().val;
bool lastOpIsConst = lastOp->IsCnsIntOrI();

if (!lastOpIsConst)
{
// Try folding expression to Int const
lastOp = tryFoldExprConst(lastOp, &lastOpIsConst);
}

// The imm-HWintrinsics that do not accept all imm8 values may throw
// ArgumentOutOfRangeException when the imm argument is not in the valid range
if (!HWIntrinsicInfo::HasFullRangeImm(intrinsic))
{
if (!mustExpand && lastOp->IsCnsIntOrI() &&
if (!mustExpand && lastOpIsConst &&
!HWIntrinsicInfo::isInImmRange(intrinsic, (int)lastOp->AsIntCon()->IconValue()))
{
return nullptr;
}
}

if (!lastOp->IsCnsIntOrI())
if (!lastOpIsConst)
{
if (HWIntrinsicInfo::NoJmpTableImm(intrinsic))
{
Expand Down

0 comments on commit e898a55

Please sign in to comment.