1 //===- unittests/Frontend/CompilerInvocationTest.cpp - CI tests //---------===//
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/Frontend/CompilerInvocation.h"
10 #include "clang/Frontend/CompilerInstance.h"
11 #include "clang/Frontend/TextDiagnosticBuffer.h"
12 #include "llvm/Support/Host.h"
13 
14 #include "gmock/gmock.h"
15 #include "gtest/gtest.h"
16 
17 using namespace llvm;
18 using namespace clang;
19 
20 using ::testing::Contains;
21 using ::testing::StrEq;
22 
23 namespace {
24 class CommandLineTest : public ::testing::Test {
25 public:
26   IntrusiveRefCntPtr<DiagnosticsEngine> Diags;
27   SmallVector<const char *, 32> GeneratedArgs;
28   SmallVector<std::string, 32> GeneratedArgsStorage;
29   CompilerInvocation Invocation;
30 
operator ()(const Twine & Arg)31   const char *operator()(const Twine &Arg) {
32     return GeneratedArgsStorage.emplace_back(Arg.str()).c_str();
33   }
34 
CommandLineTest()35   CommandLineTest()
36       : Diags(CompilerInstance::createDiagnostics(new DiagnosticOptions(),
37                                                   new TextDiagnosticBuffer())) {
38   }
39 };
40 
41 // Boolean option with a keypath that defaults to true.
42 // The only flag with a negative spelling can set the keypath to false.
43 
TEST_F(CommandLineTest,BoolOptionDefaultTrueSingleFlagNotPresent)44 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagNotPresent) {
45   const char *Args[] = {""};
46 
47   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
48 
49   ASSERT_FALSE(Diags->hasErrorOccurred());
50   ASSERT_TRUE(Invocation.getFrontendOpts().UseTemporary);
51 
52   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
53 
54   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-temp-file"))));
55 }
56 
TEST_F(CommandLineTest,BoolOptionDefaultTrueSingleFlagPresent)57 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagPresent) {
58   const char *Args[] = {"-fno-temp-file"};
59 
60   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
61 
62   ASSERT_FALSE(Diags->hasErrorOccurred());
63   ASSERT_FALSE(Invocation.getFrontendOpts().UseTemporary);
64 
65   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
66 
67   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-temp-file")));
68 }
69 
TEST_F(CommandLineTest,BoolOptionDefaultTrueSingleFlagUnknownPresent)70 TEST_F(CommandLineTest, BoolOptionDefaultTrueSingleFlagUnknownPresent) {
71   const char *Args[] = {"-ftemp-file"};
72 
73   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
74 
75   // Driver-only flag.
76   ASSERT_TRUE(Diags->hasErrorOccurred());
77   ASSERT_TRUE(Invocation.getFrontendOpts().UseTemporary);
78 }
79 
TEST_F(CommandLineTest,CanGenerateCC1CommandLineFlag)80 TEST_F(CommandLineTest, CanGenerateCC1CommandLineFlag) {
81   const char *Args[] = {"-fmodules-strict-context-hash"};
82 
83   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
84 
85   ASSERT_FALSE(Diags->hasErrorOccurred());
86 
87   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
88 
89   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fmodules-strict-context-hash")));
90 }
91 
TEST_F(CommandLineTest,CanGenerateCC1CommandLineSeparate)92 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparate) {
93   const char *TripleCStr = "i686-apple-darwin9";
94   const char *Args[] = {"-triple", TripleCStr};
95 
96   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
97 
98   ASSERT_FALSE(Diags->hasErrorOccurred());
99 
100   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
101 
102   ASSERT_THAT(GeneratedArgs, Contains(StrEq(TripleCStr)));
103 }
104 
TEST_F(CommandLineTest,CanGenerateCC1CommandLineSeparateRequiredPresent)105 TEST_F(CommandLineTest,  CanGenerateCC1CommandLineSeparateRequiredPresent) {
106   const std::string DefaultTriple =
107       llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
108   const char *Args[] = {"-triple", DefaultTriple.c_str()};
109 
110   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
111 
112   ASSERT_FALSE(Diags->hasErrorOccurred());
113 
114   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
115 
116   // Triple should always be emitted even if it is the default
117   ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str())));
118 }
119 
TEST_F(CommandLineTest,CanGenerateCC1CommandLineSeparateRequiredAbsent)120 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateRequiredAbsent) {
121   const std::string DefaultTriple =
122       llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
123   const char *Args[] = {""};
124 
125   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
126 
127   ASSERT_FALSE(Diags->hasErrorOccurred());
128 
129   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
130 
131   // Triple should always be emitted even if it is the default
132   ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str())));
133 }
134 
TEST_F(CommandLineTest,CanGenerateCC1CommandLineSeparateEnumNonDefault)135 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateEnumNonDefault) {
136   const char *Args[] = {"-mrelocation-model", "static"};
137 
138   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
139 
140   ASSERT_FALSE(Diags->hasErrorOccurred());
141 
142   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
143 
144   // Non default relocation model.
145   ASSERT_THAT(GeneratedArgs, Contains(StrEq("static")));
146 }
147 
TEST_F(CommandLineTest,CanGenerateCC1COmmandLineSeparateEnumDefault)148 TEST_F(CommandLineTest, CanGenerateCC1COmmandLineSeparateEnumDefault) {
149   const char *Args[] = {"-mrelocation-model", "pic"};
150 
151   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
152 
153   ASSERT_FALSE(Diags->hasErrorOccurred());
154 
155   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
156 
157   // Default relocation model.
158   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("pic"))));
159 }
160 
161 // Tree of boolean options that can be (directly or transitively) implied by
162 // their parent:
163 //
164 //   * -cl-unsafe-math-optimizations
165 //     * -cl-mad-enable
166 //     * -menable-unsafe-fp-math
167 //       * -freciprocal-math
168 
TEST_F(CommandLineTest,ImpliedBoolOptionsNoFlagPresent)169 TEST_F(CommandLineTest, ImpliedBoolOptionsNoFlagPresent) {
170   const char *Args[] = {""};
171 
172   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
173 
174   ASSERT_FALSE(Diags->hasErrorOccurred());
175   ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath);
176   ASSERT_FALSE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
177   ASSERT_FALSE(Invocation.getLangOpts()->UnsafeFPMath);
178   ASSERT_FALSE(Invocation.getLangOpts()->AllowRecip);
179 
180   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
181 
182   // Not generated - missing.
183   ASSERT_THAT(GeneratedArgs,
184               Not(Contains(StrEq("-cl-unsafe-math-optimizations"))));
185   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
186   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
187   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
188 }
189 
TEST_F(CommandLineTest,ImpliedBoolOptionsRootFlagPresent)190 TEST_F(CommandLineTest, ImpliedBoolOptionsRootFlagPresent) {
191   const char *Args[] = {"-cl-unsafe-math-optimizations"};
192 
193   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
194 
195   ASSERT_FALSE(Diags->hasErrorOccurred());
196   // Explicitly provided root flag.
197   ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath);
198   // Directly implied by explicitly provided root flag.
199   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
200   ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath);
201   // Transitively implied by explicitly provided root flag.
202   ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip);
203 
204   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
205 
206   // Generated - explicitly provided.
207   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations")));
208   // Not generated - implied by the generated root flag.
209   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
210   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
211   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
212 }
213 
TEST_F(CommandLineTest,ImpliedBoolOptionsAllFlagsPresent)214 TEST_F(CommandLineTest, ImpliedBoolOptionsAllFlagsPresent) {
215   const char *Args[] = {"-cl-unsafe-math-optimizations", "-cl-mad-enable",
216                         "-menable-unsafe-fp-math", "-freciprocal-math"};
217 
218   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
219 
220   ASSERT_FALSE(Diags->hasErrorOccurred());
221   ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath);
222   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
223   ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath);
224   ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip);
225 
226   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
227 
228   // Generated - explicitly provided.
229   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations")));
230   // Not generated - implied by their generated parent.
231   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable"))));
232   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math"))));
233   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
234 }
235 
TEST_F(CommandLineTest,ImpliedBoolOptionsImpliedFlagsPresent)236 TEST_F(CommandLineTest, ImpliedBoolOptionsImpliedFlagsPresent) {
237   const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math",
238                         "-freciprocal-math"};
239 
240   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
241   ASSERT_FALSE(Diags->hasErrorOccurred());
242   ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath);
243   ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD);
244   ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath);
245   ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip);
246 
247   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
248   // Not generated - missing.
249   ASSERT_THAT(GeneratedArgs,
250               Not(Contains(StrEq("-cl-unsafe-math-optimizations"))));
251   // Generated - explicitly provided.
252   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable")));
253   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math")));
254   // Not generated - implied by its generated parent.
255   ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math"))));
256 }
257 
TEST_F(CommandLineTest,PresentAndNotImpliedGenerated)258 TEST_F(CommandLineTest, PresentAndNotImpliedGenerated) {
259   const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math"};
260 
261   CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags);
262 
263   ASSERT_FALSE(Diags->hasErrorOccurred());
264 
265   Invocation.generateCC1CommandLine(GeneratedArgs, *this);
266 
267   // Present options that were not implied are generated.
268   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable")));
269   ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math")));
270 }
271 } // anonymous namespace
272