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