-
Notifications
You must be signed in to change notification settings - Fork 5k
Eliding span Slice bounds checking is not trivial #115154
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
Labels
area-CodeGen-coreclr
CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
tenet-performance
Performance related issue
Milestone
Comments
Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch |
Haven't checked all of them, but |
Yep, both scenarios there drop the bounds check the same way if they're tweaked so the predicate matches the span one. Not sure which issue this is more appropriate on, but as below. Test methodpublic static ReadOnlySpan<char> MatchLogic(ReadOnlySpan<char> span, int offset)
{
if ((uint)(offset + 1) <= (uint)span.Length) // +1<= instead of <
{
// No bounds check
return span.Slice(offset + 1);
}
return span;
} ; V02 arg1 int // offset
; V04 tmp1 int "Inlining Arg" // offset + 1
; V11 tmp8 int "field V01._length (fldOffset=0x8)" P-INDEP
--- Trying RBO in BB02 ---
Relop [000027] BB02 value unknown, trying inference
Dominator BB01 of BB02 has relop with reversed liberal VN
N005 ( 5, 5) [000006] N---G+-N-U- * GT int <l:$2c1, c:$2c2>
N003 ( 3, 3) [000002] -----+----- +--* ADD int $2c0
N001 ( 1, 1) [000000] -----+----- | +--* LCL_VAR int V02 arg1 u:1 $100
N002 ( 1, 1) [000001] -----+----- | \--* CNS_INT int 1 $41
N004 ( 1, 1) [000005] -----+----- \--* LCL_VAR int V11 tmp8 u:1 <l:$280, c:$101>
Redundant compare; current relop:
N003 ( 3, 3) [000027] N----+-N-U- * LE int <l:$2c3, c:$2c4>
N001 ( 1, 1) [000023] -----+----- +--* LCL_VAR int V04 tmp1 u:1 $2c0
N002 ( 1, 1) [000026] -----+----- \--* LCL_VAR int V11 tmp8 u:1 <l:$280, c:$101>
False successor BB02 of BB01 reaches, relop [000027] must be true
Conditional folded at BB02
BB02 becomes a BBJ_ALWAYS to BB04 Count methodpublic static int MatchLogic(ReadOnlySpan<char> span, char c)
{
int count = 0;
int pos;
while ((uint)((pos = span.IndexOf(c)) + 1) <= (uint)span.Length) // +1<= instead of <
{
count++;
// No bounds check
span = span.Slice(pos + 1);
}
return count;
} ; V03 loc1 int // pos
; V06 tmp2 int "Inlining Arg" // slice `pos + 1`
; V18 tmp14 int "Inline return value spill temp" // while conditional pos assignment
; V23 tmp19 int "field V00._length (fldOffset=0x8)" P-INDEP
--- Trying RBO in BB02 ---
Relop [000035] BB02 value unknown, trying inference
Dominator BB09 of BB02 has relop with same liberal VN
N005 ( 5, 5) [000015] N---G+-N-U- * LE int $346
N003 ( 3, 3) [000011] -----+----- +--* ADD int $345
N001 ( 1, 1) [000007] -----+----- | +--* LCL_VAR int V18 tmp14 u:3 $2c2
N002 ( 1, 1) [000010] -----+----- | \--* CNS_INT int 1 $41
N004 ( 1, 1) [000014] -----+----- \--* LCL_VAR int V23 tmp19 u:2 $2c1
Redundant compare; current relop:
N003 ( 3, 3) [000035] N----+-N-U- * LE int $346
N001 ( 1, 1) [000031] -----+----- +--* LCL_VAR int V06 tmp2 u:1 $345
N002 ( 1, 1) [000034] -----+----- \--* LCL_VAR int V23 tmp19 u:2 $2c1
True successor BB02 of BB09 reaches, relop [000035] must be true
Conditional folded at BB02
BB02 becomes a BBJ_ALWAYS to BB03 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
area-CodeGen-coreclr
CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI
tenet-performance
Performance related issue
Description
Changes in similar user logic to guarantee a valid upper bound when slicing a span result in significantly different codegen.
The five methods below progress from "first thought" to unsafe implementations of the same guarantee, with only the unsigned predicate and unsafe versions eliding the
Slice
bounds check.Configuration
Regression?
No.
Data
godbolt
Benchmark Code
TestPositiveThenMinI32 BDN Disassembly
TestPositiveThenMinI32 Disasmo
TestPredicateI32 BDN Disassembly
TestPredicateI32 Disasmo
TestPredicateU32 BDN Disassembly
TestPredicateU32 Disasmo
TestMinU32 BDN Disassembly
TestMinU32 Disasmo
TestCreateRoS BDN Disassembly
TestCreateRoS Disasmo
Analysis
The only phase responsible for allow the upper bounds checking block to be removed was 'Redundant branch opts'.
TestPredicateU32
The bounds checking branch is pruned as the predicate in user code is identical to
Slice(int,int)
after 'Morph - Global', with<
subsuming<=
.Flowgraph before redundant branch opts
TestPredicateI32
The combination of user clauses guaranteeing the subrange [0..length] are ineffective.
Flowgraph after redundant branch opts
TestMinU32
A temp value is used to store the result of
Min
, so it's not associated the same way.Flowgraph after redundant branch opts
The text was updated successfully, but these errors were encountered: