static Value getReductionInitValue(mlir::Location loc, mlir::Type type,
llvm::StringRef reductionOpName,
fir::FirOpBuilder &builder) {
- if (type.isa<FloatType>())
+ assert((fir::isa_integer(type) || fir::isa_real(type) ||
+ type.isa<fir::LogicalType>()) &&
+ "only integer, logical and real types are currently supported");
+ if (reductionOpName.contains("max")) {
+ if (auto ty = type.dyn_cast<mlir::FloatType>()) {
+ const llvm::fltSemantics &sem = ty.getFloatSemantics();
+ return builder.createRealConstant(
+ loc, type, llvm::APFloat::getLargest(sem, /*Negative=*/true));
+ }
+ unsigned bits = type.getIntOrFloatBitWidth();
+ int64_t minInt = llvm::APInt::getSignedMinValue(bits).getSExtValue();
+ return builder.createIntegerConstant(loc, type, minInt);
+ } else {
+ if (type.isa<FloatType>())
+ return builder.create<mlir::arith::ConstantOp>(
+ loc, type,
+ builder.getFloatAttr(
+ type, (double)getOperationIdentity(reductionOpName, loc)));
+
+ if (type.isa<fir::LogicalType>()) {
+ Value intConst = builder.create<mlir::arith::ConstantOp>(
+ loc, builder.getI1Type(),
+ builder.getIntegerAttr(builder.getI1Type(),
+ getOperationIdentity(reductionOpName, loc)));
+ return builder.createConvert(loc, type, intConst);
+ }
+
return builder.create<mlir::arith::ConstantOp>(
loc, type,
- builder.getFloatAttr(
- type, (double)getOperationIdentity(reductionOpName, loc)));
-
- if (type.isa<fir::LogicalType>()) {
- Value intConst = builder.create<mlir::arith::ConstantOp>(
- loc, builder.getI1Type(),
- builder.getIntegerAttr(builder.getI1Type(),
+ builder.getIntegerAttr(type,
getOperationIdentity(reductionOpName, loc)));
- return builder.createConvert(loc, type, intConst);
}
- return builder.create<mlir::arith::ConstantOp>(
- loc, type,
- builder.getIntegerAttr(type, getOperationIdentity(reductionOpName, loc)));
}
template <typename FloatOp, typename IntegerOp>
return builder.create<FloatOp>(loc, op1, op2);
}
+static omp::ReductionDeclareOp
+createMinimalReductionDecl(fir::FirOpBuilder &builder,
+ llvm::StringRef reductionOpName, mlir::Type type,
+ mlir::Location loc) {
+ mlir::ModuleOp module = builder.getModule();
+ mlir::OpBuilder modBuilder(module.getBodyRegion());
+
+ mlir::omp::ReductionDeclareOp decl =
+ modBuilder.create<omp::ReductionDeclareOp>(loc, reductionOpName, type);
+ builder.createBlock(&decl.getInitializerRegion(),
+ decl.getInitializerRegion().end(), {type}, {loc});
+ builder.setInsertionPointToEnd(&decl.getInitializerRegion().back());
+ Value init = getReductionInitValue(loc, type, reductionOpName, builder);
+ builder.create<omp::YieldOp>(loc, init);
+
+ builder.createBlock(&decl.getReductionRegion(),
+ decl.getReductionRegion().end(), {type, type},
+ {loc, loc});
+
+ return decl;
+}
+
+/// Creates an OpenMP reduction declaration and inserts it into the provided
+/// symbol table. The declaration has a constant initializer with the neutral
+/// value `initValue`, and the reduction combiner carried over from `reduce`.
+/// TODO: Generalize this for non-integer types, add atomic region.
+static omp::ReductionDeclareOp
+createReductionDecl(fir::FirOpBuilder &builder, llvm::StringRef reductionOpName,
+ const Fortran::parser::ProcedureDesignator &procDesignator,
+ mlir::Type type, mlir::Location loc) {
+ OpBuilder::InsertionGuard guard(builder);
+ mlir::ModuleOp module = builder.getModule();
+
+ auto decl =
+ module.lookupSymbol<mlir::omp::ReductionDeclareOp>(reductionOpName);
+ if (decl)
+ return decl;
+
+ decl = createMinimalReductionDecl(builder, reductionOpName, type, loc);
+ builder.setInsertionPointToEnd(&decl.getReductionRegion().back());
+ mlir::Value op1 = decl.getReductionRegion().front().getArgument(0);
+ mlir::Value op2 = decl.getReductionRegion().front().getArgument(1);
+
+ Value reductionOp;
+ if (const auto *name{
+ Fortran::parser::Unwrap<Fortran::parser::Name>(procDesignator)}) {
+ if (name->source == "max") {
+ reductionOp =
+ getReductionOperation<mlir::arith::MaxFOp, mlir::arith::MaxSIOp>(
+ builder, type, loc, op1, op2);
+ } else {
+ TODO(loc, "Reduction of some intrinsic operators is not supported");
+ }
+ }
+
+ builder.create<omp::YieldOp>(loc, reductionOp);
+ return decl;
+}
+
/// Creates an OpenMP reduction declaration and inserts it into the provided
/// symbol table. The declaration has a constant initializer with the neutral
/// value `initValue`, and the reduction combiner carried over from `reduce`.
mlir::Type type, mlir::Location loc) {
OpBuilder::InsertionGuard guard(builder);
mlir::ModuleOp module = builder.getModule();
- mlir::OpBuilder modBuilder(module.getBodyRegion());
+
auto decl =
module.lookupSymbol<mlir::omp::ReductionDeclareOp>(reductionOpName);
- if (!decl)
- decl =
- modBuilder.create<omp::ReductionDeclareOp>(loc, reductionOpName, type);
- else
+ if (decl)
return decl;
- builder.createBlock(&decl.getInitializerRegion(),
- decl.getInitializerRegion().end(), {type}, {loc});
- builder.setInsertionPointToEnd(&decl.getInitializerRegion().back());
- Value init = getReductionInitValue(loc, type, reductionOpName, builder);
- builder.create<omp::YieldOp>(loc, init);
- builder.createBlock(&decl.getReductionRegion(),
- decl.getReductionRegion().end(), {type, type},
- {loc, loc});
+ decl = createMinimalReductionDecl(builder, reductionOpName, type, loc);
builder.setInsertionPointToEnd(&decl.getReductionRegion().back());
mlir::Value op1 = decl.getReductionRegion().front().getArgument(0);
mlir::Value op2 = decl.getReductionRegion().front().getArgument(1);
return mlir::omp::ScheduleModifier::none;
}
+static std::string getReductionName(llvm::StringRef name, mlir::Type ty) {
+ return (llvm::Twine(name) +
+ (ty.isIntOrIndex() ? llvm::Twine("_i_") : llvm::Twine("_f_")) +
+ llvm::Twine(ty.getIntOrFloatBitWidth()))
+ .str();
+}
+
static std::string getReductionName(
Fortran::parser::DefinedOperator::IntrinsicOperator intrinsicOp,
mlir::Type ty) {
break;
}
- return (llvm::Twine(reductionName) +
- (ty.isIntOrIndex() ? llvm::Twine("_i_") : llvm::Twine("_f_")) +
- llvm::Twine(ty.getIntOrFloatBitWidth()))
- .str();
+ return getReductionName(reductionName, ty);
}
static void genOMP(Fortran::lower::AbstractConverter &converter,
}
}
}
- } else {
- TODO(currentLocation,
- "Reduction of intrinsic procedures is not supported");
+ } else if (auto reductionIntrinsic =
+ std::get_if<Fortran::parser::ProcedureDesignator>(
+ &redOperator.u)) {
+ if (const auto *name{Fortran::parser::Unwrap<Fortran::parser::Name>(
+ reductionIntrinsic)}) {
+ if (name->source != "max") {
+ TODO(currentLocation,
+ "Reduction of intrinsic procedures is not supported");
+ }
+ for (const auto &ompObject : objectList.v) {
+ if (const auto *name{Fortran::parser::Unwrap<Fortran::parser::Name>(
+ ompObject)}) {
+ if (const auto *symbol{name->symbol}) {
+ mlir::Value symVal = converter.getSymbolAddress(*symbol);
+ mlir::Type redType =
+ symVal.getType().cast<fir::ReferenceType>().getEleTy();
+ reductionVars.push_back(symVal);
+ assert(redType.isIntOrIndexOrFloat() &&
+ "Unsupported reduction type");
+ decl = createReductionDecl(
+ firOpBuilder, getReductionName("max", redType),
+ *reductionIntrinsic, redType, currentLocation);
+ reductionDeclSymbols.push_back(SymbolRefAttr::get(
+ firOpBuilder.getContext(), decl.getSymName()));
+ }
+ }
+ }
+ }
}
} else if (const auto &simdlenClause =
std::get_if<Fortran::parser::OmpClause::Simdlen>(
ompDeclConstruct.u);
}
+static mlir::Operation *getCompareFromReductionOp(mlir::Operation *reductionOp,
+ mlir::Value loadVal) {
+ for (auto reductionOperand : reductionOp->getOperands()) {
+ if (auto compareOp = reductionOperand.getDefiningOp()) {
+ if (compareOp->getOperand(0) == loadVal ||
+ compareOp->getOperand(1) == loadVal)
+ assert((mlir::isa<mlir::arith::CmpIOp>(compareOp) ||
+ mlir::isa<mlir::arith::CmpFOp>(compareOp)) &&
+ "Expected comparison not found in reduction intrinsic");
+ return compareOp;
+ }
+ }
+ return nullptr;
+}
+
// Generate an OpenMP reduction operation.
// TODO: Currently assumes it is either an integer addition/multiplication
// reduction, or a logical and reduction. Generalize this for various reduction
}
}
}
+ } else if (auto reductionIntrinsic =
+ std::get_if<Fortran::parser::ProcedureDesignator>(
+ &redOperator.u)) {
+ if (const auto *name{Fortran::parser::Unwrap<Fortran::parser::Name>(
+ reductionIntrinsic)}) {
+ if (name->source != "max") {
+ continue;
+ }
+ for (const auto &ompObject : objectList.v) {
+ if (const auto *name{Fortran::parser::Unwrap<Fortran::parser::Name>(
+ ompObject)}) {
+ if (const auto *symbol{name->symbol}) {
+ mlir::Value reductionVal = converter.getSymbolAddress(*symbol);
+ for (mlir::OpOperand &reductionValUse :
+ reductionVal.getUses()) {
+ if (auto loadOp = mlir::dyn_cast<fir::LoadOp>(
+ reductionValUse.getOwner())) {
+ mlir::Value loadVal = loadOp.getRes();
+ // Max is lowered as a compare -> select.
+ // Match the pattern here.
+ mlir::Operation *reductionOp =
+ findReductionChain(loadVal, &reductionVal);
+ assert(mlir::isa<mlir::arith::SelectOp>(reductionOp) &&
+ "Selection Op not found in reduction intrinsic");
+ mlir::Operation *compareOp =
+ getCompareFromReductionOp(reductionOp, loadVal);
+ updateReduction(compareOp, firOpBuilder, loadVal,
+ reductionVal);
+ }
+ }
+ }
+ }
+ }
+ }
}
}
}