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 "Opcode.h" 12#include "Program.h" 13#include "clang/AST/DeclCXX.h" 14 15using namespace clang; 16using namespace clang::interp; 17 18using APSInt = llvm::APSInt; 19using Error = llvm::Error; 20 21Expected<Function *> ByteCodeEmitter::compileFunc(const FunctionDecl *F) { 22 // Do not try to compile undefined functions. 23 if (!F->isDefined(F) || (!F->hasBody() && F->willHaveBody())) 24 return nullptr; 25 26 // Set up argument indices. 27 unsigned ParamOffset = 0; 28 SmallVector<PrimType, 8> ParamTypes; 29 llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors; 30 31 // If the return is not a primitive, a pointer to the storage where the value 32 // is initialized in is passed as the first argument. 33 QualType Ty = F->getReturnType(); 34 if (!Ty->isVoidType() && !Ctx.classify(Ty)) { 35 ParamTypes.push_back(PT_Ptr); 36 ParamOffset += align(primSize(PT_Ptr)); 37 } 38 39 // Assign descriptors to all parameters. 40 // Composite objects are lowered to pointers. 41 for (const ParmVarDecl *PD : F->parameters()) { 42 PrimType Ty; 43 if (llvm::Optional<PrimType> T = Ctx.classify(PD->getType())) { 44 Ty = *T; 45 } else { 46 Ty = PT_Ptr; 47 } 48 49 Descriptor *Desc = P.createDescriptor(PD, Ty); 50 ParamDescriptors.insert({ParamOffset, {Ty, Desc}}); 51 Params.insert({PD, ParamOffset}); 52 ParamOffset += align(primSize(Ty)); 53 ParamTypes.push_back(Ty); 54 } 55 56 // Create a handle over the emitted code. 57 Function *Func = P.createFunction(F, ParamOffset, std::move(ParamTypes), 58 std::move(ParamDescriptors)); 59 // Compile the function body. 60 if (!F->isConstexpr() || !visitFunc(F)) { 61 // Return a dummy function if compilation failed. 62 if (BailLocation) 63 return llvm::make_error<ByteCodeGenError>(*BailLocation); 64 else 65 return Func; 66 } else { 67 // Create scopes from descriptors. 68 llvm::SmallVector<Scope, 2> Scopes; 69 for (auto &DS : Descriptors) { 70 Scopes.emplace_back(std::move(DS)); 71 } 72 73 // Set the function's code. 74 Func->setCode(NextLocalOffset, std::move(Code), std::move(SrcMap), 75 std::move(Scopes)); 76 return Func; 77 } 78} 79 80Scope::Local ByteCodeEmitter::createLocal(Descriptor *D) { 81 NextLocalOffset += sizeof(Block); 82 unsigned Location = NextLocalOffset; 83 NextLocalOffset += align(D->getAllocSize()); 84 return {Location, D}; 85} 86 87void ByteCodeEmitter::emitLabel(LabelTy Label) { 88 const size_t Target = Code.size(); 89 LabelOffsets.insert({Label, Target}); 90 auto It = LabelRelocs.find(Label); 91 if (It != LabelRelocs.end()) { 92 for (unsigned Reloc : It->second) { 93 using namespace llvm::support; 94 95 /// Rewrite the operand of all jumps to this label. 96 void *Location = Code.data() + Reloc - sizeof(int32_t); 97 const int32_t Offset = Target - static_cast<int64_t>(Reloc); 98 endian::write<int32_t, endianness::native, 1>(Location, Offset); 99 } 100 LabelRelocs.erase(It); 101 } 102} 103 104int32_t ByteCodeEmitter::getOffset(LabelTy Label) { 105 // Compute the PC offset which the jump is relative to. 106 const int64_t Position = Code.size() + sizeof(Opcode) + sizeof(int32_t); 107 108 // If target is known, compute jump offset. 109 auto It = LabelOffsets.find(Label); 110 if (It != LabelOffsets.end()) { 111 return It->second - Position; 112 } 113 114 // Otherwise, record relocation and return dummy offset. 115 LabelRelocs[Label].push_back(Position); 116 return 0ull; 117} 118 119bool ByteCodeEmitter::bail(const SourceLocation &Loc) { 120 if (!BailLocation) 121 BailLocation = Loc; 122 return false; 123} 124 125template <typename... Tys> 126bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) { 127 bool Success = true; 128 129 /// Helper to write bytecode and bail out if 32-bit offsets become invalid. 130 auto emit = [this, &Success](const char *Data, size_t Size) { 131 if (Code.size() + Size > std::numeric_limits<unsigned>::max()) { 132 Success = false; 133 return; 134 } 135 Code.insert(Code.end(), Data, Data + Size); 136 }; 137 138 /// The opcode is followed by arguments. The source info is 139 /// attached to the address after the opcode. 140 emit(reinterpret_cast<const char *>(&Op), sizeof(Opcode)); 141 if (SI) 142 SrcMap.emplace_back(Code.size(), SI); 143 144 /// The initializer list forces the expression to be evaluated 145 /// for each argument in the variadic template, in order. 146 (void)std::initializer_list<int>{ 147 (emit(reinterpret_cast<const char *>(&Args), sizeof(Args)), 0)...}; 148 149 return Success; 150} 151 152bool ByteCodeEmitter::jumpTrue(const LabelTy &Label) { 153 return emitJt(getOffset(Label), SourceInfo{}); 154} 155 156bool ByteCodeEmitter::jumpFalse(const LabelTy &Label) { 157 return emitJf(getOffset(Label), SourceInfo{}); 158} 159 160bool ByteCodeEmitter::jump(const LabelTy &Label) { 161 return emitJmp(getOffset(Label), SourceInfo{}); 162} 163 164bool ByteCodeEmitter::fallthrough(const LabelTy &Label) { 165 emitLabel(Label); 166 return true; 167} 168 169//===----------------------------------------------------------------------===// 170// Opcode emitters 171//===----------------------------------------------------------------------===// 172 173#define GET_LINK_IMPL 174#include "Opcodes.inc" 175#undef GET_LINK_IMPL 176