Evaluate: Fix a subtle bug in the pointer evaluator in which we would do an
expression computation in the wrong bit-width, and end up generating a totally
bogus array reference (_g0+8589934546).
- This showed up on Prolangs/cdecl.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@99042 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index e036692..eeeeb5c 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -406,27 +406,34 @@
if (!EvaluatePointer(PExp, ResultLValue, Info))
return APValue();
- llvm::APSInt AdditionalOffset(32);
+ llvm::APSInt AdditionalOffset;
if (!EvaluateInteger(IExp, AdditionalOffset, Info))
return APValue();
- QualType PointeeType = PExp->getType()->getAs<PointerType>()->getPointeeType();
- CharUnits SizeOfPointee;
+ // Compute the new offset in the appropriate width.
+
+ QualType PointeeType =
+ PExp->getType()->getAs<PointerType>()->getPointeeType();
+ llvm::APSInt SizeOfPointee(AdditionalOffset);
// Explicitly handle GNU void* and function pointer arithmetic extensions.
if (PointeeType->isVoidType() || PointeeType->isFunctionType())
- SizeOfPointee = CharUnits::One();
+ SizeOfPointee = 1;
else
- SizeOfPointee = Info.Ctx.getTypeSizeInChars(PointeeType);
+ SizeOfPointee = Info.Ctx.getTypeSizeInChars(PointeeType).getQuantity();
- CharUnits Offset = ResultLValue.getLValueOffset();
-
+ llvm::APSInt Offset(AdditionalOffset);
+ Offset = ResultLValue.getLValueOffset().getQuantity();
if (E->getOpcode() == BinaryOperator::Add)
- Offset += AdditionalOffset.getLimitedValue() * SizeOfPointee;
+ Offset += AdditionalOffset * SizeOfPointee;
else
- Offset -= AdditionalOffset.getLimitedValue() * SizeOfPointee;
+ Offset -= AdditionalOffset * SizeOfPointee;
- return APValue(ResultLValue.getLValueBase(), Offset);
+ // Sign extend prior to converting back to a char unit.
+ if (Offset.getBitWidth() < 64)
+ Offset.extend(64);
+ return APValue(ResultLValue.getLValueBase(),
+ CharUnits::fromQuantity(Offset.getLimitedValue()));
}
APValue PointerExprEvaluator::VisitUnaryAddrOf(const UnaryOperator *E) {
diff --git a/test/CodeGen/const-arithmetic.c b/test/CodeGen/const-arithmetic.c
new file mode 100644
index 0000000..e12b4f6
--- /dev/null
+++ b/test/CodeGen/const-arithmetic.c
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck %s
+
+// CHECK: @g1 = global [2 x i8*] [i8* getelementptr (i8* getelementptr inbounds ([0 x %struct.anon]* @g0, i32 0, i32 0, i32 0), i64 -2), i8* getelementptr (i8* getelementptr inbounds ([0 x %struct.anon]* @g0, i32 0, i32 0, i32 0), i64 -46)], align 8 ; <[2 x i8*]*> [#uses=0]
+// CHECK: @g2 = global [2 x i8*] [i8* getelementptr (i8* getelementptr inbounds ([0 x %struct.anon]* @g0, i32 0, i32 0, i32 0), i64 -2), i8* getelementptr (i8* getelementptr inbounds ([0 x %struct.anon]* @g0, i32 0, i32 0, i32 0), i64 -46)], align 8 ; <[2 x i8*]*> [#uses=0]
+
+extern struct { unsigned char a, b; } g0[];
+void *g1[] = {g0 + -1, g0 + -23 };
+void *g2[] = {g0 - 1, g0 - 23 };