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 "ByteCodeGenError.h" 11 #include "Context.h" 12 #include "Floating.h" 13 #include "Opcode.h" 14 #include "Program.h" 15 #include "clang/AST/ASTLambda.h" 16 #include "clang/AST/DeclCXX.h" 17 #include "clang/Basic/Builtins.h" 18 #include <type_traits> 19 20 using namespace clang; 21 using namespace clang::interp; 22 23 Function *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; 29 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(); 34 bool HasRVO = false; 35 if (!Ty->isVoidType() && !Ctx.classify(Ty)) { 36 HasRVO = true; 37 ParamTypes.push_back(PT_Ptr); 38 ParamOffsets.push_back(ParamOffset); 39 ParamOffset += align(primSize(PT_Ptr)); 40 } 41 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)); 52 } 53 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; 58 FieldDecl *LTC; 59 60 MD->getParent()->getCaptureFields(LC, LTC); 61 62 for (auto Cap : LC) { 63 // Static lambdas cannot have any captures. If this one does, 64 // it has already been diagnosed and we can only ignore it. 65 if (MD->isStatic()) 66 return nullptr; 67 68 unsigned Offset = R->getField(Cap.second)->Offset; 69 this->LambdaCaptures[Cap.first] = { 70 Offset, Cap.second->getType()->isReferenceType()}; 71 } 72 if (LTC) 73 this->LambdaThisCapture = R->getField(LTC)->Offset; 74 } 75 } 76 77 // Assign descriptors to all parameters. 78 // Composite objects are lowered to pointers. 79 for (const ParmVarDecl *PD : FuncDecl->parameters()) { 80 std::optional<PrimType> T = Ctx.classify(PD->getType()); 81 PrimType PT = T.value_or(PT_Ptr); 82 Descriptor *Desc = P.createDescriptor(PD, PT); 83 ParamDescriptors.insert({ParamOffset, {PT, Desc}}); 84 Params.insert({PD, {ParamOffset, T != std::nullopt}}); 85 ParamOffsets.push_back(ParamOffset); 86 ParamOffset += align(primSize(PT)); 87 ParamTypes.push_back(PT); 88 } 89 90 // Create a handle over the emitted code. 91 Function *Func = P.getFunction(FuncDecl); 92 if (!Func) { 93 bool IsUnevaluatedBuiltin = false; 94 if (unsigned BI = FuncDecl->getBuiltinID()) 95 IsUnevaluatedBuiltin = Ctx.getASTContext().BuiltinInfo.isUnevaluated(BI); 96 97 Func = 98 P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes), 99 std::move(ParamDescriptors), std::move(ParamOffsets), 100 HasThisPointer, HasRVO, IsUnevaluatedBuiltin); 101 } 102 103 assert(Func); 104 // For not-yet-defined functions, we only create a Function instance and 105 // compile their body later. 106 if (!FuncDecl->isDefined()) { 107 Func->setDefined(false); 108 return Func; 109 } 110 111 Func->setDefined(true); 112 113 // Lambda static invokers are a special case that we emit custom code for. 114 bool IsEligibleForCompilation = false; 115 if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl)) 116 IsEligibleForCompilation = MD->isLambdaStaticInvoker(); 117 if (!IsEligibleForCompilation) 118 IsEligibleForCompilation = FuncDecl->isConstexpr(); 119 120 // Compile the function body. 121 if (!IsEligibleForCompilation || !visitFunc(FuncDecl)) { 122 Func->setIsFullyCompiled(true); 123 return Func; 124 } 125 126 // Create scopes from descriptors. 127 llvm::SmallVector<Scope, 2> Scopes; 128 for (auto &DS : Descriptors) { 129 Scopes.emplace_back(std::move(DS)); 130 } 131 132 // Set the function's code. 133 Func->setCode(NextLocalOffset, std::move(Code), std::move(SrcMap), 134 std::move(Scopes), FuncDecl->hasBody()); 135 Func->setIsFullyCompiled(true); 136 return Func; 137 } 138 139 Scope::Local ByteCodeEmitter::createLocal(Descriptor *D) { 140 NextLocalOffset += sizeof(Block); 141 unsigned Location = NextLocalOffset; 142 NextLocalOffset += align(D->getAllocSize()); 143 return {Location, D}; 144 } 145 146 void ByteCodeEmitter::emitLabel(LabelTy Label) { 147 const size_t Target = Code.size(); 148 LabelOffsets.insert({Label, Target}); 149 150 if (auto It = LabelRelocs.find(Label); 151 It != LabelRelocs.end()) { 152 for (unsigned Reloc : It->second) { 153 using namespace llvm::support; 154 155 // Rewrite the operand of all jumps to this label. 156 void *Location = Code.data() + Reloc - align(sizeof(int32_t)); 157 assert(aligned(Location)); 158 const int32_t Offset = Target - static_cast<int64_t>(Reloc); 159 endian::write<int32_t, llvm::endianness::native>(Location, Offset); 160 } 161 LabelRelocs.erase(It); 162 } 163 } 164 165 int32_t ByteCodeEmitter::getOffset(LabelTy Label) { 166 // Compute the PC offset which the jump is relative to. 167 const int64_t Position = 168 Code.size() + align(sizeof(Opcode)) + align(sizeof(int32_t)); 169 assert(aligned(Position)); 170 171 // If target is known, compute jump offset. 172 if (auto It = LabelOffsets.find(Label); 173 It != LabelOffsets.end()) 174 return It->second - Position; 175 176 // Otherwise, record relocation and return dummy offset. 177 LabelRelocs[Label].push_back(Position); 178 return 0ull; 179 } 180 181 /// Helper to write bytecode and bail out if 32-bit offsets become invalid. 182 /// Pointers will be automatically marshalled as 32-bit IDs. 183 template <typename T> 184 static void emit(Program &P, std::vector<std::byte> &Code, const T &Val, 185 bool &Success) { 186 size_t Size; 187 188 if constexpr (std::is_pointer_v<T>) 189 Size = sizeof(uint32_t); 190 else 191 Size = sizeof(T); 192 193 if (Code.size() + Size > std::numeric_limits<unsigned>::max()) { 194 Success = false; 195 return; 196 } 197 198 // Access must be aligned! 199 size_t ValPos = align(Code.size()); 200 Size = align(Size); 201 assert(aligned(ValPos + Size)); 202 Code.resize(ValPos + Size); 203 204 if constexpr (!std::is_pointer_v<T>) { 205 new (Code.data() + ValPos) T(Val); 206 } else { 207 uint32_t ID = P.getOrCreateNativePointer(Val); 208 new (Code.data() + ValPos) uint32_t(ID); 209 } 210 } 211 212 template <> 213 void emit(Program &P, std::vector<std::byte> &Code, const Floating &Val, 214 bool &Success) { 215 size_t Size = Val.bytesToSerialize(); 216 217 if (Code.size() + Size > std::numeric_limits<unsigned>::max()) { 218 Success = false; 219 return; 220 } 221 222 // Access must be aligned! 223 size_t ValPos = align(Code.size()); 224 Size = align(Size); 225 assert(aligned(ValPos + Size)); 226 Code.resize(ValPos + Size); 227 228 Val.serialize(Code.data() + ValPos); 229 } 230 231 template <typename... Tys> 232 bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) { 233 bool Success = true; 234 235 // The opcode is followed by arguments. The source info is 236 // attached to the address after the opcode. 237 emit(P, Code, Op, Success); 238 if (SI) 239 SrcMap.emplace_back(Code.size(), SI); 240 241 // The initializer list forces the expression to be evaluated 242 // for each argument in the variadic template, in order. 243 (void)std::initializer_list<int>{(emit(P, Code, Args, Success), 0)...}; 244 245 return Success; 246 } 247 248 bool ByteCodeEmitter::jumpTrue(const LabelTy &Label) { 249 return emitJt(getOffset(Label), SourceInfo{}); 250 } 251 252 bool ByteCodeEmitter::jumpFalse(const LabelTy &Label) { 253 return emitJf(getOffset(Label), SourceInfo{}); 254 } 255 256 bool ByteCodeEmitter::jump(const LabelTy &Label) { 257 return emitJmp(getOffset(Label), SourceInfo{}); 258 } 259 260 bool ByteCodeEmitter::fallthrough(const LabelTy &Label) { 261 emitLabel(Label); 262 return true; 263 } 264 265 //===----------------------------------------------------------------------===// 266 // Opcode emitters 267 //===----------------------------------------------------------------------===// 268 269 #define GET_LINK_IMPL 270 #include "Opcodes.inc" 271 #undef GET_LINK_IMPL 272