Skip to content

Commit 1495cea

Browse files
authored
[flang] Add hlfir.index op to represent index intrinsic function (#157575)
The change adds a new HLFIR operation. A call to index intrinsic now becomes lowered into the hlfir.index op and then naive lowering of the op translates it back to appropriate runtime call. The change set is aimed to be functionally equivalent to exiting index functionality, but is much more efficient in a case of presence of the 'kind' intrinsic parameter. Also fixed couple of parameter lowering issues which were revealed while working on the index-related functional parts.
1 parent f1cdb44 commit 1495cea

File tree

12 files changed

+573
-47
lines changed

12 files changed

+573
-47
lines changed

flang/include/flang/Lower/HlfirIntrinsics.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,14 @@ struct PreparedActualArgument {
5858
/// call, the current element value will be returned.
5959
hlfir::Entity getActual(mlir::Location loc, fir::FirOpBuilder &builder) const;
6060

61+
mlir::Type getFortranElementType() {
62+
if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual))
63+
return hlfir::getFortranElementType(actualEntity->getType());
64+
mlir::Value entity =
65+
std::get<hlfir::ElementalAddrOp>(actual).getElementEntity();
66+
return hlfir::getFortranElementType(entity.getType());
67+
}
68+
6169
void derefPointersAndAllocatables(mlir::Location loc,
6270
fir::FirOpBuilder &builder) {
6371
if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual))

flang/include/flang/Optimizer/Builder/Runtime/Character.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ mlir::Value genIndex(fir::FirOpBuilder &builder, mlir::Location loc, int kind,
6565
mlir::Value substringBase, mlir::Value substringLen,
6666
mlir::Value back);
6767

68+
/// Generate call to INDEX runtime.
69+
/// This calls the simple runtime entry points based on the KIND of the string.
70+
/// A version of interface taking a `boxchar` for string and substring.
71+
/// Uses no-descriptors flow.
72+
mlir::Value genIndex(fir::FirOpBuilder &builder, mlir::Location loc,
73+
const fir::ExtendedValue &str,
74+
const fir::ExtendedValue &substr, mlir::Value back);
75+
6876
/// Generate call to INDEX runtime.
6977
/// This calls the descriptor based runtime call implementation for the index
7078
/// intrinsic.

flang/include/flang/Optimizer/HLFIR/HLFIROps.td

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,27 @@ def hlfir_CharTrimOp
394394
let builders = [OpBuilder<(ins "mlir::Value":$chr)>];
395395
}
396396

