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