| 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 | assert(FuncDecl->isThisDeclarationADefinition()); |
| 28 | |
| 29 | // Manually created functions that haven't been assigned proper |
| 30 | // parameters yet. |
| 31 | if (!FuncDecl->param_empty() && !FuncDecl->param_begin()) |
| 32 | return; |
| 33 | |
| 34 | // Set up lambda captures. |
| 35 | if (Func->isLambdaCallOperator()) { |
| 36 | // Set up lambda capture to closure record field mapping. |
| 37 | const CXXRecordDecl *ParentDecl = Func->getParentDecl(); |
| 38 | const Record *R = P.getOrCreateRecord(RD: ParentDecl); |
| 39 | assert(R); |
| 40 | llvm::DenseMap<const ValueDecl *, FieldDecl *> LC; |
| 41 | FieldDecl *LTC; |
| 42 | |
| 43 | ParentDecl->getCaptureFields(Captures&: LC, ThisCapture&: LTC); |
| 44 | |
| 45 | for (auto Cap : LC) { |
| 46 | unsigned Offset = R->getField(FD: Cap.second)->Offset; |
| 47 | this->LambdaCaptures[Cap.first] = { |
| 48 | .Offset: Offset, .IsPtr: Cap.second->getType()->isReferenceType()}; |
| 49 | } |
| 50 | if (LTC) { |
| 51 | QualType CaptureType = R->getField(FD: LTC)->Decl->getType(); |
| 52 | this->LambdaThisCapture = {.Offset: R->getField(FD: LTC)->Offset, |
| 53 | .IsPtr: CaptureType->isPointerOrReferenceType()}; |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | bool IsValid = !FuncDecl->isInvalidDecl(); |
| 58 | // Register parameters with their offset. |
| 59 | unsigned ParamIndex = 0; |
| 60 | unsigned Drop = Func->hasRVO() + |
| 61 | (Func->hasThisPointer() && !Func->isThisPointerExplicit()); |
| 62 | |
| 63 | for (const auto &ParamDesc : llvm::drop_begin(RangeOrContainer&: Func->ParamDescriptors, N: Drop)) { |
| 64 | const ParmVarDecl *PD = FuncDecl->getParamDecl(i: ParamIndex); |
| 65 | if (PD->isInvalidDecl()) |
| 66 | IsValid = false; |
| 67 | this->Params.insert( |
| 68 | KV: {PD, {.Offset: ParamDesc.Offset, .IsPtr: Ctx.canClassify(T: PD->getType())}}); |
| 69 | ++ParamIndex; |
| 70 | } |
| 71 | |
| 72 | Func->setDefined(true); |
| 73 | |
| 74 | // Lambda static invokers are a special case that we emit custom code for. |
| 75 | bool IsEligibleForCompilation = Func->isLambdaStaticInvoker() || |
| 76 | FuncDecl->isConstexpr() || |
| 77 | FuncDecl->hasAttr<MSConstexprAttr>(); |
| 78 | |
| 79 | // Compile the function body. |
| 80 | if (!IsEligibleForCompilation || !visitFunc(E: FuncDecl)) { |
| 81 | Func->setIsFullyCompiled(true); |
| 82 | return; |
| 83 | } |
| 84 | |
| 85 | // Create scopes from descriptors. |
| 86 | llvm::SmallVector<Scope, 2> Scopes; |
| 87 | for (auto &DS : Descriptors) { |
| 88 | Scopes.emplace_back(Args: std::move(DS)); |
| 89 | } |
| 90 | |
| 91 | // Set the function's code. |
| 92 | Func->setCode(Source: FuncDecl, NewFrameSize: NextLocalOffset, NewCode: std::move(Code), NewSrcMap: std::move(SrcMap), |
| 93 | NewScopes: std::move(Scopes), NewHasBody: FuncDecl->hasBody(), NewIsValid: IsValid); |
| 94 | Func->setIsFullyCompiled(true); |
| 95 | } |
| 96 | |
| 97 | Scope::Local ByteCodeEmitter::createLocal(Descriptor *D) { |
| 98 | NextLocalOffset += sizeof(Block); |
| 99 | unsigned Location = NextLocalOffset; |
| 100 | NextLocalOffset += align(Size: D->getAllocSize()); |
| 101 | return {.Offset: Location, .Desc: D}; |
| 102 | } |
| 103 | |
| 104 | void ByteCodeEmitter::emitLabel(LabelTy Label) { |
| 105 | const size_t Target = Code.size(); |
| 106 | LabelOffsets.insert(KV: {Label, Target}); |
| 107 | |
| 108 | if (auto It = LabelRelocs.find(Val: Label); It != LabelRelocs.end()) { |
| 109 | for (unsigned Reloc : It->second) { |
| 110 | using namespace llvm::support; |
| 111 | |
| 112 | // Rewrite the operand of all jumps to this label. |
| 113 | void *Location = Code.data() + Reloc - align(Size: sizeof(int32_t)); |
| 114 | assert(aligned(Location)); |
| 115 | const int32_t Offset = Target - static_cast<int64_t>(Reloc); |
| 116 | endian::write<int32_t, llvm::endianness::native>(P: Location, V: Offset); |
| 117 | } |
| 118 | LabelRelocs.erase(I: It); |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | int32_t ByteCodeEmitter::getOffset(LabelTy Label) { |
| 123 | // Compute the PC offset which the jump is relative to. |
| 124 | const int64_t Position = |
| 125 | Code.size() + align(Size: sizeof(Opcode)) + align(Size: sizeof(int32_t)); |
| 126 | assert(aligned(Position)); |
| 127 | |
| 128 | // If target is known, compute jump offset. |
| 129 | if (auto It = LabelOffsets.find(Val: Label); It != LabelOffsets.end()) |
| 130 | return It->second - Position; |
| 131 | |
| 132 | // Otherwise, record relocation and return dummy offset. |
| 133 | LabelRelocs[Label].push_back(Elt: Position); |
| 134 | return 0ull; |
| 135 | } |
| 136 | |
| 137 | /// Helper to write bytecode and bail out if 32-bit offsets become invalid. |
| 138 | /// Pointers will be automatically marshalled as 32-bit IDs. |
| 139 | template <typename T> |
| 140 | static void emit(Program &P, llvm::SmallVectorImpl<std::byte> &Code, |
| 141 | const T &Val, bool &Success) { |
| 142 | size_t ValPos = Code.size(); |
| 143 | size_t Size; |
| 144 | |
| 145 | if constexpr (std::is_pointer_v<T>) |
| 146 | Size = align(Size: sizeof(uint32_t)); |
| 147 | else |
| 148 | Size = align(Size: sizeof(T)); |
| 149 | |
| 150 | if (ValPos + Size > std::numeric_limits<unsigned>::max()) { |
| 151 | Success = false; |
| 152 | return; |
| 153 | } |
| 154 | |
| 155 | // Access must be aligned! |
| 156 | assert(aligned(ValPos)); |
| 157 | assert(aligned(ValPos + Size)); |
| 158 | Code.resize_for_overwrite(N: ValPos + Size); |
| 159 | |
| 160 | if constexpr (!std::is_pointer_v<T>) { |
| 161 | new (Code.data() + ValPos) T(Val); |
| 162 | } else { |
| 163 | uint32_t ID = P.getOrCreateNativePointer(Ptr: Val); |
| 164 | new (Code.data() + ValPos) uint32_t(ID); |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | /// Emits a serializable value. These usually (potentially) contain |
| 169 | /// heap-allocated memory and aren't trivially copyable. |
| 170 | template <typename T> |
| 171 | static void emitSerialized(llvm::SmallVectorImpl<std::byte> &Code, const T &Val, |
| 172 | bool &Success) { |
| 173 | size_t ValPos = Code.size(); |
| 174 | size_t Size = align(Val.bytesToSerialize()); |
| 175 | |
| 176 | if (ValPos + Size > std::numeric_limits<unsigned>::max()) { |
| 177 | Success = false; |
| 178 | return; |
| 179 | } |
| 180 | |
| 181 | // Access must be aligned! |
| 182 | assert(aligned(ValPos)); |
| 183 | assert(aligned(ValPos + Size)); |
| 184 | Code.resize_for_overwrite(N: ValPos + Size); |
| 185 | |
| 186 | Val.serialize(Code.data() + ValPos); |
| 187 | } |
| 188 | |
| 189 | template <> |
| 190 | void emit(Program &P, llvm::SmallVectorImpl<std::byte> &Code, |
| 191 | const Floating &Val, bool &Success) { |
| 192 | emitSerialized(Code, Val, Success); |
| 193 | } |
| 194 | |
| 195 | template <> |
| 196 | void emit(Program &P, llvm::SmallVectorImpl<std::byte> &Code, |
| 197 | const IntegralAP<false> &Val, bool &Success) { |
| 198 | emitSerialized(Code, Val, Success); |
| 199 | } |
| 200 | |
| 201 | template <> |
| 202 | void emit(Program &P, llvm::SmallVectorImpl<std::byte> &Code, |
| 203 | const IntegralAP<true> &Val, bool &Success) { |
| 204 | emitSerialized(Code, Val, Success); |
| 205 | } |
| 206 | |
| 207 | template <> |
| 208 | void emit(Program &P, llvm::SmallVectorImpl<std::byte> &Code, |
| 209 | const FixedPoint &Val, bool &Success) { |
| 210 | emitSerialized(Code, Val, Success); |
| 211 | } |
| 212 | |
| 213 | template <typename... Tys> |
| 214 | bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &...Args, 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 (LocOverride) |
| 221 | SrcMap.emplace_back(args: Code.size(), args&: *LocOverride); |
| 222 | else if (SI) |
| 223 | SrcMap.emplace_back(args: Code.size(), args&: SI); |
| 224 | |
| 225 | (..., emit(P, Code, Args, Success)); |
| 226 | return Success; |
| 227 | } |
| 228 | |
| 229 | bool ByteCodeEmitter::jumpTrue(const LabelTy &Label) { |
| 230 | return emitJt(getOffset(Label), SourceInfo{}); |
| 231 | } |
| 232 | |
| 233 | bool ByteCodeEmitter::jumpFalse(const LabelTy &Label) { |
| 234 | return emitJf(getOffset(Label), SourceInfo{}); |
| 235 | } |
| 236 | |
| 237 | bool ByteCodeEmitter::jump(const LabelTy &Label) { |
| 238 | return emitJmp(getOffset(Label), SourceInfo{}); |
| 239 | } |
| 240 | |
| 241 | bool ByteCodeEmitter::fallthrough(const LabelTy &Label) { |
| 242 | emitLabel(Label); |
| 243 | return true; |
| 244 | } |
| 245 | |
| 246 | bool ByteCodeEmitter::speculate(const CallExpr *E, const LabelTy &EndLabel) { |
| 247 | const Expr *Arg = E->getArg(Arg: 0); |
| 248 | PrimType T = Ctx.classify(T: Arg->getType()).value_or(PT: PT_Ptr); |
| 249 | if (!this->emitBCP(getOffset(Label: EndLabel), T, E)) |
| 250 | return false; |
| 251 | if (!this->visit(E: Arg)) |
| 252 | return false; |
| 253 | return true; |
| 254 | } |
| 255 | |
| 256 | //===----------------------------------------------------------------------===// |
| 257 | // Opcode emitters |
| 258 | //===----------------------------------------------------------------------===// |
| 259 | |
| 260 | #define GET_LINK_IMPL |
| 261 | #include "Opcodes.inc" |
| 262 | #undef GET_LINK_IMPL |
| 263 | |