diff --git a/clang/include/clang/Driver/Types.h b/clang/include/clang/Driver/Types.h --- a/clang/include/clang/Driver/Types.h +++ b/clang/include/clang/Driver/Types.h @@ -66,6 +66,9 @@ /// isAcceptedByClang - Can clang handle this input type. bool isAcceptedByClang(ID Id); + /// isAcceptedByFlang - Can flang handle this input type. + bool isAcceptedByFlang(ID Id); + /// isDerivedFromC - Is the input derived from C. /// /// That is, does the lexer follow the rules of @@ -92,9 +95,6 @@ /// isOpenCL - Is this an "OpenCL" input. bool isOpenCL(ID Id); - /// isFortran - Is this a Fortran input. - bool isFortran(ID Id); - /// isSrcFile - Is this a source file, i.e. something that still has to be /// preprocessed. The logic behind this is the same that decides if the first /// compilation phase is a preprocessing one. diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp --- a/clang/lib/Driver/Driver.cpp +++ b/clang/lib/Driver/Driver.cpp @@ -6049,11 +6049,12 @@ bool Driver::ShouldUseFlangCompiler(const JobAction &JA) const { // Say "no" if there is not exactly one input of a type flang understands. if (JA.size() != 1 || - !types::isFortran((*JA.input_begin())->getType())) + !types::isAcceptedByFlang((*JA.input_begin())->getType())) return false; // And say "no" if this is not a kind of action flang understands. - if (!isa(JA) && !isa(JA) && !isa(JA)) + if (!isa(JA) && !isa(JA) && + !isa(JA)) return false; return true; diff --git a/clang/lib/Driver/Types.cpp b/clang/lib/Driver/Types.cpp --- a/clang/lib/Driver/Types.cpp +++ b/clang/lib/Driver/Types.cpp @@ -272,13 +272,16 @@ } } -bool types::isFortran(ID Id) { +bool types::isAcceptedByFlang(ID Id) { switch (Id) { default: return false; case TY_Fortran: case TY_PP_Fortran: return true; + case TY_LLVM_IR: + case TY_LLVM_BC: + return true; } } 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 @@ -40,6 +40,7 @@ LINK_COMPONENTS Passes Analysis + IRReader Option Support Target 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 @@ -73,6 +73,19 @@ } bool CodeGenAction::BeginSourceFileAction() { + llvmCtx = std::make_unique(); + + // If the input is an LLVM file, just parse it and return. + if (this->currentInput().kind().GetLanguage() == Language::LLVM_IR) { + llvm::SMDiagnostic err; + llvmModule = llvm::parseIRFile(currentInput().file(), err, *llvmCtx); + + return (nullptr != llvmModule); + } + + // Otherwise, generate an MLIR module from the input Fortran source + assert(currentInput().kind().GetLanguage() == Language::Fortran && + "Invalid input type - expecting a Fortran file"); bool res = RunPrescan() && RunParse() && RunSemanticChecks(); if (!res) return res; @@ -448,7 +461,6 @@ // Translate to LLVM IR llvm::Optional moduleName = mlirModule->getName(); - llvmCtx = std::make_unique(); llvmModule = mlir::translateModuleToLLVMIR( *mlirModule, *llvmCtx, moduleName ? *moduleName : "FIRModule"); diff --git a/flang/lib/Frontend/FrontendOptions.cpp b/flang/lib/Frontend/FrontendOptions.cpp --- a/flang/lib/Frontend/FrontendOptions.cpp +++ b/flang/lib/Frontend/FrontendOptions.cpp @@ -35,5 +35,9 @@ if (isFixedFormSuffix(extension) || isFreeFormSuffix(extension)) { return Language::Fortran; } + + if (extension == "bc" || extension == "ll") + return Language::LLVM_IR; + return Language::Unknown; } diff --git a/flang/test/Driver/emit-asm-from-llvm-bc.ll b/flang/test/Driver/emit-asm-from-llvm-bc.ll new file mode 100644 --- /dev/null +++ b/flang/test/Driver/emit-asm-from-llvm-bc.ll @@ -0,0 +1,30 @@ +; Verify that the driver can consume LLVM BC files. The expected assembly is +; fairly generic (tested on AArch64 and X86_64), but we may need to tweak when +; testing on other platforms. Note that the actual output doesn't matter as +; long as it's in Assembly format. + +;------------- +; RUN COMMANDS +;------------- +; RUN: rm -f %t.bc +; RUN: %flang_fc1 -emit-llvm-bc %s -o %t.bc +; RUN: %flang_fc1 -S -o - %t.bc | FileCheck %s +; RUN: rm -f %t.bc + +; RUN: rm -f %t.bc +; RUN: %flang -c -emit-llvm %s -o %t.bc +; RUN: %flang -S -o - %t.bc | FileCheck %s +; RUN: rm -f %t.bc + +;---------------- +; EXPECTED OUTPUT +;---------------- +; CHECK-LABEL: foo: +; CHECK: ret + +;------ +; INPUT +;------ +define void @foo() { + ret void +} diff --git a/flang/test/Driver/emit-asm-from-llvm.ll b/flang/test/Driver/emit-asm-from-llvm.ll new file mode 100644 --- /dev/null +++ b/flang/test/Driver/emit-asm-from-llvm.ll @@ -0,0 +1,23 @@ +; Verify that the driver can consume LLVM IR files. The expected assembly is +; fairly generic (verified on AArch64 and X86_64), but we may need to tweak when +; testing on other platforms. Note that the actual output doesn't matter +; as long as it's in Assembly format. + +;------------- +; RUN COMMANDS +;------------- +; RUN: %flang_fc1 -S %s -o - | FileCheck %s +; RUN: %flang -S %s -o - | FileCheck %s + +;---------------- +; EXPECTED OUTPUT +;---------------- +; CHECK-LABEL: foo: +; CHECK: ret + +;------ +; INPUT +;------ +define void @foo() { + ret void +} diff --git a/flang/test/Driver/missing-triple.ll b/flang/test/Driver/missing-triple.ll new file mode 100644 --- /dev/null +++ b/flang/test/Driver/missing-triple.ll @@ -0,0 +1,21 @@ +; Verify that the module triple is overridden by the driver - even when the +; module triple is missing. +; NOTE: At the time of writing, the tested behaviour was consistent with Clang + +;------------- +; RUN COMMANDS +;------------- +; RUN: %flang_fc1 -S %s -o - 2>&1 | FileCheck %s +; RUN: %flang -S %s -o - 2>&1 | FileCheck %s + +;---------------- +; EXPECTED OUTPUT +;---------------- +; CHECK: warning: overriding the module target triple with {{.*}} + +;------ +; INPUT +;------ +define void @foo() { + ret void +} diff --git a/flang/test/Driver/override-triple.ll b/flang/test/Driver/override-triple.ll new file mode 100644 --- /dev/null +++ b/flang/test/Driver/override-triple.ll @@ -0,0 +1,25 @@ +; Verify that the module triple is overridden by the driver - even in the presence +; of a module triple. +; NOTE: At the time of writing, the tested behaviour was consistent with Clang + +;------------- +; RUN COMMANDS +;------------- +; RUN: %flang_fc1 -S %s -o - 2>&1 | FileCheck %s +; RUN: %flang -S %s -o - 2>&1 | FileCheck %s + +;---------------- +; EXPECTED OUTPUT +;---------------- +; CHECK: warning: overriding the module target triple with {{.*}} + +;------ +; INPUT +;------ +; For the triple to be overridden by the driver, it needs to be different to the host triple. +; Use a random string to guarantee that. +target triple = "invalid-triple" + +define void @foo() { + ret void +} 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 @@ -27,7 +27,8 @@ # suffixes: A list of file extensions to treat as test files. config.suffixes = ['.c', '.cpp', '.f', '.F', '.ff', '.FOR', '.for', '.f77', '.f90', '.F90', '.ff90', '.f95', '.F95', '.ff95', '.fpp', '.FPP', '.cuf' - '.CUF', '.f18', '.F18', '.fir', '.f03', '.F03', '.f08', '.F08'] + '.CUF', '.f18', '.F18', '.fir', '.f03', '.F03', '.f08', + '.F08', '.ll'] config.substitutions.append(('%PATH%', config.environment['PATH'])) config.substitutions.append(('%llvmshlibdir', config.llvm_shlib_dir))