1 //===--- EvalEmitter.cpp - Instruction emitter for the VM -------*- C++ -*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "EvalEmitter.h" 10 #include "Context.h" 11 #include "IntegralAP.h" 12 #include "Interp.h" 13 #include "clang/AST/DeclCXX.h" 14 15 using namespace clang; 16 using namespace clang::interp; 17 18 EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent, 19 InterpStack &Stk) 20 : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), EvalResult(&Ctx) {} 21 22 EvalEmitter::~EvalEmitter() { 23 for (auto &V : Locals) { 24 Block *B = reinterpret_cast<Block *>(V.get()); 25 if (B->isInitialized()) 26 B->invokeDtor(); 27 } 28 } 29 30 /// Clean up all our resources. This needs to done in failed evaluations before 31 /// we call InterpStack::clear(), because there might be a Pointer on the stack 32 /// pointing into a Block in the EvalEmitter. 33 void EvalEmitter::cleanup() { S.cleanup(); } 34 35 EvaluationResult EvalEmitter::interpretExpr(const Expr *E, 36 bool ConvertResultToRValue, 37 bool DestroyToplevelScope) { 38 S.setEvalLocation(E->getExprLoc()); 39 this->ConvertResultToRValue = ConvertResultToRValue && !isa<ConstantExpr>(E); 40 this->CheckFullyInitialized = isa<ConstantExpr>(E); 41 EvalResult.setSource(E); 42 43 if (!this->visitExpr(E, DestroyToplevelScope)) { 44 // EvalResult may already have a result set, but something failed 45 // after that (e.g. evaluating destructors). 46 EvalResult.setInvalid(); 47 } 48 49 return std::move(this->EvalResult); 50 } 51 52 EvaluationResult EvalEmitter::interpretDecl(const VarDecl *VD, 53 bool CheckFullyInitialized) { 54 this->CheckFullyInitialized = CheckFullyInitialized; 55 S.EvaluatingDecl = VD; 56 EvalResult.setSource(VD); 57 58 if (const Expr *Init = VD->getAnyInitializer()) { 59 QualType T = VD->getType(); 60 this->ConvertResultToRValue = !Init->isGLValue() && !T->isPointerType() && 61 !T->isObjCObjectPointerType(); 62 } else 63 this->ConvertResultToRValue = false; 64 65 EvalResult.setSource(VD); 66 67 if (!this->visitDeclAndReturn(VD, S.inConstantContext())) 68 EvalResult.setInvalid(); 69 70 S.EvaluatingDecl = nullptr; 71 updateGlobalTemporaries(); 72 return std::move(this->EvalResult); 73 } 74 75 EvaluationResult EvalEmitter::interpretAsPointer(const Expr *E, 76 PtrCallback PtrCB) { 77 78 S.setEvalLocation(E->getExprLoc()); 79 this->ConvertResultToRValue = false; 80 this->CheckFullyInitialized = false; 81 this->PtrCB = PtrCB; 82 EvalResult.setSource(E); 83 84 if (!this->visitExpr(E, /*DestroyToplevelScope=*/true)) { 85 // EvalResult may already have a result set, but something failed 86 // after that (e.g. evaluating destructors). 87 EvalResult.setInvalid(); 88 } 89 90 return std::move(this->EvalResult); 91 } 92 93 void EvalEmitter::emitLabel(LabelTy Label) { CurrentLabel = Label; } 94 95 EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; } 96 97 Scope::Local EvalEmitter::createLocal(Descriptor *D) { 98 // Allocate memory for a local. 99 auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize()); 100 auto *B = new (Memory.get()) Block(Ctx.getEvalID(), D, /*isStatic=*/false); 101 B->invokeCtor(); 102 103 // Initialize local variable inline descriptor. 104 InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData()); 105 Desc.Desc = D; 106 Desc.Offset = sizeof(InlineDescriptor); 107 Desc.IsActive = true; 108 Desc.IsBase = false; 109 Desc.IsFieldMutable = false; 110 Desc.IsConst = false; 111 Desc.IsInitialized = false; 112 113 // Register the local. 114 unsigned Off = Locals.size(); 115 Locals.push_back(std::move(Memory)); 116 return {Off, D}; 117 } 118 119 bool EvalEmitter::jumpTrue(const LabelTy &Label) { 120 if (isActive()) { 121 if (S.Stk.pop<bool>()) 122 ActiveLabel = Label; 123 } 124 return true; 125 } 126 127 bool EvalEmitter::jumpFalse(const LabelTy &Label) { 128 if (isActive()) { 129 if (!S.Stk.pop<bool>()) 130 ActiveLabel = Label; 131 } 132 return true; 133 } 134 135 bool EvalEmitter::jump(const LabelTy &Label) { 136 if (isActive()) 137 CurrentLabel = ActiveLabel = Label; 138 return true; 139 } 140 141 bool EvalEmitter::fallthrough(const LabelTy &Label) { 142 if (isActive()) 143 ActiveLabel = Label; 144 CurrentLabel = Label; 145 return true; 146 } 147 148 bool EvalEmitter::speculate(const CallExpr *E, const LabelTy &EndLabel) { 149 size_t StackSizeBefore = S.Stk.size(); 150 const Expr *Arg = E->getArg(0); 151 if (!this->visit(Arg)) { 152 S.Stk.clearTo(StackSizeBefore); 153 154 if (S.inConstantContext() || Arg->HasSideEffects(S.getASTContext())) 155 return this->emitBool(false, E); 156 return Invalid(S, OpPC); 157 } 158 159 PrimType T = Ctx.classify(Arg->getType()).value_or(PT_Ptr); 160 if (T == PT_Ptr) { 161 const auto &Ptr = S.Stk.pop<Pointer>(); 162 return this->emitBool(CheckBCPResult(S, Ptr), E); 163 } 164 165 // Otherwise, this is fine! 166 if (!this->emitPop(T, E)) 167 return false; 168 return this->emitBool(true, E); 169 } 170 171 template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) { 172 if (!isActive()) 173 return true; 174 175 using T = typename PrimConv<OpType>::T; 176 EvalResult.setValue(S.Stk.pop<T>().toAPValue(Ctx.getASTContext())); 177 return true; 178 } 179 180 template <> bool EvalEmitter::emitRet<PT_Ptr>(const SourceInfo &Info) { 181 if (!isActive()) 182 return true; 183 184 const Pointer &Ptr = S.Stk.pop<Pointer>(); 185 186 if (Ptr.isFunctionPointer()) { 187 EvalResult.setValue(Ptr.toAPValue(Ctx.getASTContext())); 188 return true; 189 } 190 191 // If we're returning a raw pointer, call our callback. 192 if (this->PtrCB) 193 return (*this->PtrCB)(Ptr); 194 195 if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info)) 196 return false; 197 if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr)) 198 return false; 199 200 // Implicitly convert lvalue to rvalue, if requested. 201 if (ConvertResultToRValue) { 202 if (!Ptr.isZero() && !Ptr.isDereferencable()) 203 return false; 204 205 if (S.getLangOpts().CPlusPlus11 && Ptr.isBlockPointer() && 206 !CheckFinalLoad(S, OpPC, Ptr)) { 207 return false; 208 } 209 210 // Never allow reading from a non-const pointer, unless the memory 211 // has been created in this evaluation. 212 if (!Ptr.isZero() && !Ptr.isConst() && Ptr.isBlockPointer() && 213 Ptr.block()->getEvalID() != Ctx.getEvalID()) 214 return false; 215 216 if (std::optional<APValue> V = 217 Ptr.toRValue(Ctx, EvalResult.getSourceType())) { 218 EvalResult.setValue(*V); 219 } else { 220 return false; 221 } 222 } else { 223 if (!Ptr.isLive() && !Ptr.isTemporary()) 224 return false; 225 226 EvalResult.setValue(Ptr.toAPValue(Ctx.getASTContext())); 227 } 228 229 return true; 230 } 231 232 bool EvalEmitter::emitRetVoid(const SourceInfo &Info) { 233 EvalResult.setValid(); 234 return true; 235 } 236 237 bool EvalEmitter::emitRetValue(const SourceInfo &Info) { 238 const auto &Ptr = S.Stk.pop<Pointer>(); 239 240 if (!EvalResult.checkReturnValue(S, Ctx, Ptr, Info)) 241 return false; 242 if (CheckFullyInitialized && !EvalResult.checkFullyInitialized(S, Ptr)) 243 return false; 244 245 if (std::optional<APValue> APV = 246 Ptr.toRValue(S.getASTContext(), EvalResult.getSourceType())) { 247 EvalResult.setValue(*APV); 248 return true; 249 } 250 251 EvalResult.setInvalid(); 252 return false; 253 } 254 255 bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) { 256 if (!isActive()) 257 return true; 258 259 Block *B = getLocal(I); 260 S.Stk.push<Pointer>(B, sizeof(InlineDescriptor)); 261 return true; 262 } 263 264 template <PrimType OpType> 265 bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) { 266 if (!isActive()) 267 return true; 268 269 using T = typename PrimConv<OpType>::T; 270 271 Block *B = getLocal(I); 272 S.Stk.push<T>(*reinterpret_cast<T *>(B->data())); 273 return true; 274 } 275 276 template <PrimType OpType> 277 bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) { 278 if (!isActive()) 279 return true; 280 281 using T = typename PrimConv<OpType>::T; 282 283 Block *B = getLocal(I); 284 *reinterpret_cast<T *>(B->data()) = S.Stk.pop<T>(); 285 InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData()); 286 Desc.IsInitialized = true; 287 288 return true; 289 } 290 291 bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) { 292 if (!isActive()) 293 return true; 294 295 for (auto &Local : Descriptors[I]) { 296 Block *B = getLocal(Local.Offset); 297 S.deallocate(B); 298 } 299 300 return true; 301 } 302 303 /// Global temporaries (LifetimeExtendedTemporary) carry their value 304 /// around as an APValue, which codegen accesses. 305 /// We set their value once when creating them, but we don't update it 306 /// afterwards when code changes it later. 307 /// This is what we do here. 308 void EvalEmitter::updateGlobalTemporaries() { 309 for (const auto &[E, Temp] : S.SeenGlobalTemporaries) { 310 if (std::optional<unsigned> GlobalIndex = P.getGlobal(E)) { 311 const Pointer &Ptr = P.getPtrGlobal(*GlobalIndex); 312 APValue *Cached = Temp->getOrCreateValue(true); 313 314 if (std::optional<PrimType> T = Ctx.classify(E->getType())) { 315 TYPE_SWITCH( 316 *T, { *Cached = Ptr.deref<T>().toAPValue(Ctx.getASTContext()); }); 317 } else { 318 if (std::optional<APValue> APV = 319 Ptr.toRValue(Ctx, Temp->getTemporaryExpr()->getType())) 320 *Cached = *APV; 321 } 322 } 323 } 324 S.SeenGlobalTemporaries.clear(); 325 } 326 327 //===----------------------------------------------------------------------===// 328 // Opcode evaluators 329 //===----------------------------------------------------------------------===// 330 331 #define GET_EVAL_IMPL 332 #include "Opcodes.inc" 333 #undef GET_EVAL_IMPL 334