397+
def hlfir_IndexOp
398+
: hlfir_Op<"index", [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
399+
let summary = "index transformational intrinsic";
400+
let description = [{
401+
Search for a substring position within a string, optionally backward
402+
if back is set to true.
403+
}];
404+
405+
let arguments = (ins AnyScalarCharacterEntity:$substr,
406+
AnyScalarCharacterEntity:$str,
407+
Optional<Type<AnyLogicalLike.predicate>>:$back);
408+
409+
let results = (outs AnyIntegerType);
410+
411+
let assemblyFormat = [{
412+
$substr `in` $str (`back` $back^)? attr-dict `:` functional-type(operands, results)
413+
}];
414+
415+
let hasVerifier = 1;
416+
}
417+
397418
def hlfir_AllOp : hlfir_Op<"all", [DeclareOpInterfaceMethods<MemoryEffectsOpInterface>]> {
398419
let summary = "ALL transformational intrinsic";
399420
let description = [{

flang/lib/Lower/ConvertCall.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2214,10 +2214,15 @@ static std::optional<hlfir::EntityWithAttributes> genHLFIRIntrinsicRefCore(
22142214
const std::string intrinsicName = callContext.getProcedureName();
22152215
const fir::IntrinsicArgumentLoweringRules *argLowering =
22162216
intrinsicEntry.getArgumentLoweringRules();
2217+
mlir::Type resultType =
2218+
callContext.isElementalProcWithArrayArgs()
2219+
? hlfir::getFortranElementType(*callContext.resultType)
2220+
: *callContext.resultType;
2221+
22172222
std::optional<hlfir::EntityWithAttributes> res =
22182223
Fortran::lower::lowerHlfirIntrinsic(builder, loc, intrinsicName,
22192224
loweredActuals, argLowering,
2220-
*callContext.resultType);
2225+
resultType);
22212226
if (res)
22222227
return res;
22232228
}

flang/lib/Lower/HlfirIntrinsics.cpp

Lines changed: 74 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,11 @@ class HlfirTransformationalIntrinsic {
6969
mlir::Value loadBoxAddress(
7070
const std::optional<Fortran::lower::PreparedActualArgument> &arg);
7171

72+
mlir::Value
73+
loadTrivialScalar(const Fortran::lower::PreparedActualArgument &arg);
74+
75+
mlir::Value loadOptionalValue(Fortran::lower::PreparedActualArgument &arg);
76+
7277
void addCleanup(std::optional<hlfir::CleanupFunction> cleanup) {
7378
if (cleanup)
7479
cleanupFns.emplace_back(std::move(*cleanup));
@@ -204,6 +209,17 @@ class HlfirReshapeLowering : public HlfirTransformationalIntrinsic {
204209
mlir::Type stmtResultType) override;
205210
};
206211

212+
class HlfirIndexLowering : public HlfirTransformationalIntrinsic {
213+
public:
214+
using HlfirTransformationalIntrinsic::HlfirTransformationalIntrinsic;
215+
216+
protected:
217+
mlir::Value
218+
lowerImpl(const Fortran::lower::PreparedActualArguments &loweredActuals,
219+
const fir::IntrinsicArgumentLoweringRules *argLowering,
220+
mlir::Type stmtResultType) override;
221+
};
222+
207223
} // namespace
208224

209225
mlir::Value HlfirTransformationalIntrinsic::loadBoxAddress(
@@ -239,19 +255,22 @@ mlir::Value HlfirTransformationalIntrinsic::loadBoxAddress(
239255
return boxOrAbsent;
240256
}
241257

242-
static mlir::Value loadOptionalValue(
243-
mlir::Location loc, fir::FirOpBuilder &builder,
244-
const std::optional<Fortran::lower::PreparedActualArgument> &arg,
245-
hlfir::Entity actual) {
246-
if (!arg->handleDynamicOptional())
247-
return hlfir::loadTrivialScalar(loc, builder, actual);
258+
mlir::Value HlfirTransformationalIntrinsic::loadOptionalValue(
259+
Fortran::lower::PreparedActualArgument &arg) {
260+
mlir::Type eleType = arg.getFortranElementType();
248261

249-
mlir::Value isPresent = arg->getIsPresent();
250-
mlir::Type eleType = hlfir::getFortranElementType(actual.getType());
262+
// For an elemental call, getActual() may produce
263+
// a designator denoting the array element to be passed
264+
// to the subprogram. If the actual array is dynamically
265+
// optional the designator must be generated under
266+
// isPresent check (see also genIntrinsicRefCore).
251267
return builder
252-
.genIfOp(loc, {eleType}, isPresent,
268+
.genIfOp(loc, {eleType}, arg.getIsPresent(),
253269
/*withElseRegion=*/true)
254270
.genThen([&]() {
271+
hlfir::Entity actual = arg.getActual(loc, builder);
272+
assert(eleType == actual.getFortranElementType() &&
273+
"result type mismatch in genOptionalValue");
255274
assert(actual.isScalar() && fir::isa_trivial(eleType) &&
256275
"must be a numerical or logical scalar");
257276
hlfir::Entity val = hlfir::loadTrivialScalar(loc, builder, actual);
@@ -264,6 +283,12 @@ static mlir::Value loadOptionalValue(
264283
.getResults()[0];
265284
}
266285

286+
mlir::Value HlfirTransformationalIntrinsic::loadTrivialScalar(
287+
const Fortran::lower::PreparedActualArgument &arg) {
288+
hlfir::Entity actual = arg.getActual(loc, builder);
289+
return hlfir::loadTrivialScalar(loc, builder, actual);
290+
}
291+
267292
llvm::SmallVector<mlir::Value> HlfirTransformationalIntrinsic::getOperandVector(
268293
const Fortran::lower::PreparedActualArguments &loweredActuals,
269294
const fir::IntrinsicArgumentLoweringRules *argLowering) {
@@ -277,29 +302,33 @@ llvm::SmallVector<mlir::Value> HlfirTransformationalIntrinsic::getOperandVector(
277302
operands.emplace_back();
278303
continue;
279304
}
280-
hlfir::Entity actual = arg->getActual(loc, builder);
281305
mlir::Value valArg;
282-
283306
if (!argLowering) {
284-
valArg = hlfir::loadTrivialScalar(loc, builder, actual);
285-
} else {
286-
fir::ArgLoweringRule argRules =
287-
fir::lowerIntrinsicArgumentAs(*argLowering, i);
288-
if (argRules.lowerAs == fir::LowerIntrinsicArgAs::Box)
289-
valArg = loadBoxAddress(arg);
290-
else if (!argRules.handleDynamicOptional &&
291-
argRules.lowerAs != fir::LowerIntrinsicArgAs::Inquired)
292-
valArg = hlfir::derefPointersAndAllocatables(loc, builder, actual);
293-
else if (argRules.handleDynamicOptional &&
294-
argRules.lowerAs == fir::LowerIntrinsicArgAs::Value)
295-
valArg = loadOptionalValue(loc, builder, arg, actual);
296-
else if (argRules.handleDynamicOptional)
307+
valArg = loadTrivialScalar(*arg);
308+
operands.emplace_back(valArg);
309+
continue;
310+
}
311+
fir::ArgLoweringRule argRules =
312+
fir::lowerIntrinsicArgumentAs(*argLowering, i);
313+
if (argRules.lowerAs == fir::LowerIntrinsicArgAs::Box) {
314+
valArg = loadBoxAddress(arg);
315+
} else if (argRules.handleDynamicOptional) {
316+
if (argRules.lowerAs == fir::LowerIntrinsicArgAs::Value) {
317+
if (arg->handleDynamicOptional())
318+
valArg = loadOptionalValue(*arg);
319+
else
320+
valArg = loadTrivialScalar(*arg);
321+
} else {
297322
TODO(loc, "hlfir transformational intrinsic dynamically optional "
298323
"argument without box lowering");
324+
}
325+
} else {
326+
hlfir::Entity actual = arg->getActual(loc, builder);
327+
if (argRules.lowerAs != fir::LowerIntrinsicArgAs::Inquired)
328+
valArg = hlfir::derefPointersAndAllocatables(loc, builder, actual);
299329
else
300330
valArg = actual.getBase();
301331
}
302-
303332
operands.emplace_back(valArg);
304333
}
305334
return operands;
@@ -513,6 +542,22 @@ mlir::Value HlfirReshapeLowering::lowerImpl(
513542
operands[2], operands[3]);
514543
}
515544

545+
mlir::Value HlfirIndexLowering::lowerImpl(
546+
const Fortran::lower::PreparedActualArguments &loweredActuals,
547+
const fir::IntrinsicArgumentLoweringRules *argLowering,
548+
mlir::Type stmtResultType) {
549+
auto operands = getOperandVector(loweredActuals, argLowering);
550+
// 'kind' optional operand is unused here as it has already been
551+
// translated into result type.
552+
assert(operands.size() == 4);
553+
mlir::Value substr = operands[1];
554+
mlir::Value str = operands[0];
555+
mlir::Value back = operands[2];
556+
mlir::Value result =
557+
createOp<hlfir::IndexOp>(stmtResultType, substr, str, back);
558+
return result;
559+
}
560+
516561
std::optional<hlfir::EntityWithAttributes> Fortran::lower::lowerHlfirIntrinsic(
517562
fir::FirOpBuilder &builder, mlir::Location loc, const std::string &name,
518563
const Fortran::lower::PreparedActualArguments &loweredActuals,
@@ -567,6 +612,10 @@ std::optional<hlfir::EntityWithAttributes> Fortran::lower::lowerHlfirIntrinsic(
567612
if (name == "reshape")
568613
return HlfirReshapeLowering{builder, loc}.lower(loweredActuals, argLowering,
569614
stmtResultType);
615+
if (name == "index")
616+
return HlfirIndexLowering{builder, loc}.lower(loweredActuals, argLowering,
617+
stmtResultType);
618+
570619
if (mlir::isa<fir::CharacterType>(stmtResultType)) {
571620
if (name == "min")
572621
return HlfirCharExtremumLowering{builder, loc,

flang/lib/Optimizer/Builder/Runtime/Character.cpp

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -119,23 +119,23 @@ fir::runtime::genCharCompare(fir::FirOpBuilder &builder, mlir::Location loc,
119119
return mlir::arith::CmpIOp::create(builder, loc, cmp, tri, zero);
120120
}
121121

122+
static mlir::Value allocateIfNotInMemory(fir::FirOpBuilder &builder,
123+
mlir::Location loc, mlir::Value base) {
124+
if (fir::isa_ref_type(base.getType()))
125+
return base;
126+
auto mem =
127+
fir::AllocaOp::create(builder, loc, base.getType(), /*pinned=*/false);
128+
fir::StoreOp::create(builder, loc, base, mem);
129+
return mem;
130+
}
131+
122132
mlir::Value fir::runtime::genCharCompare(fir::FirOpBuilder &builder,
123133
mlir::Location loc,
124134
mlir::arith::CmpIPredicate cmp,
125135
const fir::ExtendedValue &lhs,
126136
const fir::ExtendedValue &rhs) {
127-
if (lhs.getBoxOf<fir::BoxValue>() || rhs.getBoxOf<fir::BoxValue>())
128-
TODO(loc, "character compare from descriptors");
129-
auto allocateIfNotInMemory = [&](mlir::Value base) -> mlir::Value {
130-
if (fir::isa_ref_type(base.getType()))
131-
return base;
132-
auto mem =
133-
fir::AllocaOp::create(builder, loc, base.getType(), /*pinned=*/false);
134-
fir::StoreOp::create(builder, loc, base, mem);
135-
return mem;
136-
};
137-
auto lhsBuffer = allocateIfNotInMemory(fir::getBase(lhs));
138-
auto rhsBuffer = allocateIfNotInMemory(fir::getBase(rhs));
137+
auto lhsBuffer = allocateIfNotInMemory(builder, loc, fir::getBase(lhs));
138+
auto rhsBuffer = allocateIfNotInMemory(builder, loc, fir::getBase(rhs));
139139
return genCharCompare(builder, loc, cmp, lhsBuffer, fir::getLen(lhs),
140140
rhsBuffer, fir::getLen(rhs));
141141
}
@@ -168,6 +168,20 @@ mlir::Value fir::runtime::genIndex(fir::FirOpBuilder &builder,
168168
return fir::CallOp::create(builder, loc, indexFunc, args).getResult(0);
169169
}
170170

171+
mlir::Value fir::runtime::genIndex(fir::FirOpBuilder &builder,
172+
mlir::Location loc,
173+
const fir::ExtendedValue &str,
174+
const fir::ExtendedValue &substr,
175+
mlir::Value back) {
176+
assert(!substr.getBoxOf<fir::BoxValue>() && !str.getBoxOf<fir::BoxValue>() &&
177+
"shall use genIndexDescriptor version");
178+
auto strBuffer = allocateIfNotInMemory(builder, loc, fir::getBase(str));
179+
auto substrBuffer = allocateIfNotInMemory(builder, loc, fir::getBase(substr));
180+
int kind = discoverKind(strBuffer.getType());
181+
return genIndex(builder, loc, kind, strBuffer, fir::getLen(str), substrBuffer,
182+
fir::getLen(substr), back);
183+
}
184+
171185
void fir::runtime::genIndexDescriptor(fir::FirOpBuilder &builder,
172186
mlir::Location loc, mlir::Value resultBox,
173187
mlir::Value stringBox,

flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -878,6 +878,28 @@ void hlfir::CharTrimOp::getEffects(
878878
getIntrinsicEffects(getOperation(), effects);
879879
}
880880

881+
//===----------------------------------------------------------------------===//
882+
// IndexOp
883+
//===----------------------------------------------------------------------===//
884+
885+
llvm::LogicalResult hlfir::IndexOp::verify() {
886+
mlir::Value substr = getSubstr();
887+
mlir::Value str = getStr();
888+
889+
unsigned charKind = getCharacterKind(substr.getType());
890+
if (charKind != getCharacterKind(str.getType()))
891+
return emitOpError("character arguments must have the same KIND");
892+
893+
return mlir::success();
894+
}
895+
896+
void hlfir::IndexOp::getEffects(
897+
llvm::SmallVectorImpl<
898+
mlir::SideEffects::EffectInstance<mlir::MemoryEffects::Effect>>
899+
&effects) {
900+
getIntrinsicEffects(getOperation(), effects);
901+
}
902+
881903
//===----------------------------------------------------------------------===//
882904
// NumericalReductionOp
883905
//===----------------------------------------------------------------------===//

flang/lib/Optimizer/HLFIR/Transforms/LowerHLFIRIntrinsics.cpp

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,45 @@ class CharTrimOpConversion
613613
}
614614
};
615615

616+
class IndexOpConversion : public HlfirIntrinsicConversion<hlfir::IndexOp> {
617+
using HlfirIntrinsicConversion<hlfir::IndexOp>::HlfirIntrinsicConversion;
618+
619+
llvm::LogicalResult
620+
matchAndRewrite(hlfir::IndexOp op,
621+
mlir::PatternRewriter &rewriter) const override {
622+
fir::FirOpBuilder builder{rewriter, op.getOperation()};
623+
const mlir::Location &loc = op->getLoc();
624+
hlfir::Entity substr{op.getSubstr()};
625+
hlfir::Entity str{op.getStr()};
626+
627+
auto [substrExv, substrCleanUp] =
628+
hlfir::translateToExtendedValue(loc, builder, substr);
629+
auto [strExv, strCleanUp] =
630+
hlfir::translateToExtendedValue(loc, builder, str);
631+
632+
mlir::Value back = op.getBack();
633+
if (!back)
634+
back = builder.createBool(loc, false);
635+
636+
mlir::Value result =
637+
fir::runtime::genIndex(builder, loc, strExv, substrExv, back);
638+
result = builder.createConvert(loc, op.getType(), result);
639+
if (strCleanUp || substrCleanUp) {
640+
mlir::OpBuilder::InsertionGuard guard(builder);
641+
builder.setInsertionPointAfter(op);
642+
if (strCleanUp)
643+
(*strCleanUp)();
644+
if (substrCleanUp)
645+
(*substrCleanUp)();
646+
}
647+
auto resultEntity = hlfir::EntityWithAttributes{result};
648+
649+
processReturnValue(op, resultEntity, /*mustBeFreed=*/false, builder,
650+
rewriter);
651+
return mlir::success();
652+
}
653+
};
654+
616655
class LowerHLFIRIntrinsics
617656
: public hlfir::impl::LowerHLFIRIntrinsicsBase<LowerHLFIRIntrinsics> {
618657
public:
@@ -627,7 +666,7 @@ class LowerHLFIRIntrinsics
627666
MaxvalOpConversion, MinvalOpConversion, MinlocOpConversion,
628667
MaxlocOpConversion, ArrayShiftOpConversion<hlfir::CShiftOp>,
629668
ArrayShiftOpConversion<hlfir::EOShiftOp>, ReshapeOpConversion,
630-
CmpCharOpConversion, CharTrimOpConversion>(context);
669+
CmpCharOpConversion, CharTrimOpConversion, IndexOpConversion>(context);
631670

632671
// While conceptually this pass is performing dialect conversion, we use
633672
// pattern rewrites here instead of dialect conversion because this pass

0 commit comments

Comments
 (0)