1 //===- unittests/Frontend/FrontendActionTest.cpp FrontendAction 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 "flang/Frontend/CompilerInstance.h" 10 #include "flang/Frontend/CompilerInvocation.h" 11 #include "flang/Frontend/FrontendOptions.h" 12 #include "flang/FrontendTool/Utils.h" 13 #include "llvm/ADT/Triple.h" 14 #include "llvm/Support/FileSystem.h" 15 #include "llvm/Support/Host.h" 16 #include "llvm/Support/TargetSelect.h" 17 #include "llvm/Support/raw_ostream.h" 18 19 #include "gtest/gtest.h" 20 21 using namespace Fortran::frontend; 22 23 namespace { 24 25 class FrontendActionTest : public ::testing::Test { 26 protected: 27 // AllSources (which is used to manage files inside every compiler 28 // instance), works with paths. So we need a filename and a path for the 29 // input file. 30 // TODO: We could use `-` for inputFilePath_, but then we'd need a way to 31 // write to stdin that's then read by AllSources. Ideally, AllSources should 32 // be capable of reading from any stream. 33 std::string inputFileName_; 34 std::string inputFilePath_; 35 // The output stream for the input file. Use this to populate the input. 36 std::unique_ptr<llvm::raw_fd_ostream> inputFileOs_; 37 38 std::error_code ec_; 39 40 CompilerInstance compInst_; 41 std::shared_ptr<CompilerInvocation> invocation_; 42 43 void SetUp() override { 44 // Generate a unique test file name. 45 const testing::TestInfo *const test_info = 46 testing::UnitTest::GetInstance()->current_test_info(); 47 inputFileName_ = std::string(test_info->name()) + "_test-file.f90"; 48 49 // Create the input file stream. Note that this stream is populated 50 // separately in every test (i.e. the input is test specific). 51 inputFileOs_ = std::make_unique<llvm::raw_fd_ostream>( 52 inputFileName_, ec_, llvm::sys::fs::OF_None); 53 if (ec_) 54 FAIL() << "Failed to create the input file"; 55 56 // Get the path of the input file. 57 llvm::SmallString<256> cwd; 58 if (std::error_code ec_ = llvm::sys::fs::current_path(cwd)) 59 FAIL() << "Failed to obtain the current working directory"; 60 inputFilePath_ = cwd.c_str(); 61 inputFilePath_ += "/" + inputFileName_; 62 63 // Prepare the compiler (CompilerInvocation + CompilerInstance) 64 compInst_.CreateDiagnostics(); 65 invocation_ = std::make_shared<CompilerInvocation>(); 66 67 compInst_.set_invocation(std::move(invocation_)); 68 compInst_.frontendOpts().inputs.push_back( 69 FrontendInputFile(inputFilePath_, Language::Fortran)); 70 } 71 72 void TearDown() override { 73 // Clear the input file. 74 llvm::sys::fs::remove(inputFileName_); 75 76 // Clear the output files. 77 // Note that these tests use an output buffer (as opposed to an output 78 // file), hence there are no physical output files to delete and 79 // `EraseFiles` is set to `false`. Also, some actions (e.g. 80 // `ParseSyntaxOnly`) don't generated output. In such cases there's no 81 // output to clear and `ClearOutputFile` returns immediately. 82 compInst_.ClearOutputFiles(/*EraseFiles=*/false); 83 } 84 }; 85 86 TEST_F(FrontendActionTest, TestInputOutput) { 87 // Populate the input file with the pre-defined input and flush it. 88 *(inputFileOs_) << "End Program arithmetic"; 89 inputFileOs_.reset(); 90 91 // Set-up the action kind. 92 compInst_.invocation().frontendOpts().programAction = InputOutputTest; 93 94 // Set-up the output stream. Using output buffer wrapped as an output 95 // stream, as opposed to an actual file (or a file descriptor). 96 llvm::SmallVector<char, 256> outputFileBuffer; 97 std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream( 98 new llvm::raw_svector_ostream(outputFileBuffer)); 99 compInst_.set_outputStream(std::move(outputFileStream)); 100 101 // Execute the action. 102 bool success = ExecuteCompilerInvocation(&compInst_); 103 104 // Validate the expected output. 105 EXPECT_TRUE(success); 106 EXPECT_TRUE(!outputFileBuffer.empty()); 107 EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data()) 108 .startswith("End Program arithmetic")); 109 } 110 111 TEST_F(FrontendActionTest, PrintPreprocessedInput) { 112 // Populate the input file with the pre-defined input and flush it. 113 *(inputFileOs_) << "#ifdef NEW\n" 114 << " Program A \n" 115 << "#else\n" 116 << " Program B\n" 117 << "#endif"; 118 inputFileOs_.reset(); 119 120 // Set-up the action kind. 121 compInst_.invocation().frontendOpts().programAction = PrintPreprocessedInput; 122 compInst_.invocation().preprocessorOpts().noReformat = true; 123 124 // Set-up the output stream. We are using output buffer wrapped as an output 125 // stream, as opposed to an actual file (or a file descriptor). 126 llvm::SmallVector<char, 256> outputFileBuffer; 127 std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream( 128 new llvm::raw_svector_ostream(outputFileBuffer)); 129 compInst_.set_outputStream(std::move(outputFileStream)); 130 131 // Execute the action. 132 bool success = ExecuteCompilerInvocation(&compInst_); 133 134 // Validate the expected output. 135 EXPECT_TRUE(success); 136 EXPECT_TRUE(!outputFileBuffer.empty()); 137 EXPECT_TRUE( 138 llvm::StringRef(outputFileBuffer.data()).startswith("program b\n")); 139 } 140 141 TEST_F(FrontendActionTest, ParseSyntaxOnly) { 142 // Populate the input file with the pre-defined input and flush it. 143 *(inputFileOs_) << "IF (A > 0.0) IF (B < 0.0) A = LOG (A)\n" 144 << "END"; 145 inputFileOs_.reset(); 146 147 // Set-up the action kind. 148 compInst_.invocation().frontendOpts().programAction = ParseSyntaxOnly; 149 150 // Set-up the output stream for the semantic diagnostics. 151 llvm::SmallVector<char, 256> outputDiagBuffer; 152 std::unique_ptr<llvm::raw_pwrite_stream> outputStream( 153 new llvm::raw_svector_ostream(outputDiagBuffer)); 154 compInst_.set_semaOutputStream(std::move(outputStream)); 155 156 // Execute the action. 157 bool success = ExecuteCompilerInvocation(&compInst_); 158 159 // Validate the expected output. 160 EXPECT_FALSE(success); 161 EXPECT_TRUE(!outputDiagBuffer.empty()); 162 EXPECT_TRUE( 163 llvm::StringRef(outputDiagBuffer.data()) 164 .contains( 165 ":1:14: error: IF statement is not allowed in IF statement\n")); 166 } 167 168 TEST_F(FrontendActionTest, EmitLLVM) { 169 // Populate the input file with the pre-defined input and flush it. 170 *(inputFileOs_) << "end program"; 171 inputFileOs_.reset(); 172 173 // Set-up the action kind. 174 compInst_.invocation().frontendOpts().programAction = EmitLLVM; 175 176 // Set-up default target triple. 177 compInst_.invocation().targetOpts().triple = 178 llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()); 179 180 // Set-up the output stream. We are using output buffer wrapped as an output 181 // stream, as opposed to an actual file (or a file descriptor). 182 llvm::SmallVector<char> outputFileBuffer; 183 std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream( 184 new llvm::raw_svector_ostream(outputFileBuffer)); 185 compInst_.set_outputStream(std::move(outputFileStream)); 186 187 // Execute the action. 188 bool success = ExecuteCompilerInvocation(&compInst_); 189 190 // Validate the expected output. 191 EXPECT_TRUE(success); 192 EXPECT_TRUE(!outputFileBuffer.empty()); 193 194 EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data()) 195 .contains("define void @_QQmain()")); 196 } 197 198 TEST_F(FrontendActionTest, EmitAsm) { 199 // Populate the input file with the pre-defined input and flush it. 200 *(inputFileOs_) << "end program"; 201 inputFileOs_.reset(); 202 203 // Set-up the action kind. 204 compInst_.invocation().frontendOpts().programAction = EmitAssembly; 205 206 // Set-up default target triple. 207 compInst_.invocation().targetOpts().triple = 208 llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()); 209 210 // Initialise LLVM backend 211 llvm::InitializeAllTargets(); 212 llvm::InitializeAllTargetMCs(); 213 llvm::InitializeAllAsmPrinters(); 214 215 // Set-up the output stream. We are using output buffer wrapped as an output 216 // stream, as opposed to an actual file (or a file descriptor). 217 llvm::SmallVector<char, 256> outputFileBuffer; 218 std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream( 219 new llvm::raw_svector_ostream(outputFileBuffer)); 220 compInst_.set_outputStream(std::move(outputFileStream)); 221 222 // Execute the action. 223 bool success = ExecuteCompilerInvocation(&compInst_); 224 225 // Validate the expected output. 226 EXPECT_TRUE(success); 227 EXPECT_TRUE(!outputFileBuffer.empty()); 228 229 EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data()).contains("_QQmain")); 230 } 231 } // namespace 232