Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions flang/include/flang/Lower/HlfirIntrinsics.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ struct PreparedActualArgument {
/// call, the current element value will be returned.
hlfir::Entity getActual(mlir::Location loc, fir::FirOpBuilder &builder) const;

/// Lower the actual argument that needs to be handled as dynamically
/// optional Value.
mlir::Value getOptionalValue(mlir::Location loc,
fir::FirOpBuilder &builder) const;

void derefPointersAndAllocatables(mlir::Location loc,
fir::FirOpBuilder &builder) {
if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual))
Expand Down
8 changes: 8 additions & 0 deletions flang/include/flang/Optimizer/Builder/Runtime/Character.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
21 changes: 21 additions & 0 deletions flang/include/flang/Optimizer/HLFIR/HLFIROps.td
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [{
Expand Down
39 changes: 38 additions & 1 deletion flang/lib/Lower/ConvertCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -3039,6 +3044,38 @@ hlfir::Entity Fortran::lower::PreparedActualArgument::getActual(
return hlfir::Entity{addr};
}

mlir::Value Fortran::lower::PreparedActualArgument::getOptionalValue(
mlir::Location loc, fir::FirOpBuilder &builder) const {
mlir::Type eleType;
if (auto *actualEntity = std::get_if<hlfir::Entity>(&actual))
eleType = hlfir::getFortranElementType(actualEntity->getType());
else
TODO(loc, "compute element type from hlfir::ElementalAddrOp");

// 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}, getIsPresent(),
/*withElseRegion=*/true)
.genThen([&]() {
hlfir::Entity actual = 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);
fir::ResultOp::create(builder, loc, val);
})
.genElse([&]() {
mlir::Value zero = fir::factory::createZeroValue(builder, loc, eleType);
fir::ResultOp::create(builder, loc, zero);
})
.getResults()[0];
}

bool Fortran::lower::isIntrinsicModuleProcRef(
const Fortran::evaluate::ProcedureRef &procRef) {
const Fortran::semantics::Symbol *symbol = procRef.proc().GetSymbol();
Expand Down
110 changes: 71 additions & 39 deletions flang/lib/Lower/HlfirIntrinsics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ class HlfirTransformationalIntrinsic {
mlir::Value loadBoxAddress(
const std::optional<Fortran::lower::PreparedActualArgument> &arg);

mlir::Value loadTrivialScalar(
const std::optional<Fortran::lower::PreparedActualArgument> &arg);

void addCleanup(std::optional<hlfir::CleanupFunction> cleanup) {
if (cleanup)
cleanupFns.emplace_back(std::move(*cleanup));
Expand Down Expand Up @@ -204,6 +207,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(
Expand Down Expand Up @@ -239,29 +253,10 @@ 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 isPresent = arg->getIsPresent();
mlir::Type eleType = hlfir::getFortranElementType(actual.getType());
return builder
.genIfOp(loc, {eleType}, isPresent,
/*withElseRegion=*/true)
.genThen([&]() {
assert(actual.isScalar() && fir::isa_trivial(eleType) &&
"must be a numerical or logical scalar");
hlfir::Entity val = hlfir::loadTrivialScalar(loc, builder, actual);
fir::ResultOp::create(builder, loc, val);
})
.genElse([&]() {
mlir::Value zero = fir::factory::createZeroValue(builder, loc, eleType);
fir::ResultOp::create(builder, loc, zero);
})
.getResults()[0];
mlir::Value HlfirTransformationalIntrinsic::loadTrivialScalar(
const std::optional<Fortran::lower::PreparedActualArgument> &arg) {
hlfir::Entity actual = arg->getActual(loc, builder);
return hlfir::loadTrivialScalar(loc, builder, actual);
}

llvm::SmallVector<mlir::Value> HlfirTransformationalIntrinsic::getOperandVector(
Expand All @@ -277,29 +272,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 = arg->getOptionalValue(loc, builder);
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;
Expand Down Expand Up @@ -513,6 +512,35 @@ 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);
assert(operands.size() == 4);
mlir::Value substr = operands[1];
mlir::Value str = operands[0];
mlir::Value back = operands[2];
mlir::Value kind = operands[3];

mlir::Type resultType;
if (kind) {
auto kindCst = fir::getIntIfConstant(kind);
assert(kindCst &&
"kind argument of index must be an integer constant expression");
unsigned bits = builder.getKindMap().getIntegerBitsize(*kindCst);
assert(bits != 0 && "failed to convert kind to integer bitsize");
resultType = builder.getIntegerType(bits);
} else {
resultType = builder.getDefaultIntegerType();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not needed, semantic resolution already did the type resolution for lowering (stmtResultType). If stmtResultType is not the integer type as defined by KIND, this is a semantics or lowering framework bug.

mlir::Value result = createOp<hlfir::IndexOp>(resultType, substr, str, back);

if (resultType != stmtResultType)
return builder.createConvert(loc, stmtResultType, result);
return result;
}

std::optional<hlfir::EntityWithAttributes> Fortran::lower::lowerHlfirIntrinsic(
fir::FirOpBuilder &builder, mlir::Location loc, const std::string &name,
const Fortran::lower::PreparedActualArguments &loweredActuals,
Expand Down Expand Up @@ -567,6 +595,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,
Expand Down
38 changes: 26 additions & 12 deletions flang/lib/Optimizer/Builder/Runtime/Character.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down Expand Up @@ -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,
Expand Down
22 changes: 22 additions & 0 deletions flang/lib/Optimizer/HLFIR/IR/HLFIROps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
//===----------------------------------------------------------------------===//
Expand Down
Loading