1 | //===--- ByteCodeEmitter.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 "ByteCodeEmitter.h" |
10 | #include "Context.h" |
11 | #include "Floating.h" |
12 | #include "IntegralAP.h" |
13 | #include "Opcode.h" |
14 | #include "Program.h" |
15 | #include "clang/AST/ASTLambda.h" |
16 | #include "clang/AST/Attr.h" |
17 | #include "clang/AST/DeclCXX.h" |
18 | #include <type_traits> |
19 | |
20 | using namespace clang; |
21 | using namespace clang::interp; |
22 | |
23 | void ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl, |
24 | Function *Func) { |
25 | assert(FuncDecl); |
26 | assert(Func); |
27 | |
28 | // Manually created functions that haven't been assigned proper |
29 | // parameters yet. |
30 | if (!FuncDecl->param_empty() && !FuncDecl->param_begin()) |
31 | return; |
32 | |
33 | if (!FuncDecl->isDefined()) |
34 | return; |
35 | |
36 | // Set up lambda captures. |
37 | if (const auto *MD = dyn_cast<CXXMethodDecl>(Val: FuncDecl); |
38 | MD && isLambdaCallOperator(MD)) { |
39 | // Set up lambda capture to closure record field mapping. |
40 | const Record *R = P.getOrCreateRecord(RD: MD->getParent()); |
41 | assert(R); |
42 | llvm::DenseMap<const ValueDecl *, FieldDecl *> LC; |
43 | FieldDecl *LTC; |
44 | |
45 | MD->getParent()->getCaptureFields(Captures&: LC, ThisCapture&: LTC); |
46 | |
47 | for (auto Cap : LC) { |
48 | unsigned Offset = R->getField(FD: Cap.second)->Offset; |
49 | this->LambdaCaptures[Cap.first] = { |
50 | .Offset: Offset, .IsPtr: Cap.second->getType()->isReferenceType()}; |
51 | } |
52 | if (LTC) { |
53 | QualType CaptureType = R->getField(FD: LTC)->Decl->getType(); |
54 | this->LambdaThisCapture = {.Offset: R->getField(FD: LTC)->Offset, |
55 | .IsPtr: CaptureType->isPointerOrReferenceType()}; |
56 | } |
57 | } |
58 | |
59 | // Register parameters with their offset. |
60 | unsigned ParamIndex = 0; |
61 | unsigned Drop = Func->hasRVO() + |
62 | (Func->hasThisPointer() && !Func->isThisPointerExplicit()); |
63 | for (auto ParamOffset : llvm::drop_begin(RangeOrContainer&: Func->ParamOffsets, N: Drop)) { |
64 | const ParmVarDecl *PD = FuncDecl->parameters()[ParamIndex]; |
65 | std::optional<PrimType> T = Ctx.classify(T: PD->getType()); |
66 | this->Params.insert(KV: {PD, {.Offset: ParamOffset, .IsPtr: T != std::nullopt}}); |
67 | ++ParamIndex; |
68 | } |
69 | |
70 | Func->setDefined(true); |
71 | |
72 | // Lambda static invokers are a special case that we emit custom code for. |
73 | bool IsEligibleForCompilation = Func->isLambdaStaticInvoker() || |
74 | FuncDecl->isConstexpr() || |
75 | FuncDecl->hasAttr<MSConstexprAttr>(); |
76 | |
77 | // Compile the function body. |
78 | if (!IsEligibleForCompilation || !visitFunc(E: FuncDecl)) { |
79 | Func->setIsFullyCompiled(true); |
80 | return; |
81 | } |
82 | |
83 | // Create scopes from descriptors. |
84 | llvm::SmallVector<Scope, 2> Scopes; |
85 | for (auto &DS : Descriptors) { |
86 | Scopes.emplace_back(Args: std::move(DS)); |
87 | } |
88 | |
89 | // Set the function's code. |
90 | Func->setCode(NewFrameSize: NextLocalOffset, NewCode: std::move(Code), NewSrcMap: std::move(SrcMap), |
91 | NewScopes: std::move(Scopes), NewHasBody: FuncDecl->hasBody()); |
92 | Func->setIsFullyCompiled(true); |
93 | } |
94 | |
95 | Scope::Local ByteCodeEmitter::createLocal(Descriptor *D) { |
96 | NextLocalOffset += sizeof(Block); |
97 | unsigned Location = NextLocalOffset; |
98 | NextLocalOffset += align(Size: D->getAllocSize()); |
99 | return {.Offset: Location, .Desc: D}; |
100 | } |
101 | |
102 | void ByteCodeEmitter::emitLabel(LabelTy Label) { |
103 | const size_t Target = Code.size(); |
104 | LabelOffsets.insert(KV: {Label, Target}); |
105 | |
106 | if (auto It = LabelRelocs.find(Val: Label); It != LabelRelocs.end()) { |
107 | for (unsigned Reloc : It->second) { |
108 | using namespace llvm::support; |
109 | |
110 | // Rewrite the operand of all jumps to this label. |
111 | void *Location = Code.data() + Reloc - align(Size: sizeof(int32_t)); |
112 | assert(aligned(Location)); |
113 | const int32_t Offset = Target - static_cast<int64_t>(Reloc); |
114 | endian::write<int32_t, llvm::endianness::native>(P: Location, V: Offset); |
115 | } |
116 | LabelRelocs.erase(I: It); |
117 | } |
118 | } |
119 | |
120 | int32_t ByteCodeEmitter::getOffset(LabelTy Label) { |
121 | // Compute the PC offset which the jump is relative to. |
122 | const int64_t Position = |
123 | Code.size() + align(Size: sizeof(Opcode)) + align(Size: sizeof(int32_t)); |
124 | assert(aligned(Position)); |
125 | |
126 | // If target is known, compute jump offset. |
127 | if (auto It = LabelOffsets.find(Val: Label); It != LabelOffsets.end()) |
128 | return It->second - Position; |
129 | |
130 | // Otherwise, record relocation and return dummy offset. |
131 | LabelRelocs[Label].push_back(Elt: Position); |
132 | return 0ull; |
133 | } |
134 | |
135 | /// Helper to write bytecode and bail out if 32-bit offsets become invalid. |
136 | /// Pointers will be automatically marshalled as 32-bit IDs. |
137 | template <typename T> |
138 | static void emit(Program &P, std::vector<std::byte> &Code, const T &Val, |
139 | bool &Success) { |
140 | size_t Size; |
141 | |
142 | if constexpr (std::is_pointer_v<T>) |
143 | Size = sizeof(uint32_t); |
144 | else |
145 | Size = sizeof(T); |
146 | |
147 | if (Code.size() + Size > std::numeric_limits<unsigned>::max()) { |
148 | Success = false; |
149 | return; |
150 | } |
151 | |
152 | // Access must be aligned! |
153 | size_t ValPos = align(Size: Code.size()); |
154 | Size = align(Size); |
155 | assert(aligned(ValPos + Size)); |
156 | Code.resize(new_size: ValPos + Size); |
157 | |
158 | if constexpr (!std::is_pointer_v<T>) { |
159 | new (Code.data() + ValPos) T(Val); |
160 | } else { |
161 | uint32_t ID = P.getOrCreateNativePointer(Ptr: Val); |
162 | new (Code.data() + ValPos) uint32_t(ID); |
163 | } |
164 | } |
165 | |
166 | /// Emits a serializable value. These usually (potentially) contain |
167 | /// heap-allocated memory and aren't trivially copyable. |
168 | template <typename T> |
169 | static void emitSerialized(std::vector<std::byte> &Code, const T &Val, |
170 | bool &Success) { |
171 | size_t Size = Val.bytesToSerialize(); |
172 | |
173 | if (Code.size() + Size > std::numeric_limits<unsigned>::max()) { |
174 | Success = false; |
175 | return; |
176 | } |
177 | |
178 | // Access must be aligned! |
179 | assert(aligned(Code.size())); |
180 | size_t ValPos = Code.size(); |
181 | Size = align(Size); |
182 | assert(aligned(ValPos + Size)); |
183 | Code.resize(new_size: ValPos + Size); |
184 | |
185 | Val.serialize(Code.data() + ValPos); |
186 | } |
187 | |
188 | template <> |
189 | void emit(Program &P, std::vector<std::byte> &Code, const Floating &Val, |
190 | bool &Success) { |
191 | emitSerialized(Code, Val, Success); |
192 | } |
193 | |
194 | template <> |
195 | void emit(Program &P, std::vector<std::byte> &Code, |
196 | const IntegralAP<false> &Val, bool &Success) { |
197 | emitSerialized(Code, Val, Success); |
198 | } |
199 | |
200 | template <> |
201 | void emit(Program &P, std::vector<std::byte> &Code, const IntegralAP<true> &Val, |
202 | bool &Success) { |
203 | emitSerialized(Code, Val, Success); |
204 | } |
205 | |
206 | template <> |
207 | void emit(Program &P, std::vector<std::byte> &Code, const FixedPoint &Val, |
208 | bool &Success) { |
209 | emitSerialized(Code, Val, Success); |
210 | } |
211 | |
212 | template <typename... Tys> |
213 | bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &...Args, |
214 | const SourceInfo &SI) { |
215 | bool Success = true; |
216 | |
217 | // The opcode is followed by arguments. The source info is |
218 | // attached to the address after the opcode. |
219 | emit(P, Code, Val: Op, Success); |
220 | if (SI) |
221 | SrcMap.emplace_back(args: Code.size(), args: SI); |
222 | |
223 | (..., emit(P, Code, Args, Success)); |
224 | return Success; |
225 | } |
226 | |
227 | bool ByteCodeEmitter::jumpTrue(const LabelTy &Label) { |
228 | return emitJt(getOffset(Label), SourceInfo{}); |
229 | } |
230 | |
231 | bool ByteCodeEmitter::jumpFalse(const LabelTy &Label) { |
232 | return emitJf(getOffset(Label), SourceInfo{}); |
233 | } |
234 | |
235 | bool ByteCodeEmitter::jump(const LabelTy &Label) { |
236 | return emitJmp(getOffset(Label), SourceInfo{}); |
237 | } |
238 | |
239 | bool ByteCodeEmitter::fallthrough(const LabelTy &Label) { |
240 | emitLabel(Label); |
241 | return true; |
242 | } |
243 | |
244 | bool ByteCodeEmitter::speculate(const CallExpr *E, const LabelTy &EndLabel) { |
245 | const Expr *Arg = E->getArg(Arg: 0); |
246 | PrimType T = Ctx.classify(T: Arg->getType()).value_or(u: PT_Ptr); |
247 | if (!this->emitBCP(getOffset(Label: EndLabel), T, E)) |
248 | return false; |
249 | if (!this->visit(E: Arg)) |
250 | return false; |
251 | return true; |
252 | } |
253 | |
254 | //===----------------------------------------------------------------------===// |
255 | // Opcode emitters |
256 | //===----------------------------------------------------------------------===// |
257 | |
258 | #define GET_LINK_IMPL |
259 | #include "Opcodes.inc" |
260 | #undef GET_LINK_IMPL |
261 | |