diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -750,7 +750,7 @@ "name matches the given POSIX regular expression">; def R_Joined : Joined<["-"], "R">, Group, Flags<[CC1Option, CoreOption]>, MetaVarName<"">, HelpText<"Enable the specified remark">; -def S : Flag<["-"], "S">, Flags<[NoXarchOption,CC1Option]>, Group, +def S : Flag<["-"], "S">, Flags<[NoXarchOption,CC1Option,FlangOption,FC1Option]>, Group, HelpText<"Only run preprocess and compilation steps">; def Tbss : JoinedOrSeparate<["-"], "Tbss">, Group, MetaVarName<"">, HelpText<"Set starting address of BSS to ">; diff --git a/flang/include/flang/Frontend/FrontendActions.h b/flang/include/flang/Frontend/FrontendActions.h --- a/flang/include/flang/Frontend/FrontendActions.h +++ b/flang/include/flang/Frontend/FrontendActions.h @@ -192,8 +192,19 @@ void ExecuteAction() override; }; -class EmitObjAction : public CodeGenAction { +class BackendAction : public CodeGenAction { +public: + enum class BackendActionTy { + Backend_EmitAssembly, ///< Emit native assembly files + Backend_EmitObj ///< Emit native object files + }; + + BackendAction(BackendActionTy act) : action{act} {}; + +private: void ExecuteAction() override; + + BackendActionTy action; }; } // namespace Fortran::frontend diff --git a/flang/include/flang/Frontend/FrontendOptions.h b/flang/include/flang/Frontend/FrontendOptions.h --- a/flang/include/flang/Frontend/FrontendOptions.h +++ b/flang/include/flang/Frontend/FrontendOptions.h @@ -40,6 +40,9 @@ /// Emit a .o file. EmitObj, + /// Emit a .s file. + EmitAssembly, + /// Parse, unparse the parse-tree and output a Fortran source file DebugUnparse, diff --git a/flang/lib/Frontend/CMakeLists.txt b/flang/lib/Frontend/CMakeLists.txt --- a/flang/lib/Frontend/CMakeLists.txt +++ b/flang/lib/Frontend/CMakeLists.txt @@ -27,6 +27,8 @@ FortranLower clangBasic clangDriver + LLVMAnalysis + LLVMTarget FIRDialect FIRSupport FIRBuilder diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -153,6 +153,9 @@ case clang::driver::options::OPT_emit_obj: opts.programAction = EmitObj; break; + case clang::driver::options::OPT_S: + opts.programAction = EmitAssembly; + break; case clang::driver::options::OPT_fdebug_unparse: opts.programAction = DebugUnparse; break; diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp --- a/flang/lib/Frontend/FrontendActions.cpp +++ b/flang/lib/Frontend/FrontendActions.cpp @@ -31,7 +31,13 @@ #include "mlir/Pass/PassManager.h" #include "mlir/Target/LLVMIR/ModuleTranslation.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Passes/PassBuilder.h" #include "llvm/Support/ErrorHandling.h" +#include "llvm/Target/TargetMachine.h" #include #include @@ -417,7 +423,6 @@ pm.addPass(std::make_unique()); pm.enableVerifier(/*verifyPasses=*/true); - mlir::PassPipelineCLParser passPipeline("", "Compiler passes to run"); // Create the pass pipeline fir::createMLIRToLLVMPassPipeline(pm); @@ -490,11 +495,90 @@ mlirModule->print(*os); } -void EmitObjAction::ExecuteAction() { +void BackendAction::ExecuteAction() { CompilerInstance &ci = this->instance(); - unsigned DiagID = ci.diagnostics().getCustomDiagID( - clang::DiagnosticsEngine::Error, "code-generation is not available yet"); - ci.diagnostics().Report(DiagID); + // Generate an LLVM module if it's not already present (it will already be + // present if the input file is an LLVM IR/BC file). + if (!llvmModule) + GenerateLLVMIR(); + + // Create `Target` + std::string error; + const std::string &theTriple = llvmModule->getTargetTriple(); + const llvm::Target *theTarget = + llvm::TargetRegistry::lookupTarget(theTriple, error); + // TODO: Make this a diagnostic once `flang-new` can consume LLVM IR files + // (in which users could use unsupported triples) + assert(theTarget && "Failed to create Target"); + + // Create `TargetMachine` + std::unique_ptr TM; + TM.reset(theTarget->createTargetMachine(theTriple, /*CPU=*/"", + /*Features=*/"", llvm::TargetOptions(), llvm::None)); + assert(TM && "Failed to create TargetMachine"); + llvmModule->setDataLayout(TM->createDataLayout()); + + // If the output stream is a file, generate it and define the corresponding + // output stream. If a pre-defined output stream is available, we will use + // that instead. + // + // NOTE: `os` is a smart pointer that will be destroyed at the end of this + // method. However, it won't be written to until `CodeGenPasses` is + // destroyed. By defining `os` before `CodeGenPasses`, we make sure that the + // output stream won't be destroyed before it is written to. This only + // applies when an output file is used (i.e. there is no pre-defined output + // stream). + // TODO: Revisit once the new PM is ready (i.e. when `CodeGenPasses` is + // updated to use it). + std::unique_ptr os; + if (ci.IsOutputStreamNull()) { + // Get the output buffer/file + switch (action) { + case BackendActionTy::Backend_EmitAssembly: + os = ci.CreateDefaultOutputFile( + /*Binary=*/false, /*InFile=*/GetCurrentFileOrBufferName(), "s"); + break; + case BackendActionTy::Backend_EmitObj: + os = ci.CreateDefaultOutputFile( + /*Binary=*/true, /*InFile=*/GetCurrentFileOrBufferName(), "o"); + break; + } + if (!os) { + unsigned diagID = ci.diagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Error, "failed to create the output file"); + ci.diagnostics().Report(diagID); + return; + } + } + + // Create an LLVM code-gen pass pipeline. Currently only the legacy pass + // manager is supported. + // TODO: Switch to the new PM once it's available in the backend. + llvm::legacy::PassManager CodeGenPasses; + CodeGenPasses.add( + createTargetTransformInfoWrapperPass(TM->getTargetIRAnalysis())); + llvm::Triple triple(theTriple); + + std::unique_ptr TLII = + std::make_unique(triple); + assert(TLII && "Failed to create TargetLibraryInfo"); + CodeGenPasses.add(new llvm::TargetLibraryInfoWrapperPass(*TLII)); + + llvm::CodeGenFileType cgft = (action == BackendActionTy::Backend_EmitAssembly) + ? llvm::CodeGenFileType::CGFT_AssemblyFile + : llvm::CodeGenFileType::CGFT_ObjectFile; + if (TM->addPassesToEmitFile(CodeGenPasses, + ci.IsOutputStreamNull() ? *os : ci.GetOutputStream(), nullptr, + cgft)) { + unsigned diagID = + ci.diagnostics().getCustomDiagID(clang::DiagnosticsEngine::Error, + "emission of this file type is not supported"); + ci.diagnostics().Report(diagID); + return; + } + + // Run the code-gen passes + CodeGenPasses.run(*llvmModule); } void InitOnlyAction::ExecuteAction() { diff --git a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp --- a/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/flang/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -38,7 +38,11 @@ case EmitLLVM: return std::make_unique(); case EmitObj: - return std::make_unique(); + return std::make_unique( + BackendAction::BackendActionTy::Backend_EmitObj); + case EmitAssembly: + return std::make_unique( + BackendAction::BackendActionTy::Backend_EmitAssembly); case DebugUnparse: return std::make_unique(); case DebugUnparseNoSema: diff --git a/flang/lib/Optimizer/Support/FIRContext.cpp b/flang/lib/Optimizer/Support/FIRContext.cpp --- a/flang/lib/Optimizer/Support/FIRContext.cpp +++ b/flang/lib/Optimizer/Support/FIRContext.cpp @@ -12,19 +12,20 @@ #include "flang/Optimizer/Support/FIRContext.h" #include "flang/Optimizer/Support/KindMapping.h" +#include "mlir/Dialect/LLVMIR/LLVMDialect.h" #include "mlir/IR/BuiltinAttributes.h" #include "mlir/IR/BuiltinOps.h" #include "llvm/Support/Host.h" -static constexpr const char *tripleName = "fir.triple"; - void fir::setTargetTriple(mlir::ModuleOp mod, llvm::StringRef triple) { auto target = fir::determineTargetTriple(triple); - mod->setAttr(tripleName, mlir::StringAttr::get(mod.getContext(), target)); + mod->setAttr(mlir::LLVM::LLVMDialect::getTargetTripleAttrName(), + mlir::StringAttr::get(mod.getContext(), target)); } llvm::Triple fir::getTargetTriple(mlir::ModuleOp mod) { - if (auto target = mod->getAttrOfType(tripleName)) + if (auto target = mod->getAttrOfType( + mlir::LLVM::LLVMDialect::getTargetTripleAttrName())) return llvm::Triple(target.getValue()); return llvm::Triple(llvm::sys::getDefaultTargetTriple()); } diff --git a/flang/test/CMakeLists.txt b/flang/test/CMakeLists.txt --- a/flang/test/CMakeLists.txt +++ b/flang/test/CMakeLists.txt @@ -46,7 +46,7 @@ flang_site_config=${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg.py) set(FLANG_TEST_DEPENDS - flang-new llvm-config FileCheck count not module_files fir-opt tco bbc + flang-new llvm-config FileCheck count not module_files fir-opt tco bbc llvm-objdump ) if (FLANG_INCLUDE_TESTS) diff --git a/flang/test/Driver/code-gen-aarch64.f90 b/flang/test/Driver/code-gen-aarch64.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Driver/code-gen-aarch64.f90 @@ -0,0 +1,31 @@ +! Test -emit-obj (X86) + +! REQUIRES: aarch64-registered-target, x86-registered-target + +!------------- +! RUN COMMANDS +!------------- +! RUN: rm -f %t.o +! RUN: %flang_fc1 -emit-obj -triple aarch64-unknown-linux-gnu %s -o %t.o +! RUN: llvm-objdump --triple aarch64-unknown-linux-gnu --disassemble-all %t.o | FileCheck %s --check-prefix=CORRECT_TRIPLE +! RUN: rm -f %t.o +! RUN: %flang -c --target=aarch64-unknown-linux-gnu %s -o %t.o +! RUN: llvm-objdump --triple aarch64-unknown-linux-gnu --disassemble-all %t.o | FileCheck %s --check-prefix=CORRECT_TRIPLE + +! RUN: %flang -c --target=aarch64-unknown-linux-gnu %s -o %t.o +! RUN: llvm-objdump --triple x86_64-unknown-linux-gnu --disassemble-all %t.o | FileCheck %s --check-prefix=INCORRECT_TRIPLE + +!---------------- +! EXPECTED OUTPUT +!---------------- +! CORRECT_TRIPLE-LABEL: <_QQmain>: +! CORRECT_TRIPLE-NEXT: ret + +! When incorrect triple is used to disassemble, there won't be a ret instruction at all. +! INCORRECT_TRIPLE-LABEL: <_QQmain>: +! INCORRECT_TRIPLE-NOT: ret + +!------ +! INPUT +!------ +end program diff --git a/flang/test/Driver/code-gen-x86.f90 b/flang/test/Driver/code-gen-x86.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Driver/code-gen-x86.f90 @@ -0,0 +1,32 @@ +! Test -emit-obj (X86) + +! REQUIRES: x86-registered-target, aarch64-registered-target +! UNSUPPORTED: darwin, macos + +!------------- +! RUN COMMANDS +!------------- +! RUN: rm -f %t.o +! RUN: %flang_fc1 -triple x86_64-unknown-linux-gnu -emit-obj %s -o %t.o +! RUN: llvm-objdump --triple x86_64-unknown-linux-gnu --disassemble-all %t.o | FileCheck %s --check-prefix=CORRECT_TRIPLE +! RUN: rm -f %t.o +! RUN: %flang --target=x86_64-unknown-linux-gnu -c %s -o %t.o +! RUN: llvm-objdump --triple x86_64-unknown-linux-gnu --disassemble-all %t.o | FileCheck %s --check-prefix=CORRECT_TRIPLE + +! RUN: %flang -c --target=x86_64-unknown-linux-gnu %s -o %t.o +! RUN: llvm-objdump --triple aarch64-unknown-linux-gnu --disassemble-all %t.o | FileCheck %s --check-prefix=INCORRECT_TRIPLE + +!---------------- +! EXPECTED OUTPUT +!---------------- +! CORRECT_TRIPLE-LABEL: <_QQmain>: +! CORRECT_TRIPLE-NEXT: retq + +! When incorrect triple is used to disassemble, there won't be a ret(q) instruction at all. +! INCORRECT_TRIPLE-LABEL: <_QQmain>: +! INCORRECT_TRIPLE-NOT: ret + +!------ +! INPUT +!------ +end program diff --git a/flang/test/Driver/code-gen.f90 b/flang/test/Driver/code-gen.f90 deleted file mode 100644 --- a/flang/test/Driver/code-gen.f90 +++ /dev/null @@ -1,19 +0,0 @@ -! Although code-generation is not yet available, we do have frontend actions -! that correspond to `-c` and `-emit-obj`. For now these actions are just a -! placeholder and running them leads to a driver error. This test makes sure -! that these actions are indeed run (rather than `-c` or `-emit-obj` being -! rejected earlier). -! TODO: Replace this file with a proper test once code-generation is available. - -!----------- -! RUN LINES -!----------- -! RUN: not %flang %s 2>&1 | FileCheck %s --check-prefix=ERROR -! RUN: not %flang -c %s 2>&1 | FileCheck %s --check-prefix=ERROR -! RUN: not %flang -emit-obj %s 2>&1 | FileCheck %s --check-prefix=ERROR -! RUN: not %flang -fc1 -emit-obj %s 2>&1 | FileCheck %s --check-prefix=ERROR - -!----------------------- -! EXPECTED OUTPUT -!----------------------- -! ERROR: code-generation is not available yet diff --git a/flang/test/Driver/driver-help-hidden.f90 b/flang/test/Driver/driver-help-hidden.f90 --- a/flang/test/Driver/driver-help-hidden.f90 +++ b/flang/test/Driver/driver-help-hidden.f90 @@ -55,6 +55,7 @@ ! CHECK-NEXT: -print-target-triple Print the normalized target triple ! CHECK-NEXT: -P Disable linemarker output in -E mode ! CHECK-NEXT: -std= Language standard to compile for +! CHECK-NEXT: -S Only run preprocess and compilation steps ! CHECK-NEXT: --target= Generate code for the given target ! CHECK-NEXT: -U Undefine macro ! CHECK-NEXT: --version Print version information diff --git a/flang/test/Driver/driver-help.f90 b/flang/test/Driver/driver-help.f90 --- a/flang/test/Driver/driver-help.f90 +++ b/flang/test/Driver/driver-help.f90 @@ -55,6 +55,7 @@ ! HELP-NEXT: -print-target-triple Print the normalized target triple ! HELP-NEXT: -P Disable linemarker output in -E mode ! HELP-NEXT: -std= Language standard to compile for +! HELP-NEXT: -S Only run preprocess and compilation steps ! HELP-NEXT: --target= Generate code for the given target ! HELP-NEXT: -U Undefine macro ! HELP-NEXT: --version Print version information @@ -128,6 +129,7 @@ ! HELP-FC1-NEXT: -plugin Use the named plugin action instead of the default action (use "help" to list available options) ! HELP-FC1-NEXT: -P Disable linemarker output in -E mode ! HELP-FC1-NEXT: -std= Language standard to compile for +! HELP-FC1-NEXT: -S Only run preprocess and compilation steps ! HELP-FC1-NEXT: -test-io Run the InputOuputTest action. Use for development and testing only. ! HELP-FC1-NEXT: -triple Specify target triple (e.g. i686-apple-darwin9) ! HELP-FC1-NEXT: -U Undefine macro diff --git a/flang/test/Driver/emit-asm-aarch64.f90 b/flang/test/Driver/emit-asm-aarch64.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Driver/emit-asm-aarch64.f90 @@ -0,0 +1,21 @@ +! Test -S (AArch64) + +! REQUIRES: aarch64-registered-target + +!------------- +! RUN COMMANDS +!------------- +! RUN: %flang_fc1 -S -triple aarch64-unknown-linux-gnu %s -o - | FileCheck %s +! RUN: %flang -S -target aarch64-unknown-linux-gnu %s -o - | FileCheck %s + +!---------------- +! EXPECTED OUTPUT +!---------------- +! CHECK-LABEL: _QQmain: +! CHECK-NEXT: .Lfunc_begin0: +! CHECK: ret + +!------ +! INPUT +!------ +end program diff --git a/flang/test/Driver/emit-asm-x86.f90 b/flang/test/Driver/emit-asm-x86.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Driver/emit-asm-x86.f90 @@ -0,0 +1,21 @@ +! Test -S (X86) + +! REQUIRES: x86-registered-target + +!------------- +! RUN COMMANDS +!------------- +! RUN: %flang_fc1 -S -triple x86_64-unknown-linux-gnu %s -o - | FileCheck %s +! RUN: %flang -S -target x86_64-unknown-linux-gnu %s -o - | FileCheck %s + +!---------------- +! EXPECTED OUTPUT +!---------------- +! CHECK-LABEL: _QQmain: +! CHECK-NEXT: .Lfunc_begin0: +! CHECK: ret + +!------ +! INPUT +!------ +end program diff --git a/flang/test/Driver/syntax-only.f90 b/flang/test/Driver/syntax-only.f90 --- a/flang/test/Driver/syntax-only.f90 +++ b/flang/test/Driver/syntax-only.f90 @@ -13,14 +13,15 @@ ! RUN: %flang -fsyntax-only %s 2>&1 | FileCheck %s --allow-empty ! RUN: %flang_fc1 %s 2>&1 | FileCheck %s --allow-empty -! RUN: not %flang %s 2>&1 | FileCheck %s --check-prefix=NO_FSYNTAX_ONLY -! RUN: not %flang_fc1 -emit-obj %s 2>&1 | FileCheck %s --check-prefix=NO_FSYNTAX_ONLY +! RUN: rm -rf %t/non-existent-dir/ +! RUN: not %flang -c %s -o %t/non-existent-dir/syntax-only.o 2>&1 | FileCheck %s --check-prefix=NO_FSYNTAX_ONLY +! RUN: not %flang_fc1 -emit-obj %s -o %t/non-existent-dir/syntax-only.o 2>&1 | FileCheck %s --check-prefix=NO_FSYNTAX_ONLY !----------------- ! EXPECTED OUTPUT !----------------- ! CHECK-NOT: error -! NO_FSYNTAX_ONLY: error: code-generation is not available yet +! NO_FSYNTAX_ONLY: error: failed to create the output file !------- ! INPUT diff --git a/flang/test/Fir/target-rewrite-triple.fir b/flang/test/Fir/target-rewrite-triple.fir --- a/flang/test/Fir/target-rewrite-triple.fir +++ b/flang/test/Fir/target-rewrite-triple.fir @@ -1,10 +1,10 @@ // RUN: fir-opt --target-rewrite %s | FileCheck %s --check-prefix=UNCHANGED // RUN: fir-opt --target-rewrite="target=x86_64-unknown-linux-gnu" %s | FileCheck %s --check-prefix=CHANGED -// UNCHANGED: fir.triple = "aarch64-unknown-linux-gnu" -// CHANGED: fir.triple = "x86_64-unknown-linux-gnu" -// CHANGED-NOT: fir.triple = "aarch64-unknown-linux-gnu" -module attributes {fir.triple = "aarch64-unknown-linux-gnu"} { +// UNCHANGED: llvm.target_triple = "aarch64-unknown-linux-gnu" +// CHANGED: llvm.target_triple = "x86_64-unknown-linux-gnu" +// CHANGED-NOT: llvm.target_triple = "aarch64-unknown-linux-gnu" +module attributes {llvm.target_triple = "aarch64-unknown-linux-gnu"} { func @dummyfunc() -> () { return } diff --git a/flang/test/lit.cfg.py b/flang/test/lit.cfg.py --- a/flang/test/lit.cfg.py +++ b/flang/test/lit.cfg.py @@ -39,6 +39,11 @@ llvm_config.feature_config( [('--assertion-mode', {'ON': 'asserts'})]) +# Targets +config.targets = frozenset(config.targets_to_build.split()) +for arch in config.targets_to_build.split(): + config.available_features.add(arch.lower() + '-registered-target') + # excludes: A list of directories to exclude from the testsuite. The 'Inputs' # subdirectories contain auxiliary inputs for various tests in their parent # directories. diff --git a/flang/test/lit.site.cfg.py.in b/flang/test/lit.site.cfg.py.in --- a/flang/test/lit.site.cfg.py.in +++ b/flang/test/lit.site.cfg.py.in @@ -18,6 +18,7 @@ config.flang_standalone_build = @FLANG_STANDALONE_BUILD@ config.has_plugins = @LLVM_ENABLE_PLUGINS@ config.cc = "@CMAKE_C_COMPILER@" +config.targets_to_build = "@TARGETS_TO_BUILD@" # Support substitution of the tools_dir with user parameters. This is # used when we can't determine the tool dir at configuration time. diff --git a/flang/tools/flang-driver/fc1_main.cpp b/flang/tools/flang-driver/fc1_main.cpp --- a/flang/tools/flang-driver/fc1_main.cpp +++ b/flang/tools/flang-driver/fc1_main.cpp @@ -20,6 +20,7 @@ #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Option/OptTable.h" +#include "llvm/Support/TargetSelect.h" #include @@ -48,6 +49,11 @@ bool success = CompilerInvocation::CreateFromArgs(flang->invocation(), argv, diags); + // Initialize targets first, so that --version shows registered targets. + llvm::InitializeAllTargets(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmPrinters(); + diagsBuffer->FlushDiagnostics(flang->diagnostics()); if (!success) diff --git a/flang/unittests/Frontend/CMakeLists.txt b/flang/unittests/Frontend/CMakeLists.txt --- a/flang/unittests/Frontend/CMakeLists.txt +++ b/flang/unittests/Frontend/CMakeLists.txt @@ -1,3 +1,7 @@ +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} +) + add_flang_unittest(FlangFrontendTests CompilerInstanceTest.cpp FrontendActionTest.cpp diff --git a/flang/unittests/Frontend/FrontendActionTest.cpp b/flang/unittests/Frontend/FrontendActionTest.cpp --- a/flang/unittests/Frontend/FrontendActionTest.cpp +++ b/flang/unittests/Frontend/FrontendActionTest.cpp @@ -11,6 +11,7 @@ #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" @@ -188,4 +189,35 @@ 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 outputFileBuffer; + std::unique_ptr 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