diff --git a/flang/include/flang/Semantics/semantics.h b/flang/include/flang/Semantics/semantics.h --- a/flang/include/flang/Semantics/semantics.h +++ b/flang/include/flang/Semantics/semantics.h @@ -220,6 +220,9 @@ Scope *GetPPCBuiltinTypesScope() { return ppcBuiltinTypesScope_; } const Scope *GetPPCBuiltinsScope() const { return ppcBuiltinsScope_; } + void UseRISCVFortranBuiltinsModule(); + const Scope *GetRISCVBuiltinsScope() const { return riscvBuiltinsScope_; } + // Saves a module file's parse tree so that it remains available // during semantics. parser::Program &SaveParseTree(parser::Program &&); @@ -282,6 +285,8 @@ const Scope *builtinsScope_{nullptr}; // module __Fortran_builtins Scope *ppcBuiltinTypesScope_{nullptr}; // module __Fortran_PPC_types const Scope *ppcBuiltinsScope_{nullptr}; // module __Fortran_PPC_intrinsics + // module __Fortran_RISCV_intrinsics + const Scope *riscvBuiltinsScope_{nullptr}; std::list modFileParseTrees_; std::unique_ptr commonBlockMap_; bool anyDefinedIntrinsicOperator_{false}; diff --git a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp --- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp +++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp @@ -1536,6 +1536,13 @@ {"__ppc_frsqrtes", "llvm.ppc.frsqrtes", genF32F32FuncType, genLibCall}, }; +static constexpr MathOperation riscvMathOperations[] = { + {"__riscv_fmadd", "llvm.fma.f32", genF32F32F32F32FuncType, + genMathOp}, + {"__riscv_fmadd", "llvm.fma.f64", genF64F64F64F64FuncType, + genMathOp}, +}; + // This helper class computes a "distance" between two function types. // The distance measures how many narrowing conversions of actual arguments // and result of "from" must be made in order to use "to" instead of "from". @@ -1684,6 +1691,10 @@ static constexpr RtMap ppcMathOps(ppcMathOperations); static_assert(ppcMathOps.Verify() && "map must be sorted"); +// RISCV +static constexpr RtMap riscvMathOps(riscvMathOperations); +static_assert(riscvMathOps.Verify() && "map must be sorted"); + /// Look for a MathOperation entry specifying how to lower a mathematical /// operation defined by \p name with its result' and operands' types /// specified in the form of a FunctionType \p funcType. @@ -1707,6 +1718,10 @@ if (fir::getTargetTriple(mod).isPPC() && range.first == range.second) { range = ppcMathOps.equal_range(name); } + // Search riscvMathOps only if targetting RISCV arch + if (fir::getTargetTriple(mod).isRISCV() && range.first == range.second) { + range = riscvMathOps.equal_range(name); + } for (auto iter = range.first; iter != range.second && iter; ++iter) { const auto &impl = *iter; auto implType = impl.typeGenerator(builder.getContext()); @@ -1836,7 +1851,8 @@ static bool isIntrinsicModuleProcedure(llvm::StringRef name) { return name.startswith("c_") || name.startswith("compiler_") || - name.startswith("ieee_") || name.startswith("__ppc_"); + name.startswith("ieee_") || name.startswith("__ppc_") || + name.startswith("__riscv_"); } /// Return the generic name of an intrinsic module procedure specific name. diff --git a/flang/lib/Semantics/resolve-names.cpp b/flang/lib/Semantics/resolve-names.cpp --- a/flang/lib/Semantics/resolve-names.cpp +++ b/flang/lib/Semantics/resolve-names.cpp @@ -7313,6 +7313,12 @@ symbol = FindSymbol(*ppcBuiltinScope, name); if (!symbol) symbol = &MakeSymbol(context().globalScope(), name.source, Attrs{}); + } else if (const auto riscvBuiltinScope = + currScope().context().GetRISCVBuiltinsScope()) { + // Check if it is a builtin from the predefined module + symbol = FindSymbol(*riscvBuiltinScope, name); + if (!symbol) + symbol = &MakeSymbol(context().globalScope(), name.source, Attrs{}); } else { symbol = &MakeSymbol(context().globalScope(), name.source, Attrs{}); } diff --git a/flang/lib/Semantics/semantics.cpp b/flang/lib/Semantics/semantics.cpp --- a/flang/lib/Semantics/semantics.cpp +++ b/flang/lib/Semantics/semantics.cpp @@ -482,6 +482,12 @@ } } +void SemanticsContext::UseRISCVFortranBuiltinsModule() { + if (riscvBuiltinsScope_ == nullptr) { + riscvBuiltinsScope_ = GetBuiltinModule("__fortran_riscv_intrinsics"); + } +} + parser::Program &SemanticsContext::SaveParseTree(parser::Program &&tree) { return modFileParseTrees_.emplace_back(std::move(tree)); } @@ -501,7 +507,10 @@ .statement.v.source == "__fortran_ppc_intrinsics" || std::get>( frontModule->value().t) - .statement.v.source == "__fortran_ppc_types")) { + .statement.v.source == "__fortran_ppc_types" || + std::get>( + frontModule->value().t) + .statement.v.source == "__fortran_riscv_intrinsics")) { // Don't try to read the builtins module when we're actually building it. } else { context_.UseFortranBuiltinsModule(); @@ -512,6 +521,9 @@ context_.UsePPCFortranBuiltinTypesModule(); context_.UsePPCFortranBuiltinsModule(); } + if (targetTriple.isRISCV()) { + context_.UseRISCVFortranBuiltinsModule(); + } } } return ValidateLabels(context_, program_) && diff --git a/flang/module/__fortran_riscv_intrinsics.f90 b/flang/module/__fortran_riscv_intrinsics.f90 new file mode 100644 --- /dev/null +++ b/flang/module/__fortran_riscv_intrinsics.f90 @@ -0,0 +1,31 @@ +!===-- module/__fortran_riscv_intrinsics.f90 -------------------------------===! +! +! 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 +! +!===------------------------------------------------------------------------===! + +module __Fortran_RISCV_intrinsics + + private + + ! fmadd + abstract interface + elemental real(4) function func_r4r4r4r4(a, x, y) + real(4), intent(in) :: a, x, y + end function func_r4r4r4r4 + elemental real(8) function func_r8r8r8r8(a, x, y) + real(8), intent(in) :: a, x, y + end function func_r8r8r8r8 + end interface + + procedure(func_r4r4r4r4) :: __riscv_fmadd_r4 + procedure(func_r8r8r8r8) :: __riscv_fmadd_r8 + interface fmadd + procedure :: __riscv_fmadd_r4 + procedure :: __riscv_fmadd_r8 + end interface fmadd + public :: fmadd + +end module __Fortran_RISCV_intrinsics diff --git a/flang/test/Lower/riscv-intrinsics.f90 b/flang/test/Lower/riscv-intrinsics.f90 new file mode 100644 --- /dev/null +++ b/flang/test/Lower/riscv-intrinsics.f90 @@ -0,0 +1,22 @@ +! RUN: bbc -emit-fir %s -outline-intrinsics -o - | FileCheck --check-prefixes="CHECK-FIR" %s +! RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck --check-prefixes="CHECK-LLVMIR" %s +! RUN: %flang -S %s -o - | FileCheck --check-prefixes="CHECK-ASM" %s +! REQUIRES: target=riscv{{.*}} + +! CHECK-LABEL: fmadd_s_test +! CHECK-FIR: fir.call @fir.__riscv_fmadd.f32.f32.f32.f32 +! CHECK-LLVMIR: call contract float @llvm.fma.f32(float %{{[0-9]}}, float %{{[0-9]}}, float %{{[0-9]}}) +! CHECK-ASM: fmadd.s fa{{[0-9]}}, fa{{[0-9]}}, fa{{[0-9]}}, fa{{[0-9]}} +subroutine fmadd_s_test(a, x, y) + real :: a, x, y, z + z = fmadd(a, x, y) +end + +! CHECK-LABEL: fmadd_d_test +! CHECK-FIR: fir.call @fir.__riscv_fmadd.f64.f64.f64.f64 +! CHECK-LLVMIR: call contract double @llvm.fma.f64(double %{{[0-9]}}, double %{{[0-9]}}, double %{{[0-9]}}) +! CHECK-ASM: fmadd.d fa{{[0-9]}}, fa{{[0-9]}}, fa{{[0-9]}}, fa{{[0-9]}} +subroutine fmadd_d_test(a, x, y) + real(8) :: a, x, y, z + z = fmadd(a, x, y) +end diff --git a/flang/tools/f18/CMakeLists.txt b/flang/tools/f18/CMakeLists.txt --- a/flang/tools/f18/CMakeLists.txt +++ b/flang/tools/f18/CMakeLists.txt @@ -10,6 +10,7 @@ "__fortran_type_info" "__fortran_ppc_types" "__fortran_ppc_intrinsics" + "__fortran_riscv_intrinsics" "ieee_arithmetic" "ieee_exceptions" "ieee_features" @@ -33,6 +34,8 @@ set(depends "") elseif(${filename} STREQUAL "__fortran_ppc_intrinsics") set(depends ${FLANG_INTRINSIC_MODULES_DIR}/__fortran_ppc_types.mod) + elseif(${filename} STREQUAL "__fortran_riscv_intrinsics") + set(depends "") else() set(depends ${FLANG_INTRINSIC_MODULES_DIR}/__fortran_builtins.mod) if(NOT ${filename} STREQUAL "__fortran_type_info")