Skip to content

Commit 1b23d6f

Browse files
Improve compare-and-branch sequences produced by Emitter (#111797)
* Improve compare-and-branch sequences produced by Emitter Introduces an overload of `genJumpToThrowHlpBlk` that allows you to pass a function that generates the branch code, before it creates an inline throw block. This allows us to choose compare-and-branch sequences such as `cbz` on ARM64 for checks added in the emitter, such as divide-by-zero. * Optimize bounds checks for zero index on ARM64 Emit `cbz` instead of `cmp+b.ls` when checking bounds for an access to the 0 index of an array. It can only throw when `arrayLength == 0`. Fixes #42514 * Move `genGetThrowHelper` to codegenarm64.cpp * Use `GetRegNum` instead of `GetReg`
1 parent f6c74b8 commit 1b23d6f

File tree

3 files changed

+129
-3
lines changed

3 files changed

+129
-3
lines changed

src/coreclr/jit/codegen.h

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,61 @@ class CodeGen final : public CodeGenInterface
230230

231231
void genExitCode(BasicBlock* block);
232232

233+
#if defined(TARGET_ARM64)
234+
BasicBlock* genGetThrowHelper(SpecialCodeKind codeKind);
235+
236+
// genEmitInlineThrow: Generate code for an inline exception.
237+
void genEmitInlineThrow(SpecialCodeKind codeKind)
238+
{
239+
genEmitHelperCall(compiler->acdHelper(codeKind), 0, EA_UNKNOWN);
240+
}
241+
242+
// throwCodeFn callback follows concept -> void(*)(BasicBlock* target, bool isInline)
243+
//
244+
// For conditional jumps:
245+
// If `isInline`, invert the condition for throw and fall into the exception block.
246+
// Otherwise emit compare and jump with the normal throw condition.
247+
// For unconditional jumps:
248+
// Only emit the unconditional jump when `isInline == false`.
249+
// When `isInline == true` the code will fallthrough to throw without any jump added.
250+
//
251+
// Parameter `target` gives a label to jump to, which is the throw block if
252+
// `isInline == false`, else the continuation.
253+
template <typename throwCodeFn>
254+
void genJumpToThrowHlpBlk(SpecialCodeKind codeKind, throwCodeFn emitJumpCode, BasicBlock* throwBlock = nullptr)
255+
{
256+
if (!throwBlock)
257+
{
258+
// If caller didn't supply a target block, then try to find a helper block.
259+
throwBlock = genGetThrowHelper(codeKind);
260+
}
261+
262+
if (throwBlock)
263+
{
264+
// check:
265+
// if (checkPassed)
266+
// goto throw;
267+
// ...
268+
// throw:
269+
// throw();
270+
emitJumpCode(throwBlock, false);
271+
}
272+
else
273+
{
274+
// check:
275+
// if (!checkPassed)
276+
// goto continue;
277+
// throw();
278+
// continue:
279+
// ...
280+
BasicBlock* over = genCreateTempLabel();
281+
emitJumpCode(over, true);
282+
genEmitInlineThrow(codeKind);
283+
genDefineTempLabel(over);
284+
}
285+
}
286+
#endif
287+
233288
void genJumpToThrowHlpBlk(emitJumpKind jumpKind, SpecialCodeKind codeKind, BasicBlock* failBlk = nullptr);
234289

235290
#if defined(TARGET_LOONGARCH64) || defined(TARGET_RISCV64)
@@ -1285,6 +1340,8 @@ class CodeGen final : public CodeGenInterface
12851340
#endif
12861341
#if defined(TARGET_ARM64)
12871342
void genCodeForJumpCompare(GenTreeOpCC* tree);
1343+
void genCompareImmAndJump(
1344+
GenCondition::Code cond, regNumber reg, ssize_t compareImm, emitAttr size, BasicBlock* target);
12881345
void genCodeForBfiz(GenTreeOp* tree);
12891346
#endif // TARGET_ARM64
12901347

src/coreclr/jit/codegenarm64.cpp

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3622,9 +3622,10 @@ void CodeGen::genCodeForDivMod(GenTreeOp* tree)
36223622
}
36233623
else
36243624
{
3625-
// Check if the divisor is zero throw a DivideByZeroException
3626-
emit->emitIns_R_I(INS_cmp, size, divisorReg, 0);
3627-
genJumpToThrowHlpBlk(EJ_eq, SCK_DIV_BY_ZERO);
3625+
genJumpToThrowHlpBlk(SCK_DIV_BY_ZERO, [&](BasicBlock* target, bool invert) {
3626+
GenCondition::Code cond = invert ? GenCondition::NE : GenCondition::EQ;
3627+
genCompareImmAndJump(cond, divisorReg, 0, size, target);
3628+
});
36283629
}
36293630
}
36303631

