1 //===--- ByteCodeEmitter.cpp - Instruction emitter for the VM ---*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "ByteCodeEmitter.h"
10 #include "ByteCodeGenError.h"
15 #include "clang/AST/ASTLambda.h"
16 #include "clang/AST/DeclCXX.h"
17 #include <type_traits>
19 using namespace clang
;
20 using namespace clang::interp
;
23 ByteCodeEmitter::compileFunc(const FunctionDecl
*FuncDecl
) {
24 // Set up argument indices.
25 unsigned ParamOffset
= 0;
26 SmallVector
<PrimType
, 8> ParamTypes
;
27 SmallVector
<unsigned, 8> ParamOffsets
;
28 llvm::DenseMap
<unsigned, Function::ParamDescriptor
> ParamDescriptors
;
30 // If the return is not a primitive, a pointer to the storage where the
31 // value is initialized in is passed as the first argument. See 'RVO'
32 // elsewhere in the code.
33 QualType Ty
= FuncDecl
->getReturnType();
35 if (!Ty
->isVoidType() && !Ctx
.classify(Ty
)) {
37 ParamTypes
.push_back(PT_Ptr
);
38 ParamOffsets
.push_back(ParamOffset
);
39 ParamOffset
+= align(primSize(PT_Ptr
));
42 // If the function decl is a member decl, the next parameter is
43 // the 'this' pointer. This parameter is pop()ed from the
44 // InterpStack when calling the function.
45 bool HasThisPointer
= false;
46 if (const auto *MD
= dyn_cast
<CXXMethodDecl
>(FuncDecl
)) {
47 if (MD
->isImplicitObjectMemberFunction()) {
48 HasThisPointer
= true;
49 ParamTypes
.push_back(PT_Ptr
);
50 ParamOffsets
.push_back(ParamOffset
);
51 ParamOffset
+= align(primSize(PT_Ptr
));
54 // Set up lambda capture to closure record field mapping.
55 if (isLambdaCallOperator(MD
)) {
56 const Record
*R
= P
.getOrCreateRecord(MD
->getParent());
57 llvm::DenseMap
<const ValueDecl
*, FieldDecl
*> LC
;
60 MD
->getParent()->getCaptureFields(LC
, LTC
);
63 unsigned Offset
= R
->getField(Cap
.second
)->Offset
;
64 this->LambdaCaptures
[Cap
.first
] = {
65 Offset
, Cap
.second
->getType()->isReferenceType()};
68 this->LambdaThisCapture
= R
->getField(LTC
)->Offset
;
72 // Assign descriptors to all parameters.
73 // Composite objects are lowered to pointers.
74 for (const ParmVarDecl
*PD
: FuncDecl
->parameters()) {
75 std::optional
<PrimType
> T
= Ctx
.classify(PD
->getType());
76 PrimType PT
= T
.value_or(PT_Ptr
);
77 Descriptor
*Desc
= P
.createDescriptor(PD
, PT
);
78 ParamDescriptors
.insert({ParamOffset
, {PT
, Desc
}});
79 Params
.insert({PD
, {ParamOffset
, T
!= std::nullopt
}});
80 ParamOffsets
.push_back(ParamOffset
);
81 ParamOffset
+= align(primSize(PT
));
82 ParamTypes
.push_back(PT
);
85 // Create a handle over the emitted code.
86 Function
*Func
= P
.getFunction(FuncDecl
);
88 Func
= P
.createFunction(FuncDecl
, ParamOffset
, std::move(ParamTypes
),
89 std::move(ParamDescriptors
),
90 std::move(ParamOffsets
), HasThisPointer
, HasRVO
);
93 // For not-yet-defined functions, we only create a Function instance and
94 // compile their body later.
95 if (!FuncDecl
->isDefined()) {
96 Func
->setDefined(false);
100 Func
->setDefined(true);
102 // Lambda static invokers are a special case that we emit custom code for.
103 bool IsEligibleForCompilation
= false;
104 if (const auto *MD
= dyn_cast
<CXXMethodDecl
>(FuncDecl
))
105 IsEligibleForCompilation
= MD
->isLambdaStaticInvoker();
106 if (!IsEligibleForCompilation
)
107 IsEligibleForCompilation
= FuncDecl
->isConstexpr();
109 // Compile the function body.
110 if (!IsEligibleForCompilation
|| !visitFunc(FuncDecl
)) {
111 // Return a dummy function if compilation failed.
113 return llvm::make_error
<ByteCodeGenError
>(*BailLocation
);
115 Func
->setIsFullyCompiled(true);
119 // Create scopes from descriptors.
120 llvm::SmallVector
<Scope
, 2> Scopes
;
121 for (auto &DS
: Descriptors
) {
122 Scopes
.emplace_back(std::move(DS
));
125 // Set the function's code.
126 Func
->setCode(NextLocalOffset
, std::move(Code
), std::move(SrcMap
),
127 std::move(Scopes
), FuncDecl
->hasBody());
128 Func
->setIsFullyCompiled(true);
132 Scope::Local
ByteCodeEmitter::createLocal(Descriptor
*D
) {
133 NextLocalOffset
+= sizeof(Block
);
134 unsigned Location
= NextLocalOffset
;
135 NextLocalOffset
+= align(D
->getAllocSize());
136 return {Location
, D
};
139 void ByteCodeEmitter::emitLabel(LabelTy Label
) {
140 const size_t Target
= Code
.size();
141 LabelOffsets
.insert({Label
, Target
});
143 if (auto It
= LabelRelocs
.find(Label
);
144 It
!= LabelRelocs
.end()) {
145 for (unsigned Reloc
: It
->second
) {
146 using namespace llvm::support
;
148 // Rewrite the operand of all jumps to this label.
149 void *Location
= Code
.data() + Reloc
- align(sizeof(int32_t));
150 assert(aligned(Location
));
151 const int32_t Offset
= Target
- static_cast<int64_t>(Reloc
);
152 endian::write
<int32_t, llvm::endianness::native
>(Location
, Offset
);
154 LabelRelocs
.erase(It
);
158 int32_t ByteCodeEmitter::getOffset(LabelTy Label
) {
159 // Compute the PC offset which the jump is relative to.
160 const int64_t Position
=
161 Code
.size() + align(sizeof(Opcode
)) + align(sizeof(int32_t));
162 assert(aligned(Position
));
164 // If target is known, compute jump offset.
165 if (auto It
= LabelOffsets
.find(Label
);
166 It
!= LabelOffsets
.end())
167 return It
->second
- Position
;
169 // Otherwise, record relocation and return dummy offset.
170 LabelRelocs
[Label
].push_back(Position
);
174 bool ByteCodeEmitter::bail(const SourceLocation
&Loc
) {
180 /// Helper to write bytecode and bail out if 32-bit offsets become invalid.
181 /// Pointers will be automatically marshalled as 32-bit IDs.
182 template <typename T
>
183 static void emit(Program
&P
, std::vector
<std::byte
> &Code
, const T
&Val
,
187 if constexpr (std::is_pointer_v
<T
>)
188 Size
= sizeof(uint32_t);
192 if (Code
.size() + Size
> std::numeric_limits
<unsigned>::max()) {
197 // Access must be aligned!
198 size_t ValPos
= align(Code
.size());
200 assert(aligned(ValPos
+ Size
));
201 Code
.resize(ValPos
+ Size
);
203 if constexpr (!std::is_pointer_v
<T
>) {
204 new (Code
.data() + ValPos
) T(Val
);
206 uint32_t ID
= P
.getOrCreateNativePointer(Val
);
207 new (Code
.data() + ValPos
) uint32_t(ID
);
212 void emit(Program
&P
, std::vector
<std::byte
> &Code
, const Floating
&Val
,
214 size_t Size
= Val
.bytesToSerialize();
216 if (Code
.size() + Size
> std::numeric_limits
<unsigned>::max()) {
221 // Access must be aligned!
222 size_t ValPos
= align(Code
.size());
224 assert(aligned(ValPos
+ Size
));
225 Code
.resize(ValPos
+ Size
);
227 Val
.serialize(Code
.data() + ValPos
);
230 template <typename
... Tys
>
231 bool ByteCodeEmitter::emitOp(Opcode Op
, const Tys
&... Args
, const SourceInfo
&SI
) {
234 // The opcode is followed by arguments. The source info is
235 // attached to the address after the opcode.
236 emit(P
, Code
, Op
, Success
);
238 SrcMap
.emplace_back(Code
.size(), SI
);
240 // The initializer list forces the expression to be evaluated
241 // for each argument in the variadic template, in order.
242 (void)std::initializer_list
<int>{(emit(P
, Code
, Args
, Success
), 0)...};
247 bool ByteCodeEmitter::jumpTrue(const LabelTy
&Label
) {
248 return emitJt(getOffset(Label
), SourceInfo
{});
251 bool ByteCodeEmitter::jumpFalse(const LabelTy
&Label
) {
252 return emitJf(getOffset(Label
), SourceInfo
{});
255 bool ByteCodeEmitter::jump(const LabelTy
&Label
) {
256 return emitJmp(getOffset(Label
), SourceInfo
{});
259 bool ByteCodeEmitter::fallthrough(const LabelTy
&Label
) {
264 //===----------------------------------------------------------------------===//
266 //===----------------------------------------------------------------------===//
268 #define GET_LINK_IMPL
269 #include "Opcodes.inc"