From 4efaa276bc0ce8f7baf6138ead11915f3e3e58d9 Mon Sep 17 00:00:00 2001 From: Eli Friedman Date: Wed, 12 Nov 2008 09:44:48 +0000 Subject: [PATCH] Some additions to tryEvaluate I've had sitting around for a while. This pushes it a lot closer to being able to deal with most of the stuff CodeGen's constant expression evaluator knows how to deal with. This also fixes PR3003. The test could possibly use some improvement, but this'll work for now. Test 6 is inspired by PR3003; the other tests are mostly just designed to exercise the new code. The reason for the funny structure of the tests is that type fixing for arrays inside of structs is the only place in Sema that calls tryEvaluate, at least for the moment. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@59125 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/AST/ExprConstant.cpp | 227 ++++++++++++++++++++++++++++++++++----- test/Sema/const-eval.c | 13 +++ 2 files changed, 215 insertions(+), 25 deletions(-) create mode 100644 test/Sema/const-eval.c diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp index 887a3aac5d..98400a450d 100644 --- a/lib/AST/ExprConstant.cpp +++ b/lib/AST/ExprConstant.cpp @@ -13,6 +13,7 @@ #include "clang/AST/APValue.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/RecordLayout.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/TargetInfo.h" @@ -62,10 +63,111 @@ struct EvalInfo { }; +static bool EvaluateLValue(const Expr *E, APValue &Result, EvalInfo &Info); static bool EvaluatePointer(const Expr *E, APValue &Result, EvalInfo &Info); static bool EvaluateInteger(const Expr *E, APSInt &Result, EvalInfo &Info); static bool EvaluateFloat(const Expr *E, APFloat &Result, EvalInfo &Info); +//===----------------------------------------------------------------------===// +// Misc utilities +//===----------------------------------------------------------------------===// + +static bool HandleConversionToBool(Expr* E, bool& Result, EvalInfo &Info) { + if (E->getType()->isIntegralType()) { + APSInt IntResult; + if (!EvaluateInteger(E, IntResult, Info)) + return false; + Result = IntResult != 0; + return true; + } else if (E->getType()->isRealFloatingType()) { + APFloat FloatResult(0.0); + if (!EvaluateFloat(E, FloatResult, Info)) + return false; + Result = !FloatResult.isZero(); + return true; + } else if (E->getType()->isPointerType()) { + APValue PointerResult; + if (!EvaluatePointer(E, PointerResult, Info)) + return false; + // FIXME: Is this accurate for all kinds of bases? If not, what would + // the check look like? + Result = PointerResult.getLValueBase() || PointerResult.getLValueOffset(); + return true; + } + + return false; +} + +//===----------------------------------------------------------------------===// +// LValue Evaluation +//===----------------------------------------------------------------------===// +namespace { +class VISIBILITY_HIDDEN LValueExprEvaluator + : public StmtVisitor { + EvalInfo &Info; +public: + + LValueExprEvaluator(EvalInfo &info) : Info(info) {} + + APValue VisitStmt(Stmt *S) { + // FIXME: Remove this when we support more expressions. + printf("Unhandled pointer statement\n"); + S->dump(); + return APValue(); + } + + APValue VisitParenExpr(ParenExpr *E) { return Visit(E->getSubExpr()); } + APValue VisitDeclRefExpr(DeclRefExpr *E) { return APValue(E, 0); } + APValue VisitPredefinedExpr(PredefinedExpr *E) { return APValue(E, 0); } + APValue VisitCompoundLiteralExpr(CompoundLiteralExpr *E); + APValue VisitMemberExpr(MemberExpr *E); + APValue VisitStringLiteral(StringLiteral *E) { return APValue(E, 0); } +}; +} // end anonymous namespace + +static bool EvaluateLValue(const Expr* E, APValue& Result, EvalInfo &Info) { + Result = LValueExprEvaluator(Info).Visit(const_cast(E)); + return Result.isLValue(); +} + +APValue LValueExprEvaluator::VisitCompoundLiteralExpr(CompoundLiteralExpr *E) { + if (E->isFileScope()) + return APValue(E, 0); + return APValue(); +} + +APValue LValueExprEvaluator::VisitMemberExpr(MemberExpr *E) { + APValue result; + QualType Ty; + if (E->isArrow()) { + if (!EvaluatePointer(E->getBase(), result, Info)) + return APValue(); + Ty = E->getBase()->getType()->getAsPointerType()->getPointeeType(); + } else { + result = Visit(E->getBase()); + if (result.isUninit()) + return APValue(); + Ty = E->getBase()->getType(); + } + + RecordDecl *RD = Ty->getAsRecordType()->getDecl(); + const ASTRecordLayout &RL = Info.Ctx.getASTRecordLayout(RD); + FieldDecl *FD = E->getMemberDecl(); + + // FIXME: This is linear time. + unsigned i = 0, e = 0; + for (i = 0, e = RD->getNumMembers(); i != e; i++) { + if (RD->getMember(i) == FD) + break; + } + + result.setLValue(result.getLValueBase(), + result.getLValueOffset() + RL.getFieldOffset(i) / 8); + + return result; +} + + //===----------------------------------------------------------------------===// // Pointer Evaluation //===----------------------------------------------------------------------===// @@ -86,6 +188,10 @@ public: APValue VisitBinaryOperator(const BinaryOperator *E); APValue VisitCastExpr(const CastExpr* E); + APValue VisitUnaryOperator(const UnaryOperator *E); + APValue VisitObjCStringLiteral(ObjCStringLiteral *E) + { return APValue(E, 0); } + APValue VisitConditionalOperator(ConditionalOperator *E); }; } // end anonymous namespace @@ -114,14 +220,33 @@ APValue PointerExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { if (!EvaluateInteger(IExp, AdditionalOffset, Info)) return APValue(); + QualType PointeeType = PExp->getType()->getAsPointerType()->getPointeeType(); + uint64_t SizeOfPointee = Info.Ctx.getTypeSize(PointeeType) / 8; + uint64_t Offset = ResultLValue.getLValueOffset(); + if (E->getOpcode() == BinaryOperator::Add) - Offset += AdditionalOffset.getZExtValue(); + Offset += AdditionalOffset.getLimitedValue() * SizeOfPointee; else - Offset -= AdditionalOffset.getZExtValue(); - + Offset -= AdditionalOffset.getLimitedValue() * SizeOfPointee; + return APValue(ResultLValue.getLValueBase(), Offset); } + +APValue PointerExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) { + if (E->getOpcode() == UnaryOperator::Extension) { + // FIXME: Deal with warnings? + return Visit(E->getSubExpr()); + } + + if (E->getOpcode() == UnaryOperator::AddrOf) { + APValue result; + if (EvaluateLValue(E->getSubExpr(), result, Info)) + return result; + } + + return APValue(); +} APValue PointerExprEvaluator::VisitCastExpr(const CastExpr* E) { @@ -142,11 +267,31 @@ APValue PointerExprEvaluator::VisitCastExpr(const CastExpr* E) { return APValue(0, Result.getZExtValue()); } } - - assert(0 && "Unhandled cast"); + + if (SubExpr->getType()->isFunctionType() || + SubExpr->getType()->isArrayType()) { + APValue Result; + if (EvaluateLValue(SubExpr, Result, Info)) + return Result; + return APValue(); + } + + //assert(0 && "Unhandled cast"); return APValue(); } +APValue PointerExprEvaluator::VisitConditionalOperator(ConditionalOperator *E) { + bool BoolResult; + if (!HandleConversionToBool(E->getCond(), BoolResult, Info)) + return APValue(); + + Expr* EvalExpr = BoolResult ? E->getTrueExpr() : E->getFalseExpr(); + + APValue Result; + if (EvaluatePointer(EvalExpr, Result, Info)) + return Result; + return APValue(); +} //===----------------------------------------------------------------------===// // Integer Evaluation @@ -349,8 +494,8 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { return false; // X && 0 -> 0, X || 1 -> 1. - if (E->getOpcode() == BinaryOperator::LAnd && RHS == 0 || - E->getOpcode() == BinaryOperator::LOr && RHS != 0) { + if ((E->getOpcode() == BinaryOperator::LAnd && RHS == 0) || + (E->getOpcode() == BinaryOperator::LOr && RHS != 0)) { Result = RHS != 0; Result.zextOrTrunc(getIntTypeSizeInBits(E->getType())); Result.setIsUnsigned(E->getType()->isUnsignedIntegerType()); @@ -470,10 +615,13 @@ bool IntExprEvaluator::VisitSizeOfAlignOfExpr(const SizeOfAlignOfExpr *E) { QualType SrcTy = E->getTypeOfArgument(); // sizeof(void) and __alignof__(void) = 1 as a gcc extension. - if (SrcTy->isVoidType()) + if (SrcTy->isVoidType()) { Result = 1; + return true; + } // sizeof(vla) is not a constantexpr: C99 6.5.3.4p2. + // FIXME: But alignof(vla) is! if (!SrcTy->isConstantSizeType()) { // FIXME: Should we attempt to evaluate this? return false; @@ -491,7 +639,7 @@ bool IntExprEvaluator::VisitSizeOfAlignOfExpr(const SizeOfAlignOfExpr *E) { // Get information about the size or align. unsigned CharSize = Info.Ctx.Target.getCharWidth(); if (isSizeOf) - Result = getIntTypeSizeInBits(SrcTy) / CharSize; + Result = Info.Ctx.getTypeSize(SrcTy) / CharSize; else Result = Info.Ctx.getTypeAlign(SrcTy) / CharSize; return true; @@ -547,6 +695,16 @@ bool IntExprEvaluator::HandleCast(SourceLocation CastLoc, Expr *SubExpr, QualType DestType) { unsigned DestWidth = getIntTypeSizeInBits(DestType); + if (DestType->isBooleanType()) { + bool BoolResult; + if (!HandleConversionToBool(SubExpr, BoolResult, Info)) + return false; + Result.zextOrTrunc(DestWidth); + Result.setIsUnsigned(DestType->isUnsignedIntegerType()); + Result = BoolResult; + return true; + } + // Handle simple integer->integer casts. if (SubExpr->getType()->isIntegerType()) { if (!Visit(SubExpr)) @@ -554,12 +712,7 @@ bool IntExprEvaluator::HandleCast(SourceLocation CastLoc, // Figure out if this is a truncate, extend or noop cast. // If the input is signed, do a sign extend, noop, or truncate. - if (DestType->isBooleanType()) { - // Conversion to bool compares against zero. - Result = Result != 0; - Result.zextOrTrunc(DestWidth); - } else - Result.extOrTrunc(DestWidth); + Result.extOrTrunc(DestWidth); Result.setIsUnsigned(DestType->isUnsignedIntegerType()); return true; } @@ -569,29 +722,22 @@ bool IntExprEvaluator::HandleCast(SourceLocation CastLoc, APValue LV; if (!EvaluatePointer(SubExpr, LV, Info)) return false; + if (LV.getLValueBase()) return false; - + Result.extOrTrunc(DestWidth); Result = LV.getLValueOffset(); Result.setIsUnsigned(DestType->isUnsignedIntegerType()); return true; } - + if (!SubExpr->getType()->isRealFloatingType()) return Error(CastLoc, diag::err_expr_not_constant, DestType); APFloat F(0.0); if (!EvaluateFloat(SubExpr, F, Info)) return Error(CastLoc, diag::err_expr_not_constant, DestType); - - // If the destination is boolean, compare against zero. - if (DestType->isBooleanType()) { - Result = !F.isZero(); - Result.zextOrTrunc(DestWidth); - Result.setIsUnsigned(DestType->isUnsignedIntegerType()); - return true; - } // Determine whether we are converting to unsigned or signed. bool DestSigned = DestType->isSignedIntegerType(); @@ -629,6 +775,8 @@ public: bool VisitUnaryOperator(const UnaryOperator *E); bool VisitBinaryOperator(const BinaryOperator *E); bool VisitFloatingLiteral(const FloatingLiteral *E); + bool VisitCastExpr(CastExpr *E); + bool VisitCXXZeroInitValueExpr(CXXZeroInitValueExpr *E); }; } // end anonymous namespace @@ -738,6 +886,35 @@ bool FloatExprEvaluator::VisitFloatingLiteral(const FloatingLiteral *E) { return true; } +bool FloatExprEvaluator::VisitCastExpr(CastExpr *E) { + Expr* SubExpr = E->getSubExpr(); + const llvm::fltSemantics& destSemantics = + Info.Ctx.getFloatTypeSemantics(E->getType()); + if (SubExpr->getType()->isIntegralType()) { + APSInt IntResult; + if (!EvaluateInteger(E, IntResult, Info)) + return false; + Result = APFloat(destSemantics, 1); + Result.convertFromAPInt(IntResult, IntResult.isSigned(), + APFloat::rmNearestTiesToEven); + return true; + } + if (SubExpr->getType()->isRealFloatingType()) { + if (!Visit(SubExpr)) + return false; + bool ignored; + Result.convert(destSemantics, APFloat::rmNearestTiesToEven, &ignored); + return true; + } + + return false; +} + +bool FloatExprEvaluator::VisitCXXZeroInitValueExpr(CXXZeroInitValueExpr *E) { + Result = APFloat::getZero(Info.Ctx.getFloatTypeSemantics(E->getType())); + return true; +} + //===----------------------------------------------------------------------===// // Top level TryEvaluate. //===----------------------------------------------------------------------===// diff --git a/test/Sema/const-eval.c b/test/Sema/const-eval.c new file mode 100644 index 0000000000..f42ed2504b --- /dev/null +++ b/test/Sema/const-eval.c @@ -0,0 +1,13 @@ +// RUN: clang -fsyntax-only -verify %s + +#define EVAL_EXPR(testno, expr) int test##testno = sizeof(struct{char qq[expr];}); +int x; +EVAL_EXPR(1, (_Bool)&x) +EVAL_EXPR(2, (int)(1.0+(double)4)) +EVAL_EXPR(3, (int)(1.0+(float)4.0)) +EVAL_EXPR(4, (_Bool)(1 ? (void*)&x : 0)) +EVAL_EXPR(5, (_Bool)(int[]){0}) +struct y {int x,y;}; +EVAL_EXPR(6, (int)(1+(struct y*)0)) +EVAL_EXPR(7, (int)&((struct y*)0)->y) +EVAL_EXPR(8, (_Bool)"asdf") -- 2.40.0