| //===--- ByteCodeEmitter.cpp - Instruction emitter for the VM ---*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ByteCodeEmitter.h" |
| #include "Context.h" |
| #include "Opcode.h" |
| #include "Program.h" |
| #include "clang/AST/DeclCXX.h" |
| |
| using namespace clang; |
| using namespace clang::interp; |
| |
| using APSInt = llvm::APSInt; |
| using Error = llvm::Error; |
| |
| Expected<Function *> ByteCodeEmitter::compileFunc(const FunctionDecl *F) { |
| // Do not try to compile undefined functions. |
| if (!F->isDefined(F) || (!F->hasBody() && F->willHaveBody())) |
| return nullptr; |
| |
| // Set up argument indices. |
| unsigned ParamOffset = 0; |
| SmallVector<PrimType, 8> ParamTypes; |
| llvm::DenseMap<unsigned, Function::ParamDescriptor> ParamDescriptors; |
| |
| // If the return is not a primitive, a pointer to the storage where the value |
| // is initialized in is passed as the first argument. |
| QualType Ty = F->getReturnType(); |
| if (!Ty->isVoidType() && !Ctx.classify(Ty)) { |
| ParamTypes.push_back(PT_Ptr); |
| ParamOffset += align(primSize(PT_Ptr)); |
| } |
| |
| // Assign descriptors to all parameters. |
| // Composite objects are lowered to pointers. |
| for (const ParmVarDecl *PD : F->parameters()) { |
| PrimType Ty; |
| if (llvm::Optional<PrimType> T = Ctx.classify(PD->getType())) { |
| Ty = *T; |
| } else { |
| Ty = PT_Ptr; |
| } |
| |
| Descriptor *Desc = P.createDescriptor(PD, Ty); |
| ParamDescriptors.insert({ParamOffset, {Ty, Desc}}); |
| Params.insert({PD, ParamOffset}); |
| ParamOffset += align(primSize(Ty)); |
| ParamTypes.push_back(Ty); |
| } |
| |
| // Create a handle over the emitted code. |
| Function *Func = P.createFunction(F, ParamOffset, std::move(ParamTypes), |
| std::move(ParamDescriptors)); |
| // Compile the function body. |
| if (!F->isConstexpr() || !visitFunc(F)) { |
| // Return a dummy function if compilation failed. |
| if (BailLocation) |
| return llvm::make_error<ByteCodeGenError>(*BailLocation); |
| else |
| return Func; |
| } else { |
| // Create scopes from descriptors. |
| llvm::SmallVector<Scope, 2> Scopes; |
| for (auto &DS : Descriptors) { |
| Scopes.emplace_back(std::move(DS)); |
| } |
| |
| // Set the function's code. |
| Func->setCode(NextLocalOffset, std::move(Code), std::move(SrcMap), |
| std::move(Scopes)); |
| return Func; |
| } |
| } |
| |
| Scope::Local ByteCodeEmitter::createLocal(Descriptor *D) { |
| NextLocalOffset += sizeof(Block); |
| unsigned Location = NextLocalOffset; |
| NextLocalOffset += align(D->getAllocSize()); |
| return {Location, D}; |
| } |
| |
| void ByteCodeEmitter::emitLabel(LabelTy Label) { |
| const size_t Target = Code.size(); |
| LabelOffsets.insert({Label, Target}); |
| auto It = LabelRelocs.find(Label); |
| if (It != LabelRelocs.end()) { |
| for (unsigned Reloc : It->second) { |
| using namespace llvm::support; |
| |
| /// Rewrite the operand of all jumps to this label. |
| void *Location = Code.data() + Reloc - sizeof(int32_t); |
| const int32_t Offset = Target - static_cast<int64_t>(Reloc); |
| endian::write<int32_t, endianness::native, 1>(Location, Offset); |
| } |
| LabelRelocs.erase(It); |
| } |
| } |
| |
| int32_t ByteCodeEmitter::getOffset(LabelTy Label) { |
| // Compute the PC offset which the jump is relative to. |
| const int64_t Position = Code.size() + sizeof(Opcode) + sizeof(int32_t); |
| |
| // If target is known, compute jump offset. |
| auto It = LabelOffsets.find(Label); |
| if (It != LabelOffsets.end()) { |
| return It->second - Position; |
| } |
| |
| // Otherwise, record relocation and return dummy offset. |
| LabelRelocs[Label].push_back(Position); |
| return 0ull; |
| } |
| |
| bool ByteCodeEmitter::bail(const SourceLocation &Loc) { |
| if (!BailLocation) |
| BailLocation = Loc; |
| return false; |
| } |
| |
| template <typename... Tys> |
| bool ByteCodeEmitter::emitOp(Opcode Op, const Tys &... Args, const SourceInfo &SI) { |
| bool Success = true; |
| |
| /// Helper to write bytecode and bail out if 32-bit offsets become invalid. |
| auto emit = [this, &Success](const char *Data, size_t Size) { |
| if (Code.size() + Size > std::numeric_limits<unsigned>::max()) { |
| Success = false; |
| return; |
| } |
| Code.insert(Code.end(), Data, Data + Size); |
| }; |
| |
| /// The opcode is followed by arguments. The source info is |
| /// attached to the address after the opcode. |
| emit(reinterpret_cast<const char *>(&Op), sizeof(Opcode)); |
| if (SI) |
| SrcMap.emplace_back(Code.size(), SI); |
| |
| /// The initializer list forces the expression to be evaluated |
| /// for each argument in the variadic template, in order. |
| (void)std::initializer_list<int>{ |
| (emit(reinterpret_cast<const char *>(&Args), sizeof(Args)), 0)...}; |
| |
| return Success; |
| } |
| |
| bool ByteCodeEmitter::jumpTrue(const LabelTy &Label) { |
| return emitJt(getOffset(Label), SourceInfo{}); |
| } |
| |
| bool ByteCodeEmitter::jumpFalse(const LabelTy &Label) { |
| return emitJf(getOffset(Label), SourceInfo{}); |
| } |
| |
| bool ByteCodeEmitter::jump(const LabelTy &Label) { |
| return emitJmp(getOffset(Label), SourceInfo{}); |
| } |
| |
| bool ByteCodeEmitter::fallthrough(const LabelTy &Label) { |
| emitLabel(Label); |
| return true; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Opcode emitters |
| //===----------------------------------------------------------------------===// |
| |
| #define GET_LINK_IMPL |
| #include "Opcodes.inc" |
| #undef GET_LINK_IMPL |