Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Optimize Math.Pow(x, c) in JIT #26552

Closed
wants to merge 10 commits into from
2 changes: 2 additions & 0 deletions src/jit/importer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3580,6 +3580,8 @@ GenTree* Compiler::impIntrinsic(GenTree* newobjThis,
case CORINFO_INTRINSIC_Ceiling:
case CORINFO_INTRINSIC_Floor:
retNode = impMathIntrinsic(method, sig, callType, intrinsicID, tailCall);
// Can be optimized during morph
isSpecial = intrinsicID == CORINFO_INTRINSIC_Pow;
break;

#if defined(_TARGET_XARCH_) || defined(_TARGET_ARM64_)
Expand Down
47 changes: 47 additions & 0 deletions src/jit/morph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8698,6 +8698,53 @@ GenTree* Compiler::fgMorphCall(GenTreeCall* call)
}
}

// Optimize Math.Pow(x, c)
if ((call->gtCallMoreFlags & GTF_CALL_M_SPECIAL_INTRINSIC) &&
info.compCompHnd->getIntrinsicID(call->gtCallMethHnd) == CORINFO_INTRINSIC_Pow)
{
noway_assert(call->fgArgInfo->ArgCount() == 2);
GenTree* arg0 = gtArgEntryByArgNum(call, 0)->node;
GenTree* arg1 = gtArgEntryByArgNum(call, 1)->node;
GenTree* newNode = nullptr;

if (arg1->IsCnsFltOrDbl())
{
noway_assert(arg0->TypeGet() == arg1->TypeGet());
GenTreeDblCon* powerCon = arg1->AsDblCon();

if (powerCon->gtDconVal == 2.0)
{
if (arg0->OperIs(GT_LCL_VAR))
{
// Math.Pow(x, 2) -> x*x where x is a local variable
newNode = gtNewOperNode(GT_MUL, powerCon->TypeGet(), arg0, gtCloneExpr(arg0));
}
else if (arg0->OperIs(GT_IND) && arg0->AsIndir()->Addr()->gtGetOp1()->OperIs(GT_LCL_VAR))
{
// Math.Pow(x, 2) -> x*x where x is a field
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way the code is written the only field that it will recognize is the first field of a struct (the field at offset 0). I doubt that was the intention.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mikedn heh, definitely not the intention, I wish I actually could try the "introduce a tmp local" scenario instead.

newNode = gtNewOperNode(GT_MUL, powerCon->TypeGet(), arg0, gtCloneExpr(arg0));
}
}
else if (powerCon->gtDconVal == 1.0)
{
// Math.Pow(x, 1) -> x
newNode = arg0;
}
else if (powerCon->gtDconVal == -1.0)
{
// Math.Pow(x, -1) -> 1/x
newNode = gtNewOperNode(GT_DIV, powerCon->TypeGet(), gtNewDconNode(1, powerCon->TypeGet()), arg0);
}

if (newNode != nullptr)
{
INDEBUG(newNode->gtDebugFlags |= GTF_DEBUG_NODE_MORPHED);
DEBUG_DESTROY_NODE(call);
return newNode;
}
}
}

// Optimize get_ManagedThreadId(get_CurrentThread)
if ((call->gtCallMoreFlags & GTF_CALL_M_SPECIAL_INTRINSIC) &&
info.compCompHnd->getIntrinsicID(call->gtCallMethHnd) == CORINFO_INTRINSIC_GetManagedThreadId)
Expand Down