-
Notifications
You must be signed in to change notification settings - Fork 15.6k
[ConstantInt] Disable implicit truncation in ConstantInt::get() #171456
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
base: main
Are you sure you want to change the base?
Conversation
Explicitly cast the value to (int) before negating, so it gets properly sign extended. Otherwise we end up with a large unsigned value instead of a negative value for large bit widths. This was found while working on #171456.
Explicitly cast the value to (int) before negating, so it gets properly sign extended. Otherwise we end up with a large unsigned value instead of a negative value for large bit widths. This was found while working on llvm/llvm-project#171456.
Explicitly cast the value to (int) before negating, so it gets properly sign extended. Otherwise we end up with a large unsigned value instead of a negative value for large bit widths. This was found while working on llvm#171456.
Use getSigned() to create the 1 or -1 constant, so it gets properly sign extended. This miscompile was found while working on #171456.
0eb2003 to
9e2cace
Compare
…172301) Use getSigned() to create the 1 or -1 constant, so it gets properly sign extended. This miscompile was found while working on llvm/llvm-project#171456.
🐧 Linux x64 Test Results
✅ The build succeeded and all tests passed. |
2f4c8d2 to
989ffc3
Compare
|
@llvm/pr-subscribers-llvm-analysis @llvm/pr-subscribers-llvm-transforms Author: Nikita Popov (nikic) ChangesDisable implicit truncation in the ConstantInt constructor by default. This means that it needs to be passed a signed/unsigned (depending on the IsSigned flag) value matching the bit width. The intention is to prevent the recurring bug where people write something like This is the continuation of #112670, which originally allowed implicit truncation in this constructor to reduce initial scope of the change. In a number of places, set ImplicitTrunc=true with a TODO to reduce the scope of the change. Other places that violate the new requirement have been fixed in advance. Full diff: https://github.com/llvm/llvm-project/pull/171456.diff 8 Files Affected:
diff --git a/llvm/include/llvm/IR/Constants.h b/llvm/include/llvm/IR/Constants.h
index 39a556abe935b..6aa79846f10f5 100644
--- a/llvm/include/llvm/IR/Constants.h
+++ b/llvm/include/llvm/IR/Constants.h
@@ -113,9 +113,8 @@ class ConstantInt final : public ConstantData {
/// If Ty is a vector type, return a Constant with a splat of the given
/// value. Otherwise return a ConstantInt for the given value.
/// \param ImplicitTrunc Whether to allow implicit truncation of the value.
- // TODO: Make ImplicitTrunc default to false.
LLVM_ABI static Constant *get(Type *Ty, uint64_t V, bool IsSigned = false,
- bool ImplicitTrunc = true);
+ bool ImplicitTrunc = false);
/// Return a ConstantInt with the specified integer value for the specified
/// type. If the type is wider than 64 bits, the value will be zero-extended
@@ -123,10 +122,9 @@ class ConstantInt final : public ConstantData {
/// be interpreted as a 64-bit signed integer and sign-extended to fit
/// the type.
/// \param ImplicitTrunc Whether to allow implicit truncation of the value.
- // TODO: Make ImplicitTrunc default to false.
LLVM_ABI static ConstantInt *get(IntegerType *Ty, uint64_t V,
bool IsSigned = false,
- bool ImplicitTrunc = true);
+ bool ImplicitTrunc = false);
/// Return a ConstantInt with the specified value for the specified type. The
/// value V will be canonicalized to an unsigned APInt. Accessing it with
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index e1f90264be7a2..770de89eccfb9 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -485,7 +485,10 @@ const SCEV *ScalarEvolution::getConstant(const APInt &Val) {
const SCEV *
ScalarEvolution::getConstant(Type *Ty, uint64_t V, bool isSigned) {
IntegerType *ITy = cast<IntegerType>(getEffectiveSCEVType(Ty));
- return getConstant(ConstantInt::get(ITy, V, isSigned));
+ // TODO: Avoid implicit trunc?
+ // See https://github.com/llvm/llvm-project/issues/112510.
+ return getConstant(
+ ConstantInt::get(ITy, V, isSigned, /*ImplicitTrunc=*/true));
}
const SCEV *ScalarEvolution::getVScale(Type *Ty) {
diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp
index 587c1372b19cb..7ec6113aaeafc 100644
--- a/llvm/lib/CodeGen/CodeGenPrepare.cpp
+++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp
@@ -6881,7 +6881,10 @@ bool CodeGenPrepare::splitLargeGEPOffsets() {
}
IRBuilder<> NewBaseBuilder(NewBaseInsertBB, NewBaseInsertPt);
// Create a new base.
- Value *BaseIndex = ConstantInt::get(PtrIdxTy, BaseOffset);
+ // TODO: Avoid implicit trunc?
+ // See https://github.com/llvm/llvm-project/issues/112510.
+ Value *BaseIndex = ConstantInt::get(
+ PtrIdxTy, BaseOffset, /*IsSigned=*/true, /*ImplicitTrunc=*/true);
NewBaseGEP = OldBase;
if (NewBaseGEP->getType() != I8PtrTy)
NewBaseGEP = NewBaseBuilder.CreatePointerCast(NewBaseGEP, I8PtrTy);
diff --git a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp
index 3906b311addf0..f4779d9a122f0 100644
--- a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp
@@ -363,7 +363,10 @@ MachineInstrBuilder MachineIRBuilder::buildConstant(const DstOp &Res,
int64_t Val) {
auto IntN = IntegerType::get(getMF().getFunction().getContext(),
Res.getLLTTy(*getMRI()).getScalarSizeInBits());
- ConstantInt *CI = ConstantInt::get(IntN, Val, true);
+ // TODO: Avoid implicit trunc?
+ // See https://github.com/llvm/llvm-project/issues/112510.
+ ConstantInt *CI =
+ ConstantInt::get(IntN, Val, /*isSigned=*/true, /*implicitTrunc=*/true);
return buildConstant(Res, *CI);
}
diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
index 51391f1aeecde..03727b77b3e1a 100644
--- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
@@ -1943,7 +1943,10 @@ Register FastISel::fastEmit_ri_(MVT VT, unsigned Opcode, Register Op0,
// fast-isel, which would be very slow.
IntegerType *ITy =
IntegerType::get(FuncInfo.Fn->getContext(), VT.getSizeInBits());
- MaterialReg = getRegForValue(ConstantInt::get(ITy, Imm));
+ // TODO: Avoid implicit trunc?
+ // See https://github.com/llvm/llvm-project/issues/112510.
+ MaterialReg = getRegForValue(
+ ConstantInt::get(ITy, Imm, /*IsSigned=*/false, /*ImplicitTrunc=*/true));
if (!MaterialReg)
return Register();
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index 5d96a67500dff..7325dee73d8f2 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -436,7 +436,10 @@ Register SPIRVGlobalRegistry::buildConstantInt(uint64_t Val,
assert(SpvType);
auto &MF = MIRBuilder.getMF();
const IntegerType *Ty = cast<IntegerType>(getTypeForSPIRVType(SpvType));
- auto *const CI = ConstantInt::get(const_cast<IntegerType *>(Ty), Val);
+ // TODO: Avoid implicit trunc?
+ // See https://github.com/llvm/llvm-project/issues/112510.
+ auto *const CI = ConstantInt::get(const_cast<IntegerType *>(Ty), Val,
+ /*IsSigned=*/false, /*ImplicitTrunc=*/true);
Register Res = find(CI, &MF);
if (Res.isValid())
return Res;
diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
index 32ee16c89b4fe..9909037ba8ec5 100644
--- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
@@ -1791,7 +1791,10 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
constToIntPtr(VectTy->getElementType(), C));
}
assert(IntPtrTy == MS.IntptrTy);
- return ConstantInt::get(MS.IntptrTy, C);
+ // TODO: Avoid implicit trunc?
+ // See https://github.com/llvm/llvm-project/issues/112510.
+ return ConstantInt::get(MS.IntptrTy, C, /*IsSigned=*/false,
+ /*ImplicitTrunc=*/true);
}
/// Returns the integer shadow offset that corresponds to a given
diff --git a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
index e12caa2136962..7f28ea6f5c875 100644
--- a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
@@ -331,7 +331,10 @@ class Immediate : public details::FixedOrScalableQuantity<Immediate, int64_t> {
}
const SCEV *getUnknownSCEV(ScalarEvolution &SE, Type *Ty) const {
- const SCEV *SU = SE.getUnknown(ConstantInt::getSigned(Ty, Quantity));
+ // TODO: Avoid implicit trunc?
+ // See https://github.com/llvm/llvm-project/issues/112510.
+ const SCEV *SU = SE.getUnknown(ConstantInt::get(
+ Ty, Quantity, /*IsSigned=*/true, /*ImplicitTrunc=*/true));
if (Scalable)
SU = SE.getMulExpr(SU, SE.getVScale(SU->getType()));
return SU;
|
|
@llvm/pr-subscribers-backend-spir-v Author: Nikita Popov (nikic) ChangesDisable implicit truncation in the ConstantInt constructor by default. This means that it needs to be passed a signed/unsigned (depending on the IsSigned flag) value matching the bit width. The intention is to prevent the recurring bug where people write something like This is the continuation of #112670, which originally allowed implicit truncation in this constructor to reduce initial scope of the change. In a number of places, set ImplicitTrunc=true with a TODO to reduce the scope of the change. Other places that violate the new requirement have been fixed in advance. Full diff: https://github.com/llvm/llvm-project/pull/171456.diff 8 Files Affected:
diff --git a/llvm/include/llvm/IR/Constants.h b/llvm/include/llvm/IR/Constants.h
index 39a556abe935ba..6aa79846f10f5f 100644
--- a/llvm/include/llvm/IR/Constants.h
+++ b/llvm/include/llvm/IR/Constants.h
@@ -113,9 +113,8 @@ class ConstantInt final : public ConstantData {
/// If Ty is a vector type, return a Constant with a splat of the given
/// value. Otherwise return a ConstantInt for the given value.
/// \param ImplicitTrunc Whether to allow implicit truncation of the value.
- // TODO: Make ImplicitTrunc default to false.
LLVM_ABI static Constant *get(Type *Ty, uint64_t V, bool IsSigned = false,
- bool ImplicitTrunc = true);
+ bool ImplicitTrunc = false);
/// Return a ConstantInt with the specified integer value for the specified
/// type. If the type is wider than 64 bits, the value will be zero-extended
@@ -123,10 +122,9 @@ class ConstantInt final : public ConstantData {
/// be interpreted as a 64-bit signed integer and sign-extended to fit
/// the type.
/// \param ImplicitTrunc Whether to allow implicit truncation of the value.
- // TODO: Make ImplicitTrunc default to false.
LLVM_ABI static ConstantInt *get(IntegerType *Ty, uint64_t V,
bool IsSigned = false,
- bool ImplicitTrunc = true);
+ bool ImplicitTrunc = false);
/// Return a ConstantInt with the specified value for the specified type. The
/// value V will be canonicalized to an unsigned APInt. Accessing it with
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index e1f90264be7a26..770de89eccfb9a 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -485,7 +485,10 @@ const SCEV *ScalarEvolution::getConstant(const APInt &Val) {
const SCEV *
ScalarEvolution::getConstant(Type *Ty, uint64_t V, bool isSigned) {
IntegerType *ITy = cast<IntegerType>(getEffectiveSCEVType(Ty));
- return getConstant(ConstantInt::get(ITy, V, isSigned));
+ // TODO: Avoid implicit trunc?
+ // See https://github.com/llvm/llvm-project/issues/112510.
+ return getConstant(
+ ConstantInt::get(ITy, V, isSigned, /*ImplicitTrunc=*/true));
}
const SCEV *ScalarEvolution::getVScale(Type *Ty) {
diff --git a/llvm/lib/CodeGen/CodeGenPrepare.cpp b/llvm/lib/CodeGen/CodeGenPrepare.cpp
index 587c1372b19cb0..7ec6113aaeafc1 100644
--- a/llvm/lib/CodeGen/CodeGenPrepare.cpp
+++ b/llvm/lib/CodeGen/CodeGenPrepare.cpp
@@ -6881,7 +6881,10 @@ bool CodeGenPrepare::splitLargeGEPOffsets() {
}
IRBuilder<> NewBaseBuilder(NewBaseInsertBB, NewBaseInsertPt);
// Create a new base.
- Value *BaseIndex = ConstantInt::get(PtrIdxTy, BaseOffset);
+ // TODO: Avoid implicit trunc?
+ // See https://github.com/llvm/llvm-project/issues/112510.
+ Value *BaseIndex = ConstantInt::get(
+ PtrIdxTy, BaseOffset, /*IsSigned=*/true, /*ImplicitTrunc=*/true);
NewBaseGEP = OldBase;
if (NewBaseGEP->getType() != I8PtrTy)
NewBaseGEP = NewBaseBuilder.CreatePointerCast(NewBaseGEP, I8PtrTy);
diff --git a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp
index 3906b311addf00..f4779d9a122f0c 100644
--- a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp
+++ b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp
@@ -363,7 +363,10 @@ MachineInstrBuilder MachineIRBuilder::buildConstant(const DstOp &Res,
int64_t Val) {
auto IntN = IntegerType::get(getMF().getFunction().getContext(),
Res.getLLTTy(*getMRI()).getScalarSizeInBits());
- ConstantInt *CI = ConstantInt::get(IntN, Val, true);
+ // TODO: Avoid implicit trunc?
+ // See https://github.com/llvm/llvm-project/issues/112510.
+ ConstantInt *CI =
+ ConstantInt::get(IntN, Val, /*isSigned=*/true, /*implicitTrunc=*/true);
return buildConstant(Res, *CI);
}
diff --git a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
index 51391f1aeecdef..03727b77b3e1a0 100644
--- a/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/FastISel.cpp
@@ -1943,7 +1943,10 @@ Register FastISel::fastEmit_ri_(MVT VT, unsigned Opcode, Register Op0,
// fast-isel, which would be very slow.
IntegerType *ITy =
IntegerType::get(FuncInfo.Fn->getContext(), VT.getSizeInBits());
- MaterialReg = getRegForValue(ConstantInt::get(ITy, Imm));
+ // TODO: Avoid implicit trunc?
+ // See https://github.com/llvm/llvm-project/issues/112510.
+ MaterialReg = getRegForValue(
+ ConstantInt::get(ITy, Imm, /*IsSigned=*/false, /*ImplicitTrunc=*/true));
if (!MaterialReg)
return Register();
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
index 5d96a67500dffd..7325dee73d8f24 100644
--- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp
@@ -436,7 +436,10 @@ Register SPIRVGlobalRegistry::buildConstantInt(uint64_t Val,
assert(SpvType);
auto &MF = MIRBuilder.getMF();
const IntegerType *Ty = cast<IntegerType>(getTypeForSPIRVType(SpvType));
- auto *const CI = ConstantInt::get(const_cast<IntegerType *>(Ty), Val);
+ // TODO: Avoid implicit trunc?
+ // See https://github.com/llvm/llvm-project/issues/112510.
+ auto *const CI = ConstantInt::get(const_cast<IntegerType *>(Ty), Val,
+ /*IsSigned=*/false, /*ImplicitTrunc=*/true);
Register Res = find(CI, &MF);
if (Res.isValid())
return Res;
diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
index 32ee16c89b4fee..9909037ba8ec51 100644
--- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
+++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp
@@ -1791,7 +1791,10 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
constToIntPtr(VectTy->getElementType(), C));
}
assert(IntPtrTy == MS.IntptrTy);
- return ConstantInt::get(MS.IntptrTy, C);
+ // TODO: Avoid implicit trunc?
+ // See https://github.com/llvm/llvm-project/issues/112510.
+ return ConstantInt::get(MS.IntptrTy, C, /*IsSigned=*/false,
+ /*ImplicitTrunc=*/true);
}
/// Returns the integer shadow offset that corresponds to a given
diff --git a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
index e12caa21369626..7f28ea6f5c8750 100644
--- a/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
+++ b/llvm/lib/Transforms/Scalar/LoopStrengthReduce.cpp
@@ -331,7 +331,10 @@ class Immediate : public details::FixedOrScalableQuantity<Immediate, int64_t> {
}
const SCEV *getUnknownSCEV(ScalarEvolution &SE, Type *Ty) const {
- const SCEV *SU = SE.getUnknown(ConstantInt::getSigned(Ty, Quantity));
+ // TODO: Avoid implicit trunc?
+ // See https://github.com/llvm/llvm-project/issues/112510.
+ const SCEV *SU = SE.getUnknown(ConstantInt::get(
+ Ty, Quantity, /*IsSigned=*/true, /*ImplicitTrunc=*/true));
if (Scalable)
SU = SE.getMulExpr(SU, SE.getVScale(SU->getType()));
return SU;
|
RKSimon
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM - just a couple of queries.
What about ConstantInt::getSigned - have you considered adding a ImplicitTrunc flag to that as well?
Also, we've only got a few weeks before next release branch (including the quiet holiday period with less reports), and its likely random assert regression bugs will inevitably appear for a few months - are we OK with that (and merging fixes etc.)?
I'd like to land this patch after the next release. |
My plan was to land this change on main and then revert it on the release branch. |
|
Crash reproducer: |
Explicitly cast the value to (int) before negating, so it gets properly sign extended. Otherwise we end up with a large unsigned value instead of a negative value for large bit widths. This was found while working on llvm#171456.
|
Crash reproducer: |
|
I've submitted #173229 to fix that case. Also included the change in this PR for further testing. |
Use getSigned() to create the 1 or -1 constant, so it gets properly sign extended. This miscompile was found while working on llvm#171456.
Fixes the issue reported at: llvm#171456 (comment)
…172875) For consistency with `ConstantInt::get()`, add an ImplicitTrunc parameter to `ConstantInt::getSigned()` as well. It currently defaults to true and will be flipped to false in the future (by llvm#171456).
LV can create step vectors that wrap around, e.g. `step-vector i1` with VF>2. Allow truncation when creating the vector constant to avoid an assertion failure with #171456. After #173494 the definition of the llvm.stepvector intrinsic has been changed to make it have wrapping semantics, so the semantics for the fixed and scalable case match now.
…#173229) LV can create step vectors that wrap around, e.g. `step-vector i1` with VF>2. Allow truncation when creating the vector constant to avoid an assertion failure with llvm/llvm-project#171456. After llvm/llvm-project#173494 the definition of the llvm.stepvector intrinsic has been changed to make it have wrapping semantics, so the semantics for the fixed and scalable case match now.
dtcxzyw
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think my existing fuzzing corpus cannot discover more violations. It should be fine to land and wait for crash reports from other continuous fuzzing systems.
Remember to notify the release manager to revert this patch in the next release.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM (with a suitable merge conflict fix) - no objections to either immediate committal (+ reversion in 22.x) or waiting for 22.x to branch
To limit scope.
To reduce scope.
Just getSigned() is sufficient for the test case, but given that we can get overflow in the other three cases, we can probably also get it here, so defensively set ImplicitTrunc=true here as well.
24a845f to
68bdf4b
Compare
topperc
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Disable implicit truncation in the ConstantInt constructor by default. This means that it needs to be passed a signed/unsigned (depending on the IsSigned flag) value matching the bit width.
The intention is to prevent the recurring bug where people write something like
ConstantInt::get(Ty, -1), and this "works" untilTyis larger than 64-bit and then the value is incorrect due to missing type extension.This is the continuation of #112670, which originally allowed implicit truncation in this constructor to reduce initial scope of the change.
In a number of places, set ImplicitTrunc=true with a TODO to reduce the scope of the change. Other places that violate the new requirement have been fixed in advance.