//=== 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 "TestCompiler.h" #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 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 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 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) { clang::LangOptions LO; LO.CPlusPlus = 1; LO.CPlusPlus11 = 1; TestCompiler Compiler(LO); clang::CompilerInstance &CI = Compiler.compiler; CI.getPreprocessor().enableIncrementalProcessing(); CI.setASTConsumer(std::move(Compiler.CG)); clang::CodeGenerator& CG = static_cast(CI.getASTConsumer()); CI.createSema(clang::TU_Prefix, nullptr); Sema& S = CI.getSema(); std::unique_ptr ParseOP(new Parser(S.getPreprocessor(), S, /*SkipFunctionBodies*/ false)); Parser &P = *ParseOP.get(); std::array, 3> M; M[0] = IncrementalParseAST(CI, P, CG, nullptr); ASSERT_TRUE(M[0]); M[1] = IncrementalParseAST(CI, P, CG, TestProgram1); ASSERT_TRUE(M[1]); ASSERT_TRUE(M[1]->getFunction("funcForProg1")); M[2] = IncrementalParseAST(CI, 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