Skip to content

Commit 87461d6

Browse files
committed
[clang][Interp] Implement __builtin_offsetof
Differential Revision: https://reviews.llvm.org/D156400
1 parent 6632882 commit 87461d6

File tree

10 files changed

+159
-698
lines changed

10 files changed

+159
-698
lines changed

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1453,6 +1453,41 @@ bool ByteCodeExprGen<Emitter>::VisitSourceLocExpr(const SourceLocExpr *E) {
14531453
return true;
14541454
}
14551455

1456+
template <class Emitter>
1457+
bool ByteCodeExprGen<Emitter>::VisitOffsetOfExpr(const OffsetOfExpr *E) {
1458+
unsigned N = E->getNumComponents();
1459+
if (N == 0)
1460+
return false;
1461+
1462+
for (unsigned I = 0; I != N; ++I) {
1463+
const OffsetOfNode &Node = E->getComponent(I);
1464+
if (Node.getKind() == OffsetOfNode::Array) {
1465+
const Expr *ArrayIndexExpr = E->getIndexExpr(Node.getArrayExprIndex());
1466+
PrimType IndexT = classifyPrim(ArrayIndexExpr->getType());
1467+
1468+
if (DiscardResult) {
1469+
if (!this->discard(ArrayIndexExpr))
1470+
return false;
1471+
continue;
1472+
}
1473+
1474+
if (!this->visit(ArrayIndexExpr))
1475+
return false;
1476+
// Cast to Sint64.
1477+
if (IndexT != PT_Sint64) {
1478+
if (!this->emitCast(IndexT, PT_Sint64, E))
1479+
return false;
1480+
}
1481+
}
1482+
}
1483+
1484+
if (DiscardResult)
1485+
return true;
1486+
1487+
PrimType T = classifyPrim(E->getType());
1488+
return this->emitOffsetOf(T, E, E);
1489+
}
1490+
14561491
template <class Emitter> bool ByteCodeExprGen<Emitter>::discard(const Expr *E) {
14571492
if (E->containsErrors())
14581493
return false;

clang/lib/AST/Interp/ByteCodeExprGen.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
105105
bool VisitCXXNoexceptExpr(const CXXNoexceptExpr *E);
106106
bool VisitCXXConstructExpr(const CXXConstructExpr *E);
107107
bool VisitSourceLocExpr(const SourceLocExpr *E);
108+
bool VisitOffsetOfExpr(const OffsetOfExpr *E);
108109

109110
protected:
110111
bool visitExpr(const Expr *E) override;

clang/lib/AST/Interp/Interp.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,10 @@ bool Interpret(InterpState &S, APValue &Result);
181181
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
182182
const CallExpr *Call);
183183

184+
/// Interpret an offsetof operation.
185+
bool InterpretOffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E,
186+
llvm::ArrayRef<int64_t> ArrayIndices, int64_t &Result);
187+
184188
enum class ArithOp { Add, Sub };
185189

186190
//===----------------------------------------------------------------------===//
@@ -1839,6 +1843,21 @@ inline bool InvalidCast(InterpState &S, CodePtr OpPC, CastKind Kind) {
18391843
return false;
18401844
}
18411845

1846+
template <PrimType Name, class T = typename PrimConv<Name>::T>
1847+
inline bool OffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E) {
1848+
llvm::SmallVector<int64_t> ArrayIndices;
1849+
for (size_t I = 0; I != E->getNumExpressions(); ++I)
1850+
ArrayIndices.emplace_back(S.Stk.pop<int64_t>());
1851+
1852+
int64_t Result;
1853+
if (!InterpretOffsetOf(S, OpPC, E, ArrayIndices, Result))
1854+
return false;
1855+
1856+
S.Stk.push<T>(T::from(Result));
1857+
1858+
return true;
1859+
}
1860+
18421861
//===----------------------------------------------------------------------===//
18431862
// Read opcode arguments
18441863
//===----------------------------------------------------------------------===//

clang/lib/AST/Interp/InterpBuiltin.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "Boolean.h"
99
#include "Interp.h"
1010
#include "PrimType.h"
11+
#include "clang/AST/RecordLayout.h"
1112
#include "clang/Basic/Builtins.h"
1213
#include "clang/Basic/TargetInfo.h"
1314

@@ -519,5 +520,79 @@ bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
519520
return false;
520521
}
521522

