diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/Interpreter')
13 files changed, 2916 insertions, 0 deletions
diff --git a/contrib/llvm-project/clang/lib/Interpreter/CodeCompletion.cpp b/contrib/llvm-project/clang/lib/Interpreter/CodeCompletion.cpp new file mode 100644 index 000000000000..791426807cb9 --- /dev/null +++ b/contrib/llvm-project/clang/lib/Interpreter/CodeCompletion.cpp @@ -0,0 +1,387 @@ +//===------ CodeCompletion.cpp - Code Completion for ClangRepl -------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements the classes which performs code completion at the REPL. +// +//===----------------------------------------------------------------------===// + +#include "clang/Interpreter/CodeCompletion.h" +#include "clang/AST/ASTImporter.h" +#include "clang/AST/DeclLookups.h" +#include "clang/AST/DeclarationName.h" +#include "clang/AST/ExternalASTSource.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Interpreter/Interpreter.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Sema/CodeCompleteConsumer.h" +#include "clang/Sema/CodeCompleteOptions.h" +#include "clang/Sema/Sema.h" +#include "llvm/Support/Debug.h" +#define DEBUG_TYPE "REPLCC" + +namespace clang { + +const std::string CodeCompletionFileName = "input_line_[Completion]"; + +clang::CodeCompleteOptions getClangCompleteOpts() { + clang::CodeCompleteOptions Opts; + Opts.IncludeCodePatterns = true; + Opts.IncludeMacros = true; + Opts.IncludeGlobals = true; + Opts.IncludeBriefComments = true; + return Opts; +} + +class ReplCompletionConsumer : public CodeCompleteConsumer { +public: + ReplCompletionConsumer(std::vector<std::string> &Results, + ReplCodeCompleter &CC) + : CodeCompleteConsumer(getClangCompleteOpts()), + CCAllocator(std::make_shared<GlobalCodeCompletionAllocator>()), + CCTUInfo(CCAllocator), Results(Results), CC(CC) {} + + // The entry of handling code completion. When the function is called, we + // create a `Context`-based handler (see classes defined below) to handle each + // completion result. + void ProcessCodeCompleteResults(class Sema &S, CodeCompletionContext Context, + CodeCompletionResult *InResults, + unsigned NumResults) final; + + CodeCompletionAllocator &getAllocator() override { return *CCAllocator; } + + CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; } + +private: + std::shared_ptr<GlobalCodeCompletionAllocator> CCAllocator; + CodeCompletionTUInfo CCTUInfo; + std::vector<std::string> &Results; + ReplCodeCompleter &CC; +}; + +/// The class CompletionContextHandler contains four interfaces, each of +/// which handles one type of completion result. +/// Its derived classes are used to create concrete handlers based on +/// \c CodeCompletionContext. +class CompletionContextHandler { +protected: + CodeCompletionContext CCC; + std::vector<std::string> &Results; + +private: + Sema &S; + +public: + CompletionContextHandler(Sema &S, CodeCompletionContext CCC, + std::vector<std::string> &Results) + : CCC(CCC), Results(Results), S(S) {} + + virtual ~CompletionContextHandler() = default; + /// Converts a Declaration completion result to a completion string, and then + /// stores it in Results. + virtual void handleDeclaration(const CodeCompletionResult &Result) { + auto PreferredType = CCC.getPreferredType(); + if (PreferredType.isNull()) { + Results.push_back(Result.Declaration->getName().str()); + return; + } + + if (auto *VD = dyn_cast<VarDecl>(Result.Declaration)) { + auto ArgumentType = VD->getType(); + if (PreferredType->isReferenceType()) { + QualType RT = PreferredType->castAs<ReferenceType>()->getPointeeType(); + Sema::ReferenceConversions RefConv; + Sema::ReferenceCompareResult RefRelationship = + S.CompareReferenceRelationship(SourceLocation(), RT, ArgumentType, + &RefConv); + switch (RefRelationship) { + case Sema::Ref_Compatible: + case Sema::Ref_Related: + Results.push_back(VD->getName().str()); + break; + case Sema::Ref_Incompatible: + break; + } + } else if (S.Context.hasSameType(ArgumentType, PreferredType)) { + Results.push_back(VD->getName().str()); + } + } + } + + /// Converts a Keyword completion result to a completion string, and then + /// stores it in Results. + virtual void handleKeyword(const CodeCompletionResult &Result) { + auto Prefix = S.getPreprocessor().getCodeCompletionFilter(); + // Add keyword to the completion results only if we are in a type-aware + // situation. + if (!CCC.getBaseType().isNull() || !CCC.getPreferredType().isNull()) + return; + if (StringRef(Result.Keyword).starts_with(Prefix)) + Results.push_back(Result.Keyword); + } + + /// Converts a Pattern completion result to a completion string, and then + /// stores it in Results. + virtual void handlePattern(const CodeCompletionResult &Result) {} + + /// Converts a Macro completion result to a completion string, and then stores + /// it in Results. + virtual void handleMacro(const CodeCompletionResult &Result) {} +}; + +class DotMemberAccessHandler : public CompletionContextHandler { +public: + DotMemberAccessHandler(Sema &S, CodeCompletionContext CCC, + std::vector<std::string> &Results) + : CompletionContextHandler(S, CCC, Results) {} + void handleDeclaration(const CodeCompletionResult &Result) override { + auto *ID = Result.Declaration->getIdentifier(); + if (!ID) + return; + if (!isa<CXXMethodDecl>(Result.Declaration)) + return; + const auto *Fun = cast<CXXMethodDecl>(Result.Declaration); + if (Fun->getParent()->getCanonicalDecl() == + CCC.getBaseType()->getAsCXXRecordDecl()->getCanonicalDecl()) { + LLVM_DEBUG(llvm::dbgs() << "[In HandleCodeCompleteDOT] Name : " + << ID->getName() << "\n"); + Results.push_back(ID->getName().str()); + } + } + + void handleKeyword(const CodeCompletionResult &Result) override {} +}; + +void ReplCompletionConsumer::ProcessCodeCompleteResults( + class Sema &S, CodeCompletionContext Context, + CodeCompletionResult *InResults, unsigned NumResults) { + + auto Prefix = S.getPreprocessor().getCodeCompletionFilter(); + CC.Prefix = Prefix; + + std::unique_ptr<CompletionContextHandler> CCH; + + // initialize fine-grained code completion handler based on the code + // completion context. + switch (Context.getKind()) { + case CodeCompletionContext::CCC_DotMemberAccess: + CCH.reset(new DotMemberAccessHandler(S, Context, this->Results)); + break; + default: + CCH.reset(new CompletionContextHandler(S, Context, this->Results)); + }; + + for (unsigned I = 0; I < NumResults; I++) { + auto &Result = InResults[I]; + switch (Result.Kind) { + case CodeCompletionResult::RK_Declaration: + if (Result.Hidden) { + break; + } + if (!Result.Declaration->getDeclName().isIdentifier() || + !Result.Declaration->getName().starts_with(Prefix)) { + break; + } + CCH->handleDeclaration(Result); + break; + case CodeCompletionResult::RK_Keyword: + CCH->handleKeyword(Result); + break; + case CodeCompletionResult::RK_Macro: + CCH->handleMacro(Result); + break; + case CodeCompletionResult::RK_Pattern: + CCH->handlePattern(Result); + break; + } + } + + std::sort(Results.begin(), Results.end()); +} + +class IncrementalSyntaxOnlyAction : public SyntaxOnlyAction { + const CompilerInstance *ParentCI; + +public: + IncrementalSyntaxOnlyAction(const CompilerInstance *ParentCI) + : ParentCI(ParentCI) {} + +protected: + void ExecuteAction() override; +}; + +class ExternalSource : public clang::ExternalASTSource { + TranslationUnitDecl *ChildTUDeclCtxt; + ASTContext &ParentASTCtxt; + TranslationUnitDecl *ParentTUDeclCtxt; + + std::unique_ptr<ASTImporter> Importer; + +public: + ExternalSource(ASTContext &ChildASTCtxt, FileManager &ChildFM, + ASTContext &ParentASTCtxt, FileManager &ParentFM); + bool FindExternalVisibleDeclsByName(const DeclContext *DC, + DeclarationName Name) override; + void + completeVisibleDeclsMap(const clang::DeclContext *childDeclContext) override; +}; + +// This method is intended to set up `ExternalASTSource` to the running +// compiler instance before the super `ExecuteAction` triggers parsing +void IncrementalSyntaxOnlyAction::ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + ExternalSource *myExternalSource = + new ExternalSource(CI.getASTContext(), CI.getFileManager(), + ParentCI->getASTContext(), ParentCI->getFileManager()); + llvm::IntrusiveRefCntPtr<clang::ExternalASTSource> astContextExternalSource( + myExternalSource); + CI.getASTContext().setExternalSource(astContextExternalSource); + CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage( + true); + + // Load all external decls into current context. Under the hood, it calls + // ExternalSource::completeVisibleDeclsMap, which make all decls on the redecl + // chain visible. + // + // This is crucial to code completion on dot members, since a bound variable + // before "." would be otherwise treated out-of-scope. + // + // clang-repl> Foo f1; + // clang-repl> f1.<tab> + CI.getASTContext().getTranslationUnitDecl()->lookups(); + SyntaxOnlyAction::ExecuteAction(); +} + +ExternalSource::ExternalSource(ASTContext &ChildASTCtxt, FileManager &ChildFM, + ASTContext &ParentASTCtxt, FileManager &ParentFM) + : ChildTUDeclCtxt(ChildASTCtxt.getTranslationUnitDecl()), + ParentASTCtxt(ParentASTCtxt), + ParentTUDeclCtxt(ParentASTCtxt.getTranslationUnitDecl()) { + ASTImporter *importer = + new ASTImporter(ChildASTCtxt, ChildFM, ParentASTCtxt, ParentFM, + /*MinimalImport : ON*/ true); + Importer.reset(importer); +} + +bool ExternalSource::FindExternalVisibleDeclsByName(const DeclContext *DC, + DeclarationName Name) { + + IdentifierTable &ParentIdTable = ParentASTCtxt.Idents; + + auto ParentDeclName = + DeclarationName(&(ParentIdTable.get(Name.getAsString()))); + + DeclContext::lookup_result lookup_result = + ParentTUDeclCtxt->lookup(ParentDeclName); + + if (!lookup_result.empty()) { + return true; + } + return false; +} + +void ExternalSource::completeVisibleDeclsMap( + const DeclContext *ChildDeclContext) { + assert(ChildDeclContext && ChildDeclContext == ChildTUDeclCtxt && + "No child decl context!"); + + if (!ChildDeclContext->hasExternalVisibleStorage()) + return; + + for (auto *DeclCtxt = ParentTUDeclCtxt; DeclCtxt != nullptr; + DeclCtxt = DeclCtxt->getPreviousDecl()) { + for (auto &IDeclContext : DeclCtxt->decls()) { + if (!llvm::isa<NamedDecl>(IDeclContext)) + continue; + + NamedDecl *Decl = llvm::cast<NamedDecl>(IDeclContext); + + auto DeclOrErr = Importer->Import(Decl); + if (!DeclOrErr) { + // if an error happens, it usually means the decl has already been + // imported or the decl is a result of a failed import. But in our + // case, every import is fresh each time code completion is + // triggered. So Import usually doesn't fail. If it does, it just means + // the related decl can't be used in code completion and we can safely + // drop it. + llvm::consumeError(DeclOrErr.takeError()); + continue; + } + + if (!llvm::isa<NamedDecl>(*DeclOrErr)) + continue; + + NamedDecl *importedNamedDecl = llvm::cast<NamedDecl>(*DeclOrErr); + + SetExternalVisibleDeclsForName(ChildDeclContext, + importedNamedDecl->getDeclName(), + importedNamedDecl); + + if (!llvm::isa<CXXRecordDecl>(importedNamedDecl)) + continue; + + auto *Record = llvm::cast<CXXRecordDecl>(importedNamedDecl); + + if (auto Err = Importer->ImportDefinition(Decl)) { + // the same as above + consumeError(std::move(Err)); + continue; + } + + Record->setHasLoadedFieldsFromExternalStorage(true); + LLVM_DEBUG(llvm::dbgs() + << "\nCXXRecrod : " << Record->getName() << " size(methods): " + << std::distance(Record->method_begin(), Record->method_end()) + << " has def?: " << Record->hasDefinition() + << " # (methods): " + << std::distance(Record->getDefinition()->method_begin(), + Record->getDefinition()->method_end()) + << "\n"); + for (auto *Meth : Record->methods()) + SetExternalVisibleDeclsForName(ChildDeclContext, Meth->getDeclName(), + Meth); + } + ChildDeclContext->setHasExternalLexicalStorage(false); + } +} + +void ReplCodeCompleter::codeComplete(CompilerInstance *InterpCI, + llvm::StringRef Content, unsigned Line, + unsigned Col, + const CompilerInstance *ParentCI, + std::vector<std::string> &CCResults) { + auto DiagOpts = DiagnosticOptions(); + auto consumer = ReplCompletionConsumer(CCResults, *this); + + auto diag = InterpCI->getDiagnosticsPtr(); + std::unique_ptr<ASTUnit> AU(ASTUnit::LoadFromCompilerInvocationAction( + InterpCI->getInvocationPtr(), std::make_shared<PCHContainerOperations>(), + diag)); + llvm::SmallVector<clang::StoredDiagnostic, 8> sd = {}; + llvm::SmallVector<const llvm::MemoryBuffer *, 1> tb = {}; + InterpCI->getFrontendOpts().Inputs[0] = FrontendInputFile( + CodeCompletionFileName, Language::CXX, InputKind::Source); + auto Act = std::make_unique<IncrementalSyntaxOnlyAction>(ParentCI); + std::unique_ptr<llvm::MemoryBuffer> MB = + llvm::MemoryBuffer::getMemBufferCopy(Content, CodeCompletionFileName); + llvm::SmallVector<ASTUnit::RemappedFile, 4> RemappedFiles; + + RemappedFiles.push_back(std::make_pair(CodeCompletionFileName, MB.get())); + // we don't want the AU destructor to release the memory buffer that MB + // owns twice, because MB handles its resource on its own. + AU->setOwnsRemappedFileBuffers(false); + AU->CodeComplete(CodeCompletionFileName, 1, Col, RemappedFiles, false, false, + false, consumer, + std::make_shared<clang::PCHContainerOperations>(), *diag, + InterpCI->getLangOpts(), InterpCI->getSourceManager(), + InterpCI->getFileManager(), sd, tb, std::move(Act)); +} + +} // namespace clang diff --git a/contrib/llvm-project/clang/lib/Interpreter/DeviceOffload.cpp b/contrib/llvm-project/clang/lib/Interpreter/DeviceOffload.cpp new file mode 100644 index 000000000000..07c9e3005e5f --- /dev/null +++ b/contrib/llvm-project/clang/lib/Interpreter/DeviceOffload.cpp @@ -0,0 +1,177 @@ +//===---------- DeviceOffload.cpp - Device Offloading------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements offloading to CUDA devices. +// +//===----------------------------------------------------------------------===// + +#include "DeviceOffload.h" + +#include "clang/Basic/TargetOptions.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Frontend/CompilerInstance.h" + +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/IR/Module.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Target/TargetMachine.h" + +namespace clang { + +IncrementalCUDADeviceParser::IncrementalCUDADeviceParser( + Interpreter &Interp, std::unique_ptr<CompilerInstance> Instance, + IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx, + llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> FS, + llvm::Error &Err) + : IncrementalParser(Interp, std::move(Instance), LLVMCtx, Err), + HostParser(HostParser), VFS(FS) { + if (Err) + return; + StringRef Arch = CI->getTargetOpts().CPU; + if (!Arch.starts_with("sm_") || Arch.substr(3).getAsInteger(10, SMVersion)) { + Err = llvm::joinErrors(std::move(Err), llvm::make_error<llvm::StringError>( + "Invalid CUDA architecture", + llvm::inconvertibleErrorCode())); + return; + } +} + +llvm::Expected<PartialTranslationUnit &> +IncrementalCUDADeviceParser::Parse(llvm::StringRef Input) { + auto PTU = IncrementalParser::Parse(Input); + if (!PTU) + return PTU.takeError(); + + auto PTX = GeneratePTX(); + if (!PTX) + return PTX.takeError(); + + auto Err = GenerateFatbinary(); + if (Err) + return std::move(Err); + + std::string FatbinFileName = + "/incr_module_" + std::to_string(PTUs.size()) + ".fatbin"; + VFS->addFile(FatbinFileName, 0, + llvm::MemoryBuffer::getMemBuffer( + llvm::StringRef(FatbinContent.data(), FatbinContent.size()), + "", false)); + + HostParser.getCI()->getCodeGenOpts().CudaGpuBinaryFileName = FatbinFileName; + + FatbinContent.clear(); + + return PTU; +} + +llvm::Expected<llvm::StringRef> IncrementalCUDADeviceParser::GeneratePTX() { + auto &PTU = PTUs.back(); + std::string Error; + + const llvm::Target *Target = llvm::TargetRegistry::lookupTarget( + PTU.TheModule->getTargetTriple(), Error); + if (!Target) + return llvm::make_error<llvm::StringError>(std::move(Error), + std::error_code()); + llvm::TargetOptions TO = llvm::TargetOptions(); + llvm::TargetMachine *TargetMachine = Target->createTargetMachine( + PTU.TheModule->getTargetTriple(), getCI()->getTargetOpts().CPU, "", TO, + llvm::Reloc::Model::PIC_); + PTU.TheModule->setDataLayout(TargetMachine->createDataLayout()); + + PTXCode.clear(); + llvm::raw_svector_ostream dest(PTXCode); + + llvm::legacy::PassManager PM; + if (TargetMachine->addPassesToEmitFile(PM, dest, nullptr, + llvm::CodeGenFileType::AssemblyFile)) { + return llvm::make_error<llvm::StringError>( + "NVPTX backend cannot produce PTX code.", + llvm::inconvertibleErrorCode()); + } + + if (!PM.run(*PTU.TheModule)) + return llvm::make_error<llvm::StringError>("Failed to emit PTX code.", + llvm::inconvertibleErrorCode()); + + PTXCode += '\0'; + while (PTXCode.size() % 8) + PTXCode += '\0'; + return PTXCode.str(); +} + +llvm::Error IncrementalCUDADeviceParser::GenerateFatbinary() { + enum FatBinFlags { + AddressSize64 = 0x01, + HasDebugInfo = 0x02, + ProducerCuda = 0x04, + HostLinux = 0x10, + HostMac = 0x20, + HostWindows = 0x40 + }; + + struct FatBinInnerHeader { + uint16_t Kind; // 0x00 + uint16_t unknown02; // 0x02 + uint32_t HeaderSize; // 0x04 + uint32_t DataSize; // 0x08 + uint32_t unknown0c; // 0x0c + uint32_t CompressedSize; // 0x10 + uint32_t SubHeaderSize; // 0x14 + uint16_t VersionMinor; // 0x18 + uint16_t VersionMajor; // 0x1a + uint32_t CudaArch; // 0x1c + uint32_t unknown20; // 0x20 + uint32_t unknown24; // 0x24 + uint32_t Flags; // 0x28 + uint32_t unknown2c; // 0x2c + uint32_t unknown30; // 0x30 + uint32_t unknown34; // 0x34 + uint32_t UncompressedSize; // 0x38 + uint32_t unknown3c; // 0x3c + uint32_t unknown40; // 0x40 + uint32_t unknown44; // 0x44 + FatBinInnerHeader(uint32_t DataSize, uint32_t CudaArch, uint32_t Flags) + : Kind(1 /*PTX*/), unknown02(0x0101), HeaderSize(sizeof(*this)), + DataSize(DataSize), unknown0c(0), CompressedSize(0), + SubHeaderSize(HeaderSize - 8), VersionMinor(2), VersionMajor(4), + CudaArch(CudaArch), unknown20(0), unknown24(0), Flags(Flags), + unknown2c(0), unknown30(0), unknown34(0), UncompressedSize(0), + unknown3c(0), unknown40(0), unknown44(0) {} + }; + + struct FatBinHeader { + uint32_t Magic; // 0x00 + uint16_t Version; // 0x04 + uint16_t HeaderSize; // 0x06 + uint32_t DataSize; // 0x08 + uint32_t unknown0c; // 0x0c + public: + FatBinHeader(uint32_t DataSize) + : Magic(0xba55ed50), Version(1), HeaderSize(sizeof(*this)), + DataSize(DataSize), unknown0c(0) {} + }; + + FatBinHeader OuterHeader(sizeof(FatBinInnerHeader) + PTXCode.size()); + FatbinContent.append((char *)&OuterHeader, + ((char *)&OuterHeader) + OuterHeader.HeaderSize); + + FatBinInnerHeader InnerHeader(PTXCode.size(), SMVersion, + FatBinFlags::AddressSize64 | + FatBinFlags::HostLinux); + FatbinContent.append((char *)&InnerHeader, + ((char *)&InnerHeader) + InnerHeader.HeaderSize); + + FatbinContent.append(PTXCode.begin(), PTXCode.end()); + + return llvm::Error::success(); +} + +IncrementalCUDADeviceParser::~IncrementalCUDADeviceParser() {} + +} // namespace clang diff --git a/contrib/llvm-project/clang/lib/Interpreter/DeviceOffload.h b/contrib/llvm-project/clang/lib/Interpreter/DeviceOffload.h new file mode 100644 index 000000000000..ce4f218c94c7 --- /dev/null +++ b/contrib/llvm-project/clang/lib/Interpreter/DeviceOffload.h @@ -0,0 +1,51 @@ +//===----------- DeviceOffload.h - Device Offloading ------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements classes required for offloading to CUDA devices. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INTERPRETER_DEVICE_OFFLOAD_H +#define LLVM_CLANG_LIB_INTERPRETER_DEVICE_OFFLOAD_H + +#include "IncrementalParser.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/VirtualFileSystem.h" + +namespace clang { + +class IncrementalCUDADeviceParser : public IncrementalParser { +public: + IncrementalCUDADeviceParser( + Interpreter &Interp, std::unique_ptr<CompilerInstance> Instance, + IncrementalParser &HostParser, llvm::LLVMContext &LLVMCtx, + llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS, + llvm::Error &Err); + + llvm::Expected<PartialTranslationUnit &> + Parse(llvm::StringRef Input) override; + + // Generate PTX for the last PTU + llvm::Expected<llvm::StringRef> GeneratePTX(); + + // Generate fatbinary contents in memory + llvm::Error GenerateFatbinary(); + + ~IncrementalCUDADeviceParser(); + +protected: + IncrementalParser &HostParser; + int SMVersion; + llvm::SmallString<1024> PTXCode; + llvm::SmallVector<char, 1024> FatbinContent; + llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> VFS; +}; + +} // namespace clang + +#endif // LLVM_CLANG_LIB_INTERPRETER_DEVICE_OFFLOAD_H diff --git a/contrib/llvm-project/clang/lib/Interpreter/IncrementalExecutor.cpp b/contrib/llvm-project/clang/lib/Interpreter/IncrementalExecutor.cpp new file mode 100644 index 000000000000..1824a5b4570a --- /dev/null +++ b/contrib/llvm-project/clang/lib/Interpreter/IncrementalExecutor.cpp @@ -0,0 +1,121 @@ +//===--- IncrementalExecutor.cpp - Incremental Execution --------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements the class which performs incremental code execution. +// +//===----------------------------------------------------------------------===// + +#include "IncrementalExecutor.h" + +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TargetOptions.h" +#include "clang/Interpreter/PartialTranslationUnit.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/ExecutionEngine/Orc/CompileUtils.h" +#include "llvm/ExecutionEngine/Orc/Debugging/DebuggerSupport.h" +#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" +#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h" +#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" +#include "llvm/ExecutionEngine/Orc/LLJIT.h" +#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h" +#include "llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h" +#include "llvm/ExecutionEngine/SectionMemoryManager.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/TargetSelect.h" + +// Force linking some of the runtimes that helps attaching to a debugger. +LLVM_ATTRIBUTE_USED void linkComponents() { + llvm::errs() << (void *)&llvm_orc_registerJITLoaderGDBWrapper + << (void *)&llvm_orc_registerJITLoaderGDBAllocAction; +} + +namespace clang { +IncrementalExecutor::IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC) + : TSCtx(TSC) {} + +llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>> +IncrementalExecutor::createDefaultJITBuilder( + llvm::orc::JITTargetMachineBuilder JTMB) { + auto JITBuilder = std::make_unique<llvm::orc::LLJITBuilder>(); + JITBuilder->setJITTargetMachineBuilder(std::move(JTMB)); + JITBuilder->setPrePlatformSetup([](llvm::orc::LLJIT &J) { + // Try to enable debugging of JIT'd code (only works with JITLink for + // ELF and MachO). + consumeError(llvm::orc::enableDebuggerSupport(J)); + return llvm::Error::success(); + }); + return std::move(JITBuilder); +} + +IncrementalExecutor::IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC, + llvm::orc::LLJITBuilder &JITBuilder, + llvm::Error &Err) + : TSCtx(TSC) { + using namespace llvm::orc; + llvm::ErrorAsOutParameter EAO(&Err); + + if (auto JitOrErr = JITBuilder.create()) + Jit = std::move(*JitOrErr); + else { + Err = JitOrErr.takeError(); + return; + } +} + +IncrementalExecutor::~IncrementalExecutor() {} + +llvm::Error IncrementalExecutor::addModule(PartialTranslationUnit &PTU) { + llvm::orc::ResourceTrackerSP RT = + Jit->getMainJITDylib().createResourceTracker(); + ResourceTrackers[&PTU] = RT; + + return Jit->addIRModule(RT, {std::move(PTU.TheModule), TSCtx}); +} + +llvm::Error IncrementalExecutor::removeModule(PartialTranslationUnit &PTU) { + + llvm::orc::ResourceTrackerSP RT = std::move(ResourceTrackers[&PTU]); + if (!RT) + return llvm::Error::success(); + + ResourceTrackers.erase(&PTU); + if (llvm::Error Err = RT->remove()) + return Err; + return llvm::Error::success(); +} + +// Clean up the JIT instance. +llvm::Error IncrementalExecutor::cleanUp() { + // This calls the global dtors of registered modules. + return Jit->deinitialize(Jit->getMainJITDylib()); +} + +llvm::Error IncrementalExecutor::runCtors() const { + return Jit->initialize(Jit->getMainJITDylib()); +} + +llvm::Expected<llvm::orc::ExecutorAddr> +IncrementalExecutor::getSymbolAddress(llvm::StringRef Name, + SymbolNameKind NameKind) const { + using namespace llvm::orc; + auto SO = makeJITDylibSearchOrder({&Jit->getMainJITDylib(), + Jit->getPlatformJITDylib().get(), + Jit->getProcessSymbolsJITDylib().get()}); + + ExecutionSession &ES = Jit->getExecutionSession(); + + auto SymOrErr = + ES.lookup(SO, (NameKind == LinkerName) ? ES.intern(Name) + : Jit->mangleAndIntern(Name)); + if (auto Err = SymOrErr.takeError()) + return std::move(Err); + return SymOrErr->getAddress(); +} + +} // end namespace clang diff --git a/contrib/llvm-project/clang/lib/Interpreter/IncrementalExecutor.h b/contrib/llvm-project/clang/lib/Interpreter/IncrementalExecutor.h new file mode 100644 index 000000000000..dbd61f0b8b1e --- /dev/null +++ b/contrib/llvm-project/clang/lib/Interpreter/IncrementalExecutor.h @@ -0,0 +1,71 @@ +//===--- IncrementalExecutor.h - Incremental Execution ----------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements the class which performs incremental code execution. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INTERPRETER_INCREMENTALEXECUTOR_H +#define LLVM_CLANG_LIB_INTERPRETER_INCREMENTALEXECUTOR_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" +#include "llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h" + +#include <memory> + +namespace llvm { +class Error; +namespace orc { +class JITTargetMachineBuilder; +class LLJIT; +class LLJITBuilder; +class ThreadSafeContext; +} // namespace orc +} // namespace llvm + +namespace clang { + +struct PartialTranslationUnit; +class TargetInfo; + +class IncrementalExecutor { + using CtorDtorIterator = llvm::orc::CtorDtorIterator; + std::unique_ptr<llvm::orc::LLJIT> Jit; + llvm::orc::ThreadSafeContext &TSCtx; + + llvm::DenseMap<const PartialTranslationUnit *, llvm::orc::ResourceTrackerSP> + ResourceTrackers; + +protected: + IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC); + +public: + enum SymbolNameKind { IRName, LinkerName }; + + IncrementalExecutor(llvm::orc::ThreadSafeContext &TSC, + llvm::orc::LLJITBuilder &JITBuilder, llvm::Error &Err); + virtual ~IncrementalExecutor(); + + virtual llvm::Error addModule(PartialTranslationUnit &PTU); + virtual llvm::Error removeModule(PartialTranslationUnit &PTU); + virtual llvm::Error runCtors() const; + virtual llvm::Error cleanUp(); + llvm::Expected<llvm::orc::ExecutorAddr> + getSymbolAddress(llvm::StringRef Name, SymbolNameKind NameKind) const; + + llvm::orc::LLJIT &GetExecutionEngine() { return *Jit; } + + static llvm::Expected<std::unique_ptr<llvm::orc::LLJITBuilder>> + createDefaultJITBuilder(llvm::orc::JITTargetMachineBuilder JTMB); +}; + +} // end namespace clang + +#endif // LLVM_CLANG_LIB_INTERPRETER_INCREMENTALEXECUTOR_H diff --git a/contrib/llvm-project/clang/lib/Interpreter/IncrementalParser.cpp b/contrib/llvm-project/clang/lib/Interpreter/IncrementalParser.cpp new file mode 100644 index 000000000000..b7c809c45098 --- /dev/null +++ b/contrib/llvm-project/clang/lib/Interpreter/IncrementalParser.cpp @@ -0,0 +1,427 @@ +//===--------- IncrementalParser.cpp - Incremental Compilation -----------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements the class which performs incremental code compilation. +// +//===----------------------------------------------------------------------===// + +#include "IncrementalParser.h" + +#include "clang/AST/DeclContextInternals.h" +#include "clang/CodeGen/BackendUtil.h" +#include "clang/CodeGen/CodeGenAction.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/FrontendTool/Utils.h" +#include "clang/Interpreter/Interpreter.h" +#include "clang/Parse/Parser.h" +#include "clang/Sema/Sema.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Timer.h" + +#include <sstream> + +namespace clang { + +class IncrementalASTConsumer final : public ASTConsumer { + Interpreter &Interp; + std::unique_ptr<ASTConsumer> Consumer; + +public: + IncrementalASTConsumer(Interpreter &InterpRef, std::unique_ptr<ASTConsumer> C) + : Interp(InterpRef), Consumer(std::move(C)) {} + + bool HandleTopLevelDecl(DeclGroupRef DGR) override final { + if (DGR.isNull()) + return true; + if (!Consumer) + return true; + + for (Decl *D : DGR) + if (auto *TSD = llvm::dyn_cast<TopLevelStmtDecl>(D); + TSD && TSD->isSemiMissing()) + TSD->setStmt(Interp.SynthesizeExpr(cast<Expr>(TSD->getStmt()))); + + return Consumer->HandleTopLevelDecl(DGR); + } + void HandleTranslationUnit(ASTContext &Ctx) override final { + Consumer->HandleTranslationUnit(Ctx); + } + void HandleInlineFunctionDefinition(FunctionDecl *D) override final { + Consumer->HandleInlineFunctionDefinition(D); + } + void HandleInterestingDecl(DeclGroupRef D) override final { + Consumer->HandleInterestingDecl(D); + } + void HandleTagDeclDefinition(TagDecl *D) override final { + Consumer->HandleTagDeclDefinition(D); + } + void HandleTagDeclRequiredDefinition(const TagDecl *D) override final { + Consumer->HandleTagDeclRequiredDefinition(D); + } + void HandleCXXImplicitFunctionInstantiation(FunctionDecl *D) override final { + Consumer->HandleCXXImplicitFunctionInstantiation(D); + } + void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override final { + Consumer->HandleTopLevelDeclInObjCContainer(D); + } + void HandleImplicitImportDecl(ImportDecl *D) override final { + Consumer->HandleImplicitImportDecl(D); + } + void CompleteTentativeDefinition(VarDecl *D) override final { + Consumer->CompleteTentativeDefinition(D); + } + void CompleteExternalDeclaration(DeclaratorDecl *D) override final { + Consumer->CompleteExternalDeclaration(D); + } + void AssignInheritanceModel(CXXRecordDecl *RD) override final { + Consumer->AssignInheritanceModel(RD); + } + void HandleCXXStaticMemberVarInstantiation(VarDecl *D) override final { + Consumer->HandleCXXStaticMemberVarInstantiation(D); + } + void HandleVTable(CXXRecordDecl *RD) override final { + Consumer->HandleVTable(RD); + } + ASTMutationListener *GetASTMutationListener() override final { + return Consumer->GetASTMutationListener(); + } + ASTDeserializationListener *GetASTDeserializationListener() override final { + return Consumer->GetASTDeserializationListener(); + } + void PrintStats() override final { Consumer->PrintStats(); } + bool shouldSkipFunctionBody(Decl *D) override final { + return Consumer->shouldSkipFunctionBody(D); + } + static bool classof(const clang::ASTConsumer *) { return true; } +}; + +/// A custom action enabling the incremental processing functionality. +/// +/// The usual \p FrontendAction expects one call to ExecuteAction and once it +/// sees a call to \p EndSourceFile it deletes some of the important objects +/// such as \p Preprocessor and \p Sema assuming no further input will come. +/// +/// \p IncrementalAction ensures it keep its underlying action's objects alive +/// as long as the \p IncrementalParser needs them. +/// +class IncrementalAction : public WrapperFrontendAction { +private: + bool IsTerminating = false; + +public: + IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx, + llvm::Error &Err) + : WrapperFrontendAction([&]() { + llvm::ErrorAsOutParameter EAO(&Err); + std::unique_ptr<FrontendAction> Act; + switch (CI.getFrontendOpts().ProgramAction) { + default: + Err = llvm::createStringError( + std::errc::state_not_recoverable, + "Driver initialization failed. " + "Incremental mode for action %d is not supported", + CI.getFrontendOpts().ProgramAction); + return Act; + case frontend::ASTDump: + [[fallthrough]]; + case frontend::ASTPrint: + [[fallthrough]]; + case frontend::ParseSyntaxOnly: + Act = CreateFrontendAction(CI); + break; + case frontend::PluginAction: + [[fallthrough]]; + case frontend::EmitAssembly: + [[fallthrough]]; + case frontend::EmitBC: + [[fallthrough]]; + case frontend::EmitObj: + [[fallthrough]]; + case frontend::PrintPreprocessedInput: + [[fallthrough]]; + case frontend::EmitLLVMOnly: + Act.reset(new EmitLLVMOnlyAction(&LLVMCtx)); + break; + } + return Act; + }()) {} + FrontendAction *getWrapped() const { return WrappedAction.get(); } + TranslationUnitKind getTranslationUnitKind() override { + return TU_Incremental; + } + + void ExecuteAction() override { + CompilerInstance &CI = getCompilerInstance(); + assert(CI.hasPreprocessor() && "No PP!"); + + // Use a code completion consumer? + CodeCompleteConsumer *CompletionConsumer = nullptr; + if (CI.hasCodeCompletionConsumer()) + CompletionConsumer = &CI.getCodeCompletionConsumer(); + + Preprocessor &PP = CI.getPreprocessor(); + PP.EnterMainSourceFile(); + + if (!CI.hasSema()) + CI.createSema(getTranslationUnitKind(), CompletionConsumer); + } + + // Do not terminate after processing the input. This allows us to keep various + // clang objects alive and to incrementally grow the current TU. + void EndSourceFile() override { + // The WrappedAction can be nullptr if we issued an error in the ctor. + if (IsTerminating && getWrapped()) + WrapperFrontendAction::EndSourceFile(); + } + + void FinalizeAction() { + assert(!IsTerminating && "Already finalized!"); + IsTerminating = true; + EndSourceFile(); + } +}; + +CodeGenerator *IncrementalParser::getCodeGen() const { + FrontendAction *WrappedAct = Act->getWrapped(); + if (!WrappedAct->hasIRSupport()) + return nullptr; + return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator(); +} + +IncrementalParser::IncrementalParser() {} + +IncrementalParser::IncrementalParser(Interpreter &Interp, + std::unique_ptr<CompilerInstance> Instance, + llvm::LLVMContext &LLVMCtx, + llvm::Error &Err) + : CI(std::move(Instance)) { + llvm::ErrorAsOutParameter EAO(&Err); + Act = std::make_unique<IncrementalAction>(*CI, LLVMCtx, Err); + if (Err) + return; + CI->ExecuteAction(*Act); + + if (getCodeGen()) + CachedInCodeGenModule = GenModule(); + + std::unique_ptr<ASTConsumer> IncrConsumer = + std::make_unique<IncrementalASTConsumer>(Interp, CI->takeASTConsumer()); + CI->setASTConsumer(std::move(IncrConsumer)); + Consumer = &CI->getASTConsumer(); + P.reset( + new Parser(CI->getPreprocessor(), CI->getSema(), /*SkipBodies=*/false)); + P->Initialize(); + + // An initial PTU is needed as CUDA includes some headers automatically + auto PTU = ParseOrWrapTopLevelDecl(); + if (auto E = PTU.takeError()) { + consumeError(std::move(E)); // FIXME + return; // PTU.takeError(); + } + + if (getCodeGen()) { + PTU->TheModule = GenModule(); + assert(PTU->TheModule && "Failed to create initial PTU"); + } +} + +IncrementalParser::~IncrementalParser() { + P.reset(); + Act->FinalizeAction(); +} + +llvm::Expected<PartialTranslationUnit &> +IncrementalParser::ParseOrWrapTopLevelDecl() { + // Recover resources if we crash before exiting this method. + Sema &S = CI->getSema(); + llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S); + Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true); + Sema::LocalEagerInstantiationScope LocalInstantiations(S); + + PTUs.emplace_back(PartialTranslationUnit()); + PartialTranslationUnit &LastPTU = PTUs.back(); + // Add a new PTU. + ASTContext &C = S.getASTContext(); + C.addTranslationUnitDecl(); + LastPTU.TUPart = C.getTranslationUnitDecl(); + + // Skip previous eof due to last incremental input. + if (P->getCurToken().is(tok::annot_repl_input_end)) { + P->ConsumeAnyToken(); + // FIXME: Clang does not call ExitScope on finalizing the regular TU, we + // might want to do that around HandleEndOfTranslationUnit. + P->ExitScope(); + S.CurContext = nullptr; + // Start a new PTU. + P->EnterScope(Scope::DeclScope); + S.ActOnTranslationUnitScope(P->getCurScope()); + } + + Parser::DeclGroupPtrTy ADecl; + Sema::ModuleImportState ImportState; + for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF; + AtEOF = P->ParseTopLevelDecl(ADecl, ImportState)) { + if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get())) + return llvm::make_error<llvm::StringError>("Parsing failed. " + "The consumer rejected a decl", + std::error_code()); + } + + DiagnosticsEngine &Diags = getCI()->getDiagnostics(); + if (Diags.hasErrorOccurred()) { + PartialTranslationUnit MostRecentPTU = {C.getTranslationUnitDecl(), + nullptr}; + CleanUpPTU(MostRecentPTU); + + Diags.Reset(/*soft=*/true); + Diags.getClient()->clear(); + return llvm::make_error<llvm::StringError>("Parsing failed.", + std::error_code()); + } + + // Process any TopLevelDecls generated by #pragma weak. + for (Decl *D : S.WeakTopLevelDecls()) { + DeclGroupRef DGR(D); + Consumer->HandleTopLevelDecl(DGR); + } + + LocalInstantiations.perform(); + GlobalInstantiations.perform(); + + Consumer->HandleTranslationUnit(C); + + return LastPTU; +} + +llvm::Expected<PartialTranslationUnit &> +IncrementalParser::Parse(llvm::StringRef input) { + Preprocessor &PP = CI->getPreprocessor(); + assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?"); + + std::ostringstream SourceName; + SourceName << "input_line_" << InputCount++; + + // Create an uninitialized memory buffer, copy code in and append "\n" + size_t InputSize = input.size(); // don't include trailing 0 + // MemBuffer size should *not* include terminating zero + std::unique_ptr<llvm::MemoryBuffer> MB( + llvm::WritableMemoryBuffer::getNewUninitMemBuffer(InputSize + 1, + SourceName.str())); + char *MBStart = const_cast<char *>(MB->getBufferStart()); + memcpy(MBStart, input.data(), InputSize); + MBStart[InputSize] = '\n'; + + SourceManager &SM = CI->getSourceManager(); + + // FIXME: Create SourceLocation, which will allow clang to order the overload + // candidates for example + SourceLocation NewLoc = SM.getLocForStartOfFile(SM.getMainFileID()); + + // Create FileID for the current buffer. + FileID FID = SM.createFileID(std::move(MB), SrcMgr::C_User, /*LoadedID=*/0, + /*LoadedOffset=*/0, NewLoc); + + // NewLoc only used for diags. + if (PP.EnterSourceFile(FID, /*DirLookup=*/nullptr, NewLoc)) + return llvm::make_error<llvm::StringError>("Parsing failed. " + "Cannot enter source file.", + std::error_code()); + + auto PTU = ParseOrWrapTopLevelDecl(); + if (!PTU) + return PTU.takeError(); + + if (PP.getLangOpts().DelayedTemplateParsing) { + // Microsoft-specific: + // Late parsed templates can leave unswallowed "macro"-like tokens. + // They will seriously confuse the Parser when entering the next + // source file. So lex until we are EOF. + Token Tok; + do { + PP.Lex(Tok); + } while (Tok.isNot(tok::annot_repl_input_end)); + } else { + Token AssertTok; + PP.Lex(AssertTok); + assert(AssertTok.is(tok::annot_repl_input_end) && + "Lexer must be EOF when starting incremental parse!"); + } + + if (std::unique_ptr<llvm::Module> M = GenModule()) + PTU->TheModule = std::move(M); + + return PTU; +} + +std::unique_ptr<llvm::Module> IncrementalParser::GenModule() { + static unsigned ID = 0; + if (CodeGenerator *CG = getCodeGen()) { + // Clang's CodeGen is designed to work with a single llvm::Module. In many + // cases for convenience various CodeGen parts have a reference to the + // llvm::Module (TheModule or Module) which does not change when a new + // module is pushed. However, the execution engine wants to take ownership + // of the module which does not map well to CodeGen's design. To work this + // around we created an empty module to make CodeGen happy. We should make + // sure it always stays empty. + assert((!CachedInCodeGenModule || + (CachedInCodeGenModule->empty() && + CachedInCodeGenModule->global_empty() && + CachedInCodeGenModule->alias_empty() && + CachedInCodeGenModule->ifunc_empty())) && + "CodeGen wrote to a readonly module"); + std::unique_ptr<llvm::Module> M(CG->ReleaseModule()); + CG->StartModule("incr_module_" + std::to_string(ID++), M->getContext()); + return M; + } + return nullptr; +} + +void IncrementalParser::CleanUpPTU(PartialTranslationUnit &PTU) { + TranslationUnitDecl *MostRecentTU = PTU.TUPart; + if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) { + for (auto &&[Key, List] : *Map) { + DeclContextLookupResult R = List.getLookupResult(); + std::vector<NamedDecl *> NamedDeclsToRemove; + bool RemoveAll = true; + for (NamedDecl *D : R) { + if (D->getTranslationUnitDecl() == MostRecentTU) + NamedDeclsToRemove.push_back(D); + else + RemoveAll = false; + } + if (LLVM_LIKELY(RemoveAll)) { + Map->erase(Key); + } else { + for (NamedDecl *D : NamedDeclsToRemove) + List.remove(D); + } + } + } + + // FIXME: We should de-allocate MostRecentTU + for (Decl *D : MostRecentTU->decls()) { + auto *ND = dyn_cast<NamedDecl>(D); + if (!ND) + continue; + // Check if we need to clean up the IdResolver chain. + if (ND->getDeclName().getFETokenInfo() && !D->getLangOpts().ObjC && + !D->getLangOpts().CPlusPlus) + getCI()->getSema().IdResolver.RemoveDecl(ND); + } +} + +llvm::StringRef IncrementalParser::GetMangledName(GlobalDecl GD) const { + CodeGenerator *CG = getCodeGen(); + assert(CG); + return CG->GetMangledName(GD); +} +} // end namespace clang diff --git a/contrib/llvm-project/clang/lib/Interpreter/IncrementalParser.h b/contrib/llvm-project/clang/lib/Interpreter/IncrementalParser.h new file mode 100644 index 000000000000..f63bce50acd3 --- /dev/null +++ b/contrib/llvm-project/clang/lib/Interpreter/IncrementalParser.h @@ -0,0 +1,96 @@ +//===--- IncrementalParser.h - Incremental Compilation ----------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements the class which performs incremental code compilation. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INTERPRETER_INCREMENTALPARSER_H +#define LLVM_CLANG_LIB_INTERPRETER_INCREMENTALPARSER_H + +#include "clang/AST/GlobalDecl.h" +#include "clang/Interpreter/PartialTranslationUnit.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Error.h" + +#include <list> +#include <memory> +namespace llvm { +class LLVMContext; +class Module; +} // namespace llvm + +namespace clang { +class ASTConsumer; +class CodeGenerator; +class CompilerInstance; +class IncrementalAction; +class Interpreter; +class Parser; +/// Provides support for incremental compilation. Keeps track of the state +/// changes between the subsequent incremental input. +/// +class IncrementalParser { +protected: + /// Long-lived, incremental parsing action. + std::unique_ptr<IncrementalAction> Act; + + /// Compiler instance performing the incremental compilation. + std::unique_ptr<CompilerInstance> CI; + + /// Parser. + std::unique_ptr<Parser> P; + + /// Consumer to process the produced top level decls. Owned by Act. + ASTConsumer *Consumer = nullptr; + + /// Counts the number of direct user input lines that have been parsed. + unsigned InputCount = 0; + + /// List containing every information about every incrementally parsed piece + /// of code. + std::list<PartialTranslationUnit> PTUs; + + /// When CodeGen is created the first llvm::Module gets cached in many places + /// and we must keep it alive. + std::unique_ptr<llvm::Module> CachedInCodeGenModule; + + IncrementalParser(); + +public: + IncrementalParser(Interpreter &Interp, + std::unique_ptr<CompilerInstance> Instance, + llvm::LLVMContext &LLVMCtx, llvm::Error &Err); + virtual ~IncrementalParser(); + + CompilerInstance *getCI() { return CI.get(); } + CodeGenerator *getCodeGen() const; + + /// Parses incremental input by creating an in-memory file. + ///\returns a \c PartialTranslationUnit which holds information about the + /// \c TranslationUnitDecl and \c llvm::Module corresponding to the input. + virtual llvm::Expected<PartialTranslationUnit &> Parse(llvm::StringRef Input); + + /// Uses the CodeGenModule mangled name cache and avoids recomputing. + ///\returns the mangled name of a \c GD. + llvm::StringRef GetMangledName(GlobalDecl GD) const; + + void CleanUpPTU(PartialTranslationUnit &PTU); + + std::list<PartialTranslationUnit> &getPTUs() { return PTUs; } + + std::unique_ptr<llvm::Module> GenModule(); + +private: + llvm::Expected<PartialTranslationUnit &> ParseOrWrapTopLevelDecl(); +}; +} // end namespace clang + +#endif // LLVM_CLANG_LIB_INTERPRETER_INCREMENTALPARSER_H diff --git a/contrib/llvm-project/clang/lib/Interpreter/Interpreter.cpp b/contrib/llvm-project/clang/lib/Interpreter/Interpreter.cpp new file mode 100644 index 000000000000..985d0b7c0ef3 --- /dev/null +++ b/contrib/llvm-project/clang/lib/Interpreter/Interpreter.cpp @@ -0,0 +1,965 @@ +//===------ Interpreter.cpp - Incremental Compilation and Execution -------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements the component which performs incremental code +// compilation and execution. +// +//===----------------------------------------------------------------------===// + +#include "DeviceOffload.h" +#include "IncrementalExecutor.h" +#include "IncrementalParser.h" +#include "InterpreterUtils.h" +#ifdef __EMSCRIPTEN__ +#include "Wasm.h" +#endif // __EMSCRIPTEN__ + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Mangle.h" +#include "clang/AST/TypeVisitor.h" +#include "clang/Basic/DiagnosticSema.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/CodeGen/CodeGenAction.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/CodeGen/ObjectFilePCHContainerOperations.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/Job.h" +#include "clang/Driver/Options.h" +#include "clang/Driver/Tool.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Interpreter/Interpreter.h" +#include "clang/Interpreter/Value.h" +#include "clang/Lex/PreprocessorOptions.h" +#include "clang/Sema/Lookup.h" +#include "llvm/ExecutionEngine/JITSymbol.h" +#include "llvm/ExecutionEngine/Orc/LLJIT.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/TargetParser/Host.h" + +#include <cstdarg> + +using namespace clang; + +// FIXME: Figure out how to unify with namespace init_convenience from +// tools/clang-import-test/clang-import-test.cpp +namespace { +/// Retrieves the clang CC1 specific flags out of the compilation's jobs. +/// \returns NULL on error. +static llvm::Expected<const llvm::opt::ArgStringList *> +GetCC1Arguments(DiagnosticsEngine *Diagnostics, + driver::Compilation *Compilation) { + // We expect to get back exactly one Command job, if we didn't something + // failed. Extract that job from the Compilation. + const driver::JobList &Jobs = Compilation->getJobs(); + if (!Jobs.size() || !isa<driver::Command>(*Jobs.begin())) + return llvm::createStringError(llvm::errc::not_supported, + "Driver initialization failed. " + "Unable to create a driver job"); + + // The one job we find should be to invoke clang again. + const driver::Command *Cmd = cast<driver::Command>(&(*Jobs.begin())); + if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") + return llvm::createStringError(llvm::errc::not_supported, + "Driver initialization failed"); + + return &Cmd->getArguments(); +} + +static llvm::Expected<std::unique_ptr<CompilerInstance>> +CreateCI(const llvm::opt::ArgStringList &Argv) { + std::unique_ptr<CompilerInstance> Clang(new CompilerInstance()); + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + + // Register the support for object-file-wrapped Clang modules. + // FIXME: Clang should register these container operations automatically. + auto PCHOps = Clang->getPCHContainerOperations(); + PCHOps->registerWriter(std::make_unique<ObjectFilePCHContainerWriter>()); + PCHOps->registerReader(std::make_unique<ObjectFilePCHContainerReader>()); + + // Buffer diagnostics from argument parsing so that we can output them using + // a well formed diagnostic object. + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = new DiagnosticOptions(); + TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer; + DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer); + bool Success = CompilerInvocation::CreateFromArgs( + Clang->getInvocation(), llvm::ArrayRef(Argv.begin(), Argv.size()), Diags); + + // Infer the builtin include path if unspecified. + if (Clang->getHeaderSearchOpts().UseBuiltinIncludes && + Clang->getHeaderSearchOpts().ResourceDir.empty()) + Clang->getHeaderSearchOpts().ResourceDir = + CompilerInvocation::GetResourcesPath(Argv[0], nullptr); + + // Create the actual diagnostics engine. + Clang->createDiagnostics(); + if (!Clang->hasDiagnostics()) + return llvm::createStringError(llvm::errc::not_supported, + "Initialization failed. " + "Unable to create diagnostics engine"); + + DiagsBuffer->FlushDiagnostics(Clang->getDiagnostics()); + if (!Success) + return llvm::createStringError(llvm::errc::not_supported, + "Initialization failed. " + "Unable to flush diagnostics"); + + // FIXME: Merge with CompilerInstance::ExecuteAction. + llvm::MemoryBuffer *MB = llvm::MemoryBuffer::getMemBuffer("").release(); + Clang->getPreprocessorOpts().addRemappedFile("<<< inputs >>>", MB); + + Clang->setTarget(TargetInfo::CreateTargetInfo( + Clang->getDiagnostics(), Clang->getInvocation().TargetOpts)); + if (!Clang->hasTarget()) + return llvm::createStringError(llvm::errc::not_supported, + "Initialization failed. " + "Target is missing"); + + Clang->getTarget().adjust(Clang->getDiagnostics(), Clang->getLangOpts()); + + // Don't clear the AST before backend codegen since we do codegen multiple + // times, reusing the same AST. + Clang->getCodeGenOpts().ClearASTBeforeBackend = false; + + Clang->getFrontendOpts().DisableFree = false; + Clang->getCodeGenOpts().DisableFree = false; + return std::move(Clang); +} + +} // anonymous namespace + +llvm::Expected<std::unique_ptr<CompilerInstance>> +IncrementalCompilerBuilder::create(std::string TT, + std::vector<const char *> &ClangArgv) { + + // If we don't know ClangArgv0 or the address of main() at this point, try + // to guess it anyway (it's possible on some platforms). + std::string MainExecutableName = + llvm::sys::fs::getMainExecutable(nullptr, nullptr); + + ClangArgv.insert(ClangArgv.begin(), MainExecutableName.c_str()); + + // Prepending -c to force the driver to do something if no action was + // specified. By prepending we allow users to override the default + // action and use other actions in incremental mode. + // FIXME: Print proper driver diagnostics if the driver flags are wrong. + // We do C++ by default; append right after argv[0] if no "-x" given + ClangArgv.insert(ClangArgv.end(), "-Xclang"); + ClangArgv.insert(ClangArgv.end(), "-fincremental-extensions"); + ClangArgv.insert(ClangArgv.end(), "-c"); + + // Put a dummy C++ file on to ensure there's at least one compile job for the + // driver to construct. + ClangArgv.push_back("<<< inputs >>>"); + + // Buffer diagnostics from argument parsing so that we can output them using a + // well formed diagnostic object. + IntrusiveRefCntPtr<DiagnosticIDs> DiagID(new DiagnosticIDs()); + IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts = + CreateAndPopulateDiagOpts(ClangArgv); + TextDiagnosticBuffer *DiagsBuffer = new TextDiagnosticBuffer; + DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagsBuffer); + + driver::Driver Driver(/*MainBinaryName=*/ClangArgv[0], TT, Diags); + Driver.setCheckInputsExist(false); // the input comes from mem buffers + llvm::ArrayRef<const char *> RF = llvm::ArrayRef(ClangArgv); + std::unique_ptr<driver::Compilation> Compilation(Driver.BuildCompilation(RF)); + + if (Compilation->getArgs().hasArg(driver::options::OPT_v)) + Compilation->getJobs().Print(llvm::errs(), "\n", /*Quote=*/false); + + auto ErrOrCC1Args = GetCC1Arguments(&Diags, Compilation.get()); + if (auto Err = ErrOrCC1Args.takeError()) + return std::move(Err); + + return CreateCI(**ErrOrCC1Args); +} + +llvm::Expected<std::unique_ptr<CompilerInstance>> +IncrementalCompilerBuilder::CreateCpp() { + std::vector<const char *> Argv; + Argv.reserve(5 + 1 + UserArgs.size()); + Argv.push_back("-xc++"); +#ifdef __EMSCRIPTEN__ + Argv.push_back("-target"); + Argv.push_back("wasm32-unknown-emscripten"); + Argv.push_back("-shared"); + Argv.push_back("-fvisibility=default"); +#endif + Argv.insert(Argv.end(), UserArgs.begin(), UserArgs.end()); + + std::string TT = TargetTriple ? *TargetTriple : llvm::sys::getProcessTriple(); + return IncrementalCompilerBuilder::create(TT, Argv); +} + +llvm::Expected<std::unique_ptr<CompilerInstance>> +IncrementalCompilerBuilder::createCuda(bool device) { + std::vector<const char *> Argv; + Argv.reserve(5 + 4 + UserArgs.size()); + + Argv.push_back("-xcuda"); + if (device) + Argv.push_back("--cuda-device-only"); + else + Argv.push_back("--cuda-host-only"); + + std::string SDKPathArg = "--cuda-path="; + if (!CudaSDKPath.empty()) { + SDKPathArg += CudaSDKPath; + Argv.push_back(SDKPathArg.c_str()); + } + + std::string ArchArg = "--offload-arch="; + if (!OffloadArch.empty()) { + ArchArg += OffloadArch; + Argv.push_back(ArchArg.c_str()); + } + + Argv.insert(Argv.end(), UserArgs.begin(), UserArgs.end()); + + std::string TT = TargetTriple ? *TargetTriple : llvm::sys::getProcessTriple(); + return IncrementalCompilerBuilder::create(TT, Argv); +} + +llvm::Expected<std::unique_ptr<CompilerInstance>> +IncrementalCompilerBuilder::CreateCudaDevice() { + return IncrementalCompilerBuilder::createCuda(true); +} + +llvm::Expected<std::unique_ptr<CompilerInstance>> +IncrementalCompilerBuilder::CreateCudaHost() { + return IncrementalCompilerBuilder::createCuda(false); +} + +Interpreter::Interpreter(std::unique_ptr<CompilerInstance> CI, + llvm::Error &ErrOut, + std::unique_ptr<llvm::orc::LLJITBuilder> JITBuilder) + : JITBuilder(std::move(JITBuilder)) { + llvm::ErrorAsOutParameter EAO(&ErrOut); + auto LLVMCtx = std::make_unique<llvm::LLVMContext>(); + TSCtx = std::make_unique<llvm::orc::ThreadSafeContext>(std::move(LLVMCtx)); + IncrParser = std::make_unique<IncrementalParser>( + *this, std::move(CI), *TSCtx->getContext(), ErrOut); + if (ErrOut) + return; + + // Not all frontends support code-generation, e.g. ast-dump actions don't + if (IncrParser->getCodeGen()) { + if (llvm::Error Err = CreateExecutor()) { + ErrOut = joinErrors(std::move(ErrOut), std::move(Err)); + return; + } + + // Process the PTUs that came from initialization. For example -include will + // give us a header that's processed at initialization of the preprocessor. + for (PartialTranslationUnit &PTU : IncrParser->getPTUs()) + if (llvm::Error Err = Execute(PTU)) { + ErrOut = joinErrors(std::move(ErrOut), std::move(Err)); + return; + } + } +} + +Interpreter::~Interpreter() { + if (IncrExecutor) { + if (llvm::Error Err = IncrExecutor->cleanUp()) + llvm::report_fatal_error( + llvm::Twine("Failed to clean up IncrementalExecutor: ") + + toString(std::move(Err))); + } +} + +// These better to put in a runtime header but we can't. This is because we +// can't find the precise resource directory in unittests so we have to hard +// code them. +const char *const Runtimes = R"( + #define __CLANG_REPL__ 1 +#ifdef __cplusplus + #define EXTERN_C extern "C" + void *__clang_Interpreter_SetValueWithAlloc(void*, void*, void*); + struct __clang_Interpreter_NewTag{} __ci_newtag; + void* operator new(__SIZE_TYPE__, void* __p, __clang_Interpreter_NewTag) noexcept; + template <class T, class = T (*)() /*disable for arrays*/> + void __clang_Interpreter_SetValueCopyArr(T* Src, void* Placement, unsigned long Size) { + for (auto Idx = 0; Idx < Size; ++Idx) + new ((void*)(((T*)Placement) + Idx), __ci_newtag) T(Src[Idx]); + } + template <class T, unsigned long N> + void __clang_Interpreter_SetValueCopyArr(const T (*Src)[N], void* Placement, unsigned long Size) { + __clang_Interpreter_SetValueCopyArr(Src[0], Placement, Size); + } +#else + #define EXTERN_C extern +#endif // __cplusplus + + EXTERN_C void __clang_Interpreter_SetValueNoAlloc(void *This, void *OutVal, void *OpaqueType, ...); +)"; + +llvm::Expected<std::unique_ptr<Interpreter>> +Interpreter::create(std::unique_ptr<CompilerInstance> CI) { + llvm::Error Err = llvm::Error::success(); + auto Interp = + std::unique_ptr<Interpreter>(new Interpreter(std::move(CI), Err)); + if (Err) + return std::move(Err); + + // Add runtime code and set a marker to hide it from user code. Undo will not + // go through that. + auto PTU = Interp->Parse(Runtimes); + if (!PTU) + return PTU.takeError(); + Interp->markUserCodeStart(); + + Interp->ValuePrintingInfo.resize(4); + return std::move(Interp); +} + +llvm::Expected<std::unique_ptr<Interpreter>> +Interpreter::createWithCUDA(std::unique_ptr<CompilerInstance> CI, + std::unique_ptr<CompilerInstance> DCI) { + // avoid writing fat binary to disk using an in-memory virtual file system + llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> IMVFS = + std::make_unique<llvm::vfs::InMemoryFileSystem>(); + llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem> OverlayVFS = + std::make_unique<llvm::vfs::OverlayFileSystem>( + llvm::vfs::getRealFileSystem()); + OverlayVFS->pushOverlay(IMVFS); + CI->createFileManager(OverlayVFS); + + auto Interp = Interpreter::create(std::move(CI)); + if (auto E = Interp.takeError()) + return std::move(E); + + llvm::Error Err = llvm::Error::success(); + auto DeviceParser = std::make_unique<IncrementalCUDADeviceParser>( + **Interp, std::move(DCI), *(*Interp)->IncrParser.get(), + *(*Interp)->TSCtx->getContext(), IMVFS, Err); + if (Err) + return std::move(Err); + + (*Interp)->DeviceParser = std::move(DeviceParser); + + return Interp; +} + +const CompilerInstance *Interpreter::getCompilerInstance() const { + return IncrParser->getCI(); +} + +CompilerInstance *Interpreter::getCompilerInstance() { + return IncrParser->getCI(); +} + +llvm::Expected<llvm::orc::LLJIT &> Interpreter::getExecutionEngine() { + if (!IncrExecutor) { + if (auto Err = CreateExecutor()) + return std::move(Err); + } + + return IncrExecutor->GetExecutionEngine(); +} + +ASTContext &Interpreter::getASTContext() { + return getCompilerInstance()->getASTContext(); +} + +const ASTContext &Interpreter::getASTContext() const { + return getCompilerInstance()->getASTContext(); +} + +void Interpreter::markUserCodeStart() { + assert(!InitPTUSize && "We only do this once"); + InitPTUSize = IncrParser->getPTUs().size(); +} + +size_t Interpreter::getEffectivePTUSize() const { + std::list<PartialTranslationUnit> &PTUs = IncrParser->getPTUs(); + assert(PTUs.size() >= InitPTUSize && "empty PTU list?"); + return PTUs.size() - InitPTUSize; +} + +llvm::Expected<PartialTranslationUnit &> +Interpreter::Parse(llvm::StringRef Code) { + // If we have a device parser, parse it first. + // The generated code will be included in the host compilation + if (DeviceParser) { + auto DevicePTU = DeviceParser->Parse(Code); + if (auto E = DevicePTU.takeError()) + return std::move(E); + } + + // Tell the interpreter sliently ignore unused expressions since value + // printing could cause it. + getCompilerInstance()->getDiagnostics().setSeverity( + clang::diag::warn_unused_expr, diag::Severity::Ignored, SourceLocation()); + return IncrParser->Parse(Code); +} + +static llvm::Expected<llvm::orc::JITTargetMachineBuilder> +createJITTargetMachineBuilder(const std::string &TT) { + if (TT == llvm::sys::getProcessTriple()) + // This fails immediately if the target backend is not registered + return llvm::orc::JITTargetMachineBuilder::detectHost(); + + // If the target backend is not registered, LLJITBuilder::create() will fail + return llvm::orc::JITTargetMachineBuilder(llvm::Triple(TT)); +} + +llvm::Error Interpreter::CreateExecutor() { + if (IncrExecutor) + return llvm::make_error<llvm::StringError>("Operation failed. " + "Execution engine exists", + std::error_code()); + if (!IncrParser->getCodeGen()) + return llvm::make_error<llvm::StringError>("Operation failed. " + "No code generator available", + std::error_code()); + if (!JITBuilder) { + const std::string &TT = getCompilerInstance()->getTargetOpts().Triple; + auto JTMB = createJITTargetMachineBuilder(TT); + if (!JTMB) + return JTMB.takeError(); + auto JB = IncrementalExecutor::createDefaultJITBuilder(std::move(*JTMB)); + if (!JB) + return JB.takeError(); + JITBuilder = std::move(*JB); + } + + llvm::Error Err = llvm::Error::success(); +#ifdef __EMSCRIPTEN__ + auto Executor = std::make_unique<WasmIncrementalExecutor>(*TSCtx); +#else + auto Executor = + std::make_unique<IncrementalExecutor>(*TSCtx, *JITBuilder, Err); +#endif + if (!Err) + IncrExecutor = std::move(Executor); + + return Err; +} + +void Interpreter::ResetExecutor() { IncrExecutor.reset(); } + +llvm::Error Interpreter::Execute(PartialTranslationUnit &T) { + assert(T.TheModule); + if (!IncrExecutor) { + auto Err = CreateExecutor(); + if (Err) + return Err; + } + // FIXME: Add a callback to retain the llvm::Module once the JIT is done. + if (auto Err = IncrExecutor->addModule(T)) + return Err; + + if (auto Err = IncrExecutor->runCtors()) + return Err; + + return llvm::Error::success(); +} + +llvm::Error Interpreter::ParseAndExecute(llvm::StringRef Code, Value *V) { + + auto PTU = Parse(Code); + if (!PTU) + return PTU.takeError(); + if (PTU->TheModule) + if (llvm::Error Err = Execute(*PTU)) + return Err; + + if (LastValue.isValid()) { + if (!V) { + LastValue.dump(); + LastValue.clear(); + } else + *V = std::move(LastValue); + } + return llvm::Error::success(); +} + +llvm::Expected<llvm::orc::ExecutorAddr> +Interpreter::getSymbolAddress(GlobalDecl GD) const { + if (!IncrExecutor) + return llvm::make_error<llvm::StringError>("Operation failed. " + "No execution engine", + std::error_code()); + llvm::StringRef MangledName = IncrParser->GetMangledName(GD); + return getSymbolAddress(MangledName); +} + +llvm::Expected<llvm::orc::ExecutorAddr> +Interpreter::getSymbolAddress(llvm::StringRef IRName) const { + if (!IncrExecutor) + return llvm::make_error<llvm::StringError>("Operation failed. " + "No execution engine", + std::error_code()); + + return IncrExecutor->getSymbolAddress(IRName, IncrementalExecutor::IRName); +} + +llvm::Expected<llvm::orc::ExecutorAddr> +Interpreter::getSymbolAddressFromLinkerName(llvm::StringRef Name) const { + if (!IncrExecutor) + return llvm::make_error<llvm::StringError>("Operation failed. " + "No execution engine", + std::error_code()); + + return IncrExecutor->getSymbolAddress(Name, IncrementalExecutor::LinkerName); +} + +llvm::Error Interpreter::Undo(unsigned N) { + + std::list<PartialTranslationUnit> &PTUs = IncrParser->getPTUs(); + if (N > getEffectivePTUSize()) + return llvm::make_error<llvm::StringError>("Operation failed. " + "Too many undos", + std::error_code()); + for (unsigned I = 0; I < N; I++) { + if (IncrExecutor) { + if (llvm::Error Err = IncrExecutor->removeModule(PTUs.back())) + return Err; + } + + IncrParser->CleanUpPTU(PTUs.back()); + PTUs.pop_back(); + } + return llvm::Error::success(); +} + +llvm::Error Interpreter::LoadDynamicLibrary(const char *name) { + auto EE = getExecutionEngine(); + if (!EE) + return EE.takeError(); + + auto &DL = EE->getDataLayout(); + + if (auto DLSG = llvm::orc::DynamicLibrarySearchGenerator::Load( + name, DL.getGlobalPrefix())) + EE->getMainJITDylib().addGenerator(std::move(*DLSG)); + else + return DLSG.takeError(); + + return llvm::Error::success(); +} + +llvm::Expected<llvm::orc::ExecutorAddr> +Interpreter::CompileDtorCall(CXXRecordDecl *CXXRD) { + assert(CXXRD && "Cannot compile a destructor for a nullptr"); + if (auto Dtor = Dtors.find(CXXRD); Dtor != Dtors.end()) + return Dtor->getSecond(); + + if (CXXRD->hasIrrelevantDestructor()) + return llvm::orc::ExecutorAddr{}; + + CXXDestructorDecl *DtorRD = + getCompilerInstance()->getSema().LookupDestructor(CXXRD); + + llvm::StringRef Name = + IncrParser->GetMangledName(GlobalDecl(DtorRD, Dtor_Base)); + auto AddrOrErr = getSymbolAddress(Name); + if (!AddrOrErr) + return AddrOrErr.takeError(); + + Dtors[CXXRD] = *AddrOrErr; + return AddrOrErr; +} + +static constexpr llvm::StringRef MagicRuntimeInterface[] = { + "__clang_Interpreter_SetValueNoAlloc", + "__clang_Interpreter_SetValueWithAlloc", + "__clang_Interpreter_SetValueCopyArr", "__ci_newtag"}; + +static std::unique_ptr<RuntimeInterfaceBuilder> +createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx, + Sema &S); + +std::unique_ptr<RuntimeInterfaceBuilder> Interpreter::FindRuntimeInterface() { + if (llvm::all_of(ValuePrintingInfo, [](Expr *E) { return E != nullptr; })) + return nullptr; + + Sema &S = getCompilerInstance()->getSema(); + ASTContext &Ctx = S.getASTContext(); + + auto LookupInterface = [&](Expr *&Interface, llvm::StringRef Name) { + LookupResult R(S, &Ctx.Idents.get(Name), SourceLocation(), + Sema::LookupOrdinaryName, + RedeclarationKind::ForVisibleRedeclaration); + S.LookupQualifiedName(R, Ctx.getTranslationUnitDecl()); + if (R.empty()) + return false; + + CXXScopeSpec CSS; + Interface = S.BuildDeclarationNameExpr(CSS, R, /*ADL=*/false).get(); + return true; + }; + + if (!LookupInterface(ValuePrintingInfo[NoAlloc], + MagicRuntimeInterface[NoAlloc])) + return nullptr; + if (Ctx.getLangOpts().CPlusPlus) { + if (!LookupInterface(ValuePrintingInfo[WithAlloc], + MagicRuntimeInterface[WithAlloc])) + return nullptr; + if (!LookupInterface(ValuePrintingInfo[CopyArray], + MagicRuntimeInterface[CopyArray])) + return nullptr; + if (!LookupInterface(ValuePrintingInfo[NewTag], + MagicRuntimeInterface[NewTag])) + return nullptr; + } + + return createInProcessRuntimeInterfaceBuilder(*this, Ctx, S); +} + +namespace { + +class InterfaceKindVisitor + : public TypeVisitor<InterfaceKindVisitor, Interpreter::InterfaceKind> { + friend class InProcessRuntimeInterfaceBuilder; + + ASTContext &Ctx; + Sema &S; + Expr *E; + llvm::SmallVector<Expr *, 3> Args; + +public: + InterfaceKindVisitor(ASTContext &Ctx, Sema &S, Expr *E) + : Ctx(Ctx), S(S), E(E) {} + + Interpreter::InterfaceKind VisitRecordType(const RecordType *Ty) { + return Interpreter::InterfaceKind::WithAlloc; + } + + Interpreter::InterfaceKind + VisitMemberPointerType(const MemberPointerType *Ty) { + return Interpreter::InterfaceKind::WithAlloc; + } + + Interpreter::InterfaceKind + VisitConstantArrayType(const ConstantArrayType *Ty) { + return Interpreter::InterfaceKind::CopyArray; + } + + Interpreter::InterfaceKind + VisitFunctionProtoType(const FunctionProtoType *Ty) { + HandlePtrType(Ty); + return Interpreter::InterfaceKind::NoAlloc; + } + + Interpreter::InterfaceKind VisitPointerType(const PointerType *Ty) { + HandlePtrType(Ty); + return Interpreter::InterfaceKind::NoAlloc; + } + + Interpreter::InterfaceKind VisitReferenceType(const ReferenceType *Ty) { + ExprResult AddrOfE = S.CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, E); + assert(!AddrOfE.isInvalid() && "Can not create unary expression"); + Args.push_back(AddrOfE.get()); + return Interpreter::InterfaceKind::NoAlloc; + } + + Interpreter::InterfaceKind VisitBuiltinType(const BuiltinType *Ty) { + if (Ty->isNullPtrType()) + Args.push_back(E); + else if (Ty->isFloatingType()) + Args.push_back(E); + else if (Ty->isIntegralOrEnumerationType()) + HandleIntegralOrEnumType(Ty); + else if (Ty->isVoidType()) { + // Do we need to still run `E`? + } + + return Interpreter::InterfaceKind::NoAlloc; + } + + Interpreter::InterfaceKind VisitEnumType(const EnumType *Ty) { + HandleIntegralOrEnumType(Ty); + return Interpreter::InterfaceKind::NoAlloc; + } + +private: + // Force cast these types to the uint that fits the register size. That way we + // reduce the number of overloads of `__clang_Interpreter_SetValueNoAlloc`. + void HandleIntegralOrEnumType(const Type *Ty) { + uint64_t PtrBits = Ctx.getTypeSize(Ctx.VoidPtrTy); + QualType UIntTy = Ctx.getBitIntType(/*Unsigned=*/true, PtrBits); + TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(UIntTy); + ExprResult CastedExpr = + S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E); + assert(!CastedExpr.isInvalid() && "Cannot create cstyle cast expr"); + Args.push_back(CastedExpr.get()); + } + + void HandlePtrType(const Type *Ty) { + TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ctx.VoidPtrTy); + ExprResult CastedExpr = + S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E); + assert(!CastedExpr.isInvalid() && "Can not create cstyle cast expression"); + Args.push_back(CastedExpr.get()); + } +}; + +class InProcessRuntimeInterfaceBuilder : public RuntimeInterfaceBuilder { + Interpreter &Interp; + ASTContext &Ctx; + Sema &S; + +public: + InProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &C, Sema &S) + : Interp(Interp), Ctx(C), S(S) {} + + TransformExprFunction *getPrintValueTransformer() override { + return &transformForValuePrinting; + } + +private: + static ExprResult transformForValuePrinting(RuntimeInterfaceBuilder *Builder, + Expr *E, + ArrayRef<Expr *> FixedArgs) { + auto *B = static_cast<InProcessRuntimeInterfaceBuilder *>(Builder); + + // Get rid of ExprWithCleanups. + if (auto *EWC = llvm::dyn_cast_if_present<ExprWithCleanups>(E)) + E = EWC->getSubExpr(); + + InterfaceKindVisitor Visitor(B->Ctx, B->S, E); + + // The Interpreter* parameter and the out parameter `OutVal`. + for (Expr *E : FixedArgs) + Visitor.Args.push_back(E); + + QualType Ty = E->getType(); + QualType DesugaredTy = Ty.getDesugaredType(B->Ctx); + + // For lvalue struct, we treat it as a reference. + if (DesugaredTy->isRecordType() && E->isLValue()) { + DesugaredTy = B->Ctx.getLValueReferenceType(DesugaredTy); + Ty = B->Ctx.getLValueReferenceType(Ty); + } + + Expr *TypeArg = CStyleCastPtrExpr(B->S, B->Ctx.VoidPtrTy, + (uintptr_t)Ty.getAsOpaquePtr()); + // The QualType parameter `OpaqueType`, represented as `void*`. + Visitor.Args.push_back(TypeArg); + + // We push the last parameter based on the type of the Expr. Note we need + // special care for rvalue struct. + Interpreter::InterfaceKind Kind = Visitor.Visit(&*DesugaredTy); + switch (Kind) { + case Interpreter::InterfaceKind::WithAlloc: + case Interpreter::InterfaceKind::CopyArray: { + // __clang_Interpreter_SetValueWithAlloc. + ExprResult AllocCall = B->S.ActOnCallExpr( + /*Scope=*/nullptr, + B->Interp + .getValuePrintingInfo()[Interpreter::InterfaceKind::WithAlloc], + E->getBeginLoc(), Visitor.Args, E->getEndLoc()); + assert(!AllocCall.isInvalid() && "Can't create runtime interface call!"); + + TypeSourceInfo *TSI = + B->Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation()); + + // Force CodeGen to emit destructor. + if (auto *RD = Ty->getAsCXXRecordDecl()) { + auto *Dtor = B->S.LookupDestructor(RD); + Dtor->addAttr(UsedAttr::CreateImplicit(B->Ctx)); + B->Interp.getCompilerInstance()->getASTConsumer().HandleTopLevelDecl( + DeclGroupRef(Dtor)); + } + + // __clang_Interpreter_SetValueCopyArr. + if (Kind == Interpreter::InterfaceKind::CopyArray) { + const auto *ConstantArrTy = + cast<ConstantArrayType>(DesugaredTy.getTypePtr()); + size_t ArrSize = B->Ctx.getConstantArrayElementCount(ConstantArrTy); + Expr *ArrSizeExpr = IntegerLiteralExpr(B->Ctx, ArrSize); + Expr *Args[] = {E, AllocCall.get(), ArrSizeExpr}; + return B->S.ActOnCallExpr( + /*Scope *=*/nullptr, + B->Interp + .getValuePrintingInfo()[Interpreter::InterfaceKind::CopyArray], + SourceLocation(), Args, SourceLocation()); + } + Expr *Args[] = { + AllocCall.get(), + B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NewTag]}; + ExprResult CXXNewCall = B->S.BuildCXXNew( + E->getSourceRange(), + /*UseGlobal=*/true, /*PlacementLParen=*/SourceLocation(), Args, + /*PlacementRParen=*/SourceLocation(), + /*TypeIdParens=*/SourceRange(), TSI->getType(), TSI, std::nullopt, + E->getSourceRange(), E); + + assert(!CXXNewCall.isInvalid() && + "Can't create runtime placement new call!"); + + return B->S.ActOnFinishFullExpr(CXXNewCall.get(), + /*DiscardedValue=*/false); + } + // __clang_Interpreter_SetValueNoAlloc. + case Interpreter::InterfaceKind::NoAlloc: { + return B->S.ActOnCallExpr( + /*Scope=*/nullptr, + B->Interp.getValuePrintingInfo()[Interpreter::InterfaceKind::NoAlloc], + E->getBeginLoc(), Visitor.Args, E->getEndLoc()); + } + default: + llvm_unreachable("Unhandled Interpreter::InterfaceKind"); + } + } +}; +} // namespace + +static std::unique_ptr<RuntimeInterfaceBuilder> +createInProcessRuntimeInterfaceBuilder(Interpreter &Interp, ASTContext &Ctx, + Sema &S) { + return std::make_unique<InProcessRuntimeInterfaceBuilder>(Interp, Ctx, S); +} + +// This synthesizes a call expression to a speciall +// function that is responsible for generating the Value. +// In general, we transform: +// clang-repl> x +// To: +// // 1. If x is a built-in type like int, float. +// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, x); +// // 2. If x is a struct, and a lvalue. +// __clang_Interpreter_SetValueNoAlloc(ThisInterp, OpaqueValue, xQualType, +// &x); +// // 3. If x is a struct, but a rvalue. +// new (__clang_Interpreter_SetValueWithAlloc(ThisInterp, OpaqueValue, +// xQualType)) (x); + +Expr *Interpreter::SynthesizeExpr(Expr *E) { + Sema &S = getCompilerInstance()->getSema(); + ASTContext &Ctx = S.getASTContext(); + + if (!RuntimeIB) { + RuntimeIB = FindRuntimeInterface(); + AddPrintValueCall = RuntimeIB->getPrintValueTransformer(); + } + + assert(AddPrintValueCall && + "We don't have a runtime interface for pretty print!"); + + // Create parameter `ThisInterp`. + auto *ThisInterp = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)this); + + // Create parameter `OutVal`. + auto *OutValue = CStyleCastPtrExpr(S, Ctx.VoidPtrTy, (uintptr_t)&LastValue); + + // Build `__clang_Interpreter_SetValue*` call. + ExprResult Result = + AddPrintValueCall(RuntimeIB.get(), E, {ThisInterp, OutValue}); + + // It could fail, like printing an array type in C. (not supported) + if (Result.isInvalid()) + return E; + return Result.get(); +} + +// Temporary rvalue struct that need special care. +REPL_EXTERNAL_VISIBILITY void * +__clang_Interpreter_SetValueWithAlloc(void *This, void *OutVal, + void *OpaqueType) { + Value &VRef = *(Value *)OutVal; + VRef = Value(static_cast<Interpreter *>(This), OpaqueType); + return VRef.getPtr(); +} + +extern "C" void REPL_EXTERNAL_VISIBILITY __clang_Interpreter_SetValueNoAlloc( + void *This, void *OutVal, void *OpaqueType, ...) { + Value &VRef = *(Value *)OutVal; + Interpreter *I = static_cast<Interpreter *>(This); + VRef = Value(I, OpaqueType); + if (VRef.isVoid()) + return; + + va_list args; + va_start(args, /*last named param*/ OpaqueType); + + QualType QT = VRef.getType(); + if (VRef.getKind() == Value::K_PtrOrObj) { + VRef.setPtr(va_arg(args, void *)); + } else { + if (const auto *ET = QT->getAs<EnumType>()) + QT = ET->getDecl()->getIntegerType(); + switch (QT->castAs<BuiltinType>()->getKind()) { + default: + llvm_unreachable("unknown type kind!"); + break; + // Types shorter than int are resolved as int, else va_arg has UB. + case BuiltinType::Bool: + VRef.setBool(va_arg(args, int)); + break; + case BuiltinType::Char_S: + VRef.setChar_S(va_arg(args, int)); + break; + case BuiltinType::SChar: + VRef.setSChar(va_arg(args, int)); + break; + case BuiltinType::Char_U: + VRef.setChar_U(va_arg(args, unsigned)); + break; + case BuiltinType::UChar: + VRef.setUChar(va_arg(args, unsigned)); + break; + case BuiltinType::Short: + VRef.setShort(va_arg(args, int)); + break; + case BuiltinType::UShort: + VRef.setUShort(va_arg(args, unsigned)); + break; + case BuiltinType::Int: + VRef.setInt(va_arg(args, int)); + break; + case BuiltinType::UInt: + VRef.setUInt(va_arg(args, unsigned)); + break; + case BuiltinType::Long: + VRef.setLong(va_arg(args, long)); + break; + case BuiltinType::ULong: + VRef.setULong(va_arg(args, unsigned long)); + break; + case BuiltinType::LongLong: + VRef.setLongLong(va_arg(args, long long)); + break; + case BuiltinType::ULongLong: + VRef.setULongLong(va_arg(args, unsigned long long)); + break; + // Types shorter than double are resolved as double, else va_arg has UB. + case BuiltinType::Float: + VRef.setFloat(va_arg(args, double)); + break; + case BuiltinType::Double: + VRef.setDouble(va_arg(args, double)); + break; + case BuiltinType::LongDouble: + VRef.setLongDouble(va_arg(args, long double)); + break; + // See REPL_BUILTIN_TYPES. + } + } + va_end(args); +} + +// A trampoline to work around the fact that operator placement new cannot +// really be forward declared due to libc++ and libstdc++ declaration mismatch. +// FIXME: __clang_Interpreter_NewTag is ODR violation because we get the same +// definition in the interpreter runtime. We should move it in a runtime header +// which gets included by the interpreter and here. +struct __clang_Interpreter_NewTag {}; +REPL_EXTERNAL_VISIBILITY void * +operator new(size_t __sz, void *__p, __clang_Interpreter_NewTag) noexcept { + // Just forward to the standard operator placement new. + return operator new(__sz, __p); +} diff --git a/contrib/llvm-project/clang/lib/Interpreter/InterpreterUtils.cpp b/contrib/llvm-project/clang/lib/Interpreter/InterpreterUtils.cpp new file mode 100644 index 000000000000..45f6322b8461 --- /dev/null +++ b/contrib/llvm-project/clang/lib/Interpreter/InterpreterUtils.cpp @@ -0,0 +1,111 @@ +//===--- InterpreterUtils.cpp - Incremental Utils --------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements some common utils used in the incremental library. +// +//===----------------------------------------------------------------------===// + +#include "InterpreterUtils.h" + +namespace clang { + +IntegerLiteral *IntegerLiteralExpr(ASTContext &C, uint64_t Val) { + return IntegerLiteral::Create(C, llvm::APSInt::getUnsigned(Val), + C.UnsignedLongLongTy, SourceLocation()); +} + +Expr *CStyleCastPtrExpr(Sema &S, QualType Ty, Expr *E) { + ASTContext &Ctx = S.getASTContext(); + if (!Ty->isPointerType()) + Ty = Ctx.getPointerType(Ty); + + TypeSourceInfo *TSI = Ctx.getTrivialTypeSourceInfo(Ty, SourceLocation()); + Expr *Result = + S.BuildCStyleCastExpr(SourceLocation(), TSI, SourceLocation(), E).get(); + assert(Result && "Cannot create CStyleCastPtrExpr"); + return Result; +} + +Expr *CStyleCastPtrExpr(Sema &S, QualType Ty, uintptr_t Ptr) { + ASTContext &Ctx = S.getASTContext(); + return CStyleCastPtrExpr(S, Ty, IntegerLiteralExpr(Ctx, (uint64_t)Ptr)); +} + +Sema::DeclGroupPtrTy CreateDGPtrFrom(Sema &S, Decl *D) { + SmallVector<Decl *, 1> DeclsInGroup; + DeclsInGroup.push_back(D); + Sema::DeclGroupPtrTy DeclGroupPtr = S.BuildDeclaratorGroup(DeclsInGroup); + return DeclGroupPtr; +} + +NamespaceDecl *LookupNamespace(Sema &S, llvm::StringRef Name, + const DeclContext *Within) { + DeclarationName DName = &S.Context.Idents.get(Name); + LookupResult R(S, DName, SourceLocation(), + Sema::LookupNestedNameSpecifierName); + R.suppressDiagnostics(); + if (!Within) + S.LookupName(R, S.TUScope); + else { + if (const auto *TD = dyn_cast<clang::TagDecl>(Within); + TD && !TD->getDefinition()) + // No definition, no lookup result. + return nullptr; + + S.LookupQualifiedName(R, const_cast<DeclContext *>(Within)); + } + + if (R.empty()) + return nullptr; + + R.resolveKind(); + + return dyn_cast<NamespaceDecl>(R.getFoundDecl()); +} + +NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name, + const DeclContext *Within) { + DeclarationName DName = &S.Context.Idents.get(Name); + LookupResult R(S, DName, SourceLocation(), Sema::LookupOrdinaryName, + RedeclarationKind::ForVisibleRedeclaration); + + R.suppressDiagnostics(); + + if (!Within) + S.LookupName(R, S.TUScope); + else { + const DeclContext *PrimaryWithin = nullptr; + if (const auto *TD = dyn_cast<TagDecl>(Within)) + PrimaryWithin = llvm::dyn_cast_or_null<DeclContext>(TD->getDefinition()); + else + PrimaryWithin = Within->getPrimaryContext(); + + // No definition, no lookup result. + if (!PrimaryWithin) + return nullptr; + + S.LookupQualifiedName(R, const_cast<DeclContext *>(PrimaryWithin)); + } + + if (R.empty()) + return nullptr; + R.resolveKind(); + + if (R.isSingleResult()) + return llvm::dyn_cast<NamedDecl>(R.getFoundDecl()); + + return nullptr; +} + +std::string GetFullTypeName(ASTContext &Ctx, QualType QT) { + PrintingPolicy Policy(Ctx.getPrintingPolicy()); + Policy.SuppressScope = false; + Policy.AnonymousTagLocations = false; + return QT.getAsString(Policy); +} +} // namespace clang diff --git a/contrib/llvm-project/clang/lib/Interpreter/InterpreterUtils.h b/contrib/llvm-project/clang/lib/Interpreter/InterpreterUtils.h new file mode 100644 index 000000000000..8df158c17d49 --- /dev/null +++ b/contrib/llvm-project/clang/lib/Interpreter/InterpreterUtils.h @@ -0,0 +1,54 @@ +//===--- InterpreterUtils.h - Incremental Utils --------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements some common utils used in the incremental library. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_INTERPRETER_UTILS_H +#define LLVM_CLANG_INTERPRETER_UTILS_H + +#include "clang/AST/ASTContext.h" +#include "clang/AST/Mangle.h" +#include "clang/AST/TypeVisitor.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/CodeGen/ObjectFilePCHContainerOperations.h" +#include "clang/Driver/Compilation.h" +#include "clang/Driver/Driver.h" +#include "clang/Driver/Job.h" +#include "clang/Driver/Options.h" +#include "clang/Driver/Tool.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/TextDiagnosticBuffer.h" +#include "clang/Lex/PreprocessorOptions.h" + +#include "clang/Sema/Lookup.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/Errc.h" +#include "llvm/TargetParser/Host.h" + +namespace clang { +IntegerLiteral *IntegerLiteralExpr(ASTContext &C, uint64_t Val); + +Expr *CStyleCastPtrExpr(Sema &S, QualType Ty, Expr *E); + +Expr *CStyleCastPtrExpr(Sema &S, QualType Ty, uintptr_t Ptr); + +Sema::DeclGroupPtrTy CreateDGPtrFrom(Sema &S, Decl *D); + +NamespaceDecl *LookupNamespace(Sema &S, llvm::StringRef Name, + const DeclContext *Within = nullptr); + +NamedDecl *LookupNamed(Sema &S, llvm::StringRef Name, + const DeclContext *Within); + +std::string GetFullTypeName(ASTContext &Ctx, QualType QT); +} // namespace clang + +#endif diff --git a/contrib/llvm-project/clang/lib/Interpreter/Value.cpp b/contrib/llvm-project/clang/lib/Interpreter/Value.cpp new file mode 100644 index 000000000000..eb2ce9c9fd33 --- /dev/null +++ b/contrib/llvm-project/clang/lib/Interpreter/Value.cpp @@ -0,0 +1,269 @@ +//===------------ Value.cpp - Definition of interpreter value -------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// This file defines the class that used to represent a value in incremental +// C++. +// +//===----------------------------------------------------------------------===// + +#include "clang/Interpreter/Value.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Type.h" +#include "clang/Interpreter/Interpreter.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_os_ostream.h" +#include <cassert> +#include <cstdint> +#include <utility> + +namespace { + +// This is internal buffer maintained by Value, used to hold temporaries. +class ValueStorage { +public: + using DtorFunc = void (*)(void *); + + static unsigned char *CreatePayload(void *DtorF, size_t AllocSize, + size_t ElementsSize) { + if (AllocSize < sizeof(Canary)) + AllocSize = sizeof(Canary); + unsigned char *Buf = + new unsigned char[ValueStorage::getPayloadOffset() + AllocSize]; + ValueStorage *VS = new (Buf) ValueStorage(DtorF, AllocSize, ElementsSize); + std::memcpy(VS->getPayload(), Canary, sizeof(Canary)); + return VS->getPayload(); + } + + unsigned char *getPayload() { return Storage; } + const unsigned char *getPayload() const { return Storage; } + + static unsigned getPayloadOffset() { + static ValueStorage Dummy(nullptr, 0, 0); + return Dummy.getPayload() - reinterpret_cast<unsigned char *>(&Dummy); + } + + static ValueStorage *getFromPayload(void *Payload) { + ValueStorage *R = reinterpret_cast<ValueStorage *>( + (unsigned char *)Payload - getPayloadOffset()); + return R; + } + + void Retain() { ++RefCnt; } + + void Release() { + assert(RefCnt > 0 && "Can't release if reference count is already zero"); + if (--RefCnt == 0) { + // We have a non-trivial dtor. + if (Dtor && IsAlive()) { + assert(Elements && "We at least should have 1 element in Value"); + size_t Stride = AllocSize / Elements; + for (size_t Idx = 0; Idx < Elements; ++Idx) + (*Dtor)(getPayload() + Idx * Stride); + } + delete[] reinterpret_cast<unsigned char *>(this); + } + } + + // Check whether the storage is valid by validating the canary bits. + // If someone accidentally write some invalid bits in the storage, the canary + // will be changed first, and `IsAlive` will return false then. + bool IsAlive() const { + return std::memcmp(getPayload(), Canary, sizeof(Canary)) != 0; + } + +private: + ValueStorage(void *DtorF, size_t AllocSize, size_t ElementsNum) + : RefCnt(1), Dtor(reinterpret_cast<DtorFunc>(DtorF)), + AllocSize(AllocSize), Elements(ElementsNum) {} + + mutable unsigned RefCnt; + DtorFunc Dtor = nullptr; + size_t AllocSize = 0; + size_t Elements = 0; + unsigned char Storage[1]; + + // These are some canary bits that are used for protecting the storage been + // damaged. + static constexpr unsigned char Canary[8] = {0x4c, 0x37, 0xad, 0x8f, + 0x2d, 0x23, 0x95, 0x91}; +}; +} // namespace + +namespace clang { + +static Value::Kind ConvertQualTypeToKind(const ASTContext &Ctx, QualType QT) { + if (Ctx.hasSameType(QT, Ctx.VoidTy)) + return Value::K_Void; + + if (const auto *ET = QT->getAs<EnumType>()) + QT = ET->getDecl()->getIntegerType(); + + const auto *BT = QT->getAs<BuiltinType>(); + if (!BT || BT->isNullPtrType()) + return Value::K_PtrOrObj; + + switch (QT->castAs<BuiltinType>()->getKind()) { + default: + assert(false && "Type not supported"); + return Value::K_Unspecified; +#define X(type, name) \ + case BuiltinType::name: \ + return Value::K_##name; + REPL_BUILTIN_TYPES +#undef X + } +} + +Value::Value(Interpreter *In, void *Ty) : Interp(In), OpaqueType(Ty) { + setKind(ConvertQualTypeToKind(getASTContext(), getType())); + if (ValueKind == K_PtrOrObj) { + QualType Canon = getType().getCanonicalType(); + if ((Canon->isPointerType() || Canon->isObjectType() || + Canon->isReferenceType()) && + (Canon->isRecordType() || Canon->isConstantArrayType() || + Canon->isMemberPointerType())) { + IsManuallyAlloc = true; + // Compile dtor function. + Interpreter &Interp = getInterpreter(); + void *DtorF = nullptr; + size_t ElementsSize = 1; + QualType DtorTy = getType(); + + if (const auto *ArrTy = + llvm::dyn_cast<ConstantArrayType>(DtorTy.getTypePtr())) { + DtorTy = ArrTy->getElementType(); + llvm::APInt ArrSize(sizeof(size_t) * 8, 1); + do { + ArrSize *= ArrTy->getSize(); + ArrTy = llvm::dyn_cast<ConstantArrayType>( + ArrTy->getElementType().getTypePtr()); + } while (ArrTy); + ElementsSize = static_cast<size_t>(ArrSize.getZExtValue()); + } + if (const auto *RT = DtorTy->getAs<RecordType>()) { + if (CXXRecordDecl *CXXRD = + llvm::dyn_cast<CXXRecordDecl>(RT->getDecl())) { + if (llvm::Expected<llvm::orc::ExecutorAddr> Addr = + Interp.CompileDtorCall(CXXRD)) + DtorF = reinterpret_cast<void *>(Addr->getValue()); + else + llvm::logAllUnhandledErrors(Addr.takeError(), llvm::errs()); + } + } + + size_t AllocSize = + getASTContext().getTypeSizeInChars(getType()).getQuantity(); + unsigned char *Payload = + ValueStorage::CreatePayload(DtorF, AllocSize, ElementsSize); + setPtr((void *)Payload); + } + } +} + +Value::Value(const Value &RHS) + : Interp(RHS.Interp), OpaqueType(RHS.OpaqueType), Data(RHS.Data), + ValueKind(RHS.ValueKind), IsManuallyAlloc(RHS.IsManuallyAlloc) { + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Retain(); +} + +Value::Value(Value &&RHS) noexcept { + Interp = std::exchange(RHS.Interp, nullptr); + OpaqueType = std::exchange(RHS.OpaqueType, nullptr); + Data = RHS.Data; + ValueKind = std::exchange(RHS.ValueKind, K_Unspecified); + IsManuallyAlloc = std::exchange(RHS.IsManuallyAlloc, false); + + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Release(); +} + +Value &Value::operator=(const Value &RHS) { + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Release(); + + Interp = RHS.Interp; + OpaqueType = RHS.OpaqueType; + Data = RHS.Data; + ValueKind = RHS.ValueKind; + IsManuallyAlloc = RHS.IsManuallyAlloc; + + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Retain(); + + return *this; +} + +Value &Value::operator=(Value &&RHS) noexcept { + if (this != &RHS) { + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Release(); + + Interp = std::exchange(RHS.Interp, nullptr); + OpaqueType = std::exchange(RHS.OpaqueType, nullptr); + ValueKind = std::exchange(RHS.ValueKind, K_Unspecified); + IsManuallyAlloc = std::exchange(RHS.IsManuallyAlloc, false); + + Data = RHS.Data; + } + return *this; +} + +void Value::clear() { + if (IsManuallyAlloc) + ValueStorage::getFromPayload(getPtr())->Release(); + ValueKind = K_Unspecified; + OpaqueType = nullptr; + Interp = nullptr; + IsManuallyAlloc = false; +} + +Value::~Value() { clear(); } + +void *Value::getPtr() const { + assert(ValueKind == K_PtrOrObj); + return Data.m_Ptr; +} + +QualType Value::getType() const { + return QualType::getFromOpaquePtr(OpaqueType); +} + +Interpreter &Value::getInterpreter() { + assert(Interp != nullptr && + "Can't get interpreter from a default constructed value"); + return *Interp; +} + +const Interpreter &Value::getInterpreter() const { + assert(Interp != nullptr && + "Can't get interpreter from a default constructed value"); + return *Interp; +} + +ASTContext &Value::getASTContext() { return getInterpreter().getASTContext(); } + +const ASTContext &Value::getASTContext() const { + return getInterpreter().getASTContext(); +} + +void Value::dump() const { print(llvm::outs()); } + +void Value::printType(llvm::raw_ostream &Out) const { + Out << "Not implement yet.\n"; +} +void Value::printData(llvm::raw_ostream &Out) const { + Out << "Not implement yet.\n"; +} +void Value::print(llvm::raw_ostream &Out) const { + assert(OpaqueType != nullptr && "Can't print default Value"); + Out << "Not implement yet.\n"; +} + +} // namespace clang diff --git a/contrib/llvm-project/clang/lib/Interpreter/Wasm.cpp b/contrib/llvm-project/clang/lib/Interpreter/Wasm.cpp new file mode 100644 index 000000000000..aa10b160ccf8 --- /dev/null +++ b/contrib/llvm-project/clang/lib/Interpreter/Wasm.cpp @@ -0,0 +1,149 @@ +//===----------------- Wasm.cpp - Wasm Interpreter --------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements interpreter support for code execution in WebAssembly. +// +//===----------------------------------------------------------------------===// + +#include "Wasm.h" +#include "IncrementalExecutor.h" + +#include <llvm/IR/LegacyPassManager.h> +#include <llvm/IR/Module.h> +#include <llvm/MC/TargetRegistry.h> +#include <llvm/Target/TargetMachine.h> + +#include <clang/Interpreter/Interpreter.h> + +#include <string> + +namespace lld { +enum Flavor { + Invalid, + Gnu, // -flavor gnu + MinGW, // -flavor gnu MinGW + WinLink, // -flavor link + Darwin, // -flavor darwin + Wasm, // -flavor wasm +}; + +using Driver = bool (*)(llvm::ArrayRef<const char *>, llvm::raw_ostream &, + llvm::raw_ostream &, bool, bool); + +struct DriverDef { + Flavor f; + Driver d; +}; + +struct Result { + int retCode; + bool canRunAgain; +}; + +Result lldMain(llvm::ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, llvm::ArrayRef<DriverDef> drivers); + +namespace wasm { +bool link(llvm::ArrayRef<const char *> args, llvm::raw_ostream &stdoutOS, + llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput); +} // namespace wasm +} // namespace lld + +#include <dlfcn.h> + +namespace clang { + +WasmIncrementalExecutor::WasmIncrementalExecutor( + llvm::orc::ThreadSafeContext &TSC) + : IncrementalExecutor(TSC) {} + +llvm::Error WasmIncrementalExecutor::addModule(PartialTranslationUnit &PTU) { + std::string ErrorString; + + const llvm::Target *Target = llvm::TargetRegistry::lookupTarget( + PTU.TheModule->getTargetTriple(), ErrorString); + if (!Target) { + return llvm::make_error<llvm::StringError>("Failed to create Wasm Target: ", + llvm::inconvertibleErrorCode()); + } + + llvm::TargetOptions TO = llvm::TargetOptions(); + llvm::TargetMachine *TargetMachine = Target->createTargetMachine( + PTU.TheModule->getTargetTriple(), "", "", TO, llvm::Reloc::Model::PIC_); + PTU.TheModule->setDataLayout(TargetMachine->createDataLayout()); + std::string ObjectFileName = PTU.TheModule->getName().str() + ".o"; + std::string BinaryFileName = PTU.TheModule->getName().str() + ".wasm"; + + std::error_code Error; + llvm::raw_fd_ostream ObjectFileOutput(llvm::StringRef(ObjectFileName), Error); + + llvm::legacy::PassManager PM; + if (TargetMachine->addPassesToEmitFile(PM, ObjectFileOutput, nullptr, + llvm::CodeGenFileType::ObjectFile)) { + return llvm::make_error<llvm::StringError>( + "Wasm backend cannot produce object.", llvm::inconvertibleErrorCode()); + } + + if (!PM.run(*PTU.TheModule)) { + + return llvm::make_error<llvm::StringError>("Failed to emit Wasm object.", + llvm::inconvertibleErrorCode()); + } + + ObjectFileOutput.close(); + + std::vector<const char *> LinkerArgs = {"wasm-ld", + "-shared", + "--import-memory", + "--experimental-pic", + "--stack-first", + "--allow-undefined", + ObjectFileName.c_str(), + "-o", + BinaryFileName.c_str()}; + + const lld::DriverDef WasmDriver = {lld::Flavor::Wasm, &lld::wasm::link}; + std::vector<lld::DriverDef> WasmDriverArgs; + WasmDriverArgs.push_back(WasmDriver); + lld::Result Result = + lld::lldMain(LinkerArgs, llvm::outs(), llvm::errs(), WasmDriverArgs); + + if (Result.retCode) + return llvm::make_error<llvm::StringError>( + "Failed to link incremental module", llvm::inconvertibleErrorCode()); + + void *LoadedLibModule = + dlopen(BinaryFileName.c_str(), RTLD_NOW | RTLD_GLOBAL); + if (LoadedLibModule == nullptr) { + llvm::errs() << dlerror() << '\n'; + return llvm::make_error<llvm::StringError>( + "Failed to load incremental module", llvm::inconvertibleErrorCode()); + } + + return llvm::Error::success(); +} + +llvm::Error WasmIncrementalExecutor::removeModule(PartialTranslationUnit &PTU) { + return llvm::make_error<llvm::StringError>("Not implemented yet", + llvm::inconvertibleErrorCode()); +} + +llvm::Error WasmIncrementalExecutor::runCtors() const { + // This seems to be automatically done when using dlopen() + return llvm::Error::success(); +} + +llvm::Error WasmIncrementalExecutor::cleanUp() { + // Can't call cleanUp through IncrementalExecutor as it + // tries to deinitialize JIT which hasn't been initialized + return llvm::Error::success(); +} + +WasmIncrementalExecutor::~WasmIncrementalExecutor() = default; + +} // namespace clang
\ No newline at end of file diff --git a/contrib/llvm-project/clang/lib/Interpreter/Wasm.h b/contrib/llvm-project/clang/lib/Interpreter/Wasm.h new file mode 100644 index 000000000000..4632613326d3 --- /dev/null +++ b/contrib/llvm-project/clang/lib/Interpreter/Wasm.h @@ -0,0 +1,38 @@ +//===------------------ Wasm.h - Wasm Interpreter ---------------*- 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 +// +//===----------------------------------------------------------------------===// +// +// This file implements interpreter support for code execution in WebAssembly. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_INTERPRETER_WASM_H +#define LLVM_CLANG_LIB_INTERPRETER_WASM_H + +#ifndef __EMSCRIPTEN__ +#error "This requires emscripten." +#endif // __EMSCRIPTEN__ + +#include "IncrementalExecutor.h" + +namespace clang { + +class WasmIncrementalExecutor : public IncrementalExecutor { +public: + WasmIncrementalExecutor(llvm::orc::ThreadSafeContext &TSC); + + llvm::Error addModule(PartialTranslationUnit &PTU) override; + llvm::Error removeModule(PartialTranslationUnit &PTU) override; + llvm::Error runCtors() const override; + llvm::Error cleanUp() override; + + ~WasmIncrementalExecutor() override; +}; + +} // namespace clang + +#endif // LLVM_CLANG_LIB_INTERPRETER_WASM_H |