| //===--- 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 |