Skip to content

Commit

Permalink
[clang][Interp] Implement __builtin_offsetof
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D156400
  • Loading branch information
tbaederr committed Sep 11, 2023
1 parent 6632882 commit 87461d6
Show file tree
Hide file tree
Showing 10 changed files with 159 additions and 698 deletions.
35 changes: 35 additions & 0 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1453,6 +1453,41 @@ bool ByteCodeExprGen<Emitter>::VisitSourceLocExpr(const SourceLocExpr *E) {
return true;
}

template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitOffsetOfExpr(const OffsetOfExpr *E) {
unsigned N = E->getNumComponents();
if (N == 0)
return false;

for (unsigned I = 0; I != N; ++I) {
const OffsetOfNode &Node = E->getComponent(I);
if (Node.getKind() == OffsetOfNode::Array) {
const Expr *ArrayIndexExpr = E->getIndexExpr(Node.getArrayExprIndex());
PrimType IndexT = classifyPrim(ArrayIndexExpr->getType());

if (DiscardResult) {
if (!this->discard(ArrayIndexExpr))
return false;
continue;
}

if (!this->visit(ArrayIndexExpr))
return false;
// Cast to Sint64.
if (IndexT != PT_Sint64) {
if (!this->emitCast(IndexT, PT_Sint64, E))
return false;
}
}
}

if (DiscardResult)
return true;

PrimType T = classifyPrim(E->getType());
return this->emitOffsetOf(T, E, E);
}

template <class Emitter> bool ByteCodeExprGen<Emitter>::discard(const Expr *E) {
if (E->containsErrors())
return false;
Expand Down
1 change: 1 addition & 0 deletions clang/lib/AST/Interp/ByteCodeExprGen.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
bool VisitCXXNoexceptExpr(const CXXNoexceptExpr *E);
bool VisitCXXConstructExpr(const CXXConstructExpr *E);
bool VisitSourceLocExpr(const SourceLocExpr *E);
bool VisitOffsetOfExpr(const OffsetOfExpr *E);

protected:
bool visitExpr(const Expr *E) override;
Expand Down
19 changes: 19 additions & 0 deletions clang/lib/AST/Interp/Interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,10 @@ bool Interpret(InterpState &S, APValue &Result);
bool InterpretBuiltin(InterpState &S, CodePtr OpPC, const Function *F,
const CallExpr *Call);

/// Interpret an offsetof operation.
bool InterpretOffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E,
llvm::ArrayRef<int64_t> ArrayIndices, int64_t &Result);

enum class ArithOp { Add, Sub };

//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -1839,6 +1843,21 @@ inline bool InvalidCast(InterpState &S, CodePtr OpPC, CastKind Kind) {
return false;
}

template <PrimType Name, class T = typename PrimConv<Name>::T>
inline bool OffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E) {
llvm::SmallVector<int64_t> ArrayIndices;
for (size_t I = 0; I != E->getNumExpressions(); ++I)
ArrayIndices.emplace_back(S.Stk.pop<int64_t>());

int64_t Result;
if (!InterpretOffsetOf(S, OpPC, E, ArrayIndices, Result))
return false;

S.Stk.push<T>(T::from(Result));

return true;
}

//===----------------------------------------------------------------------===//
// Read opcode arguments
//===----------------------------------------------------------------------===//
Expand Down
75 changes: 75 additions & 0 deletions clang/lib/AST/Interp/InterpBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "Boolean.h"
#include "Interp.h"
#include "PrimType.h"
#include "clang/AST/RecordLayout.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/TargetInfo.h"

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

bool InterpretOffsetOf(InterpState &S, CodePtr OpPC, const OffsetOfExpr *E,
llvm::ArrayRef<int64_t> ArrayIndices,
int64_t &IntResult) {
CharUnits Result;
unsigned N = E->getNumComponents();
assert(N > 0);

unsigned ArrayIndex = 0;
QualType CurrentType = E->getTypeSourceInfo()->getType();
for (unsigned I = 0; I != N; ++I) {
const OffsetOfNode &Node = E->getComponent(I);
switch (Node.getKind()) {
case OffsetOfNode::Field: {
const FieldDecl *MemberDecl = Node.getField();
const RecordType *RT = CurrentType->getAs<RecordType>();
if (!RT)
return false;
RecordDecl *RD = RT->getDecl();
if (RD->isInvalidDecl())
return false;
const ASTRecordLayout &RL = S.getCtx().getASTRecordLayout(RD);
unsigned FieldIndex = MemberDecl->getFieldIndex();
assert(FieldIndex < RL.getFieldCount() && "offsetof field in wrong type");
Result += S.getCtx().toCharUnitsFromBits(RL.getFieldOffset(FieldIndex));
CurrentType = MemberDecl->getType().getNonReferenceType();
break;
}
case OffsetOfNode::Array: {
// When generating bytecode, we put all the index expressions as Sint64 on
// the stack.
int64_t Index = ArrayIndices[ArrayIndex];
const ArrayType *AT = S.getCtx().getAsArrayType(CurrentType);
if (!AT)
return false;
CurrentType = AT->getElementType();
CharUnits ElementSize = S.getCtx().getTypeSizeInChars(CurrentType);
Result += Index * ElementSize;
++ArrayIndex;
break;
}
case OffsetOfNode::Base: {
const CXXBaseSpecifier *BaseSpec = Node.getBase();
if (BaseSpec->isVirtual())
return false;

// Find the layout of the class whose base we are looking into.
const RecordType *RT = CurrentType->getAs<RecordType>();
if (!RT)
return false;
const RecordDecl *RD = RT->getDecl();
if (RD->isInvalidDecl())
return false;
const ASTRecordLayout &RL = S.getCtx().getASTRecordLayout(RD);

// Find the base class itself.
CurrentType = BaseSpec->getType();
const RecordType *BaseRT = CurrentType->getAs<RecordType>();
if (!BaseRT)
return false;

// Add the offset to the base.
Result += RL.getBaseClassOffset(cast<CXXRecordDecl>(BaseRT->getDecl()));
break;
}
case OffsetOfNode::Identifier:
llvm_unreachable("Dependent OffsetOfExpr?");
}
}

IntResult = Result.getQuantity();

return true;
}

} // namespace interp
} // namespace clang
7 changes: 7 additions & 0 deletions clang/lib/AST/Interp/Opcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def ArgRoundingMode : ArgType { let Name = "llvm::RoundingMode"; }
def ArgLETD: ArgType { let Name = "const LifetimeExtendedTemporaryDecl *"; }
def ArgCastKind : ArgType { let Name = "CastKind"; }
def ArgCallExpr : ArgType { let Name = "const CallExpr *"; }
def ArgOffsetOfExpr : ArgType { let Name = "const OffsetOfExpr *"; }

//===----------------------------------------------------------------------===//
// Classes of types instructions operate on.
Expand Down Expand Up @@ -198,6 +199,12 @@ def CallPtr : Opcode {
let Types = [];
}

def OffsetOf : Opcode {
let Types = [IntegerTypeClass];
let Args = [ArgOffsetOfExpr];
let HasGroup = 1;
}

//===----------------------------------------------------------------------===//
// Frame management
//===----------------------------------------------------------------------===//
Expand Down

0 comments on commit 87461d6

Please sign in to comment.