This patch adds support for:
* `-S` in Flang's compiler and frontend drivers,
and implements:
* `-emit-obj` in Flang's frontend driver and `-c` in Flang's compiler
driver (this is consistent with Clang).
(these options were already available before, but only as placeholders).
The semantics of these options in Clang and Flang are identical.
The `EmitObjAction` frontend action is renamed as `BackendAction`. This
new name more accurately reflects the fact that this action will
primarily run the code-gen/backend pipeline in LLVM. It also makes more
sense as an action implementing both `-emit-obj` and `-S` (originally,
it was just `-emit-obj`).
`tripleName` from FirContext.cpp is deleted and, when a target triple is
required, `mlir::LLVM::LLVMDialect::getTargetTripleAttrName()` is used
instead. In practice, this means that `fir.triple` is replaced with
`llvm.target_triple`. The former was effectively ignored. The latter is
used when lowering from the LLVM dialect in MLIR to LLVM IR (i.e. it's
embedded in the generated LLVM IR module). The driver can then re-use
it when configuring the backend. With this change, the LLVM IR files
generated by e.g. `tco` will from now on contain the correct target
triple.
The code-gen.f90 test is replaced with code-gen-x86.f90 and
code-gen-aarch64.f90. With 2 seperate files we can verify that
`--target` is correctly taken into account. LIT configuration is updated
to enable e.g.:
```
! REQUIRES: aarch64-registered-target
```
Differential Revision: https://reviews.llvm.org/D120568
224 lines
8.1 KiB
C++
224 lines
8.1 KiB
C++
//===- unittests/Frontend/FrontendActionTest.cpp FrontendAction tests-----===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "flang/Frontend/CompilerInstance.h"
|
|
#include "flang/Frontend/CompilerInvocation.h"
|
|
#include "flang/Frontend/FrontendOptions.h"
|
|
#include "flang/FrontendTool/Utils.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/TargetSelect.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
|
|
#include "gtest/gtest.h"
|
|
|
|
using namespace Fortran::frontend;
|
|
|
|
namespace {
|
|
|
|
class FrontendActionTest : public ::testing::Test {
|
|
protected:
|
|
// AllSources (which is used to manage files inside every compiler
|
|
// instance), works with paths. So we need a filename and a path for the
|
|
// input file.
|
|
// TODO: We could use `-` for inputFilePath_, but then we'd need a way to
|
|
// write to stdin that's then read by AllSources. Ideally, AllSources should
|
|
// be capable of reading from any stream.
|
|
std::string inputFileName_;
|
|
std::string inputFilePath_;
|
|
// The output stream for the input file. Use this to populate the input.
|
|
std::unique_ptr<llvm::raw_fd_ostream> inputFileOs_;
|
|
|
|
std::error_code ec_;
|
|
|
|
CompilerInstance compInst_;
|
|
std::shared_ptr<CompilerInvocation> invocation_;
|
|
|
|
void SetUp() override {
|
|
// Generate a unique test file name.
|
|
const testing::TestInfo *const test_info =
|
|
testing::UnitTest::GetInstance()->current_test_info();
|
|
inputFileName_ = std::string(test_info->name()) + "_test-file.f90";
|
|
|
|
// Create the input file stream. Note that this stream is populated
|
|
// separately in every test (i.e. the input is test specific).
|
|
inputFileOs_ = std::make_unique<llvm::raw_fd_ostream>(
|
|
inputFileName_, ec_, llvm::sys::fs::OF_None);
|
|
if (ec_)
|
|
FAIL() << "Failed to create the input file";
|
|
|
|
// Get the path of the input file.
|
|
llvm::SmallString<256> cwd;
|
|
if (std::error_code ec_ = llvm::sys::fs::current_path(cwd))
|
|
FAIL() << "Failed to obtain the current working directory";
|
|
inputFilePath_ = cwd.c_str();
|
|
inputFilePath_ += "/" + inputFileName_;
|
|
|
|
// Prepare the compiler (CompilerInvocation + CompilerInstance)
|
|
compInst_.CreateDiagnostics();
|
|
invocation_ = std::make_shared<CompilerInvocation>();
|
|
|
|
compInst_.set_invocation(std::move(invocation_));
|
|
compInst_.frontendOpts().inputs.push_back(
|
|
FrontendInputFile(inputFilePath_, Language::Fortran));
|
|
}
|
|
|
|
void TearDown() override {
|
|
// Clear the input file.
|
|
llvm::sys::fs::remove(inputFileName_);
|
|
|
|
// Clear the output files.
|
|
// Note that these tests use an output buffer (as opposed to an output
|
|
// file), hence there are no physical output files to delete and
|
|
// `EraseFiles` is set to `false`. Also, some actions (e.g.
|
|
// `ParseSyntaxOnly`) don't generated output. In such cases there's no
|
|
// output to clear and `ClearOutputFile` returns immediately.
|
|
compInst_.ClearOutputFiles(/*EraseFiles=*/false);
|
|
}
|
|
};
|
|
|
|
TEST_F(FrontendActionTest, TestInputOutput) {
|
|
// Populate the input file with the pre-defined input and flush it.
|
|
*(inputFileOs_) << "End Program arithmetic";
|
|
inputFileOs_.reset();
|
|
|
|
// Set-up the action kind.
|
|
compInst_.invocation().frontendOpts().programAction = InputOutputTest;
|
|
|
|
// Set-up the output stream. Using output buffer wrapped as an output
|
|
// stream, as opposed to an actual file (or a file descriptor).
|
|
llvm::SmallVector<char, 256> outputFileBuffer;
|
|
std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
|
|
new llvm::raw_svector_ostream(outputFileBuffer));
|
|
compInst_.set_outputStream(std::move(outputFileStream));
|
|
|
|
// Execute the action.
|
|
bool success = ExecuteCompilerInvocation(&compInst_);
|
|
|
|
// Validate the expected output.
|
|
EXPECT_TRUE(success);
|
|
EXPECT_TRUE(!outputFileBuffer.empty());
|
|
EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data())
|
|
.startswith("End Program arithmetic"));
|
|
}
|
|
|
|
TEST_F(FrontendActionTest, PrintPreprocessedInput) {
|
|
// Populate the input file with the pre-defined input and flush it.
|
|
*(inputFileOs_) << "#ifdef NEW\n"
|
|
<< " Program A \n"
|
|
<< "#else\n"
|
|
<< " Program B\n"
|
|
<< "#endif";
|
|
inputFileOs_.reset();
|
|
|
|
// Set-up the action kind.
|
|
compInst_.invocation().frontendOpts().programAction = PrintPreprocessedInput;
|
|
compInst_.invocation().preprocessorOpts().noReformat = true;
|
|
|
|
// Set-up the output stream. We are using output buffer wrapped as an output
|
|
// stream, as opposed to an actual file (or a file descriptor).
|
|
llvm::SmallVector<char, 256> outputFileBuffer;
|
|
std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
|
|
new llvm::raw_svector_ostream(outputFileBuffer));
|
|
compInst_.set_outputStream(std::move(outputFileStream));
|
|
|
|
// Execute the action.
|
|
bool success = ExecuteCompilerInvocation(&compInst_);
|
|
|
|
// Validate the expected output.
|
|
EXPECT_TRUE(success);
|
|
EXPECT_TRUE(!outputFileBuffer.empty());
|
|
EXPECT_TRUE(
|
|
llvm::StringRef(outputFileBuffer.data()).startswith("program b\n"));
|
|
}
|
|
|
|
TEST_F(FrontendActionTest, ParseSyntaxOnly) {
|
|
// Populate the input file with the pre-defined input and flush it.
|
|
*(inputFileOs_) << "IF (A > 0.0) IF (B < 0.0) A = LOG (A)\n"
|
|
<< "END";
|
|
inputFileOs_.reset();
|
|
|
|
// Set-up the action kind.
|
|
compInst_.invocation().frontendOpts().programAction = ParseSyntaxOnly;
|
|
|
|
// Set-up the output stream for the semantic diagnostics.
|
|
llvm::SmallVector<char, 256> outputDiagBuffer;
|
|
std::unique_ptr<llvm::raw_pwrite_stream> outputStream(
|
|
new llvm::raw_svector_ostream(outputDiagBuffer));
|
|
compInst_.set_semaOutputStream(std::move(outputStream));
|
|
|
|
// Execute the action.
|
|
bool success = ExecuteCompilerInvocation(&compInst_);
|
|
|
|
// Validate the expected output.
|
|
EXPECT_FALSE(success);
|
|
EXPECT_TRUE(!outputDiagBuffer.empty());
|
|
EXPECT_TRUE(
|
|
llvm::StringRef(outputDiagBuffer.data())
|
|
.contains(
|
|
":1:14: error: IF statement is not allowed in IF statement\n"));
|
|
}
|
|
|
|
TEST_F(FrontendActionTest, EmitLLVM) {
|
|
// Populate the input file with the pre-defined input and flush it.
|
|
*(inputFileOs_) << "end program";
|
|
inputFileOs_.reset();
|
|
|
|
// Set-up the action kind.
|
|
compInst_.invocation().frontendOpts().programAction = EmitLLVM;
|
|
compInst_.invocation().preprocessorOpts().noReformat = true;
|
|
|
|
// Set-up the output stream. We are using output buffer wrapped as an output
|
|
// stream, as opposed to an actual file (or a file descriptor).
|
|
llvm::SmallVector<char> outputFileBuffer;
|
|
std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
|
|
new llvm::raw_svector_ostream(outputFileBuffer));
|
|
compInst_.set_outputStream(std::move(outputFileStream));
|
|
|
|
// Execute the action.
|
|
bool success = ExecuteCompilerInvocation(&compInst_);
|
|
|
|
// Validate the expected output.
|
|
EXPECT_TRUE(success);
|
|
EXPECT_TRUE(!outputFileBuffer.empty());
|
|
|
|
EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data())
|
|
.contains("define void @_QQmain()"));
|
|
}
|
|
|
|
TEST_F(FrontendActionTest, EmitAsm) {
|
|
// Populate the input file with the pre-defined input and flush it.
|
|
*(inputFileOs_) << "end program";
|
|
inputFileOs_.reset();
|
|
|
|
// Set-up the action kind.
|
|
compInst_.invocation().frontendOpts().programAction = EmitAssembly;
|
|
compInst_.invocation().preprocessorOpts().noReformat = true;
|
|
|
|
// Initialise LLVM backend
|
|
llvm::InitializeAllTargets();
|
|
llvm::InitializeAllTargetMCs();
|
|
llvm::InitializeAllAsmPrinters();
|
|
|
|
// Set-up the output stream. We are using output buffer wrapped as an output
|
|
// stream, as opposed to an actual file (or a file descriptor).
|
|
llvm::SmallVector<char, 256> outputFileBuffer;
|
|
std::unique_ptr<llvm::raw_pwrite_stream> outputFileStream(
|
|
new llvm::raw_svector_ostream(outputFileBuffer));
|
|
compInst_.set_outputStream(std::move(outputFileStream));
|
|
|
|
// Execute the action.
|
|
bool success = ExecuteCompilerInvocation(&compInst_);
|
|
|
|
// Validate the expected output.
|
|
EXPECT_TRUE(success);
|
|
EXPECT_TRUE(!outputFileBuffer.empty());
|
|
|
|
EXPECT_TRUE(llvm::StringRef(outputFileBuffer.data()).contains("_QQmain"));
|
|
}
|
|
} // namespace
|