[flang-commits] [flang] 9471637 - [flang][hlfir] Add hlfir.dot_product intrinsic
Jacob Crawley via flang-commits
flang-commits at lists.llvm.org
Wed Jun 7 04:45:55 PDT 2023
Author: Jacob Crawley
Date: 2023-06-07T11:42:14Z
New Revision: 9471637f3c7c67b2c6a5badac9df5cd48b605dc7
URL: https://github.com/llvm/llvm-project/commit/9471637f3c7c67b2c6a5badac9df5cd48b605dc7
DIFF: https://github.com/llvm/llvm-project/commit/9471637f3c7c67b2c6a5badac9df5cd48b605dc7.diff
LOG: [flang][hlfir] Add hlfir.dot_product intrinsic
Adds a new HLFIR operation for the DOT_PRODUCT intrinsic according to
the design set out in flang/docs/HighLevel.md. This patch includes all
the necessary changes to create a new HLFIR operation and lower it into
the fir runtime call.
Differential Revision: https://reviews.llvm.org/D152252
Added:
flang/test/HLFIR/dot_product-lowering.fir
flang/test/HLFIR/dot_product.fir
flang/test/Lower/HLFIR/dot_product.f90
Modified:
flang/include/flang/Optimizer/HLFIR/HLFIROps.td
flang/lib/Lower/ConvertCall.cpp
flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIRIntrinsics.cpp
flang/test/HLFIR/invalid.fir
Removed:
################################################################################
diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
index 142a70c639127..2dd85a2c5c181 100644
--- a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
+++ b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
@@ -430,6 +430,29 @@ def hlfir_SumOp : hlfir_Op<"sum", [AttrSizedOperandSegments,
let hasVerifier = 1;
}
+def hlifr_DotProductOp : hlfir_Op<"dot_product",
+ [DeclareOpInterfaceMethods<ArithFastMathInterface>]> {
+ let summary = "DOT_PRODUCT transformational intrinsic";
+ let description = [{
+ Dot product of two vectors
+ }];
+
+ let arguments = (ins
+ AnyFortranNumericalOrLogicalArrayObject:$lhs,
+ AnyFortranNumericalOrLogicalArrayObject:$rhs,
+ DefaultValuedAttr<Arith_FastMathAttr,
+ "::mlir::arith::FastMathFlags::none">:$fastmath
+ );
+
+ let results = (outs AnyFortranValue);
+
+ let assemblyFormat = [{
+ $lhs $rhs attr-dict `:` functional-type(operands, results)
+ }];
+
+ let hasVerifier = 1;
+}
+
def hlfir_MatmulOp : hlfir_Op<"matmul",
[DeclareOpInterfaceMethods<ArithFastMathInterface>]> {
let summary = "MATMUL transformational intrinsic";
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index 66af19b94e78d..604291cdbec6d 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -1488,6 +1488,15 @@ genHLFIRIntrinsicRefCore(PreparedActualArguments &loweredActuals,
return buildReductionIntrinsic(loweredActuals, loc, builder, callContext,
buildAllOperation, false);
}
+ if (intrinsicName == "dot_product") {
+ llvm::SmallVector<mlir::Value> operands = getOperandVector(loweredActuals);
+ mlir::Type resultTy =
+ computeResultType(operands[0], *callContext.resultType);
+ hlfir::DotProductOp dotProductOp = builder.create<hlfir::DotProductOp>(
+ loc, resultTy, operands[0], operands[1]);
+
+ return {hlfir::EntityWithAttributes{dotProductOp.getResult()}};
+ }
// TODO add hlfir operations for other transformational intrinsics here
diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
index adf8b72993e4c..c094b66338512 100644
--- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
+++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
@@ -669,6 +669,53 @@ mlir::LogicalResult hlfir::SumOp::verify() {
return verifyNumericalReductionOp<hlfir::SumOp *>(this);
}
+//===----------------------------------------------------------------------===//
+// DotProductOp
+//===----------------------------------------------------------------------===//
+
+mlir::LogicalResult hlfir::DotProductOp::verify() {
+ mlir::Value lhs = getLhs();
+ mlir::Value rhs = getRhs();
+ fir::SequenceType lhsTy =
+ hlfir::getFortranElementOrSequenceType(lhs.getType())
+ .cast<fir::SequenceType>();
+ fir::SequenceType rhsTy =
+ hlfir::getFortranElementOrSequenceType(rhs.getType())
+ .cast<fir::SequenceType>();
+ llvm::ArrayRef<int64_t> lhsShape = lhsTy.getShape();
+ llvm::ArrayRef<int64_t> rhsShape = rhsTy.getShape();
+ std::size_t lhsRank = lhsShape.size();
+ std::size_t rhsRank = rhsShape.size();
+ mlir::Type lhsEleTy = lhsTy.getEleTy();
+ mlir::Type rhsEleTy = rhsTy.getEleTy();
+ mlir::Type resultTy = getResult().getType();
+
+ if ((lhsRank != 1) || (rhsRank != 1))
+ return emitOpError("both arrays must have rank 1");
+
+ int64_t lhsSize = lhsShape[0];
+ int64_t rhsSize = rhsShape[0];
+
+ if (lhsSize != rhsSize)
+ return emitOpError("both arrays must have the same size");
+
+ if (mlir::isa<fir::LogicalType>(lhsEleTy) !=
+ mlir::isa<fir::LogicalType>(rhsEleTy))
+ return emitOpError("if one array is logical, so should the other be");
+
+ if (mlir::isa<fir::LogicalType>(lhsEleTy) !=
+ mlir::isa<fir::LogicalType>(resultTy))
+ return emitOpError("the result type should be a logical only if the "
+ "argument types are logical");
+
+ if (!hlfir::isFortranScalarNumericalType(resultTy) &&
+ !mlir::isa<fir::LogicalType>(resultTy))
+ return emitOpError(
+ "the result must be of scalar numerical or logical type");
+
+ return mlir::success();
+}
+
//===----------------------------------------------------------------------===//
// MatmulOp
//===----------------------------------------------------------------------===//
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIRIntrinsics.cpp b/flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIRIntrinsics.cpp
index 0ffb2ac9ca0cb..ac7eafc862fd6 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIRIntrinsics.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIRIntrinsics.cpp
@@ -277,6 +277,38 @@ struct MatmulOpConversion : public HlfirIntrinsicConversion<hlfir::MatmulOp> {
}
};
+struct DotProductOpConversion
+ : public HlfirIntrinsicConversion<hlfir::DotProductOp> {
+ using HlfirIntrinsicConversion<hlfir::DotProductOp>::HlfirIntrinsicConversion;
+
+ mlir::LogicalResult
+ matchAndRewrite(hlfir::DotProductOp dotProduct,
+ mlir::PatternRewriter &rewriter) const override {
+ fir::KindMapping kindMapping{rewriter.getContext()};
+ fir::FirOpBuilder builder{rewriter, kindMapping};
+ const mlir::Location &loc = dotProduct->getLoc();
+
+ mlir::Value lhs = dotProduct.getLhs();
+ mlir::Value rhs = dotProduct.getRhs();
+ llvm::SmallVector<IntrinsicArgument, 2> inArgs;
+ inArgs.push_back({lhs, lhs.getType()});
+ inArgs.push_back({rhs, rhs.getType()});
+
+ auto *argLowering = fir::getIntrinsicArgumentLowering("dot_product");
+ llvm::SmallVector<fir::ExtendedValue, 2> args =
+ lowerArguments(dotProduct, inArgs, rewriter, argLowering);
+
+ mlir::Type scalarResultType =
+ hlfir::getFortranElementType(dotProduct.getType());
+
+ auto [resultExv, mustBeFreed] = fir::genIntrinsicCall(
+ builder, loc, "dot_product", scalarResultType, args);
+
+ processReturnValue(dotProduct, resultExv, mustBeFreed, builder, rewriter);
+ return mlir::success();
+ }
+};
+
class TransposeOpConversion
: public HlfirIntrinsicConversion<hlfir::TransposeOp> {
using HlfirIntrinsicConversion<hlfir::TransposeOp>::HlfirIntrinsicConversion;
@@ -356,14 +388,15 @@ class LowerHLFIRIntrinsics
mlir::RewritePatternSet patterns(context);
patterns.insert<MatmulOpConversion, MatmulTransposeOpConversion,
AllOpConversion, AnyOpConversion, SumOpConversion,
- ProductOpConversion, TransposeOpConversion>(context);
+ ProductOpConversion, TransposeOpConversion,
+ DotProductOpConversion>(context);
mlir::ConversionTarget target(*context);
target.addLegalDialect<mlir::BuiltinDialect, mlir::arith::ArithDialect,
mlir::func::FuncDialect, fir::FIROpsDialect,
hlfir::hlfirDialect>();
target.addIllegalOp<hlfir::MatmulOp, hlfir::MatmulTransposeOp, hlfir::SumOp,
hlfir::ProductOp, hlfir::TransposeOp, hlfir::AnyOp,
- hlfir::AllOp>();
+ hlfir::AllOp, hlfir::DotProductOp>();
target.markUnknownOpDynamicallyLegal(
[](mlir::Operation *) { return true; });
if (mlir::failed(
diff --git a/flang/test/HLFIR/dot_product-lowering.fir b/flang/test/HLFIR/dot_product-lowering.fir
new file mode 100644
index 0000000000000..efc113087fea0
--- /dev/null
+++ b/flang/test/HLFIR/dot_product-lowering.fir
@@ -0,0 +1,80 @@
+// Test hlfir.matmul operation lowering to fir runtime call
+// RUN: fir-opt %s -lower-hlfir-intrinsics | FileCheck %s
+
+func.func @_QPdot_product1(%arg0: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "lhs"}, %arg1: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "rhs"}, %arg2: !fir.ref<i32> {fir.bindc_name = "res"}) {
+ %0:2 = hlfir.declare %arg0 {uniq_name = "_QFdot_product1Elhs"} : (!fir.box<!fir.array<?xi32>>) -> (!fir.box<!fir.array<?xi32>>, !fir.box<!fir.array<?xi32>>)
+ %1:2 = hlfir.declare %arg2 {uniq_name = "_QFdot_product1Eres"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %2:2 = hlfir.declare %arg1 {uniq_name = "_QFdot_product1Erhs"} : (!fir.box<!fir.array<?xi32>>) -> (!fir.box<!fir.array<?xi32>>, !fir.box<!fir.array<?xi32>>)
+ %3 = hlfir.dot_product %0#0 %2#0 {fastmath = #arith.fastmath<contract>} : (!fir.box<!fir.array<?xi32>>, !fir.box<!fir.array<?xi32>>) -> i32
+ hlfir.assign %3 to %1#0 : i32, !fir.ref<i32>
+ return
+}
+// CHECK-LABEL: func.func @_QPdot_product1(
+// CHECK: %[[ARG0:.*]]: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "lhs"}
+// CHECK: %[[ARG1:.*]]: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "rhs"}
+// CHECK: %[[ARG2:.*]]: !fir.ref<i32> {fir.bindc_name = "res"}
+// CHECK-DAG: %[[LHS_VAR:.*]]:2 = hlfir.declare %[[ARG0]]
+// CHECK-DAG: %[[RHS_VAR:.*]]:2 = hlfir.declare %[[ARG1]]
+// CHECK-DAG: %[[RES_VAR:.*]]:2 = hlfir.declare %[[ARG2]]
+
+// CHECK-DAG: %[[LHS_ARG:.*]] = fir.convert %[[LHS_VAR]]#1 : (!fir.box<!fir.array<?xi32>>) -> !fir.box<none>
+// CHECK-DAG: %[[RHS_ARG:.*]] = fir.convert %[[RHS_VAR]]#1 : (!fir.box<!fir.array<?xi32>>) -> !fir.box<none>
+
+// CHECK: %[[NONE:.*]] = fir.call @_FortranADotProductInteger4(%[[LHS_ARG]], %[[RHS_ARG]], %[[LOC_STR:.*]], %[[LOC_N:.*]])
+// CHECK-NEXT: hlfir.assign %[[NONE]] to %[[RES_VAR]]#0 : i32, !fir.ref<i32>
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+
+func.func @_QPdot_product2(%arg0: !fir.box<!fir.array<?x!fir.logical<4>>> {fir.bindc_name = "lhs"}, %arg1: !fir.box<!fir.array<?x!fir.logical<4>>> {fir.bindc_name = "rhs"}, %arg2: !fir.ref<!fir.logical<4>> {fir.bindc_name = "res"}) {
+ %0:2 = hlfir.declare %arg0 {uniq_name = "_QFdot_product2Elhs"} : (!fir.box<!fir.array<?x!fir.logical<4>>>) -> (!fir.box<!fir.array<?x!fir.logical<4>>>, !fir.box<!fir.array<?x!fir.logical<4>>>)
+ %1:2 = hlfir.declare %arg2 {uniq_name = "_QFdot_product2Eres"} : (!fir.ref<!fir.logical<4>>) -> (!fir.ref<!fir.logical<4>>, !fir.ref<!fir.logical<4>>)
+ %2:2 = hlfir.declare %arg1 {uniq_name = "_QFdot_product2Erhs"} : (!fir.box<!fir.array<?x!fir.logical<4>>>) -> (!fir.box<!fir.array<?x!fir.logical<4>>>, !fir.box<!fir.array<?x!fir.logical<4>>>)
+ %3 = hlfir.dot_product %0#0 %2#0 {fastmath = #arith.fastmath<contract>} : (!fir.box<!fir.array<?x!fir.logical<4>>>, !fir.box<!fir.array<?x!fir.logical<4>>>) -> !fir.logical<4>
+ hlfir.assign %3 to %1#0 : !fir.logical<4>, !fir.ref<!fir.logical<4>>
+ return
+}
+// CHECK-LABEL: func.func @_QPdot_product2(
+// CHECK: %[[ARG0:.*]]: !fir.box<!fir.array<?x!fir.logical<4>>> {fir.bindc_name = "lhs"}
+// CHECK: %[[ARG1:.*]]: !fir.box<!fir.array<?x!fir.logical<4>>> {fir.bindc_name = "rhs"}
+// CHECK: %[[ARG2:.*]]: !fir.ref<!fir.logical<4>> {fir.bindc_name = "res"}
+// CHECK-DAG: %[[LHS_VAR:.*]]:2 = hlfir.declare %[[ARG0]]
+// CHECK-DAG: %[[RHS_VAR:.*]]:2 = hlfir.declare %[[ARG1]]
+// CHECK-DAG: %[[RES_VAR:.*]]:2 = hlfir.declare %[[ARG2]]
+
+// CHECK-DAG: %[[LHS_ARG:.*]] = fir.convert %[[LHS_VAR]]#1 : (!fir.box<!fir.array<?x!fir.logical<4>>>) -> !fir.box<none>
+// CHECK-DAG: %[[RHS_ARG:.*]] = fir.convert %[[RHS_VAR]]#1 : (!fir.box<!fir.array<?x!fir.logical<4>>>) -> !fir.box<none>
+
+// CHECK: %[[NONE:.*]] = fir.call @_FortranADotProductLogical(%[[LHS_ARG]], %[[RHS_ARG]], %[[LOC_STR:.*]], %[[LOC_N:.*]])
+// CHECK-NEXT: hlfir.assign %[[NONE]] to %[[RES_VAR]]#0 : i1, !fir.ref<!fir.logical<4>>
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+
+ func.func @_QPdot_product3(%arg0: !fir.ref<!fir.array<5xi32>> {fir.bindc_name = "lhs"}, %arg1: !fir.ref<!fir.array<5xi32>> {fir.bindc_name = "rhs"}, %arg2: !fir.ref<i32> {fir.bindc_name = "res"}) {
+ %c5 = arith.constant 5 : index
+ %0 = fir.shape %c5 : (index) -> !fir.shape<1>
+ %1:2 = hlfir.declare %arg0(%0) {uniq_name = "_QFdot_product3Elhs"} : (!fir.ref<!fir.array<5xi32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<5xi32>>, !fir.ref<!fir.array<5xi32>>)
+ %2:2 = hlfir.declare %arg2 {uniq_name = "_QFdot_product3Eres"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+ %c5_0 = arith.constant 5 : index
+ %3 = fir.shape %c5_0 : (index) -> !fir.shape<1>
+ %4:2 = hlfir.declare %arg1(%3) {uniq_name = "_QFdot_product3Erhs"} : (!fir.ref<!fir.array<5xi32>>, !fir.shape<1>) -> (!fir.ref<!fir.array<5xi32>>, !fir.ref<!fir.array<5xi32>>)
+ %5 = hlfir.dot_product %1#0 %4#0 {fastmath = #arith.fastmath<contract>} : (!fir.ref<!fir.array<5xi32>>, !fir.ref<!fir.array<5xi32>>) -> i32
+ hlfir.assign %5 to %2#0 : i32, !fir.ref<i32>
+ return
+}
+// CHECK-LABEL: func.func @_QPdot_product3(
+// CHECK: %[[ARG0:.*]]: !fir.ref<!fir.array<5xi32>> {fir.bindc_name = "lhs"}
+// CHECK: %[[ARG1:.*]]: !fir.ref<!fir.array<5xi32>> {fir.bindc_name = "rhs"}
+// CHECK: %[[ARG2:.*]]: !fir.ref<i32> {fir.bindc_name = "res"}
+// CHECK-DAG: %[[LHS_VAR:.*]]:2 = hlfir.declare %[[ARG0]]
+// CHECK-DAG: %[[RHS_VAR:.*]]:2 = hlfir.declare %[[ARG1]]
+// CHECK-DAG: %[[RES_VAR:.*]]:2 = hlfir.declare %[[ARG2]]
+
+// CHECK-DAG: %[[LHS_BOX:.*]] = fir.embox %[[LHS_VAR]]#1
+// CHECK-DAG: %[[RHS_BOX:.*]] = fir.embox %[[RHS_VAR]]#1
+// CHECK-DAG: %[[LHS_ARG:.*]] = fir.convert %[[LHS_BOX]] : (!fir.box<!fir.array<5xi32>>) -> !fir.box<none>
+// CHECK-DAG: %[[RHS_ARG:.*]] = fir.convert %[[RHS_BOX]] : (!fir.box<!fir.array<5xi32>>) -> !fir.box<none>
+
+// CHECK: %[[NONE:.*]] = fir.call @_FortranADotProductInteger4(%[[LHS_ARG]], %[[RHS_ARG]], %[[LOC_STR:.*]], %[[LOC_N:.*]])
+// CHECK-NEXT: hlfir.assign %[[NONE]] to %[[RES_VAR]]#0 : i32, !fir.ref<i32>
+// CHECK-NEXT: return
+// CHECK-NEXT: }
\ No newline at end of file
diff --git a/flang/test/HLFIR/dot_product.fir b/flang/test/HLFIR/dot_product.fir
new file mode 100644
index 0000000000000..78b293eeff5c8
--- /dev/null
+++ b/flang/test/HLFIR/dot_product.fir
@@ -0,0 +1,51 @@
+// Test hlfir.dot_product operation parse, verify (no errors), and unparse
+
+// RUN: fir-opt %s | fir-opt | FileCheck %s
+
+// arguments are expressions of known shape
+func.func @dot_product0(%arg0: !hlfir.expr<2xi32>, %arg1: !hlfir.expr<2xi32>) {
+ %res = hlfir.dot_product %arg0 %arg1 : (!hlfir.expr<2xi32>, !hlfir.expr<2xi32>) -> i32
+ return
+}
+// CHECK-LABEL: func.func @dot_product0
+// CHECK: %[[ARG0:.*]]: !hlfir.expr<2xi32>,
+// CHECK: %[[ARG1:.*]]: !hlfir.expr<2xi32>
+// CHECK-NEXT: %[[RES:.*]] = hlfir.dot_product %[[ARG0]] %[[ARG1]] : (!hlfir.expr<2xi32>, !hlfir.expr<2xi32>) -> i32
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+
+// arguments are expressions of assumed shape
+func.func @dot_product1(%arg0: !hlfir.expr<?xi32>, %arg1: !hlfir.expr<?xi32>) {
+ %res = hlfir.dot_product %arg0 %arg1 : (!hlfir.expr<?xi32>, !hlfir.expr<?xi32>) -> i32
+ return
+}
+// CHECK-LABEL: func.func @dot_product1
+// CHECK: %[[ARG0:.*]]: !hlfir.expr<?xi32>,
+// CHECK: %[[ARG1:.*]]: !hlfir.expr<?xi32>
+// CHECK-NEXT: %[[RES:.*]] = hlfir.dot_product %[[ARG0]] %[[ARG1]] : (!hlfir.expr<?xi32>, !hlfir.expr<?xi32>) -> i32
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+
+// arguments are boxed arrays
+func.func @dot_product2(%arg0: !fir.box<!fir.array<2xi32>>, %arg1: !fir.box<!fir.array<2xi32>>) {
+ %res = hlfir.dot_product %arg0 %arg1 : (!fir.box<!fir.array<2xi32>>, !fir.box<!fir.array<2xi32>>) -> i32
+ return
+}
+// CHECK-LABEL: func.func @dot_product2
+// CHECK: %[[ARG0:.*]]: !fir.box<!fir.array<2xi32>>,
+// CHECK: %[[ARG1:.*]]: !fir.box<!fir.array<2xi32>>
+// CHECK-NEXT: %[[RES:.*]] = hlfir.dot_product %[[ARG0]] %[[ARG1]] : (!fir.box<!fir.array<2xi32>>, !fir.box<!fir.array<2xi32>>) -> i32
+// CHECK-NEXT: return
+// CHECK-NEXT: }
+
+// arguments are logical
+func.func @dot_product3(%arg0: !fir.box<!fir.array<2x!fir.logical<4>>>, %arg1: !fir.box<!fir.array<2x!fir.logical<4>>>) {
+ %res = hlfir.dot_product %arg0 %arg1 : (!fir.box<!fir.array<2x!fir.logical<4>>>, !fir.box<!fir.array<2x!fir.logical<4>>>) -> !fir.logical<4>
+ return
+}
+// CHECK-LABEL: func.func @dot_product3
+// CHECK: %[[ARG0:.*]]: !fir.box<!fir.array<2x!fir.logical<4>>>,
+// CHECK: %[[ARG1:.*]]: !fir.box<!fir.array<2x!fir.logical<4>>>
+// CHECK-NEXT: %[[RES:.*]] = hlfir.dot_product %[[ARG0]] %[[ARG1]] : (!fir.box<!fir.array<2x!fir.logical<4>>>, !fir.box<!fir.array<2x!fir.logical<4>>>) -> !fir.logical<4>
+// CHECK-NEXT: return
+// CHECK-NEXT: }
\ No newline at end of file
diff --git a/flang/test/HLFIR/invalid.fir b/flang/test/HLFIR/invalid.fir
index 8dc5679346bc1..01bccdf80428b 100644
--- a/flang/test/HLFIR/invalid.fir
+++ b/flang/test/HLFIR/invalid.fir
@@ -508,6 +508,41 @@ func.func @bad_matmul8(%arg0: !hlfir.expr<2xi32>, %arg1: !hlfir.expr<2x3xi32>) {
return
}
+// -----
+func.func @bad_dot_product1(%arg0: !hlfir.expr<2xi32>, %arg1: !hlfir.expr<2x3xi32>) {
+ // expected-error at +1 {{'hlfir.dot_product' op both arrays must have rank 1}}
+ %0 = hlfir.dot_product %arg0 %arg1 : (!hlfir.expr<2xi32>, !hlfir.expr<2x3xi32>) -> i32
+ return
+}
+
+// -----
+func.func @bad_dot_product2(%arg0: !hlfir.expr<2xi32>, %arg1: !hlfir.expr<3xi32>) {
+ // expected-error at +1 {{'hlfir.dot_product' op both arrays must have the same size}}
+ %0 = hlfir.dot_product %arg0 %arg1 : (!hlfir.expr<2xi32>, !hlfir.expr<3xi32>) -> i32
+ return
+}
+
+// -----
+func.func @bad_dot_product3(%arg0: !hlfir.expr<2xi32>, %arg1: !hlfir.expr<2x!fir.logical<4>>) {
+ // expected-error at +1 {{'hlfir.dot_product' op if one array is logical, so should the other be}}
+ %0 = hlfir.dot_product %arg0 %arg1 : (!hlfir.expr<2xi32>, !hlfir.expr<2x!fir.logical<4>>) -> i32
+ return
+}
+
+// -----
+func.func @bad_dot_product4(%arg0: !hlfir.expr<2xi32>, %arg1: !hlfir.expr<2xi32>) {
+ // expected-error at +1 {{'hlfir.dot_product' op the result type should be a logical only if the argument types are logical}}
+ %0 = hlfir.dot_product %arg0 %arg1 : (!hlfir.expr<2xi32>, !hlfir.expr<2xi32>) -> !fir.logical<4>
+ return
+}
+
+// -----
+func.func @bad_dot_product5(%arg0: !hlfir.expr<2xi32>, %arg1: !hlfir.expr<2xi32>) {
+ // expected-error at +1 {{'hlfir.dot_product' op the result must be of scalar numerical or logical type}}
+ %0 = hlfir.dot_product %arg0 %arg1 : (!hlfir.expr<2xi32>, !hlfir.expr<2xi32>) -> !hlfir.expr<i32>
+ return
+}
+
// -----
func.func @bad_transpose1(%arg0: !hlfir.expr<2xi32>) {
// expected-error at +1 {{'hlfir.transpose' op input and output arrays should have rank 2}}
diff --git a/flang/test/Lower/HLFIR/dot_product.f90 b/flang/test/Lower/HLFIR/dot_product.f90
new file mode 100644
index 0000000000000..94ee46d63a288
--- /dev/null
+++ b/flang/test/Lower/HLFIR/dot_product.f90
@@ -0,0 +1,53 @@
+! Test lowering of DOT_PRODUCT intrinsic to HLFIR
+! RUN: bbc -emit-hlfir -o - %s 2>&1 | FileCheck %s
+
+! dot product with numerical arguments
+subroutine dot_product1(lhs, rhs, res)
+ integer lhs(:), rhs(:), res
+ res = DOT_PRODUCT(lhs,rhs)
+end subroutine
+! CHECK-LABEL: func.func @_QPdot_product1
+! CHECK: %[[LHS:.*]]: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "lhs"}
+! CHECK: %[[RHS:.*]]: !fir.box<!fir.array<?xi32>> {fir.bindc_name = "rhs"}
+! CHECK: %[[RES:.*]]: !fir.ref<i32> {fir.bindc_name = "res"}
+! CHECK-DAG: %[[LHS_VAR:.*]]:2 = hlfir.declare %[[LHS]]
+! CHECK-DAG: %[[RHS_VAR:.*]]:2 = hlfir.declare %[[RHS]]
+! CHECK-DAG: %[[RES_VAR:.*]]:2 = hlfir.declare %[[RES]]
+! CHECK-NEXT: %[[EXPR:.*]] = hlfir.dot_product %[[LHS_VAR]]#0 %[[RHS_VAR]]#0 {fastmath = #arith.fastmath<contract>} : (!fir.box<!fir.array<?xi32>>, !fir.box<!fir.array<?xi32>>) -> i32
+! CHECK-NEXT: hlfir.assign %[[EXPR]] to %[[RES_VAR]]#0 : i32, !fir.ref<i32>
+! CHECK-NEXT: return
+! CHECK-NEXT: }
+
+! dot product with logical arguments
+subroutine dot_product2(lhs, rhs, res)
+ logical lhs(:), rhs(:), res
+ res = DOT_PRODUCT(lhs,rhs)
+end subroutine
+! CHECK-LABEL: func.func @_QPdot_product2
+! CHECK: %[[LHS:.*]]: !fir.box<!fir.array<?x!fir.logical<4>>> {fir.bindc_name = "lhs"}
+! CHECK: %[[RHS:.*]]: !fir.box<!fir.array<?x!fir.logical<4>>> {fir.bindc_name = "rhs"}
+! CHECK: %[[RES:.*]]: !fir.ref<!fir.logical<4>> {fir.bindc_name = "res"}
+! CHECK-DAG: %[[LHS_VAR:.*]]:2 = hlfir.declare %[[LHS]]
+! CHECK-DAG: %[[RHS_VAR:.*]]:2 = hlfir.declare %[[RHS]]
+! CHECK-DAG: %[[RES_VAR:.*]]:2 = hlfir.declare %[[RES]]
+! CHECK-NEXT: %[[EXPR:.*]] = hlfir.dot_product %[[LHS_VAR]]#0 %[[RHS_VAR]]#0 {fastmath = #arith.fastmath<contract>} : (!fir.box<!fir.array<?x!fir.logical<4>>>, !fir.box<!fir.array<?x!fir.logical<4>>>) -> !fir.logical<4>
+! CHECK-NEXT: hlfir.assign %[[EXPR]] to %[[RES_VAR]]#0 : !fir.logical<4>, !fir.ref<!fir.logical<4>>
+! CHECK-NEXT: return
+! CHECK-NEXT: }
+
+! arguments are of known shape
+subroutine dot_product3(lhs, rhs, res)
+ integer lhs(5), rhs(5), res
+ res = DOT_PRODUCT(lhs,rhs)
+end subroutine
+! CHECK-LABEL: func.func @_QPdot_product3
+! CHECK: %[[LHS:.*]]: !fir.ref<!fir.array<5xi32>> {fir.bindc_name = "lhs"}
+! CHECK: %[[RHS:.*]]: !fir.ref<!fir.array<5xi32>> {fir.bindc_name = "rhs"}
+! CHECK: %[[RES:.*]]: !fir.ref<i32> {fir.bindc_name = "res"}
+! CHECK-DAG: %[[LHS_VAR:.*]]:2 = hlfir.declare %[[LHS]]
+! CHECK-DAG: %[[RHS_VAR:.*]]:2 = hlfir.declare %[[RHS]]
+! CHECK-DAG: %[[RES_VAR:.*]]:2 = hlfir.declare %[[RES]]
+! CHECK-NEXT: %[[EXPR:.*]] = hlfir.dot_product %[[LHS_VAR]]#0 %[[RHS_VAR]]#0 {fastmath = #arith.fastmath<contract>} : (!fir.ref<!fir.array<5xi32>>, !fir.ref<!fir.array<5xi32>>) -> i32
+! CHECK-NEXT: hlfir.assign %[[EXPR]] to %[[RES_VAR]]#0 : i32, !fir.ref<i32>
+! CHECK-NEXT: return
+! CHECK-NEXT: }
More information about the flang-commits
mailing list