1 //=== unittests/CodeGen/IncrementalProcessingTest.cpp - IncrementalCodeGen ===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "clang/AST/ASTConsumer.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/RecursiveASTVisitor.h"
12 #include "clang/Basic/TargetInfo.h"
13 #include "clang/CodeGen/ModuleBuilder.h"
14 #include "clang/Frontend/CompilerInstance.h"
15 #include "clang/Lex/Preprocessor.h"
16 #include "clang/Parse/Parser.h"
17 #include "clang/Sema/Sema.h"
18 #include "llvm/ADT/Triple.h"
19 #include "llvm/IR/LLVMContext.h"
20 #include "llvm/IR/Module.h"
21 #include "llvm/Support/Host.h"
22 #include "llvm/Support/MemoryBuffer.h"
23 #include "gtest/gtest.h"
24
25 #include <memory>
26
27 using namespace llvm;
28 using namespace clang;
29
30 namespace {
31
32 // Incremental processing produces several modules, all using the same "main
33 // file". Make sure CodeGen can cope with that, e.g. for static initializers.
34 const char TestProgram1[] =
35 "extern \"C\" int funcForProg1() { return 17; }\n"
36 "struct EmitCXXGlobalInitFunc1 {\n"
37 " EmitCXXGlobalInitFunc1() {}\n"
38 "} test1;";
39
40 const char TestProgram2[] =
41 "extern \"C\" int funcForProg2() { return 42; }\n"
42 "struct EmitCXXGlobalInitFunc2 {\n"
43 " EmitCXXGlobalInitFunc2() {}\n"
44 "} test2;";
45
46
47 /// An incremental version of ParseAST().
48 static std::unique_ptr<llvm::Module>
IncrementalParseAST(CompilerInstance & CI,Parser & P,CodeGenerator & CG,const char * code)49 IncrementalParseAST(CompilerInstance& CI, Parser& P,
50 CodeGenerator& CG, const char* code) {
51 static int counter = 0;
52 struct IncreaseCounterOnRet {
53 ~IncreaseCounterOnRet() {
54 ++counter;
55 }
56 } ICOR;
57
58 Sema& S = CI.getSema();
59 clang::SourceManager &SM = S.getSourceManager();
60 if (!code) {
61 // Main file
62 SM.setMainFileID(SM.createFileID(
63 llvm::MemoryBuffer::getMemBuffer(" "), clang::SrcMgr::C_User));
64
65 S.getPreprocessor().EnterMainSourceFile();
66 P.Initialize();
67 } else {
68 FileID FID = SM.createFileID(
69 llvm::MemoryBuffer::getMemBuffer(code), clang::SrcMgr::C_User);
70 SourceLocation MainStartLoc = SM.getLocForStartOfFile(SM.getMainFileID());
71 SourceLocation InclLoc = MainStartLoc.getLocWithOffset(counter);
72 S.getPreprocessor().EnterSourceFile(FID, 0, InclLoc);
73 }
74
75 ExternalASTSource *External = S.getASTContext().getExternalSource();
76 if (External)
77 External->StartTranslationUnit(&CG);
78
79 Parser::DeclGroupPtrTy ADecl;
80 for (bool AtEOF = P.ParseFirstTopLevelDecl(ADecl); !AtEOF;
81 AtEOF = P.ParseTopLevelDecl(ADecl)) {
82 // If we got a null return and something *was* parsed, ignore it. This
83 // is due to a top-level semicolon, an action override, or a parse error
84 // skipping something.
85 if (ADecl && !CG.HandleTopLevelDecl(ADecl.get()))
86 return nullptr;
87 }
88
89 // Process any TopLevelDecls generated by #pragma weak.
90 for (Decl *D : S.WeakTopLevelDecls())
91 CG.HandleTopLevelDecl(DeclGroupRef(D));
92
93 CG.HandleTranslationUnit(S.getASTContext());
94
95 std::unique_ptr<llvm::Module> M(CG.ReleaseModule());
96 // Switch to next module.
97 CG.StartModule("incremental-module-" + std::to_string(counter),
98 M->getContext());
99 return M;
100 }
101
getGlobalInit(llvm::Module & M)102 const Function* getGlobalInit(llvm::Module& M) {
103 for (const auto& Func: M)
104 if (Func.hasName() && Func.getName().startswith("_GLOBAL__sub_I_"))
105 return &Func;
106
107 return nullptr;
108 }
109
TEST(IncrementalProcessing,EmitCXXGlobalInitFunc)110 TEST(IncrementalProcessing, EmitCXXGlobalInitFunc) {
111 LLVMContext Context;
112 CompilerInstance compiler;
113
114 compiler.createDiagnostics();
115 compiler.getLangOpts().CPlusPlus = 1;
116 compiler.getLangOpts().CPlusPlus11 = 1;
117
118 compiler.getTargetOpts().Triple = llvm::Triple::normalize(
119 llvm::sys::getProcessTriple());
120 compiler.setTarget(clang::TargetInfo::CreateTargetInfo(
121 compiler.getDiagnostics(),
122 std::make_shared<clang::TargetOptions>(
123 compiler.getTargetOpts())));
124
125 compiler.createFileManager();
126 compiler.createSourceManager(compiler.getFileManager());
127 compiler.createPreprocessor(clang::TU_Prefix);
128 compiler.getPreprocessor().enableIncrementalProcessing();
129
130 compiler.createASTContext();
131
132 CodeGenerator* CG =
133 CreateLLVMCodeGen(
134 compiler.getDiagnostics(),
135 "main-module",
136 compiler.getHeaderSearchOpts(),
137 compiler.getPreprocessorOpts(),
138 compiler.getCodeGenOpts(),
139 Context);
140 compiler.setASTConsumer(std::unique_ptr<ASTConsumer>(CG));
141 compiler.createSema(clang::TU_Prefix, nullptr);
142 Sema& S = compiler.getSema();
143
144 std::unique_ptr<Parser> ParseOP(new Parser(S.getPreprocessor(), S,
145 /*SkipFunctionBodies*/ false));
146 Parser &P = *ParseOP.get();
147
148 std::array<std::unique_ptr<llvm::Module>, 3> M;
149 M[0] = IncrementalParseAST(compiler, P, *CG, nullptr);
150 ASSERT_TRUE(M[0]);
151
152 M[1] = IncrementalParseAST(compiler, P, *CG, TestProgram1);
153 ASSERT_TRUE(M[1]);
154 ASSERT_TRUE(M[1]->getFunction("funcForProg1"));
155
156 M[2] = IncrementalParseAST(compiler, P, *CG, TestProgram2);
157 ASSERT_TRUE(M[2]);
158 ASSERT_TRUE(M[2]->getFunction("funcForProg2"));
159 // First code should not end up in second module:
160 ASSERT_FALSE(M[2]->getFunction("funcForProg1"));
161
162 // Make sure global inits exist and are unique:
163 const Function* GlobalInit1 = getGlobalInit(*M[1]);
164 ASSERT_TRUE(GlobalInit1);
165
166 const Function* GlobalInit2 = getGlobalInit(*M[2]);
167 ASSERT_TRUE(GlobalInit2);
168
169 ASSERT_FALSE(GlobalInit1->getName() == GlobalInit2->getName());
170
171 }
172
173 } // end anonymous namespace
174