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