[flang-commits] [flang] [flang] Simplify hlfir.index in a few limited cases. (PR #157883)
via flang-commits
flang-commits at lists.llvm.org
Wed Sep 10 08:41:11 PDT 2025
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-flang-fir-hlfir
Author: Valery Dmitriev (valerydmit)
<details>
<summary>Changes</summary>
Primarily targeted simplification case of substring being
a singleton by inlining a search loop (with an exception
where runtime function performs better).
Few trivial simplifications also covered.
---
Patch is 92.78 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/157883.diff
14 Files Affected:
- (modified) flang/include/flang/Lower/HlfirIntrinsics.h (+8)
- (modified) flang/include/flang/Optimizer/Builder/Runtime/Character.h (+8)
- (modified) flang/include/flang/Optimizer/HLFIR/HLFIROps.td (+21)
- (modified) flang/lib/Lower/ConvertCall.cpp (+6-1)
- (modified) flang/lib/Lower/HlfirIntrinsics.cpp (+74-25)
- (modified) flang/lib/Optimizer/Builder/Runtime/Character.cpp (+26-12)
- (modified) flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp (+22)
- (modified) flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIRIntrinsics.cpp (+40-1)
- (modified) flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp (+209)
- (added) flang/test/HLFIR/index-lowering.fir (+198)
- (modified) flang/test/HLFIR/invalid.fir (+6)
- (added) flang/test/HLFIR/simplify-hlfir-intrinsics-index.fir (+345)
- (added) flang/test/Lower/HLFIR/index.f90 (+162)
- (modified) flang/test/Lower/volatile-string.f90 (+2-8)
``````````diff
diff --git a/flang/include/flang/Lower/HlfirIntrinsics.h b/flang/include/flang/Lower/HlfirIntrinsics.h
index f01f1c7dcd9bb..930bbeb6fb452 100644
--- a/flang/include/flang/Lower/HlfirIntrinsics.h
+++ b/flang/include/flang/Lower/HlfirIntrinsics.h
@@ -58,6 +58,14 @@ struct PreparedActualArgument {
/// call, the current element value will be returned.
hlfir::Entity getActual(mlir::Location loc, fir::FirOpBuilder &builder) const;
+ mlir::Type getFortranElementType() {
+ if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual))
+ return hlfir::getFortranElementType(actualEntity->getType());
+ mlir::Value entity =
+ std::get<hlfir::ElementalAddrOp>(actual).getElementEntity();
+ return hlfir::getFortranElementType(entity.getType());
+ }
+
void derefPointersAndAllocatables(mlir::Location loc,
fir::FirOpBuilder &builder) {
if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual))
diff --git a/flang/include/flang/Optimizer/Builder/Runtime/Character.h b/flang/include/flang/Optimizer/Builder/Runtime/Character.h
index d1c521de94438..261ac348a4024 100644
--- a/flang/include/flang/Optimizer/Builder/Runtime/Character.h
+++ b/flang/include/flang/Optimizer/Builder/Runtime/Character.h
@@ -65,6 +65,14 @@ mlir::Value genIndex(fir::FirOpBuilder &builder, mlir::Location loc, int kind,
mlir::Value substringBase, mlir::Value substringLen,
mlir::Value back);
+/// Generate call to INDEX runtime.
+/// This calls the simple runtime entry points based on the KIND of the string.
+/// A version of interface taking a `boxchar` for string and substring.
+/// Uses no-descriptors flow.
+mlir::Value genIndex(fir::FirOpBuilder &builder, mlir::Location loc,
+ const fir::ExtendedValue &str,
+ const fir::ExtendedValue &substr, mlir::Value back);
+
/// Generate call to INDEX runtime.
/// This calls the descriptor based runtime call implementation for the index
/// intrinsic.
diff --git a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
index 9a22b2dc2f58d..90512586a6520 100644
--- a/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
+++ b/flang/include/flang/Optimizer/HLFIR/HLFIROps.td
@@ -394,6 +394,27 @@ def hlfir_CharTrimOp
let builders = [OpBuilder<(ins "mlir::Value":$chr)>];
}
+def hlfir_IndexOp
+ : hlfir_Op<"index", [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
+ let summary = "index transformational intrinsic";
+ let description = [{
+ Search for a substring position within a string, optionally backward
+ if back is set to true.
+ }];
+
+ let arguments = (ins AnyScalarCharacterEntity:$substr,
+ AnyScalarCharacterEntity:$str,
+ Optional<Type<AnyLogicalLike.predicate>>:$back);
+
+ let results = (outs AnyIntegerType);
+
+ let assemblyFormat = [{
+ $substr `in` $str (`back` $back^)? attr-dict `:` functional-type(operands, results)
+ }];
+
+ let hasVerifier = 1;
+}
+
def hlfir_AllOp : hlfir_Op<"all", [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
let summary = "ALL transformational intrinsic";
let description = [{
diff --git a/flang/lib/Lower/ConvertCall.cpp b/flang/lib/Lower/ConvertCall.cpp
index cf8458f716ae5..b20785d3609e6 100644
--- a/flang/lib/Lower/ConvertCall.cpp
+++ b/flang/lib/Lower/ConvertCall.cpp
@@ -2193,10 +2193,15 @@ static std::optional<hlfir::EntityWithAttributes> genHLFIRIntrinsicRefCore(
const std::string intrinsicName = callContext.getProcedureName();
const fir::IntrinsicArgumentLoweringRules *argLowering =
intrinsicEntry.getArgumentLoweringRules();
+ mlir::Type resultType =
+ callContext.isElementalProcWithArrayArgs()
+ ? hlfir::getFortranElementType(*callContext.resultType)
+ : *callContext.resultType;
+
std::optional<hlfir::EntityWithAttributes> res =
Fortran::lower::lowerHlfirIntrinsic(builder, loc, intrinsicName,
loweredActuals, argLowering,
- *callContext.resultType);
+ resultType);
if (res)
return res;
}
diff --git a/flang/lib/Lower/HlfirIntrinsics.cpp b/flang/lib/Lower/HlfirIntrinsics.cpp
index b9731e993db8f..27c8bb87f7542 100644
--- a/flang/lib/Lower/HlfirIntrinsics.cpp
+++ b/flang/lib/Lower/HlfirIntrinsics.cpp
@@ -69,6 +69,11 @@ class HlfirTransformationalIntrinsic {
mlir::Value loadBoxAddress(
const std::optional<Fortran::lower::PreparedActualArgument> &arg);
+ mlir::Value
+ loadTrivialScalar(const Fortran::lower::PreparedActualArgument &arg);
+
+ mlir::Value loadOptionalValue(Fortran::lower::PreparedActualArgument &arg);
+
void addCleanup(std::optional<hlfir::CleanupFunction> cleanup) {
if (cleanup)
cleanupFns.emplace_back(std::move(*cleanup));
@@ -204,6 +209,17 @@ class HlfirReshapeLowering : public HlfirTransformationalIntrinsic {
mlir::Type stmtResultType) override;
};
+class HlfirIndexLowering : public HlfirTransformationalIntrinsic {
+public:
+ using HlfirTransformationalIntrinsic::HlfirTransformationalIntrinsic;
+
+protected:
+ mlir::Value
+ lowerImpl(const Fortran::lower::PreparedActualArguments &loweredActuals,
+ const fir::IntrinsicArgumentLoweringRules *argLowering,
+ mlir::Type stmtResultType) override;
+};
+
} // namespace
mlir::Value HlfirTransformationalIntrinsic::loadBoxAddress(
@@ -239,19 +255,22 @@ mlir::Value HlfirTransformationalIntrinsic::loadBoxAddress(
return boxOrAbsent;
}
-static mlir::Value loadOptionalValue(
- mlir::Location loc, fir::FirOpBuilder &builder,
- const std::optional<Fortran::lower::PreparedActualArgument> &arg,
- hlfir::Entity actual) {
- if (!arg->handleDynamicOptional())
- return hlfir::loadTrivialScalar(loc, builder, actual);
+mlir::Value HlfirTransformationalIntrinsic::loadOptionalValue(
+ Fortran::lower::PreparedActualArgument &arg) {
+ mlir::Type eleType = arg.getFortranElementType();
- mlir::Value isPresent = arg->getIsPresent();
- mlir::Type eleType = hlfir::getFortranElementType(actual.getType());
+ // For an elemental call, getActual() may produce
+ // a designator denoting the array element to be passed
+ // to the subprogram. If the actual array is dynamically
+ // optional the designator must be generated under
+ // isPresent check (see also genIntrinsicRefCore).
return builder
- .genIfOp(loc, {eleType}, isPresent,
+ .genIfOp(loc, {eleType}, arg.getIsPresent(),
/*withElseRegion=*/true)
.genThen([&]() {
+ hlfir::Entity actual = arg.getActual(loc, builder);
+ assert(eleType == actual.getFortranElementType() &&
+ "result type mismatch in genOptionalValue");
assert(actual.isScalar() && fir::isa_trivial(eleType) &&
"must be a numerical or logical scalar");
hlfir::Entity val = hlfir::loadTrivialScalar(loc, builder, actual);
@@ -264,6 +283,12 @@ static mlir::Value loadOptionalValue(
.getResults()[0];
}
+mlir::Value HlfirTransformationalIntrinsic::loadTrivialScalar(
+ const Fortran::lower::PreparedActualArgument &arg) {
+ hlfir::Entity actual = arg.getActual(loc, builder);
+ return hlfir::loadTrivialScalar(loc, builder, actual);
+}
+
llvm::SmallVector<mlir::Value> HlfirTransformationalIntrinsic::getOperandVector(
const Fortran::lower::PreparedActualArguments &loweredActuals,
const fir::IntrinsicArgumentLoweringRules *argLowering) {
@@ -277,29 +302,33 @@ llvm::SmallVector<mlir::Value> HlfirTransformationalIntrinsic::getOperandVector(
operands.emplace_back();
continue;
}
- hlfir::Entity actual = arg->getActual(loc, builder);
mlir::Value valArg;
-
if (!argLowering) {
- valArg = hlfir::loadTrivialScalar(loc, builder, actual);
- } else {
- fir::ArgLoweringRule argRules =
- fir::lowerIntrinsicArgumentAs(*argLowering, i);
- if (argRules.lowerAs == fir::LowerIntrinsicArgAs::Box)
- valArg = loadBoxAddress(arg);
- else if (!argRules.handleDynamicOptional &&
- argRules.lowerAs != fir::LowerIntrinsicArgAs::Inquired)
- valArg = hlfir::derefPointersAndAllocatables(loc, builder, actual);
- else if (argRules.handleDynamicOptional &&
- argRules.lowerAs == fir::LowerIntrinsicArgAs::Value)
- valArg = loadOptionalValue(loc, builder, arg, actual);
- else if (argRules.handleDynamicOptional)
+ valArg = loadTrivialScalar(*arg);
+ operands.emplace_back(valArg);
+ continue;
+ }
+ fir::ArgLoweringRule argRules =
+ fir::lowerIntrinsicArgumentAs(*argLowering, i);
+ if (argRules.lowerAs == fir::LowerIntrinsicArgAs::Box) {
+ valArg = loadBoxAddress(arg);
+ } else if (argRules.handleDynamicOptional) {
+ if (argRules.lowerAs == fir::LowerIntrinsicArgAs::Value) {
+ if (arg->handleDynamicOptional())
+ valArg = loadOptionalValue(*arg);
+ else
+ valArg = loadTrivialScalar(*arg);
+ } else {
TODO(loc, "hlfir transformational intrinsic dynamically optional "
"argument without box lowering");
+ }
+ } else {
+ hlfir::Entity actual = arg->getActual(loc, builder);
+ if (argRules.lowerAs != fir::LowerIntrinsicArgAs::Inquired)
+ valArg = hlfir::derefPointersAndAllocatables(loc, builder, actual);
else
valArg = actual.getBase();
}
-
operands.emplace_back(valArg);
}
return operands;
@@ -513,6 +542,22 @@ mlir::Value HlfirReshapeLowering::lowerImpl(
operands[2], operands[3]);
}
+mlir::Value HlfirIndexLowering::lowerImpl(
+ const Fortran::lower::PreparedActualArguments &loweredActuals,
+ const fir::IntrinsicArgumentLoweringRules *argLowering,
+ mlir::Type stmtResultType) {
+ auto operands = getOperandVector(loweredActuals, argLowering);
+ // 'kind' optional operand is unused here as it has already been
+ // translated into result type.
+ assert(operands.size() == 4);
+ mlir::Value substr = operands[1];
+ mlir::Value str = operands[0];
+ mlir::Value back = operands[2];
+ mlir::Value result =
+ createOp<hlfir::IndexOp>(stmtResultType, substr, str, back);
+ return result;
+}
+
std::optional<hlfir::EntityWithAttributes> Fortran::lower::lowerHlfirIntrinsic(
fir::FirOpBuilder &builder, mlir::Location loc, const std::string &name,
const Fortran::lower::PreparedActualArguments &loweredActuals,
@@ -567,6 +612,10 @@ std::optional<hlfir::EntityWithAttributes> Fortran::lower::lowerHlfirIntrinsic(
if (name == "reshape")
return HlfirReshapeLowering{builder, loc}.lower(loweredActuals, argLowering,
stmtResultType);
+ if (name == "index")
+ return HlfirIndexLowering{builder, loc}.lower(loweredActuals, argLowering,
+ stmtResultType);
+
if (mlir::isa<fir::CharacterType>(stmtResultType)) {
if (name == "min")
return HlfirCharExtremumLowering{builder, loc,
diff --git a/flang/lib/Optimizer/Builder/Runtime/Character.cpp b/flang/lib/Optimizer/Builder/Runtime/Character.cpp
index 57fb0cccf6863..540ecba299dc3 100644
--- a/flang/lib/Optimizer/Builder/Runtime/Character.cpp
+++ b/flang/lib/Optimizer/Builder/Runtime/Character.cpp
@@ -119,23 +119,23 @@ fir::runtime::genCharCompare(fir::FirOpBuilder &builder, mlir::Location loc,
return mlir::arith::CmpIOp::create(builder, loc, cmp, tri, zero);
}
+static mlir::Value allocateIfNotInMemory(fir::FirOpBuilder &builder,
+ mlir::Location loc, mlir::Value base) {
+ if (fir::isa_ref_type(base.getType()))
+ return base;
+ auto mem =
+ fir::AllocaOp::create(builder, loc, base.getType(), /*pinned=*/false);
+ fir::StoreOp::create(builder, loc, base, mem);
+ return mem;
+}
+
mlir::Value fir::runtime::genCharCompare(fir::FirOpBuilder &builder,
mlir::Location loc,
mlir::arith::CmpIPredicate cmp,
const fir::ExtendedValue &lhs,
const fir::ExtendedValue &rhs) {
- if (lhs.getBoxOf<fir::BoxValue>() || rhs.getBoxOf<fir::BoxValue>())
- TODO(loc, "character compare from descriptors");
- auto allocateIfNotInMemory = [&](mlir::Value base) -> mlir::Value {
- if (fir::isa_ref_type(base.getType()))
- return base;
- auto mem =
- fir::AllocaOp::create(builder, loc, base.getType(), /*pinned=*/false);
- fir::StoreOp::create(builder, loc, base, mem);
- return mem;
- };
- auto lhsBuffer = allocateIfNotInMemory(fir::getBase(lhs));
- auto rhsBuffer = allocateIfNotInMemory(fir::getBase(rhs));
+ auto lhsBuffer = allocateIfNotInMemory(builder, loc, fir::getBase(lhs));
+ auto rhsBuffer = allocateIfNotInMemory(builder, loc, fir::getBase(rhs));
return genCharCompare(builder, loc, cmp, lhsBuffer, fir::getLen(lhs),
rhsBuffer, fir::getLen(rhs));
}
@@ -168,6 +168,20 @@ mlir::Value fir::runtime::genIndex(fir::FirOpBuilder &builder,
return fir::CallOp::create(builder, loc, indexFunc, args).getResult(0);
}
+mlir::Value fir::runtime::genIndex(fir::FirOpBuilder &builder,
+ mlir::Location loc,
+ const fir::ExtendedValue &str,
+ const fir::ExtendedValue &substr,
+ mlir::Value back) {
+ assert(!substr.getBoxOf<fir::BoxValue>() && !str.getBoxOf<fir::BoxValue>() &&
+ "shall use genIndexDescriptor version");
+ auto strBuffer = allocateIfNotInMemory(builder, loc, fir::getBase(str));
+ auto substrBuffer = allocateIfNotInMemory(builder, loc, fir::getBase(substr));
+ int kind = discoverKind(strBuffer.getType());
+ return genIndex(builder, loc, kind, strBuffer, fir::getLen(str), substrBuffer,
+ fir::getLen(substr), back);
+}
+
void fir::runtime::genIndexDescriptor(fir::FirOpBuilder &builder,
mlir::Location loc, mlir::Value resultBox,
mlir::Value stringBox,
diff --git a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
index ffec4ffbb3b80..1a63b1bea3177 100644
--- a/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
+++ b/flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
@@ -878,6 +878,28 @@ void hlfir::CharTrimOp::getEffects(
getIntrinsicEffects(getOperation(), effects);
}
+//===----------------------------------------------------------------------===//
+// IndexOp
+//===----------------------------------------------------------------------===//
+
+llvm::LogicalResult hlfir::IndexOp::verify() {
+ mlir::Value substr = getSubstr();
+ mlir::Value str = getStr();
+
+ unsigned charKind = getCharacterKind(substr.getType());
+ if (charKind != getCharacterKind(str.getType()))
+ return emitOpError("character arguments must have the same KIND");
+
+ return mlir::success();
+}
+
+void hlfir::IndexOp::getEffects(
+ llvm::SmallVectorImpl<
+ mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
+ &effects) {
+ getIntrinsicEffects(getOperation(), effects);
+}
+
//===----------------------------------------------------------------------===//
// NumericalReductionOp
//===----------------------------------------------------------------------===//
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIRIntrinsics.cpp b/flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIRIntrinsics.cpp
index a913cfadcefc2..4239e579ae70b 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIRIntrinsics.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIRIntrinsics.cpp
@@ -613,6 +613,45 @@ class CharTrimOpConversion
}
};
+class IndexOpConversion : public HlfirIntrinsicConversion<hlfir::IndexOp> {
+ using HlfirIntrinsicConversion<hlfir::IndexOp>::HlfirIntrinsicConversion;
+
+ llvm::LogicalResult
+ matchAndRewrite(hlfir::IndexOp op,
+ mlir::PatternRewriter &rewriter) const override {
+ fir::FirOpBuilder builder{rewriter, op.getOperation()};
+ const mlir::Location &loc = op->getLoc();
+ hlfir::Entity substr{op.getSubstr()};
+ hlfir::Entity str{op.getStr()};
+
+ auto [substrExv, substrCleanUp] =
+ hlfir::translateToExtendedValue(loc, builder, substr);
+ auto [strExv, strCleanUp] =
+ hlfir::translateToExtendedValue(loc, builder, str);
+
+ mlir::Value back = op.getBack();
+ if (!back)
+ back = builder.createBool(loc, false);
+
+ mlir::Value result =
+ fir::runtime::genIndex(builder, loc, strExv, substrExv, back);
+ result = builder.createConvert(loc, op.getType(), result);
+ if (strCleanUp || substrCleanUp) {
+ mlir::OpBuilder::InsertionGuard guard(builder);
+ builder.setInsertionPointAfter(op);
+ if (strCleanUp)
+ (*strCleanUp)();
+ if (substrCleanUp)
+ (*substrCleanUp)();
+ }
+ auto resultEntity = hlfir::EntityWithAttributes{result};
+
+ processReturnValue(op, resultEntity, /*mustBeFreed=*/false, builder,
+ rewriter);
+ return mlir::success();
+ }
+};
+
class LowerHLFIRIntrinsics
: public hlfir::impl::LowerHLFIRIntrinsicsBase<LowerHLFIRIntrinsics> {
public:
@@ -627,7 +666,7 @@ class LowerHLFIRIntrinsics
MaxvalOpConversion, MinvalOpConversion, MinlocOpConversion,
MaxlocOpConversion, ArrayShiftOpConversion<hlfir::CShiftOp>,
ArrayShiftOpConversion<hlfir::EOShiftOp>, ReshapeOpConversion,
- CmpCharOpConversion, CharTrimOpConversion>(context);
+ CmpCharOpConversion, CharTrimOpConversion, IndexOpConversion>(context);
// While conceptually this pass is performing dialect conversion, we use
// pattern rewrites here instead of dialect conversion because this pass
diff --git a/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp b/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp
index d8e36ea294cdb..67112382c0e8f 100644
--- a/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp
+++ b/flang/lib/Optimizer/HLFIR/Transforms/SimplifyHLFIRIntrinsics.cpp
@@ -2284,6 +2284,214 @@ class CmpCharOpConversion : public mlir::OpRewritePattern<hlfir::CmpCharOp> {
}
};
+static std::pair<mlir::Value, hlfir::AssociateOp>
+getVariable(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value val) {
+ // If it is an expression - create a variable from it, or forward
+ // the value otherwise.
+ hlfir::AssociateOp associate;
+ if (!mlir::isa<hlfir::ExprType>(val.getType()))
+ return {val, associate};
+ hlfir::Entity entity{val};
+ mlir::NamedAttribute byRefAttr = fir::getAdaptToByRefAttr(builder);
+ associate = hlfir::genAssociateExpr(loc, builder, entity, entity.getType(),
+ "", byRefAttr);
+ return {associate.getBase(), associate};
+}
+
+class IndexOpConversion : public mlir::OpRewritePattern<hlfir::IndexOp> {
+public:
+ using mlir::OpRewritePattern<hlfir::IndexOp>::OpRewritePattern;
+
+ llvm::LogicalResult
+ matchAndRewrite(hlfir::IndexOp op,
+ mlir::PatternRewriter &rewriter) const override {
+ // We simplify only limited cases:
+ // 1) a substring length shall be known at compile time
+ // 2) if a substring length is 0 then replace with 1 for forward search,
+ // or otherwise with the string length + 1 (builder shall const-fold if
+ // lookup direction is known at compile time).
+ // 3) for known string length at compile time, if it is
+ // shorter than substring => replace with zero.
+ // 4) if a substring length is one => inline as simple search loop
+ // 5) for forward search with input strings of kind=1 runtime is faster.
+ // Do not simplify in all the other cases relying on a runtime call.
+
+ fir::FirOpBuilder builder{rewriter, op.getOperation()};
+ const mlir::Location &loc = op->getLoc();
+
+ auto resultTy = op.getType();
+ mlir::Value back = op.getBack();
+ mlir::Value substrLen =
+ hlfir::genCharLength(loc, builder, hlfir::Entity{op.getSubstr()});
+
+ auto substrLenCst = fir::getIntIfConstant(substrLen);
+ if (!substr...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/157883
More information about the flang-commits
mailing list