Skip to content

Commit bae19aa

Browse files
authored
Fold Span.Slice(X) like bound checks (#122040)
[Diffs](https://dev.azure.com/dnceng-public/public/_build/results?buildId=1215131&view=ms.vss-build-web.run-extensions-tab). ```cs static Vector256<int> Test(Span<int> values) { if (values.Length < 16) return default; return Vector256.Create(values.Slice(8)); } ``` Was: ```asm ; Method Program:Test(System.Span`1[int]):System.Runtime.Intrinsics.Vector256`1[int] (FullOpts) sub rsp, 40 mov rax, bword ptr [rdx] mov edx, dword ptr [rdx+0x08] cmp edx, 16 jl SHORT G_M55981_IG04 add rax, 32 add edx, -8 cmp edx, 8 jl SHORT G_M55981_IG07 vmovups ymm0, ymmword ptr [rax] vmovups ymmword ptr [rcx], ymm0 jmp SHORT G_M55981_IG05 G_M55981_IG04: vxorps ymm0, ymm0, ymm0 vmovups ymmword ptr [rcx], ymm0 G_M55981_IG05: mov rax, rcx vzeroupper add rsp, 40 ret G_M55981_IG07: mov ecx, 6 call [System.ThrowHelper:ThrowArgumentOutOfRangeException(int)] int3 ; Total bytes of code: 68 ``` Now: ```asm ; Method Program:Test(System.Span`1[int]):System.Runtime.Intrinsics.Vector256`1[int] (FullOpts) mov rax, bword ptr [rdx] mov edx, dword ptr [rdx+0x08] cmp edx, 16 jl SHORT G_M55981_IG04 add rax, 32 vmovups ymm0, ymmword ptr [rax] vmovups ymmword ptr [rcx], ymm0 jmp SHORT G_M55981_IG05 G_M55981_IG04: vxorps ymm0, ymm0, ymm0 vmovups ymmword ptr [rcx], ymm0 G_M55981_IG05: mov rax, rcx vzeroupper ret ; Total bytes of code: 40 ```
1 parent 626ad32 commit bae19aa

File tree

2 files changed

+78
-48
lines changed

2 files changed

+78
-48
lines changed

src/coreclr/jit/assertionprop.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4461,6 +4461,36 @@ GenTree* Compiler::optAssertionPropGlobal_RelOp(ASSERT_VALARG_TP assertions,
44614461
return optAssertionProp_Update(newTree, tree, stmt);
44624462
}
44634463
}
4464+
4465+
// If op1VN is actually ADD(X, CNS), we can try peeling the constant offset and adjusting op2Cns accordingly.
4466+
// It's a bit more complicated for unsigned comparisons, so only do it for signed ones for now.
4467+
//
4468+
if (!tree->IsUnsigned())
4469+
{
4470+
ValueNum peeledOp1VN = op1VN;
4471+
int peeledOffset = 0;
4472+
vnStore->PeelOffsetsI32(&peeledOp1VN, &peeledOffset);
4473+
4474+
if (peeledOffset != 0)
4475+
{
4476+
Range peeledOffsetRng = Range(Limit(Limit::keConstant, peeledOffset));
4477+
Range peeledOp1Rng = Range(Limit(Limit::keUnknown));
4478+
if (RangeCheck::TryGetRangeFromAssertions(this, peeledOp1VN, assertions, &peeledOp1Rng))
4479+
{
4480+
// Subtract handles overflow internally.
4481+
rng2 = RangeOps::Subtract(rng2, peeledOffsetRng);
4482+
4483+
RangeOps::RelationKind kind =
4484+
RangeOps::EvalRelop(tree->OperGet(), tree->IsUnsigned(), peeledOp1Rng, rng2);
4485+
if ((kind != RangeOps::RelationKind::Unknown))
4486+
{
4487+
newTree = kind == RangeOps::RelationKind::AlwaysTrue ? gtNewTrue() : gtNewFalse();
4488+
newTree = gtWrapWithSideEffects(newTree, tree, GTF_ALL_EFFECT);
4489+
return optAssertionProp_Update(newTree, tree, stmt);
4490+
}
4491+
}
4492+
}
4493+
}
44644494
}
44654495

44664496
// Else check if we have an equality check involving a local or an indir

src/coreclr/jit/rangecheck.h

Lines changed: 48 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,25 @@ struct RangeOps
328328
return Limit(Limit::keUnknown);
329329
}
330330

