This patch should catch more integer shift left undefined behavior according
to C and C++ standard semantics. Specifically it should catch the case where
a signed integer type when shifted is no longer representable in the result
type.
There are some general cleanups that can be made to the existing undefined
behavior check here given the this change, but I just wanted to add
functionality here. Let me know if I should just commit freely when adding
these types of checks (provided proper testing and certainty that the
behavior is in fact undefined). Also any guidance on other languages or
specs I should be checking prior to enabling such checks (Currently looking
at C99, C++98, C++03, and C++0x) would be welcome.
Finally, the testcase is a bit lame. I'd really like to test this by
creating sources that exhibit the undefined behavior being checked, compile
and run them verifying crash and non-crash inputs. Is that do-able in the
current test harness? Seems unlikely, but if folks have ideas I'm all ears.
Thanks!
diff --git a/lib/CodeGen/CGExprScalar.cpp b/lib/CodeGen/CGExprScalar.cpp
index b1d4575..6eac4dc 100644
--- a/lib/CodeGen/CGExprScalar.cpp
+++ b/lib/CodeGen/CGExprScalar.cpp
@@ -2015,14 +2015,37 @@ Value *ScalarExprEmitter::EmitShl(const BinOpInfo &Ops) {
if (Ops.LHS->getType() != RHS->getType())
RHS = Builder.CreateIntCast(RHS, Ops.LHS->getType(), false, "sh_prom");
- if (CGF.CatchUndefined
- && isa<llvm::IntegerType>(Ops.LHS->getType())) {
- unsigned Width = cast<llvm::IntegerType>(Ops.LHS->getType())->getBitWidth();
- llvm::BasicBlock *Cont = CGF.createBasicBlock("cont");
- CGF.Builder.CreateCondBr(Builder.CreateICmpULT(RHS,
- llvm::ConstantInt::get(RHS->getType(), Width)),
- Cont, CGF.getTrapBB());
- CGF.EmitBlock(Cont);
+ if (CGF.CatchUndefined) {
+ // Always trap shift amounts that exceed the bitwidth.
+ if (const llvm::IntegerType *LHSValueTy =
+ dyn_cast<llvm::IntegerType>(Ops.LHS->getType())) {
+ unsigned Width = LHSValueTy->getBitWidth();
+ llvm::BasicBlock *Cont = CGF.createBasicBlock("cont");
+ CGF.Builder.CreateCondBr(Builder.CreateICmpULT(RHS,
+ llvm::ConstantInt::get(RHS->getType(), Width)),
+ Cont, CGF.getTrapBB());
+ CGF.EmitBlock(Cont);
+ }
+
+ // Trap shifts of signed integers which are not representable in the
+ // resulting type. We use Clang's types to correctly detect the sign.
+ if (const BinaryOperator *Op = dyn_cast<BinaryOperator>(Ops.E)) {
+ if (const BuiltinType *LHSTy = Op->getLHS()->getType()->getAs<BuiltinType>()) {
+ if (LHSTy->isSignedInteger()) {
+ // We verify that the shift result is representable by computing it,
+ // shifting it back into place and checking that the value did not
+ // change. If the value changed, then bits were lost during the
+ // transformation.
+ Value *Result = Builder.CreateShl(Ops.LHS, RHS, "shl");
+ Value *RecomputedLHS = Builder.CreateAShr(Result, RHS, "recomp");
+ llvm::BasicBlock *Cont = CGF.createBasicBlock("cont");
+ Builder.CreateCondBr(Builder.CreateICmpEQ(RecomputedLHS, Ops.LHS),
+ Cont, CGF.getTrapBB());
+ CGF.EmitBlock(Cont);
+ return Result;
+ }
+ }
+ }
}
return Builder.CreateShl(Ops.LHS, RHS, "shl");
diff --git a/test/CodeGen/catch-undef-behavior.c b/test/CodeGen/catch-undef-behavior.c
index fef1587..79a82b4 100644
--- a/test/CodeGen/catch-undef-behavior.c
+++ b/test/CodeGen/catch-undef-behavior.c
@@ -1,4 +1,21 @@
-// RUN: %clang_cc1 -fcatch-undefined-behavior -emit-llvm-only %s
+// RUN: %clang_cc1 -triple x86_64-pc-linux-gnu -fcatch-undefined-behavior -emit-llvm -o - %s | FileCheck %s
+
+void test_shl(int a, int b) {
+ (void)(a << b);
+
+ // CHECK: @test_shl
+ //
+ // We fixed the triple to a x86-64, check for a 32-bitwidth test due to 'int'
+ // type.
+ // CHECK: icmp ult i32 {{.*}}, 32
+ //
+ // We recompute the original value from the shifted value to check for data
+ // loss. Look for the equality test as there shouldn't be any others.
+ // CHECK: icmp eq
+ //
+ // CHECK: ret
+}
+
// PR6805
void foo() {
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits