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 @@ -115,12 +115,17 @@ enum class Language : uint8_t { Unknown, + /// MLIR: we accept this so that we can run the optimizer on it, and compile + /// it to LLVM IR, assembly or object code. + MLIR, + /// LLVM IR: we accept this so that we can run the optimizer on it, /// and compile it to assembly or object code. LLVM_IR, /// @{ Languages that the frontend can parse and compile. Fortran, + /// @} }; // Source file layout 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 @@ -269,10 +269,12 @@ .Case("f95-cpp-input", Language::Fortran) .Default(Language::Unknown); - // Some special cases cannot be combined with suffixes. + // Flang's intermediate representations. if (dashX.isUnknown()) dashX = llvm::StringSwitch(xValue) .Case("ir", Language::LLVM_IR) + .Case("fir", Language::MLIR) + .Case("mlir", Language::MLIR) .Default(Language::Unknown); if (dashX.isUnknown()) 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 @@ -34,6 +34,7 @@ #include "mlir/IR/Dialect.h" #include "mlir/Pass/PassManager.h" #include "mlir/Target/LLVMIR/ModuleTranslation.h" +#include "mlir/Parser/Parser.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/DiagnosticFrontend.h" #include "llvm/ADT/StringRef.h" @@ -96,6 +97,34 @@ return true; } + // Load the MLIR dialects required by Flang + mlir::DialectRegistry registry; + mlirCtx = std::make_unique(registry); + fir::support::registerNonCodegenDialects(registry); + fir::support::loadNonCodegenDialects(*mlirCtx); + fir::support::loadDialects(*mlirCtx); + fir::support::registerLLVMTranslation(*mlirCtx); + + // If the input is an MLIR file, just parse it and return. + if (this->getCurrentInput().getKind().getLanguage() == Language::MLIR) { + llvm::SourceMgr sourceMgr; + llvm::ErrorOr> fileOrErr = + llvm::MemoryBuffer::getFileOrSTDIN(getCurrentInput().getFile()); + sourceMgr.AddNewSourceBuffer(std::move(*fileOrErr), llvm::SMLoc()); + mlir::OwningOpRef module = + mlir::parseSourceFile(sourceMgr, mlirCtx.get()); + + if (!module || mlir::failed(module->verifyInvariants())) { + unsigned diagID = ci.getDiagnostics().getCustomDiagID( + clang::DiagnosticsEngine::Error, "Could not parse FIR"); + ci.getDiagnostics().Report(diagID); + return false; + } + + mlirModule = std::make_unique(module.release()); + return true; + } + // Otherwise, generate an MLIR module from the input Fortran source if (getCurrentInput().getKind().getLanguage() != Language::Fortran) { unsigned diagID = ci.getDiagnostics().getCustomDiagID( @@ -108,12 +137,6 @@ if (!res) return res; - // Load the MLIR dialects required by Flang - mlir::DialectRegistry registry; - mlirCtx = std::make_unique(registry); - fir::support::registerNonCodegenDialects(registry); - fir::support::loadNonCodegenDialects(*mlirCtx); - // Create a LoweringBridge const common::IntrinsicTypeDefaultKinds &defKinds = ci.getInvocation().getSemanticsContext().defaultKinds(); 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 @@ -42,6 +42,8 @@ if (extension == "bc" || extension == "ll") return Language::LLVM_IR; + if (extension == "fir" || extension == "mlir") + return Language::MLIR; return Language::Unknown; } diff --git a/flang/test/Driver/emit-asm-from-mlir.mlir b/flang/test/Driver/emit-asm-from-mlir.mlir new file mode 100644 --- /dev/null +++ b/flang/test/Driver/emit-asm-from-mlir.mlir @@ -0,0 +1,19 @@ +; Verify that the driver can consume MLIR/FIR files. + +;------------- +; RUN COMMANDS +;------------- +; RUN: %flang_fc1 -S %s -o - | FileCheck %s + +;---------------- +; EXPECTED OUTPUT +;---------------- +; CHECK-LABEL: foo: +; CHECK: ret + +;------ +; INPUT +;------ +func.func @foo() { + return +} diff --git a/flang/test/Driver/parse-fir-error.ll b/flang/test/Driver/parse-fir-error.ll new file mode 100644 --- /dev/null +++ b/flang/test/Driver/parse-fir-error.ll @@ -0,0 +1,21 @@ +; This file is a valid LLVM IR file, but we force the driver to treat it as +; FIR (with the `-x` flag). This way we verify that the driver +; correctly rejects invalid FIR input. + +;---------- +; RUN LINES +;---------- +; Input type is implicit (correctly assumed to be LLVM IR) +; RUN: %flang_fc1 -S %s -o - + +; Input type is explicitly set as FIR +; Verify that parsing errors are correctly reported by the driver +; RUN: not %flang_fc1 -S -x fir %s 2>&1 | FileCheck %s --check-prefix=ERROR +; RUN: not %flang_fc1 -S %s -x mlir 2>&1 | FileCheck %s --check-prefix=ERROR + +; ERROR: error: unexpected character +; ERROR: error: Could not parse FIR + +define void @foo() { + ret void +} diff --git a/flang/test/Fir/addrof.fir b/flang/test/Fir/addrof.fir --- a/flang/test/Fir/addrof.fir +++ b/flang/test/Fir/addrof.fir @@ -1,4 +1,5 @@ // RUN: tco %s | FileCheck %s +// RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck %s // CHECK: @var_x = external global i32 fir.global @var_x : !fir.int<4> {} diff --git a/flang/test/Fir/alloc.fir b/flang/test/Fir/alloc.fir --- a/flang/test/Fir/alloc.fir +++ b/flang/test/Fir/alloc.fir @@ -1,4 +1,5 @@ // RUN: tco %s | FileCheck %s +// RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck %s // UNSUPPORTED: system-windows diff --git a/flang/test/Fir/arrayset.fir b/flang/test/Fir/arrayset.fir --- a/flang/test/Fir/arrayset.fir +++ b/flang/test/Fir/arrayset.fir @@ -1,4 +1,5 @@ // RUN: tco %s | FileCheck %s +// RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck %s // CHECK-LABEL: define void @x(ptr %0) func.func @x(%arr : !fir.ref>) { diff --git a/flang/test/Fir/boxchar.fir b/flang/test/Fir/boxchar.fir --- a/flang/test/Fir/boxchar.fir +++ b/flang/test/Fir/boxchar.fir @@ -1,4 +1,5 @@ // RUN: tco --target=x86_64-unknown-linux-gnu %s | FileCheck %s +// RUN: %flang_fc1 -emit-llvm -triple x86_64-unknown-linux-gnu %s -o - | FileCheck %s // Test of building and passing boxchar. diff --git a/flang/test/Fir/embox.fir b/flang/test/Fir/embox.fir --- a/flang/test/Fir/embox.fir +++ b/flang/test/Fir/embox.fir @@ -1,4 +1,5 @@ // RUN: tco %s | FileCheck %s +// RUN: %flang_fc1 -mmlir -disable-external-name-interop -emit-llvm %s -o -| FileCheck %s // CHECK-LABEL: define void @_QPtest_callee(ptr %0) diff --git a/flang/test/Fir/global.fir b/flang/test/Fir/global.fir --- a/flang/test/Fir/global.fir +++ b/flang/test/Fir/global.fir @@ -1,4 +1,5 @@ // RUN: tco %s | FileCheck %s +// RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck %s // CHECK: @g_i0 = global i32 0 fir.global @g_i0 : i32 { diff --git a/flang/test/Fir/ignore-missing-type-descriptor.fir b/flang/test/Fir/ignore-missing-type-descriptor.fir --- a/flang/test/Fir/ignore-missing-type-descriptor.fir +++ b/flang/test/Fir/ignore-missing-type-descriptor.fir @@ -3,6 +3,7 @@ // having to care with providing an ABI compliant derived type descriptor object. // Missing derived type descriptor pointers are replaced by null pointers. // RUN: tco --ignore-missing-type-desc -o - %s | FileCheck %s +// RUN: %flang_fc1 -emit-llvm -mmlir --ignore-missing-type-desc -o - %s | FileCheck %s !some_freestyle_type = !fir.type diff --git a/flang/test/Fir/inline.fir b/flang/test/Fir/inline.fir --- a/flang/test/Fir/inline.fir +++ b/flang/test/Fir/inline.fir @@ -1,4 +1,5 @@ // RUN: tco --target=x86_64-unknown-linux-gnu --inline-all %s -o - | FileCheck %s +// RUN: %flang_fc1 -triple x86_64-unknown-linux-gnu -mmlir --inline-all -emit-llvm %s -o - | FileCheck %s // CHECK-LABEL: @add func.func @add(%a : i32, %b : i32) -> i32 { diff --git a/flang/test/Fir/optional.fir b/flang/test/Fir/optional.fir --- a/flang/test/Fir/optional.fir +++ b/flang/test/Fir/optional.fir @@ -1,4 +1,5 @@ // RUN: tco %s | FileCheck %s +// RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck %s // Test fir.is_present and fir.absent codegen diff --git a/flang/test/Fir/peephole.fir b/flang/test/Fir/peephole.fir --- a/flang/test/Fir/peephole.fir +++ b/flang/test/Fir/peephole.fir @@ -1,4 +1,5 @@ // RUN: tco %s | FileCheck %s +// RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck %s // Test peephole optimizations diff --git a/flang/test/Fir/rebox.fir b/flang/test/Fir/rebox.fir --- a/flang/test/Fir/rebox.fir +++ b/flang/test/Fir/rebox.fir @@ -1,4 +1,5 @@ // RUN: tco %s | FileCheck %s +// RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck %s // Test applying slice on fir.box // subroutine foo(x) diff --git a/flang/test/Fir/select.fir b/flang/test/Fir/select.fir --- a/flang/test/Fir/select.fir +++ b/flang/test/Fir/select.fir @@ -1,6 +1,7 @@ // Test lowering FIR to LLVM IR of fir.select{|_rank|_case} // RUN: tco %s | FileCheck %s +// RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck %s // CHECK-LABEL: @f func.func @f(%a : i32) -> i32 { diff --git a/flang/test/Fir/widechar.fir b/flang/test/Fir/widechar.fir --- a/flang/test/Fir/widechar.fir +++ b/flang/test/Fir/widechar.fir @@ -1,4 +1,5 @@ // RUN: tco %s | FileCheck %s +// RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck %s // CHECK-LABEL: @character_literal1 func.func @character_literal1() -> !fir.char<1,13> { diff --git a/flang/test/Lower/common-block.f90 b/flang/test/Lower/common-block.f90 --- a/flang/test/Lower/common-block.f90 +++ b/flang/test/Lower/common-block.f90 @@ -1,4 +1,5 @@ ! RUN: bbc %s -o - | tco | FileCheck %s +! RUN: %flang -emit-llvm -S -mmlir -disable-external-name-interop %s -o - | FileCheck %s ! CHECK: @_QB = common global [8 x i8] zeroinitializer ! CHECK: @_QBrien = common global [1 x i8] zeroinitializer diff --git a/flang/test/Lower/complex-part.f90 b/flang/test/Lower/complex-part.f90 --- a/flang/test/Lower/complex-part.f90 +++ b/flang/test/Lower/complex-part.f90 @@ -1,4 +1,5 @@ ! RUN: bbc %s -o - | tco | FileCheck %s +! RUN: %flang -emit-llvm -S -mmlir -disable-external-name-interop %s -o - | FileCheck %s COMPLEX c c%RE = 3.14 diff --git a/flang/test/Lower/forall/character-1.f90 b/flang/test/Lower/forall/character-1.f90 --- a/flang/test/Lower/forall/character-1.f90 +++ b/flang/test/Lower/forall/character-1.f90 @@ -1,4 +1,5 @@ ! RUN: bbc %s -o - | tco | FileCheck %s +! RUN: %flang -emit-llvm -S -mmlir -disable-external-name-interop %s -o - | FileCheck %s ! Test from Fortran source through to LLVM IR. ! UNSUPPORTED: system-windows @@ -23,7 +24,7 @@ ! CHECK: %[[elesize:.*]] = getelementptr { {{.*}}, [1 x [3 x i64]] }, ptr %[[arg]], i32 0, i32 1 ! CHECK: %[[esval:.*]] = load i64, ptr %[[elesize]] ! CHECK: %[[mul:.*]] = mul i64 1, %[[esval]] -! CHECK: %[[mul2:.*]] = mul i64 %[[mul]], %[[extval]], !dbg !17 +! CHECK: %[[mul2:.*]] = mul i64 %[[mul]], %[[extval]] ! CHECK: %[[buff:.*]] = call ptr @malloc(i64 %[[mul2]]) ! CHECK: %[[to:.*]] = getelementptr i8, ptr %[[buff]], i64 % ! CHECK: call void @llvm.memmove.p0.p0.i64(ptr %[[to]], ptr %{{.*}}, i64 %{{.*}}, i1 false)