From 876c59778ef6b1d301f030914075e92b6594fa5b Mon Sep 17 00:00:00 2001 From: ykiko Date: Mon, 14 Oct 2024 19:26:51 +0800 Subject: [PATCH] Refactor `Compiler` class. --- .vscode/launch.json | 2 +- include/Compiler/Compiler.h | 58 ++++++++++---- src/Compiler/Compiler.cpp | 150 +++++++++++++++++++----------------- tests/AST/Compiler.cpp | 75 ++++++++++-------- 4 files changed, 163 insertions(+), 122 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 51da5601..0233858b 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -23,7 +23,7 @@ "program": "${workspaceFolder}/build/bin/clice_test", "args": [ "--test-dir=/home/ykiko/C++/clice2/tests/Source", - "--gtest_filter=clice.Compiler" + "--gtest_filter=clice.PCM" ], "cwd": "${workspaceFolder}" } diff --git a/include/Compiler/Compiler.h b/include/Compiler/Compiler.h index 666051c1..338b507b 100644 --- a/include/Compiler/Compiler.h +++ b/include/Compiler/Compiler.h @@ -16,32 +16,58 @@ std::unique_ptr createInvocation(StringRef filename, std::unique_ptr createInstance(std::shared_ptr invocation); -/// - build AST -/// - build module -/// - build preamble -/// - build CodeCompletion class Compiler { public: - Compiler( // clang::DiagnosticsEngine& engine, - llvm::IntrusiveRefCntPtr vfs = llvm::vfs::getRealFileSystem()) : vfs(vfs) {} + Compiler(llvm::StringRef filepath, + llvm::StringRef content, + llvm::ArrayRef args, + llvm::IntrusiveRefCntPtr vfs = llvm::vfs::getRealFileSystem()); - /// build PreCompiledHeader. - void buildPCH(llvm::StringRef filename, llvm::StringRef content, llvm::ArrayRef args); + ~Compiler(); - void applyPCH(clang::CompilerInvocation& invocation, - llvm::StringRef filename, - llvm::StringRef content, - llvm::StringRef filepath); + /// Is success, return true. + bool applyPCH(llvm::StringRef filepath, std::uint32_t bound, bool endAtStart = false); - /// build PreCompiledModule. - void buildPCM(llvm::StringRef filename, llvm::StringRef content, llvm::ArrayRef args); + bool applyPCM(llvm::StringRef filepath, llvm::StringRef name); /// build AST. void buildAST(); + /// Generate the PCH(PreCompiledHeader) to output path. Generally execute `clang::GeneratePCHAction`. + /// The Header part of the source file is stored in the PCH file. Bound is the size of the header part. + void generatePCH(llvm::StringRef outpath, std::uint32_t bound, bool endAtStart = false); + + /// Generate the PCM(PreCompiledModule) to output path. Generally execute + /// `clang::GenerateReducedModuleInterfaceAction`. + void generatePCM(llvm::StringRef outpath); + + void codeCompletion(llvm::StringRef filepath, + std::uint32_t line, + std::uint32_t column, + clang::CodeCompleteConsumer* consumer); + + clang::Sema& sema() { + return instance->getSema(); + } + + clang::SourceManager& srcMgr() { + return instance->getSourceManager(); + } + + clang::ASTContext& context() { + return instance->getASTContext(); + } + + clang::TranslationUnitDecl* tu() { + return instance->getASTContext().getTranslationUnitDecl(); + } + private: - // clang::DiagnosticsEngine& engine; - llvm::IntrusiveRefCntPtr vfs; + std::string filepath; + std::string content; + std::unique_ptr action; + std::unique_ptr instance; + std::unique_ptr buffer; }; } // namespace clice diff --git a/src/Compiler/Compiler.cpp b/src/Compiler/Compiler.cpp index a79a5c0a..3255a10b 100644 --- a/src/Compiler/Compiler.cpp +++ b/src/Compiler/Compiler.cpp @@ -54,110 +54,118 @@ std::unique_ptr createInstance(std::shared_ptr args, + llvm::IntrusiveRefCntPtr vfs) : filepath(filepath), content(content) { + // TODO: figure out should we use createInvocation? + clang::CreateInvocationOptions options; + auto invocation = clang::createInvocation(args, options); - invocation.getLangOpts().SkipODRCheckInGMF = true; + instance = std::make_unique(); - auto& search = invocation.getHeaderSearchOpts(); - search.ValidateASTInputFilesContent = true; + instance->setInvocation(std::move(invocation)); - // TODO: insert all module name with their corresponding module files. - auto& prebuilts = search.PrebuiltModuleFiles; + instance->createDiagnostics(new clang::TextDiagnosticPrinter(llvm::outs(), new clang::DiagnosticOptions()), true); - // TODO: must set pcm output file. - auto& frontend = invocation.getFrontendOpts(); - frontend.OutputFile = "..."; - - auto instance = createInstance(std::make_shared(invocation)); - - clang::GenerateReducedModuleInterfaceAction action; - instance->ExecuteAction(action); - - // TODO: check this. - instance->getDiagnostics().hasErrorOccurred(); + if(!instance->createTarget()) { + llvm::errs() << "Failed to create target\n"; + std::terminate(); + } } -void Compiler::buildPCH(llvm::StringRef filename, llvm::StringRef content, llvm::ArrayRef args) { - // compute preamble bounds, i.e. the range of the preamble. - // if MaxLines set to false(0), i.e. limit the number of lines. - auto bounds = clang::Lexer::ComputePreamble(content, {}, false); - - auto invocation = createInvocation(filename, content.substr(0, bounds.Size), args); - - auto instance = createInstance(std::move(invocation)); - - auto& frontend = instance->getFrontendOpts(); - frontend.OutputFile = "/home/ykiko/C++/clice2/build/cache/xxx.pch"; +Compiler::~Compiler() { + if(action) { + action->EndSourceFile(); + } +} +bool Compiler::applyPCH(llvm::StringRef filepath, std::uint32_t bound, bool endAtStart) { + // TODO: check reuseable? auto& preproc = instance->getPreprocessorOpts(); - preproc.PrecompiledPreambleBytes.first = 0; - preproc.PrecompiledPreambleBytes.second = false; - preproc.GeneratePreamble = true; + preproc.UsePredefines = false; + preproc.ImplicitPCHInclude = filepath; + preproc.PrecompiledPreambleBytes.first = bound; + preproc.PrecompiledPreambleBytes.second = endAtStart; + preproc.DisablePCHOrModuleValidation = clang::DisableValidationForModuleKind::PCH; + return true; +} - llvm::outs() << "bounds size: " << bounds.Size << "\n"; +bool Compiler::applyPCM(llvm::StringRef filepath, llvm::StringRef name) { + // TODO: check reuseable? + instance->getHeaderSearchOpts().PrebuiltModuleFiles.try_emplace(name.str(), filepath); + return true; +} - // use to collect information in the process of building preamble, such as include files and macros - // TODO: inherit from clang::PreambleCallbacks and collect the information - clang::PreambleCallbacks callbacks = {}; +void Compiler::generatePCH(llvm::StringRef outpath, std::uint32_t bound, bool endAtStart) { + action = std::make_unique(); - auto action = clang::GeneratePCHAction(); + instance->getFrontendOpts().OutputFile = outpath; - if(!action.BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) { + auto buffer = llvm::MemoryBuffer::getMemBufferCopy(content.substr(0, bound)); + instance->getPreprocessorOpts().addRemappedFile(filepath, buffer.release()); + + if(!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) { llvm::errs() << "Failed to begin source file\n"; std::terminate(); } - if(auto error = action.Execute()) { + if(auto error = action->Execute()) { llvm::errs() << "Failed to execute action: " << error << "\n"; std::terminate(); } - - // instance->getASTContext().getTranslationUnitDecl()->dump(); - - action.EndSourceFile(); } -void Compiler::applyPCH(clang::CompilerInvocation& invocation, - llvm::StringRef filename, - llvm::StringRef content, - llvm::StringRef filepath) { +void Compiler::generatePCM(llvm::StringRef outpath) { + action = std::make_unique(); - auto bounds = clang::Lexer::ComputePreamble(content, invocation.getLangOpts(), false); + instance->getFrontendOpts().OutputFile = outpath; - auto& PreprocessorOpts = invocation.getPreprocessorOpts(); + auto buffer = llvm::MemoryBuffer::getMemBufferCopy(content); + instance->getPreprocessorOpts().addRemappedFile(filepath, buffer.release()); - PreprocessorOpts.PrecompiledPreambleBytes.first = bounds.Size; - PreprocessorOpts.PrecompiledPreambleBytes.second = bounds.PreambleEndsAtStartOfLine; - PreprocessorOpts.DisablePCHOrModuleValidation = clang::DisableValidationForModuleKind::PCH; + llvm::outs() << "file: " << filepath << "\n"; - // key point - PreprocessorOpts.ImplicitPCHInclude = filepath; - - PreprocessorOpts.UsePredefines = false; -} - -void Compiler::buildPCM(llvm::StringRef filename, llvm::StringRef content, llvm::ArrayRef args) { - auto invocation = createInvocation(filename, content, args); - - auto instance = createInstance(std::move(invocation)); - - auto& frontend = instance->getFrontendOpts(); - frontend.OutputFile = "/home/ykiko/C++/clice2/build/cache/xxx.pcm"; - - auto action = clang::GenerateReducedModuleInterfaceAction(); - - if(!action.BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) { + if(!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) { llvm::errs() << "Failed to begin source file\n"; std::terminate(); } - if(auto error = action.Execute()) { + if(auto error = action->Execute()) { llvm::errs() << "Failed to execute action: " << error << "\n"; std::terminate(); } +} - action.EndSourceFile(); +void Compiler::codeCompletion(llvm::StringRef filepath, + std::uint32_t line, + std::uint32_t column, + clang::CodeCompleteConsumer* consumer) { + instance->setCodeCompletionConsumer(consumer); + + auto& codeComplete = instance->getFrontendOpts().CodeCompletionAt; + codeComplete.FileName = filepath; + codeComplete.Line = line; + codeComplete.Column = column; + + buildAST(); +} + +void Compiler::buildAST() { + action = std::make_unique(); + + auto buffer = llvm::MemoryBuffer::getMemBufferCopy(content); + instance->getPreprocessorOpts().addRemappedFile(filepath, buffer.release()); + + if(!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) { + llvm::errs() << "Failed to begin source file\n"; + std::terminate(); + } + + if(auto error = action->Execute()) { + llvm::errs() << "Failed to execute action: " << error << "\n"; + std::terminate(); + } } } // namespace clice diff --git a/tests/AST/Compiler.cpp b/tests/AST/Compiler.cpp index ceaab01d..4d8cd9d5 100644 --- a/tests/AST/Compiler.cpp +++ b/tests/AST/Compiler.cpp @@ -48,25 +48,18 @@ int main(){ "/home/ykiko/C++/clice2/build/lib/clang/20", }; - Compiler compiler; - compiler.buildPCH("main.cpp", code, compileArgs); - - auto invocation = createInvocation("main.cpp", code, compileArgs); - compiler.applyPCH(*invocation, "main.cpp", code, "/home/ykiko/C++/clice2/build/cache/xxx.pch"); - auto instance = createInstance(std::move(invocation)); - auto action = std::make_unique(); - - if(!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) { - llvm::errs() << "Failed to begin source file\n"; - std::terminate(); + auto bounds = clang::Lexer::ComputePreamble(code, {}, false); + { + Compiler compiler("main.cpp", code, compileArgs); + compiler.generatePCH("/home/ykiko/C++/clice2/build/cache/xxx.pch", + bounds.Size, + bounds.PreambleEndsAtStartOfLine); } - if(auto error = action->Execute()) { - llvm::errs() << "Failed to execute action: " << error << "\n"; - std::terminate(); - } - - instance->getASTContext().getTranslationUnitDecl()->dump(); + Compiler compiler("main.cpp", code, compileArgs); + compiler.applyPCH("/home/ykiko/C++/clice2/build/cache/xxx.pch", bounds.Size, bounds.PreambleEndsAtStartOfLine); + compiler.buildAST(); + compiler.tu()->dump(); } TEST(clice, PCM) { @@ -79,8 +72,14 @@ export constexpr int f() { )"; const char* code = R"( +module; + +export module Main; + import M; +module: private; + int main() { constexpr int x = f(); return x; @@ -95,26 +94,34 @@ int main() { "/home/ykiko/C++/clice2/build/lib/clang/20", }; - Compiler compiler; - compiler.buildPCM("main.cpp", mod, compileArgs); - - auto invocation = createInvocation("main.cpp", code, compileArgs); - // compiler.applyPCH(*invocation, "main.cpp", code, "/home/ykiko/C++/clice2/build/cache/xxx.pch"); - invocation->getHeaderSearchOpts().PrebuiltModuleFiles.emplace("M", "/home/ykiko/C++/clice2/build/cache/xxx.pcm"); - auto instance = createInstance(std::move(invocation)); - auto action = std::make_unique(); - - if(!action->BeginSourceFile(*instance, instance->getFrontendOpts().Inputs[0])) { - llvm::errs() << "Failed to begin source file\n"; - std::terminate(); + { + Compiler compiler("main.cpp", mod, compileArgs); + compiler.generatePCM("/home/ykiko/C++/clice2/build/cache/M.pcm"); } - if(auto error = action->Execute()) { - llvm::errs() << "Failed to execute action: " << error << "\n"; - std::terminate(); - } + static Compiler compiler("main.cpp", code, compileArgs); + compiler.applyPCM("/home/ykiko/C++/clice2/build/cache/M.pcm", "M"); + compiler.buildAST(); + compiler.tu()->dump(); - instance->getASTContext().getTranslationUnitDecl()->dump(); + class ModuleVisitor : public clang::RecursiveASTVisitor { + public: + bool VisitImportDecl(clang::ImportDecl* decl) { + for(auto loc: decl->getIdentifierLocs()) { + loc.dump(compiler.srcMgr()); + } + auto mod = decl->getImportedModule(); + mod->DefinitionLoc.dump(compiler.srcMgr()); + // llvm::outs() << "Module: " << decl->getImportedModule()->getFullModuleName() << "\n"; + + return true; + } + }; + + ModuleVisitor visitor; + visitor.TraverseAST(compiler.context()); + + // instance->getASTContext().getTranslationUnitDecl()->dump(); } } // namespace