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