diff --git a/flang/include/flang/Optimizer/Builder/RISCVIntrinsicCall.h b/flang/include/flang/Optimizer/Builder/RISCVIntrinsicCall.h new file mode 100644 --- /dev/null +++ b/flang/include/flang/Optimizer/Builder/RISCVIntrinsicCall.h @@ -0,0 +1,32 @@ +//==-- Builder/RISCVIntrinsicCall.h - lowering of RISCV intrinsics -*-C++-*-==// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef FORTRAN_LOWER_RISCVINTRINSICCALL_H +#define FORTRAN_LOWER_RISCVINTRINSICCALL_H + +#include "flang/Common/static-multimap-view.h" +#include "flang/Optimizer/Builder/IntrinsicCall.h" +#include "mlir/Dialect/Math/IR/Math.h" + +namespace fir { + +struct RISCVIntrinsicLibrary : IntrinsicLibrary { + + // Constructors. + explicit RISCVIntrinsicLibrary(fir::FirOpBuilder &builder, mlir::Location loc) + : IntrinsicLibrary(builder, loc) {} + RISCVIntrinsicLibrary() = delete; + RISCVIntrinsicLibrary(const RISCVIntrinsicLibrary &) = delete; +}; + +std::pair +checkRISCVMathOperationsRange(llvm::StringRef name); + +} // namespace fir + +#endif // FORTRAN_LOWER_RISCVINTRINSICCALL_H 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 @@ -221,6 +221,9 @@ Scope *GetPPCBuiltinTypesScope() { return ppcBuiltinTypesScope_; } const Scope *GetPPCBuiltinsScope() const { return ppcBuiltinsScope_; } + void UseRISCVBuiltinsModule(); + 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 &&); @@ -284,6 +287,8 @@ Scope *ppcBuiltinTypesScope_{nullptr}; // module __Fortran_PPC_types std::optional cudaBuiltinsScope_; // module __CUDA_builtins 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/CMakeLists.txt b/flang/lib/Optimizer/Builder/CMakeLists.txt --- a/flang/lib/Optimizer/Builder/CMakeLists.txt +++ b/flang/lib/Optimizer/Builder/CMakeLists.txt @@ -12,6 +12,7 @@ LowLevelIntrinsics.cpp MutableBox.cpp PPCIntrinsicCall.cpp + RISCVIntrinsicCall.cpp Runtime/Allocatable.cpp Runtime/ArrayConstructor.cpp Runtime/Assign.cpp 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 @@ -21,6 +21,7 @@ #include "flang/Optimizer/Builder/FIRBuilder.h" #include "flang/Optimizer/Builder/MutableBox.h" #include "flang/Optimizer/Builder/PPCIntrinsicCall.h" +#include "flang/Optimizer/Builder/RISCVIntrinsicCall.h" #include "flang/Optimizer/Builder/Runtime/Allocatable.h" #include "flang/Optimizer/Builder/Runtime/Character.h" #include "flang/Optimizer/Builder/Runtime/Command.h" @@ -1182,6 +1183,10 @@ if (fir::getTargetTriple(mod).isPPC() && range.first == range.second) { range = checkPPCMathOperationsRange(name); } + // Search riscvMathOps only if targetting RISCV arch + if (fir::getTargetTriple(mod).isRISCV() && range.first == range.second) { + range = checkRISCVMathOperationsRange(name); + } for (auto iter = range.first; iter != range.second && iter; ++iter) { const auto &impl = *iter; auto implType = impl.typeGenerator(builder.getContext(), builder); @@ -1311,7 +1316,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. @@ -1322,6 +1328,11 @@ llvm::StringRef name = specificName.startswith(builtin) ? specificName.drop_front(builtin.size()) : specificName; + if (name.startswith("__riscv_")) { + if (name.ends_with("_i4") || name.ends_with("_i8")) + return name.drop_back(3); + return name; + } size_t size = name.size(); if (isIntrinsicModuleProcedure(name)) while (isdigit(name[size - 1])) diff --git a/flang/lib/Optimizer/Builder/RISCVIntrinsicCall.cpp b/flang/lib/Optimizer/Builder/RISCVIntrinsicCall.cpp new file mode 100644 --- /dev/null +++ b/flang/lib/Optimizer/Builder/RISCVIntrinsicCall.cpp @@ -0,0 +1,64 @@ +//===-- RISCVIntrinsicCall.cpp --------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +// Helper routines for constructing the FIR dialect of MLIR for RISC-V +// intrinsics. Extensive use of MLIR interfaces and MLIR's coding style +// (https://mlir.llvm.org/getting_started/DeveloperGuide/) is used in this +// module. +// +//===----------------------------------------------------------------------===// + +#include "flang/Optimizer/Builder/RISCVIntrinsicCall.h" +#include "flang/Optimizer/Builder/FIRBuilder.h" +#include "flang/Optimizer/Builder/IntrinsicCall.h" +#include "flang/Optimizer/Builder/MutableBox.h" + +namespace fir { + +// RISCV specific intrinsic handlers. +static constexpr MathOperation riscvMathOperations[] = { + {"__riscv_brev8", "llvm.riscv.brev8.i32", + genFuncType, Ty::Integer<4>>, genLibCall}, + {"__riscv_brev8", "llvm.riscv.brev8.i64", + genFuncType, Ty::Integer<8>>, genLibCall}, + {"__riscv_clmul", "llvm.riscv.clmul.i32", + genFuncType, Ty::Integer<4>, Ty::Integer<4>>, genLibCall}, + {"__riscv_clmul", "llvm.riscv.clmul.i64", + genFuncType, Ty::Integer<8>, Ty::Integer<8>>, genLibCall}, + {"__riscv_clmulh", "llvm.riscv.clmulh.i32", + genFuncType, Ty::Integer<4>, Ty::Integer<4>>, genLibCall}, + {"__riscv_clmulh", "llvm.riscv.clmulh.i64", + genFuncType, Ty::Integer<8>, Ty::Integer<8>>, genLibCall}, + {"__riscv_clmulr", "llvm.riscv.clmulr.i32", + genFuncType, Ty::Integer<4>, Ty::Integer<4>>, genLibCall}, + {"__riscv_clmulr", "llvm.riscv.clmulr.i64", + genFuncType, Ty::Integer<8>, Ty::Integer<8>>, genLibCall}, + {"__riscv_orc_b", "llvm.riscv.orc.b.i32", + genFuncType, Ty::Integer<4>>, genLibCall}, + {"__riscv_orc_b", "llvm.riscv.orc.b.i64", + genFuncType, Ty::Integer<8>>, genLibCall}, + {"__riscv_unzip", "llvm.riscv.unzip.i32", + genFuncType, Ty::Integer<4>>, genLibCall}, + {"__riscv_xperm4", "llvm.riscv.xperm4.i32", + genFuncType, Ty::Integer<4>, Ty::Integer<4>>, genLibCall}, + {"__riscv_xperm4", "llvm.riscv.xperm4.i64", + genFuncType, Ty::Integer<8>, Ty::Integer<8>>, genLibCall}, + {"__riscv_zip", "llvm.riscv.zip.i32", + genFuncType, Ty::Integer<4>>, genLibCall}, +}; + +using RtMap = Fortran::common::StaticMultimapView; +static constexpr RtMap riscvMathOps(riscvMathOperations); +static_assert(riscvMathOps.Verify() && "map must be sorted"); + +std::pair +checkRISCVMathOperationsRange(llvm::StringRef name) { + return riscvMathOps.equal_range(name); +} + +} // namespace fir 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 @@ -7469,6 +7469,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 @@ -499,6 +499,12 @@ } } +void SemanticsContext::UseRISCVBuiltinsModule() { + if (riscvBuiltinsScope_ == nullptr) { + riscvBuiltinsScope_ = GetBuiltinModule("__riscv_intrinsics"); + } +} + parser::Program &SemanticsContext::SaveParseTree(parser::Program &&tree) { return modFileParseTrees_.emplace_back(std::move(tree)); } @@ -515,7 +521,10 @@ .statement.v.source == "__fortran_builtins" || std::get>( frontModule->value().t) - .statement.v.source == "__ppc_types")) { + .statement.v.source == "__ppc_types" || + std::get>( + frontModule->value().t) + .statement.v.source == "__riscv_intrinsics")) { // Don't try to read the builtins module when we're actually building it. } else if (frontModule && std::get>(frontModule->value().t) @@ -531,6 +540,9 @@ context_.UsePPCBuiltinTypesModule(); context_.UsePPCBuiltinsModule(); } + if (targetTriple.isRISCV()) { + context_.UseRISCVBuiltinsModule(); + } } } return ValidateLabels(context_, program_) && diff --git a/flang/module/__riscv_intrinsics.f90 b/flang/module/__riscv_intrinsics.f90 new file mode 100644 --- /dev/null +++ b/flang/module/__riscv_intrinsics.f90 @@ -0,0 +1,96 @@ +!===-- 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 __riscv_intrinsics + + private + + abstract interface + elemental integer(4) function func_i4i4(x) + integer, intent(in) :: x + end function func_i4i4 + elemental integer(8) function func_i8i8(x) + integer(8), intent(in) :: x + end function func_i8i8 + elemental integer(4) function func_i4i4i4(x, y) + integer, intent(in) :: x, y + end function func_i4i4i4 + elemental integer(8) function func_i8i8i8(x, y) + integer(8), intent(in) :: x, y + end function func_i8i8i8 + end interface + + ! brev8 + procedure(func_i4i4) :: __riscv_brev8_i4 + procedure(func_i8i8) :: __riscv_brev8_i8 + interface brev8 + procedure :: __riscv_brev8_i4 + procedure :: __riscv_brev8_i8 + end interface brev8 + public :: brev8 + + ! clmul + procedure(func_i4i4i4) :: __riscv_clmul_i4 + procedure(func_i8i8i8) :: __riscv_clmul_i8 + interface clmul + procedure :: __riscv_clmul_i4 + procedure :: __riscv_clmul_i8 + end interface clmul + public :: clmul + + ! clmulh + procedure(func_i4i4i4) :: __riscv_clmulh_i4 + procedure(func_i8i8i8) :: __riscv_clmulh_i8 + interface clmulh + procedure :: __riscv_clmulh_i4 + procedure :: __riscv_clmulh_i8 + end interface clmulh + public :: clmulh + + ! clmulr + procedure(func_i4i4i4) :: __riscv_clmulr_i4 + procedure(func_i8i8i8) :: __riscv_clmulr_i8 + interface clmulr + procedure :: __riscv_clmulr_i4 + procedure :: __riscv_clmulr_i8 + end interface clmulr + public :: clmulr + + ! orc.b + procedure(func_i4i4) :: __riscv_orc_b_i4 + procedure(func_i8i8) :: __riscv_orc_b_i8 + interface orc_b + procedure :: __riscv_orc_b_i4 + procedure :: __riscv_orc_b_i8 + end interface orc_b + public :: orc_b + + ! unzip + procedure(func_i4i4) :: __riscv_unzip_i4 + interface unzip + procedure :: __riscv_unzip_i4 + end interface unzip + public :: unzip + + ! xperm4 + procedure(func_i4i4i4) :: __riscv_xperm4_i4 + procedure(func_i8i8i8) :: __riscv_xperm4_i8 + interface xperm4 + procedure :: __riscv_xperm4_i4 + procedure :: __riscv_xperm4_i8 + end interface xperm4 + public :: xperm4 + + ! zip + procedure(func_i4i4) :: __riscv_zip_i4 + interface zip + procedure :: __riscv_zip_i4 + end interface zip + public :: zip + +end module __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,115 @@ +! RUN: bbc -emit-fir %s -outline-intrinsics -o - | FileCheck --check-prefix="CHECK-FIR" %s +! RUN: %flang_fc1 -emit-llvm %s -o - | FileCheck --check-prefix="CHECK-LLVMIR" %s +! REQUIRES: target=riscv{{.*}} + +! CHECK-LABEL: brev8_i32_test +! CHECK-FIR: fir.call @fir.__riscv_brev8.i32.i32 +! CHECK-LLVMIR: call i32 @llvm.riscv.brev8.i32(i32 %{{[0-9]}}) +subroutine brev8_i32_test(x) + integer :: x, r + r = brev8(x) +end + +! CHECK-LABEL: brev8_i64_test +! CHECK-FIR: fir.call @fir.__riscv_brev8.i64.i64 +! CHECK-LLVMIR: call i64 @llvm.riscv.brev8.i64(i64 %{{[0-9]}}) +subroutine brev8_i64_test(x) + integer(8) :: x, r + r = brev8(x) +end + +! CHECK-LABEL: clmul_i32_test +! CHECK-FIR: fir.call @fir.__riscv_clmul.i32.i32.i32 +! CHECK-LLVMIR: call i32 @llvm.riscv.clmul.i32(i32 %{{[0-9]}}, i32 %{{[0-9]}}) +subroutine clmul_i32_test(x, y) + integer :: x, y, r + r = clmul(x, y) +end + +! CHECK-LABEL: clmul_i64_test +! CHECK-FIR: fir.call @fir.__riscv_clmul.i64.i64.i64 +! CHECK-LLVMIR: call i64 @llvm.riscv.clmul.i64(i64 %{{[0-9]}}, i64 %{{[0-9]}}) +subroutine clmul_i64_test(x, y) + integer(8) :: x, y, r + r = clmul(x, y) +end + +! CHECK-LABEL: clmulh_i32_test +! CHECK-FIR: fir.call @fir.__riscv_clmulh.i32.i32 +! CHECK-LLVMIR: call i32 @llvm.riscv.clmulh.i32(i32 %{{[0-9]}}, i32 %{{[0-9]}}) +subroutine clmulh_i32_test(x, y) + integer :: x, y, r + r = clmulh(x, y) +end + +! CHECK-LABEL: clmulh_i64_test +! CHECK-FIR: fir.call @fir.__riscv_clmulh.i64.i64 +! CHECK-LLVMIR: call i64 @llvm.riscv.clmulh.i64(i64 %{{[0-9]}}, i64 %{{[0-9]}}) +subroutine clmulh_i64_test(x, y) + integer(8) :: x, y, r + r = clmulh(x, y) +end + +! CHECK-LABEL: clmulr_i32_test +! CHECK-FIR: fir.call @fir.__riscv_clmulr.i32.i32.i32 +! CHECK-LLVMIR: call i32 @llvm.riscv.clmulr.i32(i32 %{{[0-9]}}, i32 %{{[0-9]}}) +subroutine clmulr_i32_test(x, y) + integer :: x, y, r + r = clmulr(x, y) +end + +! CHECK-LABEL: clmulr_i64_test +! CHECK-FIR: fir.call @fir.__riscv_clmulr.i64.i64.i64 +! CHECK-LLVMIR: call i64 @llvm.riscv.clmulr.i64(i64 %{{[0-9]}}, i64 %{{[0-9]}}) +subroutine clmulr_i64_test(x, y) + integer(8) :: x, y, r + r = clmulr(x, y) +end + +! CHECK-LABEL: orc_b_i32_test +! CHECK-FIR: fir.call @fir.__riscv_orc_b.i32.i32 +! CHECK-LLVMIR: call i32 @llvm.riscv.orc.b.i32(i32 %{{[0-9]}}) +subroutine orc_b_i32_test(x) + integer :: x, r + r = orc_b(x) +end + +! CHECK-LABEL: orc_b_i64_test +! CHECK-FIR: fir.call @fir.__riscv_orc_b.i64.i64 +! CHECK-LLVMIR: call i64 @llvm.riscv.orc.b.i64(i64 %{{[0-9]}}) +subroutine orc_b_i64_test(x) + integer(8) :: x, r + r = orc_b(x) +end + +! CHECK-LABEL: unzip_i32_test +! CHECK-FIR: fir.call @fir.__riscv_unzip.i32.i32 +! CHECK-LLVMIR: call i32 @llvm.riscv.unzip.i32(i32 %{{[0-9]}}) +subroutine unzip_i32_test(x) + integer :: x, r + r = unzip(x) +end + +! CHECK-LABEL: xperm4_i32_test +! CHECK-FIR: fir.call @fir.__riscv_xperm4.i32.i32.i32 +! CHECK-LLVMIR: call i32 @llvm.riscv.xperm4.i32(i32 %{{[0-9]}}, i32 %{{[0-9]}}) +subroutine xperm4_i32_test(x, y) + integer :: x, y, r + r = xperm4(x, y) +end + +! CHECK-LABEL: xperm4_i64_test +! CHECK-FIR: fir.call @fir.__riscv_xperm4.i64.i64.i64 +! CHECK-LLVMIR: call i64 @llvm.riscv.xperm4.i64(i64 %{{[0-9]}}, i64 %{{[0-9]}}) +subroutine xperm4_i64_test(x, y) + integer(8) :: x, y, r + r = xperm4(x, y) +end + +! CHECK-LABEL: zip_i32_test +! CHECK-FIR: fir.call @fir.__riscv_zip.i32.i32 +! CHECK-LLVMIR: call i32 @llvm.riscv.zip.i32(i32 %{{[0-9]}}) +subroutine zip_i32_test(x) + integer :: x, r + r = zip(x) +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" "__ppc_types" "__ppc_intrinsics" + "__riscv_intrinsics" "__cuda_builtins" "ieee_arithmetic" "ieee_exceptions" @@ -34,6 +35,8 @@ set(depends "") elseif(${filename} STREQUAL "__ppc_intrinsics") set(depends ${FLANG_INTRINSIC_MODULES_DIR}/__ppc_types.mod) + elseif(${filename} STREQUAL "__riscv_intrinsics") + set(depends "") else() set(depends ${FLANG_INTRINSIC_MODULES_DIR}/__fortran_builtins.mod) if(NOT ${filename} STREQUAL "__fortran_type_info")