205 changes: 205 additions & 0 deletions flang/lib/Optimizer/Builder/Runtime/Reduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
#include "flang/Optimizer/Support/Utils.h"
#include "flang/Runtime/reduce.h"
#include "flang/Runtime/reduction.h"
#include "mlir/Dialect/Func/IR/FuncOps.h"

Expand Down Expand Up @@ -466,6 +467,87 @@ struct ForcedIParity16 {
}
};

/// Placeholder for real*16 version of Reduce Intrinsic
struct ForcedReduceReal16 {
static constexpr const char *name = ExpandAndQuoteKey(RTNAME(ReduceReal16));
static constexpr fir::runtime::FuncTypeBuilderFunc getTypeModel() {
return [](mlir::MLIRContext *ctx) {
auto ty = mlir::FloatType::getF128(ctx);
auto boxTy =
fir::runtime::getModel<const Fortran::runtime::Descriptor &>()(ctx);
auto opTy = mlir::FunctionType::get(ctx, {ty, ty}, ty);
auto strTy = fir::ReferenceType::get(mlir::IntegerType::get(ctx, 8));
auto intTy = mlir::IntegerType::get(ctx, 8 * sizeof(int));
auto refTy = fir::ReferenceType::get(ty);
auto i1Ty = mlir::IntegerType::get(ctx, 1);
return mlir::FunctionType::get(
ctx, {boxTy, opTy, strTy, intTy, intTy, boxTy, refTy, i1Ty}, {ty});
};
}
};

/// Placeholder for integer*16 version of Reduce Intrinsic
struct ForcedReduceInteger16 {
static constexpr const char *name =
ExpandAndQuoteKey(RTNAME(ReduceInteger16));
static constexpr fir::runtime::FuncTypeBuilderFunc getTypeModel() {
return [](mlir::MLIRContext *ctx) {
auto ty = mlir::IntegerType::get(ctx, 128);
auto boxTy =
fir::runtime::getModel<const Fortran::runtime::Descriptor &>()(ctx);
auto opTy = mlir::FunctionType::get(ctx, {ty, ty}, ty);
auto strTy = fir::ReferenceType::get(mlir::IntegerType::get(ctx, 8));
auto intTy = mlir::IntegerType::get(ctx, 8 * sizeof(int));
auto refTy = fir::ReferenceType::get(ty);
auto i1Ty = mlir::IntegerType::get(ctx, 1);
return mlir::FunctionType::get(
ctx, {boxTy, opTy, strTy, intTy, intTy, boxTy, refTy, i1Ty}, {ty});
};
}
};

/// Placeholder for complex(10) version of Reduce Intrinsic
struct ForcedReduceComplex10 {
static constexpr const char *name =
ExpandAndQuoteKey(RTNAME(CppReduceComplex10));
static constexpr fir::runtime::FuncTypeBuilderFunc getTypeModel() {
return [](mlir::MLIRContext *ctx) {
auto ty = mlir::ComplexType::get(mlir::FloatType::getF80(ctx));
auto boxTy =
fir::runtime::getModel<const Fortran::runtime::Descriptor &>()(ctx);
auto opTy = mlir::FunctionType::get(ctx, {ty, ty}, ty);
auto strTy = fir::ReferenceType::get(mlir::IntegerType::get(ctx, 8));
auto intTy = mlir::IntegerType::get(ctx, 8 * sizeof(int));
auto refTy = fir::ReferenceType::get(ty);
auto i1Ty = mlir::IntegerType::get(ctx, 1);
return mlir::FunctionType::get(
ctx, {refTy, boxTy, opTy, strTy, intTy, intTy, boxTy, refTy, i1Ty},
{});
};
}
};

