1 //=== unittests/CodeGen/IncrementalProcessingTest.cpp - IncrementalCodeGen ===//
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
7 //===----------------------------------------------------------------------===//
9 #include "TestCompiler.h"
11 #include "clang/AST/ASTConsumer.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/AST/RecursiveASTVisitor.h"
14 #include "clang/Basic/TargetInfo.h"
15 #include "clang/CodeGen/ModuleBuilder.h"
16 #include "clang/Frontend/CompilerInstance.h"
17 #include "clang/Lex/Preprocessor.h"
18 #include "clang/Parse/Parser.h"
19 #include "clang/Sema/Sema.h"
20 #include "llvm/ADT/Triple.h"
21 #include "llvm/IR/LLVMContext.h"
22 #include "llvm/IR/Module.h"
23 #include "llvm/Support/Host.h"
24 #include "llvm/Support/MemoryBuffer.h"
25 #include "gtest/gtest.h"
30 using namespace clang
;
34 // Incremental processing produces several modules, all using the same "main
35 // file". Make sure CodeGen can cope with that, e.g. for static initializers.
36 const char TestProgram1
[] =
37 "extern \"C\" int funcForProg1() { return 17; }\n"
38 "struct EmitCXXGlobalInitFunc1 {\n"
39 " EmitCXXGlobalInitFunc1() {}\n"
42 const char TestProgram2
[] =
43 "extern \"C\" int funcForProg2() { return 42; }\n"
44 "struct EmitCXXGlobalInitFunc2 {\n"
45 " EmitCXXGlobalInitFunc2() {}\n"
49 /// An incremental version of ParseAST().
50 static std::unique_ptr
<llvm::Module
>
51 IncrementalParseAST(CompilerInstance
& CI
, Parser
& P
,
52 CodeGenerator
& CG
, const char* code
) {
53 static int counter
= 0;
54 struct IncreaseCounterOnRet
{
55 ~IncreaseCounterOnRet() {
60 Sema
& S
= CI
.getSema();
61 clang::SourceManager
&SM
= S
.getSourceManager();
64 SM
.setMainFileID(SM
.createFileID(
65 llvm::MemoryBuffer::getMemBuffer(" "), clang::SrcMgr::C_User
));
67 S
.getPreprocessor().EnterMainSourceFile();
70 FileID FID
= SM
.createFileID(
71 llvm::MemoryBuffer::getMemBuffer(code
), clang::SrcMgr::C_User
);
72 SourceLocation MainStartLoc
= SM
.getLocForStartOfFile(SM
.getMainFileID());
73 SourceLocation InclLoc
= MainStartLoc
.getLocWithOffset(counter
);
74 S
.getPreprocessor().EnterSourceFile(FID
, 0, InclLoc
);
77 ExternalASTSource
*External
= S
.getASTContext().getExternalSource();
79 External
->StartTranslationUnit(&CG
);
81 Parser::DeclGroupPtrTy ADecl
;
82 for (bool AtEOF
= P
.ParseFirstTopLevelDecl(ADecl
); !AtEOF
;
83 AtEOF
= P
.ParseTopLevelDecl(ADecl
)) {
84 // If we got a null return and something *was* parsed, ignore it. This
85 // is due to a top-level semicolon, an action override, or a parse error
86 // skipping something.
87 if (ADecl
&& !CG
.HandleTopLevelDecl(ADecl
.get()))
91 // Process any TopLevelDecls generated by #pragma weak.
92 for (Decl
*D
: S
.WeakTopLevelDecls())
93 CG
.HandleTopLevelDecl(DeclGroupRef(D
));
95 CG
.HandleTranslationUnit(S
.getASTContext());
97 std::unique_ptr
<llvm::Module
> M(CG
.ReleaseModule());
98 // Switch to next module.
99 CG
.StartModule("incremental-module-" + std::to_string(counter
),
104 const Function
* getGlobalInit(llvm::Module
& M
) {
105 for (const auto& Func
: M
)
106 if (Func
.hasName() && Func
.getName().startswith("_GLOBAL__sub_I_"))
112 TEST(IncrementalProcessing
, EmitCXXGlobalInitFunc
) {
113 clang::LangOptions LO
;
116 TestCompiler
Compiler(LO
);
117 clang::CompilerInstance
&CI
= Compiler
.compiler
;
118 CI
.getPreprocessor().enableIncrementalProcessing();
119 CI
.setASTConsumer(std::move(Compiler
.CG
));
120 clang::CodeGenerator
& CG
=
121 static_cast<clang::CodeGenerator
&>(CI
.getASTConsumer());
122 CI
.createSema(clang::TU_Prefix
, nullptr);
124 Sema
& S
= CI
.getSema();
126 std::unique_ptr
<Parser
> ParseOP(new Parser(S
.getPreprocessor(), S
,
127 /*SkipFunctionBodies*/ false));
128 Parser
&P
= *ParseOP
.get();
130 std::array
<std::unique_ptr
<llvm::Module
>, 3> M
;
131 M
[0] = IncrementalParseAST(CI
, P
, CG
, nullptr);
134 M
[1] = IncrementalParseAST(CI
, P
, CG
, TestProgram1
);
136 ASSERT_TRUE(M
[1]->getFunction("funcForProg1"));
138 M
[2] = IncrementalParseAST(CI
, P
, CG
, TestProgram2
);
140 ASSERT_TRUE(M
[2]->getFunction("funcForProg2"));
141 // First code should not end up in second module:
142 ASSERT_FALSE(M
[2]->getFunction("funcForProg1"));
144 // Make sure global inits exist and are unique:
145 const Function
* GlobalInit1
= getGlobalInit(*M
[1]);
146 ASSERT_TRUE(GlobalInit1
);
148 const Function
* GlobalInit2
= getGlobalInit(*M
[2]);
149 ASSERT_TRUE(GlobalInit2
);
151 ASSERT_FALSE(GlobalInit1
->getName() == GlobalInit2
->getName());
155 } // end anonymous namespace