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> invoc; 42 43 void SetUp() override { 44 // Generate a unique test file name. 45 const testing::TestInfo *const testInfo = 46 testing::UnitTest::GetInstance()->current_test_info(); 47 inputFileName = std::string(testInfo->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 invoc = std::make_shared<CompilerInvocation>(); 66 67 compInst.setInvocation(std::move(invoc)); 68 compInst.getFrontendOpts().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.getInvocation().getFrontendOpts().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.setOutputStream(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.getInvocation().getFrontendOpts().programAction = 122 PrintPreprocessedInput; 123 compInst.getInvocation().getPreprocessorOpts().noReformat = true; 124 125 // Set-up the output stream. We are using output buffer wrapped as an output 126 // stream, as opposed to an actual file (or a file descriptor). 127 llvm::SmallVector<char, 256> outputFileBuffer; 128 std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream( 129 new llvm::raw_svector_ostream(outputFileBuffer)); 130 compInst.setOutputStream(std::move(outputFileStream)); 131 132 // Execute the action. 133 bool success = executeCompilerInvocation(&compInst); 134 135 // Validate the expected output. 136 EXPECT_TRUE(success); 137 EXPECT_TRUE(!outputFileBuffer.empty()); 138 EXPECT_TRUE( 139 llvm::StringRef(outputFileBuffer.data()).startswith("program b\n")); 140 } 141 142 TEST_F(FrontendActionTest, ParseSyntaxOnly) { 143 // Populate the input file with the pre-defined input and flush it. 144 *(inputFileOs) << "IF (A > 0.0) IF (B < 0.0) A = LOG (A)\n" 145 << "END"; 146 inputFileOs.reset(); 147 148 // Set-up the action kind. 149 compInst.getInvocation().getFrontendOpts().programAction = ParseSyntaxOnly; 150 151 // Set-up the output stream for the semantic diagnostics. 152 llvm::SmallVector<char, 256> outputDiagBuffer; 153 std::unique_ptr<llvm::raw_pwrite_stream> outputStream( 154 new llvm::raw_svector_ostream(outputDiagBuffer)); 155 compInst.setSemaOutputStream(std::move(outputStream)); 156 157 // Execute the action. 158 bool success = executeCompilerInvocation(&compInst); 159 160 // Validate the expected output. 161 EXPECT_FALSE(success); 162 EXPECT_TRUE(!outputDiagBuffer.empty()); 163 EXPECT_TRUE( 164 llvm::StringRef(outputDiagBuffer.data()) 165 .contains( 166 ":1:14: error: IF statement is not allowed in IF statement\n")); 167 } 168 169 TEST_F(FrontendActionTest, EmitLLVM) { 170 // Populate the input file with the pre-defined input and flush it. 171 *(inputFileOs) << "end program"; 172 inputFileOs.reset(); 173 174 // Set-up the action kind. 175 compInst.getInvocation().getFrontendOpts().programAction = EmitLLVM; 176 177 // Set-up default target triple. 178 compInst.getInvocation().getTargetOpts().triple = 179 llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()); 180 181 // Set-up the output stream. We are using output buffer wrapped as an output 182 // stream, as opposed to an actual file (or a file descriptor). 183 llvm::SmallVector<char> outputFileBuffer; 184 std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream( 185 new llvm::raw_svector_ostream(outputFileBuffer)); 186 compInst.setOutputStream(std::move(outputFileStream)); 187 188 // Execute the action. 189 bool success = executeCompilerInvocation(&compInst); 190 191 // Validate the expected output. 192 EXPECT_TRUE(success); 193 EXPECT_TRUE(!outputFileBuffer.empty()); 194 195 EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data()) 196 .contains("define void @_QQmain()")); 197 } 198 199 TEST_F(FrontendActionTest, EmitAsm) { 200 // Populate the input file with the pre-defined input and flush it. 201 *(inputFileOs) << "end program"; 202 inputFileOs.reset(); 203 204 // Set-up the action kind. 205 compInst.getInvocation().getFrontendOpts().programAction = EmitAssembly; 206 207 // Set-up default target triple. 208 compInst.getInvocation().getTargetOpts().triple = 209 llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple()); 210 211 // Initialise LLVM backend 212 llvm::InitializeAllTargets(); 213 llvm::InitializeAllTargetMCs(); 214 llvm::InitializeAllAsmPrinters(); 215 216 // Set-up the output stream. We are using output buffer wrapped as an output 217 // stream, as opposed to an actual file (or a file descriptor). 218 llvm::SmallVector<char, 256> outputFileBuffer; 219 std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream( 220 new llvm::raw_svector_ostream(outputFileBuffer)); 221 compInst.setOutputStream(std::move(outputFileStream)); 222 223 // Execute the action. 224 bool success = executeCompilerInvocation(&compInst); 225 226 // Validate the expected output. 227 EXPECT_TRUE(success); 228 EXPECT_TRUE(!outputFileBuffer.empty()); 229 230 EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data()).contains("_QQmain")); 231 } 232 } // namespace 233