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