
*SUMMARY* Currently, the frontend driver assumes that a target triple is either: * provided by the frontend itself (e.g. when lowering and generating code), * specified through the `-triple/-target` command line flags. If `-triple/-target` is not used, the frontend will simply use the host triple. This is going to be insufficient when e.g. consuming an LLVM IR file that has no triple specified (reading LLVM files is WIP, see D124667). We shouldn't require the triple to be specified via the command line in such situation. Instead, the frontend driver should contain a good default, e.g. the host triple. This patch updates Flang's `CompilerInvocation` to do just that, i.e. defines its default target triple. Similarly to Clang: * the default `CompilerInvocation` triple is set as the host triple, * the value specified with `-triple` takes precedence over the frontend driver default and the current module triple, * the frontend driver default takes precedence over the module triple. *TESTS* This change requires 2 unit tests to be updated. That's because relevant frontend actions are updated to assume that there's always a valid triple available in the current `CompilerInvocation`. This update is required because the unit tests bypass the regular `CompilerInvocation` set-up (in particular, they don't call `CompilerInvocation::CreateFromArgs`). I've also taken the liberty to disable the pre-precossor formatting in the affected unit tests as well (it is not required). No new tests are added. As `flang-new -fc1` does not support consuming LLVM IR files just yet, it is not possible to compile an LLVM IR file without a triple. More specifically, atm all LLVM IR files are generated and stored internally and the driver makes sure that these contain a valid target triple. This is about to change in D124667 (which adds support for reading LLVM IR/BC files) and that's where tests for exercising the default frontend driver triple will be added. *WHAT DOES CLANG DO?* For reference, the default target triple for Clang's `CompilerInvocation` is set through option marshalling infra [1] in Options.td. Please check the definition of the `-triple` flag: ``` def triple : Separate<["-"], "triple">, HelpText<"Specify target triple (e.g. i686-apple-darwin9)">, MarshallingInfoString<TargetOpts<"Triple">, "llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple())">, AlwaysEmit, Normalizer<"normalizeTriple">; ``` Ideally, we should re-use the marshalling infra in Flang. [1] https://clang.llvm.org/docs/InternalsManual.html#option-marshalling-infrastructure Differential Revision: https://reviews.llvm.org/D124664
232 lines
8.4 KiB
C++
232 lines
8.4 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/ADT/Triple.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/Host.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;
|
|
|
|
// Set-up default target triple.
|
|
compInst_.invocation().targetOpts().triple =
|
|
llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
|
|
|
|
// 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;
|
|
|
|
// Set-up default target triple.
|
|
compInst_.invocation().targetOpts().triple =
|
|
llvm::Triple::normalize(llvm::sys::getDefaultTargetTriple());
|
|
|
|
// 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
|