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 "Opcode.h"
12 #include "Program.h"
13 #include "clang/AST/DeclCXX.h"
14 #include <type_traits>
15
16 using namespace clang;
17 using namespace clang::interp;
18
19 using APSInt = llvm::APSInt;
20 using Error = llvm::Error;
21
compileFunc(const FunctionDecl * F)22 Expected<Function *> ByteCodeEmitter::compileFunc(const FunctionDecl *F) {
23 // Do not try to compile undefined functions.
24 if (!F->isDefined(F) || (!F->hasBody() && F->willHaveBody()))
25 return nullptr;
26
27 // Set up argument indices.
28 unsigned ParamOffset = 0;
29 SmallVector<PrimType, 8> ParamTypes;
30 llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors;
31
32 // If the return is not a primitive, a pointer to the storage where the value
33 // is initialized in is passed as the first argument.
34 QualType Ty = F->getReturnType();
35 if (!Ty->isVoidType() && !Ctx.classify(Ty)) {
36 ParamTypes.push_back(PT_Ptr);
37 ParamOffset += align(primSize(PT_Ptr));
38 }
39
40 // Assign descriptors to all parameters.
41 // Composite objects are lowered to pointers.
42 for (const ParmVarDecl *PD : F->parameters()) {
43 PrimType Ty;
44 if (llvm::Optional<PrimType> T = Ctx.classify(PD->getType())) {
45 Ty = *T;
46 } else {
47 Ty = PT_Ptr;
48 }
49
50 Descriptor *Desc = P.createDescriptor(PD, Ty);
51 ParamDescriptors.insert({ParamOffset, {Ty, Desc}});
52 Params.insert({PD, ParamOffset});
53 ParamOffset += align(primSize(Ty));
54 ParamTypes.push_back(Ty);
55 }
56
57 // Create a handle over the emitted code.
58 Function *Func = P.createFunction(F, ParamOffset, std::move(ParamTypes),
59 std::move(ParamDescriptors));
60 // Compile the function body.
61 if (!F->isConstexpr() || !visitFunc(F)) {
62 // Return a dummy function if compilation failed.
63 if (BailLocation)
64 return llvm::make_error<ByteCodeGenError>(*BailLocation);
65 else
66 return Func;
67 } else {
68 // Create scopes from descriptors.
69 llvm::SmallVector<Scope, 2> Scopes;
70 for (auto &DS : Descriptors) {
71 Scopes.emplace_back(std::move(DS));
72 }
73
74 // Set the function's code.
75 Func->setCode(NextLocalOffset, std::move(Code), std::move(SrcMap),
76 std::move(Scopes));
77 return Func;
78 }
79 }
80
createLocal(Descriptor * D)81 Scope::Local ByteCodeEmitter::createLocal(Descriptor *D) {
82 NextLocalOffset += sizeof(Block);
83 unsigned Location = NextLocalOffset;
84 NextLocalOffset += align(D->getAllocSize());
85 return {Location, D};
86 }
87
emitLabel(LabelTy Label)88 void ByteCodeEmitter::emitLabel(LabelTy Label) {
89 const size_t Target = Code.size();
90 LabelOffsets.insert({Label, Target});
91 auto It = LabelRelocs.find(Label);
92 if (It != LabelRelocs.end()) {
93 for (unsigned Reloc : It->second) {
94 using namespace llvm::support;
95
96 /// Rewrite the operand of all jumps to this label.
97 void *Location = Code.data() + Reloc - sizeof(int32_t);
98 const int32_t Offset = Target - static_cast<int64_t>(Reloc);
99 endian::write<int32_t, endianness::native, 1>(Location, Offset);
100 }
101 LabelRelocs.erase(It);
102 }
103 }
104
getOffset(LabelTy Label)105 int32_t ByteCodeEmitter::getOffset(LabelTy Label) {
106 // Compute the PC offset which the jump is relative to.
107 const int64_t Position = Code.size() + sizeof(Opcode) + sizeof(int32_t);
108
109 // If target is known, compute jump offset.
110 auto It = LabelOffsets.find(Label);
111 if (It != LabelOffsets.end()) {
112 return It->second - Position;
113 }
114
115 // Otherwise, record relocation and return dummy offset.
116 LabelRelocs[Label].push_back(Position);
117 return 0ull;
118 }
119
bail(const SourceLocation & Loc)120 bool ByteCodeEmitter::bail(const SourceLocation &Loc) {
121 if (!BailLocation)
122 BailLocation = Loc;
123 return false;
124 }
125
126 /// Helper to write bytecode and bail out if 32-bit offsets become invalid.
127 /// Pointers will be automatically marshalled as 32-bit IDs.
128 template <typename T>
129 static std::enable_if_t<!std::is_pointer<T>::value, void>
emit(Program & P,std::vector<char> & Code,const T & Val,bool & Success)130 emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) {
131 size_t Size = sizeof(Val);
132 if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
133 Success = false;
134 return;
135 }
136
137 const char *Data = reinterpret_cast<const char *>(&Val);
138 Code.insert(Code.end(), Data, Data + Size);
139 }
140
141 template <typename T>
142 static std::enable_if_t<std::is_pointer<T>::value, void>
emit(Program & P,std::vector<char> & Code,const T & Val,bool & Success)143 emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) {
144 size_t Size = sizeof(uint32_t);
145 if (Code.size() + Size > std::numeric_limits<unsigned>::max()) {
146 Success = false;
147 return;
148 }
149
150 uint32_t ID = P.getOrCreateNativePointer(Val);
151 const char *Data = reinterpret_cast<const char *>(&ID);
152 Code.insert(Code.end(), Data, Data + Size);
153 }
154
155 template <typename... Tys>
emitOp(Opcode Op,const Tys &...Args,const SourceInfo & SI)156 bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) {
157 bool Success = true;
158
159 /// The opcode is followed by arguments. The source info is
160 /// attached to the address after the opcode.
161 emit(P, Code, Op, Success);
162 if (SI)
163 SrcMap.emplace_back(Code.size(), SI);
164
165 /// The initializer list forces the expression to be evaluated
166 /// for each argument in the variadic template, in order.
167 (void)std::initializer_list<int>{(emit(P, Code, Args, Success), 0)...};
168
169 return Success;
170 }
171
jumpTrue(const LabelTy & Label)172 bool ByteCodeEmitter::jumpTrue(const LabelTy &Label) {
173 return emitJt(getOffset(Label), SourceInfo{});
174 }
175
jumpFalse(const LabelTy & Label)176 bool ByteCodeEmitter::jumpFalse(const LabelTy &Label) {
177 return emitJf(getOffset(Label), SourceInfo{});
178 }
179
jump(const LabelTy & Label)180 bool ByteCodeEmitter::jump(const LabelTy &Label) {
181 return emitJmp(getOffset(Label), SourceInfo{});
182 }
183
fallthrough(const LabelTy & Label)184 bool ByteCodeEmitter::fallthrough(const LabelTy &Label) {
185 emitLabel(Label);
186 return true;
187 }
188
189 //===----------------------------------------------------------------------===//
190 // Opcode emitters
191 //===----------------------------------------------------------------------===//
192
193 #define GET_LINK_IMPL
194 #include "Opcodes.inc"
195 #undef GET_LINK_IMPL
196