diff options
Diffstat (limited to 'clang/lib/AST/Interp/ByteCodeEmitter.cpp')
-rw-r--r-- | clang/lib/AST/Interp/ByteCodeEmitter.cpp | 135 |
1 files changed, 81 insertions, 54 deletions
diff --git a/clang/lib/AST/Interp/ByteCodeEmitter.cpp b/clang/lib/AST/Interp/ByteCodeEmitter.cpp index a69b23fd613c..4633d1e0823b 100644 --- a/clang/lib/AST/Interp/ByteCodeEmitter.cpp +++ b/clang/lib/AST/Interp/ByteCodeEmitter.cpp @@ -19,51 +19,72 @@ 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)); - } +Expected<Function *> +ByteCodeEmitter::compileFunc(const FunctionDecl *FuncDecl) { + // Function is not defined at all or not yet. We will + // create a Function instance but not compile the body. That + // will (maybe) happen later. + bool HasBody = FuncDecl->hasBody(FuncDecl); + + // Create a handle over the emitted code. + Function *Func = P.getFunction(FuncDecl); + if (!Func) { + // 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. See 'RVO' + // elsewhere in the code. + QualType Ty = FuncDecl->getReturnType(); + bool HasRVO = false; + if (!Ty->isVoidType() && !Ctx.classify(Ty)) { + HasRVO = true; + ParamTypes.push_back(PT_Ptr); + ParamOffset += align(primSize(PT_Ptr)); + } + + // If the function decl is a member decl, the next parameter is + // the 'this' pointer. This parameter is pop()ed from the + // InterpStack when calling the function. + bool HasThisPointer = false; + if (const auto *MD = dyn_cast<CXXMethodDecl>(FuncDecl); + MD && MD->isInstance()) { + HasThisPointer = true; + 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; + // Assign descriptors to all parameters. + // Composite objects are lowered to pointers. + for (const ParmVarDecl *PD : FuncDecl->parameters()) { + PrimType Ty = Ctx.classify(PD->getType()).value_or(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); } - Descriptor *Desc = P.createDescriptor(PD, Ty); - ParamDescriptors.insert({ParamOffset, {Ty, Desc}}); - Params.insert({PD, ParamOffset}); - ParamOffset += align(primSize(Ty)); - ParamTypes.push_back(Ty); + Func = + P.createFunction(FuncDecl, ParamOffset, std::move(ParamTypes), + std::move(ParamDescriptors), HasThisPointer, HasRVO); } - // Create a handle over the emitted code. - Function *Func = P.createFunction(F, ParamOffset, std::move(ParamTypes), - std::move(ParamDescriptors)); + assert(Func); + if (!HasBody) + return Func; + // Compile the function body. - if (!F->isConstexpr() || !visitFunc(F)) { + if (!FuncDecl->isConstexpr() || !visitFunc(FuncDecl)) { // Return a dummy function if compilation failed. if (BailLocation) return llvm::make_error<ByteCodeGenError>(*BailLocation); - else + else { + Func->setIsFullyCompiled(true); return Func; + } } else { // Create scopes from descriptors. llvm::SmallVector<Scope, 2> Scopes; @@ -74,6 +95,7 @@ Expected<Function *> ByteCodeEmitter::compileFunc(const FunctionDecl *F) { // Set the function's code. Func->setCode(NextLocalOffset, std::move(Code), std::move(SrcMap), std::move(Scopes)); + Func->setIsFullyCompiled(true); return Func; } } @@ -94,7 +116,8 @@ void ByteCodeEmitter::emitLabel(LabelTy Label) { using namespace llvm::support; /// Rewrite the operand of all jumps to this label. - void *Location = Code.data() + Reloc - sizeof(int32_t); + void *Location = Code.data() + Reloc - align(sizeof(int32_t)); + assert(aligned(Location)); const int32_t Offset = Target - static_cast<int64_t>(Reloc); endian::write<int32_t, endianness::native, 1>(Location, Offset); } @@ -104,7 +127,9 @@ void ByteCodeEmitter::emitLabel(LabelTy Label) { 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); + const int64_t Position = + Code.size() + align(sizeof(Opcode)) + align(sizeof(int32_t)); + assert(aligned(Position)); // If target is known, compute jump offset. auto It = LabelOffsets.find(Label); @@ -126,30 +151,32 @@ bool ByteCodeEmitter::bail(const SourceLocation &Loc) { /// Helper to write bytecode and bail out if 32-bit offsets become invalid. /// Pointers will be automatically marshalled as 32-bit IDs. template <typename T> -static std::enable_if_t<!std::is_pointer<T>::value, void> -emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) { - size_t Size = sizeof(Val); - if (Code.size() + Size > std::numeric_limits<unsigned>::max()) { - Success = false; - return; - } +static void emit(Program &P, std::vector<char> &Code, const T &Val, + bool &Success) { + size_t Size; - const char *Data = reinterpret_cast<const char *>(&Val); - Code.insert(Code.end(), Data, Data + Size); -} + if constexpr (std::is_pointer_v<T>) + Size = sizeof(uint32_t); + else + Size = sizeof(T); -template <typename T> -static std::enable_if_t<std::is_pointer<T>::value, void> -emit(Program &P, std::vector<char> &Code, const T &Val, bool &Success) { - size_t Size = sizeof(uint32_t); if (Code.size() + Size > std::numeric_limits<unsigned>::max()) { Success = false; return; } - uint32_t ID = P.getOrCreateNativePointer(Val); - const char *Data = reinterpret_cast<const char *>(&ID); - Code.insert(Code.end(), Data, Data + Size); + // Access must be aligned! + size_t ValPos = align(Code.size()); + Size = align(Size); + assert(aligned(ValPos + Size)); + Code.resize(ValPos + Size); + + if constexpr (!std::is_pointer_v<T>) { + new (Code.data() + ValPos) T(Val); + } else { + uint32_t ID = P.getOrCreateNativePointer(Val); + new (Code.data() + ValPos) uint32_t(ID); + } } template <typename... Tys> |