blob: df69714344f14a4fedf93f4e5a13dee40f320516 [file] [edit]
//===- OpenACCUtilsReduction.cpp - OpenACC reduction utilities ------------===//
//
// 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
//
//===----------------------------------------------------------------------===//
#include "mlir/Dialect/OpenACC/OpenACCUtilsReduction.h"
#include "mlir/Dialect/Arith/Utils/Utils.h"
#include "mlir/Dialect/Complex/IR/Complex.h"
#include "mlir/Dialect/OpenACC/OpenACCUtilsCG.h"
#include "llvm/Support/raw_ostream.h"
using namespace mlir;
namespace mlir {
namespace acc {
static bool isFloatOrComplexType(Type ty) {
return isa<FloatType, ComplexType>(ty);
}
SmallVector<GPUParallelDimAttr>
getReductionCombineParDims(ReductionCombineOp reductionCombineOp) {
if (GPUParallelDimsAttr parDimsAttr = getParDimsAttr(reductionCombineOp))
return SmallVector<GPUParallelDimAttr>(parDimsAttr.getArray());
llvm_unreachable(
"expected parallel dimensions attribute for reduction combine op");
}
SmallVector<GPUParallelDimAttr>
getReductionCombineParDims(ReductionCombineRegionOp combineRegionOp) {
for (Operation *user : combineRegionOp.getSrcVar().getUsers()) {
if (auto accumulateOp = dyn_cast<ReductionAccumulateOp>(user))
return SmallVector<GPUParallelDimAttr>(
accumulateOp.getParDims().getArray());
}
if (GPUParallelDimsAttr parDimsAttr = getParDimsAttr(combineRegionOp))
return SmallVector<GPUParallelDimAttr>(parDimsAttr.getArray());
return {};
}
ReductionOperator translateAtomicRMWKind(arith::AtomicRMWKind kind) {
switch (kind) {
case arith::AtomicRMWKind::addf:
case arith::AtomicRMWKind::addi:
return ReductionOperator::AccAdd;
case arith::AtomicRMWKind::mulf:
case arith::AtomicRMWKind::muli:
return ReductionOperator::AccMul;
case arith::AtomicRMWKind::maxs:
case arith::AtomicRMWKind::maxu:
case arith::AtomicRMWKind::maximumf:
case arith::AtomicRMWKind::maxnumf:
return ReductionOperator::AccMax;
case arith::AtomicRMWKind::minu:
case arith::AtomicRMWKind::mins:
case arith::AtomicRMWKind::minimumf:
case arith::AtomicRMWKind::minnumf:
return ReductionOperator::AccMin;
case arith::AtomicRMWKind::andi:
return ReductionOperator::AccIand;
case arith::AtomicRMWKind::ori:
return ReductionOperator::AccIor;
case arith::AtomicRMWKind::xori:
return ReductionOperator::AccXor;
case arith::AtomicRMWKind::assign:
break;
}
llvm_unreachable("unsupported atomic kind");
}
std::optional<arith::AtomicRMWKind>
translateACCReductionOperator(ReductionOperator redOp, Type type) {
if (type.isInteger() && type.isUnsignedInteger())
return std::nullopt;
if (auto reducible = dyn_cast<ReducibleType>(type)) {
if (std::optional<arith::AtomicRMWKind> kind =
reducible.getAtomicRMWKind(redOp))
return kind;
return std::nullopt;
}
switch (redOp) {
case ReductionOperator::AccAdd:
if (type.isInteger())
return arith::AtomicRMWKind::addi;
if (isFloatOrComplexType(type))
return arith::AtomicRMWKind::addf;
break;
case ReductionOperator::AccMul:
if (type.isInteger())
return arith::AtomicRMWKind::muli;
if (isFloatOrComplexType(type))
return arith::AtomicRMWKind::mulf;
break;
case ReductionOperator::AccMax:
if (type.isInteger())
return arith::AtomicRMWKind::maxs;
if (type.isFloat())
return arith::AtomicRMWKind::maxnumf;
break;
case ReductionOperator::AccMaximumf:
return arith::AtomicRMWKind::maximumf;
case ReductionOperator::AccMaxnumf:
return arith::AtomicRMWKind::maxnumf;
case ReductionOperator::AccMin:
if (type.isInteger())
return arith::AtomicRMWKind::mins;
if (type.isFloat())
return arith::AtomicRMWKind::minnumf;
break;
case ReductionOperator::AccMinimumf:
return arith::AtomicRMWKind::minimumf;
case ReductionOperator::AccMinnumf:
return arith::AtomicRMWKind::minnumf;
case ReductionOperator::AccIand:
case ReductionOperator::AccLand:
if (type.isInteger())
return arith::AtomicRMWKind::andi;
break;
case ReductionOperator::AccIor:
case ReductionOperator::AccLor:
if (type.isInteger())
return arith::AtomicRMWKind::ori;
break;
case ReductionOperator::AccXor:
case ReductionOperator::AccNeqv:
if (type.isInteger())
return arith::AtomicRMWKind::xori;
break;
case ReductionOperator::AccEqv:
case ReductionOperator::AccNone:
break;
}
return std::nullopt;
}
static TypedAttr getReductionIdentityValueAttr(arith::AtomicRMWKind kind,
Type type, OpBuilder &builder,
Location loc,
bool useOnlyFiniteValue) {
if (type.isIntOrIndexOrFloat()) {
TypedAttr attr = arith::getIdentityValueAttr(kind, type, builder, loc,
useOnlyFiniteValue);
if (!attr)
emitError(loc) << "reduction identity: operator not supported " << kind;
return attr;
}
if (auto complexTy = dyn_cast<ComplexType>(type)) {
auto eltTy = dyn_cast<FloatType>(complexTy.getElementType());
if (!eltTy) {
emitError(loc) << "reduction identity: complex with non-floating "
"element type";
return nullptr;
}
switch (kind) {
case arith::AtomicRMWKind::addf: {
TypedAttr scalarAttr = arith::getIdentityValueAttr(
kind, eltTy, builder, loc, useOnlyFiniteValue);
assert(scalarAttr && "expected scalar identity for complex reduction");
double d = cast<FloatAttr>(scalarAttr).getValue().convertToDouble();
return complex::NumberAttr::get(complexTy, d, d);
}
case arith::AtomicRMWKind::mulf: {
TypedAttr scalarAttr = arith::getIdentityValueAttr(
kind, eltTy, builder, loc, useOnlyFiniteValue);
assert(scalarAttr &&
"expected scalar identity for complex mulf reduction");
auto realPart = cast<FloatAttr>(scalarAttr).getValue();
return complex::NumberAttr::get(complexTy, realPart.convertToDouble(),
0.0);
}
default:
emitError(loc)
<< "reduction identity: operator not supported for complex " << kind;
return nullptr;
}
}
emitError(loc) << "reduction identity: type not supported " << type;
return nullptr;
}
Value createIdentityValue(OpBuilder &b, Location loc, Type type,
arith::AtomicRMWKind kind, bool useOnlyFiniteValue) {
TypedAttr typedAttr =
getReductionIdentityValueAttr(kind, type, b, loc, useOnlyFiniteValue);
assert(typedAttr && "expected identity attribute");
if (auto numAttr = dyn_cast<complex::NumberAttr>(typedAttr)) {
auto complexTy = cast<ComplexType>(numAttr.getType());
auto floatElt = cast<FloatType>(complexTy.getElementType());
Value realVal = arith::ConstantOp::create(
b, loc, b.getFloatAttr(floatElt, numAttr.getReal()));
Value imagVal = arith::ConstantOp::create(
b, loc, b.getFloatAttr(floatElt, numAttr.getImag()));
return complex::CreateOp::create(b, loc, complexTy, realVal, imagVal);
}
return arith::ConstantOp::create(b, loc, typedAttr);
}
Value generateReductionOp(OpBuilder &b, Location loc, Value lhs, Value rhs,
arith::AtomicRMWKind kind) {
assert(lhs.getType() == rhs.getType() &&
"expected same type for lhs and rhs");
if (isa<ComplexType>(lhs.getType())) {
switch (kind) {
case arith::AtomicRMWKind::addf:
return complex::AddOp::create(b, loc, lhs, rhs);
case arith::AtomicRMWKind::mulf:
return complex::MulOp::create(b, loc, lhs, rhs);
default:
llvm_unreachable("unsupported complex atomic reduction kind");
}
}
return arith::getReductionOp(kind, b, loc, lhs, rhs);
}
} // namespace acc
} // namespace mlir