aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm-project/clang/lib/AST/Interp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/clang/lib/AST/Interp')
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Boolean.h27
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.cpp135
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.h4
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.cpp1138
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.h171
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/ByteCodeGenError.h12
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.cpp203
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.h16
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Context.cpp39
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Context.h6
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Descriptor.cpp93
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Descriptor.h92
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Disasm.cpp28
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.cpp29
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.h12
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Function.cpp15
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Function.h74
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Integral.h126
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp93
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Interp.h547
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/InterpBlock.h53
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.cpp90
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.h42
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/InterpStack.cpp2
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/InterpStack.h67
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/InterpState.h3
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Opcodes.td118
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp26
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Pointer.h49
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/PrimType.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/PrimType.h14
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Program.cpp123
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Program.h54
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Record.h22
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Source.cpp4
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/Source.h16
-rw-r--r--contrib/llvm-project/clang/lib/AST/Interp/State.h5
37 files changed, 2741 insertions, 811 deletions
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Boolean.h b/contrib/llvm-project/clang/lib/AST/Interp/Boolean.h
index 2baa717311bc..3122388a49a5 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Boolean.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Boolean.h
@@ -22,7 +22,7 @@ namespace clang {
namespace interp {
/// Wrapper around boolean types.
-class Boolean {
+class Boolean final {
private:
/// Underlying boolean.
bool V;
@@ -46,9 +46,15 @@ class Boolean {
Boolean operator-() const { return Boolean(V); }
Boolean operator~() const { return Boolean(true); }
- explicit operator unsigned() const { return V; }
+ explicit operator int8_t() const { return V; }
+ explicit operator uint8_t() const { return V; }
+ explicit operator int16_t() const { return V; }
+ explicit operator uint16_t() const { return V; }
+ explicit operator int32_t() const { return V; }
+ explicit operator uint32_t() const { return V; }
explicit operator int64_t() const { return V; }
explicit operator uint64_t() const { return V; }
+ explicit operator bool() const { return V; }
APSInt toAPSInt() const {
return APSInt(APInt(1, static_cast<uint64_t>(V), false), true);
@@ -84,9 +90,10 @@ class Boolean {
static Boolean min(unsigned NumBits) { return Boolean(false); }
static Boolean max(unsigned NumBits) { return Boolean(true); }
- template <typename T>
- static std::enable_if_t<std::is_integral<T>::value, Boolean> from(T Value) {
- return Boolean(Value != 0);
+ template <typename T> static Boolean from(T Value) {
+ if constexpr (std::is_integral<T>::value)
+ return Boolean(Value != 0);
+ return Boolean(static_cast<decltype(Boolean::V)>(Value) != 0);
}
template <unsigned SrcBits, bool SrcSign>
@@ -134,6 +141,16 @@ class Boolean {
*R = Boolean(A.V && B.V);
return false;
}
+
+ static bool inv(Boolean A, Boolean *R) {
+ *R = Boolean(!A.V);
+ return false;
+ }
+
+ static bool neg(Boolean A, Boolean *R) {
+ *R = Boolean(A.V);
+ return false;
+ }
};
inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Boolean &B) {
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.cpp
index a69b23fd613c..4633d1e0823b 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.cpp
+++ b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.cpp
@@ -19,51 +19,72 @@ using namespace clang::interp;
using APSInt = llvm::APSInt;
using Error = llvm::Error;
-Expected<Function *> ByteCodeEmitter::compileFunc(const FunctionDecl *F) {
- // Do not try to compile undefined functions.
- if (!F->isDefined(F) || (!F->hasBody() && F->willHaveBody()))
- return nullptr;
-
- // Set up argument indices.
- unsigned ParamOffset = 0;
- SmallVector<PrimType, 8> ParamTypes;
- llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors;
-
- // If the return is not a primitive, a pointer to the storage where the value
- // is initialized in is passed as the first argument.
- QualType Ty = F->getReturnType();
- if (!Ty->isVoidType() && !Ctx.classify(Ty)) {
- ParamTypes.push_back(PT_Ptr);
- ParamOffset += align(primSize(PT_Ptr));
- }
+Expected<Function *>
+ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) {
+ // Function is not defined at all or not yet. We will
+ // create a Function instance but not compile the body. That
+ // will (maybe) happen later.
+ bool HasBody = FuncDecl->hasBody(FuncDecl);
+
+ // Create a handle over the emitted code.
+ Function *Func = P.getFunction(FuncDecl);
+ if (!Func) {
+ // Set up argument indices.
+ unsigned ParamOffset = 0;
+ SmallVector<PrimType, 8> ParamTypes;
+ llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors;
+
+ // If the return is not a primitive, a pointer to the storage where the
+ // value is initialized in is passed as the first argument. See 'RVO'
+ // elsewhere in the code.
+ QualType Ty = FuncDecl->getReturnType();
+ bool HasRVO = false;
+ if (!Ty->isVoidType() && !Ctx.classify(Ty)) {
+ HasRVO = true;
+ ParamTypes.push_back(PT_Ptr);
+ ParamOffset += align(primSize(PT_Ptr));
+ }
+
+ // If the function decl is a member decl, the next parameter is
+ // the 'this' pointer. This parameter is pop()ed from the
+ // InterpStack when calling the function.
+ bool HasThisPointer = false;
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl);
+ MD && MD->isInstance()) {
+ HasThisPointer = true;
+ ParamTypes.push_back(PT_Ptr);
+ ParamOffset += align(primSize(PT_Ptr));
+ }
- // Assign descriptors to all parameters.
- // Composite objects are lowered to pointers.
- for (const ParmVarDecl *PD : F->parameters()) {
- PrimType Ty;
- if (llvm::Optional<PrimType> T = Ctx.classify(PD->getType())) {
- Ty = *T;
- } else {
- Ty = PT_Ptr;
+ // Assign descriptors to all parameters.
+ // Composite objects are lowered to pointers.
+ for (const ParmVarDecl *PD : FuncDecl->parameters()) {
+ PrimType Ty = Ctx.classify(PD->getType()).value_or(PT_Ptr);
+ Descriptor *Desc = P.createDescriptor(PD, Ty);
+ ParamDescriptors.insert({ParamOffset, {Ty, Desc}});
+ Params.insert({PD, ParamOffset});
+ ParamOffset += align(primSize(Ty));
+ ParamTypes.push_back(Ty);
}
- Descriptor *Desc = P.createDescriptor(PD, Ty);
- ParamDescriptors.insert({ParamOffset, {Ty, Desc}});
- Params.insert({PD, ParamOffset});
- ParamOffset += align(primSize(Ty));
- ParamTypes.push_back(Ty);
+ Func =
+ P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes),
+ std::move(ParamDescriptors), HasThisPointer, HasRVO);
}
- // Create a handle over the emitted code.
- Function *Func = P.createFunction(F, ParamOffset, std::move(ParamTypes),
- std::move(ParamDescriptors));
+ assert(Func);
+ if (!HasBody)
+ return Func;
+
// Compile the function body.
- if (!F->isConstexpr() || !visitFunc(F)) {
+ if (!FuncDecl->isConstexpr() || !visitFunc(FuncDecl)) {
// Return a dummy function if compilation failed.
if (BailLocation)
return llvm::make_error<ByteCodeGenError>(*BailLocation);
- else
+ else {
+ Func->setIsFullyCompiled(true);
return Func;
+ }
} else {
// Create scopes from descriptors.
llvm::SmallVector<Scope, 2> Scopes;
@@ -74,6 +95,7 @@ Expected<Function *> ByteCodeEmitter::compileFunc(const FunctionDecl *F) {
// Set the function's code.
Func->setCode(NextLocalOffset, std::move(Code), std::move(SrcMap),
std::move(Scopes));
+ Func->setIsFullyCompiled(true);
return Func;
}
}
@@ -94,7 +116,8 @@ void ByteCodeEmitter::emitLabel(LabelTy Label) {
using namespace llvm::support;
/// Rewrite the operand of all jumps to this label.
- void *Location = Code.data() + Reloc - sizeof(int32_t);
+ void *Location = Code.data() + Reloc - align(sizeof(int32_t));
+ assert(aligned(Location));
const int32_t Offset = Target - static_cast<int64_t>(Reloc);
endian::write<int32_t, endianness::native, 1>(Location, Offset);
}
@@ -104,7 +127,9 @@ void ByteCodeEmitter::emitLabel(LabelTy Label) {
int32_t ByteCodeEmitter::getOffset(LabelTy Label) {
// Compute the PC offset which the jump is relative to.
- const int64_t Position = Code.size() + sizeof(Opcode) + sizeof(int32_t);
+ const int64_t Position =
+ Code.size() + align(sizeof(Opcode)) + align(sizeof(int32_t));
+ assert(aligned(Position));
// If target is known, compute jump offset.
auto It = LabelOffsets.find(Label);
@@ -126,30 +151,32 @@ bool ByteCodeEmitter::bail(const SourceLocation &Loc) {
/// Helper to write bytecode and bail out if 32-bit offsets become invalid.
/// Pointers will be automatically marshalled as 32-bit IDs.
template <typename T>
-static std::enable_if_t<!std::is_pointer<T>::value, void>
-emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) {
- size_t Size = sizeof(Val);
- if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
- Success = false;
- return;
- }
+static void emit(Program &P, std::vector<char> &Code, const T &Val,
+ bool &Success) {
+ size_t Size;
- const char *Data = reinterpret_cast<const char *>(&Val);
- Code.insert(Code.end(), Data, Data + Size);
-}
+ if constexpr (std::is_pointer_v<T>)
+ Size = sizeof(uint32_t);
+ else
+ Size = sizeof(T);
-template <typename T>
-static std::enable_if_t<std::is_pointer<T>::value, void>
-emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) {
- size_t Size = sizeof(uint32_t);
if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
Success = false;
return;
}
- uint32_t ID = P.getOrCreateNativePointer(Val);
- const char *Data = reinterpret_cast<const char *>(&ID);
- Code.insert(Code.end(), Data, Data + Size);
+ // Access must be aligned!
+ size_t ValPos = align(Code.size());
+ Size = align(Size);
+ assert(aligned(ValPos + Size));
+ Code.resize(ValPos + Size);
+
+ if constexpr (!std::is_pointer_v<T>) {
+ new (Code.data() + ValPos) T(Val);
+ } else {
+ uint32_t ID = P.getOrCreateNativePointer(Val);
+ new (Code.data() + ValPos) uint32_t(ID);
+ }
}
template <typename... Tys>
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.h b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.h
index 03452a350c96..30da06b20250 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeEmitter.h
@@ -37,7 +37,7 @@ protected:
public:
/// Compiles the function into the module.
- llvm::Expected<Function *> compileFunc(const FunctionDecl *F);
+ llvm::Expected<Function *> compileFunc(const FunctionDecl *FuncDecl);
protected:
ByteCodeEmitter(Context &Ctx, Program &P) : Ctx(Ctx), P(P) {}
@@ -83,7 +83,7 @@ private:
/// Offset of the next local variable.
unsigned NextLocalOffset = 0;
/// Location of a failure.
- llvm::Optional<SourceLocation> BailLocation;
+ std::optional<SourceLocation> BailLocation;
/// Label information for linker.
llvm::DenseMap<LabelTy, unsigned> LabelOffsets;
/// Location of label relocations.
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 9b729e347a24..615dbdefefbe 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -9,6 +9,7 @@
#include "ByteCodeExprGen.h"
#include "ByteCodeEmitter.h"
#include "ByteCodeGenError.h"
+#include "ByteCodeStmtGen.h"
#include "Context.h"
#include "Function.h"
#include "PrimType.h"
@@ -19,8 +20,6 @@ using namespace clang;
using namespace clang::interp;
using APSInt = llvm::APSInt;
-template <typename T> using Expected = llvm::Expected<T>;
-template <typename T> using Optional = llvm::Optional<T>;
namespace clang {
namespace interp {
@@ -42,45 +41,19 @@ private:
/// Scope used to handle initialization methods.
template <class Emitter> class OptionScope {
public:
- using InitFnRef = typename ByteCodeExprGen<Emitter>::InitFnRef;
- using ChainedInitFnRef = std::function<bool(InitFnRef)>;
-
/// Root constructor, compiling or discarding primitives.
OptionScope(ByteCodeExprGen<Emitter> *Ctx, bool NewDiscardResult)
- : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult),
- OldInitFn(std::move(Ctx->InitFn)) {
+ : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult) {
Ctx->DiscardResult = NewDiscardResult;
- Ctx->InitFn = llvm::Optional<InitFnRef>{};
- }
-
- /// Root constructor, setting up compilation state.
- OptionScope(ByteCodeExprGen<Emitter> *Ctx, InitFnRef NewInitFn)
- : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult),
- OldInitFn(std::move(Ctx->InitFn)) {
- Ctx->DiscardResult = true;
- Ctx->InitFn = NewInitFn;
}
- /// Extends the chain of initialisation pointers.
- OptionScope(ByteCodeExprGen<Emitter> *Ctx, ChainedInitFnRef NewInitFn)
- : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult),
- OldInitFn(std::move(Ctx->InitFn)) {
- assert(OldInitFn && "missing initializer");
- Ctx->InitFn = [this, NewInitFn] { return NewInitFn(*OldInitFn); };
- }
-
- ~OptionScope() {
- Ctx->DiscardResult = OldDiscardResult;
- Ctx->InitFn = std::move(OldInitFn);
- }
+ ~OptionScope() { Ctx->DiscardResult = OldDiscardResult; }
private:
/// Parent context.
ByteCodeExprGen<Emitter> *Ctx;
/// Old discard flag to restore.
bool OldDiscardResult;
- /// Old pointer emitter to restore.
- llvm::Optional<InitFnRef> OldInitFn;
};
} // namespace interp
@@ -106,6 +79,22 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
});
}
+ case CK_UncheckedDerivedToBase:
+ case CK_DerivedToBase: {
+ if (!this->visit(SubExpr))
+ return false;
+
+ const CXXRecordDecl *FromDecl = getRecordDecl(SubExpr);
+ assert(FromDecl);
+ const CXXRecordDecl *ToDecl = getRecordDecl(CE);
+ assert(ToDecl);
+ const Record *R = getRecord(FromDecl);
+ const Record::Base *ToBase = R->getBase(ToDecl);
+ assert(ToBase);
+
+ return this->emitGetPtrBase(ToBase->Offset, CE);
+ }
+
case CK_ArrayToPointerDecay:
case CK_AtomicToNonAtomic:
case CK_ConstructorConversion:
@@ -113,16 +102,30 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
case CK_NonAtomicToAtomic:
case CK_NoOp:
case CK_UserDefinedConversion:
- return this->Visit(SubExpr);
+ case CK_NullToPointer:
+ return this->visit(SubExpr);
+
+ case CK_IntegralToBoolean:
+ case CK_IntegralCast: {
+ std::optional<PrimType> FromT = classify(SubExpr->getType());
+ std::optional<PrimType> ToT = classify(CE->getType());
+ if (!FromT || !ToT)
+ return false;
+
+ if (!this->visit(SubExpr))
+ return false;
+
+ // TODO: Emit this only if FromT != ToT.
+ return this->emitCast(*FromT, *ToT, CE);
+ }
case CK_ToVoid:
return discard(SubExpr);
- default: {
- // TODO: implement other casts.
- return this->bail(CE);
- }
+ default:
+ assert(false && "Cast not implemented");
}
+ llvm_unreachable("Unhandled clang::CastKind enum");
}
template <class Emitter>
@@ -130,16 +133,12 @@ bool ByteCodeExprGen<Emitter>::VisitIntegerLiteral(const IntegerLiteral *LE) {
if (DiscardResult)
return true;
- auto Val = LE->getValue();
- QualType LitTy = LE->getType();
- if (Optional<PrimType> T = classify(LitTy))
- return emitConst(*T, getIntWidth(LitTy), LE->getValue(), LE);
- return this->bail(LE);
+ return this->emitConst(LE->getValue(), LE);
}
template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitParenExpr(const ParenExpr *PE) {
- return this->Visit(PE->getSubExpr());
+ return this->visit(PE->getSubExpr());
}
template <class Emitter>
@@ -152,7 +151,7 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
case BO_Comma:
if (!discard(LHS))
return false;
- if (!this->Visit(RHS))
+ if (!this->visit(RHS))
return false;
return true;
default:
@@ -160,53 +159,398 @@ bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) {
}
// Typecheck the args.
- Optional<PrimType> LT = classify(LHS->getType());
- Optional<PrimType> RT = classify(RHS->getType());
- if (!LT || !RT) {
+ std::optional<PrimType> LT = classify(LHS->getType());
+ std::optional<PrimType> RT = classify(RHS->getType());
+ std::optional<PrimType> T = classify(BO->getType());
+ if (!LT || !RT || !T) {
return this->bail(BO);
}
- if (Optional<PrimType> T = classify(BO->getType())) {
- if (!visit(LHS))
+ auto Discard = [this, T, BO](bool Result) {
+ if (!Result)
return false;
- if (!visit(RHS))
+ return DiscardResult ? this->emitPop(*T, BO) : true;
+ };
+
+ // Pointer arithmetic special case.
+ if (BO->getOpcode() == BO_Add || BO->getOpcode() == BO_Sub) {
+ if (*T == PT_Ptr || (*LT == PT_Ptr && *RT == PT_Ptr))
+ return this->VisitPointerArithBinOp(BO);
+ }
+
+ if (!visit(LHS) || !visit(RHS))
+ return false;
+
+ switch (BO->getOpcode()) {
+ case BO_EQ:
+ return Discard(this->emitEQ(*LT, BO));
+ case BO_NE:
+ return Discard(this->emitNE(*LT, BO));
+ case BO_LT:
+ return Discard(this->emitLT(*LT, BO));
+ case BO_LE:
+ return Discard(this->emitLE(*LT, BO));
+ case BO_GT:
+ return Discard(this->emitGT(*LT, BO));
+ case BO_GE:
+ return Discard(this->emitGE(*LT, BO));
+ case BO_Sub:
+ return Discard(this->emitSub(*T, BO));
+ case BO_Add:
+ return Discard(this->emitAdd(*T, BO));
+ case BO_Mul:
+ return Discard(this->emitMul(*T, BO));
+ case BO_Rem:
+ return Discard(this->emitRem(*T, BO));
+ case BO_Div:
+ return Discard(this->emitDiv(*T, BO));
+ case BO_Assign:
+ if (DiscardResult)
+ return this->emitStorePop(*T, BO);
+ return this->emitStore(*T, BO);
+ case BO_And:
+ return Discard(this->emitBitAnd(*T, BO));
+ case BO_Or:
+ return Discard(this->emitBitOr(*T, BO));
+ case BO_Shl:
+ return Discard(this->emitShl(*LT, *RT, BO));
+ case BO_Shr:
+ return Discard(this->emitShr(*LT, *RT, BO));
+ case BO_Xor:
+ return Discard(this->emitBitXor(*T, BO));
+ case BO_LAnd:
+ case BO_LOr:
+ default:
+ return this->bail(BO);
+ }
+
+ llvm_unreachable("Unhandled binary op");
+}
+
+/// Perform addition/subtraction of a pointer and an integer or
+/// subtraction of two pointers.
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitPointerArithBinOp(const BinaryOperator *E) {
+ BinaryOperatorKind Op = E->getOpcode();
+ const Expr *LHS = E->getLHS();
+ const Expr *RHS = E->getRHS();
+
+ if ((Op != BO_Add && Op != BO_Sub) ||
+ (!LHS->getType()->isPointerType() && !RHS->getType()->isPointerType()))
+ return false;
+
+ std::optional<PrimType> LT = classify(LHS);
+ std::optional<PrimType> RT = classify(RHS);
+
+ if (!LT || !RT)
+ return false;
+
+ if (LHS->getType()->isPointerType() && RHS->getType()->isPointerType()) {
+ if (Op != BO_Sub)
+ return false;
+
+ assert(E->getType()->isIntegerType());
+ if (!visit(RHS) || !visit(LHS))
+ return false;
+
+ return this->emitSubPtr(classifyPrim(E->getType()), E);
+ }
+
+ PrimType OffsetType;
+ if (LHS->getType()->isIntegerType()) {
+ if (!visit(RHS) || !visit(LHS))
+ return false;
+ OffsetType = *LT;
+ } else if (RHS->getType()->isIntegerType()) {
+ if (!visit(LHS) || !visit(RHS))
+ return false;
+ OffsetType = *RT;
+ } else {
+ return false;
+ }
+
+ if (Op == BO_Add)
+ return this->emitAddOffset(OffsetType, E);
+ else if (Op == BO_Sub)
+ return this->emitSubOffset(OffsetType, E);
+
+ return this->bail(E);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitImplicitValueInitExpr(const ImplicitValueInitExpr *E) {
+ if (std::optional<PrimType> T = classify(E))
+ return this->emitZero(*T, E);
+
+ return false;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitArraySubscriptExpr(
+ const ArraySubscriptExpr *E) {
+ const Expr *Base = E->getBase();
+ const Expr *Index = E->getIdx();
+ PrimType IndexT = classifyPrim(Index->getType());
+
+ // Take pointer of LHS, add offset from RHS, narrow result.
+ // What's left on the stack after this is a pointer.
+ if (!this->visit(Base))
+ return false;
+
+ if (!this->visit(Index))
+ return false;
+
+ if (!this->emitAddOffset(IndexT, E))
+ return false;
+
+ if (!this->emitNarrowPtr(E))
+ return false;
+
+ if (DiscardResult)
+ return this->emitPopPtr(E);
+
+ return true;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitInitListExpr(const InitListExpr *E) {
+ for (const Expr *Init : E->inits()) {
+ if (!this->visit(Init))
return false;
+ }
+ return true;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitSubstNonTypeTemplateParmExpr(
+ const SubstNonTypeTemplateParmExpr *E) {
+ return this->visit(E->getReplacement());
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitConstantExpr(const ConstantExpr *E) {
+ // TODO: Check if the ConstantExpr already has a value set and if so,
+ // use that instead of evaluating it again.
+ return this->visit(E->getSubExpr());
+}
+
+static CharUnits AlignOfType(QualType T, const ASTContext &ASTCtx,
+ UnaryExprOrTypeTrait Kind) {
+ bool AlignOfReturnsPreferred =
+ ASTCtx.getLangOpts().getClangABICompat() <= LangOptions::ClangABI::Ver7;
- auto Discard = [this, T, BO](bool Result) {
- if (!Result)
+ // C++ [expr.alignof]p3:
+ // When alignof is applied to a reference type, the result is the
+ // alignment of the referenced type.
+ if (const auto *Ref = T->getAs<ReferenceType>())
+ T = Ref->getPointeeType();
+
+ // __alignof is defined to return the preferred alignment.
+ // Before 8, clang returned the preferred alignment for alignof and
+ // _Alignof as well.
+ if (Kind == UETT_PreferredAlignOf || AlignOfReturnsPreferred)
+ return ASTCtx.toCharUnitsFromBits(ASTCtx.getPreferredTypeAlign(T));
+
+ return ASTCtx.getTypeAlignInChars(T);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitUnaryExprOrTypeTraitExpr(
+ const UnaryExprOrTypeTraitExpr *E) {
+ UnaryExprOrTypeTrait Kind = E->getKind();
+ ASTContext &ASTCtx = Ctx.getASTContext();
+
+ if (Kind == UETT_SizeOf) {
+ QualType ArgType = E->getTypeOfArgument();
+ CharUnits Size;
+ if (ArgType->isVoidType() || ArgType->isFunctionType())
+ Size = CharUnits::One();
+ else {
+ if (ArgType->isDependentType() || !ArgType->isConstantSizeType())
return false;
- return DiscardResult ? this->emitPop(*T, BO) : true;
- };
-
- switch (BO->getOpcode()) {
- case BO_EQ:
- return Discard(this->emitEQ(*LT, BO));
- case BO_NE:
- return Discard(this->emitNE(*LT, BO));
- case BO_LT:
- return Discard(this->emitLT(*LT, BO));
- case BO_LE:
- return Discard(this->emitLE(*LT, BO));
- case BO_GT:
- return Discard(this->emitGT(*LT, BO));
- case BO_GE:
- return Discard(this->emitGE(*LT, BO));
- case BO_Sub:
- return Discard(this->emitSub(*T, BO));
- case BO_Add:
- return Discard(this->emitAdd(*T, BO));
- case BO_Mul:
- return Discard(this->emitMul(*T, BO));
- default:
- return this->bail(BO);
+
+ Size = ASTCtx.getTypeSizeInChars(ArgType);
}
+
+ return this->emitConst(Size.getQuantity(), E);
+ }
+
+ if (Kind == UETT_AlignOf || Kind == UETT_PreferredAlignOf) {
+ CharUnits Size;
+
+ if (E->isArgumentType()) {
+ QualType ArgType = E->getTypeOfArgument();
+
+ Size = AlignOfType(ArgType, ASTCtx, Kind);
+ } else {
+ // Argument is an expression, not a type.
+ const Expr *Arg = E->getArgumentExpr()->IgnoreParens();
+
+ // The kinds of expressions that we have special-case logic here for
+ // should be kept up to date with the special checks for those
+ // expressions in Sema.
+
+ // alignof decl is always accepted, even if it doesn't make sense: we
+ // default to 1 in those cases.
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(Arg))
+ Size = ASTCtx.getDeclAlign(DRE->getDecl(),
+ /*RefAsPointee*/ true);
+ else if (const auto *ME = dyn_cast<MemberExpr>(Arg))
+ Size = ASTCtx.getDeclAlign(ME->getMemberDecl(),
+ /*RefAsPointee*/ true);
+ else
+ Size = AlignOfType(Arg->getType(), ASTCtx, Kind);
+ }
+
+ return this->emitConst(Size.getQuantity(), E);
}
- return this->bail(BO);
+ return false;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitMemberExpr(const MemberExpr *E) {
+ if (DiscardResult)
+ return true;
+
+ // 'Base.Member'
+ const Expr *Base = E->getBase();
+ const ValueDecl *Member = E->getMemberDecl();
+
+ if (!this->visit(Base))
+ return false;
+
+ // Base above gives us a pointer on the stack.
+ // TODO: Implement non-FieldDecl members.
+ if (const auto *FD = dyn_cast<FieldDecl>(Member)) {
+ const RecordDecl *RD = FD->getParent();
+ const Record *R = getRecord(RD);
+ const Record::Field *F = R->getField(FD);
+ // Leave a pointer to the field on the stack.
+ if (F->Decl->getType()->isReferenceType())
+ return this->emitGetFieldPop(PT_Ptr, F->Offset, E);
+ return this->emitGetPtrField(F->Offset, E);
+ }
+
+ return false;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitArrayInitIndexExpr(
+ const ArrayInitIndexExpr *E) {
+ // ArrayIndex might not be set if a ArrayInitIndexExpr is being evaluated
+ // stand-alone, e.g. via EvaluateAsInt().
+ if (!ArrayIndex)
+ return false;
+ return this->emitConst(*ArrayIndex, E);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitOpaqueValueExpr(const OpaqueValueExpr *E) {
+ return this->visit(E->getSourceExpr());
}
template <class Emitter>
-bool ByteCodeExprGen<Emitter>::discard(const Expr *E) {
+bool ByteCodeExprGen<Emitter>::VisitAbstractConditionalOperator(
+ const AbstractConditionalOperator *E) {
+ const Expr *Condition = E->getCond();
+ const Expr *TrueExpr = E->getTrueExpr();
+ const Expr *FalseExpr = E->getFalseExpr();
+
+ LabelTy LabelEnd = this->getLabel(); // Label after the operator.
+ LabelTy LabelFalse = this->getLabel(); // Label for the false expr.
+
+ if (!this->visit(Condition))
+ return false;
+ if (!this->jumpFalse(LabelFalse))
+ return false;
+
+ if (!this->visit(TrueExpr))
+ return false;
+ if (!this->jump(LabelEnd))
+ return false;
+
+ this->emitLabel(LabelFalse);
+
+ if (!this->visit(FalseExpr))
+ return false;
+
+ this->fallthrough(LabelEnd);
+ this->emitLabel(LabelEnd);
+
+ return true;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitStringLiteral(const StringLiteral *E) {
+ unsigned StringIndex = P.createGlobalString(E);
+ return this->emitGetPtrGlobal(StringIndex, E);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitCharacterLiteral(
+ const CharacterLiteral *E) {
+ return this->emitConst(E->getValue(), E);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitCompoundAssignOperator(
+ const CompoundAssignOperator *E) {
+ const Expr *LHS = E->getLHS();
+ const Expr *RHS = E->getRHS();
+ std::optional<PrimType> LT = classify(E->getLHS()->getType());
+ std::optional<PrimType> RT = classify(E->getRHS()->getType());
+
+ if (!LT || !RT)
+ return false;
+
+ assert(!E->getType()->isPointerType() &&
+ "Support pointer arithmethic in compound assignment operators");
+
+ // Get LHS pointer, load its value and get RHS value.
+ if (!visit(LHS))
+ return false;
+ if (!this->emitLoad(*LT, E))
+ return false;
+ if (!visit(RHS))
+ return false;
+
+ // Perform operation.
+ switch (E->getOpcode()) {
+ case BO_AddAssign:
+ if (!this->emitAdd(*LT, E))
+ return false;
+ break;
+ case BO_SubAssign:
+ if (!this->emitSub(*LT, E))
+ return false;
+ break;
+
+ case BO_MulAssign:
+ case BO_DivAssign:
+ case BO_RemAssign:
+ case BO_ShlAssign:
+ if (!this->emitShl(*LT, *RT, E))
+ return false;
+ break;
+ case BO_ShrAssign:
+ if (!this->emitShr(*LT, *RT, E))
+ return false;
+ break;
+ case BO_AndAssign:
+ case BO_XorAssign:
+ case BO_OrAssign:
+ default:
+ llvm_unreachable("Unimplemented compound assign operator");
+ }
+
+ // And store the result in LHS.
+ if (DiscardResult)
+ return this->emitStorePop(*LT, E);
+ return this->emitStore(*LT, E);
+}
+
+template <class Emitter> bool ByteCodeExprGen<Emitter>::discard(const Expr *E) {
OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/true);
return this->Visit(E);
}
@@ -219,7 +563,7 @@ bool ByteCodeExprGen<Emitter>::visit(const Expr *E) {
template <class Emitter>
bool ByteCodeExprGen<Emitter>::visitBool(const Expr *E) {
- if (Optional<PrimType> T = classify(E->getType())) {
+ if (std::optional<PrimType> T = classify(E->getType())) {
return visit(E);
} else {
return this->bail(E);
@@ -257,7 +601,7 @@ template <class Emitter>
bool ByteCodeExprGen<Emitter>::dereference(
const Expr *LV, DerefKind AK, llvm::function_ref<bool(PrimType)> Direct,
llvm::function_ref<bool(PrimType)> Indirect) {
- if (Optional<PrimType> T = classify(LV->getType())) {
+ if (std::optional<PrimType> T = classify(LV->getType())) {
if (!LV->refersToBitField()) {
// Only primitive, non bit-field types can be dereferenced directly.
if (auto *DE = dyn_cast<DeclRefExpr>(LV)) {
@@ -350,7 +694,7 @@ bool ByteCodeExprGen<Emitter>::dereferenceVar(
return false;
return DiscardResult ? true : this->emitGetPtrLocal(L.Offset, LV);
}
- } else if (auto Idx = getGlobalIdx(VD)) {
+ } else if (auto Idx = P.getGlobal(VD)) {
switch (AK) {
case DerefKind::Read:
if (!this->emitGetGlobal(T, *Idx, LV))
@@ -382,7 +726,7 @@ bool ByteCodeExprGen<Emitter>::dereferenceVar(
if (VD->hasLocalStorage() && VD->hasInit() && !VD->isConstexpr()) {
QualType VT = VD->getType();
if (VT.isConstQualified() && VT->isFundamentalType())
- return this->Visit(VD->getInit());
+ return this->visit(VD->getInit());
}
}
@@ -391,27 +735,27 @@ bool ByteCodeExprGen<Emitter>::dereferenceVar(
}
template <class Emitter>
-bool ByteCodeExprGen<Emitter>::emitConst(PrimType T, unsigned NumBits,
- const APInt &Value, const Expr *E) {
- switch (T) {
+template <typename T>
+bool ByteCodeExprGen<Emitter>::emitConst(T Value, const Expr *E) {
+ switch (classifyPrim(E->getType())) {
case PT_Sint8:
- return this->emitConstSint8(Value.getSExtValue(), E);
+ return this->emitConstSint8(Value, E);
case PT_Uint8:
- return this->emitConstUint8(Value.getZExtValue(), E);
+ return this->emitConstUint8(Value, E);
case PT_Sint16:
- return this->emitConstSint16(Value.getSExtValue(), E);
+ return this->emitConstSint16(Value, E);
case PT_Uint16:
- return this->emitConstUint16(Value.getZExtValue(), E);
+ return this->emitConstUint16(Value, E);
case PT_Sint32:
- return this->emitConstSint32(Value.getSExtValue(), E);
+ return this->emitConstSint32(Value, E);
case PT_Uint32:
- return this->emitConstUint32(Value.getZExtValue(), E);
+ return this->emitConstUint32(Value, E);
case PT_Sint64:
- return this->emitConstSint64(Value.getSExtValue(), E);
+ return this->emitConstSint64(Value, E);
case PT_Uint64:
- return this->emitConstUint64(Value.getZExtValue(), E);
+ return this->emitConstUint64(Value, E);
case PT_Bool:
- return this->emitConstBool(Value.getBoolValue(), E);
+ return this->emitConstBool(Value, E);
case PT_Ptr:
llvm_unreachable("Invalid integral type");
break;
@@ -420,11 +764,29 @@ bool ByteCodeExprGen<Emitter>::emitConst(PrimType T, unsigned NumBits,
}
template <class Emitter>
+bool ByteCodeExprGen<Emitter>::emitConst(const APSInt &Value, const Expr *E) {
+ if (Value.isSigned())
+ return this->emitConst(Value.getSExtValue(), E);
+ return this->emitConst(Value.getZExtValue(), E);
+}
+
+template <class Emitter>
unsigned ByteCodeExprGen<Emitter>::allocateLocalPrimitive(DeclTy &&Src,
PrimType Ty,
bool IsConst,
bool IsExtended) {
- Descriptor *D = P.createDescriptor(Src, Ty, IsConst, Src.is<const Expr *>());
+ // Make sure we don't accidentally register the same decl twice.
+ if (const auto *VD =
+ dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>())) {
+ assert(!P.getGlobal(VD));
+ assert(Locals.find(VD) == Locals.end());
+ }
+
+ // FIXME: There are cases where Src.is<Expr*>() is wrong, e.g.
+ // (int){12} in C. Consider using Expr::isTemporaryObject() instead
+ // or isa<MaterializeTemporaryExpr>().
+ Descriptor *D = P.createDescriptor(Src, Ty, Descriptor::InlineDescMD, IsConst,
+ Src.is<const Expr *>());
Scope::Local Local = this->createLocal(D);
if (auto *VD = dyn_cast_or_null<ValueDecl>(Src.dyn_cast<const Decl *>()))
Locals.insert({VD, Local});
@@ -433,23 +795,34 @@ unsigned ByteCodeExprGen<Emitter>::allocateLocalPrimitive(DeclTy &&Src,
}
template <class Emitter>
-llvm::Optional<unsigned>
+std::optional<unsigned>
ByteCodeExprGen<Emitter>::allocateLocal(DeclTy &&Src, bool IsExtended) {
- QualType Ty;
+ // Make sure we don't accidentally register the same decl twice.
+ if (const auto *VD =
+ dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>())) {
+ assert(!P.getGlobal(VD));
+ assert(Locals.find(VD) == Locals.end());
+ }
+ QualType Ty;
const ValueDecl *Key = nullptr;
+ const Expr *Init = nullptr;
bool IsTemporary = false;
- if (auto *VD = dyn_cast_or_null<ValueDecl>(Src.dyn_cast<const Decl *>())) {
+ if (auto *VD = dyn_cast_if_present<ValueDecl>(Src.dyn_cast<const Decl *>())) {
Key = VD;
Ty = VD->getType();
+
+ if (const auto *VarD = dyn_cast<VarDecl>(VD))
+ Init = VarD->getInit();
}
if (auto *E = Src.dyn_cast<const Expr *>()) {
IsTemporary = true;
Ty = E->getType();
}
- Descriptor *D = P.createDescriptor(Src, Ty.getTypePtr(),
- Ty.isConstQualified(), IsTemporary);
+ Descriptor *D = P.createDescriptor(
+ Src, Ty.getTypePtr(), Descriptor::InlineDescMD, Ty.isConstQualified(),
+ IsTemporary, /*IsMutable=*/false, Init);
if (!D)
return {};
@@ -460,38 +833,230 @@ ByteCodeExprGen<Emitter>::allocateLocal(DeclTy &&Src, bool IsExtended) {
return Local.Offset;
}
+// NB: When calling this function, we have a pointer to the
+// array-to-initialize on the stack.
template <class Emitter>
-bool ByteCodeExprGen<Emitter>::visitInitializer(
- const Expr *Init, InitFnRef InitFn) {
- OptionScope<Emitter> Scope(this, InitFn);
- return this->Visit(Init);
+bool ByteCodeExprGen<Emitter>::visitArrayInitializer(const Expr *Initializer) {
+ assert(Initializer->getType()->isArrayType());
+
+ // TODO: Fillers?
+ if (const auto *InitList = dyn_cast<InitListExpr>(Initializer)) {
+ unsigned ElementIndex = 0;
+ for (const Expr *Init : InitList->inits()) {
+ if (std::optional<PrimType> T = classify(Init->getType())) {
+ // Visit the primitive element like normal.
+ if (!this->emitDupPtr(Init))
+ return false;
+ if (!this->visit(Init))
+ return false;
+ if (!this->emitInitElem(*T, ElementIndex, Init))
+ return false;
+ } else {
+ // Advance the pointer currently on the stack to the given
+ // dimension and narrow().
+ if (!this->emitDupPtr(Init))
+ return false;
+ if (!this->emitConstUint32(ElementIndex, Init))
+ return false;
+ if (!this->emitAddOffsetUint32(Init))
+ return false;
+ if (!this->emitNarrowPtr(Init))
+ return false;
+
+ if (!visitInitializer(Init))
+ return false;
+ }
+ if (!this->emitPopPtr(Init))
+ return false;
+
+ ++ElementIndex;
+ }
+ return true;
+ } else if (const auto *DIE = dyn_cast<CXXDefaultInitExpr>(Initializer)) {
+ return this->visitInitializer(DIE->getExpr());
+ } else if (const auto *AILE = dyn_cast<ArrayInitLoopExpr>(Initializer)) {
+ // TODO: This compiles to quite a lot of bytecode if the array is larger.
+ // Investigate compiling this to a loop, or at least try to use
+ // the AILE's Common expr.
+ const Expr *SubExpr = AILE->getSubExpr();
+ size_t Size = AILE->getArraySize().getZExtValue();
+ std::optional<PrimType> ElemT = classify(SubExpr->getType());
+
+ // So, every iteration, we execute an assignment here
+ // where the LHS is on the stack (the target array)
+ // and the RHS is our SubExpr.
+ for (size_t I = 0; I != Size; ++I) {
+ ArrayIndexScope<Emitter> IndexScope(this, I);
+
+ if (!this->emitDupPtr(SubExpr)) // LHS
+ return false;
+
+ if (ElemT) {
+ if (!this->visit(SubExpr))
+ return false;
+ if (!this->emitInitElem(*ElemT, I, Initializer))
+ return false;
+ } else {
+ // Narrow to our array element and recurse into visitInitializer()
+ if (!this->emitConstUint64(I, SubExpr))
+ return false;
+
+ if (!this->emitAddOffsetUint64(SubExpr))
+ return false;
+
+ if (!this->emitNarrowPtr(SubExpr))
+ return false;
+
+ if (!visitInitializer(SubExpr))
+ return false;
+ }
+
+ if (!this->emitPopPtr(Initializer))
+ return false;
+ }
+ return true;
+ } else if (const auto *IVIE = dyn_cast<ImplicitValueInitExpr>(Initializer)) {
+ const ArrayType *AT = IVIE->getType()->getAsArrayTypeUnsafe();
+ assert(AT);
+ const auto *CAT = cast<ConstantArrayType>(AT);
+ size_t NumElems = CAT->getSize().getZExtValue();
+
+ if (std::optional<PrimType> ElemT = classify(CAT->getElementType())) {
+ // TODO(perf): For int and bool types, we can probably just skip this
+ // since we memset our Block*s to 0 and so we have the desired value
+ // without this.
+ for (size_t I = 0; I != NumElems; ++I) {
+ if (!this->emitZero(*ElemT, Initializer))
+ return false;
+ if (!this->emitInitElem(*ElemT, I, Initializer))
+ return false;
+ }
+ } else {
+ assert(false && "default initializer for non-primitive type");
+ }
+
+ return true;
+ } else if (const auto *Ctor = dyn_cast<CXXConstructExpr>(Initializer)) {
+ const ConstantArrayType *CAT =
+ Ctx.getASTContext().getAsConstantArrayType(Ctor->getType());
+ assert(CAT);
+ size_t NumElems = CAT->getSize().getZExtValue();
+ const Function *Func = getFunction(Ctor->getConstructor());
+ if (!Func || !Func->isConstexpr())
+ return false;
+
+ // FIXME(perf): We're calling the constructor once per array element here,
+ // in the old intepreter we had a special-case for trivial constructors.
+ for (size_t I = 0; I != NumElems; ++I) {
+ if (!this->emitDupPtr(Initializer))
+ return false;
+ if (!this->emitConstUint64(I, Initializer))
+ return false;
+ if (!this->emitAddOffsetUint64(Initializer))
+ return false;
+ if (!this->emitNarrowPtr(Initializer))
+ return false;
+
+ // Constructor arguments.
+ for (const auto *Arg : Ctor->arguments()) {
+ if (!this->visit(Arg))
+ return false;
+ }
+
+ if (!this->emitCall(Func, Initializer))
+ return false;
+ }
+ return true;
+ }
+
+ assert(false && "Unknown expression for array initialization");
+ return false;
}
template <class Emitter>
-bool ByteCodeExprGen<Emitter>::getPtrVarDecl(const VarDecl *VD, const Expr *E) {
- // Generate a pointer to the local, loading refs.
- if (Optional<unsigned> Idx = getGlobalIdx(VD)) {
- if (VD->getType()->isReferenceType())
- return this->emitGetGlobalPtr(*Idx, E);
- else
- return this->emitGetPtrGlobal(*Idx, E);
+bool ByteCodeExprGen<Emitter>::visitRecordInitializer(const Expr *Initializer) {
+ Initializer = Initializer->IgnoreParenImpCasts();
+ assert(Initializer->getType()->isRecordType());
+
+ if (const auto CtorExpr = dyn_cast<CXXConstructExpr>(Initializer)) {
+ const Function *Func = getFunction(CtorExpr->getConstructor());
+
+ if (!Func || !Func->isConstexpr())
+ return false;
+
+ // The This pointer is already on the stack because this is an initializer,
+ // but we need to dup() so the call() below has its own copy.
+ if (!this->emitDupPtr(Initializer))
+ return false;
+
+ // Constructor arguments.
+ for (const auto *Arg : CtorExpr->arguments()) {
+ if (!this->visit(Arg))
+ return false;
+ }
+
+ return this->emitCall(Func, Initializer);
+ } else if (const auto *InitList = dyn_cast<InitListExpr>(Initializer)) {
+ const Record *R = getRecord(InitList->getType());
+
+ unsigned InitIndex = 0;
+ for (const Expr *Init : InitList->inits()) {
+ const Record::Field *FieldToInit = R->getField(InitIndex);
+
+ if (!this->emitDupPtr(Initializer))
+ return false;
+
+ if (std::optional<PrimType> T = classify(Init)) {
+ if (!this->visit(Init))
+ return false;
+
+ if (!this->emitInitField(*T, FieldToInit->Offset, Initializer))
+ return false;
+
+ if (!this->emitPopPtr(Initializer))
+ return false;
+ } else {
+ // Non-primitive case. Get a pointer to the field-to-initialize
+ // on the stack and recurse into visitInitializer().
+ if (!this->emitGetPtrField(FieldToInit->Offset, Init))
+ return false;
+
+ if (!this->visitInitializer(Init))
+ return false;
+
+ if (!this->emitPopPtr(Initializer))
+ return false;
+ }
+ ++InitIndex;
+ }
+
+ return true;
+ } else if (const CallExpr *CE = dyn_cast<CallExpr>(Initializer)) {
+ // RVO functions expect a pointer to initialize on the stack.
+ // Dup our existing pointer so it has its own copy to use.
+ if (!this->emitDupPtr(Initializer))
+ return false;
+
+ return this->VisitCallExpr(CE);
+ } else if (const auto *DIE = dyn_cast<CXXDefaultInitExpr>(Initializer)) {
+ return this->visitInitializer(DIE->getExpr());
}
- return this->bail(VD);
+
+ return false;
}
template <class Emitter>
-llvm::Optional<unsigned>
-ByteCodeExprGen<Emitter>::getGlobalIdx(const VarDecl *VD) {
- if (VD->isConstexpr()) {
- // Constexpr decl - it must have already been defined.
- return P.getGlobal(VD);
- }
- if (!VD->hasLocalStorage()) {
- // Not constexpr, but a global var - can have pointer taken.
- Program::DeclScope Scope(P, VD);
- return P.getOrCreateGlobal(VD);
- }
- return {};
+bool ByteCodeExprGen<Emitter>::visitInitializer(const Expr *Initializer) {
+ QualType InitializerType = Initializer->getType();
+
+ if (InitializerType->isArrayType())
+ return visitArrayInitializer(Initializer);
+
+ if (InitializerType->isRecordType())
+ return visitRecordInitializer(Initializer);
+
+ // Otherwise, visit the expression like normal.
+ return this->visit(Initializer);
}
template <class Emitter>
@@ -516,52 +1081,339 @@ Record *ByteCodeExprGen<Emitter>::getRecord(const RecordDecl *RD) {
}
template <class Emitter>
+const Function *ByteCodeExprGen<Emitter>::getFunction(const FunctionDecl *FD) {
+ assert(FD);
+ const Function *Func = P.getFunction(FD);
+ bool IsBeingCompiled = Func && !Func->isFullyCompiled();
+ bool WasNotDefined = Func && !Func->hasBody();
+
+ if (IsBeingCompiled)
+ return Func;
+
+ if (!Func || WasNotDefined) {
+ if (auto R = ByteCodeStmtGen<ByteCodeEmitter>(Ctx, P).compileFunc(FD))
+ Func = *R;
+ else {
+ llvm::consumeError(R.takeError());
+ return nullptr;
+ }
+ }
+
+ return Func;
+}
+
+template <class Emitter>
bool ByteCodeExprGen<Emitter>::visitExpr(const Expr *Exp) {
ExprScope<Emitter> RootScope(this);
if (!visit(Exp))
return false;
- if (Optional<PrimType> T = classify(Exp))
+ if (std::optional<PrimType> T = classify(Exp))
return this->emitRet(*T, Exp);
else
return this->emitRetValue(Exp);
}
+/// Toplevel visitDecl().
+/// We get here from evaluateAsInitializer().
+/// We need to evaluate the initializer and return its value.
template <class Emitter>
bool ByteCodeExprGen<Emitter>::visitDecl(const VarDecl *VD) {
+ std::optional<PrimType> VarT = classify(VD->getType());
+
+ // Create and initialize the variable.
+ if (!this->visitVarDecl(VD))
+ return false;
+
+ // Get a pointer to the variable
+ if (shouldBeGloballyIndexed(VD)) {
+ auto GlobalIndex = P.getGlobal(VD);
+ assert(GlobalIndex); // visitVarDecl() didn't return false.
+ if (!this->emitGetPtrGlobal(*GlobalIndex, VD))
+ return false;
+ } else {
+ auto Local = Locals.find(VD);
+ assert(Local != Locals.end()); // Same here.
+ if (!this->emitGetPtrLocal(Local->second.Offset, VD))
+ return false;
+ }
+
+ // Return the value
+ if (VarT) {
+ if (!this->emitLoadPop(*VarT, VD))
+ return false;
+
+ return this->emitRet(*VarT, VD);
+ }
+
+ return this->emitRetValue(VD);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::visitVarDecl(const VarDecl *VD) {
const Expr *Init = VD->getInit();
+ std::optional<PrimType> VarT = classify(VD->getType());
+
+ if (shouldBeGloballyIndexed(VD)) {
+ std::optional<unsigned> GlobalIndex = P.getOrCreateGlobal(VD, Init);
+
+ if (!GlobalIndex)
+ return this->bail(VD);
+
+ assert(Init);
+ {
+ DeclScope<Emitter> LocalScope(this, VD);
- if (Optional<unsigned> I = P.createGlobal(VD)) {
- if (Optional<PrimType> T = classify(VD->getType())) {
- {
- // Primitive declarations - compute the value and set it.
- DeclScope<Emitter> LocalScope(this, VD);
- if (!visit(Init))
+ if (VarT) {
+ if (!this->visit(Init))
return false;
+ return this->emitInitGlobal(*VarT, *GlobalIndex, VD);
}
+ return this->visitGlobalInitializer(Init, *GlobalIndex);
+ }
+ } else {
+ VariableScope<Emitter> LocalScope(this);
+ if (VarT) {
+ unsigned Offset = this->allocateLocalPrimitive(
+ VD, *VarT, VD->getType().isConstQualified());
+ if (Init) {
+ // Compile the initializer in its own scope.
+ ExprScope<Emitter> Scope(this);
+ if (!this->visit(Init))
+ return false;
- // If the declaration is global, save the value for later use.
- if (!this->emitDup(*T, VD))
- return false;
- if (!this->emitInitGlobal(*T, *I, VD))
- return false;
- return this->emitRet(*T, VD);
+ return this->emitSetLocal(*VarT, Offset, VD);
+ }
} else {
- {
- // Composite declarations - allocate storage and initialize it.
- DeclScope<Emitter> LocalScope(this, VD);
- if (!visitGlobalInitializer(Init, *I))
+ if (std::optional<unsigned> Offset = this->allocateLocal(VD)) {
+ if (Init)
+ return this->visitLocalInitializer(Init, *Offset);
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
+ assert(!E->getBuiltinCallee() && "Builtin functions aren't supported yet");
+
+ const Decl *Callee = E->getCalleeDecl();
+ if (const auto *FuncDecl = dyn_cast_or_null<FunctionDecl>(Callee)) {
+ const Function *Func = getFunction(FuncDecl);
+ if (!Func)
+ return false;
+ // If the function is being compiled right now, this is a recursive call.
+ // In that case, the function can't be valid yet, even though it will be
+ // later.
+ // If the function is already fully compiled but not constexpr, it was
+ // found to be faulty earlier on, so bail out.
+ if (Func->isFullyCompiled() && !Func->isConstexpr())
+ return false;
+
+ QualType ReturnType = E->getCallReturnType(Ctx.getASTContext());
+ std::optional<PrimType> T = classify(ReturnType);
+
+ if (Func->hasRVO() && DiscardResult) {
+ // If we need to discard the return value but the function returns its
+ // value via an RVO pointer, we need to create one such pointer just
+ // for this call.
+ if (std::optional<unsigned> LocalIndex = allocateLocal(E)) {
+ if (!this->emitGetPtrLocal(*LocalIndex, E))
return false;
}
+ }
- // Return a pointer to the global.
- if (!this->emitGetPtrGlobal(*I, VD))
+ // Put arguments on the stack.
+ for (const auto *Arg : E->arguments()) {
+ if (!this->visit(Arg))
return false;
- return this->emitRetValue(VD);
}
+
+ // In any case call the function. The return value will end up on the stack and
+ // if the function has RVO, we already have the pointer on the stack to write
+ // the result into.
+ if (!this->emitCall(Func, E))
+ return false;
+
+ if (DiscardResult && !ReturnType->isVoidType() && T)
+ return this->emitPop(*T, E);
+
+ return true;
+ } else {
+ assert(false && "We don't support non-FunctionDecl callees right now.");
}
- return this->bail(VD);
+ return false;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitCXXMemberCallExpr(
+ const CXXMemberCallExpr *E) {
+ // Get a This pointer on the stack.
+ if (!this->visit(E->getImplicitObjectArgument()))
+ return false;
+
+ return VisitCallExpr(E);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitCXXDefaultInitExpr(
+ const CXXDefaultInitExpr *E) {
+ return this->visit(E->getExpr());
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitCXXDefaultArgExpr(
+ const CXXDefaultArgExpr *E) {
+ return this->visit(E->getExpr());
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitCXXBoolLiteralExpr(
+ const CXXBoolLiteralExpr *E) {
+ if (DiscardResult)
+ return true;
+
+ return this->emitConstBool(E->getValue(), E);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitCXXNullPtrLiteralExpr(
+ const CXXNullPtrLiteralExpr *E) {
+ if (DiscardResult)
+ return true;
+
+ return this->emitNullPtr(E);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitCXXThisExpr(const CXXThisExpr *E) {
+ if (DiscardResult)
+ return true;
+ return this->emitThis(E);
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitUnaryOperator(const UnaryOperator *E) {
+ const Expr *SubExpr = E->getSubExpr();
+ std::optional<PrimType> T = classify(SubExpr->getType());
+
+ // TODO: Support pointers for inc/dec operators.
+ switch (E->getOpcode()) {
+ case UO_PostInc: { // x++
+ if (!this->visit(SubExpr))
+ return false;
+
+ return DiscardResult ? this->emitIncPop(*T, E) : this->emitInc(*T, E);
+ }
+ case UO_PostDec: { // x--
+ if (!this->visit(SubExpr))
+ return false;
+
+ return DiscardResult ? this->emitDecPop(*T, E) : this->emitDec(*T, E);
+ }
+ case UO_PreInc: { // ++x
+ if (!this->visit(SubExpr))
+ return false;
+
+ // Post-inc and pre-inc are the same if the value is to be discarded.
+ if (DiscardResult)
+ return this->emitIncPop(*T, E);
+
+ this->emitLoad(*T, E);
+ this->emitConst(1, E);
+ this->emitAdd(*T, E);
+ return this->emitStore(*T, E);
+ }
+ case UO_PreDec: { // --x
+ if (!this->visit(SubExpr))
+ return false;
+
+ // Post-dec and pre-dec are the same if the value is to be discarded.
+ if (DiscardResult)
+ return this->emitDecPop(*T, E);
+
+ this->emitLoad(*T, E);
+ this->emitConst(1, E);
+ this->emitSub(*T, E);
+ return this->emitStore(*T, E);
+ }
+ case UO_LNot: // !x
+ if (!this->visit(SubExpr))
+ return false;
+ // The Inv doesn't change anything, so skip it if we don't need the result.
+ return DiscardResult ? this->emitPop(*T, E) : this->emitInvBool(E);
+ case UO_Minus: // -x
+ if (!this->visit(SubExpr))
+ return false;
+ return DiscardResult ? this->emitPop(*T, E) : this->emitNeg(*T, E);
+ case UO_Plus: // +x
+ if (!this->visit(SubExpr)) // noop
+ return false;
+ return DiscardResult ? this->emitPop(*T, E) : true;
+ case UO_AddrOf: // &x
+ // We should already have a pointer when we get here.
+ if (!this->visit(SubExpr))
+ return false;
+ return DiscardResult ? this->emitPop(*T, E) : true;
+ case UO_Deref: // *x
+ return dereference(
+ SubExpr, DerefKind::Read,
+ [](PrimType) {
+ llvm_unreachable("Dereferencing requires a pointer");
+ return false;
+ },
+ [this, E](PrimType T) {
+ return DiscardResult ? this->emitPop(T, E) : true;
+ });
+ case UO_Not: // ~x
+ if (!this->visit(SubExpr))
+ return false;
+ return DiscardResult ? this->emitPop(*T, E) : this->emitComp(*T, E);
+ case UO_Real: // __real x
+ case UO_Imag: // __imag x
+ case UO_Extension:
+ case UO_Coawait:
+ assert(false && "Unhandled opcode");
+ }
+
+ return false;
+}
+
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitDeclRefExpr(const DeclRefExpr *E) {
+ const auto *Decl = E->getDecl();
+ // References are implemented via pointers, so when we see a DeclRefExpr
+ // pointing to a reference, we need to get its value directly (i.e. the
+ // pointer to the actual value) instead of a pointer to the pointer to the
+ // value.
+ bool IsReference = Decl->getType()->isReferenceType();
+
+ if (auto It = Locals.find(Decl); It != Locals.end()) {
+ const unsigned Offset = It->second.Offset;
+
+ if (IsReference)
+ return this->emitGetLocal(PT_Ptr, Offset, E);
+ return this->emitGetPtrLocal(Offset, E);
+ } else if (auto GlobalIndex = P.getGlobal(Decl)) {
+ if (IsReference)
+ return this->emitGetGlobal(PT_Ptr, *GlobalIndex, E);
+
+ return this->emitGetPtrGlobal(*GlobalIndex, E);
+ } else if (const auto *PVD = dyn_cast<ParmVarDecl>(Decl)) {
+ if (auto It = this->Params.find(PVD); It != this->Params.end()) {
+ if (IsReference)
+ return this->emitGetParam(PT_Ptr, It->second, E);
+ return this->emitGetPtrParam(It->second, E);
+ }
+ } else if (const auto *ECD = dyn_cast<EnumConstantDecl>(Decl)) {
+ return this->emitConst(ECD->getInitVal(), E);
+ }
+
+ return false;
}
template <class Emitter>
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.h b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.h
index 82aa413dabbc..c7fcc59e5a60 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -22,7 +22,6 @@
#include "clang/AST/Expr.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/TargetInfo.h"
-#include "llvm/ADT/Optional.h"
namespace clang {
class QualType;
@@ -34,25 +33,17 @@ template <class Emitter> class RecordScope;
template <class Emitter> class VariableScope;
template <class Emitter> class DeclScope;
template <class Emitter> class OptionScope;
+template <class Emitter> class ArrayIndexScope;
/// Compilation context for expressions.
template <class Emitter>
class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
public Emitter {
protected:
- // Emitters for opcodes of various arities.
- using NullaryFn = bool (ByteCodeExprGen::*)(const SourceInfo &);
- using UnaryFn = bool (ByteCodeExprGen::*)(PrimType, const SourceInfo &);
- using BinaryFn = bool (ByteCodeExprGen::*)(PrimType, PrimType,
- const SourceInfo &);
-
// Aliases for types defined in the emitter.
using LabelTy = typename Emitter::LabelTy;
using AddrTy = typename Emitter::AddrTy;
- // Reference to a function generating the pointer of an initialized object.s
- using InitFnRef = std::function<bool()>;
-
/// Current compilation context.
Context &Ctx;
/// Program to link to.
@@ -64,11 +55,34 @@ public:
ByteCodeExprGen(Context &Ctx, Program &P, Tys &&... Args)
: Emitter(Ctx, P, Args...), Ctx(Ctx), P(P) {}
- // Expression visitors - result returned on stack.
+ // Expression visitors - result returned on interp stack.
bool VisitCastExpr(const CastExpr *E);
bool VisitIntegerLiteral(const IntegerLiteral *E);
bool VisitParenExpr(const ParenExpr *E);
bool VisitBinaryOperator(const BinaryOperator *E);
+ bool VisitPointerArithBinOp(const BinaryOperator *E);
+ bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E);
+ bool VisitCallExpr(const CallExpr *E);
+ bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *E);
+ bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E);
+ bool VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E);
+ bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E);
+ bool VisitCXXThisExpr(const CXXThisExpr *E);
+ bool VisitUnaryOperator(const UnaryOperator *E);
+ bool VisitDeclRefExpr(const DeclRefExpr *E);
+ bool VisitImplicitValueInitExpr(const ImplicitValueInitExpr *E);
+ bool VisitSubstNonTypeTemplateParmExpr(const SubstNonTypeTemplateParmExpr *E);
+ bool VisitArraySubscriptExpr(const ArraySubscriptExpr *E);
+ bool VisitInitListExpr(const InitListExpr *E);
+ bool VisitConstantExpr(const ConstantExpr *E);
+ bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E);
+ bool VisitMemberExpr(const MemberExpr *E);
+ bool VisitArrayInitIndexExpr(const ArrayInitIndexExpr *E);
+ bool VisitOpaqueValueExpr(const OpaqueValueExpr *E);
+ bool VisitAbstractConditionalOperator(const AbstractConditionalOperator *E);
+ bool VisitStringLiteral(const StringLiteral *E);
+ bool VisitCharacterLiteral(const CharacterLiteral *E);
+ bool VisitCompoundAssignOperator(const CompoundAssignOperator *E);
protected:
bool visitExpr(const Expr *E) override;
@@ -85,31 +99,18 @@ protected:
Record *getRecord(QualType Ty);
Record *getRecord(const RecordDecl *RD);
- /// Returns the size int bits of an integer.
- unsigned getIntWidth(QualType Ty) {
- auto &ASTContext = Ctx.getASTContext();
- return ASTContext.getIntWidth(Ty);
- }
-
- /// Returns the value of CHAR_BIT.
- unsigned getCharBit() const {
- auto &ASTContext = Ctx.getASTContext();
- return ASTContext.getTargetInfo().getCharWidth();
- }
+ // Returns a function for the given FunctionDecl.
+ // If the function does not exist yet, it is compiled.
+ const Function *getFunction(const FunctionDecl *FD);
/// Classifies a type.
- llvm::Optional<PrimType> classify(const Expr *E) const {
+ std::optional<PrimType> classify(const Expr *E) const {
return E->isGLValue() ? PT_Ptr : classify(E->getType());
}
- llvm::Optional<PrimType> classify(QualType Ty) const {
+ std::optional<PrimType> classify(QualType Ty) const {
return Ctx.classify(Ty);
}
- /// Checks if a pointer needs adjustment.
- bool needsAdjust(QualType Ty) const {
- return true;
- }
-
/// Classifies a known primitive type
PrimType classifyPrim(QualType Ty) const {
if (auto T = classify(Ty)) {
@@ -122,29 +123,49 @@ protected:
bool discard(const Expr *E);
/// Evaluates an expression and places result on stack.
bool visit(const Expr *E);
- /// Compiles an initializer for a local.
- bool visitInitializer(const Expr *E, InitFnRef GenPtr);
+ /// Compiles an initializer.
+ bool visitInitializer(const Expr *E);
+ /// Compiles an array initializer.
+ bool visitArrayInitializer(const Expr *Initializer);
+ /// Compiles a record initializer.
+ bool visitRecordInitializer(const Expr *Initializer);
+ /// Creates and initializes a variable from the given decl.
+ bool visitVarDecl(const VarDecl *VD);
/// Visits an expression and converts it to a boolean.
bool visitBool(const Expr *E);
/// Visits an initializer for a local.
bool visitLocalInitializer(const Expr *Init, unsigned I) {
- return visitInitializer(Init, [this, I, Init] {
- return this->emitGetPtrLocal(I, Init);
- });
+ if (!this->emitGetPtrLocal(I, Init))
+ return false;
+
+ if (!visitInitializer(Init))
+ return false;
+
+ return this->emitPopPtr(Init);
}
/// Visits an initializer for a global.
bool visitGlobalInitializer(const Expr *Init, unsigned I) {
- return visitInitializer(Init, [this, I, Init] {
- return this->emitGetPtrGlobal(I, Init);
- });
+ if (!this->emitGetPtrGlobal(I, Init))
+ return false;
+
+ if (!visitInitializer(Init))
+ return false;
+
+ return this->emitPopPtr(Init);
}
/// Visits a delegated initializer.
bool visitThisInitializer(const Expr *I) {
- return visitInitializer(I, [this, I] { return this->emitThis(I); });
+ if (!this->emitThis(I))
+ return false;
+
+ if (!visitInitializer(I))
+ return false;
+
+ return this->emitPopPtr(I);
}
/// Creates a local primitive value.
@@ -152,8 +173,7 @@ protected:
bool IsExtended = false);
/// Allocates a space storing a local given its type.
- llvm::Optional<unsigned> allocateLocal(DeclTy &&Decl,
- bool IsExtended = false);
+ std::optional<unsigned> allocateLocal(DeclTy &&Decl, bool IsExtended = false);
private:
friend class VariableScope<Emitter>;
@@ -161,6 +181,7 @@ private:
friend class RecordScope<Emitter>;
friend class DeclScope<Emitter>;
friend class OptionScope<Emitter>;
+ friend class ArrayIndexScope<Emitter>;
/// Emits a zero initializer.
bool visitZeroInitializer(PrimType T, const Expr *E);
@@ -188,28 +209,28 @@ private:
DerefKind AK, llvm::function_ref<bool(PrimType)> Direct,
llvm::function_ref<bool(PrimType)> Indirect);
- /// Emits an APInt constant.
- bool emitConst(PrimType T, unsigned NumBits, const llvm::APInt &Value,
- const Expr *E);
+ /// Emits an APSInt constant.
+ bool emitConst(const APSInt &Value, const Expr *E);
+ bool emitConst(const APInt &Value, const Expr *E) {
+ return emitConst(static_cast<APSInt>(Value), E);
+ }
/// Emits an integer constant.
- template <typename T> bool emitConst(const Expr *E, T Value) {
- QualType Ty = E->getType();
- unsigned NumBits = getIntWidth(Ty);
- APInt WrappedValue(NumBits, Value, std::is_signed<T>::value);
- return emitConst(*Ctx.classify(Ty), NumBits, WrappedValue, E);
+ template <typename T> bool emitConst(T Value, const Expr *E);
+
+ /// Returns the CXXRecordDecl for the type of the given expression,
+ /// or nullptr if no such decl exists.
+ const CXXRecordDecl *getRecordDecl(const Expr *E) const {
+ QualType T = E->getType();
+ if (const auto *RD = T->getPointeeCXXRecordDecl())
+ return RD;
+ return T->getAsCXXRecordDecl();
}
- /// Returns a pointer to a variable declaration.
- bool getPtrVarDecl(const VarDecl *VD, const Expr *E);
-
- /// Returns the index of a global.
- llvm::Optional<unsigned> getGlobalIdx(const VarDecl *VD);
-
- /// Emits the initialized pointer.
- bool emitInitFn() {
- assert(InitFn && "missing initializer");
- return (*InitFn)();
+ /// Returns whether we should create a global variable for the
+ /// given VarDecl.
+ bool shouldBeGloballyIndexed(const VarDecl *VD) const {
+ return VD->hasGlobalStorage() || VD->isConstexpr();
}
protected:
@@ -222,14 +243,11 @@ protected:
/// Current scope.
VariableScope<Emitter> *VarScope = nullptr;
- /// Current argument index.
- llvm::Optional<uint64_t> ArrayIndex;
+ /// Current argument index. Needed to emit ArrayInitIndexExpr.
+ std::optional<uint64_t> ArrayIndex;
/// Flag indicating if return value is to be discarded.
bool DiscardResult = false;
-
- /// Expression being initialized.
- llvm::Optional<InitFnRef> InitFn = {};
};
extern template class ByteCodeExprGen<ByteCodeEmitter>;
@@ -238,6 +256,11 @@ extern template class ByteCodeExprGen<EvalEmitter>;
/// Scope chain managing the variable lifetimes.
template <class Emitter> class VariableScope {
public:
+ VariableScope(ByteCodeExprGen<Emitter> *Ctx)
+ : Ctx(Ctx), Parent(Ctx->VarScope) {
+ Ctx->VarScope = this;
+ }
+
virtual ~VariableScope() { Ctx->VarScope = this->Parent; }
void add(const Scope::Local &Local, bool IsExtended) {
@@ -262,11 +285,6 @@ public:
VariableScope *getParent() { return Parent; }
protected:
- VariableScope(ByteCodeExprGen<Emitter> *Ctx)
- : Ctx(Ctx), Parent(Ctx->VarScope) {
- Ctx->VarScope = this;
- }
-
/// ByteCodeExprGen instance.
ByteCodeExprGen<Emitter> *Ctx;
/// Link to the parent scope.
@@ -300,7 +318,7 @@ public:
protected:
/// Index of the scope in the chain.
- Optional<unsigned> Idx;
+ std::optional<unsigned> Idx;
};
/// Scope for storage declared in a compound statement.
@@ -320,10 +338,25 @@ public:
ExprScope(ByteCodeExprGen<Emitter> *Ctx) : LocalScope<Emitter>(Ctx) {}
void addExtended(const Scope::Local &Local) override {
+ assert(this->Parent);
this->Parent->addLocal(Local);
}
};
+template <class Emitter> class ArrayIndexScope final {
+public:
+ ArrayIndexScope(ByteCodeExprGen<Emitter> *Ctx, uint64_t Index) : Ctx(Ctx) {
+ OldArrayIndex = Ctx->ArrayIndex;
+ Ctx->ArrayIndex = Index;
+ }
+
+ ~ArrayIndexScope() { Ctx->ArrayIndex = OldArrayIndex; }
+
+private:
+ ByteCodeExprGen<Emitter> *Ctx;
+ std::optional<uint64_t> OldArrayIndex;
+};
+
} // namespace interp
} // namespace clang
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeGenError.h b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeGenError.h
index a4fa4917705d..af464b5ed4ab 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeGenError.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeGenError.h
@@ -20,19 +20,19 @@ namespace interp {
/// Error thrown by the compiler.
struct ByteCodeGenError : public llvm::ErrorInfo<ByteCodeGenError> {
public:
- ByteCodeGenError(SourceLocation Loc) : Loc(Loc) {}
- ByteCodeGenError(const Stmt *S) : ByteCodeGenError(S->getBeginLoc()) {}
- ByteCodeGenError(const Decl *D) : ByteCodeGenError(D->getBeginLoc()) {}
+ ByteCodeGenError(SourceRange Range) : Range(Range) {}
+ ByteCodeGenError(const Stmt *S) : ByteCodeGenError(S->getSourceRange()) {}
+ ByteCodeGenError(const Decl *D) : ByteCodeGenError(D->getSourceRange()) {}
void log(raw_ostream &OS) const override { OS << "unimplemented feature"; }
- const SourceLocation &getLoc() const { return Loc; }
+ const SourceRange &getRange() const { return Range; }
static char ID;
private:
- // Start of the item where the error occurred.
- SourceLocation Loc;
+ // Range of the item where the error occurred.
+ SourceRange Range;
// Users are not expected to use error_code.
std::error_code convertToErrorCode() const override {
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
index 90e84149b055..af97c57c98b7 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -94,11 +94,63 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
// Classify the return type.
ReturnType = this->classify(F->getReturnType());
- // Set up fields and context if a constructor.
- if (auto *MD = dyn_cast<CXXMethodDecl>(F))
- return this->bail(MD);
+ // Constructor. Set up field initializers.
+ if (const auto Ctor = dyn_cast<CXXConstructorDecl>(F)) {
+ const RecordDecl *RD = Ctor->getParent();
+ const Record *R = this->getRecord(RD);
+ if (!R)
+ return false;
+
+ for (const auto *Init : Ctor->inits()) {
+ const Expr *InitExpr = Init->getInit();
+ if (const FieldDecl *Member = Init->getMember()) {
+ const Record::Field *F = R->getField(Member);
+
+ if (std::optional<PrimType> T = this->classify(InitExpr)) {
+ if (!this->emitThis(InitExpr))
+ return false;
+
+ if (!this->visit(InitExpr))
+ return false;
+
+ if (!this->emitInitField(*T, F->Offset, InitExpr))
+ return false;
+
+ if (!this->emitPopPtr(InitExpr))
+ return false;
+ } else {
+ // Non-primitive case. Get a pointer to the field-to-initialize
+ // on the stack and call visitInitialzer() for it.
+ if (!this->emitThis(InitExpr))
+ return false;
+
+ if (!this->emitGetPtrField(F->Offset, InitExpr))
+ return false;
+
+ if (!this->visitInitializer(InitExpr))
+ return false;
+
+ if (!this->emitPopPtr(InitExpr))
+ return false;
+ }
+ } else if (const Type *Base = Init->getBaseClass()) {
+ // Base class initializer.
+ // Get This Base and call initializer on it.
+ auto *BaseDecl = Base->getAsCXXRecordDecl();
+ assert(BaseDecl);
+ const Record::Base *B = R->getBase(BaseDecl);
+ assert(B);
+ if (!this->emitGetPtrThisBase(B->Offset, InitExpr))
+ return false;
+ if (!this->visitInitializer(InitExpr))
+ return false;
+ if (!this->emitPopPtr(InitExpr))
+ return false;
+ }
+ }
+ }
- if (auto *Body = F->getBody())
+ if (const auto *Body = F->getBody())
if (!visitStmt(Body))
return false;
@@ -120,6 +172,16 @@ bool ByteCodeStmtGen<Emitter>::visitStmt(const Stmt *S) {
return visitReturnStmt(cast<ReturnStmt>(S));
case Stmt::IfStmtClass:
return visitIfStmt(cast<IfStmt>(S));
+ case Stmt::WhileStmtClass:
+ return visitWhileStmt(cast<WhileStmt>(S));
+ case Stmt::DoStmtClass:
+ return visitDoStmt(cast<DoStmt>(S));
+ case Stmt::ForStmtClass:
+ return visitForStmt(cast<ForStmt>(S));
+ case Stmt::BreakStmtClass:
+ return visitBreakStmt(cast<BreakStmt>(S));
+ case Stmt::ContinueStmtClass:
+ return visitContinueStmt(cast<ContinueStmt>(S));
case Stmt::NullStmtClass:
return true;
default: {
@@ -145,7 +207,7 @@ bool ByteCodeStmtGen<Emitter>::visitDeclStmt(const DeclStmt *DS) {
for (auto *D : DS->decls()) {
// Variable declarator.
if (auto *VD = dyn_cast<VarDecl>(D)) {
- if (!visitVarDecl(VD))
+ if (!this->visitVarDecl(VD))
return false;
continue;
}
@@ -171,18 +233,21 @@ bool ByteCodeStmtGen<Emitter>::visitReturnStmt(const ReturnStmt *RS) {
return this->emitRet(*ReturnType, RS);
} else {
// RVO - construct the value in the return location.
- auto ReturnLocation = [this, RE] { return this->emitGetParamPtr(0, RE); };
- if (!this->visitInitializer(RE, ReturnLocation))
+ if (!this->emitRVOPtr(RE))
return false;
+ if (!this->visitInitializer(RE))
+ return false;
+ if (!this->emitPopPtr(RE))
+ return false;
+
this->emitCleanup();
return this->emitRetVoid(RS);
}
- } else {
- this->emitCleanup();
- if (!this->emitRetVoid(RS))
- return false;
- return true;
}
+
+ // Void return.
+ this->emitCleanup();
+ return this->emitRetVoid(RS);
}
template <class Emitter>
@@ -231,33 +296,99 @@ bool ByteCodeStmtGen<Emitter>::visitIfStmt(const IfStmt *IS) {
}
template <class Emitter>
-bool ByteCodeStmtGen<Emitter>::visitVarDecl(const VarDecl *VD) {
- auto DT = VD->getType();
+bool ByteCodeStmtGen<Emitter>::visitWhileStmt(const WhileStmt *S) {
+ const Expr *Cond = S->getCond();
+ const Stmt *Body = S->getBody();
- if (!VD->hasLocalStorage()) {
- // No code generation required.
- return true;
- }
+ LabelTy CondLabel = this->getLabel(); // Label before the condition.
+ LabelTy EndLabel = this->getLabel(); // Label after the loop.
+ LoopScope<Emitter> LS(this, EndLabel, CondLabel);
- // Integers, pointers, primitives.
- if (Optional<PrimType> T = this->classify(DT)) {
- auto Off = this->allocateLocalPrimitive(VD, *T, DT.isConstQualified());
- // Compile the initialiser in its own scope.
- {
- ExprScope<Emitter> Scope(this);
- if (!this->visit(VD->getInit()))
- return false;
- }
- // Set the value.
- return this->emitSetLocal(*T, Off, VD);
- } else {
- // Composite types - allocate storage and initialize it.
- if (auto Off = this->allocateLocal(VD)) {
- return this->visitLocalInitializer(VD->getInit(), *Off);
- } else {
- return this->bail(VD);
- }
+ this->emitLabel(CondLabel);
+ if (!this->visitBool(Cond))
+ return false;
+ if (!this->jumpFalse(EndLabel))
+ return false;
+
+ if (!this->visitStmt(Body))
+ return false;
+ if (!this->jump(CondLabel))
+ return false;
+
+ this->emitLabel(EndLabel);
+
+ return true;
+}
+
+template <class Emitter>
+bool ByteCodeStmtGen<Emitter>::visitDoStmt(const DoStmt *S) {
+ const Expr *Cond = S->getCond();
+ const Stmt *Body = S->getBody();
+
+ LabelTy StartLabel = this->getLabel();
+ LabelTy EndLabel = this->getLabel();
+ LabelTy CondLabel = this->getLabel();
+ LoopScope<Emitter> LS(this, EndLabel, CondLabel);
+
+ this->emitLabel(StartLabel);
+ if (!this->visitStmt(Body))
+ return false;
+ this->emitLabel(CondLabel);
+ if (!this->visitBool(Cond))
+ return false;
+ if (!this->jumpTrue(StartLabel))
+ return false;
+ this->emitLabel(EndLabel);
+ return true;
+}
+
+template <class Emitter>
+bool ByteCodeStmtGen<Emitter>::visitForStmt(const ForStmt *S) {
+ // for (Init; Cond; Inc) { Body }
+ const Stmt *Init = S->getInit();
+ const Expr *Cond = S->getCond();
+ const Expr *Inc = S->getInc();
+ const Stmt *Body = S->getBody();
+
+ LabelTy EndLabel = this->getLabel();
+ LabelTy CondLabel = this->getLabel();
+ LabelTy IncLabel = this->getLabel();
+ LoopScope<Emitter> LS(this, EndLabel, IncLabel);
+
+ if (Init && !this->visitStmt(Init))
+ return false;
+ this->emitLabel(CondLabel);
+ if (Cond) {
+ if (!this->visitBool(Cond))
+ return false;
+ if (!this->jumpFalse(EndLabel))
+ return false;
}
+ if (Body && !this->visitStmt(Body))
+ return false;
+ this->emitLabel(IncLabel);
+ if (Inc && !this->discard(Inc))
+ return false;
+ if (!this->jump(CondLabel))
+ return false;
+ this->emitLabel(EndLabel);
+ return true;
+}
+
+template <class Emitter>
+bool ByteCodeStmtGen<Emitter>::visitBreakStmt(const BreakStmt *S) {
+ if (!BreakLabel)
+ return false;
+
+ return this->jump(*BreakLabel);
+}
+
+template <class Emitter>
+bool ByteCodeStmtGen<Emitter>::visitContinueStmt(const ContinueStmt *S) {
+ if (!ContinueLabel)
+ return false;
+
+ return this->jump(*ContinueLabel);
}
namespace clang {
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.h b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.h
index 3bc665b84b4d..829e199f827c 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/ByteCodeStmtGen.h
@@ -22,7 +22,6 @@
#include "clang/AST/Decl.h"
#include "clang/AST/Expr.h"
#include "clang/AST/StmtVisitor.h"
-#include "llvm/ADT/Optional.h"
namespace clang {
namespace interp {
@@ -33,10 +32,10 @@ template <class Emitter> class LabelScope;
/// Compilation context for statements.
template <class Emitter>
-class ByteCodeStmtGen : public ByteCodeExprGen<Emitter> {
+class ByteCodeStmtGen final : public ByteCodeExprGen<Emitter> {
using LabelTy = typename Emitter::LabelTy;
using AddrTy = typename Emitter::AddrTy;
- using OptLabelTy = llvm::Optional<LabelTy>;
+ using OptLabelTy = std::optional<LabelTy>;
using CaseMap = llvm::DenseMap<const SwitchCase *, LabelTy>;
public:
@@ -58,13 +57,14 @@ private:
bool visitDeclStmt(const DeclStmt *DS);
bool visitReturnStmt(const ReturnStmt *RS);
bool visitIfStmt(const IfStmt *IS);
+ bool visitWhileStmt(const WhileStmt *S);
+ bool visitDoStmt(const DoStmt *S);
+ bool visitForStmt(const ForStmt *S);
+ bool visitBreakStmt(const BreakStmt *S);
+ bool visitContinueStmt(const ContinueStmt *S);
- /// Compiles a variable declaration.
- bool visitVarDecl(const VarDecl *VD);
-
-private:
/// Type of the expression returned by the function.
- llvm::Optional<PrimType> ReturnType;
+ std::optional<PrimType> ReturnType;
/// Switch case mapping.
CaseMap CaseLabels;
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Context.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Context.cpp
index 3bfcdfcd4c58..16471242f328 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Context.cpp
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Context.cpp
@@ -27,39 +27,52 @@ Context::Context(ASTContext &Ctx) : Ctx(Ctx), P(new Program(*this)) {}
Context::~Context() {}
bool Context::isPotentialConstantExpr(State &Parent, const FunctionDecl *FD) {
+ assert(Stk.empty());
Function *Func = P->getFunction(FD);
- if (!Func) {
+ if (!Func || !Func->hasBody()) {
if (auto R = ByteCodeStmtGen<ByteCodeEmitter>(*this, *P).compileFunc(FD)) {
Func = *R;
} else {
handleAllErrors(R.takeError(), [&Parent](ByteCodeGenError &Err) {
- Parent.FFDiag(Err.getLoc(), diag::err_experimental_clang_interp_failed);
+ Parent.FFDiag(Err.getRange().getBegin(),
+ diag::err_experimental_clang_interp_failed)
+ << Err.getRange();
});
return false;
}
}
- if (!Func->isConstexpr())
- return false;
-
- APValue Dummy;
- return Run(Parent, Func, Dummy);
+ return Func->isConstexpr();
}
bool Context::evaluateAsRValue(State &Parent, const Expr *E, APValue &Result) {
+ assert(Stk.empty());
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);
- return Check(Parent, C.interpretExpr(E));
+ if (Check(Parent, C.interpretExpr(E))) {
+ assert(Stk.empty());
+ return true;
+ }
+
+ Stk.clear();
+ return false;
}
bool Context::evaluateAsInitializer(State &Parent, const VarDecl *VD,
APValue &Result) {
+ assert(Stk.empty());
ByteCodeExprGen<EvalEmitter> C(*this, *P, Parent, Stk, Result);
- return Check(Parent, C.interpretDecl(VD));
+ if (Check(Parent, C.interpretDecl(VD))) {
+ assert(Stk.empty());
+ return true;
+ }
+
+ Stk.clear();
+ return false;
}
const LangOptions &Context::getLangOpts() const { return Ctx.getLangOpts(); }
-llvm::Optional<PrimType> Context::classify(QualType T) {
+std::optional<PrimType> Context::classify(QualType T) const {
if (T->isReferenceType() || T->isPointerType()) {
return PT_Ptr;
}
@@ -112,7 +125,7 @@ unsigned Context::getCharBit() const {
bool Context::Run(State &Parent, Function *Func, APValue &Result) {
InterpState State(Parent, *P, Stk, *this);
- State.Current = new InterpFrame(State, Func, nullptr, {}, {});
+ State.Current = new InterpFrame(State, Func, /*Caller=*/nullptr, {});
if (Interpret(State, Result))
return true;
Stk.clear();
@@ -123,7 +136,9 @@ bool Context::Check(State &Parent, llvm::Expected<bool> &&Flag) {
if (Flag)
return *Flag;
handleAllErrors(Flag.takeError(), [&Parent](ByteCodeGenError &Err) {
- Parent.FFDiag(Err.getLoc(), diag::err_experimental_clang_interp_failed);
+ Parent.FFDiag(Err.getRange().getBegin(),
+ diag::err_experimental_clang_interp_failed)
+ << Err.getRange();
});
return false;
}
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Context.h b/contrib/llvm-project/clang/lib/AST/Interp/Context.h
index 0627d9fb14f5..e49422e64b87 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Context.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Context.h
@@ -18,7 +18,6 @@
#include "InterpStack.h"
#include "clang/AST/APValue.h"
-#include "llvm/ADT/PointerIntPair.h"
namespace clang {
class ASTContext;
@@ -33,7 +32,7 @@ class State;
enum PrimType : unsigned;
/// Holds all information required to evaluate constexpr code in a module.
-class Context {
+class Context final {
public:
/// Initialises the constexpr VM.
Context(ASTContext &Ctx);
@@ -60,7 +59,7 @@ public:
unsigned getCharBit() const;
/// Classifies an expression.
- llvm::Optional<PrimType> classify(QualType T);
+ std::optional<PrimType> classify(QualType T) const;
private:
/// Runs a function.
@@ -69,7 +68,6 @@ private:
/// Checks a result from the interpreter.
bool Check(State &Parent, llvm::Expected<bool> &&R);
-private:
/// Current compilation context.
ASTContext &Ctx;
/// Interpreter stack, shared across invocations.
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Descriptor.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Descriptor.cpp
index 5c1a8a9cf306..04bc8681dd6e 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Descriptor.cpp
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Descriptor.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "Descriptor.h"
+#include "Boolean.h"
#include "Pointer.h"
#include "PrimType.h"
#include "Record.h"
@@ -39,6 +40,11 @@ static void ctorArrayTy(Block *, char *Ptr, bool, bool, bool, Descriptor *D) {
template <typename T>
static void dtorArrayTy(Block *, char *Ptr, Descriptor *D) {
+ InitMap *IM = *reinterpret_cast<InitMap **>(Ptr);
+ if (IM != (InitMap *)-1)
+ free(IM);
+
+ Ptr += sizeof(InitMap *);
for (unsigned I = 0, NE = D->getNumElems(); I < NE; ++I) {
reinterpret_cast<T *>(Ptr)[I].~T();
}
@@ -72,9 +78,10 @@ static void ctorArrayDesc(Block *B, char *Ptr, bool IsConst, bool IsMutable,
Desc->IsBase = false;
Desc->IsActive = IsActive;
Desc->IsConst = IsConst || D->IsConst;
- Desc->IsMutable = IsMutable || D->IsMutable;
+ Desc->IsFieldMutable = IsMutable || D->IsMutable;
if (auto Fn = D->ElemDesc->CtorFn)
- Fn(B, ElemLoc, Desc->IsConst, Desc->IsMutable, IsActive, D->ElemDesc);
+ Fn(B, ElemLoc, Desc->IsConst, Desc->IsFieldMutable, IsActive,
+ D->ElemDesc);
}
}
@@ -121,13 +128,14 @@ static void ctorRecord(Block *B, char *Ptr, bool IsConst, bool IsMutable,
auto *Desc = reinterpret_cast<InlineDescriptor *>(Ptr + SubOff) - 1;
Desc->Offset = SubOff;
Desc->Desc = F;
- Desc->IsInitialized = (B->isStatic() || F->IsArray) && !IsBase;
+ Desc->IsInitialized = F->IsArray && !IsBase;
Desc->IsBase = IsBase;
Desc->IsActive = IsActive && !IsUnion;
Desc->IsConst = IsConst || F->IsConst;
- Desc->IsMutable = IsMutable || F->IsMutable;
+ Desc->IsFieldMutable = IsMutable || F->IsMutable;
if (auto Fn = F->CtorFn)
- Fn(B, Ptr + SubOff, Desc->IsConst, Desc->IsMutable, Desc->IsActive, F);
+ Fn(B, Ptr + SubOff, Desc->IsConst, Desc->IsFieldMutable, Desc->IsActive,
+ F);
};
for (const auto &B : D->ElemRecord->bases())
CtorSub(B.Offset, B.Desc, /*isBase=*/true);
@@ -178,26 +186,30 @@ static BlockCtorFn getCtorArrayPrim(PrimType Type) {
}
static BlockDtorFn getDtorArrayPrim(PrimType Type) {
- COMPOSITE_TYPE_SWITCH(Type, return dtorArrayTy<T>, return nullptr);
+ TYPE_SWITCH(Type, return dtorArrayTy<T>);
+ llvm_unreachable("unknown Expr");
}
static BlockMoveFn getMoveArrayPrim(PrimType Type) {
COMPOSITE_TYPE_SWITCH(Type, return moveArrayTy<T>, return nullptr);
}
-Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsConst,
- bool IsTemporary, bool IsMutable)
- : Source(D), ElemSize(primSize(Type)), Size(ElemSize), AllocSize(Size),
- IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
- CtorFn(getCtorPrim(Type)), DtorFn(getDtorPrim(Type)),
- MoveFn(getMovePrim(Type)) {
+Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
+ bool IsConst, bool IsTemporary, bool IsMutable)
+ : Source(D), ElemSize(primSize(Type)), Size(ElemSize),
+ MDSize(MD.value_or(0)), AllocSize(align(Size + MDSize)), IsConst(IsConst),
+ IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(getCtorPrim(Type)),
+ DtorFn(getDtorPrim(Type)), MoveFn(getMovePrim(Type)) {
+ assert(AllocSize >= Size);
assert(Source && "Missing source");
}
-Descriptor::Descriptor(const DeclTy &D, PrimType Type, size_t NumElems,
- bool IsConst, bool IsTemporary, bool IsMutable)
+Descriptor::Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD,
+ size_t NumElems, bool IsConst, bool IsTemporary,
+ bool IsMutable)
: Source(D), ElemSize(primSize(Type)), Size(ElemSize * NumElems),
- AllocSize(align(Size) + sizeof(InitMap *)), IsConst(IsConst),
+ MDSize(MD.value_or(0)),
+ AllocSize(align(Size) + sizeof(InitMap *) + MDSize), IsConst(IsConst),
IsMutable(IsMutable), IsTemporary(IsTemporary), IsArray(true),
CtorFn(getCtorArrayPrim(Type)), DtorFn(getDtorArrayPrim(Type)),
MoveFn(getMoveArrayPrim(Type)) {
@@ -206,39 +218,42 @@ Descriptor::Descriptor(const DeclTy &D, PrimType Type, size_t NumElems,
Descriptor::Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary,
UnknownSize)
- : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark),
+ : Source(D), ElemSize(primSize(Type)), Size(UnknownSizeMark), MDSize(0),
AllocSize(alignof(void *)), IsConst(true), IsMutable(false),
IsTemporary(IsTemporary), IsArray(true), CtorFn(getCtorArrayPrim(Type)),
DtorFn(getDtorArrayPrim(Type)), MoveFn(getMoveArrayPrim(Type)) {
assert(Source && "Missing source");
}
-Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems,
- bool IsConst, bool IsTemporary, bool IsMutable)
+Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD,
+ unsigned NumElems, bool IsConst, bool IsTemporary,
+ bool IsMutable)
: Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
- Size(ElemSize * NumElems),
- AllocSize(std::max<size_t>(alignof(void *), Size)), ElemDesc(Elem),
- IsConst(IsConst), IsMutable(IsMutable), IsTemporary(IsTemporary),
- IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc),
- MoveFn(moveArrayDesc) {
+ Size(ElemSize * NumElems), MDSize(MD.value_or(0)),
+ AllocSize(std::max<size_t>(alignof(void *), Size) + MDSize),
+ ElemDesc(Elem), IsConst(IsConst), IsMutable(IsMutable),
+ IsTemporary(IsTemporary), IsArray(true), CtorFn(ctorArrayDesc),
+ DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
assert(Source && "Missing source");
}
Descriptor::Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary,
UnknownSize)
: Source(D), ElemSize(Elem->getAllocSize() + sizeof(InlineDescriptor)),
- Size(UnknownSizeMark), AllocSize(alignof(void *)), ElemDesc(Elem),
- IsConst(true), IsMutable(false), IsTemporary(IsTemporary), IsArray(true),
- CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc), MoveFn(moveArrayDesc) {
+ Size(UnknownSizeMark), MDSize(0), AllocSize(alignof(void *)),
+ ElemDesc(Elem), IsConst(true), IsMutable(false), IsTemporary(IsTemporary),
+ IsArray(true), CtorFn(ctorArrayDesc), DtorFn(dtorArrayDesc),
+ MoveFn(moveArrayDesc) {
assert(Source && "Missing source");
}
-Descriptor::Descriptor(const DeclTy &D, Record *R, bool IsConst,
- bool IsTemporary, bool IsMutable)
+Descriptor::Descriptor(const DeclTy &D, Record *R, MetadataSize MD,
+ bool IsConst, bool IsTemporary, bool IsMutable)
: Source(D), ElemSize(std::max<size_t>(alignof(void *), R->getFullSize())),
- Size(ElemSize), AllocSize(Size), ElemRecord(R), IsConst(IsConst),
- IsMutable(IsMutable), IsTemporary(IsTemporary), CtorFn(ctorRecord),
- DtorFn(dtorRecord), MoveFn(moveRecord) {
+ Size(ElemSize), MDSize(MD.value_or(0)), AllocSize(Size + MDSize),
+ ElemRecord(R), IsConst(IsConst), IsMutable(IsMutable),
+ IsTemporary(IsTemporary), CtorFn(ctorRecord), DtorFn(dtorRecord),
+ MoveFn(moveRecord) {
assert(Source && "Missing source");
}
@@ -259,9 +274,7 @@ SourceLocation Descriptor::getLocation() const {
}
InitMap::InitMap(unsigned N) : UninitFields(N) {
- for (unsigned I = 0; I < N / PER_FIELD; ++I) {
- data()[I] = 0;
- }
+ std::fill_n(data(), (N + PER_FIELD - 1) / PER_FIELD, 0);
}
InitMap::T *InitMap::data() {
@@ -269,9 +282,14 @@ InitMap::T *InitMap::data() {
return reinterpret_cast<T *>(Start);
}
+const InitMap::T *InitMap::data() const {
+ auto *Start = reinterpret_cast<const char *>(this) + align(sizeof(InitMap));
+ return reinterpret_cast<const T *>(Start);
+}
+
bool InitMap::initialize(unsigned I) {
unsigned Bucket = I / PER_FIELD;
- unsigned Mask = 1ull << static_cast<uint64_t>(I % PER_FIELD);
+ T Mask = T(1) << (I % PER_FIELD);
if (!(data()[Bucket] & Mask)) {
data()[Bucket] |= Mask;
UninitFields -= 1;
@@ -279,10 +297,9 @@ bool InitMap::initialize(unsigned I) {
return UninitFields == 0;
}
-bool InitMap::isInitialized(unsigned I) {
+bool InitMap::isInitialized(unsigned I) const {
unsigned Bucket = I / PER_FIELD;
- unsigned Mask = 1ull << static_cast<uint64_t>(I % PER_FIELD);
- return data()[Bucket] & Mask;
+ return data()[Bucket] & (T(1) << (I % PER_FIELD));
}
InitMap *InitMap::allocate(unsigned N) {
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Descriptor.h b/contrib/llvm-project/clang/lib/AST/Interp/Descriptor.h
index 11072cab3e90..6ef4fc2f4c9b 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Descriptor.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Descriptor.h
@@ -47,8 +47,36 @@ using BlockMoveFn = void (*)(Block *Storage, char *SrcFieldPtr,
/// Object size as used by the interpreter.
using InterpSize = unsigned;
+/// Inline descriptor embedded in structures and arrays.
+///
+/// Such descriptors precede all composite array elements and structure fields.
+/// If the base of a pointer is not zero, the base points to the end of this
+/// structure. The offset field is used to traverse the pointer chain up
+/// to the root structure which allocated the object.
+struct InlineDescriptor {
+ /// Offset inside the structure/array.
+ unsigned Offset;
+
+ /// Flag indicating if the storage is constant or not.
+ /// Relevant for primitive fields.
+ unsigned IsConst : 1;
+ /// For primitive fields, it indicates if the field was initialized.
+ /// Primitive fields in static storage are always initialized.
+ /// Arrays are always initialized, even though their elements might not be.
+ /// Base classes are initialized after the constructor is invoked.
+ unsigned IsInitialized : 1;
+ /// Flag indicating if the field is an embedded base class.
+ unsigned IsBase : 1;
+ /// Flag indicating if the field is the active member of a union.
+ unsigned IsActive : 1;
+ /// Flag indicating if the field is mutable (if in a record).
+ unsigned IsFieldMutable : 1;
+
+ Descriptor *Desc;
+};
+
/// Describes a memory block created by an allocation site.
-struct Descriptor {
+struct Descriptor final {
private:
/// Original declaration, used to emit the error message.
const DeclTy Source;
@@ -56,6 +84,8 @@ private:
const InterpSize ElemSize;
/// Size of the storage, in host bytes.
const InterpSize Size;
+ // Size of the metadata.
+ const InterpSize MDSize;
/// Size of the allocation (storage + metadata), in host bytes.
const InterpSize AllocSize;
@@ -66,6 +96,9 @@ public:
/// Token to denote structures of unknown size.
struct UnknownSize {};
+ using MetadataSize = std::optional<InterpSize>;
+ static constexpr MetadataSize InlineDescMD = sizeof(InlineDescriptor);
+
/// Pointer to the record, if block contains records.
Record *const ElemRecord = nullptr;
/// Descriptor of the array element.
@@ -85,26 +118,26 @@ public:
const BlockMoveFn MoveFn = nullptr;
/// Allocates a descriptor for a primitive.
- Descriptor(const DeclTy &D, PrimType Type, bool IsConst, bool IsTemporary,
- bool IsMutable);
+ Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, bool IsConst,
+ bool IsTemporary, bool IsMutable);
/// Allocates a descriptor for an array of primitives.
- Descriptor(const DeclTy &D, PrimType Type, size_t NumElems, bool IsConst,
- bool IsTemporary, bool IsMutable);
+ Descriptor(const DeclTy &D, PrimType Type, MetadataSize MD, size_t NumElems,
+ bool IsConst, bool IsTemporary, bool IsMutable);
/// Allocates a descriptor for an array of primitives of unknown size.
Descriptor(const DeclTy &D, PrimType Type, bool IsTemporary, UnknownSize);
/// Allocates a descriptor for an array of composites.
- Descriptor(const DeclTy &D, Descriptor *Elem, unsigned NumElems, bool IsConst,
- bool IsTemporary, bool IsMutable);
+ Descriptor(const DeclTy &D, Descriptor *Elem, MetadataSize MD,
+ unsigned NumElems, bool IsConst, bool IsTemporary, bool IsMutable);
/// Allocates a descriptor for an array of composites of unknown size.
Descriptor(const DeclTy &D, Descriptor *Elem, bool IsTemporary, UnknownSize);
/// Allocates a descriptor for a record.
- Descriptor(const DeclTy &D, Record *R, bool IsConst, bool IsTemporary,
- bool IsMutable);
+ Descriptor(const DeclTy &D, Record *R, MetadataSize MD, bool IsConst,
+ bool IsTemporary, bool IsMutable);
QualType getType() const;
SourceLocation getLocation() const;
@@ -113,15 +146,15 @@ public:
const Expr *asExpr() const { return Source.dyn_cast<const Expr *>(); }
const ValueDecl *asValueDecl() const {
- return dyn_cast_or_null<ValueDecl>(asDecl());
+ return dyn_cast_if_present<ValueDecl>(asDecl());
}
const FieldDecl *asFieldDecl() const {
- return dyn_cast_or_null<FieldDecl>(asDecl());
+ return dyn_cast_if_present<FieldDecl>(asDecl());
}
const RecordDecl *asRecordDecl() const {
- return dyn_cast_or_null<RecordDecl>(asDecl());
+ return dyn_cast_if_present<RecordDecl>(asDecl());
}
/// Returns the size of the object without metadata.
@@ -134,6 +167,8 @@ public:
unsigned getAllocSize() const { return AllocSize; }
/// returns the size of an element when the structure is viewed as an array.
unsigned getElemSize() const { return ElemSize; }
+ /// Returns the size of the metadata.
+ unsigned getMetadataSize() const { return MDSize; }
/// Returns the number of elements stored in the block.
unsigned getNumElems() const {
@@ -154,39 +189,11 @@ public:
bool isArray() const { return IsArray; }
};
-/// Inline descriptor embedded in structures and arrays.
-///
-/// Such descriptors precede all composite array elements and structure fields.
-/// If the base of a pointer is not zero, the base points to the end of this
-/// structure. The offset field is used to traverse the pointer chain up
-/// to the root structure which allocated the object.
-struct InlineDescriptor {
- /// Offset inside the structure/array.
- unsigned Offset;
-
- /// Flag indicating if the storage is constant or not.
- /// Relevant for primitive fields.
- unsigned IsConst : 1;
- /// For primitive fields, it indicates if the field was initialized.
- /// Primitive fields in static storage are always initialized.
- /// Arrays are always initialized, even though their elements might not be.
- /// Base classes are initialized after the constructor is invoked.
- unsigned IsInitialized : 1;
- /// Flag indicating if the field is an embedded base class.
- unsigned IsBase : 1;
- /// Flag indicating if the field is the active member of a union.
- unsigned IsActive : 1;
- /// Flag indicating if the field is mutable (if in a record).
- unsigned IsMutable : 1;
-
- Descriptor *Desc;
-};
-
/// Bitfield tracking the initialisation status of elements of primitive arrays.
/// A pointer to this is embedded at the end of all primitive arrays.
/// If the map was not yet created and nothing was initialized, the pointer to
/// this structure is 0. If the object was fully initialized, the pointer is -1.
-struct InitMap {
+struct InitMap final {
private:
/// Type packing bits.
using T = uint64_t;
@@ -198,13 +205,14 @@ private:
/// Returns a pointer to storage.
T *data();
+ const T *data() const;
public:
/// Initializes an element. Returns true when object if fully initialized.
bool initialize(unsigned I);
/// Checks if an element was initialized.
- bool isInitialized(unsigned I);
+ bool isInitialized(unsigned I) const;
/// Allocates a map holding N elements.
static InitMap *allocate(unsigned N);
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Disasm.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Disasm.cpp
index 36adbe296b0c..d31e879d516f 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Disasm.cpp
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Disasm.cpp
@@ -21,17 +21,13 @@
using namespace clang;
using namespace clang::interp;
-template <typename T>
-inline std::enable_if_t<!std::is_pointer<T>::value, T> ReadArg(Program &P,
- CodePtr OpPC) {
- return OpPC.read<T>();
-}
-
-template <typename T>
-inline std::enable_if_t<std::is_pointer<T>::value, T> ReadArg(Program &P,
- CodePtr OpPC) {
- uint32_t ID = OpPC.read<uint32_t>();
- return reinterpret_cast<T>(P.getNativePointer(ID));
+template <typename T> inline T ReadArg(Program &P, CodePtr &OpPC) {
+ if constexpr (std::is_pointer_v<T>) {
+ uint32_t ID = OpPC.read<uint32_t>();
+ return reinterpret_cast<T>(P.getNativePointer(ID));
+ } else {
+ return OpPC.read<T>();
+ }
}
LLVM_DUMP_METHOD void Function::dump() const { dump(llvm::errs()); }
@@ -40,10 +36,11 @@ LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const {
if (F) {
if (auto *Cons = dyn_cast<CXXConstructorDecl>(F)) {
DeclarationName Name = Cons->getParent()->getDeclName();
- OS << Name << "::" << Name << ":\n";
+ OS << Name << "::" << Name;
} else {
- OS << F->getDeclName() << ":\n";
+ OS << F->getDeclName();
}
+ OS << " " << (const void*)this << ":\n";
} else {
OS << "<<expr>>\n";
}
@@ -51,6 +48,7 @@ LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const {
OS << "frame size: " << getFrameSize() << "\n";
OS << "arg size: " << getArgSize() << "\n";
OS << "rvo: " << hasRVO() << "\n";
+ OS << "this arg: " << hasThisPointer() << "\n";
auto PrintName = [&OS](const char *Name) {
OS << Name;
@@ -74,6 +72,10 @@ LLVM_DUMP_METHOD void Function::dump(llvm::raw_ostream &OS) const {
LLVM_DUMP_METHOD void Program::dump() const { dump(llvm::errs()); }
LLVM_DUMP_METHOD void Program::dump(llvm::raw_ostream &OS) const {
+ OS << ":: Program\n";
+ OS << "Global Variables: " << Globals.size() << "\n";
+ OS << "Functions: " << Funcs.size() << "\n";
+ OS << "\n";
for (auto &Func : Funcs) {
Func.second->dump();
}
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.cpp b/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.cpp
index 22e8695b9211..72fd3b45254b 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.cpp
+++ b/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.cpp
@@ -23,7 +23,8 @@ EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent,
InterpStack &Stk, APValue &Result)
: Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), Result(Result) {
// Create a dummy frame for the interpreter which does not have locals.
- S.Current = new InterpFrame(S, nullptr, nullptr, CodePtr(), Pointer());
+ S.Current =
+ new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr());
}
llvm::Expected<bool> EvalEmitter::interpretExpr(const Expr *E) {
@@ -54,6 +55,16 @@ Scope::Local EvalEmitter::createLocal(Descriptor *D) {
auto *B = new (Memory.get()) Block(D, /*isStatic=*/false);
B->invokeCtor();
+ // Initialize local variable inline descriptor.
+ InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
+ Desc.Desc = D;
+ Desc.Offset = sizeof(InlineDescriptor);
+ Desc.IsActive = true;
+ Desc.IsBase = false;
+ Desc.IsFieldMutable = false;
+ Desc.IsConst = false;
+ Desc.IsInitialized = false;
+
// Register the local.
unsigned Off = Locals.size();
Locals.insert({Off, std::move(Memory)});
@@ -123,7 +134,7 @@ bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
const Pointer &FP = Ptr.atField(F.Offset);
QualType FieldTy = F.Decl->getType();
if (FP.isActive()) {
- if (llvm::Optional<PrimType> T = Ctx.classify(FieldTy)) {
+ if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
TYPE_SWITCH(*T, Ok &= ReturnValue<T>(FP.deref<T>(), Value));
} else {
Ok &= Composite(FieldTy, FP, Value);
@@ -145,7 +156,7 @@ bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
const Pointer &FP = Ptr.atField(FD->Offset);
APValue &Value = R.getStructField(I);
- if (llvm::Optional<PrimType> T = Ctx.classify(FieldTy)) {
+ if (std::optional<PrimType> T = Ctx.classify(FieldTy)) {
TYPE_SWITCH(*T, Ok &= ReturnValue<T>(FP.deref<T>(), Value));
} else {
Ok &= Composite(FieldTy, FP, Value);
@@ -177,7 +188,7 @@ bool EvalEmitter::emitRetValue(const SourceInfo &Info) {
for (unsigned I = 0; I < NumElems; ++I) {
APValue &Slot = R.getArrayInitializedElt(I);
const Pointer &EP = Ptr.atIndex(I);
- if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) {
+ if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
TYPE_SWITCH(*T, Ok &= ReturnValue<T>(EP.deref<T>(), Slot));
} else {
Ok &= Composite(ElemTy, EP.narrow(), Slot);
@@ -199,7 +210,8 @@ bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) {
auto It = Locals.find(I);
assert(It != Locals.end() && "Missing local variable");
- S.Stk.push<Pointer>(reinterpret_cast<Block *>(It->second.get()));
+ Block *B = reinterpret_cast<Block *>(It->second.get());
+ S.Stk.push<Pointer>(B, sizeof(InlineDescriptor));
return true;
}
@@ -213,7 +225,7 @@ bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) {
auto It = Locals.find(I);
assert(It != Locals.end() && "Missing local variable");
auto *B = reinterpret_cast<Block *>(It->second.get());
- S.Stk.push<T>(*reinterpret_cast<T *>(B + 1));
+ S.Stk.push<T>(*reinterpret_cast<T *>(B->data()));
return true;
}
@@ -227,7 +239,10 @@ bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) {
auto It = Locals.find(I);
assert(It != Locals.end() && "Missing local variable");
auto *B = reinterpret_cast<Block *>(It->second.get());
- *reinterpret_cast<T *>(B + 1) = S.Stk.pop<T>();
+ *reinterpret_cast<T *>(B->data()) = S.Stk.pop<T>();
+ InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData());
+ Desc.IsInitialized = true;
+
return true;
}
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.h b/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.h
index eec2ff8ee753..6b6d0d621901 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/EvalEmitter.h
@@ -23,7 +23,6 @@
#include "llvm/Support/Error.h"
namespace clang {
-class FunctionDecl;
namespace interp {
class Context;
class Function;
@@ -71,7 +70,7 @@ protected:
Local createLocal(Descriptor *D);
/// Returns the source location of the current opcode.
- SourceInfo getSource(Function *F, CodePtr PC) const override {
+ SourceInfo getSource(const Function *F, CodePtr PC) const override {
return F ? F->getSource(PC) : CurrentSource;
}
@@ -97,7 +96,7 @@ private:
// value which is mapped to the location of the opcode being evaluated.
CodePtr OpPC;
/// Location of a failure.
- llvm::Optional<SourceLocation> BailLocation;
+ std::optional<SourceLocation> BailLocation;
/// Location of the current instruction.
SourceInfo CurrentSource;
@@ -110,12 +109,7 @@ private:
/// Since expressions can only jump forward, predicated execution is
/// used to deal with if-else statements.
- bool isActive() { return CurrentLabel == ActiveLabel; }
-
- /// Helper to invoke a method.
- bool ExecuteCall(Function *F, Pointer &&This, const SourceInfo &Info);
- /// Helper to emit a diagnostic on a missing method.
- bool ExecuteNoCall(const FunctionDecl *F, const SourceInfo &Info);
+ bool isActive() const { return CurrentLabel == ActiveLabel; }
protected:
#define GET_EVAL_PROTO
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Function.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Function.cpp
index 6ba97df1cd30..40001faad411 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Function.cpp
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Function.cpp
@@ -17,13 +17,11 @@ using namespace clang::interp;
Function::Function(Program &P, const FunctionDecl *F, unsigned ArgSize,
llvm::SmallVector<PrimType, 8> &&ParamTypes,
- llvm::DenseMap<unsigned, ParamDescriptor> &&Params)
+ llvm::DenseMap<unsigned, ParamDescriptor> &&Params,
+ bool HasThisPointer, bool HasRVO)
: P(P), Loc(F->getBeginLoc()), F(F), ArgSize(ArgSize),
- ParamTypes(std::move(ParamTypes)), Params(std::move(Params)) {}
-
-CodePtr Function::getCodeBegin() const { return Code.data(); }
-
-CodePtr Function::getCodeEnd() const { return Code.data() + Code.size(); }
+ ParamTypes(std::move(ParamTypes)), Params(std::move(Params)),
+ HasThisPointer(HasThisPointer), HasRVO(HasRVO) {}
Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const {
auto It = Params.find(Offset);
@@ -32,11 +30,12 @@ Function::ParamDescriptor Function::getParamDescriptor(unsigned Offset) const {
}
SourceInfo Function::getSource(CodePtr PC) const {
+ assert(PC >= getCodeBegin() && "PC does not belong to this function");
+ assert(PC <= getCodeEnd() && "PC Does not belong to this function");
unsigned Offset = PC - getCodeBegin();
using Elem = std::pair<unsigned, SourceInfo>;
auto It = llvm::lower_bound(SrcMap, Elem{Offset, {}}, llvm::less_first());
- if (It == SrcMap.end() || It->first != Offset)
- llvm::report_fatal_error("missing source location");
+ assert(It != SrcMap.end());
return It->second;
}
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Function.h b/contrib/llvm-project/clang/lib/AST/Interp/Function.h
index ac1dffea1160..5b2a77f1a12d 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Function.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Function.h
@@ -29,7 +29,7 @@ enum PrimType : uint32_t;
/// Describes a scope block.
///
/// The block gathers all the descriptors of the locals defined in this block.
-class Scope {
+class Scope final {
public:
/// Information about a local's storage.
struct Local {
@@ -43,7 +43,7 @@ public:
Scope(LocalVectorTy &&Descriptors) : Descriptors(std::move(Descriptors)) {}
- llvm::iterator_range<LocalVectorTy::iterator> locals() {
+ llvm::iterator_range<LocalVectorTy::const_iterator> locals() const {
return llvm::make_range(Descriptors.begin(), Descriptors.end());
}
@@ -56,23 +56,42 @@ private:
///
/// Contains links to the bytecode of the function, as well as metadata
/// describing all arguments and stack-local variables.
-class Function {
+///
+/// # Calling Convention
+///
+/// When calling a function, all argument values must be on the stack.
+///
+/// If the function has a This pointer (i.e. hasThisPointer() returns true,
+/// the argument values need to be preceeded by a Pointer for the This object.
+///
+/// If the function uses Return Value Optimization, the arguments (and
+/// potentially the This pointer) need to be proceeded by a Pointer pointing
+/// to the location to construct the returned value.
+///
+/// After the function has been called, it will remove all arguments,
+/// including RVO and This pointer, from the stack.
+///
+class Function final {
public:
using ParamDescriptor = std::pair<PrimType, Descriptor *>;
/// Returns the size of the function's local stack.
unsigned getFrameSize() const { return FrameSize; }
- /// Returns the size of the argument stackx
+ /// Returns the size of the argument stack.
unsigned getArgSize() const { return ArgSize; }
/// Returns a pointer to the start of the code.
- CodePtr getCodeBegin() const;
+ CodePtr getCodeBegin() const { return Code.data(); }
/// Returns a pointer to the end of the code.
- CodePtr getCodeEnd() const;
+ CodePtr getCodeEnd() const { return Code.data() + Code.size(); }
/// Returns the original FunctionDecl.
const FunctionDecl *getDecl() const { return F; }
+ /// Returns the name of the function decl this code
+ /// was generated for.
+ const std::string getName() const { return F->getNameInfo().getAsString(); }
+
/// Returns the location.
SourceLocation getLoc() const { return Loc; }
@@ -80,21 +99,24 @@ public:
ParamDescriptor getParamDescriptor(unsigned Offset) const;
/// Checks if the first argument is a RVO pointer.
- bool hasRVO() const { return ParamTypes.size() != Params.size(); }
+ bool hasRVO() const { return HasRVO; }
/// Range over the scope blocks.
- llvm::iterator_range<llvm::SmallVector<Scope, 2>::iterator> scopes() {
+ llvm::iterator_range<llvm::SmallVector<Scope, 2>::const_iterator>
+ scopes() const {
return llvm::make_range(Scopes.begin(), Scopes.end());
}
/// Range over argument types.
- using arg_reverse_iterator = SmallVectorImpl<PrimType>::reverse_iterator;
- llvm::iterator_range<arg_reverse_iterator> args_reverse() {
- return llvm::make_range(ParamTypes.rbegin(), ParamTypes.rend());
+ using arg_reverse_iterator =
+ SmallVectorImpl<PrimType>::const_reverse_iterator;
+ llvm::iterator_range<arg_reverse_iterator> args_reverse() const {
+ return llvm::reverse(ParamTypes);
}
/// Returns a specific scope.
Scope &getScope(unsigned Idx) { return Scopes[Idx]; }
+ const Scope &getScope(unsigned Idx) const { return Scopes[Idx]; }
/// Returns the source information at a given PC.
SourceInfo getSource(CodePtr PC) const;
@@ -108,11 +130,22 @@ public:
/// Checks if the function is a constructor.
bool isConstructor() const { return isa<CXXConstructorDecl>(F); }
+ /// Checks if the function is fully done compiling.
+ bool isFullyCompiled() const { return IsFullyCompiled; }
+
+ bool hasThisPointer() const { return HasThisPointer; }
+
+ // Checks if the funtion already has a body attached.
+ bool hasBody() const { return HasBody; }
+
+ unsigned getNumParams() const { return ParamTypes.size(); }
+
private:
/// Construct a function representing an actual function.
Function(Program &P, const FunctionDecl *F, unsigned ArgSize,
llvm::SmallVector<PrimType, 8> &&ParamTypes,
- llvm::DenseMap<unsigned, ParamDescriptor> &&Params);
+ llvm::DenseMap<unsigned, ParamDescriptor> &&Params,
+ bool HasThisPointer, bool HasRVO);
/// Sets the code of a function.
void setCode(unsigned NewFrameSize, std::vector<char> &&NewCode, SourceMap &&NewSrcMap,
@@ -122,8 +155,11 @@ private:
SrcMap = std::move(NewSrcMap);
Scopes = std::move(NewScopes);
IsValid = true;
+ HasBody = true;
}
+ void setIsFullyCompiled(bool FC) { IsFullyCompiled = FC; }
+
private:
friend class Program;
friend class ByteCodeEmitter;
@@ -135,7 +171,7 @@ private:
/// Declaration this function was compiled from.
const FunctionDecl *F;
/// Local area size: storage + metadata.
- unsigned FrameSize;
+ unsigned FrameSize = 0;
/// Size of the argument stack.
unsigned ArgSize;
/// Program code.
@@ -150,6 +186,18 @@ private:
llvm::DenseMap<unsigned, ParamDescriptor> Params;
/// Flag to indicate if the function is valid.
bool IsValid = false;
+ /// Flag to indicate if the function is done being
+ /// compiled to bytecode.
+ bool IsFullyCompiled = false;
+ /// Flag indicating if this function takes the this pointer
+ /// as the first implicit argument
+ bool HasThisPointer = false;
+ /// Whether this function has Return Value Optimization, i.e.
+ /// the return value is constructed in the caller's stack frame.
+ /// This is done for functions that return non-primive values.
+ bool HasRVO = false;
+ /// If we've already compiled the function's body.
+ bool HasBody = false;
public:
/// Dumps the disassembled bytecode to \c llvm::errs().
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Integral.h b/contrib/llvm-project/clang/lib/AST/Interp/Integral.h
index 46cd611ee389..8a742333ae57 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Integral.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Integral.h
@@ -53,17 +53,17 @@ template <> struct Repr<64, true> { using Type = int64_t; };
/// These wrappers are required to shared an interface between APSint and
/// builtin primitive numeral types, while optimising for storage and
/// allowing methods operating on primitive type to compile to fast code.
-template <unsigned Bits, bool Signed> class Integral {
+template <unsigned Bits, bool Signed> class Integral final {
private:
template <unsigned OtherBits, bool OtherSigned> friend class Integral;
// The primitive representing the integral.
- using T = typename Repr<Bits, Signed>::Type;
- T V;
+ using ReprT = typename Repr<Bits, Signed>::Type;
+ ReprT V;
/// Primitive representing limits.
- static const auto Min = std::numeric_limits<T>::min();
- static const auto Max = std::numeric_limits<T>::max();
+ static const auto Min = std::numeric_limits<ReprT>::min();
+ static const auto Max = std::numeric_limits<ReprT>::max();
/// Construct an integral from anything that is convertible to storage.
template <typename T> explicit Integral(T V) : V(V) {}
@@ -107,7 +107,7 @@ public:
return APSInt(APInt(Bits, static_cast<uint64_t>(V), Signed), !Signed);
}
APSInt toAPSInt(unsigned NumBits) const {
- if (Signed)
+ if constexpr (Signed)
return APSInt(toAPSInt().sextOrTrunc(NumBits), !Signed);
else
return APSInt(toAPSInt().zextOrTrunc(NumBits), !Signed);
@@ -124,25 +124,27 @@ public:
bool isMin() const { return *this == min(bitWidth()); }
- bool isMinusOne() const { return Signed && V == T(-1); }
+ bool isMinusOne() const { return Signed && V == ReprT(-1); }
constexpr static bool isSigned() { return Signed; }
- bool isNegative() const { return V < T(0); }
+ bool isNegative() const { return V < ReprT(0); }
bool isPositive() const { return !isNegative(); }
ComparisonCategoryResult compare(const Integral &RHS) const {
return Compare(V, RHS.V);
}
- unsigned countLeadingZeros() const { return llvm::countLeadingZeros<T>(V); }
+ unsigned countLeadingZeros() const {
+ return llvm::countLeadingZeros<ReprT>(V);
+ }
Integral truncate(unsigned TruncBits) const {
if (TruncBits >= Bits)
return *this;
- const T BitMask = (T(1) << T(TruncBits)) - 1;
- const T SignBit = T(1) << (TruncBits - 1);
- const T ExtMask = ~BitMask;
+ const ReprT BitMask = (ReprT(1) << ReprT(TruncBits)) - 1;
+ const ReprT SignBit = ReprT(1) << (TruncBits - 1);
+ const ReprT ExtMask = ~BitMask;
return Integral((V & BitMask) | (Signed && (V & SignBit) ? ExtMask : 0));
}
@@ -155,9 +157,11 @@ public:
return Integral(Max);
}
- template <typename T>
- static std::enable_if_t<std::is_integral<T>::value, Integral> from(T Value) {
- return Integral(Value);
+ template <typename ValT> static Integral from(ValT Value) {
+ if constexpr (std::is_integral<ValT>::value)
+ return Integral(Value);
+ else
+ return Integral::from(static_cast<Integral::ReprT>(Value));
}
template <unsigned SrcBits, bool SrcSign>
@@ -167,7 +171,7 @@ public:
}
template <bool SrcSign> static Integral from(Integral<0, SrcSign> Value) {
- if (SrcSign)
+ if constexpr (SrcSign)
return Integral(Value.V.getSExtValue());
else
return Integral(Value.V.getZExtValue());
@@ -180,15 +184,15 @@ public:
}
static bool inRange(int64_t Value, unsigned NumBits) {
- return CheckRange<T, Min, Max>(Value);
+ return CheckRange<ReprT, Min, Max>(Value);
}
static bool increment(Integral A, Integral *R) {
- return add(A, Integral(T(1)), A.bitWidth(), R);
+ return add(A, Integral(ReprT(1)), A.bitWidth(), R);
}
static bool decrement(Integral A, Integral *R) {
- return sub(A, Integral(T(1)), A.bitWidth(), R);
+ return sub(A, Integral(ReprT(1)), A.bitWidth(), R);
}
static bool add(Integral A, Integral B, unsigned OpBits, Integral *R) {
@@ -203,56 +207,74 @@ public:
return CheckMulUB(A.V, B.V, R->V);
}
-private:
- template <typename T>
- static std::enable_if_t<std::is_signed<T>::value, bool> CheckAddUB(T A, T B,
- T &R) {
- return llvm::AddOverflow<T>(A, B, R);
+ static bool rem(Integral A, Integral B, unsigned OpBits, Integral *R) {
+ *R = Integral(A.V % B.V);
+ return false;
+ }
+
+ static bool div(Integral A, Integral B, unsigned OpBits, Integral *R) {
+ *R = Integral(A.V / B.V);
+ return false;
}
- template <typename T>
- static std::enable_if_t<std::is_unsigned<T>::value, bool> CheckAddUB(T A, T B,
- T &R) {
- R = A + B;
+ static bool bitAnd(Integral A, Integral B, unsigned OpBits, Integral *R) {
+ *R = Integral(A.V & B.V);
return false;
}
- template <typename T>
- static std::enable_if_t<std::is_signed<T>::value, bool> CheckSubUB(T A, T B,
- T &R) {
- return llvm::SubOverflow<T>(A, B, R);
+ static bool bitOr(Integral A, Integral B, unsigned OpBits, Integral *R) {
+ *R = Integral(A.V | B.V);
+ return false;
}
- template <typename T>
- static std::enable_if_t<std::is_unsigned<T>::value, bool> CheckSubUB(T A, T B,
- T &R) {
- R = A - B;
+ static bool bitXor(Integral A, Integral B, unsigned OpBits, Integral *R) {
+ *R = Integral(A.V ^ B.V);
return false;
}
- template <typename T>
- static std::enable_if_t<std::is_signed<T>::value, bool> CheckMulUB(T A, T B,
- T &R) {
- return llvm::MulOverflow<T>(A, B, R);
+ static bool neg(Integral A, Integral *R) {
+ *R = -A;
+ return false;
}
- template <typename T>
- static std::enable_if_t<std::is_unsigned<T>::value, bool> CheckMulUB(T A, T B,
- T &R) {
- R = A * B;
+ static bool comp(Integral A, Integral *R) {
+ *R = Integral(~A.V);
return false;
}
- template <typename T, T Min, T Max>
- static std::enable_if_t<std::is_signed<T>::value, bool>
- CheckRange(int64_t V) {
- return Min <= V && V <= Max;
+private:
+ template <typename T> static bool CheckAddUB(T A, T B, T &R) {
+ if constexpr (std::is_signed_v<T>) {
+ return llvm::AddOverflow<T>(A, B, R);
+ } else {
+ R = A + B;
+ return false;
+ }
}
- template <typename T, T Min, T Max>
- static std::enable_if_t<std::is_unsigned<T>::value, bool>
- CheckRange(int64_t V) {
- return V >= 0 && static_cast<uint64_t>(V) <= Max;
+ template <typename T> static bool CheckSubUB(T A, T B, T &R) {
+ if constexpr (std::is_signed_v<T>) {
+ return llvm::SubOverflow<T>(A, B, R);
+ } else {
+ R = A - B;
+ return false;
+ }
+ }
+
+ template <typename T> static bool CheckMulUB(T A, T B, T &R) {
+ if constexpr (std::is_signed_v<T>) {
+ return llvm::MulOverflow<T>(A, B, R);
+ } else {
+ R = A * B;
+ return false;
+ }
+ }
+ template <typename T, T Min, T Max> static bool CheckRange(int64_t V) {
+ if constexpr (std::is_signed_v<T>) {
+ return Min <= V && V <= Max;
+ } else {
+ return V >= 0 && static_cast<uint64_t>(V) <= Max;
+ }
}
};
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp
index cec3f6d6160e..6a600b306bad 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Interp.cpp
@@ -1,4 +1,4 @@
-//===--- InterpState.cpp - Interpreter for the constexpr VM -----*- C++ -*-===//
+//===------- Interp.cpp - Interpreter for the constexpr VM ------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -201,8 +201,8 @@ bool CheckArray(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
AccessKinds AK) {
- const auto &Src = S.Current->getSource(OpPC);
if (Ptr.isZero()) {
+ const auto &Src = S.Current->getSource(OpPC);
if (Ptr.isField())
S.FFDiag(Src, diag::note_constexpr_null_subobject) << CSK_Field;
@@ -213,6 +213,7 @@ bool CheckLive(InterpState &S, CodePtr OpPC, const Pointer &Ptr,
}
if (!Ptr.isLive()) {
+ const auto &Src = S.Current->getSource(OpPC);
bool IsTemp = Ptr.isTemporary();
S.FFDiag(Src, diag::note_constexpr_lifetime_ended, 1) << AK << !IsTemp;
@@ -330,17 +331,18 @@ bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
return true;
}
-bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F) {
- const SourceLocation &Loc = S.Current->getLocation(OpPC);
+bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F) {
if (F->isVirtual()) {
if (!S.getLangOpts().CPlusPlus20) {
+ const SourceLocation &Loc = S.Current->getLocation(OpPC);
S.CCEDiag(Loc, diag::note_constexpr_virtual_call);
return false;
}
}
if (!F->isConstexpr()) {
+ const SourceLocation &Loc = S.Current->getLocation(OpPC);
if (S.getLangOpts().CPlusPlus11) {
const FunctionDecl *DiagDecl = F->getDecl();
@@ -398,9 +400,92 @@ bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD) {
S.Note(MD->getLocation(), diag::note_declared_at);
return false;
}
+
+static void DiagnoseUninitializedSubobject(InterpState &S, const SourceInfo &SI,
+ QualType SubObjType,
+ SourceLocation SubObjLoc) {
+ S.FFDiag(SI, diag::note_constexpr_uninitialized) << true << SubObjType;
+ if (SubObjLoc.isValid())
+ S.Note(SubObjLoc, diag::note_constexpr_subobject_declared_here);
+}
+
+static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC,
+ const Pointer &BasePtr, const Record *R);
+
+static bool CheckArrayInitialized(InterpState &S, CodePtr OpPC,
+ const Pointer &BasePtr,
+ const ConstantArrayType *CAT) {
+ bool Result = true;
+ size_t NumElems = CAT->getSize().getZExtValue();
+ QualType ElemType = CAT->getElementType();
+
+ if (isa<RecordType>(ElemType.getTypePtr())) {
+ const Record *R = BasePtr.getElemRecord();
+ for (size_t I = 0; I != NumElems; ++I) {
+ Pointer ElemPtr = BasePtr.atIndex(I).narrow();
+ Result &= CheckFieldsInitialized(S, OpPC, ElemPtr, R);
+ }
+ } else if (auto *ElemCAT = dyn_cast<ConstantArrayType>(ElemType)) {
+ for (size_t I = 0; I != NumElems; ++I) {
+ Pointer ElemPtr = BasePtr.atIndex(I).narrow();
+ Result &= CheckArrayInitialized(S, OpPC, ElemPtr, ElemCAT);
+ }
+ } else {
+ for (size_t I = 0; I != NumElems; ++I) {
+ if (!BasePtr.atIndex(I).isInitialized()) {
+ DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC), ElemType,
+ BasePtr.getFieldDesc()->getLocation());
+ Result = false;
+ }
+ }
+ }
+
+ return Result;
+}
+
+static bool CheckFieldsInitialized(InterpState &S, CodePtr OpPC,
+ const Pointer &BasePtr, const Record *R) {
+ assert(R);
+ bool Result = true;
+ // Check all fields of this record are initialized.
+ for (const Record::Field &F : R->fields()) {
+ Pointer FieldPtr = BasePtr.atField(F.Offset);
+ QualType FieldType = F.Decl->getType();
+
+ if (FieldType->isRecordType()) {
+ Result &= CheckFieldsInitialized(S, OpPC, FieldPtr, FieldPtr.getRecord());
+ } else if (FieldType->isArrayType()) {
+ const auto *CAT =
+ cast<ConstantArrayType>(FieldType->getAsArrayTypeUnsafe());
+ Result &= CheckArrayInitialized(S, OpPC, FieldPtr, CAT);
+ } else if (!FieldPtr.isInitialized()) {
+ DiagnoseUninitializedSubobject(S, S.Current->getSource(OpPC),
+ F.Decl->getType(), F.Decl->getLocation());
+ Result = false;
+ }
+ }
+ return Result;
+}
+
+bool CheckCtorCall(InterpState &S, CodePtr OpPC, const Pointer &This) {
+ assert(!This.isZero());
+ const Record *R = This.getRecord();
+ return CheckFieldsInitialized(S, OpPC, This, R);
+}
+
bool Interpret(InterpState &S, APValue &Result) {
+ // The current stack frame when we started Interpret().
+ // This is being used by the ops to determine wheter
+ // to return from this function and thus terminate
+ // interpretation.
+ const InterpFrame *StartFrame = S.Current;
+ assert(!S.Current->isRoot());
CodePtr PC = S.Current->getPC();
+ // Empty program.
+ if (!PC)
+ return true;
+
for (;;) {
auto Op = PC.read<Opcode>();
CodePtr OpPC = PC;
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Interp.h b/contrib/llvm-project/clang/lib/AST/Interp/Interp.h
index a1d90f26ba46..ed3accd98a90 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Interp.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Interp.h
@@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_AST_INTERP_INTERP_H
#define LLVM_CLANG_AST_INTERP_INTERP_H
+#include "Boolean.h"
#include "Function.h"
#include "InterpFrame.h"
#include "InterpStack.h"
@@ -30,7 +31,6 @@
#include "llvm/Support/Endian.h"
#include <limits>
#include <type_traits>
-#include <vector>
namespace clang {
namespace interp {
@@ -84,7 +84,7 @@ bool CheckInvoke(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
bool CheckInit(InterpState &S, CodePtr OpPC, const Pointer &Ptr);
/// Checks if a method can be called.
-bool CheckCallable(InterpState &S, CodePtr OpPC, Function *F);
+bool CheckCallable(InterpState &S, CodePtr OpPC, const Function *F);
/// Checks the 'this' pointer.
bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This);
@@ -92,7 +92,53 @@ bool CheckThis(InterpState &S, CodePtr OpPC, const Pointer &This);
/// Checks if a method is pure virtual.
bool CheckPure(InterpState &S, CodePtr OpPC, const CXXMethodDecl *MD);
-template <typename T> inline bool IsTrue(const T &V) { return !V.isZero(); }
+/// Checks that all fields are initialized after a constructor call.
+bool CheckCtorCall(InterpState &S, CodePtr OpPC, const Pointer &This);
+
+/// Checks if the shift operation is legal.
+template <typename RT>
+bool CheckShift(InterpState &S, CodePtr OpPC, const RT &RHS, unsigned Bits) {
+ if (RHS.isNegative()) {
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
+ return false;
+ }
+
+ // C++11 [expr.shift]p1: Shift width must be less than the bit width of
+ // the shifted type.
+ if (Bits > 1 && RHS >= RT::from(Bits, RHS.bitWidth())) {
+ const Expr *E = S.Current->getExpr(OpPC);
+ const APSInt Val = RHS.toAPSInt();
+ QualType Ty = E->getType();
+ S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits;
+ return false;
+ }
+ return true;
+}
+
+/// Checks if Div/Rem operation on LHS and RHS is valid.
+template <typename T>
+bool CheckDivRem(InterpState &S, CodePtr OpPC, const T &LHS, const T &RHS) {
+ if (RHS.isZero()) {
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ S.FFDiag(Loc, diag::note_expr_divide_by_zero);
+ return false;
+ }
+
+ if (LHS.isSigned() && LHS.isMin() && RHS.isNegative() && RHS.isMinusOne()) {
+ APSInt LHSInt = LHS.toAPSInt();
+ SmallString<32> Trunc;
+ (-LHSInt.extend(LHSInt.getBitWidth() + 1)).toString(Trunc, 10);
+ const SourceInfo &Loc = S.Current->getSource(OpPC);
+ const Expr *E = S.Current->getExpr(OpPC);
+ S.CCEDiag(Loc, diag::note_constexpr_overflow) << Trunc << E->getType();
+ return false;
+ }
+ return true;
+}
+
+/// Interpreter entry point.
+bool Interpret(InterpState &S, APValue &Result);
//===----------------------------------------------------------------------===//
// Add, Sub, Mul
@@ -154,6 +200,240 @@ bool Mul(InterpState &S, CodePtr OpPC) {
return AddSubMulHelper<T, T::mul, std::multiplies>(S, OpPC, Bits, LHS, RHS);
}
+/// 1) Pops the RHS from the stack.
+/// 2) Pops the LHS from the stack.
+/// 3) Pushes 'LHS & RHS' on the stack
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool BitAnd(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+
+ unsigned Bits = RHS.bitWidth();
+ T Result;
+ if (!T::bitAnd(LHS, RHS, Bits, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+ return false;
+}
+
+/// 1) Pops the RHS from the stack.
+/// 2) Pops the LHS from the stack.
+/// 3) Pushes 'LHS | RHS' on the stack
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool BitOr(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+
+ unsigned Bits = RHS.bitWidth();
+ T Result;
+ if (!T::bitOr(LHS, RHS, Bits, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+ return false;
+}
+
+/// 1) Pops the RHS from the stack.
+/// 2) Pops the LHS from the stack.
+/// 3) Pushes 'LHS ^ RHS' on the stack
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool BitXor(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+
+ unsigned Bits = RHS.bitWidth();
+ T Result;
+ if (!T::bitXor(LHS, RHS, Bits, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+ return false;
+}
+
+/// 1) Pops the RHS from the stack.
+/// 2) Pops the LHS from the stack.
+/// 3) Pushes 'LHS % RHS' on the stack (the remainder of dividing LHS by RHS).
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Rem(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+
+ if (!CheckDivRem(S, OpPC, LHS, RHS))
+ return false;
+
+ const unsigned Bits = RHS.bitWidth() * 2;
+ T Result;
+ if (!T::rem(LHS, RHS, Bits, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+ return false;
+}
+
+/// 1) Pops the RHS from the stack.
+/// 2) Pops the LHS from the stack.
+/// 3) Pushes 'LHS / RHS' on the stack
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Div(InterpState &S, CodePtr OpPC) {
+ const T &RHS = S.Stk.pop<T>();
+ const T &LHS = S.Stk.pop<T>();
+
+ if (!CheckDivRem(S, OpPC, LHS, RHS))
+ return false;
+
+ const unsigned Bits = RHS.bitWidth() * 2;
+ T Result;
+ if (!T::div(LHS, RHS, Bits, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// Inv
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Inv(InterpState &S, CodePtr OpPC) {
+ using BoolT = PrimConv<PT_Bool>::T;
+ const T &Val = S.Stk.pop<T>();
+ const unsigned Bits = Val.bitWidth();
+ Boolean R;
+ Boolean::inv(BoolT::from(Val, Bits), &R);
+
+ S.Stk.push<BoolT>(R);
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Neg
+//===----------------------------------------------------------------------===//
+
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Neg(InterpState &S, CodePtr OpPC) {
+ const T &Val = S.Stk.pop<T>();
+ T Result;
+ T::neg(Val, &Result);
+
+ S.Stk.push<T>(Result);
+ return true;
+}
+
+enum class PushVal : bool {
+ No,
+ Yes,
+};
+enum class IncDecOp {
+ Inc,
+ Dec,
+};
+
+template <typename T, IncDecOp Op, PushVal DoPush>
+bool IncDecHelper(InterpState &S, CodePtr OpPC, const Pointer &Ptr) {
+ T Value = Ptr.deref<T>();
+ T Result;
+
+ if constexpr (DoPush == PushVal::Yes)
+ S.Stk.push<T>(Result);
+
+ if constexpr (Op == IncDecOp::Inc) {
+ if (!T::increment(Value, &Result)) {
+ Ptr.deref<T>() = Result;
+ return true;
+ }
+ } else {
+ if (!T::decrement(Value, &Result)) {
+ Ptr.deref<T>() = Result;
+ return true;
+ }
+ }
+
+ // Something went wrong with the previous operation. Compute the
+ // result with another bit of precision.
+ unsigned Bits = Value.bitWidth() + 1;
+ APSInt APResult;
+ if constexpr (Op == IncDecOp::Inc)
+ APResult = ++Value.toAPSInt(Bits);
+ else
+ APResult = --Value.toAPSInt(Bits);
+
+ // Report undefined behaviour, stopping if required.
+ const Expr *E = S.Current->getExpr(OpPC);
+ QualType Type = E->getType();
+ if (S.checkingForUndefinedBehavior()) {
+ SmallString<32> Trunc;
+ APResult.trunc(Result.bitWidth()).toString(Trunc, 10);
+ auto Loc = E->getExprLoc();
+ S.report(Loc, diag::warn_integer_constant_overflow) << Trunc << Type;
+ return true;
+ }
+
+ S.CCEDiag(E, diag::note_constexpr_overflow) << APResult << Type;
+ return S.noteUndefinedBehavior();
+}
+
+/// 1) Pops a pointer from the stack
+/// 2) Load the value from the pointer
+/// 3) Writes the value increased by one back to the pointer
+/// 4) Pushes the original (pre-inc) value on the stack.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Inc(InterpState &S, CodePtr OpPC) {
+ // FIXME: Check initialization of Ptr
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ return IncDecHelper<T, IncDecOp::Inc, PushVal::Yes>(S, OpPC, Ptr);
+}
+
+/// 1) Pops a pointer from the stack
+/// 2) Load the value from the pointer
+/// 3) Writes the value increased by one back to the pointer
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool IncPop(InterpState &S, CodePtr OpPC) {
+ // FIXME: Check initialization of Ptr
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ return IncDecHelper<T, IncDecOp::Inc, PushVal::No>(S, OpPC, Ptr);
+}
+
+/// 1) Pops a pointer from the stack
+/// 2) Load the value from the pointer
+/// 3) Writes the value decreased by one back to the pointer
+/// 4) Pushes the original (pre-dec) value on the stack.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Dec(InterpState &S, CodePtr OpPC) {
+ // FIXME: Check initialization of Ptr
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ return IncDecHelper<T, IncDecOp::Dec, PushVal::Yes>(S, OpPC, Ptr);
+}
+
+/// 1) Pops a pointer from the stack
+/// 2) Load the value from the pointer
+/// 3) Writes the value decreased by one back to the pointer
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool DecPop(InterpState &S, CodePtr OpPC) {
+ // FIXME: Check initialization of Ptr
+ const Pointer &Ptr = S.Stk.pop<Pointer>();
+
+ return IncDecHelper<T, IncDecOp::Dec, PushVal::No>(S, OpPC, Ptr);
+}
+
+/// 1) Pops the value from the stack.
+/// 2) Pushes the bitwise complemented value on the stack (~V).
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+bool Comp(InterpState &S, CodePtr OpPC) {
+ const T &Val = S.Stk.pop<T>();
+ T Result;
+ if (!T::comp(Val, &Result)) {
+ S.Stk.push<T>(Result);
+ return true;
+ }
+
+ return false;
+}
+
//===----------------------------------------------------------------------===//
// EQ, NE, GT, GE, LT, LE
//===----------------------------------------------------------------------===//
@@ -209,6 +489,16 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
} else {
unsigned VL = LHS.getByteOffset();
unsigned VR = RHS.getByteOffset();
+
+ // In our Pointer class, a pointer to an array and a pointer to the first
+ // element in the same array are NOT equal. They have the same Base value,
+ // but a different Offset. This is a pretty rare case, so we fix this here
+ // by comparing pointers to the first elements.
+ if (LHS.inArray() && LHS.isRoot())
+ VL = LHS.atIndex(0).getByteOffset();
+ if (RHS.inArray() && RHS.isRoot())
+ VR = RHS.atIndex(0).getByteOffset();
+
S.Stk.push<BoolT>(BoolT::from(Fn(Compare(VL, VR))));
return true;
}
@@ -304,7 +594,10 @@ bool Const(InterpState &S, CodePtr OpPC, const T &Arg) {
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool GetLocal(InterpState &S, CodePtr OpPC, uint32_t I) {
- S.Stk.push<T>(S.Current->getLocal<T>(I));
+ const Pointer &Ptr = S.Current->getLocalPointer(I);
+ if (!CheckLoad(S, OpPC, Ptr))
+ return false;
+ S.Stk.push<T>(Ptr.deref<T>());
return true;
}
@@ -329,6 +622,8 @@ bool SetParam(InterpState &S, CodePtr OpPC, uint32_t I) {
return true;
}
+/// 1) Peeks a pointer on the stack
+/// 2) Pushes the value of the pointer's field on the stack
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool GetField(InterpState &S, CodePtr OpPC, uint32_t I) {
const Pointer &Obj = S.Stk.peek<Pointer>();
@@ -358,6 +653,8 @@ bool SetField(InterpState &S, CodePtr OpPC, uint32_t I) {
return true;
}
+/// 1) Pops a pointer from the stack
+/// 2) Pushes the value of the pointer's field on the stack
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool GetFieldPop(InterpState &S, CodePtr OpPC, uint32_t I) {
const Pointer &Obj = S.Stk.pop<Pointer>();
@@ -463,10 +760,13 @@ bool InitThisFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) {
return true;
}
+/// 1) Pops the value from the stack
+/// 2) Peeks a pointer from the stack
+/// 3) Pushes the value to field I of the pointer on the stack
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) {
const T &Value = S.Stk.pop<T>();
- const Pointer &Field = S.Stk.pop<Pointer>().atField(I);
+ const Pointer &Field = S.Stk.peek<Pointer>().atField(I);
Field.deref<T>() = Value;
Field.activate();
Field.initialize();
@@ -516,6 +816,8 @@ inline bool GetPtrGlobal(InterpState &S, CodePtr OpPC, uint32_t I) {
return true;
}
+/// 1) Pops a Pointer from the stack
+/// 2) Pushes Pointer.atField(Off) on the stack
inline bool GetPtrField(InterpState &S, CodePtr OpPC, uint32_t Off) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckNull(S, OpPC, Ptr, CSK_Field))
@@ -638,6 +940,8 @@ bool Store(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.peek<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
+ if (!Ptr.isRoot())
+ Ptr.initialize();
Ptr.deref<T>() = Value;
return true;
}
@@ -648,6 +952,8 @@ bool StorePop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
+ if (!Ptr.isRoot())
+ Ptr.initialize();
Ptr.deref<T>() = Value;
return true;
}
@@ -658,6 +964,8 @@ bool StoreBitField(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.peek<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
+ if (!Ptr.isRoot())
+ Ptr.initialize();
if (auto *FD = Ptr.getField()) {
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
} else {
@@ -672,6 +980,8 @@ bool StoreBitFieldPop(InterpState &S, CodePtr OpPC) {
const Pointer &Ptr = S.Stk.pop<Pointer>();
if (!CheckStore(S, OpPC, Ptr))
return false;
+ if (!Ptr.isRoot())
+ Ptr.initialize();
if (auto *FD = Ptr.getField()) {
Ptr.deref<T>() = Value.truncate(FD->getBitWidthValue(S.getCtx()));
} else {
@@ -691,6 +1001,9 @@ bool InitPop(InterpState &S, CodePtr OpPC) {
return true;
}
+/// 1) Pops the value from the stack
+/// 2) Peeks a pointer and gets its index \Idx
+/// 3) Sets the value on the pointer, leaving the pointer on the stack.
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) {
const T &Value = S.Stk.pop<T>();
@@ -702,6 +1015,7 @@ bool InitElem(InterpState &S, CodePtr OpPC, uint32_t Idx) {
return true;
}
+/// The same as InitElem, but pops the pointer as well.
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitElemPop(InterpState &S, CodePtr OpPC, uint32_t Idx) {
const T &Value = S.Stk.pop<T>();
@@ -721,23 +1035,25 @@ template <class T, bool Add> bool OffsetHelper(InterpState &S, CodePtr OpPC) {
// Fetch the pointer and the offset.
const T &Offset = S.Stk.pop<T>();
const Pointer &Ptr = S.Stk.pop<Pointer>();
- if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex))
- return false;
+
if (!CheckRange(S, OpPC, Ptr, CSK_ArrayToPointer))
return false;
- // Get a version of the index comparable to the type.
- T Index = T::from(Ptr.getIndex(), Offset.bitWidth());
- // A zero offset does not change the pointer, but in the case of an array
- // it has to be adjusted to point to the first element instead of the array.
+ // A zero offset does not change the pointer.
if (Offset.isZero()) {
- S.Stk.push<Pointer>(Index.isZero() ? Ptr.atIndex(0) : Ptr);
+ S.Stk.push<Pointer>(Ptr);
return true;
}
+
+ if (!CheckNull(S, OpPC, Ptr, CSK_ArrayIndex))
+ return false;
+
// Arrays of unknown bounds cannot have pointers into them.
if (!CheckArray(S, OpPC, Ptr))
return false;
+ // Get a version of the index comparable to the type.
+ T Index = T::from(Ptr.getIndex(), Offset.bitWidth());
// Compute the largest index into the array.
unsigned MaxIndex = Ptr.getNumElems();
@@ -754,23 +1070,34 @@ template <class T, bool Add> bool OffsetHelper(InterpState &S, CodePtr OpPC) {
return false;
};
- // If the new offset would be negative, bail out.
- if (Add && Offset.isNegative() && (Offset.isMin() || -Offset > Index))
- return InvalidOffset();
- if (!Add && Offset.isPositive() && Index < Offset)
- return InvalidOffset();
-
- // If the new offset would be out of bounds, bail out.
unsigned MaxOffset = MaxIndex - Ptr.getIndex();
- if (Add && Offset.isPositive() && Offset > MaxOffset)
- return InvalidOffset();
- if (!Add && Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset))
- return InvalidOffset();
+ if constexpr (Add) {
+ // If the new offset would be negative, bail out.
+ if (Offset.isNegative() && (Offset.isMin() || -Offset > Index))
+ return InvalidOffset();
+
+ // If the new offset would be out of bounds, bail out.
+ if (Offset.isPositive() && Offset > MaxOffset)
+ return InvalidOffset();
+ } else {
+ // If the new offset would be negative, bail out.
+ if (Offset.isPositive() && Index < Offset)
+ return InvalidOffset();
+
+ // If the new offset would be out of bounds, bail out.
+ if (Offset.isNegative() && (Offset.isMin() || -Offset > MaxOffset))
+ return InvalidOffset();
+ }
// Offset is valid - compute it on unsigned.
int64_t WideIndex = static_cast<int64_t>(Index);
int64_t WideOffset = static_cast<int64_t>(Offset);
- int64_t Result = Add ? (WideIndex + WideOffset) : (WideIndex - WideOffset);
+ int64_t Result;
+ if constexpr (Add)
+ Result = WideIndex + WideOffset;
+ else
+ Result = WideIndex - WideOffset;
+
S.Stk.push<Pointer>(Ptr.atIndex(static_cast<unsigned>(Result)));
return true;
}
@@ -785,6 +1112,23 @@ bool SubOffset(InterpState &S, CodePtr OpPC) {
return OffsetHelper<T, false>(S, OpPC);
}
+/// 1) Pops a Pointer from the stack.
+/// 2) Pops another Pointer from the stack.
+/// 3) Pushes the different of the indices of the two pointers on the stack.
+template <PrimType Name, class T = typename PrimConv<Name>::T>
+inline bool SubPtr(InterpState &S, CodePtr OpPC) {
+ const Pointer &LHS = S.Stk.pop<Pointer>();
+ const Pointer &RHS = S.Stk.pop<Pointer>();
+
+ if (!Pointer::hasSameArray(LHS, RHS)) {
+ // TODO: Diagnose.
+ return false;
+ }
+
+ T A = T::from(LHS.getIndex());
+ T B = T::from(RHS.getIndex());
+ return AddSubMulHelper<T, T::sub, std::minus>(S, OpPC, A.bitWidth(), A, B);
+}
//===----------------------------------------------------------------------===//
// Destroy
@@ -840,88 +1184,47 @@ inline bool This(InterpState &S, CodePtr OpPC) {
return true;
}
+inline bool RVOPtr(InterpState &S, CodePtr OpPC) {
+ assert(S.Current->getFunction()->hasRVO());
+ S.Stk.push<Pointer>(S.Current->getRVOPtr());
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// Shr, Shl
//===----------------------------------------------------------------------===//
-template <PrimType TR, PrimType TL, class T = typename PrimConv<TR>::T>
-unsigned Trunc(InterpState &S, CodePtr OpPC, unsigned Bits, const T &V) {
- // C++11 [expr.shift]p1: Shift width must be less than the bit width of
- // the shifted type.
- if (Bits > 1 && V >= T::from(Bits, V.bitWidth())) {
- const Expr *E = S.Current->getExpr(OpPC);
- const APSInt Val = V.toAPSInt();
- QualType Ty = E->getType();
- S.CCEDiag(E, diag::note_constexpr_large_shift) << Val << Ty << Bits;
- return Bits;
- } else {
- return static_cast<unsigned>(V);
- }
-}
-
-template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::T>
-inline bool ShiftRight(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) {
- if (RHS >= V.bitWidth()) {
- S.Stk.push<T>(T::from(0, V.bitWidth()));
- } else {
- S.Stk.push<T>(T::from(V >> RHS, V.bitWidth()));
- }
- return true;
-}
+template <PrimType NameL, PrimType NameR>
+inline bool Shr(InterpState &S, CodePtr OpPC) {
+ using LT = typename PrimConv<NameL>::T;
+ using RT = typename PrimConv<NameR>::T;
+ const auto &RHS = S.Stk.pop<RT>();
+ const auto &LHS = S.Stk.pop<LT>();
+ const unsigned Bits = LHS.bitWidth();
-template <PrimType TL, PrimType TR, typename T = typename PrimConv<TL>::T>
-inline bool ShiftLeft(InterpState &S, CodePtr OpPC, const T &V, unsigned RHS) {
- if (V.isSigned() && !S.getLangOpts().CPlusPlus20) {
- // C++11 [expr.shift]p2: A signed left shift must have a non-negative
- // operand, and must not overflow the corresponding unsigned type.
- // C++2a [expr.shift]p2: E1 << E2 is the unique value congruent to
- // E1 x 2^E2 module 2^N.
- if (V.isNegative()) {
- const Expr *E = S.Current->getExpr(OpPC);
- S.CCEDiag(E, diag::note_constexpr_lshift_of_negative) << V.toAPSInt();
- } else if (V.countLeadingZeros() < RHS) {
- S.CCEDiag(S.Current->getExpr(OpPC), diag::note_constexpr_lshift_discards);
- }
- }
+ if (!CheckShift<RT>(S, OpPC, RHS, Bits))
+ return false;
- if (V.bitWidth() == 1) {
- S.Stk.push<T>(V);
- } else if (RHS >= V.bitWidth()) {
- S.Stk.push<T>(T::from(0, V.bitWidth()));
- } else {
- S.Stk.push<T>(T::from(V.toUnsigned() << RHS, V.bitWidth()));
- }
+ unsigned URHS = static_cast<unsigned>(RHS);
+ S.Stk.push<LT>(LT::from(static_cast<unsigned>(LHS) >> URHS, LHS.bitWidth()));
return true;
}
-template <PrimType TL, PrimType TR>
-inline bool Shr(InterpState &S, CodePtr OpPC) {
- const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>();
- const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>();
+template <PrimType NameL, PrimType NameR>
+inline bool Shl(InterpState &S, CodePtr OpPC) {
+ using LT = typename PrimConv<NameL>::T;
+ using RT = typename PrimConv<NameR>::T;
+ const auto &RHS = S.Stk.pop<RT>();
+ const auto &LHS = S.Stk.pop<LT>();
const unsigned Bits = LHS.bitWidth();
- if (RHS.isSigned() && RHS.isNegative()) {
- const SourceInfo &Loc = S.Current->getSource(OpPC);
- S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
- return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS));
- } else {
- return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS));
- }
-}
+ if (!CheckShift<RT>(S, OpPC, RHS, Bits))
+ return false;
-template <PrimType TL, PrimType TR>
-inline bool Shl(InterpState &S, CodePtr OpPC) {
- const auto &RHS = S.Stk.pop<typename PrimConv<TR>::T>();
- const auto &LHS = S.Stk.pop<typename PrimConv<TL>::T>();
- const unsigned Bits = LHS.bitWidth();
+ unsigned URHS = static_cast<unsigned>(RHS);
+ S.Stk.push<LT>(LT::from(static_cast<unsigned>(LHS) << URHS, LHS.bitWidth()));
- if (RHS.isSigned() && RHS.isNegative()) {
- const SourceInfo &Loc = S.Current->getSource(OpPC);
- S.CCEDiag(Loc, diag::note_constexpr_negative_shift) << RHS.toAPSInt();
- return ShiftRight<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, -RHS));
- } else {
- return ShiftLeft<TL, TR>(S, OpPC, LHS, Trunc<TR, TL>(S, OpPC, Bits, RHS));
- }
+ return true;
}
//===----------------------------------------------------------------------===//
@@ -950,26 +1253,56 @@ inline bool ExpandPtr(InterpState &S, CodePtr OpPC) {
return true;
}
+inline bool Call(InterpState &S, CodePtr &PC, const Function *Func) {
+ auto NewFrame = std::make_unique<InterpFrame>(S, Func, PC);
+ Pointer ThisPtr;
+ if (Func->hasThisPointer()) {
+ ThisPtr = NewFrame->getThis();
+ if (!CheckInvoke(S, PC, ThisPtr)) {
+ return false;
+ }
+ }
+
+ if (!CheckCallable(S, PC, Func))
+ return false;
+
+ InterpFrame *FrameBefore = S.Current;
+ S.Current = NewFrame.get();
+
+ APValue CallResult;
+ // Note that we cannot assert(CallResult.hasValue()) here since
+ // Ret() above only sets the APValue if the curent frame doesn't
+ // have a caller set.
+ if (Interpret(S, CallResult)) {
+ NewFrame.release(); // Frame was delete'd already.
+ assert(S.Current == FrameBefore);
+
+ // For constructors, check that all fields have been initialized.
+ if (Func->isConstructor() && !CheckCtorCall(S, PC, ThisPtr))
+ return false;
+
+ return true;
+ }
+
+ // Interpreting the function failed somehow. Reset to
+ // previous state.
+ S.Current = FrameBefore;
+ return false;
+}
+
//===----------------------------------------------------------------------===//
// Read opcode arguments
//===----------------------------------------------------------------------===//
-template <typename T>
-inline std::enable_if_t<!std::is_pointer<T>::value, T> ReadArg(InterpState &S,
- CodePtr OpPC) {
- return OpPC.read<T>();
-}
-
-template <typename T>
-inline std::enable_if_t<std::is_pointer<T>::value, T> ReadArg(InterpState &S,
- CodePtr OpPC) {
- uint32_t ID = OpPC.read<uint32_t>();
- return reinterpret_cast<T>(S.P.getNativePointer(ID));
+template <typename T> inline T ReadArg(InterpState &S, CodePtr &OpPC) {
+ if constexpr (std::is_pointer<T>::value) {
+ uint32_t ID = OpPC.read<uint32_t>();
+ return reinterpret_cast<T>(S.P.getNativePointer(ID));
+ } else {
+ return OpPC.read<T>();
+ }
}
-/// Interpreter entry point.
-bool Interpret(InterpState &S, APValue &Result);
-
} // namespace interp
} // namespace clang
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/InterpBlock.h b/contrib/llvm-project/clang/lib/AST/Interp/InterpBlock.h
index 2d5386e60b8c..f790c50a9123 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/InterpBlock.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/InterpBlock.h
@@ -31,11 +31,25 @@ enum PrimType : unsigned;
/// A memory block, either on the stack or in the heap.
///
-/// The storage described by the block immediately follows it in memory.
-class Block {
+/// The storage described by the block is immediately followed by
+/// optional metadata, which is followed by the actual data.
+///
+/// Block* rawData() data()
+/// │ │ │
+/// │ │ │
+/// ▼ ▼ ▼
+/// ┌───────────────┬─────────────────────────┬─────────────────┐
+/// │ Block │ Metadata │ Data │
+/// │ sizeof(Block) │ Desc->getMetadataSize() │ Desc->getSize() │
+/// └───────────────┴─────────────────────────┴─────────────────┘
+///
+/// Desc->getAllocSize() describes the size after the Block, i.e.
+/// the data size and the metadata size.
+///
+class Block final {
public:
// Creates a new block.
- Block(const llvm::Optional<unsigned> &DeclID, Descriptor *Desc,
+ Block(const std::optional<unsigned> &DeclID, Descriptor *Desc,
bool IsStatic = false, bool IsExtern = false)
: DeclID(DeclID), IsStatic(IsStatic), IsExtern(IsExtern), Desc(Desc) {}
@@ -56,10 +70,27 @@ public:
/// Returns the size of the block.
InterpSize getSize() const { return Desc->getAllocSize(); }
/// Returns the declaration ID.
- llvm::Optional<unsigned> getDeclID() const { return DeclID; }
+ std::optional<unsigned> getDeclID() const { return DeclID; }
/// Returns a pointer to the stored data.
- char *data() { return reinterpret_cast<char *>(this + 1); }
+ /// You are allowed to read Desc->getSize() bytes from this address.
+ char *data() {
+ // rawData might contain metadata as well.
+ size_t DataOffset = Desc->getMetadataSize();
+ return rawData() + DataOffset;
+ }
+ const char *data() const {
+ // rawData might contain metadata as well.
+ size_t DataOffset = Desc->getMetadataSize();
+ return rawData() + DataOffset;
+ }
+
+ /// Returns a pointer to the raw data, including metadata.
+ /// You are allowed to read Desc->getAllocSize() bytes from this address.
+ char *rawData() { return reinterpret_cast<char *>(this) + sizeof(Block); }
+ const char *rawData() const {
+ return reinterpret_cast<const char *>(this) + sizeof(Block);
+ }
/// Returns a view over the data.
template <typename T>
@@ -67,12 +98,18 @@ public:
/// Invokes the constructor.
void invokeCtor() {
- std::memset(data(), 0, getSize());
+ std::memset(rawData(), 0, Desc->getAllocSize());
if (Desc->CtorFn)
Desc->CtorFn(this, data(), Desc->IsConst, Desc->IsMutable,
/*isActive=*/true, Desc);
}
+ // Invokes the Destructor.
+ void invokeDtor() {
+ if (Desc->DtorFn)
+ Desc->DtorFn(this, data(), Desc);
+ }
+
protected:
friend class Pointer;
friend class DeadBlock;
@@ -92,7 +129,7 @@ protected:
/// Start of the chain of pointers.
Pointer *Pointers = nullptr;
/// Unique identifier of the declaration.
- llvm::Optional<unsigned> DeclID;
+ std::optional<unsigned> DeclID;
/// Flag indicating if the block has static storage duration.
bool IsStatic = false;
/// Flag indicating if the block is an extern.
@@ -107,7 +144,7 @@ protected:
///
/// Dead blocks are chained in a double-linked list to deallocate them
/// whenever pointers become dead.
-class DeadBlock {
+class DeadBlock final {
public:
/// Copies the block.
DeadBlock(DeadBlock *&Root, Block *Blk);
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.cpp b/contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.cpp
index 9d01bf0333fe..40644c538c6a 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.cpp
+++ b/contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.cpp
@@ -7,37 +7,68 @@
//===----------------------------------------------------------------------===//
#include "InterpFrame.h"
+#include "Boolean.h"
#include "Function.h"
-#include "Interp.h"
#include "InterpStack.h"
+#include "InterpState.h"
+#include "Pointer.h"
#include "PrimType.h"
#include "Program.h"
+#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclCXX.h"
using namespace clang;
using namespace clang::interp;
-InterpFrame::InterpFrame(InterpState &S, Function *Func, InterpFrame *Caller,
- CodePtr RetPC, Pointer &&This)
- : Caller(Caller), S(S), Func(Func), This(std::move(This)), RetPC(RetPC),
+InterpFrame::InterpFrame(InterpState &S, const Function *Func,
+ InterpFrame *Caller, CodePtr RetPC)
+ : Caller(Caller), S(S), Func(Func), RetPC(RetPC),
ArgSize(Func ? Func->getArgSize() : 0),
Args(static_cast<char *>(S.Stk.top())), FrameOffset(S.Stk.size()) {
- if (Func) {
- if (unsigned FrameSize = Func->getFrameSize()) {
- Locals = std::make_unique<char[]>(FrameSize);
- for (auto &Scope : Func->scopes()) {
- for (auto &Local : Scope.locals()) {
- Block *B = new (localBlock(Local.Offset)) Block(Local.Desc);
- B->invokeCtor();
- }
- }
+ if (!Func)
+ return;
+
+ unsigned FrameSize = Func->getFrameSize();
+ if (FrameSize == 0)
+ return;
+
+ Locals = std::make_unique<char[]>(FrameSize);
+ for (auto &Scope : Func->scopes()) {
+ for (auto &Local : Scope.locals()) {
+ Block *B = new (localBlock(Local.Offset)) Block(Local.Desc);
+ B->invokeCtor();
+ InlineDescriptor *ID = localInlineDesc(Local.Offset);
+ ID->Desc = Local.Desc;
+ ID->IsActive = true;
+ ID->Offset = sizeof(InlineDescriptor);
+ ID->IsBase = false;
+ ID->IsFieldMutable = false;
+ ID->IsConst = false;
+ ID->IsInitialized = false;
}
}
}
+InterpFrame::InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC)
+ : InterpFrame(S, Func, S.Current, RetPC) {
+ // As per our calling convention, the this pointer is
+ // part of the ArgSize.
+ // If the function has RVO, the RVO pointer is first.
+ // If the fuction has a This pointer, that one is next.
+ // Then follow the actual arguments (but those are handled
+ // in getParamPointer()).
+ if (Func->hasRVO())
+ RVOPtr = stackRef<Pointer>(0);
+
+ if (Func->hasThisPointer()) {
+ if (Func->hasRVO())
+ This = stackRef<Pointer>(sizeof(Pointer));
+ else
+ This = stackRef<Pointer>(0);
+ }
+}
+
InterpFrame::~InterpFrame() {
- if (Func && Func->isConstructor() && This.isBaseClass())
- This.initialize();
for (auto &Param : Params)
S.deallocate(reinterpret_cast<Block *>(Param.second.get()));
}
@@ -95,17 +126,17 @@ void print(llvm::raw_ostream &OS, const Pointer &P, ASTContext &Ctx,
}
printDesc(P.getDeclDesc());
- for (auto It = Levels.rbegin(); It != Levels.rend(); ++It) {
- if (It->inArray()) {
- OS << "[" << It->expand().getIndex() << "]";
+ for (const auto &It : Levels) {
+ if (It.inArray()) {
+ OS << "[" << It.expand().getIndex() << "]";
continue;
}
- if (auto Index = It->getIndex()) {
+ if (auto Index = It.getIndex()) {
OS << " + " << Index;
continue;
}
OS << ".";
- printDesc(It->getFieldDesc());
+ printDesc(It.getFieldDesc());
}
}
@@ -117,16 +148,15 @@ void InterpFrame::describe(llvm::raw_ostream &OS) {
OS << "->";
}
OS << *F << "(";
- unsigned Off = Func->hasRVO() ? primSize(PT_Ptr) : 0;
+ unsigned Off = 0;
+
+ Off += Func->hasRVO() ? primSize(PT_Ptr) : 0;
+ Off += Func->hasThisPointer() ? primSize(PT_Ptr) : 0;
+
for (unsigned I = 0, N = F->getNumParams(); I < N; ++I) {
QualType Ty = F->getParamDecl(I)->getType();
- PrimType PrimTy;
- if (llvm::Optional<PrimType> T = S.Ctx.classify(Ty)) {
- PrimTy = *T;
- } else {
- PrimTy = PT_Ptr;
- }
+ PrimType PrimTy = S.Ctx.classify(Ty).value_or(PT_Ptr);
TYPE_SWITCH(PrimTy, print(OS, stackRef<T>(Off), S.getCtx(), Ty));
Off += align(primSize(PrimTy));
@@ -152,10 +182,10 @@ const FunctionDecl *InterpFrame::getCallee() const {
return Func->getDecl();
}
-Pointer InterpFrame::getLocalPointer(unsigned Offset) {
+Pointer InterpFrame::getLocalPointer(unsigned Offset) const {
assert(Offset < Func->getFrameSize() && "Invalid local offset.");
- return Pointer(
- reinterpret_cast<Block *>(Locals.get() + Offset - sizeof(Block)));
+ return Pointer(reinterpret_cast<Block *>(localBlock(Offset)),
+ sizeof(InlineDescriptor));
}
Pointer InterpFrame::getParamPointer(unsigned Off) {
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.h b/contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.h
index 304e2ad66537..bfa02c90ebec 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/InterpFrame.h
@@ -14,7 +14,6 @@
#define LLVM_CLANG_AST_INTERP_INTERPFRAME_H
#include "Frame.h"
-#include "Pointer.h"
#include "Program.h"
#include "State.h"
#include <cstdint>
@@ -24,6 +23,7 @@ namespace clang {
namespace interp {
class Function;
class InterpState;
+class Pointer;
/// Frame storing local variables.
class InterpFrame final : public Frame {
@@ -32,8 +32,14 @@ public:
InterpFrame *Caller;
/// Creates a new frame for a method call.
- InterpFrame(InterpState &S, Function *Func, InterpFrame *Caller,
- CodePtr RetPC, Pointer &&This);
+ InterpFrame(InterpState &S, const Function *Func, InterpFrame *Caller,
+ CodePtr RetPC);
+
+ /// Creates a new frame with the values that make sense.
+ /// I.e., the caller is the current frame of S,
+ /// the This() pointer is the current Pointer on the top of S's stack,
+ /// and the RVO pointer is before that.
+ InterpFrame(InterpState &S, const Function *Func, CodePtr RetPC);
/// Destroys the frame, killing all live pointers to stack slots.
~InterpFrame();
@@ -57,26 +63,27 @@ public:
const FunctionDecl *getCallee() const override;
/// Returns the current function.
- Function *getFunction() const { return Func; }
+ const Function *getFunction() const { return Func; }
/// Returns the offset on the stack at which the frame starts.
size_t getFrameOffset() const { return FrameOffset; }
/// Returns the value of a local variable.
- template <typename T> const T &getLocal(unsigned Offset) {
+ template <typename T> const T &getLocal(unsigned Offset) const {
return localRef<T>(Offset);
}
/// Mutates a local variable.
template <typename T> void setLocal(unsigned Offset, const T &Value) {
localRef<T>(Offset) = Value;
+ localInlineDesc(Offset)->IsInitialized = true;
}
/// Returns a pointer to a local variables.
- Pointer getLocalPointer(unsigned Offset);
+ Pointer getLocalPointer(unsigned Offset) const;
/// Returns the value of an argument.
- template <typename T> const T &getParam(unsigned Offset) {
+ template <typename T> const T &getParam(unsigned Offset) const {
auto Pt = Params.find(Offset);
if (Pt == Params.end()) {
return stackRef<T>(Offset);
@@ -96,6 +103,9 @@ public:
/// Returns the 'this' pointer.
const Pointer &getThis() const { return This; }
+ /// Returns the RVO pointer, if the Function has one.
+ const Pointer &getRVOPtr() const { return RVOPtr; }
+
/// Checks if the frame is a root frame - return should quit the interpreter.
bool isRoot() const { return !Func; }
@@ -112,27 +122,35 @@ public:
private:
/// Returns an original argument from the stack.
- template <typename T> const T &stackRef(unsigned Offset) {
+ template <typename T> const T &stackRef(unsigned Offset) const {
+ assert(Args);
return *reinterpret_cast<const T *>(Args - ArgSize + Offset);
}
/// Returns an offset to a local.
- template <typename T> T &localRef(unsigned Offset) {
- return *reinterpret_cast<T *>(Locals.get() + Offset);
+ template <typename T> T &localRef(unsigned Offset) const {
+ return getLocalPointer(Offset).deref<T>();
}
/// Returns a pointer to a local's block.
- void *localBlock(unsigned Offset) {
+ void *localBlock(unsigned Offset) const {
return Locals.get() + Offset - sizeof(Block);
}
+ // Returns the inline descriptor of the local.
+ InlineDescriptor *localInlineDesc(unsigned Offset) const {
+ return reinterpret_cast<InlineDescriptor *>(Locals.get() + Offset);
+ }
+
private:
/// Reference to the interpreter state.
InterpState &S;
/// Reference to the function being executed.
- Function *Func;
+ const Function *Func;
/// Current object pointer for methods.
Pointer This;
+ /// Pointer the non-primitive return value gets constructed in.
+ Pointer RVOPtr;
/// Return address.
CodePtr RetPC;
/// The size of all the arguments.
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/InterpStack.cpp b/contrib/llvm-project/clang/lib/AST/Interp/InterpStack.cpp
index 5c803f3d9424..7fe678e62192 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/InterpStack.cpp
+++ b/contrib/llvm-project/clang/lib/AST/Interp/InterpStack.cpp
@@ -46,7 +46,7 @@ void *InterpStack::grow(size_t Size) {
return Object;
}
-void *InterpStack::peek(size_t Size) {
+void *InterpStack::peek(size_t Size) const {
assert(Chunk && "Stack is empty!");
StackChunk *Ptr = Chunk;
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/InterpStack.h b/contrib/llvm-project/clang/lib/AST/Interp/InterpStack.h
index b02d3c6a34b0..3adaad96515e 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/InterpStack.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/InterpStack.h
@@ -13,7 +13,9 @@
#ifndef LLVM_CLANG_AST_INTERP_INTERPSTACK_H
#define LLVM_CLANG_AST_INTERP_INTERPSTACK_H
+#include "PrimType.h"
#include <memory>
+#include <vector>
namespace clang {
namespace interp {
@@ -29,10 +31,18 @@ public:
/// Constructs a value in place on the top of the stack.
template <typename T, typename... Tys> void push(Tys &&... Args) {
new (grow(aligned_size<T>())) T(std::forward<Tys>(Args)...);
+#ifndef NDEBUG
+ ItemTypes.push_back(toPrimType<T>());
+#endif
}
/// Returns the value from the top of the stack and removes it.
template <typename T> T pop() {
+#ifndef NDEBUG
+ assert(!ItemTypes.empty());
+ assert(ItemTypes.back() == toPrimType<T>());
+ ItemTypes.pop_back();
+#endif
auto *Ptr = &peek<T>();
auto Value = std::move(*Ptr);
Ptr->~T();
@@ -42,18 +52,22 @@ public:
/// Discards the top value from the stack.
template <typename T> void discard() {
+#ifndef NDEBUG
+ assert(ItemTypes.back() == toPrimType<T>());
+ ItemTypes.pop_back();
+#endif
auto *Ptr = &peek<T>();
Ptr->~T();
shrink(aligned_size<T>());
}
/// Returns a reference to the value on the top of the stack.
- template <typename T> T &peek() {
+ template <typename T> T &peek() const {
return *reinterpret_cast<T *>(peek(aligned_size<T>()));
}
/// Returns a pointer to the top object.
- void *top() { return Chunk ? peek(0) : nullptr; }
+ void *top() const { return Chunk ? peek(0) : nullptr; }
/// Returns the size of the stack in bytes.
size_t size() const { return StackSize; }
@@ -61,6 +75,9 @@ public:
/// Clears the stack without calling any destructors.
void clear();
+ // Returns whether the stack is empty.
+ bool empty() const { return StackSize == 0; }
+
private:
/// All stack slots are aligned to the native pointer alignment for storage.
/// The size of an object is rounded up to a pointer alignment multiple.
@@ -72,7 +89,7 @@ private:
/// Grows the stack to accommodate a value and returns a pointer to it.
void *grow(size_t Size);
/// Returns a pointer from the top of the stack.
- void *peek(size_t Size);
+ void *peek(size_t Size) const;
/// Shrinks the stack.
void shrink(size_t Size);
@@ -94,10 +111,13 @@ private:
: Next(nullptr), Prev(Prev), End(reinterpret_cast<char *>(this + 1)) {}
/// Returns the size of the chunk, minus the header.
- size_t size() { return End - start(); }
+ size_t size() const { return End - start(); }
/// Returns a pointer to the start of the data region.
char *start() { return reinterpret_cast<char *>(this + 1); }
+ const char *start() const {
+ return reinterpret_cast<const char *>(this + 1);
+ }
};
static_assert(sizeof(StackChunk) < ChunkSize, "Invalid chunk size");
@@ -105,6 +125,45 @@ private:
StackChunk *Chunk = nullptr;
/// Total size of the stack.
size_t StackSize = 0;
+
+#ifndef NDEBUG
+ /// vector recording the type of data we pushed into the stack.
+ std::vector<PrimType> ItemTypes;
+
+ template <typename T> static constexpr PrimType toPrimType() {
+ if constexpr (std::is_same_v<T, Pointer>)
+ return PT_Ptr;
+ else if constexpr (std::is_same_v<T, bool> ||
+ std::is_same_v<T, Boolean>)
+ return PT_Bool;
+ else if constexpr (std::is_same_v<T, int8_t> ||
+ std::is_same_v<T, Integral<8, true>>)
+ return PT_Sint8;
+ else if constexpr (std::is_same_v<T, uint8_t> ||
+ std::is_same_v<T, Integral<8, false>>)
+ return PT_Uint8;
+ else if constexpr (std::is_same_v<T, int16_t> ||
+ std::is_same_v<T, Integral<16, true>>)
+ return PT_Sint16;
+ else if constexpr (std::is_same_v<T, uint16_t> ||
+ std::is_same_v<T, Integral<16, false>>)
+ return PT_Uint16;
+ else if constexpr (std::is_same_v<T, int32_t> ||
+ std::is_same_v<T, Integral<32, true>>)
+ return PT_Sint32;
+ else if constexpr (std::is_same_v<T, uint32_t> ||
+ std::is_same_v<T, Integral<32, false>>)
+ return PT_Uint32;
+ else if constexpr (std::is_same_v<T, int64_t> ||
+ std::is_same_v<T, Integral<64, true>>)
+ return PT_Sint64;
+ else if constexpr (std::is_same_v<T, uint64_t> ||
+ std::is_same_v<T, Integral<64, false>>)
+ return PT_Uint64;
+
+ llvm_unreachable("unknown type push()'ed into InterpStack");
+ }
+#endif
};
} // namespace interp
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/InterpState.h b/contrib/llvm-project/clang/lib/AST/Interp/InterpState.h
index 57e36c4c63ea..033080637385 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/InterpState.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/InterpState.h
@@ -65,6 +65,7 @@ public:
bool noteUndefinedBehavior() override {
return Parent.noteUndefinedBehavior();
}
+ bool inConstantContext() const { return Parent.InConstantContext; }
bool hasActiveDiagnostic() override { return Parent.hasActiveDiagnostic(); }
void setActiveDiagnostic(bool Flag) override {
Parent.setActiveDiagnostic(Flag);
@@ -81,7 +82,7 @@ public:
void deallocate(Block *B);
/// Delegates source mapping to the mapper.
- SourceInfo getSource(Function *F, CodePtr PC) const override {
+ SourceInfo getSource(const Function *F, CodePtr PC) const override {
return M ? M->getSource(F, PC) : F->getSource(PC);
}
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Opcodes.td b/contrib/llvm-project/clang/lib/AST/Interp/Opcodes.td
index 638d5b3d2357..058475b2d399 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Opcodes.td
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Opcodes.td
@@ -42,18 +42,8 @@ def ArgSint64 : ArgType { let Name = "int64_t"; }
def ArgUint64 : ArgType { let Name = "uint64_t"; }
def ArgBool : ArgType { let Name = "bool"; }
-def ArgFunction : ArgType { let Name = "Function *"; }
-def ArgRecord : ArgType { let Name = "Record *"; }
-
-def ArgSema : ArgType { let Name = "const fltSemantics *"; }
-
-def ArgExpr : ArgType { let Name = "const Expr *"; }
-def ArgFloatingLiteral : ArgType { let Name = "const FloatingLiteral *"; }
-def ArgCXXMethodDecl : ArgType { let Name = "const CXXMethodDecl *"; }
-def ArgFunctionDecl : ArgType { let Name = "const FunctionDecl *"; }
+def ArgFunction : ArgType { let Name = "const Function *"; }
def ArgRecordDecl : ArgType { let Name = "const RecordDecl *"; }
-def ArgCXXRecordDecl : ArgType { let Name = "const CXXRecordDecl *"; }
-def ArgValueDecl : ArgType { let Name = "const ValueDecl *"; }
def ArgRecordField : ArgType { let Name = "const Record::Field *"; }
//===----------------------------------------------------------------------===//
@@ -64,15 +54,28 @@ class TypeClass {
list<Type> Types;
}
-def AluTypeClass : TypeClass {
+def NumberTypeClass : TypeClass {
+ let Types = [Sint8, Uint8, Sint16, Uint16, Sint32,
+ Uint32, Sint64, Uint64];
+}
+
+def IntegerTypeClass : TypeClass {
let Types = [Sint8, Uint8, Sint16, Uint16, Sint32,
- Uint32, Sint64, Uint64, Bool];
+ Uint32, Sint64, Uint64];
+}
+
+def AluTypeClass : TypeClass {
+ let Types = !listconcat(NumberTypeClass.Types, [Bool]);
}
def PtrTypeClass : TypeClass {
let Types = [Ptr];
}
+def BoolTypeClass : TypeClass {
+ let Types = [Bool];
+}
+
def AllTypeClass : TypeClass {
let Types = !listconcat(AluTypeClass.Types, PtrTypeClass.Types);
}
@@ -105,6 +108,11 @@ class AluOpcode : Opcode {
let HasGroup = 1;
}
+class IntegerOpcode : Opcode {
+ let Types = [IntegerTypeClass];
+ let HasGroup = 1;
+}
+
//===----------------------------------------------------------------------===//
// Jump opcodes
//===----------------------------------------------------------------------===//
@@ -149,6 +157,13 @@ def RetValue : Opcode {
// [] -> EXIT
def NoRet : Opcode {}
+
+def Call : Opcode {
+ let Args = [ArgFunction];
+ let Types = [];
+ let ChangesPC = 1;
+}
+
//===----------------------------------------------------------------------===//
// Frame management
//===----------------------------------------------------------------------===//
@@ -183,6 +198,7 @@ def ConstBool : ConstOpcode<Bool, ArgBool>;
// [] -> [Integer]
def Zero : Opcode {
let Types = [AluTypeClass];
+ let HasGroup = 1;
}
// [] -> [Pointer]
@@ -253,6 +269,9 @@ def GetPtrThisVirtBase : Opcode {
// [] -> [Pointer]
def This : Opcode;
+// [] -> [Pointer]
+def RVOPtr : Opcode;
+
// [Pointer] -> [Pointer]
def NarrowPtr : Opcode;
// [Pointer] -> [Pointer]
@@ -374,6 +393,12 @@ def AddOffset : AluOpcode;
// [Pointer, Integral] -> [Pointer]
def SubOffset : AluOpcode;
+// Pointer, Pointer] - [Integral]
+def SubPtr : Opcode {
+ let Types = [IntegerTypeClass];
+ let HasGroup = 1;
+}
+
//===----------------------------------------------------------------------===//
// Binary operators.
//===----------------------------------------------------------------------===//
@@ -382,6 +407,73 @@ def SubOffset : AluOpcode;
def Sub : AluOpcode;
def Add : AluOpcode;
def Mul : AluOpcode;
+def Rem : Opcode {
+ let Types = [NumberTypeClass];
+ let HasGroup = 1;
+}
+
+def Shl : Opcode {
+ let Types = [IntegerTypeClass, IntegerTypeClass];
+ let HasGroup = 1;
+}
+
+def Shr : Opcode {
+ let Types = [IntegerTypeClass, IntegerTypeClass];
+ let HasGroup = 1;
+}
+
+def BitAnd : IntegerOpcode;
+def BitOr : IntegerOpcode;
+def Div : Opcode {
+ let Types = [NumberTypeClass];
+ let HasGroup = 1;
+}
+def BitXor : IntegerOpcode;
+
+//===----------------------------------------------------------------------===//
+// Unary operators.
+//===----------------------------------------------------------------------===//
+
+// [Real] -> [Real]
+def Inv: Opcode {
+ let Types = [BoolTypeClass];
+ let HasGroup = 1;
+}
+
+def Inc: IntegerOpcode;
+def IncPop : IntegerOpcode;
+def Dec: IntegerOpcode;
+def DecPop: IntegerOpcode;
+
+// [Real] -> [Real]
+def Neg: Opcode {
+ let Types = [AluTypeClass];
+ let HasGroup = 1;
+}
+
+// [Real] -> [Real]
+def Comp: Opcode {
+ let Types = [NumberTypeClass];
+ let HasGroup = 1;
+}
+
+//===----------------------------------------------------------------------===//
+// Cast.
+//===----------------------------------------------------------------------===//
+// TODO: Expand this to handle casts between more types.
+
+def FromCastTypeClass : TypeClass {
+ let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool];
+}
+
+def ToCastTypeClass : TypeClass {
+ let Types = [Uint8, Sint8, Uint16, Sint16, Uint32, Sint32, Uint64, Sint64, Bool];
+}
+
+def Cast: Opcode {
+ let Types = [FromCastTypeClass, ToCastTypeClass];
+ let HasGroup = 1;
+}
//===----------------------------------------------------------------------===//
// Comparison opcodes.
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp
index ef2638e2a36b..fd8c98fae039 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Pointer.cpp
@@ -16,6 +16,9 @@ using namespace clang::interp;
Pointer::Pointer(Block *Pointee) : Pointer(Pointee, 0, 0) {}
+Pointer::Pointer(Block *Pointee, unsigned BaseAndOffset)
+ : Pointer(Pointee, BaseAndOffset, BaseAndOffset) {}
+
Pointer::Pointer(const Pointer &P) : Pointer(P.Pointee, P.Base, P.Offset) {}
Pointer::Pointer(Pointer &&P)
@@ -106,7 +109,7 @@ APValue Pointer::toAPValue() const {
// Build the path into the object.
Pointer Ptr = *this;
- while (Ptr.isField()) {
+ while (Ptr.isField() || Ptr.isArrayElement()) {
if (Ptr.isArrayElement()) {
Path.push_back(APValue::LValuePathEntry::ArrayIndex(Ptr.getIndex()));
Ptr = Ptr.getArray();
@@ -129,14 +132,21 @@ APValue Pointer::toAPValue() const {
}
}
+ // We assemble the LValuePath starting from the innermost pointer to the
+ // outermost one. SO in a.b.c, the first element in Path will refer to
+ // the field 'c', while later code expects it to refer to 'a'.
+ // Just invert the order of the elements.
+ std::reverse(Path.begin(), Path.end());
+
return APValue(Base, Offset, Path, IsOnePastEnd, IsNullPtr);
}
bool Pointer::isInitialized() const {
assert(Pointee && "Cannot check if null pointer was initialized");
Descriptor *Desc = getFieldDesc();
+ assert(Desc);
if (Desc->isPrimitiveArray()) {
- if (Pointee->IsStatic)
+ if (isStatic() && Base == 0)
return true;
// Primitive array field are stored in a bitset.
InitMap *Map = getInitMap();
@@ -154,8 +164,14 @@ bool Pointer::isInitialized() const {
void Pointer::initialize() const {
assert(Pointee && "Cannot initialize null pointer");
Descriptor *Desc = getFieldDesc();
- if (Desc->isPrimitiveArray()) {
- if (!Pointee->IsStatic) {
+
+ assert(Desc);
+ if (Desc->isArray()) {
+ if (Desc->isPrimitiveArray()) {
+ // Primitive global arrays don't have an initmap.
+ if (isStatic() && Base == 0)
+ return;
+
// Primitive array initializer.
InitMap *&Map = getInitMap();
if (Map == (InitMap *)-1)
@@ -189,5 +205,5 @@ bool Pointer::hasSameBase(const Pointer &A, const Pointer &B) {
}
bool Pointer::hasSameArray(const Pointer &A, const Pointer &B) {
- return A.Base == B.Base && A.getFieldDesc()->IsArray;
+ return hasSameBase(A, B) && A.Base == B.Base && A.getFieldDesc()->IsArray;
}
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Pointer.h b/contrib/llvm-project/clang/lib/AST/Interp/Pointer.h
index 587531aec82a..1462d01c2412 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Pointer.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Pointer.h
@@ -33,14 +33,40 @@ enum PrimType : unsigned;
///
/// This object can be allocated into interpreter stack frames. If pointing to
/// a live block, it is a link in the chain of pointers pointing to the block.
+///
+/// In the simplest form, a Pointer has a Block* (the pointee) and both Base
+/// and Offset are 0, which means it will point to raw data.
+///
+/// The Base field is used to access metadata about the data. For primitive
+/// arrays, the Base is followed by an InitMap. In a variety of cases, the
+/// Base is preceded by an InlineDescriptor, which is used to track the
+/// initialization state, among other things.
+///
+/// The Offset field is used to access the actual data. In other words, the
+/// data the pointer decribes can be found at
+/// Pointee->rawData() + Pointer.Offset.
+///
+///
+/// Pointee Offset
+/// │ │
+/// │ │
+/// ▼ ▼
+/// ┌───────┬────────────┬─────────┬────────────────────────────┐
+/// │ Block │ InlineDesc │ InitMap │ Actual Data │
+/// └───────┴────────────┴─────────┴────────────────────────────┘
+/// ▲
+/// │
+/// │
+/// Base
class Pointer {
private:
- static constexpr unsigned PastEndMark = (unsigned)-1;
- static constexpr unsigned RootPtrMark = (unsigned)-1;
+ static constexpr unsigned PastEndMark = ~0u;
+ static constexpr unsigned RootPtrMark = ~0u;
public:
Pointer() {}
Pointer(Block *B);
+ Pointer(Block *B, unsigned BaseAndOffset);
Pointer(const Pointer &P);
Pointer(Pointer &&P);
~Pointer();
@@ -216,6 +242,8 @@ public:
/// Returns the record descriptor of a class.
Record *getRecord() const { return getFieldDesc()->ElemRecord; }
+ // Returns the element record type, if this is a non-primive array.
+ Record *getElemRecord() const { return getFieldDesc()->ElemDesc->ElemRecord; }
/// Returns the field information.
const FieldDecl *getField() const { return getFieldDesc()->asFieldDecl(); }
@@ -232,7 +260,9 @@ public:
bool isStaticTemporary() const { return isStatic() && isTemporary(); }
/// Checks if the field is mutable.
- bool isMutable() const { return Base != 0 && getInlineDesc()->IsMutable; }
+ bool isMutable() const {
+ return Base != 0 && getInlineDesc()->IsFieldMutable;
+ }
/// Checks if an object was initialized.
bool isInitialized() const;
/// Checks if the object is active.
@@ -246,7 +276,7 @@ public:
}
/// Returns the declaration ID.
- llvm::Optional<unsigned> getDeclID() const { return Pointee->getDeclID(); }
+ std::optional<unsigned> getDeclID() const { return Pointee->getDeclID(); }
/// Returns the byte offset from the start.
unsigned getByteOffset() const {
@@ -276,12 +306,12 @@ public:
/// Dereferences the pointer, if it's live.
template <typename T> T &deref() const {
assert(isLive() && "Invalid pointer");
- return *reinterpret_cast<T *>(Pointee->data() + Offset);
+ return *reinterpret_cast<T *>(Pointee->rawData() + Offset);
}
/// Dereferences a primitive element.
template <typename T> T &elem(unsigned I) const {
- return reinterpret_cast<T *>(Pointee->data())[I];
+ return reinterpret_cast<T *>(Pointee->rawData())[I];
}
/// Initializes a field.
@@ -298,7 +328,7 @@ public:
/// Prints the pointer.
void print(llvm::raw_ostream &OS) const {
- OS << "{" << Base << ", " << Offset << ", ";
+ OS << Pointee << " {" << Base << ", " << Offset << ", ";
if (Pointee)
OS << Pointee->getSize();
else
@@ -318,12 +348,13 @@ private:
/// Returns a descriptor at a given offset.
InlineDescriptor *getDescriptor(unsigned Offset) const {
assert(Offset != 0 && "Not a nested pointer");
- return reinterpret_cast<InlineDescriptor *>(Pointee->data() + Offset) - 1;
+ return reinterpret_cast<InlineDescriptor *>(Pointee->rawData() + Offset) -
+ 1;
}
/// Returns a reference to the pointer which stores the initialization map.
InitMap *&getInitMap() const {
- return *reinterpret_cast<InitMap **>(Pointee->data() + Base);
+ return *reinterpret_cast<InitMap **>(Pointee->rawData() + Base);
}
/// The block the pointer is pointing to.
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/PrimType.cpp b/contrib/llvm-project/clang/lib/AST/Interp/PrimType.cpp
index 082bfaf3c207..eda90e1c36c2 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/PrimType.cpp
+++ b/contrib/llvm-project/clang/lib/AST/Interp/PrimType.cpp
@@ -1,4 +1,4 @@
-//===--- Type.cpp - Types for the constexpr VM ------------------*- C++ -*-===//
+//===--- PrimType.cpp - Types for the constexpr VM --------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
@@ -7,6 +7,8 @@
//===----------------------------------------------------------------------===//
#include "PrimType.h"
+#include "Boolean.h"
+#include "Pointer.h"
using namespace clang;
using namespace clang::interp;
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/PrimType.h b/contrib/llvm-project/clang/lib/AST/Interp/PrimType.h
index de4bf9bf802e..c8f2a600fb3c 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/PrimType.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/PrimType.h
@@ -13,16 +13,17 @@
#ifndef LLVM_CLANG_AST_INTERP_TYPE_H
#define LLVM_CLANG_AST_INTERP_TYPE_H
+#include "Integral.h"
#include <climits>
#include <cstddef>
#include <cstdint>
-#include "Boolean.h"
-#include "Integral.h"
-#include "Pointer.h"
namespace clang {
namespace interp {
+class Pointer;
+class Boolean;
+
/// Enumeration of the primitive types of the VM.
enum PrimType : unsigned {
PT_Sint8,
@@ -58,6 +59,13 @@ constexpr size_t align(size_t Size) {
return ((Size + alignof(void *) - 1) / alignof(void *)) * alignof(void *);
}
+constexpr bool aligned(uintptr_t Value) { return Value == align(Value); }
+static_assert(aligned(sizeof(void *)));
+
+static inline bool aligned(const void *P) {
+ return aligned(reinterpret_cast<uintptr_t>(P));
+}
+
inline bool isPrimitiveIntegral(PrimType Type) {
switch (Type) {
case PT_Bool:
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Program.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Program.cpp
index e310c9678140..5305ddd8de18 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Program.cpp
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Program.cpp
@@ -53,10 +53,11 @@ unsigned Program::createGlobalString(const StringLiteral *S) {
}
// Create a descriptor for the string.
- Descriptor *Desc = allocateDescriptor(S, CharType, S->getLength() + 1,
- /*isConst=*/true,
- /*isTemporary=*/false,
- /*isMutable=*/false);
+ Descriptor *Desc =
+ allocateDescriptor(S, CharType, std::nullopt, S->getLength() + 1,
+ /*isConst=*/true,
+ /*isTemporary=*/false,
+ /*isMutable=*/false);
// Allocate storage for the string.
// The byte length does not include the null terminator.
@@ -64,6 +65,7 @@ unsigned Program::createGlobalString(const StringLiteral *S) {
unsigned Sz = Desc->getAllocSize();
auto *G = new (Allocator, Sz) Global(Desc, /*isStatic=*/true,
/*isExtern=*/false);
+ G->block()->invokeCtor();
Globals.push_back(G);
// Construct the string in storage.
@@ -99,13 +101,13 @@ Pointer Program::getPtrGlobal(unsigned Idx) {
return Pointer(Globals[Idx]->block());
}
-llvm::Optional<unsigned> Program::getGlobal(const ValueDecl *VD) {
+std::optional<unsigned> Program::getGlobal(const ValueDecl *VD) {
auto It = GlobalIndices.find(VD);
if (It != GlobalIndices.end())
return It->second;
// Find any previous declarations which were already evaluated.
- llvm::Optional<unsigned> Index;
+ std::optional<unsigned> Index;
for (const Decl *P = VD; P; P = P->getPreviousDecl()) {
auto It = GlobalIndices.find(P);
if (It != GlobalIndices.end()) {
@@ -123,18 +125,19 @@ llvm::Optional<unsigned> Program::getGlobal(const ValueDecl *VD) {
return Index;
}
-llvm::Optional<unsigned> Program::getOrCreateGlobal(const ValueDecl *VD) {
+std::optional<unsigned> Program::getOrCreateGlobal(const ValueDecl *VD,
+ const Expr *Init) {
if (auto Idx = getGlobal(VD))
return Idx;
- if (auto Idx = createGlobal(VD)) {
+ if (auto Idx = createGlobal(VD, Init)) {
GlobalIndices[VD] = *Idx;
return Idx;
}
return {};
}
-llvm::Optional<unsigned> Program::getOrCreateDummy(const ParmVarDecl *PD) {
+std::optional<unsigned> Program::getOrCreateDummy(const ParmVarDecl *PD) {
auto &ASTCtx = Ctx.getASTContext();
// Create a pointer to an incomplete array of the specified elements.
@@ -153,7 +156,9 @@ llvm::Optional<unsigned> Program::getOrCreateDummy(const ParmVarDecl *PD) {
return {};
}
-llvm::Optional<unsigned> Program::createGlobal(const ValueDecl *VD) {
+std::optional<unsigned> Program::createGlobal(const ValueDecl *VD,
+ const Expr *Init) {
+ assert(!getGlobal(VD));
bool IsStatic, IsExtern;
if (auto *Var = dyn_cast<VarDecl>(VD)) {
IsStatic = !Var->hasLocalStorage();
@@ -162,7 +167,7 @@ llvm::Optional<unsigned> Program::createGlobal(const ValueDecl *VD) {
IsStatic = false;
IsExtern = true;
}
- if (auto Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern)) {
+ if (auto Idx = createGlobal(VD, VD->getType(), IsStatic, IsExtern, Init)) {
for (const Decl *P = VD; P; P = P->getPreviousDecl())
GlobalIndices[P] = *Idx;
return *Idx;
@@ -170,20 +175,22 @@ llvm::Optional<unsigned> Program::createGlobal(const ValueDecl *VD) {
return {};
}
-llvm::Optional<unsigned> Program::createGlobal(const Expr *E) {
+std::optional<unsigned> Program::createGlobal(const Expr *E) {
return createGlobal(E, E->getType(), /*isStatic=*/true, /*isExtern=*/false);
}
-llvm::Optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
- bool IsStatic, bool IsExtern) {
+std::optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
+ bool IsStatic, bool IsExtern,
+ const Expr *Init) {
// Create a descriptor for the global.
Descriptor *Desc;
const bool IsConst = Ty.isConstQualified();
const bool IsTemporary = D.dyn_cast<const Expr *>();
if (auto T = Ctx.classify(Ty)) {
- Desc = createDescriptor(D, *T, IsConst, IsTemporary);
+ Desc = createDescriptor(D, *T, std::nullopt, IsConst, IsTemporary);
} else {
- Desc = createDescriptor(D, Ty.getTypePtr(), IsConst, IsTemporary);
+ Desc = createDescriptor(D, Ty.getTypePtr(), std::nullopt, IsConst,
+ IsTemporary);
}
if (!Desc)
return {};
@@ -201,24 +208,12 @@ llvm::Optional<unsigned> Program::createGlobal(const DeclTy &D, QualType Ty,
}
Function *Program::getFunction(const FunctionDecl *F) {
- F = F->getDefinition();
+ F = F->getCanonicalDecl();
+ assert(F);
auto It = Funcs.find(F);
return It == Funcs.end() ? nullptr : It->second.get();
}
-llvm::Expected<Function *> Program::getOrCreateFunction(const FunctionDecl *F) {
- if (Function *Func = getFunction(F)) {
- return Func;
- }
-
- // Try to compile the function if it wasn't compiled yet.
- if (const FunctionDecl *FD = F->getDefinition())
- return ByteCodeStmtGen<ByteCodeEmitter>(Ctx, *this).compileFunc(FD);
-
- // A relocation which traps if not resolved.
- return nullptr;
-}
-
Record *Program::getOrCreateRecord(const RecordDecl *RD) {
// Use the actual definition as a key.
RD = RD->getDefinition();
@@ -231,8 +226,13 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) {
return It->second;
}
+ // We insert nullptr now and replace that later, so recursive calls
+ // to this function with the same RecordDecl don't run into
+ // infinite recursion.
+ Records.insert({RD, nullptr});
+
// Number of bytes required by fields and base classes.
- unsigned Size = 0;
+ unsigned BaseSize = 0;
// Number of bytes required by virtual base.
unsigned VirtSize = 0;
@@ -240,7 +240,7 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) {
auto GetBaseDesc = [this](const RecordDecl *BD, Record *BR) -> Descriptor * {
if (!BR)
return nullptr;
- return allocateDescriptor(BD, BR, /*isConst=*/false,
+ return allocateDescriptor(BD, BR, std::nullopt, /*isConst=*/false,
/*isTemporary=*/false,
/*isMutable=*/false);
};
@@ -256,9 +256,9 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) {
const RecordDecl *BD = Spec.getType()->castAs<RecordType>()->getDecl();
Record *BR = getOrCreateRecord(BD);
if (Descriptor *Desc = GetBaseDesc(BD, BR)) {
- Size += align(sizeof(InlineDescriptor));
- Bases.push_back({BD, Size, Desc, BR});
- Size += align(BR->getSize());
+ BaseSize += align(sizeof(InlineDescriptor));
+ Bases.push_back({BD, BaseSize, Desc, BR});
+ BaseSize += align(BR->getSize());
continue;
}
return nullptr;
@@ -282,39 +282,41 @@ Record *Program::getOrCreateRecord(const RecordDecl *RD) {
Record::FieldList Fields;
for (const FieldDecl *FD : RD->fields()) {
// Reserve space for the field's descriptor and the offset.
- Size += align(sizeof(InlineDescriptor));
+ BaseSize += align(sizeof(InlineDescriptor));
// Classify the field and add its metadata.
QualType FT = FD->getType();
const bool IsConst = FT.isConstQualified();
const bool IsMutable = FD->isMutable();
Descriptor *Desc;
- if (llvm::Optional<PrimType> T = Ctx.classify(FT)) {
- Desc = createDescriptor(FD, *T, IsConst, /*isTemporary=*/false,
- IsMutable);
+ if (std::optional<PrimType> T = Ctx.classify(FT)) {
+ Desc = createDescriptor(FD, *T, std::nullopt, IsConst,
+ /*isTemporary=*/false, IsMutable);
} else {
- Desc = createDescriptor(FD, FT.getTypePtr(), IsConst,
+ Desc = createDescriptor(FD, FT.getTypePtr(), std::nullopt, IsConst,
/*isTemporary=*/false, IsMutable);
}
if (!Desc)
return nullptr;
- Fields.push_back({FD, Size, Desc});
- Size += align(Desc->getAllocSize());
+ Fields.push_back({FD, BaseSize, Desc});
+ BaseSize += align(Desc->getAllocSize());
}
Record *R = new (Allocator) Record(RD, std::move(Bases), std::move(Fields),
- std::move(VirtBases), VirtSize, Size);
- Records.insert({RD, R});
+ std::move(VirtBases), VirtSize, BaseSize);
+ Records[RD] = R;
return R;
}
Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
+ Descriptor::MetadataSize MDSize,
bool IsConst, bool IsTemporary,
- bool IsMutable) {
+ bool IsMutable, const Expr *Init) {
// Classes and structures.
if (auto *RT = Ty->getAs<RecordType>()) {
if (auto *Record = getOrCreateRecord(RT->getDecl()))
- return allocateDescriptor(D, Record, IsConst, IsTemporary, IsMutable);
+ return allocateDescriptor(D, Record, MDSize, IsConst, IsTemporary,
+ IsMutable);
}
// Arrays.
@@ -323,38 +325,39 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
// Array of well-known bounds.
if (auto CAT = dyn_cast<ConstantArrayType>(ArrayType)) {
size_t NumElems = CAT->getSize().getZExtValue();
- if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) {
+ if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
// Arrays of primitives.
unsigned ElemSize = primSize(*T);
if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems) {
return {};
}
- return allocateDescriptor(D, *T, NumElems, IsConst, IsTemporary,
+ return allocateDescriptor(D, *T, MDSize, NumElems, IsConst, IsTemporary,
IsMutable);
} else {
// Arrays of composites. In this case, the array is a list of pointers,
// followed by the actual elements.
- Descriptor *Desc =
- createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary);
- if (!Desc)
+ Descriptor *ElemDesc = createDescriptor(
+ D, ElemTy.getTypePtr(), std::nullopt, IsConst, IsTemporary);
+ if (!ElemDesc)
return nullptr;
- InterpSize ElemSize = Desc->getAllocSize() + sizeof(InlineDescriptor);
+ InterpSize ElemSize =
+ ElemDesc->getAllocSize() + sizeof(InlineDescriptor);
if (std::numeric_limits<unsigned>::max() / ElemSize <= NumElems)
return {};
- return allocateDescriptor(D, Desc, NumElems, IsConst, IsTemporary,
- IsMutable);
+ return allocateDescriptor(D, ElemDesc, MDSize, NumElems, IsConst,
+ IsTemporary, IsMutable);
}
}
// Array of unknown bounds - cannot be accessed and pointer arithmetic
// is forbidden on pointers to such objects.
if (isa<IncompleteArrayType>(ArrayType)) {
- if (llvm::Optional<PrimType> T = Ctx.classify(ElemTy)) {
+ if (std::optional<PrimType> T = Ctx.classify(ElemTy)) {
return allocateDescriptor(D, *T, IsTemporary,
Descriptor::UnknownSize{});
} else {
- Descriptor *Desc =
- createDescriptor(D, ElemTy.getTypePtr(), IsConst, IsTemporary);
+ Descriptor *Desc = createDescriptor(D, ElemTy.getTypePtr(), MDSize,
+ IsConst, IsTemporary);
if (!Desc)
return nullptr;
return allocateDescriptor(D, Desc, IsTemporary,
@@ -366,13 +369,15 @@ Descriptor *Program::createDescriptor(const DeclTy &D, const Type *Ty,
// Atomic types.
if (auto *AT = Ty->getAs<AtomicType>()) {
const Type *InnerTy = AT->getValueType().getTypePtr();
- return createDescriptor(D, InnerTy, IsConst, IsTemporary, IsMutable);
+ return createDescriptor(D, InnerTy, MDSize, IsConst, IsTemporary,
+ IsMutable);
}
// Complex types - represented as arrays of elements.
if (auto *CT = Ty->getAs<ComplexType>()) {
PrimType ElemTy = *Ctx.classify(CT->getElementType());
- return allocateDescriptor(D, ElemTy, 2, IsConst, IsTemporary, IsMutable);
+ return allocateDescriptor(D, ElemTy, MDSize, 2, IsConst, IsTemporary,
+ IsMutable);
}
return nullptr;
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Program.h b/contrib/llvm-project/clang/lib/AST/Interp/Program.h
index ca985af8ad30..5a80dd1ed748 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Program.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Program.h
@@ -37,10 +37,26 @@ class Context;
class Record;
/// The program contains and links the bytecode for all functions.
-class Program {
+class Program final {
public:
Program(Context &Ctx) : Ctx(Ctx) {}
+ ~Program() {
+ // Manually destroy all the blocks. They are almost all harmless,
+ // but primitive arrays might have an InitMap* heap allocated and
+ // that needs to be freed.
+ for (Global *G : Globals)
+ G->block()->invokeDtor();
+
+ // Records might actually allocate memory themselves, but they
+ // are allocated using a BumpPtrAllocator. Call their desctructors
+ // here manually so they are properly freeing their resources.
+ for (auto RecordPair : Records) {
+ if (Record *R = RecordPair.second)
+ R->~Record();
+ }
+ }
+
/// Marshals a native pointer to an ID for embedding in bytecode.
unsigned getOrCreateNativePointer(const void *Ptr);
@@ -60,23 +76,25 @@ public:
}
/// Finds a global's index.
- llvm::Optional<unsigned> getGlobal(const ValueDecl *VD);
+ std::optional<unsigned> getGlobal(const ValueDecl *VD);
/// Returns or creates a global an creates an index to it.
- llvm::Optional<unsigned> getOrCreateGlobal(const ValueDecl *VD);
+ std::optional<unsigned> getOrCreateGlobal(const ValueDecl *VD,
+ const Expr *Init = nullptr);
/// Returns or creates a dummy value for parameters.
- llvm::Optional<unsigned> getOrCreateDummy(const ParmVarDecl *PD);
+ std::optional<unsigned> getOrCreateDummy(const ParmVarDecl *PD);
/// Creates a global and returns its index.
- llvm::Optional<unsigned> createGlobal(const ValueDecl *VD);
+ std::optional<unsigned> createGlobal(const ValueDecl *VD, const Expr *E);
/// Creates a global from a lifetime-extended temporary.
- llvm::Optional<unsigned> createGlobal(const Expr *E);
+ std::optional<unsigned> createGlobal(const Expr *E);
/// Creates a new function from a code range.
template <typename... Ts>
Function *createFunction(const FunctionDecl *Def, Ts &&... Args) {
+ Def = Def->getCanonicalDecl();
auto *Func = new Function(*this, Def, std::forward<Ts>(Args)...);
Funcs.insert({Def, std::unique_ptr<Function>(Func)});
return Func;
@@ -92,26 +110,23 @@ public:
/// Returns a function.
Function *getFunction(const FunctionDecl *F);
- /// Returns a pointer to a function if it exists and can be compiled.
- /// If a function couldn't be compiled, an error is returned.
- /// If a function was not yet defined, a null pointer is returned.
- llvm::Expected<Function *> getOrCreateFunction(const FunctionDecl *F);
-
/// Returns a record or creates one if it does not exist.
Record *getOrCreateRecord(const RecordDecl *RD);
/// Creates a descriptor for a primitive type.
Descriptor *createDescriptor(const DeclTy &D, PrimType Type,
- bool IsConst = false,
- bool IsTemporary = false,
+ Descriptor::MetadataSize MDSize = std::nullopt,
+ bool IsConst = false, bool IsTemporary = false,
bool IsMutable = false) {
- return allocateDescriptor(D, Type, IsConst, IsTemporary, IsMutable);
+ return allocateDescriptor(D, Type, MDSize, IsConst, IsTemporary, IsMutable);
}
/// Creates a descriptor for a composite type.
Descriptor *createDescriptor(const DeclTy &D, const Type *Ty,
+ Descriptor::MetadataSize MDSize = std::nullopt,
bool IsConst = false, bool IsTemporary = false,
- bool IsMutable = false);
+ bool IsMutable = false,
+ const Expr *Init = nullptr);
/// Context to manage declaration lifetimes.
class DeclScope {
@@ -124,17 +139,18 @@ public:
};
/// Returns the current declaration ID.
- llvm::Optional<unsigned> getCurrentDecl() const {
+ std::optional<unsigned> getCurrentDecl() const {
if (CurrentDeclaration == NoDeclaration)
- return llvm::Optional<unsigned>{};
+ return std::optional<unsigned>{};
return LastDeclaration;
}
private:
friend class DeclScope;
- llvm::Optional<unsigned> createGlobal(const DeclTy &D, QualType Ty,
- bool IsStatic, bool IsExtern);
+ std::optional<unsigned> createGlobal(const DeclTy &D, QualType Ty,
+ bool IsStatic, bool IsExtern,
+ const Expr *Init = nullptr);
/// Reference to the VM context.
Context &Ctx;
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Record.h b/contrib/llvm-project/clang/lib/AST/Interp/Record.h
index 9cdee9003752..1742cb1cc4ee 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Record.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Record.h
@@ -13,14 +13,16 @@
#ifndef LLVM_CLANG_AST_INTERP_RECORD_H
#define LLVM_CLANG_AST_INTERP_RECORD_H
-#include "Pointer.h"
+#include "Descriptor.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
namespace clang {
namespace interp {
class Program;
/// Structure/Class descriptor.
-class Record {
+class Record final {
public:
/// Describes a record field.
struct Field {
@@ -47,6 +49,8 @@ public:
public:
/// Returns the underlying declaration.
const RecordDecl *getDecl() const { return Decl; }
+ /// Returns the name of the underlying declaration.
+ const std::string getName() const { return Decl->getNameAsString(); }
/// Checks if the record is a union.
bool isUnion() const { return getDecl()->isUnion(); }
/// Returns the size of the record.
@@ -59,13 +63,20 @@ public:
const Base *getBase(const RecordDecl *FD) const;
/// Returns a virtual base descriptor.
const Base *getVirtualBase(const RecordDecl *RD) const;
+ // Returns the destructor of the record, if any.
+ const CXXDestructorDecl *getDestructor() const {
+ if (const auto *CXXDecl = dyn_cast<CXXRecordDecl>(Decl))
+ return CXXDecl->getDestructor();
+ return nullptr;
+ }
using const_field_iter = FieldList::const_iterator;
llvm::iterator_range<const_field_iter> fields() const {
return llvm::make_range(Fields.begin(), Fields.end());
}
- unsigned getNumFields() { return Fields.size(); }
+ unsigned getNumFields() const { return Fields.size(); }
+ const Field *getField(unsigned I) const { return &Fields[I]; }
Field *getField(unsigned I) { return &Fields[I]; }
using const_base_iter = BaseList::const_iterator;
@@ -73,7 +84,7 @@ public:
return llvm::make_range(Bases.begin(), Bases.end());
}
- unsigned getNumBases() { return Bases.size(); }
+ unsigned getNumBases() const { return Bases.size(); }
Base *getBase(unsigned I) { return &Bases[I]; }
using const_virtual_iter = VirtualBaseList::const_iterator;
@@ -81,7 +92,7 @@ public:
return llvm::make_range(VirtualBases.begin(), VirtualBases.end());
}
- unsigned getNumVirtualBases() { return VirtualBases.size(); }
+ unsigned getNumVirtualBases() const { return VirtualBases.size(); }
Base *getVirtualBase(unsigned I) { return &VirtualBases[I]; }
private:
@@ -108,7 +119,6 @@ private:
llvm::DenseMap<const FieldDecl *, Field *> FieldMap;
/// Mapping from declarations to virtual bases.
llvm::DenseMap<const RecordDecl *, Base *> VirtualBaseMap;
- /// Mapping from
/// Size of the structure.
unsigned BaseSize;
/// Size of all virtual bases.
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Source.cpp b/contrib/llvm-project/clang/lib/AST/Interp/Source.cpp
index 4bec87812638..467cde116843 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Source.cpp
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Source.cpp
@@ -28,12 +28,12 @@ const Expr *SourceInfo::asExpr() const {
return nullptr;
}
-const Expr *SourceMapper::getExpr(Function *F, CodePtr PC) const {
+const Expr *SourceMapper::getExpr(const Function *F, CodePtr PC) const {
if (const Expr *E = getSource(F, PC).asExpr())
return E;
llvm::report_fatal_error("missing source expression");
}
-SourceLocation SourceMapper::getLocation(Function *F, CodePtr PC) const {
+SourceLocation SourceMapper::getLocation(const Function *F, CodePtr PC) const {
return getSource(F, PC).getLoc();
}
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/Source.h b/contrib/llvm-project/clang/lib/AST/Interp/Source.h
index 6acaf406b47a..99ffce34c12f 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/Source.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/Source.h
@@ -13,6 +13,7 @@
#ifndef LLVM_CLANG_AST_INTERP_SOURCE_H
#define LLVM_CLANG_AST_INTERP_SOURCE_H
+#include "PrimType.h"
#include "clang/AST/Decl.h"
#include "clang/AST/Stmt.h"
#include "llvm/Support/Endian.h"
@@ -22,7 +23,7 @@ namespace interp {
class Function;
/// Pointer into the code segment.
-class CodePtr {
+class CodePtr final {
public:
CodePtr() : Ptr(nullptr) {}
@@ -43,11 +44,14 @@ public:
bool operator!=(const CodePtr &RHS) const { return Ptr != RHS.Ptr; }
+ operator bool() const { return Ptr; }
+
/// Reads data and advances the pointer.
template <typename T> std::enable_if_t<!std::is_pointer<T>::value, T> read() {
+ assert(aligned(Ptr));
using namespace llvm::support;
T Value = endian::read<T, endianness::native, 1>(Ptr);
- Ptr += sizeof(T);
+ Ptr += align(sizeof(T));
return Value;
}
@@ -63,7 +67,7 @@ private:
};
/// Describes the statement/declaration an opcode was generated from.
-class SourceInfo {
+class SourceInfo final {
public:
SourceInfo() {}
SourceInfo(const Stmt *E) : Source(E) {}
@@ -89,12 +93,12 @@ public:
virtual ~SourceMapper() {}
/// Returns source information for a given PC in a function.
- virtual SourceInfo getSource(Function *F, CodePtr PC) const = 0;
+ virtual SourceInfo getSource(const Function *F, CodePtr PC) const = 0;
/// Returns the expression if an opcode belongs to one, null otherwise.
- const Expr *getExpr(Function *F, CodePtr PC) const;
+ const Expr *getExpr(const Function *F, CodePtr PC) const;
/// Returns the location from which an opcode originates.
- SourceLocation getLocation(Function *F, CodePtr PC) const;
+ SourceLocation getLocation(const Function *F, CodePtr PC) const;
};
} // namespace interp
diff --git a/contrib/llvm-project/clang/lib/AST/Interp/State.h b/contrib/llvm-project/clang/lib/AST/Interp/State.h
index d9a645a3eb3e..131fbcf3cffc 100644
--- a/contrib/llvm-project/clang/lib/AST/Interp/State.h
+++ b/contrib/llvm-project/clang/lib/AST/Interp/State.h
@@ -71,6 +71,7 @@ public:
virtual unsigned getCallStackDepth() = 0;
public:
+ State() : InConstantContext(false) {}
// Diagnose that the evaluation could not be folded (FF => FoldFailure)
OptionalDiagnostic
FFDiag(SourceLocation Loc,
@@ -118,6 +119,10 @@ public:
const LangOptions &getLangOpts() const;
+ /// Whether or not we're in a context where the front end requires a
+ /// constant value.
+ bool InConstantContext;
+
private:
void addCallStack(unsigned Limit);