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>(FuncDecl); 38 MD && isLambdaCallOperator(MD)) { 39 // Set up lambda capture to closure record field mapping. 40 const Record *R = P.getOrCreateRecord(MD->getParent()); 41 assert(R); 42 llvm::DenseMap<const ValueDecl *, FieldDecl *> LC; 43 FieldDecl *LTC; 44 45 MD->getParent()->getCaptureFields(LC, LTC); 46 47 for (auto Cap : LC) { 48 unsigned Offset = R->getField(Cap.second)->Offset; 49 this->LambdaCaptures[Cap.first] = { 50 Offset, Cap.second->getType()->isReferenceType()}; 51 } 52 if (LTC) { 53 QualType CaptureType = R->getField(LTC)->Decl->getType(); 54 this->LambdaThisCapture = {R->getField(LTC)->Offset, 55 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(Func->ParamOffsets, Drop)) { 64 const ParmVarDecl *PD = FuncDecl->parameters()[ParamIndex]; 65 std::optional<PrimType> T = Ctx.classify(PD->getType()); 66 this->Params.insert({PD, {ParamOffset, 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(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(std::move(DS)); 87 } 88 89 // Set the function's code. 90 Func->setCode(NextLocalOffset, std::move(Code), std::move(SrcMap), 91 std::move(Scopes), 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(D->getAllocSize()); 99 return {Location, D}; 100 } 101 102 void ByteCodeEmitter::emitLabel(LabelTy Label) { 103 const size_t Target = Code.size(); 104 LabelOffsets.insert({Label, Target}); 105 106 if (auto It = LabelRelocs.find(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(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>(Location, Offset); 115 } 116 LabelRelocs.erase(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(sizeof(Opcode)) + align(sizeof(int32_t)); 124 assert(aligned(Position)); 125 126 // If target is known, compute jump offset. 127 if (auto It = LabelOffsets.find(Label); It != LabelOffsets.end()) 128 return It->second - Position; 129 130 // Otherwise, record relocation and return dummy offset. 131 LabelRelocs[Label].push_back(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(Code.size()); 154 Size = align(Size); 155 assert(aligned(ValPos + Size)); 156 Code.resize(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(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(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, Op, Success); 220 if (SI) 221 SrcMap.emplace_back(Code.size(), 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(0); 246 PrimType T = Ctx.classify(Arg->getType()).value_or(PT_Ptr); 247 if (!this->emitBCP(getOffset(EndLabel), T, E)) 248 return false; 249 if (!this->visit(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