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 31 const char *operator()(const Twine &Arg) { 32 return GeneratedArgsStorage.emplace_back(Arg.str()).c_str(); 33 } 34 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 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 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 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 80 // Boolean option with a keypath that defaults to true. 81 // The flag with negative spelling can set the keypath to false. 82 // The flag with positive spelling can reset the keypath to true. 83 84 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentNone) { 85 const char *Args[] = {""}; 86 87 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 88 ASSERT_FALSE(Diags->hasErrorOccurred()); 89 ASSERT_TRUE(Invocation.getCodeGenOpts().Autolink); 90 91 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 92 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fautolink")))); 93 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fno-autolink")))); 94 } 95 96 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentNegChange) { 97 const char *Args[] = {"-fno-autolink"}; 98 99 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 100 ASSERT_FALSE(Diags->hasErrorOccurred()); 101 ASSERT_FALSE(Invocation.getCodeGenOpts().Autolink); 102 103 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 104 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fno-autolink"))); 105 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-fautolink")))); 106 } 107 108 TEST_F(CommandLineTest, BoolOptionDefaultTruePresentPosReset) { 109 const char *Args[] = {"-fautolink"}; 110 111 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 112 ASSERT_TRUE(Diags->hasErrorOccurred()); // Driver-only flag. 113 ASSERT_TRUE(Invocation.getCodeGenOpts().Autolink); 114 } 115 116 // Boolean option with a keypath that defaults to false. 117 // The flag with negative spelling can set the keypath to true. 118 // The flag with positive spelling can reset the keypath to false. 119 120 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNone) { 121 const char *Args[] = {""}; 122 123 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 124 ASSERT_FALSE(Diags->hasErrorOccurred()); 125 ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables); 126 127 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 128 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-ginline-line-tables")))); 129 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-inline-line-tables")))); 130 } 131 132 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegChange) { 133 const char *Args[] = {"-gno-inline-line-tables"}; 134 135 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 136 ASSERT_FALSE(Diags->hasErrorOccurred()); 137 ASSERT_TRUE(Invocation.getCodeGenOpts().NoInlineLineTables); 138 139 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 140 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-gno-inline-line-tables"))); 141 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-ginline-line-tables")))); 142 } 143 144 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosReset) { 145 const char *Args[] = {"-ginline-line-tables"}; 146 147 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 148 ASSERT_TRUE(Diags->hasErrorOccurred()); // Driver-only flag. 149 ASSERT_FALSE(Invocation.getCodeGenOpts().NoInlineLineTables); 150 } 151 152 // Boolean option with a keypath that defaults to false. 153 // The flag with positive spelling can set the keypath to true. 154 // The flag with negative spelling can reset the keypath to false. 155 156 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNoneX) { 157 const char *Args[] = {""}; 158 159 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 160 ASSERT_FALSE(Diags->hasErrorOccurred()); 161 ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash); 162 163 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 164 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gcodeview-ghash")))); 165 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-codeview-ghash")))); 166 } 167 168 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentPosChange) { 169 const char *Args[] = {"-gcodeview-ghash"}; 170 171 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 172 ASSERT_FALSE(Diags->hasErrorOccurred()); 173 ASSERT_TRUE(Invocation.getCodeGenOpts().CodeViewGHash); 174 175 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 176 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-gcodeview-ghash"))); 177 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-gno-codeview-ghash")))); 178 } 179 180 TEST_F(CommandLineTest, BoolOptionDefaultFalsePresentNegReset) { 181 const char *Args[] = {"-gno-codeview-ghash"}; 182 183 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 184 ASSERT_TRUE(Diags->hasErrorOccurred()); // Driver-only flag. 185 ASSERT_FALSE(Invocation.getCodeGenOpts().CodeViewGHash); 186 } 187 188 // Boolean option with a keypath that defaults to an arbitrary expression. 189 // The flag with positive spelling can set the keypath to true. 190 // The flag with negative spelling can set the keypath to false. 191 192 static constexpr unsigned PassManagerDefault = 193 !static_cast<unsigned>(LLVM_ENABLE_NEW_PASS_MANAGER); 194 195 static constexpr const char *PassManagerResetByFlag = 196 LLVM_ENABLE_NEW_PASS_MANAGER ? "-fno-legacy-pass-manager" 197 : "-flegacy-pass-manager"; 198 199 static constexpr const char *PassManagerChangedByFlag = 200 LLVM_ENABLE_NEW_PASS_MANAGER ? "-flegacy-pass-manager" 201 : "-fno-legacy-pass-manager"; 202 203 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentNone) { 204 const char *Args = {""}; 205 206 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 207 208 ASSERT_FALSE(Diags->hasErrorOccurred()); 209 ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, PassManagerDefault); 210 211 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 212 213 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag)))); 214 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerChangedByFlag)))); 215 } 216 217 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentChange) { 218 const char *Args[] = {PassManagerChangedByFlag}; 219 220 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 221 ASSERT_FALSE(Diags->hasErrorOccurred()); 222 ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, !PassManagerDefault); 223 224 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 225 ASSERT_THAT(GeneratedArgs, Contains(StrEq(PassManagerChangedByFlag))); 226 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag)))); 227 } 228 229 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentReset) { 230 const char *Args[] = {PassManagerResetByFlag}; 231 232 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 233 ASSERT_FALSE(Diags->hasErrorOccurred()); 234 ASSERT_EQ(Invocation.getCodeGenOpts().LegacyPassManager, PassManagerDefault); 235 236 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 237 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerResetByFlag)))); 238 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq(PassManagerChangedByFlag)))); 239 } 240 241 TEST_F(CommandLineTest, CanGenerateCC1CommandLineFlag) { 242 const char *Args[] = {"-fmodules-strict-context-hash"}; 243 244 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 245 246 ASSERT_FALSE(Diags->hasErrorOccurred()); 247 248 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 249 250 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fmodules-strict-context-hash"))); 251 } 252 253 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparate) { 254 const char *TripleCStr = "i686-apple-darwin9"; 255 const char *Args[] = {"-triple", TripleCStr}; 256 257 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 258 259 ASSERT_FALSE(Diags->hasErrorOccurred()); 260 261 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 262 263 ASSERT_THAT(GeneratedArgs, Contains(StrEq(TripleCStr))); 264 } 265 266 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateRequiredPresent) { 267 const std::string DefaultTriple = 268 llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()); 269 const char *Args[] = {"-triple", DefaultTriple.c_str()}; 270 271 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 272 273 ASSERT_FALSE(Diags->hasErrorOccurred()); 274 275 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 276 277 // Triple should always be emitted even if it is the default 278 ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str()))); 279 } 280 281 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateRequiredAbsent) { 282 const std::string DefaultTriple = 283 llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()); 284 const char *Args[] = {""}; 285 286 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 287 288 ASSERT_FALSE(Diags->hasErrorOccurred()); 289 290 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 291 292 // Triple should always be emitted even if it is the default 293 ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str()))); 294 } 295 296 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateEnumNonDefault) { 297 const char *Args[] = {"-mrelocation-model", "static"}; 298 299 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 300 301 ASSERT_FALSE(Diags->hasErrorOccurred()); 302 303 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 304 305 // Non default relocation model. 306 ASSERT_THAT(GeneratedArgs, Contains(StrEq("static"))); 307 } 308 309 TEST_F(CommandLineTest, CanGenerateCC1COmmandLineSeparateEnumDefault) { 310 const char *Args[] = {"-mrelocation-model", "pic"}; 311 312 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 313 314 ASSERT_FALSE(Diags->hasErrorOccurred()); 315 316 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 317 318 // Default relocation model. 319 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("pic")))); 320 } 321 322 // Tree of boolean options that can be (directly or transitively) implied by 323 // their parent: 324 // 325 // * -cl-unsafe-math-optimizations 326 // * -cl-mad-enable 327 // * -menable-unsafe-fp-math 328 // * -freciprocal-math 329 330 TEST_F(CommandLineTest, ImpliedBoolOptionsNoFlagPresent) { 331 const char *Args[] = {""}; 332 333 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 334 335 ASSERT_FALSE(Diags->hasErrorOccurred()); 336 ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath); 337 ASSERT_FALSE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 338 ASSERT_FALSE(Invocation.getLangOpts()->UnsafeFPMath); 339 ASSERT_FALSE(Invocation.getLangOpts()->AllowRecip); 340 341 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 342 343 // Not generated - missing. 344 ASSERT_THAT(GeneratedArgs, 345 Not(Contains(StrEq("-cl-unsafe-math-optimizations")))); 346 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); 347 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math")))); 348 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 349 } 350 351 TEST_F(CommandLineTest, ImpliedBoolOptionsRootFlagPresent) { 352 const char *Args[] = {"-cl-unsafe-math-optimizations"}; 353 354 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 355 356 ASSERT_FALSE(Diags->hasErrorOccurred()); 357 // Explicitly provided root flag. 358 ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath); 359 // Directly implied by explicitly provided root flag. 360 ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 361 ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath); 362 // Transitively implied by explicitly provided root flag. 363 ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip); 364 365 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 366 367 // Generated - explicitly provided. 368 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations"))); 369 // Not generated - implied by the generated root flag. 370 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); 371 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math")))); 372 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 373 } 374 375 TEST_F(CommandLineTest, ImpliedBoolOptionsAllFlagsPresent) { 376 const char *Args[] = {"-cl-unsafe-math-optimizations", "-cl-mad-enable", 377 "-menable-unsafe-fp-math", "-freciprocal-math"}; 378 379 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 380 381 ASSERT_FALSE(Diags->hasErrorOccurred()); 382 ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath); 383 ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 384 ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath); 385 ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip); 386 387 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 388 389 // Generated - explicitly provided. 390 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations"))); 391 // Not generated - implied by their generated parent. 392 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); 393 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math")))); 394 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 395 } 396 397 TEST_F(CommandLineTest, ImpliedBoolOptionsImpliedFlagsPresent) { 398 const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math", 399 "-freciprocal-math"}; 400 401 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 402 ASSERT_FALSE(Diags->hasErrorOccurred()); 403 ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath); 404 ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 405 ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath); 406 ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip); 407 408 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 409 // Not generated - missing. 410 ASSERT_THAT(GeneratedArgs, 411 Not(Contains(StrEq("-cl-unsafe-math-optimizations")))); 412 // Generated - explicitly provided. 413 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable"))); 414 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math"))); 415 // Not generated - implied by its generated parent. 416 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 417 } 418 419 TEST_F(CommandLineTest, PresentAndNotImpliedGenerated) { 420 const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math"}; 421 422 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 423 424 ASSERT_FALSE(Diags->hasErrorOccurred()); 425 426 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 427 428 // Present options that were not implied are generated. 429 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable"))); 430 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math"))); 431 } 432 } // anonymous namespace 433