523+
bool InterpretOffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E,
524+
llvm::ArrayRef<int64_t> ArrayIndices,
525+
int64_t &IntResult) {
526+
CharUnits Result;
527+
unsigned N = E->getNumComponents();
528+
assert(N > 0);
529+
530+
unsigned ArrayIndex = 0;
531+
QualType CurrentType = E->getTypeSourceInfo()->getType();
532+
for (unsigned I = 0; I != N; ++I) {
533+
const OffsetOfNode &Node = E->getComponent(I);
534+
switch (Node.getKind()) {
535+
case OffsetOfNode::Field: {
536+
const FieldDecl *MemberDecl = Node.getField();
537+
const RecordType *RT = CurrentType->getAs<RecordType>();
538+
if (!RT)
539+
return false;
540+
RecordDecl *RD = RT->getDecl();
541+
if (RD->isInvalidDecl())
542+
return false;
543+
const ASTRecordLayout &RL = S.getCtx().getASTRecordLayout(RD);
544+
unsigned FieldIndex = MemberDecl->getFieldIndex();
545+
assert(FieldIndex < RL.getFieldCount() && "offsetof field in wrong type");
546+
Result += S.getCtx().toCharUnitsFromBits(RL.getFieldOffset(FieldIndex));
547+
CurrentType = MemberDecl->getType().getNonReferenceType();
548+
break;
549+
}
550+
case OffsetOfNode::Array: {
551+
// When generating bytecode, we put all the index expressions as Sint64 on
552+
// the stack.
553+
int64_t Index = ArrayIndices[ArrayIndex];
554+
const ArrayType *AT = S.getCtx().getAsArrayType(CurrentType);
555+
if (!AT)
556+
return false;
557+
CurrentType = AT->getElementType();
558+
CharUnits ElementSize = S.getCtx().getTypeSizeInChars(CurrentType);
559+
Result += Index * ElementSize;
560+
++ArrayIndex;
561+
break;
562+
}
563+
case OffsetOfNode::Base: {
564+
const CXXBaseSpecifier *BaseSpec = Node.getBase();
565+
if (BaseSpec->isVirtual())
566+
return false;
567+
568+
// Find the layout of the class whose base we are looking into.
569+
const RecordType *RT = CurrentType->getAs<RecordType>();
570+
if (!RT)
571+
return false;
572+
const RecordDecl *RD = RT->getDecl();
573+
if (RD->isInvalidDecl())
574+
return false;
575+
const ASTRecordLayout &RL = S.getCtx().getASTRecordLayout(RD);
576+
577+
// Find the base class itself.
578+
CurrentType = BaseSpec->getType();
579+
const RecordType *BaseRT = CurrentType->getAs<RecordType>();
580+
if (!BaseRT)
581+
return false;
582+
583+
// Add the offset to the base.
584+
Result += RL.getBaseClassOffset(cast<CXXRecordDecl>(BaseRT->getDecl()));
585+
break;
586+
}
587+
case OffsetOfNode::Identifier:
588+
llvm_unreachable("Dependent OffsetOfExpr?");
589+
}
590+
}
591+
592+
IntResult = Result.getQuantity();
593+
594+
return true;
595+
}
596+
522597
} // namespace interp
523598
} // namespace clang

clang/lib/AST/Interp/Opcodes.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ def ArgRoundingMode : ArgType { let Name = "llvm::RoundingMode"; }
5353
def ArgLETD: ArgType { let Name = "const LifetimeExtendedTemporaryDecl *"; }
5454
def ArgCastKind : ArgType { let Name = "CastKind"; }
5555
def ArgCallExpr : ArgType { let Name = "const CallExpr *"; }
56+
def ArgOffsetOfExpr : ArgType { let Name = "const OffsetOfExpr *"; }
5657

5758
//===----------------------------------------------------------------------===//
5859
// Classes of types instructions operate on.
@@ -198,6 +199,12 @@ def CallPtr : Opcode {
198199
let Types = [];
199200
}
200201

202+
def OffsetOf : Opcode {
203+
let Types = [IntegerTypeClass];
204+
let Args = [ArgOffsetOfExpr];
205+
let HasGroup = 1;
206+
}
207+
201208
//===----------------------------------------------------------------------===//
202209
// Frame management
203210
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)