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 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 &&); @@ -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/IntrinsicCall.cpp b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp --- a/flang/lib/Optimizer/Builder/IntrinsicCall.cpp +++ b/flang/lib/Optimizer/Builder/IntrinsicCall.cpp @@ -686,6 +686,12 @@ return mlir::FunctionType::get(context, {itype, ftype}, {ftype}); } +template +static mlir::FunctionType genIntIntFuncType(mlir::MLIRContext *context) { + auto itype = mlir::IntegerType::get(context, Bits); + return mlir::FunctionType::get(context, {itype}, {itype}); +} + template static mlir::FunctionType genIntIntIntFuncType(mlir::MLIRContext *context) { auto itype = mlir::IntegerType::get(context, Bits); @@ -1137,6 +1143,36 @@ {"__ppc_frsqrtes", "llvm.ppc.frsqrtes", genF32F32FuncType, genLibCall}, }; +static constexpr MathOperation riscvMathOperations[] = { + {"__riscv_brev8", "llvm.riscv.brev8.i32", genIntIntFuncType<32>, + genLibCall}, + {"__riscv_brev8", "llvm.riscv.brev8.i64", genIntIntFuncType<64>, + genLibCall}, + {"__riscv_clmul", "llvm.riscv.clmul.i32", genIntIntIntFuncType<32>, + genLibCall}, + {"__riscv_clmul", "llvm.riscv.clmul.i64", genIntIntIntFuncType<64>, + genLibCall}, + {"__riscv_clmulh", "llvm.riscv.clmulh.i32", genIntIntIntFuncType<32>, + genLibCall}, + {"__riscv_clmulh", "llvm.riscv.clmulh.i64", genIntIntIntFuncType<64>, + genLibCall}, + {"__riscv_clmulr", "llvm.riscv.clmulr.i32", genIntIntIntFuncType<32>, + genLibCall}, + {"__riscv_clmulr", "llvm.riscv.clmulr.i64", genIntIntIntFuncType<64>, + genLibCall}, + {"__riscv_orc_b", "llvm.riscv.orc.b.i32", genIntIntFuncType<32>, + genLibCall}, + {"__riscv_orc_b", "llvm.riscv.orc.b.i64", genIntIntFuncType<64>, + genLibCall}, + {"__riscv_unzip", "llvm.riscv.unzip.i32", genIntIntFuncType<32>, + genLibCall}, + {"__riscv_xperm4", "llvm.riscv.xperm4.i32", genIntIntIntFuncType<32>, + genLibCall}, + {"__riscv_xperm4", "llvm.riscv.xperm4.i64", genIntIntIntFuncType<64>, + genLibCall}, + {"__riscv_zip", "llvm.riscv.zip.i32", genIntIntFuncType<32>, genLibCall}, +}; + // 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". @@ -1285,6 +1321,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. @@ -1308,6 +1348,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()); @@ -1437,7 +1481,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. @@ -1448,6 +1493,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/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 @@ -7462,6 +7462,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::UseRISCVFortranBuiltinsModule() { + if (riscvBuiltinsScope_ == nullptr) { + riscvBuiltinsScope_ = GetBuiltinModule("__fortran_riscv_intrinsics"); + } +} + parser::Program &SemanticsContext::SaveParseTree(parser::Program &&tree) { return modFileParseTrees_.emplace_back(std::move(tree)); } @@ -518,7 +524,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(); @@ -529,6 +538,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,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 __Fortran_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 __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,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" "__fortran_ppc_types" "__fortran_ppc_intrinsics" + "__fortran_riscv_intrinsics" "__cuda_builtins" "ieee_arithmetic" "ieee_exceptions" @@ -34,6 +35,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")