@@ -5071,6 +5072,34 @@ void CodeGen::genCodeForJumpCompare(GenTreeOpCC* tree)
50715072
}
50725073
}
50735074

5075+
void CodeGen::genCompareImmAndJump(
5076+
GenCondition::Code cond, regNumber reg, ssize_t compareImm, emitAttr size, BasicBlock* target)
5077+
{
5078+
// For ARM64 we only expect equality comparisons.
5079+
assert((cond == GenCondition::EQ) || (cond == GenCondition::NE));
5080+
5081+
if (compareImm == 0)
5082+
{
5083+
// We can use cbz/cbnz
5084+
instruction ins = (cond == GenCondition::EQ) ? INS_cbz : INS_cbnz;
5085+
GetEmitter()->emitIns_J_R(ins, size, target, reg);
5086+
}
5087+
else if (isPow2(compareImm))
5088+
{
5089+
// We can use tbz/tbnz
5090+
instruction ins = (cond == GenCondition::EQ) ? INS_tbz : INS_tbnz;
5091+
int imm = genLog2((size_t)compareImm);
5092+
GetEmitter()->emitIns_J_R_I(ins, size, target, reg, imm);
5093+
}
5094+
else
5095+
{
5096+
// Emit compare and branch pair default.
5097+
emitJumpKind jumpKind = (cond == GenCondition::EQ) ? EJ_eq : EJ_ne;
5098+
GetEmitter()->emitIns_R_I(INS_cmp, size, reg, compareImm);
5099+
inst_JMP(jumpKind, target);
5100+
}
5101+
}
5102+
50745103
//---------------------------------------------------------------------
50755104
// genSPtoFPdelta - return offset from the stack pointer (Initial-SP) to the frame pointer. The frame pointer
50765105
// will point to the saved frame pointer slot (i.e., there will be frame pointer chaining).
@@ -5939,4 +5968,27 @@ insOpts CodeGen::ShiftOpToInsOpts(genTreeOps shiftOp)
59395968
}
59405969
}
59415970

5971+
//---------------------------------------------------------------------------------
5972+
// genGetThrowHelper: Search for the throw helper for the exception kind `codeKind`
5973+
BasicBlock* CodeGen::genGetThrowHelper(SpecialCodeKind codeKind)
5974+
{
5975+
BasicBlock* excpRaisingBlock = nullptr;
5976+
if (compiler->fgUseThrowHelperBlocks())
5977+
{
5978+
// For code with throw helper blocks, find and use the helper block for
5979+
// raising the exception. The block may be shared by other trees too.
5980+
Compiler::AddCodeDsc* add = compiler->fgFindExcptnTarget(codeKind, compiler->compCurBB);
5981+
PREFIX_ASSUME_MSG((add != nullptr), ("ERROR: failed to find exception throw block"));
5982+
assert(add->acdUsed);
5983+
excpRaisingBlock = add->acdDstBlk;
5984+
#if !FEATURE_FIXED_OUT_ARGS
5985+
assert(add->acdStkLvlInit || isFramePointerUsed());
5986+
#endif // !FEATURE_FIXED_OUT_ARGS
5987+
5988+
noway_assert(excpRaisingBlock != nullptr);
5989+
}
5990+
5991+
return excpRaisingBlock;
5992+
}
5993+
59425994
#endif // TARGET_ARM64

src/coreclr/jit/codegenarmarch.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1474,6 +1474,23 @@ void CodeGen::genRangeCheck(GenTree* oper)
14741474
src1 = arrLen;
14751475
src2 = arrIndex;
14761476
jmpKind = EJ_ls;
1477+
1478+
#if defined(TARGET_ARM64)
1479+
if (arrIndex->IsIntegralConst(0))
1480+
{
1481+
assert(!arrLen->isContained());
1482+
// For (index == 0), we can just test if (length == 0) as this is the only case that would throw.
1483+
// This may lead to an optimization by using cbz/tbnz.
1484+
genJumpToThrowHlpBlk(
1485+
bndsChk->gtThrowKind,
1486+
[&](BasicBlock* target, bool isInline) {
1487+
genCompareImmAndJump(isInline ? GenCondition::NE : GenCondition::EQ, arrLen->GetRegNum(), 0,
1488+
emitActualTypeSize(arrLen), target);
1489+
},
1490+
bndsChk->gtIndRngFailBB);
1491+
return;
1492+
}
1493+
#endif
14771494
}
14781495
else
14791496
{

0 commit comments

Comments
 (0)