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 15 using namespace clang; 16 using namespace clang::interp; 17 18 using APSInt = llvm::APSInt; 19 using Error = llvm::Error; 20 21 Expected<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 80 Scope::Local ByteCodeEmitter::createLocal(Descriptor *D) { 81 NextLocalOffset += sizeof(Block); 82 unsigned Location = NextLocalOffset; 83 NextLocalOffset += align(D->getAllocSize()); 84 return {Location, D}; 85 } 86 87 void 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 104 int32_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 119 bool ByteCodeEmitter::bail(const SourceLocation &Loc) { 120 if (!BailLocation) 121 BailLocation = Loc; 122 return false; 123 } 124 125 template <typename... Tys> 126 bool 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 152 bool ByteCodeEmitter::jumpTrue(const LabelTy &Label) { 153 return emitJt(getOffset(Label), SourceInfo{}); 154 } 155 156 bool ByteCodeEmitter::jumpFalse(const LabelTy &Label) { 157 return emitJf(getOffset(Label), SourceInfo{}); 158 } 159 160 bool ByteCodeEmitter::jump(const LabelTy &Label) { 161 return emitJmp(getOffset(Label), SourceInfo{}); 162 } 163 164 bool 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