331+
// Perform 'value' - 'cns'
332+
static Limit SubtractConstantLimit(const Limit& value, const Limit& cns)
333+
{
334+
assert(cns.IsConstant());
335+
336+
if (cns.GetConstant() == INT_MIN)
337+
{
338+
// Subtracting INT_MIN would overflow
339+
return Limit(Limit::keUnknown);
340+
}
341+
342+
Limit l = value;
343+
if (l.AddConstant(-cns.GetConstant()))
344+
{
345+
return l;
346+
}
347+
return Limit(Limit::keUnknown);
348+
}
349+
331350
// Perform 'value' * 'cns'
332351
static Limit MultiplyConstantLimit(const Limit& value, const Limit& cns)
333352
{
@@ -352,9 +371,9 @@ struct RangeOps
352371
return Limit(Limit::keUnknown);
353372
}
354373

355-
// Given two ranges "r1" and "r2", perform an add operation on the
356-
// ranges.
357-
static Range Add(Range& r1, Range& r2)
374+
// Given two ranges "r1" and "r2", perform a generic 'op' operation on the ranges.
375+
template <typename Operation>
376+
static Range ApplyRangeOp(Range& r1, Range& r2, Operation op)
358377
{
359378
Limit& r1lo = r1.LowerLimit();
360379
Limit& r1hi = r1.UpperLimit();
@@ -376,23 +395,45 @@ struct RangeOps
376395

377396
if (r1lo.IsConstant())
378397
{
379-
result.lLimit = AddConstantLimit(r2lo, r1lo);
398+
result.lLimit = op(r2lo, r1lo);
380399
}
381400
if (r2lo.IsConstant())
382401
{
383-
result.lLimit = AddConstantLimit(r1lo, r2lo);
402+
result.lLimit = op(r1lo, r2lo);
384403
}
385404
if (r1hi.IsConstant())
386405
{
387-
result.uLimit = AddConstantLimit(r2hi, r1hi);
406+
result.uLimit = op(r2hi, r1hi);
388407
}
389408
if (r2hi.IsConstant())
390409
{
391-
result.uLimit = AddConstantLimit(r1hi, r2hi);
410+
result.uLimit = op(r1hi, r2hi);
392411
}
412+
393413
return result;
394414
}
395415

416+
static Range Add(Range& r1, Range& r2)
417+
{
418+
return ApplyRangeOp(r1, r2, [](Limit& a, Limit& b) {
419+
return AddConstantLimit(a, b);
420+
});
421+
}
422+
423+
static Range Subtract(Range& r1, Range& r2)
424+
{
425+
return ApplyRangeOp(r1, r2, [](Limit& a, Limit& b) {
426+
return SubtractConstantLimit(a, b);
427+
});
428+
}
429+
430+
static Range Multiply(Range& r1, Range& r2)
431+
{
432+
return ApplyRangeOp(r1, r2, [](Limit& a, Limit& b) {
433+
return MultiplyConstantLimit(a, b);
434+
});
435+
}
436+
396437
static Range ShiftRight(Range& r1, Range& r2)
397438
{
398439
Limit& r1lo = r1.LowerLimit();
@@ -430,47 +471,6 @@ struct RangeOps
430471
return result;
431472
}
432473

433-
// Given two ranges "r1" and "r2", perform an multiply operation on the
434-
// ranges.
435-
static Range Multiply(Range& r1, Range& r2)
436-
{
437-
Limit& r1lo = r1.LowerLimit();
438-
Limit& r1hi = r1.UpperLimit();
439-
Limit& r2lo = r2.LowerLimit();
440-
Limit& r2hi = r2.UpperLimit();
441-
442-
Range result = Limit(Limit::keUnknown);
443-
444-
// Check lo ranges if they are dependent and not unknown.
445-
if ((r1lo.IsDependent() && !r1lo.IsUnknown()) || (r2lo.IsDependent() && !r2lo.IsUnknown()))
446-
{
447-
result.lLimit = Limit(Limit::keDependent);
448-
}
449-
// Check hi ranges if they are dependent and not unknown.
450-
if ((r1hi.IsDependent() && !r1hi.IsUnknown()) || (r2hi.IsDependent() && !r2hi.IsUnknown()))
451-
{
452-
result.uLimit = Limit(Limit::keDependent);
453-
}
454-
455-
if (r1lo.IsConstant())
456-
{
457-
result.lLimit = MultiplyConstantLimit(r2lo, r1lo);
458-
}
459-
if (r2lo.IsConstant())
460-
{
461-
result.lLimit = MultiplyConstantLimit(r1lo, r2lo);
462-
}
463-
if (r1hi.IsConstant())
464-
{
465-
result.uLimit = MultiplyConstantLimit(r2hi, r1hi);
466-
}
467-
if (r2hi.IsConstant())
468-
{
469-
result.uLimit = MultiplyConstantLimit(r1hi, r2hi);
470-
}
471-
return result;
472-
}
473-
474474
// Given two ranges "r1" and "r2", do a Phi merge. If "monIncreasing" is true,
475475
// then ignore the dependent variables for the lower bound but not for the upper bound.
476476
static Range Merge(const Range& r1, const Range& r2, bool monIncreasing)

0 commit comments

Comments
 (0)