//===--- 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 using Expected = llvm::Expected; template using Optional = llvm::Optional; namespace clang { namespace interp { /// Scope used to handle temporaries in toplevel variable declarations. template class DeclScope final : public LocalScope { public: DeclScope(ByteCodeExprGen *Ctx, const VarDecl *VD) : LocalScope(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 OptionScope { public: using InitFnRef = typename ByteCodeExprGen::InitFnRef; using ChainedInitFnRef = std::function; /// Root constructor, compiling or discarding primitives. OptionScope(ByteCodeExprGen *Ctx, bool NewDiscardResult) : Ctx(Ctx), OldDiscardResult(Ctx->DiscardResult), OldInitFn(std::move(Ctx->InitFn)) { Ctx->DiscardResult = NewDiscardResult; Ctx->InitFn = llvm::Optional{}; } /// Root constructor, setting up compilation state. OptionScope(ByteCodeExprGen *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 *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 *Ctx; /// Old discard flag to restore. bool OldDiscardResult; /// Old pointer emitter to restore. llvm::Optional OldInitFn; }; } // namespace interp } // namespace clang template bool ByteCodeExprGen::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 bool ByteCodeExprGen::VisitIntegerLiteral(const IntegerLiteral *LE) { if (DiscardResult) return true; auto Val = LE->getValue(); QualType LitTy = LE->getType(); if (Optional T = classify(LitTy)) return emitConst(*T, getIntWidth(LitTy), LE->getValue(), LE); return this->bail(LE); } template bool ByteCodeExprGen::VisitParenExpr(const ParenExpr *PE) { return this->Visit(PE->getSubExpr()); } template bool ByteCodeExprGen::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 LT = classify(LHS->getType()); Optional RT = classify(RHS->getType()); if (!LT || !RT) { return this->bail(BO); } if (Optional 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 bool ByteCodeExprGen::discard(const Expr *E) { OptionScope Scope(this, /*discardResult=*/true); return this->Visit(E); } template bool ByteCodeExprGen::visit(const Expr *E) { OptionScope Scope(this, /*discardResult=*/false); return this->Visit(E); } template bool ByteCodeExprGen::visitBool(const Expr *E) { if (Optional T = classify(E->getType())) { return visit(E); } else { return this->bail(E); } } template bool ByteCodeExprGen::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 bool ByteCodeExprGen::dereference( const Expr *LV, DerefKind AK, llvm::function_ref Direct, llvm::function_ref Indirect) { if (Optional T = classify(LV->getType())) { if (!LV->refersToBitField()) { // Only primitive, non bit-field types can be dereferenced directly. if (auto *DE = dyn_cast(LV)) { if (!DE->getDecl()->getType()->isReferenceType()) { if (auto *PD = dyn_cast(DE->getDecl())) return dereferenceParam(LV, *T, PD, AK, Direct, Indirect); if (auto *VD = dyn_cast(DE->getDecl())) return dereferenceVar(LV, *T, VD, AK, Direct, Indirect); } } } if (!visit(LV)) return false; return Indirect(*T); } return false; } template bool ByteCodeExprGen::dereferenceParam( const Expr *LV, PrimType T, const ParmVarDecl *PD, DerefKind AK, llvm::function_ref Direct, llvm::function_ref 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 bool ByteCodeExprGen::dereferenceVar( const Expr *LV, PrimType T, const VarDecl *VD, DerefKind AK, llvm::function_ref Direct, llvm::function_ref 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 bool ByteCodeExprGen::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 unsigned ByteCodeExprGen::allocateLocalPrimitive(DeclTy &&Src, PrimType Ty, bool IsConst, bool IsExtended) { Descriptor *D = P.createDescriptor(Src, Ty, IsConst, Src.is()); Scope::Local Local = this->createLocal(D); if (auto *VD = dyn_cast_or_null(Src.dyn_cast())) Locals.insert({VD, Local}); VarScope->add(Local, IsExtended); return Local.Offset; } template llvm::Optional ByteCodeExprGen::allocateLocal(DeclTy &&Src, bool IsExtended) { QualType Ty; const ValueDecl *Key = nullptr; bool IsTemporary = false; if (auto *VD = dyn_cast_or_null(Src.dyn_cast())) { Key = VD; Ty = VD->getType(); } if (auto *E = Src.dyn_cast()) { 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 bool ByteCodeExprGen::visitInitializer( const Expr *Init, InitFnRef InitFn) { OptionScope Scope(this, InitFn); return this->Visit(Init); } template bool ByteCodeExprGen::getPtrVarDecl(const VarDecl *VD, const Expr *E) { // Generate a pointer to the local, loading refs. if (Optional Idx = getGlobalIdx(VD)) { if (VD->getType()->isReferenceType()) return this->emitGetGlobalPtr(*Idx, E); else return this->emitGetPtrGlobal(*Idx, E); } return this->bail(VD); } template llvm::Optional ByteCodeExprGen::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 const RecordType *ByteCodeExprGen::getRecordTy(QualType Ty) { if (auto *PT = dyn_cast(Ty)) return PT->getPointeeType()->getAs(); else return Ty->getAs(); } template Record *ByteCodeExprGen::getRecord(QualType Ty) { if (auto *RecordTy = getRecordTy(Ty)) { return getRecord(RecordTy->getDecl()); } return nullptr; } template Record *ByteCodeExprGen::getRecord(const RecordDecl *RD) { return P.getOrCreateRecord(RD); } template bool ByteCodeExprGen::visitExpr(const Expr *Exp) { ExprScope RootScope(this); if (!visit(Exp)) return false; if (Optional T = classify(Exp)) return this->emitRet(*T, Exp); else return this->emitRetValue(Exp); } template bool ByteCodeExprGen::visitDecl(const VarDecl *VD) { const Expr *Init = VD->getInit(); if (Optional I = P.createGlobal(VD)) { if (Optional T = classify(VD->getType())) { { // Primitive declarations - compute the value and set it. DeclScope 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 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 void ByteCodeExprGen::emitCleanup() { for (VariableScope *C = VarScope; C; C = C->getParent()) C->emitDestruction(); } namespace clang { namespace interp { template class ByteCodeExprGen; template class ByteCodeExprGen; } // namespace interp } // namespace clang