diff options
-rw-r--r-- | AST/Expr.cpp | 48 | ||||
-rw-r--r-- | CodeGen/CGExprConstant.cpp | 9 | ||||
-rw-r--r-- | CodeGen/CGExprScalar.cpp | 13 | ||||
-rw-r--r-- | include/clang/AST/Expr.h | 3 | ||||
-rw-r--r-- | test/Sema/offsetof.c | 4 |
5 files changed, 75 insertions, 2 deletions
diff --git a/AST/Expr.cpp b/AST/Expr.cpp index 51aa09c6ba..210d1a280b 100644 --- a/AST/Expr.cpp +++ b/AST/Expr.cpp @@ -662,7 +662,7 @@ bool Expr::isIntegerConstantExpr(llvm::APSInt &Result, ASTContext &Ctx, // Get the operand value. If this is sizeof/alignof, do not evalute the // operand. This affects C99 6.6p3. - if (!Exp->isSizeOfAlignOfOp() && + if (!Exp->isSizeOfAlignOfOp() && !Exp->isOffsetOfOp() && !Exp->getSubExpr()->isIntegerConstantExpr(Result, Ctx, Loc,isEvaluated)) return false; @@ -718,6 +718,8 @@ bool Expr::isIntegerConstantExpr(llvm::APSInt &Result, ASTContext &Ctx, case UnaryOperator::Not: Result = ~Result; break; + case UnaryOperator::OffsetOf: + Result = Exp->evaluateOffsetOf(Ctx); } break; } @@ -1069,6 +1071,50 @@ bool ChooseExpr::isConditionTrue(ASTContext &C) const { return CondVal != 0; } +static int64_t evaluateOffsetOf(ASTContext& C, const Expr *E) +{ + if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) { + QualType Ty = ME->getBase()->getType(); + + RecordDecl *RD = Ty->getAsRecordType()->getDecl(); + const ASTRecordLayout &RL = C.getASTRecordLayout(RD, SourceLocation()); + FieldDecl *FD = ME->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; + } + + return RL.getFieldOffset(i) + evaluateOffsetOf(C, ME->getBase()); + } else if (const ArraySubscriptExpr *ASE = dyn_cast<ArraySubscriptExpr>(E)) { + const Expr *Base = ASE->getBase(); + llvm::APSInt Idx(32); + bool ICE = ASE->getIdx()->isIntegerConstantExpr(Idx, C); + assert(ICE && "Array index is not a constant integer!"); + + int64_t size = C.getTypeSize(ASE->getType(), SourceLocation()); + size *= Idx.getSExtValue(); + + return size + evaluateOffsetOf(C, Base); + } else if (isa<CompoundLiteralExpr>(E)) + return 0; + + assert(0 && "Unknown offsetof subexpression!"); + return 0; +} + +int64_t UnaryOperator::evaluateOffsetOf(ASTContext& C) const +{ + assert(Opc == OffsetOf && "Unary operator not offsetof!"); + + unsigned CharSize = + C.Target.getCharWidth(C.getFullLoc(getOperatorLoc())); + + return ::evaluateOffsetOf(C, Val) / CharSize; +} + //===----------------------------------------------------------------------===// // Child Iterators for iterating over subexpressions/substatements //===----------------------------------------------------------------------===// diff --git a/CodeGen/CGExprConstant.cpp b/CodeGen/CGExprConstant.cpp index 3b1c62bb16..e03db991c4 100644 --- a/CodeGen/CGExprConstant.cpp +++ b/CodeGen/CGExprConstant.cpp @@ -235,6 +235,15 @@ public: llvm::Constant *VisitUnaryAddrOf(const UnaryOperator *E) { return EmitLValue(E->getSubExpr()); } + llvm::Constant *VisitUnaryOffsetOf(const UnaryOperator *E) { + int64_t Val = E->evaluateOffsetOf(CGM.getContext()); + + assert(E->getType()->isIntegerType() && "Result type must be an integer!"); + + uint32_t ResultWidth = static_cast<uint32_t>( + CGM.getContext().getTypeSize(E->getType(), SourceLocation())); + return llvm::ConstantInt::get(llvm::APInt(ResultWidth, Val)); + } // Binary operators llvm::Constant *VisitBinOr(const BinaryOperator *E) { diff --git a/CodeGen/CGExprScalar.cpp b/CodeGen/CGExprScalar.cpp index 652729ac27..e1771e80f8 100644 --- a/CodeGen/CGExprScalar.cpp +++ b/CodeGen/CGExprScalar.cpp @@ -213,7 +213,8 @@ public: Value *VisitUnaryExtension(const UnaryOperator *E) { return Visit(E->getSubExpr()); } - + Value *VisitUnaryOffsetOf(const UnaryOperator *E); + // Binary Operators. Value *EmitMul(const BinOpInfo &Ops) { return Builder.CreateMul(Ops.LHS, Ops.RHS, "mul"); @@ -628,6 +629,16 @@ Value *ScalarExprEmitter::VisitUnaryImag(const UnaryOperator *E) { return llvm::Constant::getNullValue(ConvertType(E->getType())); } +Value *ScalarExprEmitter::VisitUnaryOffsetOf(const UnaryOperator *E) +{ + int64_t Val = E->evaluateOffsetOf(CGF.getContext()); + + assert(E->getType()->isIntegerType() && "Result type must be an integer!"); + + uint32_t ResultWidth = static_cast<uint32_t>( + CGF.getContext().getTypeSize(E->getType(), SourceLocation())); + return llvm::ConstantInt::get(llvm::APInt(ResultWidth, Val)); +} //===----------------------------------------------------------------------===// // Binary Operators diff --git a/include/clang/AST/Expr.h b/include/clang/AST/Expr.h index 4eb4072a87..13bf6f52b7 100644 --- a/include/clang/AST/Expr.h +++ b/include/clang/AST/Expr.h @@ -429,6 +429,7 @@ public: bool isPostfix() const { return isPostfix(Opc); } bool isIncrementDecrementOp() const { return Opc>=PostInc && Opc<=PreDec; } bool isSizeOfAlignOfOp() const { return Opc == SizeOf || Opc == AlignOf; } + bool isOffsetOfOp() const { return Opc == OffsetOf; } static bool isArithmeticOp(Opcode Op) { return Op >= Plus && Op <= LNot; } /// getOpcodeStr - Turn an Opcode enum value into the punctuation char it @@ -448,6 +449,8 @@ public: } static bool classof(const UnaryOperator *) { return true; } + int64_t evaluateOffsetOf(ASTContext& C) const; + // Iterators virtual child_iterator child_begin(); virtual child_iterator child_end(); diff --git a/test/Sema/offsetof.c b/test/Sema/offsetof.c index 3decec5ed4..7b8564a6a5 100644 --- a/test/Sema/offsetof.c +++ b/test/Sema/offsetof.c @@ -19,5 +19,9 @@ void swap() x = __builtin_offsetof(struct external_sun3_core, X[42].f2); // expected-error {{no member named 'f2'}} x = __builtin_offsetof(int, X[42].f2); // expected-error {{offsetof requires struct}} + + int a[__builtin_offsetof(struct external_sun3_core, X) == 4 ? 1 : -1]; + int b[__builtin_offsetof(struct external_sun3_core, X[42]) == 340 ? 1 : -1]; + int c[__builtin_offsetof(struct external_sun3_core, X[42].f2) == 344 ? 1 : -1]; } |