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