[llvm] Switch lookup tables undef linear mapping (PR #95539)
via llvm-commits
llvm-commits at lists.llvm.org
Fri Jun 14 06:09:24 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: DaPorkchop_ (DaMatrix)
<details>
<summary>Changes</summary>
---
Patch is 28.52 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/95539.diff
3 Files Affected:
- (modified) llvm/lib/Transforms/Utils/SimplifyCFG.cpp (+139-34)
- (modified) llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll (+384-3)
- (modified) llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table_big.ll (+3-3)
``````````diff
diff --git a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
index 292739b6c5fda..26f9be403d2d8 100644
--- a/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
+++ b/llvm/lib/Transforms/Utils/SimplifyCFG.cpp
@@ -6266,7 +6266,8 @@ SwitchLookupTable::SwitchLookupTable(
assert(Values.size() && "Can't build lookup table without values!");
assert(TableSize >= Values.size() && "Can't fit values in table!");
- // If all values in the table are equal, this is that value.
+ // If all values in the table are equal, ignoring any values that are poison,
+ // this is that value.
SingleValue = Values.begin()->second;
Type *ValueType = Values.begin()->second->getType();
@@ -6281,8 +6282,15 @@ SwitchLookupTable::SwitchLookupTable(
uint64_t Idx = (CaseVal->getValue() - Offset->getValue()).getLimitedValue();
TableContents[Idx] = CaseRes;
- if (CaseRes != SingleValue)
- SingleValue = nullptr;
+ if (CaseRes != SingleValue) {
+ if (SingleValue && isa<PoisonValue>(SingleValue)) {
+ // All of the switch cases until now have returned poison; ignore them
+ // and use this value as the single constant value.
+ SingleValue = CaseRes;
+ } else if (!isa<PoisonValue>(CaseRes)) {
+ SingleValue = nullptr;
+ }
+ }
}
// Fill in any holes in the table with the default result.
@@ -6295,7 +6303,10 @@ SwitchLookupTable::SwitchLookupTable(
TableContents[I] = DefaultValue;
}
- if (DefaultValue != SingleValue)
+ // If the default value is poison, all the holes are poison.
+ bool DefaultValueIsPoison = isa<PoisonValue>(DefaultValue);
+
+ if (DefaultValue != SingleValue && !DefaultValueIsPoison)
SingleValue = nullptr;
}
@@ -6306,45 +6317,137 @@ SwitchLookupTable::SwitchLookupTable(
return;
}
+ assert(
+ 2 <= std::count_if(TableContents.begin(), TableContents.end(),
+ [](const auto *C) { return !isa<PoisonValue>(C); }) &&
+ "Should be a SingleValue table.");
+
// Check if we can derive the value with a linear transformation from the
// table index.
if (isa<IntegerType>(ValueType)) {
- bool LinearMappingPossible = true;
- APInt PrevVal;
- APInt DistToPrev;
+ // If we can find two consecutive lookup table entries with a defined value,
+ // we can unambiguously determine the multiplier by computing the difference
+ bool FoundMultiplier = false;
+ APInt OffsetVal;
+ APInt MultiplierVal;
+ for (uint64_t I = 1; I < TableSize; ++I) {
+ ConstantInt *ConstValPrev = dyn_cast<ConstantInt>(TableContents[I - 1]);
+ ConstantInt *ConstValNext = dyn_cast<ConstantInt>(TableContents[I]);
+ if (ConstValPrev && ConstValNext) {
+ FoundMultiplier = true;
+
+ const APInt &PrevVal = ConstValPrev->getValue();
+ const APInt &NextVal = ConstValNext->getValue();
+ MultiplierVal = NextVal - PrevVal;
+ OffsetVal = NextVal - MultiplierVal * I;
+ break;
+ }
+ }
+
+ // If there are no two consecutive lookup table entries with a defined
+ // value, find any two defined entries and try to compute a multiplier
+ // based on their values using the multiplicative inverse.
+ if (!FoundMultiplier) {
+ auto NotPoisonPredicate = [](const auto *C) {
+ return !isa<PoisonValue>(C);
+ };
+
+ // Find the first two table entries with a defined value.
+ // This choice is not optimal - we'd get best results if we preferred to
+ // choose a pair of entries with different values, separated by an odd
+ // number of table indices. However, that would add a lot of complexity,
+ // and it would only improve detection of linear mappings with overflows,
+ // which are not as common.
+ auto First = std::find_if(TableContents.begin(), TableContents.end(),
+ NotPoisonPredicate);
+ auto Second =
+ std::find_if(First + 1, TableContents.end(), NotPoisonPredicate);
+ assert(First != TableContents.end() && Second != TableContents.end() &&
+ "Should have at least two defined values");
+ assert(isa<ConstantInt>(*First) && isa<ConstantInt>(*Second) &&
+ "Should both be ConstantInt");
+
+ uint64_t FirstIndex = First - TableContents.begin();
+ uint64_t SecondIndex = Second - TableContents.begin();
+ const APInt &FirstVal = cast<ConstantInt>(*First)->getValue();
+ const APInt &SecondVal = cast<ConstantInt>(*Second)->getValue();
+
+ // The number of table indices separating the two values, truncated
+ APInt IndexDist(FirstVal.getBitWidth(), SecondIndex - FirstIndex);
+
+ // The difference between the two values
+ APInt ValDist = SecondVal - FirstVal;
+
+ // We need to find an integer M such that M * IndexDist = ValDist.
+ // If the multiplicative inverse of IndexDist exists, we can compute M by:
+ // M = ValDist * (1 / IndexDist)
+ // However, this only works if IndexDist is odd.
+
+ // If IndexDist is even (specifically, if it's divisible by 2^N), we can
+ // make it odd if ValDist is also divisible by 2^N.
+ // M * IndexDist = ValDist
+ // M * (IndexDist / 2^N) = ValDist / 2^N
+ unsigned N = IndexDist.countTrailingZeros();
+ if (N <= ValDist.countTrailingZeros()) {
+ IndexDist.ashrInPlace(N);
+ ValDist.ashrInPlace(N);
+ }
+
+ // If IndexDist is odd (i.e. bit 0 is set), we can proceed:
+ if (IndexDist[0]) {
+ FoundMultiplier = true;
+ MultiplierVal = IndexDist.multiplicativeInverse() * ValDist;
+ OffsetVal = FirstVal - MultiplierVal * FirstIndex;
+
+ assert(OffsetVal + MultiplierVal * FirstIndex == FirstVal &&
+ OffsetVal + MultiplierVal * SecondIndex == SecondVal);
+ }
+ }
+
+ bool LinearMappingPossible = false;
// When linear map is monotonic and signed overflow doesn't happen on
// maximum index, we can attach nsw on Add and Mul.
bool NonMonotonic = false;
- assert(TableSize >= 2 && "Should be a SingleValue table.");
- // Check if there is the same distance between two consecutive values.
- for (uint64_t I = 0; I < TableSize; ++I) {
- ConstantInt *ConstVal = dyn_cast<ConstantInt>(TableContents[I]);
- if (!ConstVal) {
- // This is an undef. We could deal with it, but undefs in lookup tables
- // are very seldom. It's probably not worth the additional complexity.
- LinearMappingPossible = false;
- break;
- }
- const APInt &Val = ConstVal->getValue();
- if (I != 0) {
- APInt Dist = Val - PrevVal;
- if (I == 1) {
- DistToPrev = Dist;
- } else if (Dist != DistToPrev) {
+ // Check if the offset and multiplier actually give the correct result in
+ // all cases.
+ if (FoundMultiplier) {
+ LinearMappingPossible = true;
+ for (uint64_t I = 0; I < TableSize; ++I) {
+ ConstantInt *ConstVal = dyn_cast<ConstantInt>(TableContents[I]);
+ if (!ConstVal) {
+ if (isa<PoisonValue>(TableContents[I])) {
+ // This is poison. We don't care what the linear mapping evaluates
+ // to, so we'll allow it.
+ continue;
+ }
+
+ // TODO: Can we treat undef the same as poison?
+ assert(isa<UndefValue>(TableContents[I]) && "Should be undef");
+ // This is an undef. We could deal with it, but undefs in lookup
+ // tables are very seldom. It's probably not worth the additional
+ // complexity.
+ LinearMappingPossible = false;
+ break;
+ }
+ const APInt &Val = ConstVal->getValue();
+ APInt ComputedVal = OffsetVal + MultiplierVal * I;
+ if (Val != ComputedVal) {
LinearMappingPossible = false;
break;
}
- NonMonotonic |=
- Dist.isStrictlyPositive() ? Val.sle(PrevVal) : Val.sgt(PrevVal);
+ if (I != 0) {
+ APInt PrevVal = ComputedVal - MultiplierVal;
+ NonMonotonic |= MultiplierVal.isStrictlyPositive() ? Val.sle(PrevVal)
+ : Val.sgt(PrevVal);
+ }
}
- PrevVal = Val;
}
if (LinearMappingPossible) {
- LinearOffset = cast<ConstantInt>(TableContents[0]);
- LinearMultiplier = ConstantInt::get(M.getContext(), DistToPrev);
+ LinearOffset = ConstantInt::get(M.getContext(), OffsetVal);
+ LinearMultiplier = ConstantInt::get(M.getContext(), MultiplierVal);
bool MayWrap = false;
- APInt M = LinearMultiplier->getValue();
- (void)M.smul_ov(APInt(M.getBitWidth(), TableSize - 1), MayWrap);
+ (void)MultiplierVal.smul_ov(
+ APInt(MultiplierVal.getBitWidth(), TableSize - 1), MayWrap);
LinearMapValWrapped = NonMonotonic || MayWrap;
Kind = LinearMapKind;
++NumLinearMaps;
@@ -6752,8 +6855,8 @@ static bool SwitchToLookupTable(SwitchInst *SI, IRBuilder<> &Builder,
// If the table has holes but the default destination doesn't produce any
// constant results, the lookup table entries corresponding to the holes will
- // contain undefined values.
- bool AllHolesAreUndefined = TableHasHoles && !HasDefaultResults;
+ // contain poison.
+ bool AllHolesArePoison = TableHasHoles && !HasDefaultResults;
// If the default destination doesn't produce a constant result but is still
// reachable, and the lookup table has holes, we need to use a mask to
@@ -6761,7 +6864,7 @@ static bool SwitchToLookupTable(SwitchInst *SI, IRBuilder<> &Builder,
// to the default case.
// The mask is unnecessary if the table has holes but the default destination
// is unreachable, as in that case the holes must also be unreachable.
- bool NeedMask = AllHolesAreUndefined && DefaultIsReachable;
+ bool NeedMask = AllHolesArePoison && DefaultIsReachable;
if (NeedMask) {
// As an extra penalty for the validity test we require more cases.
if (SI->getNumCases() < 4) // FIXME: Find best threshold value (benchmark).
@@ -6906,9 +7009,11 @@ static bool SwitchToLookupTable(SwitchInst *SI, IRBuilder<> &Builder,
for (PHINode *PHI : PHIs) {
const ResultListTy &ResultList = ResultLists[PHI];
+ Type *ResultType = ResultList.begin()->second->getType();
+
// Use any value to fill the lookup table holes.
Constant *DV =
- AllHolesAreUndefined ? ResultLists[PHI][0].second : DefaultResults[PHI];
+ AllHolesArePoison ? PoisonValue::get(ResultType) : DefaultResults[PHI];
StringRef FuncName = Fn->getName();
SwitchLookupTable Table(Mod, TableSize, TableIndexOffset, ResultList, DV,
DL, FuncName);
diff --git a/llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll b/llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll
index 845c5008e3837..884046b2efa7a 100644
--- a/llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll
+++ b/llvm/test/Transforms/SimplifyCFG/X86/switch_to_lookup_table.ll
@@ -34,11 +34,12 @@ target triple = "x86_64-unknown-linux-gnu"
; CHECK: @switch.table.unreachable_case = private unnamed_addr constant [9 x i32] [i32 0, i32 0, i32 0, i32 2, i32 -1, i32 1, i32 1, i32 1, i32 1], align 4
; CHECK: @switch.table.unreachable_default = private unnamed_addr constant [4 x i32] [i32 42, i32 52, i32 1, i32 2], align 4
; CHECK: @switch.table.nodefaultnoholes = private unnamed_addr constant [4 x i32] [i32 55, i32 123, i32 0, i32 -1], align 4
-; CHECK: @switch.table.nodefaultwithholes = private unnamed_addr constant [6 x i32] [i32 55, i32 123, i32 0, i32 -1, i32 55, i32 -1], align 4
+; CHECK: @switch.table.nodefaultwithholes = private unnamed_addr constant [6 x i32] [i32 55, i32 123, i32 0, i32 -1, i32 poison, i32 -1], align 4
; CHECK: @switch.table.threecases = private unnamed_addr constant [3 x i32] [i32 10, i32 7, i32 5], align 4
-; CHECK: @switch.table.covered_switch_with_bit_tests = private unnamed_addr constant [8 x i32] [i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 1, i32 1], align 4
+; CHECK: @switch.table.covered_switch_with_bit_tests = private unnamed_addr constant [8 x i32] [i32 2, i32 2, i32 poison, i32 poison, i32 poison, i32 poison, i32 1, i32 1], align 4
; CHECK: @switch.table.signed_overflow1 = private unnamed_addr constant [4 x i32] [i32 3333, i32 4444, i32 1111, i32 2222], align 4
-; CHECK: @switch.table.signed_overflow2 = private unnamed_addr constant [4 x i32] [i32 3333, i32 4444, i32 2222, i32 2222], align 4
+; CHECK: @switch.table.signed_overflow2 = private unnamed_addr constant [4 x i32] [i32 3333, i32 4444, i32 poison, i32 2222], align 4
+; CHECK: @switch.table.linearmap_unreachable_holes_noconsecutive_signed_overflow_negative1 = private unnamed_addr constant [7 x i32] [i32 1111, i32 poison, i32 2222, i32 poison, i32 3333, i32 poison, i32 4444], align 4
;.
define i32 @f(i32 %c) {
; CHECK-LABEL: @f(
@@ -2151,3 +2152,383 @@ return: ; preds = %sw.default, %entry,
%retval.0 = phi { i8, i8 } [ undef, %entry ], [ undef, %entry ], [ undef, %entry ], [ %1, %sw.default ]
ret { i8, i8 } %retval.0
}
+
+; The switch has a hole which falls through to an unreachable default case, but it can still be optimized into a constant load because
+; the poison value used for the hole is ignored.
+define i32 @constant_hole_unreachable_default(i32 %x) {
+; CHECK-LABEL: @constant_hole_unreachable_default(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: ret i32 1
+;
+entry:
+ switch i32 %x, label %sw.default [
+ i32 0, label %bb0
+ i32 2, label %bb0
+ i32 3, label %bb0
+ i32 4, label %bb0
+ ]
+
+sw.default: unreachable
+bb0: br label %return
+
+return:
+ %res = phi i32 [ 1, %bb0 ]
+ ret i32 %res
+}
+
+; The switch has a hole which falls through to an unreachable default case and the first case explicitly returns poison, but it can still
+; be optimized into a constant load because the poison values are ignored.
+define i32 @constant_hole_unreachable_default_firstpoison(i32 %x) {
+; CHECK-LABEL: @constant_hole_unreachable_default_firstpoison(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: ret i32 1
+;
+entry:
+ switch i32 %x, label %sw.default [
+ i32 0, label %bb.poison
+ i32 2, label %bb0
+ i32 3, label %bb0
+ i32 4, label %bb0
+ ]
+
+sw.default: unreachable
+bb.poison: br label %return
+bb0: br label %return
+
+return:
+ %res = phi i32 [ poison, %bb.poison ], [ 1, %bb0 ]
+ ret i32 %res
+}
+
+; The switch has a hole which falls through to an unreachable default case and the first case explicitly returns poison, but it can still
+; be optimized into a constant load because the poison values are ignored.
+define i32 @constant_hole_unreachable_default_lastpoison(i32 %x) {
+; CHECK-LABEL: @constant_hole_unreachable_default_lastpoison(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: ret i32 1
+;
+entry:
+ switch i32 %x, label %sw.default [
+ i32 0, label %bb0
+ i32 2, label %bb0
+ i32 3, label %bb0
+ i32 4, label %bb.poison
+ ]
+
+sw.default: unreachable
+bb.poison: br label %return
+bb0: br label %return
+
+return:
+ %res = phi i32 [ poison, %bb.poison ], [ 1, %bb0 ]
+ ret i32 %res
+}
+
+; The switch has a hole which falls through to an unreachable default case, but can still be optimized into a linear mapping 2*x+1.
+define i32 @linearmap_hole_unreachable_default(i32 %x) {
+; CHECK-LABEL: @linearmap_hole_unreachable_default(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[SWITCH_IDX_MULT:%.*]] = mul nsw i32 [[X:%.*]], 2
+; CHECK-NEXT: [[SWITCH_OFFSET:%.*]] = add nsw i32 [[SWITCH_IDX_MULT]], 1
+; CHECK-NEXT: ret i32 [[SWITCH_OFFSET]]
+;
+entry:
+ switch i32 %x, label %sw.default [
+ i32 0, label %bb0
+ i32 2, label %bb2
+ i32 3, label %bb3
+ i32 4, label %bb4
+ ]
+
+sw.default: unreachable
+bb0: br label %return
+bb2: br label %return
+bb3: br label %return
+bb4: br label %return
+
+return:
+ %res = phi i32 [ 1, %bb0 ], [ 5, %bb2 ], [ 7, %bb3 ], [ 9, %bb4 ]
+ ret i32 %res
+}
+
+; We can optimize this into a linear mapping even though every case is separated from the others by an unreachable hole.
+define i32 @linearmap_unreachable_holes_noconsecutive(i32 %x) {
+; CHECK-LABEL: @linearmap_unreachable_holes_noconsecutive(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[SWITCH_IDX_MULT:%.*]] = mul nsw i32 [[X:%.*]], 2
+; CHECK-NEXT: [[SWITCH_OFFSET:%.*]] = add nsw i32 [[SWITCH_IDX_MULT]], 1
+; CHECK-NEXT: ret i32 [[SWITCH_OFFSET]]
+;
+entry:
+ switch i32 %x, label %sw.default [
+ i32 0, label %bb0
+ i32 2, label %bb2
+ i32 4, label %bb4
+ i32 6, label %bb6
+ ]
+
+sw.default: unreachable
+bb0: br label %return
+bb2: br label %return
+bb4: br label %return
+bb6: br label %return
+
+return:
+ %res = phi i32 [ 1, %bb0 ], [ 5, %bb2 ], [ 9, %bb4 ], [ 13, %bb6 ]
+ ret i32 %res
+}
+
+; The switch is very sparse; every case is separated from the othery by a hole which falls through to an unreachable default case.
+; Despite that, it can still be optimized into a linear mapping 2*x+1.
+define i32 @linearmap_unreachable_holes_noconsecutive_neg(i32 %x) {
+; CHECK-LABEL: @linearmap_unreachable_holes_noconsecutive_neg(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[SWITCH_TABLEIDX:%.*]] = sub nsw i32 [[X:%.*]], -1
+; CHECK-NEXT: [[SWITCH_IDX_MULT:%.*]] = mul nsw i32 [[SWITCH_TABLEIDX]], 2
+; CHECK-NEXT: [[SWITCH_OFFSET:%.*]] = add nsw i32 [[SWITCH_IDX_MULT]], -1
+; CHECK-NEXT: ret i32 [[SWITCH_OFFSET]]
+;
+entry:
+ switch i32 %x, label %sw.default [
+ i32 -1, label %bb0
+ i32 2, label %bb2
+ i32 4, label %bb4
+ i32 6, label %bb6
+ ]
+
+sw.default: unreachable
+bb0: br label %return
+bb2: br label %return
+bb4: br label %return
+bb6: br label %return
+
+return:
+ %res = phi i32 [ -1, %bb0 ], [ 5, %bb2 ], [ 9, %bb4 ], [ 13, %bb6 ]
+ ret i32 %res
+}
+
+; This corresponds to @signed_overflow_negative above.
+; We can't optimize this into a linear mapping because it would need a multiplier of 1111/2 (which isn't an integer)
+define i32 @linearmap_unreachable_holes_noconsecutive_signed_overflow_negative1(i32 %x) {
+; CHECK-LABEL: @linearmap_unreachable_holes_noconsecutive_signed_overflow_negative1(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TRUNC:%.*]] = trunc i32 [[X:%.*]] to i3
+; CHECK-NEXT: [[SWITCH_TABLEIDX:%.*]] = sub i3 [[TRUNC]], -4
+; CHECK-NEXT: [[SWITCH_TABLEIDX_ZEXT:%.*]] = zext i3 [[SWITCH_TABLEIDX]] to i4
+; CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds [7 x i32], ptr @switch.table.linearmap_unreachable_holes_noconsecutive_signed_overflow_negative1, i32 0, i4 [[SWITCH_TABLEIDX_ZEXT]]
+; CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i32, ptr [[SWITCH_GEP]], align 4
+; CHECK-NEXT: ret i32 [[SWITCH_LOAD]]
+;
+entry:
+ %trunc = trunc i32 %x to i3
+ switch i3 %trunc, label %sw.default [
+ i3 0, label %bb0
+ i3 2, label %bb2
+ i3 -4, label %bbm4
+ i3 -2, label %bbm2
+ ]
+
+sw.default: unreachable
+bb0: br label %return
+bb2: br label %return
+bbm4: br label %return
+bbm2: br label %return
+
+return:
+ %res = phi i32 [ 3333, %bb0 ], [ 4444, %bb2 ], [ 1111, %bbm4 ], [ 2222, %bbm2 ]
+ ret i32 %res
+}
+
+; This corresponds to @signed_overflow_negative above.
+; We can optimize this into a linear mapping.
+define i32 @linearmap_unreachable_holes_noconsecutive_signed_overflow_negative2(i32 %x) {
+; CHECK-LABEL: @linearmap_unreachable_holes_noconsecutive_signed_overflow_negative2(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TRUNC:%.*]] = trunc i32 [[X:%.*]] to i3
+; CHECK-NEXT: [[SWITCH_TABLEIDX:%.*]] = sub i3 [[TRUNC]], -4
+; CHECK-NEXT: [[SWITCH_IDX_CAST:%.*]] = zext i3 [[SWITCH_TABLEIDX]] to i32
+; CHECK-NEXT: [[SWITCH_IDX_MULT:%.*]] = mul nsw i32 [[SWITCH_IDX_CAST]], 1111
+; CHECK-NEXT: [[SWITCH_OFFSET:%.*]] = add nsw i32 [[SWITCH_IDX_MULT]], 1111
+; CHECK-NEXT: ret i32 [[SWITCH_OFFSET]]
+;
+entry:
+ %trunc = trunc i32 %x to i3
+ switch i3 %trunc, label %sw.default [
+ i3 0, label %bb0
+ i3 2, label %bb2
+ i3 -4, label %bbm4
+ i3 -2, label %bbm2
+ ]
+
+sw.default: unreachable
+bb0: br label %return
+bb2: br label %return
+bbm4: br label %return
+bbm2: br label %return
+
+return:
+ %res = phi i32 [ 5555, %bb0 ], [ 7777, %bb2 ], [ 1111, %bbm4 ], [ 3333, %bbm2 ]
+ ret i32 %res
+}
+
+; This corresponds to @linearmap_inc_nsw above.
+define i8 @linearmap_unreachable_holes_noconsecutive_signed_linearmap_inc_nsw(i32 %x) {
+; CHECK-LABEL:...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/95539
More information about the llvm-commits
mailing list