Skip to content

Commit 5486a26

Browse files
EgorBojakobbotsch
andauthored
Clone blocks with bounds checks (#112595)
Co-authored-by: Jakob Botsch Nielsen <Jakob.botsch.nielsen@gmail.com>
1 parent c37cfcc commit 5486a26

File tree

12 files changed

+754
-8
lines changed

12 files changed

+754
-8
lines changed

src/coreclr/jit/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ set( JIT_SOURCES
159159
promotiondecomposition.cpp
160160
promotionliveness.cpp
161161
rangecheck.cpp
162+
rangecheckcloning.cpp
162163
rationalize.cpp
163164
redundantbranchopts.cpp
164165
regalloc.cpp
@@ -356,6 +357,7 @@ set( JIT_HEADERS
356357
priorityqueue.h
357358
promotion.h
358359
rangecheck.h
360+
rangecheckcloning.h
359361
rationalize.h
360362
regalloc.h
361363
registerargconvention.h

src/coreclr/jit/block.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -464,13 +464,14 @@ enum BasicBlockFlags : uint64_t
464464
BBF_HAS_VALUE_PROFILE = MAKE_BBFLAG(39), // Block has a node that needs a value probing
465465

466466
BBF_HAS_NEWARR = MAKE_BBFLAG(40), // BB contains 'new' of an array type.
467+
BBF_MAY_HAVE_BOUNDS_CHECKS = MAKE_BBFLAG(41), // BB *likely* has a bounds check (after rangecheck phase).
467468

468469
// The following are sets of flags.
469470

470471
// Flags to update when two blocks are compacted
471472

472473
BBF_COMPACT_UPD = BBF_GC_SAFE_POINT | BBF_NEEDS_GCPOLL | BBF_HAS_JMP | BBF_HAS_IDX_LEN | BBF_HAS_MD_IDX_LEN | BBF_BACKWARD_JUMP | \
473-
BBF_HAS_NEWOBJ | BBF_HAS_NEWARR | BBF_HAS_NULLCHECK | BBF_HAS_MDARRAYREF | BBF_LOOP_HEAD,
474+
BBF_HAS_NEWOBJ | BBF_HAS_NEWARR | BBF_HAS_NULLCHECK | BBF_HAS_MDARRAYREF | BBF_LOOP_HEAD | BBF_MAY_HAVE_BOUNDS_CHECKS,
474475

475476
// Flags a block should not have had before it is split.
476477

@@ -489,14 +490,14 @@ enum BasicBlockFlags : uint64_t
489490
// TODO: Should BBF_RUN_RARELY be added to BBF_SPLIT_GAINED ?
490491

491492
BBF_SPLIT_GAINED = BBF_DONT_REMOVE | BBF_HAS_JMP | BBF_BACKWARD_JUMP | BBF_HAS_IDX_LEN | BBF_HAS_MD_IDX_LEN | BBF_PROF_WEIGHT | BBF_HAS_NEWARR | \
492-
BBF_HAS_NEWOBJ | BBF_KEEP_BBJ_ALWAYS | BBF_CLONED_FINALLY_END | BBF_HAS_NULLCHECK | BBF_HAS_HISTOGRAM_PROFILE | BBF_HAS_VALUE_PROFILE | BBF_HAS_MDARRAYREF | BBF_NEEDS_GCPOLL,
493+
BBF_HAS_NEWOBJ | BBF_KEEP_BBJ_ALWAYS | BBF_CLONED_FINALLY_END | BBF_HAS_NULLCHECK | BBF_HAS_HISTOGRAM_PROFILE | BBF_HAS_VALUE_PROFILE | BBF_HAS_MDARRAYREF | BBF_NEEDS_GCPOLL | BBF_MAY_HAVE_BOUNDS_CHECKS,
493494

494495
// Flags that must be propagated to a new block if code is copied from a block to a new block. These are flags that
495496
// limit processing of a block if the code in question doesn't exist. This is conservative; we might not
496497
// have actually copied one of these type of tree nodes, but if we only copy a portion of the block's statements,
497498
// we don't know (unless we actually pay close attention during the copy).
498499

499-
BBF_COPY_PROPAGATE = BBF_HAS_NEWOBJ | BBF_HAS_NEWARR | BBF_HAS_NULLCHECK | BBF_HAS_IDX_LEN | BBF_HAS_MD_IDX_LEN | BBF_HAS_MDARRAYREF,
500+
BBF_COPY_PROPAGATE = BBF_HAS_NEWOBJ | BBF_HAS_NEWARR | BBF_HAS_NULLCHECK | BBF_HAS_IDX_LEN | BBF_HAS_MD_IDX_LEN | BBF_HAS_MDARRAYREF | BBF_MAY_HAVE_BOUNDS_CHECKS,
500501
};
501502

502503
FORCEINLINE

src/coreclr/jit/compiler.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4929,6 +4929,7 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
49294929
bool doAssertionProp = true;
49304930
bool doVNBasedIntrinExpansion = true;
49314931
bool doRangeAnalysis = true;
4932+
bool doRangeCheckCloning = true;
49324933
bool doVNBasedDeadStoreRemoval = true;
49334934

49344935
#if defined(OPT_CONFIG)
@@ -4941,6 +4942,7 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
49414942
doCse = doValueNum;
49424943
doAssertionProp = doValueNum && (JitConfig.JitDoAssertionProp() != 0);
49434944
doRangeAnalysis = doAssertionProp && (JitConfig.JitDoRangeAnalysis() != 0);
4945+
doRangeCheckCloning = doValueNum && doRangeAnalysis;
49444946
doOptimizeIVs = doAssertionProp && (JitConfig.JitDoOptimizeIVs() != 0);
49454947
doVNBasedDeadStoreRemoval = doValueNum && (JitConfig.JitDoVNBasedDeadStoreRemoval() != 0);
49464948
doVNBasedIntrinExpansion = doValueNum;
@@ -5053,6 +5055,13 @@ void Compiler::compCompile(void** methodCodePtr, uint32_t* methodCodeSize, JitFl
50535055
DoPhase(this, PHASE_VN_BASED_DEAD_STORE_REMOVAL, &Compiler::optVNBasedDeadStoreRemoval);
50545056
}
50555057

5058+
if (doRangeCheckCloning)
5059+
{
5060+
// Clone blocks with subsequent bounds checks
5061+
//
5062+
DoPhase(this, PHASE_RANGE_CHECK_CLONING, &Compiler::optRangeCheckCloning);
5063+
}
5064+
50565065
if (doVNBasedIntrinExpansion)
50575066
{
50585067
// Expand some intrinsics based on VN data

src/coreclr/jit/compiler.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ void* operator new[](size_t n, Compiler* context, CompMemKind cmk);
114114

115115
// Requires the definitions of "operator new" so including "LoopCloning.h" after the definitions.
116116
#include "loopcloning.h"
117+
#include "rangecheckcloning.h"
117118

118119
/*****************************************************************************/
119120

@@ -3809,7 +3810,7 @@ class Compiler
38093810
bool gtStoreDefinesField(
38103811
LclVarDsc* fieldVarDsc, ssize_t offset, unsigned size, ssize_t* pFieldStoreOffset, unsigned* pFieldStoreSize);
38113812

3812-
void gtPeelOffsets(GenTree** addr, target_ssize_t* offset, FieldSeq** fldSeq = nullptr);
3813+
void gtPeelOffsets(GenTree** addr, target_ssize_t* offset, FieldSeq** fldSeq = nullptr) const;
38133814

38143815
// Return true if call is a recursive call; return false otherwise.
38153816
// Note when inlining, this looks for calls back to the root method.
@@ -6669,12 +6670,12 @@ class Compiler
66696670
void fgInsertStmtAfter(BasicBlock* block, Statement* insertionPoint, Statement* stmt);
66706671
void fgInsertStmtBefore(BasicBlock* block, Statement* insertionPoint, Statement* stmt);
66716672

6672-
private:
6673-
Statement* fgInsertStmtListAfter(BasicBlock* block, Statement* stmtAfter, Statement* stmtList);
6674-
66756673
// Create a new temporary variable to hold the result of *ppTree,
66766674
// and transform the graph accordingly.
66776675
GenTree* fgInsertCommaFormTemp(GenTree** ppTree);
6676+
6677+
private:
6678+
Statement* fgInsertStmtListAfter(BasicBlock* block, Statement* stmtAfter, Statement* stmtList);
66786679
TempInfo fgMakeTemp(GenTree* value);
66796680
GenTree* fgMakeMultiUse(GenTree** ppTree);
66806681

@@ -7219,6 +7220,7 @@ class Compiler
72197220
bool optCanonicalizeExit(FlowGraphNaturalLoop* loop, BasicBlock* exit);
72207221

72217222
PhaseStatus optCloneLoops();
7223+
PhaseStatus optRangeCheckCloning();
72227224
bool optShouldCloneLoop(FlowGraphNaturalLoop* loop, LoopCloneContext* context);
72237225
void optCloneLoop(FlowGraphNaturalLoop* loop, LoopCloneContext* context);
72247226
PhaseStatus optUnrollLoops(); // Unrolls loops (needs to have cost info)

src/coreclr/jit/compmemkind.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ CompMemKindMacro(ZeroInit)
6666
CompMemKindMacro(Pgo)
6767
CompMemKindMacro(MaskConversionOpt)
6868
CompMemKindMacro(TryRegionClone)
69+
CompMemKindMacro(RangeCheckCloning)
6970
//clang-format on
7071

7172
#undef CompMemKindMacro

src/coreclr/jit/compphases.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ CompPhaseNameMacro(PHASE_VN_COPY_PROP, "VN based copy prop",
9898
CompPhaseNameMacro(PHASE_VN_BASED_INTRINSIC_EXPAND, "VN based intrinsic expansion", false, -1, false)
9999
CompPhaseNameMacro(PHASE_OPTIMIZE_BRANCHES, "Redundant branch opts", false, -1, false)
100100
CompPhaseNameMacro(PHASE_ASSERTION_PROP_MAIN, "Assertion prop", false, -1, false)
101+
CompPhaseNameMacro(PHASE_RANGE_CHECK_CLONING, "Clone blocks with range checks", false, -1, false)
101102
CompPhaseNameMacro(PHASE_IF_CONVERSION, "If conversion", false, -1, false)
102103
CompPhaseNameMacro(PHASE_VN_BASED_DEAD_STORE_REMOVAL,"VN-based dead store removal", false, -1, false)
103104
CompPhaseNameMacro(PHASE_EMPTY_FINALLY_3, "Remove empty finally 3", false, -1, false)

src/coreclr/jit/promotiondecomposition.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1206,7 +1206,7 @@ class DecompositionPlan
12061206
// offset - [out] The sum of offset peeled such that ADD(addr, offset) is equivalent to the original addr.
12071207
// fldSeq - [out, optional] The combined field sequence for all the peeled offsets.
12081208
//
1209-
void Compiler::gtPeelOffsets(GenTree** addr, target_ssize_t* offset, FieldSeq** fldSeq)
1209+
void Compiler::gtPeelOffsets(GenTree** addr, target_ssize_t* offset, FieldSeq** fldSeq) const
12101210
{
12111211
assert((*addr)->TypeIs(TYP_I_IMPL, TYP_BYREF, TYP_REF));
12121212
*offset = 0;

src/coreclr/jit/rangecheck.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,6 +1732,14 @@ bool RangeCheck::OptimizeRangeChecks()
17321732
return madeChanges;
17331733
}
17341734

1735+
if (tree->OperIs(GT_BOUNDS_CHECK))
1736+
{
1737+
// Leave a hint for optRangeCheckCloning to improve the JIT TP.
1738+
// NOTE: it doesn't have to be precise and being properly maintained
1739+
// during transformations, it's just a hint.
1740+
block->SetFlags(BBF_MAY_HAVE_BOUNDS_CHECKS);
1741+
}
1742+
17351743
OptimizeRangeCheck(block, stmt, tree);
17361744
}
17371745

0 commit comments

Comments
 (0)