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 // NOTE: The following tests need to be updated when we start enabling the new 181 // pass manager by default. 182 183 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentNone) { 184 const char *Args = {""}; 185 186 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 187 188 ASSERT_FALSE(Diags->hasErrorOccurred()); 189 ASSERT_FALSE(Invocation.getCodeGenOpts().ExperimentalNewPassManager); 190 191 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 192 ASSERT_THAT(GeneratedArgs, 193 Not(Contains(StrEq("-fexperimental-new-pass-manager")))); 194 } 195 196 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentPos) { 197 const char *Args[] = {"-fexperimental-new-pass-manager"}; 198 199 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 200 ASSERT_FALSE(Diags->hasErrorOccurred()); 201 ASSERT_TRUE(Invocation.getCodeGenOpts().ExperimentalNewPassManager); 202 203 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 204 ASSERT_THAT(GeneratedArgs, 205 Contains(StrEq("-fexperimental-new-pass-manager"))); 206 ASSERT_THAT(GeneratedArgs, 207 Not(Contains(StrEq("-fno-experimental-new-pass-manager")))); 208 } 209 210 TEST_F(CommandLineTest, BoolOptionDefaultArbitraryTwoFlagsPresentNeg) { 211 const char *Args[] = {"-fno-experimental-new-pass-manager"}; 212 213 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 214 ASSERT_FALSE(Diags->hasErrorOccurred()); 215 ASSERT_FALSE(Invocation.getCodeGenOpts().ExperimentalNewPassManager); 216 217 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 218 ASSERT_THAT(GeneratedArgs, 219 Contains(StrEq("-fno-experimental-new-pass-manager"))); 220 ASSERT_THAT(GeneratedArgs, 221 Not(Contains(StrEq("-fexperimental-new-pass-manager")))); 222 } 223 224 TEST_F(CommandLineTest, CanGenerateCC1CommandLineFlag) { 225 const char *Args[] = {"-fmodules-strict-context-hash"}; 226 227 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 228 229 ASSERT_FALSE(Diags->hasErrorOccurred()); 230 231 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 232 233 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-fmodules-strict-context-hash"))); 234 } 235 236 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparate) { 237 const char *TripleCStr = "i686-apple-darwin9"; 238 const char *Args[] = {"-triple", TripleCStr}; 239 240 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 241 242 ASSERT_FALSE(Diags->hasErrorOccurred()); 243 244 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 245 246 ASSERT_THAT(GeneratedArgs, Contains(StrEq(TripleCStr))); 247 } 248 249 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateRequiredPresent) { 250 const std::string DefaultTriple = 251 llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()); 252 const char *Args[] = {"-triple", DefaultTriple.c_str()}; 253 254 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 255 256 ASSERT_FALSE(Diags->hasErrorOccurred()); 257 258 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 259 260 // Triple should always be emitted even if it is the default 261 ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str()))); 262 } 263 264 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateRequiredAbsent) { 265 const std::string DefaultTriple = 266 llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()); 267 const char *Args[] = {""}; 268 269 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 270 271 ASSERT_FALSE(Diags->hasErrorOccurred()); 272 273 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 274 275 // Triple should always be emitted even if it is the default 276 ASSERT_THAT(GeneratedArgs, Contains(StrEq(DefaultTriple.c_str()))); 277 } 278 279 TEST_F(CommandLineTest, CanGenerateCC1CommandLineSeparateEnumNonDefault) { 280 const char *Args[] = {"-mrelocation-model", "static"}; 281 282 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 283 284 ASSERT_FALSE(Diags->hasErrorOccurred()); 285 286 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 287 288 // Non default relocation model. 289 ASSERT_THAT(GeneratedArgs, Contains(StrEq("static"))); 290 } 291 292 TEST_F(CommandLineTest, CanGenerateCC1COmmandLineSeparateEnumDefault) { 293 const char *Args[] = {"-mrelocation-model", "pic"}; 294 295 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 296 297 ASSERT_FALSE(Diags->hasErrorOccurred()); 298 299 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 300 301 // Default relocation model. 302 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("pic")))); 303 } 304 305 // Tree of boolean options that can be (directly or transitively) implied by 306 // their parent: 307 // 308 // * -cl-unsafe-math-optimizations 309 // * -cl-mad-enable 310 // * -menable-unsafe-fp-math 311 // * -freciprocal-math 312 313 TEST_F(CommandLineTest, ImpliedBoolOptionsNoFlagPresent) { 314 const char *Args[] = {""}; 315 316 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 317 318 ASSERT_FALSE(Diags->hasErrorOccurred()); 319 ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath); 320 ASSERT_FALSE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 321 ASSERT_FALSE(Invocation.getLangOpts()->UnsafeFPMath); 322 ASSERT_FALSE(Invocation.getLangOpts()->AllowRecip); 323 324 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 325 326 // Not generated - missing. 327 ASSERT_THAT(GeneratedArgs, 328 Not(Contains(StrEq("-cl-unsafe-math-optimizations")))); 329 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); 330 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math")))); 331 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 332 } 333 334 TEST_F(CommandLineTest, ImpliedBoolOptionsRootFlagPresent) { 335 const char *Args[] = {"-cl-unsafe-math-optimizations"}; 336 337 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 338 339 ASSERT_FALSE(Diags->hasErrorOccurred()); 340 // Explicitly provided root flag. 341 ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath); 342 // Directly implied by explicitly provided root flag. 343 ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 344 ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath); 345 // Transitively implied by explicitly provided root flag. 346 ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip); 347 348 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 349 350 // Generated - explicitly provided. 351 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations"))); 352 // Not generated - implied by the generated root flag. 353 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); 354 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math")))); 355 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 356 } 357 358 TEST_F(CommandLineTest, ImpliedBoolOptionsAllFlagsPresent) { 359 const char *Args[] = {"-cl-unsafe-math-optimizations", "-cl-mad-enable", 360 "-menable-unsafe-fp-math", "-freciprocal-math"}; 361 362 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 363 364 ASSERT_FALSE(Diags->hasErrorOccurred()); 365 ASSERT_TRUE(Invocation.getLangOpts()->CLUnsafeMath); 366 ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 367 ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath); 368 ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip); 369 370 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 371 372 // Generated - explicitly provided. 373 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-unsafe-math-optimizations"))); 374 // Not generated - implied by their generated parent. 375 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-cl-mad-enable")))); 376 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-menable-unsafe-fp-math")))); 377 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 378 } 379 380 TEST_F(CommandLineTest, ImpliedBoolOptionsImpliedFlagsPresent) { 381 const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math", 382 "-freciprocal-math"}; 383 384 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 385 ASSERT_FALSE(Diags->hasErrorOccurred()); 386 ASSERT_FALSE(Invocation.getLangOpts()->CLUnsafeMath); 387 ASSERT_TRUE(Invocation.getCodeGenOpts().LessPreciseFPMAD); 388 ASSERT_TRUE(Invocation.getLangOpts()->UnsafeFPMath); 389 ASSERT_TRUE(Invocation.getLangOpts()->AllowRecip); 390 391 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 392 // Not generated - missing. 393 ASSERT_THAT(GeneratedArgs, 394 Not(Contains(StrEq("-cl-unsafe-math-optimizations")))); 395 // Generated - explicitly provided. 396 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable"))); 397 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math"))); 398 // Not generated - implied by its generated parent. 399 ASSERT_THAT(GeneratedArgs, Not(Contains(StrEq("-freciprocal-math")))); 400 } 401 402 TEST_F(CommandLineTest, PresentAndNotImpliedGenerated) { 403 const char *Args[] = {"-cl-mad-enable", "-menable-unsafe-fp-math"}; 404 405 CompilerInvocation::CreateFromArgs(Invocation, Args, *Diags); 406 407 ASSERT_FALSE(Diags->hasErrorOccurred()); 408 409 Invocation.generateCC1CommandLine(GeneratedArgs, *this); 410 411 // Present options that were not implied are generated. 412 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-cl-mad-enable"))); 413 ASSERT_THAT(GeneratedArgs, Contains(StrEq("-menable-unsafe-fp-math"))); 414 } 415 } // anonymous namespace 416