Completely reimplement __builtin_offsetof, based on a patch by Roberto
Amadini.
This change introduces a new expression node type, OffsetOfExpr, that
describes __builtin_offsetof. Previously, __builtin_offsetof was
implemented using a unary operator whose subexpression involved
various synthesized array-subscript and member-reference expressions,
which was ugly and made it very hard to instantiate as a
template. OffsetOfExpr represents the AST more faithfully, with proper
type source information and a more compact representation.
OffsetOfExpr also has support for dependent __builtin_offsetof
expressions; it can be value-dependent, but will never be
type-dependent (like sizeof or alignof). This commit introduces
template instantiation for __builtin_offsetof as well.
There are two major caveats to this patch:
1) CodeGen cannot handle the case where __builtin_offsetof is not a
constant expression, so it produces an error. So, to avoid
regressing in C, we retain the old UnaryOperator-based
__builtin_offsetof implementation in C while using the shiny new
OffsetOfExpr implementation in C++. The old implementation can go
away once we have proper CodeGen support for this case, which we
expect won't cause much trouble in C++.
2) __builtin_offsetof doesn't work well with non-POD class types,
particularly when the designated field is found within a base
class. I will address this in a subsequent patch.
Fixes PR5880 and a bunch of assertions when building Boost.Python
tests.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@102542 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/AST/Expr.cpp b/lib/AST/Expr.cpp
index 8770bfd..308f6b4 100644
--- a/lib/AST/Expr.cpp
+++ b/lib/AST/Expr.cpp
@@ -549,6 +549,57 @@
return FnType->getResultType();
}
+OffsetOfExpr *OffsetOfExpr::Create(ASTContext &C, QualType type,
+ SourceLocation OperatorLoc,
+ TypeSourceInfo *tsi,
+ OffsetOfNode* compsPtr, unsigned numComps,
+ Expr** exprsPtr, unsigned numExprs,
+ SourceLocation RParenLoc) {
+ void *Mem = C.Allocate(sizeof(OffsetOfExpr) +
+ sizeof(OffsetOfNode) * numComps +
+ sizeof(Expr*) * numExprs);
+
+ return new (Mem) OffsetOfExpr(C, type, OperatorLoc, tsi, compsPtr, numComps,
+ exprsPtr, numExprs, RParenLoc);
+}
+
+OffsetOfExpr *OffsetOfExpr::CreateEmpty(ASTContext &C,
+ unsigned numComps, unsigned numExprs) {
+ void *Mem = C.Allocate(sizeof(OffsetOfExpr) +
+ sizeof(OffsetOfNode) * numComps +
+ sizeof(Expr*) * numExprs);
+ return new (Mem) OffsetOfExpr(numComps, numExprs);
+}
+
+OffsetOfExpr::OffsetOfExpr(ASTContext &C, QualType type,
+ SourceLocation OperatorLoc, TypeSourceInfo *tsi,
+ OffsetOfNode* compsPtr, unsigned numComps,
+ Expr** exprsPtr, unsigned numExprs,
+ SourceLocation RParenLoc)
+ : Expr(OffsetOfExprClass, type, /*TypeDependent=*/false,
+ /*ValueDependent=*/tsi->getType()->isDependentType() ||
+ hasAnyTypeDependentArguments(exprsPtr, numExprs) ||
+ hasAnyValueDependentArguments(exprsPtr, numExprs)),
+ OperatorLoc(OperatorLoc), RParenLoc(RParenLoc), TSInfo(tsi),
+ NumComps(numComps), NumExprs(numExprs)
+{
+ for(unsigned i = 0; i < numComps; ++i) {
+ setComponent(i, compsPtr[i]);
+ }
+
+ for(unsigned i = 0; i < numExprs; ++i) {
+ setIndexExpr(i, exprsPtr[i]);
+ }
+}
+
+IdentifierInfo *OffsetOfExpr::OffsetOfNode::getFieldName() const {
+ assert(getKind() == Field || getKind() == Identifier);
+ if (getKind() == Field)
+ return getField()->getIdentifier();
+
+ return reinterpret_cast<IdentifierInfo *> (Data & ~(uintptr_t)Mask);
+}
+
MemberExpr *MemberExpr::Create(ASTContext &C, Expr *base, bool isarrow,
NestedNameSpecifier *qual,
SourceRange qualrange,
@@ -1891,7 +1942,7 @@
case UnaryOperator::AddrOf:
case UnaryOperator::Deref:
return ICEDiag(2, E->getLocStart());
-
+ case UnaryOperator::OffsetOf:
case UnaryOperator::Extension:
case UnaryOperator::LNot:
case UnaryOperator::Plus:
@@ -1900,7 +1951,9 @@
case UnaryOperator::Real:
case UnaryOperator::Imag:
return CheckICE(Exp->getSubExpr(), Ctx);
- case UnaryOperator::OffsetOf:
+ }
+ }
+ case Expr::OffsetOfExprClass: {
// Note that per C99, offsetof must be an ICE. And AFAIK, using
// Evaluate matches the proposed gcc behavior for cases like
// "offsetof(struct s{int x[4];}, x[!.0])". This doesn't affect
@@ -1908,7 +1961,6 @@
// array subscripts that aren't ICEs, and if the array subscripts
// are ICEs, the value of the offsetof must be an integer constant.
return CheckEvalInICE(E, Ctx);
- }
}
case Expr::SizeOfAlignOfExprClass: {
const SizeOfAlignOfExpr *Exp = cast<SizeOfAlignOfExpr>(E);
@@ -2702,6 +2754,15 @@
Stmt::child_iterator UnaryOperator::child_begin() { return &Val; }
Stmt::child_iterator UnaryOperator::child_end() { return &Val+1; }
+// OffsetOfExpr
+Stmt::child_iterator OffsetOfExpr::child_begin() {
+ return reinterpret_cast<Stmt **> (reinterpret_cast<OffsetOfNode *> (this + 1)
+ + NumComps);
+}
+Stmt::child_iterator OffsetOfExpr::child_end() {
+ return child_iterator(&*child_begin() + NumExprs);
+}
+
// SizeOfAlignOfExpr
Stmt::child_iterator SizeOfAlignOfExpr::child_begin() {
// If this is of a type and the type is a VLA type (and not a typedef), the
diff --git a/lib/AST/ExprConstant.cpp b/lib/AST/ExprConstant.cpp
index a52cf6f..7233518 100644
--- a/lib/AST/ExprConstant.cpp
+++ b/lib/AST/ExprConstant.cpp
@@ -16,7 +16,9 @@
#include "clang/AST/CharUnits.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/TypeLoc.h"
#include "clang/AST/ASTDiagnostic.h"
+#include "clang/AST/Expr.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/TargetInfo.h"
#include "llvm/ADT/SmallString.h"
@@ -228,7 +230,7 @@
APValue VisitStmt(Stmt *S) {
return APValue();
}
-
+
APValue VisitParenExpr(ParenExpr *E) { return Visit(E->getSubExpr()); }
APValue VisitDeclRefExpr(DeclRefExpr *E);
APValue VisitPredefinedExpr(PredefinedExpr *E) { return APValue(E); }
@@ -828,6 +830,7 @@
bool VisitCallExpr(CallExpr *E);
bool VisitBinaryOperator(const BinaryOperator *E);
+ bool VisitOffsetOfExpr(const OffsetOfExpr *E);
bool VisitUnaryOperator(const UnaryOperator *E);
bool VisitConditionalOperator(const ConditionalOperator *E);
@@ -1372,6 +1375,61 @@
return Success(Info.Ctx.getTypeSizeInChars(SrcTy).getQuantity(), E);
}
+bool IntExprEvaluator::VisitOffsetOfExpr(const OffsetOfExpr *E) {
+ CharUnits Result;
+ unsigned n = E->getNumComponents();
+ OffsetOfExpr* OOE = const_cast<OffsetOfExpr*>(E);
+ if (n == 0)
+ return false;
+ QualType CurrentType = E->getTypeSourceInfo()->getType();
+ for (unsigned i = 0; i != n; ++i) {
+ OffsetOfExpr::OffsetOfNode ON = OOE->getComponent(i);
+ switch (ON.getKind()) {
+ case OffsetOfExpr::OffsetOfNode::Array: {
+ Expr *Idx = OOE->getIndexExpr(ON.getArrayExprIndex());
+ APSInt IdxResult;
+ if (!EvaluateInteger(Idx, IdxResult, Info))
+ return false;
+ const ArrayType *AT = Info.Ctx.getAsArrayType(CurrentType);
+ if (!AT)
+ return false;
+ CurrentType = AT->getElementType();
+ CharUnits ElementSize = Info.Ctx.getTypeSizeInChars(CurrentType);
+ Result += IdxResult.getSExtValue() * ElementSize;
+ break;
+ }
+
+ case OffsetOfExpr::OffsetOfNode::Field: {
+ FieldDecl *MemberDecl = ON.getField();
+ const RecordType *RT = CurrentType->getAs<RecordType>();
+ if (!RT)
+ return false;
+ RecordDecl *RD = RT->getDecl();
+ const ASTRecordLayout &RL = Info.Ctx.getASTRecordLayout(RD);
+ unsigned i = 0;
+ // FIXME: It would be nice if we didn't have to loop here!
+ for (RecordDecl::field_iterator Field = RD->field_begin(),
+ FieldEnd = RD->field_end();
+ Field != FieldEnd; (void)++Field, ++i) {
+ if (*Field == MemberDecl)
+ break;
+ }
+ if (i < RL.getFieldCount())
+ Result += CharUnits::fromQuantity(
+ RL.getFieldOffset(i) / Info.Ctx.getCharWidth());
+ else
+ return false;
+ CurrentType = MemberDecl->getType().getNonReferenceType();
+ break;
+ }
+
+ case OffsetOfExpr::OffsetOfNode::Identifier:
+ llvm_unreachable("dependent __builtin_offsetof");
+ }
+ }
+ return Success(Result.getQuantity(), E);
+}
+
bool IntExprEvaluator::VisitUnaryOperator(const UnaryOperator *E) {
// Special case unary operators that do not need their subexpression
// evaluated. offsetof/sizeof/alignof are all special.
@@ -1380,12 +1438,12 @@
// directly Evaluate it as an l-value.
APValue LV;
if (!EvaluateLValue(E->getSubExpr(), LV, Info))
- return false;
+ return false;
if (LV.getLValueBase())
- return false;
+ return false;
return Success(LV.getLValueOffset().getQuantity(), E);
}
-
+
if (E->getOpcode() == UnaryOperator::LNot) {
// LNot's operand isn't necessarily an integer, so we handle it specially.
bool bres;
diff --git a/lib/AST/StmtPrinter.cpp b/lib/AST/StmtPrinter.cpp
index 3996528..b50bb0c 100644
--- a/lib/AST/StmtPrinter.cpp
+++ b/lib/AST/StmtPrinter.cpp
@@ -17,6 +17,7 @@
#include "clang/AST/DeclObjC.h"
#include "clang/AST/PrettyPrinter.h"
#include "llvm/Support/Format.h"
+#include "clang/AST/Expr.h"
using namespace clang;
//===----------------------------------------------------------------------===//
@@ -703,6 +704,35 @@
OS << ")";
}
+void StmtPrinter::VisitOffsetOfExpr(OffsetOfExpr *Node) {
+ OS << "__builtin_offsetof(";
+ OS << Node->getTypeSourceInfo()->getType().getAsString() << ", ";
+ bool PrintedSomething = false;
+ for (unsigned i = 0, n = Node->getNumComponents(); i < n; ++i) {
+ OffsetOfExpr::OffsetOfNode ON = Node->getComponent(i);
+ if (ON.getKind() == OffsetOfExpr::OffsetOfNode::Array) {
+ // Array node
+ OS << "[";
+ PrintExpr(Node->getIndexExpr(ON.getArrayExprIndex()));
+ OS << "]";
+ PrintedSomething = true;
+ continue;
+ }
+
+ // Field or identifier node.
+ IdentifierInfo *Id = ON.getFieldName();
+ if (!Id)
+ continue;
+
+ if (PrintedSomething)
+ OS << ".";
+ else
+ PrintedSomething = true;
+ OS << Id->getName();
+ }
+ OS << ")";
+}
+
void StmtPrinter::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *Node) {
OS << (Node->isSizeOf() ? "sizeof" : "__alignof");
if (Node->isArgumentType())
diff --git a/lib/AST/StmtProfile.cpp b/lib/AST/StmtProfile.cpp
index 3a19ec2..3d528f3 100644
--- a/lib/AST/StmtProfile.cpp
+++ b/lib/AST/StmtProfile.cpp
@@ -261,6 +261,30 @@
ID.AddInteger(S->getOpcode());
}
+void StmtProfiler::VisitOffsetOfExpr(OffsetOfExpr *S) {
+ VisitType(S->getTypeSourceInfo()->getType());
+ unsigned n = S->getNumComponents();
+ for (unsigned i = 0; i < n; ++i) {
+ const OffsetOfExpr::OffsetOfNode& ON = S->getComponent(i);
+ ID.AddInteger(ON.getKind());
+ switch (ON.getKind()) {
+ case OffsetOfExpr::OffsetOfNode::Array:
+ // Expressions handled below.
+ break;
+
+ case OffsetOfExpr::OffsetOfNode::Field:
+ VisitDecl(ON.getField());
+ break;
+
+ case OffsetOfExpr::OffsetOfNode::Identifier:
+ ID.AddPointer(ON.getFieldName());
+ break;
+ }
+ }
+
+ VisitExpr(S);
+}
+
void StmtProfiler::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *S) {
VisitExpr(S);
ID.AddBoolean(S->isSizeOf());