Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve compare-and-branch sequences produced by Emitter #111797

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions src/coreclr/jit/codegen.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,61 @@ class CodeGen final : public CodeGenInterface

void genExitCode(BasicBlock* block);

#if defined(TARGET_ARM64)
BasicBlock* genGetThrowHelper(SpecialCodeKind codeKind);

// genEmitInlineThrow: Generate code for an inline exception.
void genEmitInlineThrow(SpecialCodeKind codeKind)
{
genEmitHelperCall(compiler->acdHelper(codeKind), 0, EA_UNKNOWN);
}

// throwCodeFn callback follows concept -> void(*)(BasicBlock* target, bool isInline)
//
// For conditional jumps:
// If `isInline`, invert the condition for throw and fall into the exception block.
// Otherwise emit compare and jump with the normal throw condition.
// For unconditional jumps:
// Only emit the unconditional jump when `isInline == false`.
// When `isInline == true` the code will fallthrough to throw without any jump added.
//
// Parameter `target` gives a label to jump to, which is the throw block if
// `isInline == false`, else the continuation.
template <typename throwCodeFn>
void genJumpToThrowHlpBlk(SpecialCodeKind codeKind, throwCodeFn emitJumpCode, BasicBlock* throwBlock = nullptr)
{
if (!throwBlock)
{
// If caller didn't supply a target block, then try to find a helper block.
throwBlock = genGetThrowHelper(codeKind);
}

if (throwBlock)
{
// check:
// if (checkPassed)
// goto throw;
// ...
// throw:
// throw();
emitJumpCode(throwBlock, false);
}
else
{
// check:
// if (!checkPassed)
// goto continue;
// throw();
// continue:
// ...
BasicBlock* over = genCreateTempLabel();
emitJumpCode(over, true);
genEmitInlineThrow(codeKind);
genDefineTempLabel(over);
}
}
#endif

void genJumpToThrowHlpBlk(emitJumpKind jumpKind, SpecialCodeKind codeKind, BasicBlock* failBlk = nullptr);

#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
Expand Down Expand Up @@ -1284,6 +1339,8 @@ class CodeGen final : public CodeGenInterface
#endif
#if defined(TARGET_ARM64)
void genCodeForJumpCompare(GenTreeOpCC* tree);
void genCompareImmAndJump(
GenCondition::Code cond, regNumber reg, ssize_t compareImm, emitAttr size, BasicBlock* target);
void genCodeForBfiz(GenTreeOp* tree);
#endif // TARGET_ARM64

Expand Down
58 changes: 55 additions & 3 deletions src/coreclr/jit/codegenarm64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3613,9 +3613,10 @@ void CodeGen::genCodeForDivMod(GenTreeOp* tree)
}
else
{
// Check if the divisor is zero throw a DivideByZeroException
emit->emitIns_R_I(INS_cmp, size, divisorReg, 0);
genJumpToThrowHlpBlk(EJ_eq, SCK_DIV_BY_ZERO);
genJumpToThrowHlpBlk(SCK_DIV_BY_ZERO, [&](BasicBlock* target, bool invert) {
GenCondition::Code cond = invert ? GenCondition::NE : GenCondition::EQ;
genCompareImmAndJump(cond, divisorReg, 0, size, target);
});
}
}

Expand Down Expand Up @@ -5056,6 +5057,34 @@ void CodeGen::genCodeForJumpCompare(GenTreeOpCC* tree)
}
}

void CodeGen::genCompareImmAndJump(
GenCondition::Code cond, regNumber reg, ssize_t compareImm, emitAttr size, BasicBlock* target)
{
// For ARM64 we only expect equality comparisons.
assert((cond == GenCondition::EQ) || (cond == GenCondition::NE));

if (compareImm == 0)
Copy link
Member

Choose a reason for hiding this comment

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

this method is just called with compareImm == 0. Are you planning to use it for other scenarios as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes I would try and use this helper anywhere I have an immediate and want to compare-and-branch as it would handle the instruction selection generally.

Maybe if we used it here it might emit tbnz sometimes for 2^n? Otherwise it just simplifies that block.

if (comparand->IsIntegralConst(0))

Possibly another cbz case:
GetEmitter()->emitIns_R_I(INS_cmp, EA_4BYTE, data->GetRegNum(), 0);

{
// We can use cbz/cbnz
instruction ins = (cond == GenCondition::EQ) ? INS_cbz : INS_cbnz;
GetEmitter()->emitIns_J_R(ins, size, target, reg);
}
else if (isPow2(compareImm))
{
// We can use tbz/tbnz
instruction ins = (cond == GenCondition::EQ) ? INS_tbz : INS_tbnz;
int imm = genLog2((size_t)compareImm);
GetEmitter()->emitIns_J_R_I(ins, size, target, reg, imm);
}
else
{
// Emit compare and branch pair default.
emitJumpKind jumpKind = (cond == GenCondition::EQ) ? EJ_eq : EJ_ne;
GetEmitter()->emitIns_R_I(INS_cmp, size, reg, compareImm);
inst_JMP(jumpKind, target);
}
}

//---------------------------------------------------------------------
// genSPtoFPdelta - return offset from the stack pointer (Initial-SP) to the frame pointer. The frame pointer
// will point to the saved frame pointer slot (i.e., there will be frame pointer chaining).
Expand Down Expand Up @@ -5924,4 +5953,27 @@ insOpts CodeGen::ShiftOpToInsOpts(genTreeOps shiftOp)
}
}

//---------------------------------------------------------------------------------
// genGetThrowHelper: Search for the throw helper for the exception kind `codeKind`
BasicBlock* CodeGen::genGetThrowHelper(SpecialCodeKind codeKind)
{
BasicBlock* excpRaisingBlock = nullptr;
if (compiler->fgUseThrowHelperBlocks())
{
// For code with throw helper blocks, find and use the helper block for
// raising the exception. The block may be shared by other trees too.
Compiler::AddCodeDsc* add = compiler->fgFindExcptnTarget(codeKind, compiler->compCurBB);
PREFIX_ASSUME_MSG((add != nullptr), ("ERROR: failed to find exception throw block"));
assert(add->acdUsed);
excpRaisingBlock = add->acdDstBlk;
#if !FEATURE_FIXED_OUT_ARGS
assert(add->acdStkLvlInit || isFramePointerUsed());
#endif // !FEATURE_FIXED_OUT_ARGS

noway_assert(excpRaisingBlock != nullptr);
}

return excpRaisingBlock;
}

#endif // TARGET_ARM64
17 changes: 17 additions & 0 deletions src/coreclr/jit/codegenarmarch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1472,6 +1472,23 @@ void CodeGen::genRangeCheck(GenTree* oper)
src1 = arrLen;
src2 = arrIndex;
jmpKind = EJ_ls;

#if defined(TARGET_ARM64)
if (arrIndex->IsIntegralConst(0))
{
assert(!arrLen->isContained());
// For (index == 0), we can just test if (length == 0) as this is the only case that would throw.
// This may lead to an optimization by using cbz/tbnz.
genJumpToThrowHlpBlk(
bndsChk->gtThrowKind,
[&](BasicBlock* target, bool isInline) {
genCompareImmAndJump(isInline ? GenCondition::NE : GenCondition::EQ, arrLen->GetRegNum(), 0,
emitActualTypeSize(arrLen), target);
},
bndsChk->gtIndRngFailBB);
return;
}
#endif
}
else
{
Expand Down
Loading