| //=== unittests/CodeGen/IncrementalProcessingTest.cpp - IncrementalCodeGen ===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "clang/AST/ASTConsumer.h" |
| #include "clang/AST/ASTContext.h" |
| #include "clang/AST/RecursiveASTVisitor.h" |
| #include "clang/Basic/TargetInfo.h" |
| #include "clang/CodeGen/ModuleBuilder.h" |
| #include "clang/Frontend/CompilerInstance.h" |
| #include "clang/Lex/Preprocessor.h" |
| #include "clang/Parse/Parser.h" |
| #include "clang/Sema/Sema.h" |
| #include "llvm/ADT/Triple.h" |
| #include "llvm/IR/LLVMContext.h" |
| #include "llvm/IR/Module.h" |
| #include "llvm/Support/Host.h" |
| #include "llvm/Support/MemoryBuffer.h" |
| #include "gtest/gtest.h" |
| |
| #include <memory> |
| |
| using namespace llvm; |
| using namespace clang; |
| |
| namespace { |
| |
| // Incremental processing produces several modules, all using the same "main |
| // file". Make sure CodeGen can cope with that, e.g. for static initializers. |
| const char TestProgram1[] = |
| "extern \"C\" int funcForProg1() { return 17; }\n" |
| "struct EmitCXXGlobalInitFunc1 {\n" |
| " EmitCXXGlobalInitFunc1() {}\n" |
| "} test1;"; |
| |
| const char TestProgram2[] = |
| "extern \"C\" int funcForProg2() { return 42; }\n" |
| "struct EmitCXXGlobalInitFunc2 {\n" |
| " EmitCXXGlobalInitFunc2() {}\n" |
| "} test2;"; |
| |
| |
| /// An incremental version of ParseAST(). |
| static std::unique_ptr<llvm::Module> |
| IncrementalParseAST(CompilerInstance& CI, Parser& P, |
| CodeGenerator& CG, const char* code) { |
| static int counter = 0; |
| struct IncreaseCounterOnRet { |
| ~IncreaseCounterOnRet() { |
| ++counter; |
| } |
| } ICOR; |
| |
| Sema& S = CI.getSema(); |
| clang::SourceManager &SM = S.getSourceManager(); |
| if (!code) { |
| // Main file |
| SM.setMainFileID(SM.createFileID( |
| llvm::MemoryBuffer::getMemBuffer(" "), clang::SrcMgr::C_User)); |
| |
| S.getPreprocessor().EnterMainSourceFile(); |
| P.Initialize(); |
| } else { |
| FileID FID = SM.createFileID( |
| llvm::MemoryBuffer::getMemBuffer(code), clang::SrcMgr::C_User); |
| SourceLocation MainStartLoc = SM.getLocForStartOfFile(SM.getMainFileID()); |
| SourceLocation InclLoc = MainStartLoc.getLocWithOffset(counter); |
| S.getPreprocessor().EnterSourceFile(FID, 0, InclLoc); |
| } |
| |
| ExternalASTSource *External = S.getASTContext().getExternalSource(); |
| if (External) |
| External->StartTranslationUnit(&CG); |
| |
| Parser::DeclGroupPtrTy ADecl; |
| for (bool AtEOF = P.ParseFirstTopLevelDecl(ADecl); !AtEOF; |
| AtEOF = P.ParseTopLevelDecl(ADecl)) { |
| // If we got a null return and something *was* parsed, ignore it. This |
| // is due to a top-level semicolon, an action override, or a parse error |
| // skipping something. |
| if (ADecl && !CG.HandleTopLevelDecl(ADecl.get())) |
| return nullptr; |
| } |
| |
| // Process any TopLevelDecls generated by #pragma weak. |
| for (Decl *D : S.WeakTopLevelDecls()) |
| CG.HandleTopLevelDecl(DeclGroupRef(D)); |
| |
| CG.HandleTranslationUnit(S.getASTContext()); |
| |
| std::unique_ptr<llvm::Module> M(CG.ReleaseModule()); |
| // Switch to next module. |
| CG.StartModule("incremental-module-" + std::to_string(counter), |
| M->getContext()); |
| return M; |
| } |
| |
| const Function* getGlobalInit(llvm::Module& M) { |
| for (const auto& Func: M) |
| if (Func.hasName() && Func.getName().startswith("_GLOBAL__sub_I_")) |
| return &Func; |
| |
| return nullptr; |
| } |
| |
| TEST(IncrementalProcessing, EmitCXXGlobalInitFunc) { |
| LLVMContext Context; |
| CompilerInstance compiler; |
| |
| compiler.createDiagnostics(); |
| compiler.getLangOpts().CPlusPlus = 1; |
| compiler.getLangOpts().CPlusPlus11 = 1; |
| |
| compiler.getTargetOpts().Triple = llvm::Triple::normalize( |
| llvm::sys::getProcessTriple()); |
| compiler.setTarget(clang::TargetInfo::CreateTargetInfo( |
| compiler.getDiagnostics(), |
| std::make_shared<clang::TargetOptions>( |
| compiler.getTargetOpts()))); |
| |
| compiler.createFileManager(); |
| compiler.createSourceManager(compiler.getFileManager()); |
| compiler.createPreprocessor(clang::TU_Prefix); |
| compiler.getPreprocessor().enableIncrementalProcessing(); |
| |
| compiler.createASTContext(); |
| |
| CodeGenerator* CG = |
| CreateLLVMCodeGen( |
| compiler.getDiagnostics(), |
| "main-module", |
| compiler.getHeaderSearchOpts(), |
| compiler.getPreprocessorOpts(), |
| compiler.getCodeGenOpts(), |
| Context); |
| compiler.setASTConsumer(std::unique_ptr<ASTConsumer>(CG)); |
| compiler.createSema(clang::TU_Prefix, nullptr); |
| Sema& S = compiler.getSema(); |
| |
| std::unique_ptr<Parser> ParseOP(new Parser(S.getPreprocessor(), S, |
| /*SkipFunctionBodies*/ false)); |
| Parser &P = *ParseOP.get(); |
| |
| std::array<std::unique_ptr<llvm::Module>, 3> M; |
| M[0] = IncrementalParseAST(compiler, P, *CG, nullptr); |
| ASSERT_TRUE(M[0]); |
| |
| M[1] = IncrementalParseAST(compiler, P, *CG, TestProgram1); |
| ASSERT_TRUE(M[1]); |
| ASSERT_TRUE(M[1]->getFunction("funcForProg1")); |
| |
| M[2] = IncrementalParseAST(compiler, P, *CG, TestProgram2); |
| ASSERT_TRUE(M[2]); |
| ASSERT_TRUE(M[2]->getFunction("funcForProg2")); |
| // First code should not end up in second module: |
| ASSERT_FALSE(M[2]->getFunction("funcForProg1")); |
| |
| // Make sure global inits exist and are unique: |
| const Function* GlobalInit1 = getGlobalInit(*M[1]); |
| ASSERT_TRUE(GlobalInit1); |
| |
| const Function* GlobalInit2 = getGlobalInit(*M[2]); |
| ASSERT_TRUE(GlobalInit2); |
| |
| ASSERT_FALSE(GlobalInit1->getName() == GlobalInit2->getName()); |
| |
| } |
| |
| } // end anonymous namespace |