/src/llvm-project/clang/lib/AST/Interp/EvalEmitter.cpp
Line | Count | Source (jump to first uncovered line) |
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 "ByteCodeGenError.h" |
11 | | #include "Context.h" |
12 | | #include "IntegralAP.h" |
13 | | #include "Interp.h" |
14 | | #include "Opcode.h" |
15 | | #include "clang/AST/DeclCXX.h" |
16 | | |
17 | | using namespace clang; |
18 | | using namespace clang::interp; |
19 | | |
20 | | EvalEmitter::EvalEmitter(Context &Ctx, Program &P, State &Parent, |
21 | | InterpStack &Stk, APValue &Result) |
22 | 0 | : Ctx(Ctx), P(P), S(Parent, P, Stk, Ctx, this), Result(Result) { |
23 | | // Create a dummy frame for the interpreter which does not have locals. |
24 | 0 | S.Current = |
25 | 0 | new InterpFrame(S, /*Func=*/nullptr, /*Caller=*/nullptr, CodePtr()); |
26 | 0 | } |
27 | | |
28 | 0 | EvalEmitter::~EvalEmitter() { |
29 | 0 | for (auto &[K, V] : Locals) { |
30 | 0 | Block *B = reinterpret_cast<Block *>(V.get()); |
31 | 0 | if (B->isInitialized()) |
32 | 0 | B->invokeDtor(); |
33 | 0 | } |
34 | 0 | } |
35 | | |
36 | 0 | llvm::Expected<bool> EvalEmitter::interpretExpr(const Expr *E) { |
37 | 0 | if (this->visitExpr(E)) |
38 | 0 | return true; |
39 | 0 | if (BailLocation) |
40 | 0 | return llvm::make_error<ByteCodeGenError>(*BailLocation); |
41 | 0 | return false; |
42 | 0 | } |
43 | | |
44 | 0 | llvm::Expected<bool> EvalEmitter::interpretDecl(const VarDecl *VD) { |
45 | 0 | if (this->visitDecl(VD)) |
46 | 0 | return true; |
47 | 0 | if (BailLocation) |
48 | 0 | return llvm::make_error<ByteCodeGenError>(*BailLocation); |
49 | 0 | return false; |
50 | 0 | } |
51 | | |
52 | 0 | void EvalEmitter::emitLabel(LabelTy Label) { |
53 | 0 | CurrentLabel = Label; |
54 | 0 | } |
55 | | |
56 | 0 | EvalEmitter::LabelTy EvalEmitter::getLabel() { return NextLabel++; } |
57 | | |
58 | 0 | Scope::Local EvalEmitter::createLocal(Descriptor *D) { |
59 | | // Allocate memory for a local. |
60 | 0 | auto Memory = std::make_unique<char[]>(sizeof(Block) + D->getAllocSize()); |
61 | 0 | auto *B = new (Memory.get()) Block(D, /*isStatic=*/false); |
62 | 0 | B->invokeCtor(); |
63 | | |
64 | | // Initialize local variable inline descriptor. |
65 | 0 | InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData()); |
66 | 0 | Desc.Desc = D; |
67 | 0 | Desc.Offset = sizeof(InlineDescriptor); |
68 | 0 | Desc.IsActive = true; |
69 | 0 | Desc.IsBase = false; |
70 | 0 | Desc.IsFieldMutable = false; |
71 | 0 | Desc.IsConst = false; |
72 | 0 | Desc.IsInitialized = false; |
73 | | |
74 | | // Register the local. |
75 | 0 | unsigned Off = Locals.size(); |
76 | 0 | Locals.insert({Off, std::move(Memory)}); |
77 | 0 | return {Off, D}; |
78 | 0 | } |
79 | | |
80 | 0 | bool EvalEmitter::bail(const SourceLocation &Loc) { |
81 | 0 | if (!BailLocation) |
82 | 0 | BailLocation = Loc; |
83 | 0 | return false; |
84 | 0 | } |
85 | | |
86 | 0 | bool EvalEmitter::jumpTrue(const LabelTy &Label) { |
87 | 0 | if (isActive()) { |
88 | 0 | if (S.Stk.pop<bool>()) |
89 | 0 | ActiveLabel = Label; |
90 | 0 | } |
91 | 0 | return true; |
92 | 0 | } |
93 | | |
94 | 0 | bool EvalEmitter::jumpFalse(const LabelTy &Label) { |
95 | 0 | if (isActive()) { |
96 | 0 | if (!S.Stk.pop<bool>()) |
97 | 0 | ActiveLabel = Label; |
98 | 0 | } |
99 | 0 | return true; |
100 | 0 | } |
101 | | |
102 | 0 | bool EvalEmitter::jump(const LabelTy &Label) { |
103 | 0 | if (isActive()) |
104 | 0 | CurrentLabel = ActiveLabel = Label; |
105 | 0 | return true; |
106 | 0 | } |
107 | | |
108 | 0 | bool EvalEmitter::fallthrough(const LabelTy &Label) { |
109 | 0 | if (isActive()) |
110 | 0 | ActiveLabel = Label; |
111 | 0 | CurrentLabel = Label; |
112 | 0 | return true; |
113 | 0 | } |
114 | | |
115 | 0 | template <PrimType OpType> bool EvalEmitter::emitRet(const SourceInfo &Info) { |
116 | 0 | if (!isActive()) |
117 | 0 | return true; |
118 | 0 | using T = typename PrimConv<OpType>::T; |
119 | 0 | return ReturnValue<T>(S.Stk.pop<T>(), Result); |
120 | 0 | } Unexecuted instantiation: bool clang::interp::EvalEmitter::emitRet<(clang::interp::PrimType)0>(clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitRet<(clang::interp::PrimType)1>(clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitRet<(clang::interp::PrimType)2>(clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitRet<(clang::interp::PrimType)3>(clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitRet<(clang::interp::PrimType)4>(clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitRet<(clang::interp::PrimType)5>(clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitRet<(clang::interp::PrimType)6>(clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitRet<(clang::interp::PrimType)7>(clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitRet<(clang::interp::PrimType)8>(clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitRet<(clang::interp::PrimType)9>(clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitRet<(clang::interp::PrimType)10>(clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitRet<(clang::interp::PrimType)12>(clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitRet<(clang::interp::PrimType)13>(clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitRet<(clang::interp::PrimType)11>(clang::interp::SourceInfo const&) |
121 | | |
122 | 0 | bool EvalEmitter::emitRetVoid(const SourceInfo &Info) { return true; } |
123 | | |
124 | 0 | bool EvalEmitter::emitRetValue(const SourceInfo &Info) { |
125 | | // Method to recursively traverse composites. |
126 | 0 | std::function<bool(QualType, const Pointer &, APValue &)> Composite; |
127 | 0 | Composite = [this, &Composite](QualType Ty, const Pointer &Ptr, APValue &R) { |
128 | 0 | if (const auto *AT = Ty->getAs<AtomicType>()) |
129 | 0 | Ty = AT->getValueType(); |
130 | |
|
131 | 0 | if (const auto *RT = Ty->getAs<RecordType>()) { |
132 | 0 | const auto *Record = Ptr.getRecord(); |
133 | 0 | assert(Record && "Missing record descriptor"); |
134 | | |
135 | 0 | bool Ok = true; |
136 | 0 | if (RT->getDecl()->isUnion()) { |
137 | 0 | const FieldDecl *ActiveField = nullptr; |
138 | 0 | APValue Value; |
139 | 0 | for (const auto &F : Record->fields()) { |
140 | 0 | const Pointer &FP = Ptr.atField(F.Offset); |
141 | 0 | QualType FieldTy = F.Decl->getType(); |
142 | 0 | if (FP.isActive()) { |
143 | 0 | if (std::optional<PrimType> T = Ctx.classify(FieldTy)) { |
144 | 0 | TYPE_SWITCH(*T, Ok &= ReturnValue<T>(FP.deref<T>(), Value)); |
145 | 0 | } else { |
146 | 0 | Ok &= Composite(FieldTy, FP, Value); |
147 | 0 | } |
148 | 0 | break; |
149 | 0 | } |
150 | 0 | } |
151 | 0 | R = APValue(ActiveField, Value); |
152 | 0 | } else { |
153 | 0 | unsigned NF = Record->getNumFields(); |
154 | 0 | unsigned NB = Record->getNumBases(); |
155 | 0 | unsigned NV = Ptr.isBaseClass() ? 0 : Record->getNumVirtualBases(); |
156 | |
|
157 | 0 | R = APValue(APValue::UninitStruct(), NB, NF); |
158 | |
|
159 | 0 | for (unsigned I = 0; I < NF; ++I) { |
160 | 0 | const Record::Field *FD = Record->getField(I); |
161 | 0 | QualType FieldTy = FD->Decl->getType(); |
162 | 0 | const Pointer &FP = Ptr.atField(FD->Offset); |
163 | 0 | APValue &Value = R.getStructField(I); |
164 | |
|
165 | 0 | if (std::optional<PrimType> T = Ctx.classify(FieldTy)) { |
166 | 0 | TYPE_SWITCH(*T, Ok &= ReturnValue<T>(FP.deref<T>(), Value)); |
167 | 0 | } else { |
168 | 0 | Ok &= Composite(FieldTy, FP, Value); |
169 | 0 | } |
170 | 0 | } |
171 | | |
172 | 0 | for (unsigned I = 0; I < NB; ++I) { |
173 | 0 | const Record::Base *BD = Record->getBase(I); |
174 | 0 | QualType BaseTy = Ctx.getASTContext().getRecordType(BD->Decl); |
175 | 0 | const Pointer &BP = Ptr.atField(BD->Offset); |
176 | 0 | Ok &= Composite(BaseTy, BP, R.getStructBase(I)); |
177 | 0 | } |
178 | |
|
179 | 0 | for (unsigned I = 0; I < NV; ++I) { |
180 | 0 | const Record::Base *VD = Record->getVirtualBase(I); |
181 | 0 | QualType VirtBaseTy = Ctx.getASTContext().getRecordType(VD->Decl); |
182 | 0 | const Pointer &VP = Ptr.atField(VD->Offset); |
183 | 0 | Ok &= Composite(VirtBaseTy, VP, R.getStructBase(NB + I)); |
184 | 0 | } |
185 | 0 | } |
186 | 0 | return Ok; |
187 | 0 | } |
188 | | |
189 | 0 | if (Ty->isIncompleteArrayType()) { |
190 | 0 | R = APValue(APValue::UninitArray(), 0, 0); |
191 | 0 | return true; |
192 | 0 | } |
193 | | |
194 | 0 | if (const auto *AT = Ty->getAsArrayTypeUnsafe()) { |
195 | 0 | const size_t NumElems = Ptr.getNumElems(); |
196 | 0 | QualType ElemTy = AT->getElementType(); |
197 | 0 | R = APValue(APValue::UninitArray{}, NumElems, NumElems); |
198 | |
|
199 | 0 | bool Ok = true; |
200 | 0 | for (unsigned I = 0; I < NumElems; ++I) { |
201 | 0 | APValue &Slot = R.getArrayInitializedElt(I); |
202 | 0 | const Pointer &EP = Ptr.atIndex(I); |
203 | 0 | if (std::optional<PrimType> T = Ctx.classify(ElemTy)) { |
204 | 0 | TYPE_SWITCH(*T, Ok &= ReturnValue<T>(EP.deref<T>(), Slot)); |
205 | 0 | } else { |
206 | 0 | Ok &= Composite(ElemTy, EP.narrow(), Slot); |
207 | 0 | } |
208 | 0 | } |
209 | 0 | return Ok; |
210 | 0 | } |
211 | | |
212 | | // Complex types. |
213 | 0 | if (const auto *CT = Ty->getAs<ComplexType>()) { |
214 | 0 | QualType ElemTy = CT->getElementType(); |
215 | 0 | std::optional<PrimType> ElemT = Ctx.classify(ElemTy); |
216 | 0 | assert(ElemT); |
217 | | |
218 | 0 | if (ElemTy->isIntegerType()) { |
219 | 0 | INT_TYPE_SWITCH(*ElemT, { |
220 | 0 | auto V1 = Ptr.atIndex(0).deref<T>(); |
221 | 0 | auto V2 = Ptr.atIndex(1).deref<T>(); |
222 | 0 | Result = APValue(V1.toAPSInt(), V2.toAPSInt()); |
223 | 0 | return true; |
224 | 0 | }); |
225 | 0 | } else if (ElemTy->isFloatingType()) { |
226 | 0 | Result = APValue(Ptr.atIndex(0).deref<Floating>().getAPFloat(), |
227 | 0 | Ptr.atIndex(1).deref<Floating>().getAPFloat()); |
228 | 0 | return true; |
229 | 0 | } |
230 | 0 | return false; |
231 | 0 | } |
232 | 0 | llvm_unreachable("invalid value to return"); |
233 | 0 | }; |
234 | | |
235 | | // Return the composite type. |
236 | 0 | const auto &Ptr = S.Stk.pop<Pointer>(); |
237 | 0 | return Composite(Ptr.getType(), Ptr, Result); |
238 | 0 | } |
239 | | |
240 | 0 | bool EvalEmitter::emitGetPtrLocal(uint32_t I, const SourceInfo &Info) { |
241 | 0 | if (!isActive()) |
242 | 0 | return true; |
243 | | |
244 | 0 | Block *B = getLocal(I); |
245 | 0 | S.Stk.push<Pointer>(B, sizeof(InlineDescriptor)); |
246 | 0 | return true; |
247 | 0 | } |
248 | | |
249 | | template <PrimType OpType> |
250 | 0 | bool EvalEmitter::emitGetLocal(uint32_t I, const SourceInfo &Info) { |
251 | 0 | if (!isActive()) |
252 | 0 | return true; |
253 | | |
254 | 0 | using T = typename PrimConv<OpType>::T; |
255 | |
|
256 | 0 | Block *B = getLocal(I); |
257 | 0 | S.Stk.push<T>(*reinterpret_cast<T *>(B->data())); |
258 | 0 | return true; |
259 | 0 | } Unexecuted instantiation: bool clang::interp::EvalEmitter::emitGetLocal<(clang::interp::PrimType)0>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitGetLocal<(clang::interp::PrimType)1>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitGetLocal<(clang::interp::PrimType)2>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitGetLocal<(clang::interp::PrimType)3>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitGetLocal<(clang::interp::PrimType)4>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitGetLocal<(clang::interp::PrimType)5>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitGetLocal<(clang::interp::PrimType)6>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitGetLocal<(clang::interp::PrimType)7>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitGetLocal<(clang::interp::PrimType)8>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitGetLocal<(clang::interp::PrimType)9>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitGetLocal<(clang::interp::PrimType)10>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitGetLocal<(clang::interp::PrimType)12>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitGetLocal<(clang::interp::PrimType)13>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitGetLocal<(clang::interp::PrimType)11>(unsigned int, clang::interp::SourceInfo const&) |
260 | | |
261 | | template <PrimType OpType> |
262 | 0 | bool EvalEmitter::emitSetLocal(uint32_t I, const SourceInfo &Info) { |
263 | 0 | if (!isActive()) |
264 | 0 | return true; |
265 | | |
266 | 0 | using T = typename PrimConv<OpType>::T; |
267 | |
|
268 | 0 | Block *B = getLocal(I); |
269 | 0 | *reinterpret_cast<T *>(B->data()) = S.Stk.pop<T>(); |
270 | 0 | InlineDescriptor &Desc = *reinterpret_cast<InlineDescriptor *>(B->rawData()); |
271 | 0 | Desc.IsInitialized = true; |
272 | |
|
273 | 0 | return true; |
274 | 0 | } Unexecuted instantiation: bool clang::interp::EvalEmitter::emitSetLocal<(clang::interp::PrimType)0>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitSetLocal<(clang::interp::PrimType)1>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitSetLocal<(clang::interp::PrimType)2>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitSetLocal<(clang::interp::PrimType)3>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitSetLocal<(clang::interp::PrimType)4>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitSetLocal<(clang::interp::PrimType)5>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitSetLocal<(clang::interp::PrimType)6>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitSetLocal<(clang::interp::PrimType)7>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitSetLocal<(clang::interp::PrimType)8>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitSetLocal<(clang::interp::PrimType)9>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitSetLocal<(clang::interp::PrimType)10>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitSetLocal<(clang::interp::PrimType)12>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitSetLocal<(clang::interp::PrimType)13>(unsigned int, clang::interp::SourceInfo const&) Unexecuted instantiation: bool clang::interp::EvalEmitter::emitSetLocal<(clang::interp::PrimType)11>(unsigned int, clang::interp::SourceInfo const&) |
275 | | |
276 | 0 | bool EvalEmitter::emitDestroy(uint32_t I, const SourceInfo &Info) { |
277 | 0 | if (!isActive()) |
278 | 0 | return true; |
279 | | |
280 | 0 | for (auto &Local : Descriptors[I]) { |
281 | 0 | Block *B = getLocal(Local.Offset); |
282 | 0 | S.deallocate(B); |
283 | 0 | } |
284 | |
|
285 | 0 | return true; |
286 | 0 | } |
287 | | |
288 | | //===----------------------------------------------------------------------===// |
289 | | // Opcode evaluators |
290 | | //===----------------------------------------------------------------------===// |
291 | | |
292 | | #define GET_EVAL_IMPL |
293 | | #include "Opcodes.inc" |
294 | | #undef GET_EVAL_IMPL |