diff options
Diffstat (limited to 'lib/AST/Interp/ByteCodeExprGen.cpp')
-rw-r--r-- | lib/AST/Interp/ByteCodeExprGen.cpp | 580 |
1 files changed, 580 insertions, 0 deletions
diff --git a/lib/AST/Interp/ByteCodeExprGen.cpp b/lib/AST/Interp/ByteCodeExprGen.cpp new file mode 100644 index 000000000000..5c8cb4274260 --- /dev/null +++ b/lib/AST/Interp/ByteCodeExprGen.cpp @@ -0,0 +1,580 @@ +//===--- ByteCodeExprGen.cpp - Code generator for expressions ---*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "ByteCodeExprGen.h" +#include "ByteCodeEmitter.h" +#include "ByteCodeGenError.h" +#include "Context.h" +#include "Function.h" +#include "PrimType.h" +#include "Program.h" +#include "State.h" + +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 { + +/// Scope used to handle temporaries in toplevel variable declarations. +template <class Emitter> class DeclScope final : public LocalScope<Emitter> { +public: + DeclScope(ByteCodeExprGen<Emitter> *Ctx, const VarDecl *VD) + : LocalScope<Emitter>(Ctx), Scope(Ctx->P, VD) {} + + void addExtended(const Scope::Local &Local) override { + return this->addLocal(Local); + } + +private: + Program::DeclScope Scope; +}; + +/// 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->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); + } + +private: + /// Parent context. + ByteCodeExprGen<Emitter> *Ctx; + /// Old discard flag to restore. + bool OldDiscardResult; + /// Old pointer emitter to restore. + llvm::Optional<InitFnRef> OldInitFn; +}; + +} // namespace interp +} // namespace clang + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) { + auto *SubExpr = CE->getSubExpr(); + switch (CE->getCastKind()) { + + case CK_LValueToRValue: { + return dereference( + CE->getSubExpr(), DerefKind::Read, + [](PrimType) { + // Value loaded - nothing to do here. + return true; + }, + [this, CE](PrimType T) { + // Pointer on stack - dereference it. + if (!this->emitLoadPop(T, CE)) + return false; + return DiscardResult ? this->emitPop(T, CE) : true; + }); + } + + case CK_ArrayToPointerDecay: + case CK_AtomicToNonAtomic: + case CK_ConstructorConversion: + case CK_FunctionToPointerDecay: + case CK_NonAtomicToAtomic: + case CK_NoOp: + case CK_UserDefinedConversion: + return this->Visit(SubExpr); + + case CK_ToVoid: + return discard(SubExpr); + + default: { + // TODO: implement other casts. + return this->bail(CE); + } + } +} + +template <class Emitter> +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); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitParenExpr(const ParenExpr *PE) { + return this->Visit(PE->getSubExpr()); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::VisitBinaryOperator(const BinaryOperator *BO) { + const Expr *LHS = BO->getLHS(); + const Expr *RHS = BO->getRHS(); + + // Deal with operations which have composite or void types. + switch (BO->getOpcode()) { + case BO_Comma: + if (!discard(LHS)) + return false; + if (!this->Visit(RHS)) + return false; + return true; + default: + break; + } + + // Typecheck the args. + Optional<PrimType> LT = classify(LHS->getType()); + Optional<PrimType> RT = classify(RHS->getType()); + if (!LT || !RT) { + return this->bail(BO); + } + + if (Optional<PrimType> T = classify(BO->getType())) { + if (!visit(LHS)) + return false; + if (!visit(RHS)) + return false; + + auto Discard = [this, T, BO](bool Result) { + if (!Result) + 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); + } + } + + return this->bail(BO); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::discard(const Expr *E) { + OptionScope<Emitter> Scope(this, /*discardResult=*/true); + return this->Visit(E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::visit(const Expr *E) { + OptionScope<Emitter> Scope(this, /*discardResult=*/false); + return this->Visit(E); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::visitBool(const Expr *E) { + if (Optional<PrimType> T = classify(E->getType())) { + return visit(E); + } else { + return this->bail(E); + } +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::visitZeroInitializer(PrimType T, const Expr *E) { + switch (T) { + case PT_Bool: + return this->emitZeroBool(E); + case PT_Sint8: + return this->emitZeroSint8(E); + case PT_Uint8: + return this->emitZeroUint8(E); + case PT_Sint16: + return this->emitZeroSint16(E); + case PT_Uint16: + return this->emitZeroUint16(E); + case PT_Sint32: + return this->emitZeroSint32(E); + case PT_Uint32: + return this->emitZeroUint32(E); + case PT_Sint64: + return this->emitZeroSint64(E); + case PT_Uint64: + return this->emitZeroUint64(E); + case PT_Ptr: + return this->emitNullPtr(E); + } + llvm_unreachable("unknown primitive type"); +} + +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 (!LV->refersToBitField()) { + // Only primitive, non bit-field types can be dereferenced directly. + if (auto *DE = dyn_cast<DeclRefExpr>(LV)) { + if (!DE->getDecl()->getType()->isReferenceType()) { + if (auto *PD = dyn_cast<ParmVarDecl>(DE->getDecl())) + return dereferenceParam(LV, *T, PD, AK, Direct, Indirect); + if (auto *VD = dyn_cast<VarDecl>(DE->getDecl())) + return dereferenceVar(LV, *T, VD, AK, Direct, Indirect); + } + } + } + + if (!visit(LV)) + return false; + return Indirect(*T); + } + + return false; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::dereferenceParam( + const Expr *LV, PrimType T, const ParmVarDecl *PD, DerefKind AK, + llvm::function_ref<bool(PrimType)> Direct, + llvm::function_ref<bool(PrimType)> Indirect) { + auto It = this->Params.find(PD); + if (It != this->Params.end()) { + unsigned Idx = It->second; + switch (AK) { + case DerefKind::Read: + return DiscardResult ? true : this->emitGetParam(T, Idx, LV); + + case DerefKind::Write: + if (!Direct(T)) + return false; + if (!this->emitSetParam(T, Idx, LV)) + return false; + return DiscardResult ? true : this->emitGetPtrParam(Idx, LV); + + case DerefKind::ReadWrite: + if (!this->emitGetParam(T, Idx, LV)) + return false; + if (!Direct(T)) + return false; + if (!this->emitSetParam(T, Idx, LV)) + return false; + return DiscardResult ? true : this->emitGetPtrParam(Idx, LV); + } + return true; + } + + // If the param is a pointer, we can dereference a dummy value. + if (!DiscardResult && T == PT_Ptr && AK == DerefKind::Read) { + if (auto Idx = P.getOrCreateDummy(PD)) + return this->emitGetPtrGlobal(*Idx, PD); + return false; + } + + // Value cannot be produced - try to emit pointer and do stuff with it. + return visit(LV) && Indirect(T); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::dereferenceVar( + const Expr *LV, PrimType T, const VarDecl *VD, DerefKind AK, + llvm::function_ref<bool(PrimType)> Direct, + llvm::function_ref<bool(PrimType)> Indirect) { + auto It = Locals.find(VD); + if (It != Locals.end()) { + const auto &L = It->second; + switch (AK) { + case DerefKind::Read: + if (!this->emitGetLocal(T, L.Offset, LV)) + return false; + return DiscardResult ? this->emitPop(T, LV) : true; + + case DerefKind::Write: + if (!Direct(T)) + return false; + if (!this->emitSetLocal(T, L.Offset, LV)) + return false; + return DiscardResult ? true : this->emitGetPtrLocal(L.Offset, LV); + + case DerefKind::ReadWrite: + if (!this->emitGetLocal(T, L.Offset, LV)) + return false; + if (!Direct(T)) + return false; + if (!this->emitSetLocal(T, L.Offset, LV)) + return false; + return DiscardResult ? true : this->emitGetPtrLocal(L.Offset, LV); + } + } else if (auto Idx = getGlobalIdx(VD)) { + switch (AK) { + case DerefKind::Read: + if (!this->emitGetGlobal(T, *Idx, LV)) + return false; + return DiscardResult ? this->emitPop(T, LV) : true; + + case DerefKind::Write: + if (!Direct(T)) + return false; + if (!this->emitSetGlobal(T, *Idx, LV)) + return false; + return DiscardResult ? true : this->emitGetPtrGlobal(*Idx, LV); + + case DerefKind::ReadWrite: + if (!this->emitGetGlobal(T, *Idx, LV)) + return false; + if (!Direct(T)) + return false; + if (!this->emitSetGlobal(T, *Idx, LV)) + return false; + return DiscardResult ? true : this->emitGetPtrGlobal(*Idx, LV); + } + } + + // If the declaration is a constant value, emit it here even + // though the declaration was not evaluated in the current scope. + // The access mode can only be read in this case. + if (!DiscardResult && AK == DerefKind::Read) { + if (VD->hasLocalStorage() && VD->hasInit() && !VD->isConstexpr()) { + QualType VT = VD->getType(); + if (VT.isConstQualified() && VT->isFundamentalType()) + return this->Visit(VD->getInit()); + } + } + + // Value cannot be produced - try to emit pointer. + return visit(LV) && Indirect(T); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::emitConst(PrimType T, unsigned NumBits, + const APInt &Value, const Expr *E) { + switch (T) { + case PT_Sint8: + return this->emitConstSint8(Value.getSExtValue(), E); + case PT_Uint8: + return this->emitConstUint8(Value.getZExtValue(), E); + case PT_Sint16: + return this->emitConstSint16(Value.getSExtValue(), E); + case PT_Uint16: + return this->emitConstUint16(Value.getZExtValue(), E); + case PT_Sint32: + return this->emitConstSint32(Value.getSExtValue(), E); + case PT_Uint32: + return this->emitConstUint32(Value.getZExtValue(), E); + case PT_Sint64: + return this->emitConstSint64(Value.getSExtValue(), E); + case PT_Uint64: + return this->emitConstUint64(Value.getZExtValue(), E); + case PT_Bool: + return this->emitConstBool(Value.getBoolValue(), E); + case PT_Ptr: + llvm_unreachable("Invalid integral type"); + break; + } + llvm_unreachable("unknown primitive type"); +} + +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 *>()); + Scope::Local Local = this->createLocal(D); + if (auto *VD = dyn_cast_or_null<ValueDecl>(Src.dyn_cast<const Decl *>())) + Locals.insert({VD, Local}); + VarScope->add(Local, IsExtended); + return Local.Offset; +} + +template <class Emitter> +llvm::Optional<unsigned> +ByteCodeExprGen<Emitter>::allocateLocal(DeclTy &&Src, bool IsExtended) { + QualType Ty; + + const ValueDecl *Key = nullptr; + bool IsTemporary = false; + if (auto *VD = dyn_cast_or_null<ValueDecl>(Src.dyn_cast<const Decl *>())) { + Key = VD; + Ty = VD->getType(); + } + if (auto *E = Src.dyn_cast<const Expr *>()) { + IsTemporary = true; + Ty = E->getType(); + } + + Descriptor *D = P.createDescriptor(Src, Ty.getTypePtr(), + Ty.isConstQualified(), IsTemporary); + if (!D) + return {}; + + Scope::Local Local = this->createLocal(D); + if (Key) + Locals.insert({Key, Local}); + VarScope->add(Local, IsExtended); + return Local.Offset; +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::visitInitializer( + const Expr *Init, InitFnRef InitFn) { + OptionScope<Emitter> Scope(this, InitFn); + return this->Visit(Init); +} + +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); + } + return this->bail(VD); +} + +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 {}; +} + +template <class Emitter> +const RecordType *ByteCodeExprGen<Emitter>::getRecordTy(QualType Ty) { + if (auto *PT = dyn_cast<PointerType>(Ty)) + return PT->getPointeeType()->getAs<RecordType>(); + else + return Ty->getAs<RecordType>(); +} + +template <class Emitter> +Record *ByteCodeExprGen<Emitter>::getRecord(QualType Ty) { + if (auto *RecordTy = getRecordTy(Ty)) { + return getRecord(RecordTy->getDecl()); + } + return nullptr; +} + +template <class Emitter> +Record *ByteCodeExprGen<Emitter>::getRecord(const RecordDecl *RD) { + return P.getOrCreateRecord(RD); +} + +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)) + return this->emitRet(*T, Exp); + else + return this->emitRetValue(Exp); +} + +template <class Emitter> +bool ByteCodeExprGen<Emitter>::visitDecl(const VarDecl *VD) { + const Expr *Init = VD->getInit(); + + 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)) + 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); + } else { + { + // Composite declarations - allocate storage and initialize it. + DeclScope<Emitter> LocalScope(this, VD); + if (!visitGlobalInitializer(Init, *I)) + return false; + } + + // Return a pointer to the global. + if (!this->emitGetPtrGlobal(*I, VD)) + return false; + return this->emitRetValue(VD); + } + } + + return this->bail(VD); +} + +template <class Emitter> +void ByteCodeExprGen<Emitter>::emitCleanup() { + for (VariableScope<Emitter> *C = VarScope; C; C = C->getParent()) + C->emitDestruction(); +} + +namespace clang { +namespace interp { + +template class ByteCodeExprGen<ByteCodeEmitter>; +template class ByteCodeExprGen<EvalEmitter>; + +} // namespace interp +} // namespace clang |