/// Placeholder for complex(16) version of Reduce Intrinsic
struct ForcedReduceComplex16 {
static constexpr const char *name =
ExpandAndQuoteKey(RTNAME(CppReduceComplex16));
static constexpr fir::runtime::FuncTypeBuilderFunc getTypeModel() {
return [](mlir::MLIRContext *ctx) {
auto ty = mlir::ComplexType::get(mlir::FloatType::getF128(ctx));
auto boxTy =
fir::runtime::getModel<const Fortran::runtime::Descriptor &>()(ctx);
auto opTy = mlir::FunctionType::get(ctx, {ty, ty}, ty);
auto strTy = fir::ReferenceType::get(mlir::IntegerType::get(ctx, 8));
auto intTy = mlir::IntegerType::get(ctx, 8 * sizeof(int));
auto refTy = fir::ReferenceType::get(ty);
auto i1Ty = mlir::IntegerType::get(ctx, 1);
return mlir::FunctionType::get(
ctx, {refTy, boxTy, opTy, strTy, intTy, intTy, boxTy, refTy, i1Ty},
{});
};
}
};

/// Generate call to specialized runtime function that takes a mask and
/// dim argument. The All, Any, and Count intrinsics use this pattern.
template <typename FN>
Expand Down Expand Up @@ -1237,3 +1319,126 @@ void fir::runtime::genIParityDim(fir::FirOpBuilder &builder, mlir::Location loc,
/// Generate call to `IParity` intrinsic runtime routine. This is the version
/// that does not take a dim argument.
GEN_IALL_IANY_IPARITY(IParity)

/// Generate call to `Reduce` intrinsic runtime routine. This is the version
/// that does not take a DIM argument and store result in the passed result
/// value.
void fir::runtime::genReduce(fir::FirOpBuilder &builder, mlir::Location loc,
mlir::Value arrayBox, mlir::Value operation,
mlir::Value maskBox, mlir::Value identity,
mlir::Value ordered, mlir::Value resultBox) {
mlir::func::FuncOp func;
auto ty = arrayBox.getType();
auto arrTy = fir::dyn_cast_ptrOrBoxEleTy(ty);
auto eleTy = mlir::cast<fir::SequenceType>(arrTy).getEleTy();
auto dim = builder.createIntegerConstant(loc, builder.getI32Type(), 1);

assert(resultBox && "expect non null value for the result");
assert((fir::isa_char(eleTy) || fir::isa_complex(eleTy) ||
fir::isa_derived(eleTy)) &&
"expect character, complex or derived-type");

mlir::MLIRContext *ctx = builder.getContext();
fir::factory::CharacterExprHelper charHelper{builder, loc};

if (eleTy == fir::ComplexType::get(ctx, 2))
func =
fir::runtime::getRuntimeFunc<mkRTKey(CppReduceComplex2)>(loc, builder);
else if (eleTy == fir::ComplexType::get(ctx, 3))
func =
fir::runtime::getRuntimeFunc<mkRTKey(CppReduceComplex3)>(loc, builder);
else if (eleTy == fir::ComplexType::get(ctx, 4))
func =
fir::runtime::getRuntimeFunc<mkRTKey(CppReduceComplex4)>(loc, builder);
else if (eleTy == fir::ComplexType::get(ctx, 8))
func =
fir::runtime::getRuntimeFunc<mkRTKey(CppReduceComplex8)>(loc, builder);
else if (eleTy == fir::ComplexType::get(ctx, 10))
func = fir::runtime::getRuntimeFunc<ForcedReduceComplex10>(loc, builder);
else if (eleTy == fir::ComplexType::get(ctx, 16))
func = fir::runtime::getRuntimeFunc<ForcedReduceComplex16>(loc, builder);
else if (fir::isa_char(eleTy) && charHelper.getCharacterKind(eleTy) == 1)
func = fir::runtime::getRuntimeFunc<mkRTKey(ReduceChar1)>(loc, builder);
else if (fir::isa_char(eleTy) && charHelper.getCharacterKind(eleTy) == 2)
func = fir::runtime::getRuntimeFunc<mkRTKey(ReduceChar2)>(loc, builder);
else if (fir::isa_char(eleTy) && charHelper.getCharacterKind(eleTy) == 4)
func = fir::runtime::getRuntimeFunc<mkRTKey(ReduceChar4)>(loc, builder);
else if (fir::isa_derived(eleTy))
func =
fir::runtime::getRuntimeFunc<mkRTKey(ReduceDerivedType)>(loc, builder);
else
fir::intrinsicTypeTODO(builder, eleTy, loc, "REDUCE");

auto fTy = func.getFunctionType();
auto sourceFile = fir::factory::locationToFilename(builder, loc);
auto sourceLine =
fir::factory::locationToLineNo(builder, loc, fTy.getInput(4));
auto opAddr = builder.create<fir::BoxAddrOp>(loc, fTy.getInput(2), operation);
auto args = fir::runtime::createArguments(
builder, loc, fTy, resultBox, arrayBox, opAddr, sourceFile, sourceLine,
dim, maskBox, identity, ordered);
builder.create<fir::CallOp>(loc, func, args);
}

/// Generate call to `Reduce` intrinsic runtime routine. This is the version
/// that does not take DIM argument and return a scalar result.
mlir::Value fir::runtime::genReduce(fir::FirOpBuilder &builder,
mlir::Location loc, mlir::Value arrayBox,
mlir::Value operation, mlir::Value maskBox,
mlir::Value identity, mlir::Value ordered) {
mlir::func::FuncOp func;
auto ty = arrayBox.getType();
auto arrTy = fir::dyn_cast_ptrOrBoxEleTy(ty);
auto eleTy = mlir::cast<fir::SequenceType>(arrTy).getEleTy();
auto dim = builder.createIntegerConstant(loc, builder.getI32Type(), 1);

mlir::MLIRContext *ctx = builder.getContext();
fir::factory::CharacterExprHelper charHelper{builder, loc};

assert((fir::isa_real(eleTy) || fir::isa_integer(eleTy) ||
mlir::isa<fir::LogicalType>(eleTy)) &&
"expect real, interger or logical");

if (eleTy.isF16())
func = fir::runtime::getRuntimeFunc<mkRTKey(ReduceReal2)>(loc, builder);
else if (eleTy.isBF16())
func = fir::runtime::getRuntimeFunc<mkRTKey(ReduceReal3)>(loc, builder);
else if (eleTy.isF32())
func = fir::runtime::getRuntimeFunc<mkRTKey(ReduceReal4)>(loc, builder);
else if (eleTy.isF64())
func = fir::runtime::getRuntimeFunc<mkRTKey(ReduceReal8)>(loc, builder);
else if (eleTy.isF80())
func = fir::runtime::getRuntimeFunc<mkRTKey(ReduceReal10)>(loc, builder);
else if (eleTy.isF128())
func = fir::runtime::getRuntimeFunc<ForcedReduceReal16>(loc, builder);
else if (eleTy.isInteger(builder.getKindMap().getIntegerBitsize(1)))
func = fir::runtime::getRuntimeFunc<mkRTKey(ReduceInteger1)>(loc, builder);
else if (eleTy.isInteger(builder.getKindMap().getIntegerBitsize(2)))
func = fir::runtime::getRuntimeFunc<mkRTKey(ReduceInteger2)>(loc, builder);
else if (eleTy.isInteger(builder.getKindMap().getIntegerBitsize(4)))
func = fir::runtime::getRuntimeFunc<mkRTKey(ReduceInteger4)>(loc, builder);
else if (eleTy.isInteger(builder.getKindMap().getIntegerBitsize(8)))
func = fir::runtime::getRuntimeFunc<mkRTKey(ReduceInteger8)>(loc, builder);
else if (eleTy.isInteger(builder.getKindMap().getIntegerBitsize(16)))
func = fir::runtime::getRuntimeFunc<ForcedReduceInteger16>(loc, builder);
else if (eleTy == fir::LogicalType::get(ctx, 1))
func = fir::runtime::getRuntimeFunc<mkRTKey(ReduceLogical1)>(loc, builder);
else if (eleTy == fir::LogicalType::get(ctx, 2))
func = fir::runtime::getRuntimeFunc<mkRTKey(ReduceLogical2)>(loc, builder);
else if (eleTy == fir::LogicalType::get(ctx, 4))
func = fir::runtime::getRuntimeFunc<mkRTKey(ReduceLogical4)>(loc, builder);
else if (eleTy == fir::LogicalType::get(ctx, 8))
func = fir::runtime::getRuntimeFunc<mkRTKey(ReduceLogical8)>(loc, builder);
else
fir::intrinsicTypeTODO(builder, eleTy, loc, "REDUCE");

auto fTy = func.getFunctionType();
auto sourceFile = fir::factory::locationToFilename(builder, loc);
auto sourceLine =
fir::factory::locationToLineNo(builder, loc, fTy.getInput(3));
auto opAddr = builder.create<fir::BoxAddrOp>(loc, fTy.getInput(1), operation);
auto args = fir::runtime::createArguments(builder, loc, fTy, arrayBox, opAddr,
sourceFile, sourceLine, dim,
maskBox, identity, ordered);
return builder.create<fir::CallOp>(loc, func, args).getResult(0);
}
3 changes: 2 additions & 1 deletion flang/lib/Optimizer/Dialect/FIROps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1432,7 +1432,8 @@ bool fir::ConvertOp::canBeConverted(mlir::Type inType, mlir::Type outType) {
mlir::LogicalResult fir::ConvertOp::verify() {
if (canBeConverted(getValue().getType(), getType()))
return mlir::success();
return emitOpError("invalid type conversion");
return emitOpError("invalid type conversion")
<< getValue().getType() << " / " << getType();
}

//===----------------------------------------------------------------------===//
Expand Down
13 changes: 0 additions & 13 deletions flang/test/Lower/Intrinsics/Todo/reduce.f90

This file was deleted.

395 changes: 395 additions & 0 deletions flang/test/Lower/Intrinsics/reduce.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,395 @@
! RUN: bbc -emit-hlfir %s -o - | FileCheck %s

module reduce_mod

type :: t1
integer :: a
end type

contains

pure function red_int1(a,b)
integer(1), intent(in) :: a, b
integer(1) :: red_int1
red_int1 = a + b
end function

subroutine integer1(a, id)
integer(1), intent(in) :: a(:)
integer(1) :: res, id

res = reduce(a, red_int1)

res = reduce(a, red_int1, identity=id)

res = reduce(a, red_int1, identity=id, ordered = .true.)

res = reduce(a, red_int1, [.true., .true., .false.])
end subroutine

! CHECK-LABEL: func.func @_QMreduce_modPinteger1(
! CHECK-SAME: %[[ARG0:.*]]: !fir.box<!fir.array<?xi8>> {fir.bindc_name = "a"}, %[[ARG1:.*]]: !fir.ref<i8> {fir.bindc_name = "id"})
! CHECK: %[[A:.*]]:2 = hlfir.declare %[[ARG0]] dummy_scope %{{.*}} {fortran_attrs = #fir.var_attrs<intent_in>, uniq_name = "_QMreduce_modFinteger1Ea"} : (!fir.box<!fir.array<?xi8>>, !fir.dscope) -> (!fir.box<!fir.array<?xi8>>, !fir.box<!fir.array<?xi8>>)
! CHECK: %[[ID:.*]]:2 = hlfir.declare %[[ARG1]] dummy_scope %{{.*}} {uniq_name = "_QMreduce_modFinteger1Eid"} : (!fir.ref<i8>, !fir.dscope) -> (!fir.ref<i8>, !fir.ref<i8>)
! CHECK: %[[ALLOC_RES:.*]] = fir.alloca i8 {bindc_name = "res", uniq_name = "_QMreduce_modFinteger1Eres"}
! CHECK: %[[RES:.*]]:2 = hlfir.declare %[[ALLOC_RES]] {uniq_name = "_QMreduce_modFinteger1Eres"} : (!fir.ref<i8>) -> (!fir.ref<i8>, !fir.ref<i8>)
! CHECK: %[[ADDR_OP:.*]] = fir.address_of(@_QMreduce_modPred_int1) : (!fir.ref<i8>, !fir.ref<i8>) -> i8
! CHECK: %[[BOX_PROC:.*]] = fir.emboxproc %[[ADDR_OP]] : ((!fir.ref<i8>, !fir.ref<i8>) -> i8) -> !fir.boxproc<() -> ()>
! CHECK: %[[MASK:.*]] = fir.absent !fir.box<i1>
! CHECK: %[[IDENTITY:.*]] = fir.absent !fir.ref<i8>
! CHECK: %[[BOX_ADDR:.*]] = fir.box_addr %[[BOX_PROC]] : (!fir.boxproc<() -> ()>) -> ((!fir.ref<i8>, !fir.ref<i8>) -> !fir.ref<i8>)
! CHECK: %[[A_NONE:.*]] = fir.convert %[[A]]#1 : (!fir.box<!fir.array<?xi8>>) -> !fir.box<none>
! CHECK: %[[MASK_NONE:.*]] = fir.convert %[[MASK]] : (!fir.box<i1>) -> !fir.box<none>
! CHECK: %[[REDUCE_RES:.*]] = fir.call @_FortranAReduceInteger1(%[[A_NONE]], %[[BOX_ADDR]], %{{.*}}, %{{.*}}, %c1{{.*}}, %[[MASK_NONE]], %[[IDENTITY]], %false) fastmath<contract> : (!fir.box<none>, (!fir.ref<i8>, !fir.ref<i8>) -> !fir.ref<i8>, !fir.ref<i8>, i32, i32, !fir.box<none>, !fir.ref<i8>, i1) -> i8
! CHECK: hlfir.assign %[[REDUCE_RES]] to %[[RES]]#0 : i8, !fir.ref<i8>
! CHECK: %[[ADDR_OP:.*]] = fir.address_of(@_QMreduce_modPred_int1) : (!fir.ref<i8>, !fir.ref<i8>) -> i8
! CHECK: %[[BOX_PROC:.*]] = fir.emboxproc %[[ADDR_OP]] : ((!fir.ref<i8>, !fir.ref<i8>) -> i8) -> !fir.boxproc<() -> ()>
! CHECK: %[[MASK:.*]] = fir.absent !fir.box<i1>
! CHECK: %[[BOX_ADDR:.*]] = fir.box_addr %[[BOX_PROC]] : (!fir.boxproc<() -> ()>) -> ((!fir.ref<i8>, !fir.ref<i8>) -> !fir.ref<i8>)
! CHECK: %[[A_NONE:.*]] = fir.convert %[[A]]#1 : (!fir.box<!fir.array<?xi8>>) -> !fir.box<none>
! CHECK: %[[MASK_NONE:.*]] = fir.convert %[[MASK]] : (!fir.box<i1>) -> !fir.box<none>
! CHECK: %{{.*}} = fir.call @_FortranAReduceInteger1(%[[A_NONE]], %[[BOX_ADDR]], %{{.*}}, %{{.*}}, %c1{{.*}}, %[[MASK_NONE]], %[[ID]]#1, %false{{.*}}) fastmath<contract> : (!fir.box<none>, (!fir.ref<i8>, !fir.ref<i8>) -> !fir.ref<i8>, !fir.ref<i8>, i32, i32, !fir.box<none>, !fir.ref<i8>, i1) -> i8
! CHECK: fir.call @_FortranAReduceInteger1(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}#1, %true)
! CHECK: %[[MASK:.*]]:2 = hlfir.declare %{{.*}}(%{{.*}}) {fortran_attrs = #fir.var_attrs<parameter>, uniq_name = "_QQro.3xl4.0"} : (!fir.ref<!fir.array<3x!fir.logical<4>>>, !fir.shape<1>) -> (!fir.ref<!fir.array<3x!fir.logical<4>>>, !fir.ref<!fir.array<3x!fir.logical<4>>>)
! CHECK: %[[SHAPE_C3:.*]] = fir.shape %c3{{.*}} : (index) -> !fir.shape<1>
! CHECK: %[[BOXED_MASK:.*]] = fir.embox %[[MASK]]#1(%[[SHAPE_C3]]) : (!fir.ref<!fir.array<3x!fir.logical<4>>>, !fir.shape<1>) -> !fir.box<!fir.array<3x!fir.logical<4>>>
! CHECK: %[[CONV_MASK:.*]] = fir.convert %[[BOXED_MASK]] : (!fir.box<!fir.array<3x!fir.logical<4>>>) -> !fir.box<none>
! CHECK: fir.call @_FortranAReduceInteger1(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[CONV_MASK]], %{{.*}}, %false{{.*}})

pure function red_int2(a,b)
integer(2), intent(in) :: a, b
integer(2) :: red_int2
red_int2 = a + b
end function

subroutine integer2(a)
integer(2), intent(in) :: a(:)
integer(2) :: res
res = reduce(a, red_int2)
end subroutine

! CHECK: fir.call @_FortranAReduceInteger2

pure function red_int4(a,b)
integer(4), intent(in) :: a, b
integer(4) :: red_int4
red_int4 = a + b
end function

subroutine integer4(a)
integer(4), intent(in) :: a(:)
integer(4) :: res
res = reduce(a, red_int4)
end subroutine

! CHECK: fir.call @_FortranAReduceInteger4

pure function red_int8(a,b)
integer(8), intent(in) :: a, b
integer(8) :: red_int8
red_int8 = a + b
end function

subroutine integer8(a)
integer(8), intent(in) :: a(:)
integer(8) :: res
res = reduce(a, red_int8)
end subroutine

! CHECK: fir.call @_FortranAReduceInteger8

pure function red_int16(a,b)
integer(16), intent(in) :: a, b
integer(16) :: red_int16
red_int16 = a + b
end function

subroutine integer16(a)
integer(16), intent(in) :: a(:)
integer(16) :: res
res = reduce(a, red_int16)
end subroutine

! CHECK: fir.call @_FortranAReduceInteger16

pure function red_real2(a,b)
real(2), intent(in) :: a, b
real(2) :: red_real2
red_real2 = a + b
end function

subroutine real2(a)
real(2), intent(in) :: a(:)
real(2) :: res
res = reduce(a, red_real2)
end subroutine

! CHECK: fir.call @_FortranAReduceReal2

pure function red_real3(a,b)
real(3), intent(in) :: a, b
real(3) :: red_real3
red_real3 = a + b
end function

subroutine real3(a)
real(3), intent(in) :: a(:)
real(3) :: res
res = reduce(a, red_real3)
end subroutine

! CHECK: fir.call @_FortranAReduceReal3

pure function red_real4(a,b)
real(4), intent(in) :: a, b
real(4) :: red_real4
red_real4 = a + b
end function

subroutine real4(a)
real(4), intent(in) :: a(:)
real(4) :: res
res = reduce(a, red_real4)
end subroutine

! CHECK: fir.call @_FortranAReduceReal4

pure function red_real8(a,b)
real(8), intent(in) :: a, b
real(8) :: red_real8
red_real8 = a + b
end function

subroutine real8(a)
real(8), intent(in) :: a(:)
real(8) :: res
res = reduce(a, red_real8)
end subroutine

! CHECK: fir.call @_FortranAReduceReal8

pure function red_real10(a,b)
real(10), intent(in) :: a, b
real(10) :: red_real10
red_real10 = a + b
end function

subroutine real10(a)
real(10), intent(in) :: a(:)
real(10) :: res
res = reduce(a, red_real10)
end subroutine

! CHECK: fir.call @_FortranAReduceReal10

pure function red_real16(a,b)
real(16), intent(in) :: a, b
real(16) :: red_real16
red_real16 = a + b
end function

subroutine real16(a)
real(16), intent(in) :: a(:)
real(16) :: res
res = reduce(a, red_real16)
end subroutine

! CHECK: fir.call @_FortranAReduceReal16

pure function red_complex2(a,b)
complex(2), intent(in) :: a, b
complex(2) :: red_complex2
red_complex2 = a + b
end function

subroutine complex2(a)
complex(2), intent(in) :: a(:)
complex(2) :: res
res = reduce(a, red_complex2)
end subroutine

! CHECK: fir.call @_FortranACppReduceComplex2

pure function red_complex3(a,b)
complex(3), intent(in) :: a, b
complex(3) :: red_complex3
red_complex3 = a + b
end function

subroutine complex3(a)
complex(3), intent(in) :: a(:)
complex(3) :: res
res = reduce(a, red_complex3)
end subroutine

! CHECK: fir.call @_FortranACppReduceComplex3

pure function red_complex4(a,b)
complex(4), intent(in) :: a, b
complex(4) :: red_complex4
red_complex4 = a + b
end function

subroutine complex4(a)
complex(4), intent(in) :: a(:)
complex(4) :: res
res = reduce(a, red_complex4)
end subroutine

! CHECK: fir.call @_FortranACppReduceComplex4

pure function red_complex8(a,b)
complex(8), intent(in) :: a, b
complex(8) :: red_complex8
red_complex8 = a + b
end function

subroutine complex8(a)
complex(8), intent(in) :: a(:)
complex(8) :: res
res = reduce(a, red_complex8)
end subroutine

! CHECK: fir.call @_FortranACppReduceComplex8

pure function red_complex10(a,b)
complex(10), intent(in) :: a, b
complex(10) :: red_complex10
red_complex10 = a + b
end function

subroutine complex10(a)
complex(10), intent(in) :: a(:)
complex(10) :: res
res = reduce(a, red_complex10)
end subroutine

! CHECK: fir.call @_FortranACppReduceComplex10

pure function red_complex16(a,b)
complex(16), intent(in) :: a, b
complex(16) :: red_complex16
red_complex16 = a + b
end function

subroutine complex16(a)
complex(16), intent(in) :: a(:)
complex(16) :: res
res = reduce(a, red_complex16)
end subroutine

! CHECK: fir.call @_FortranACppReduceComplex16

pure function red_log1(a,b)
logical(1), intent(in) :: a, b
logical(1) :: red_log1
red_log1 = a .and. b
end function

subroutine log1(a)
logical(1), intent(in) :: a(:)
logical(1) :: res
res = reduce(a, red_log1)
end subroutine

! CHECK: fir.call @_FortranAReduceLogical1

pure function red_log2(a,b)
logical(2), intent(in) :: a, b
logical(2) :: red_log2
red_log2 = a .and. b
end function

subroutine log2(a)
logical(2), intent(in) :: a(:)
logical(2) :: res
res = reduce(a, red_log2)
end subroutine

! CHECK: fir.call @_FortranAReduceLogical2

pure function red_log4(a,b)
logical(4), intent(in) :: a, b
logical(4) :: red_log4
red_log4 = a .and. b
end function

subroutine log4(a)
logical(4), intent(in) :: a(:)
logical(4) :: res
res = reduce(a, red_log4)
end subroutine

! CHECK: fir.call @_FortranAReduceLogical4

pure function red_log8(a,b)
logical(8), intent(in) :: a, b
logical(8) :: red_log8
red_log8 = a .and. b
end function

subroutine log8(a)
logical(8), intent(in) :: a(:)
logical(8) :: res
res = reduce(a, red_log8)
end subroutine

! CHECK: fir.call @_FortranAReduceLogical8

pure function red_char1(a,b)
character(1), intent(in) :: a, b
character(1) :: red_char1
red_char1 = a // b
end function

subroutine char1(a)
character(1), intent(in) :: a(:)
character(1) :: res
res = reduce(a, red_char1)
end subroutine

! CHECK: fir.call @_FortranAReduceChar1

pure function red_char2(a,b)
character(kind=2), intent(in) :: a, b
character(kind=2) :: red_char2
red_char2 = a // b
end function

subroutine char2(a)
character(kind=2), intent(in) :: a(:)
character(kind=2) :: res
res = reduce(a, red_char2)
end subroutine

! CHECK: fir.call @_FortranAReduceChar2

pure function red_char4(a,b)
character(kind=4), intent(in) :: a, b
character(kind=4) :: red_char4
red_char4 = a // b
end function

subroutine char4(a)
character(kind=4), intent(in) :: a(:)
character(kind=4) :: res
res = reduce(a, red_char4)
end subroutine

! CHECK: fir.call @_FortranAReduceChar4

pure function red_type(a,b)
type(t1), intent(in) :: a, b
type(t1) :: red_type
red_type%a = a%a + b%a
end function

subroutine testtype(a)
type(t1), intent(in) :: a(:)
type(t1) :: res
res = reduce(a, red_type)
end subroutine

! CHECK: fir.call @_FortranAReduceDerivedType

end module