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 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 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 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 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 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 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 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 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 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 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 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