This implements the TOKENIZE intrinsic per the Fortran 2023 Standard. TOKENIZE is a more complicated addition to the flang intrinsics, as it is the first subroutine that has multiple unique footprints. Intrinsic functions have already addressed this challenge, however subroutines and functions are processed slightly differently and the function code was not a good 1:1 solution for the subroutines. To solve this the function code was used as an example to create error buffering within the intrinsics Process and select the most appropriate error message for a given subroutine footprint. A simple FIR compile test was added to show the proper compilation of each case. A thorough negative path test has also been added, ensuring that all possible errors are reported as expected. Testing prior to commit: = check-flang ========================================== ``` Testing Time: 139.51s Total Discovered Tests: 4153 Unsupported : 77 (1.85%) Passed : 4065 (97.88%) Expectedly Failed: 11 (0.26%) FLANG Container Test completed 2 minutes (160 s). Total Time: 2 minutes (160 s) Completed : Wed Feb 11 04:05:50 PM CST 2026 ``` = check-flang-rt ========================================== ``` Testing Time: 1.55s Total Discovered Tests: 258 Passed: 258 (100.00%) FLANG Container Test completed 0 minutes (55 s). Total Time: 0 minutes (56 s) Completed : Wed Feb 11 04:08:32 PM CST 2026 ``` = llvm-test-suite ========================================== ``` Testing Time: 1886.64s Total Discovered Tests: 6926 Passed: 6926 (100.00%) CCE SLES Container debug compile completed 31 minutes (1895 s). CCE SLES Container debug install completed in 0 minutes (0 s). Total Time: 31 minutes (1895 s) Completed : Wed Feb 11 05:46:52 PM CST 2026 ``` Additionally, (FYI) an executable test has been written and will be added to the llvm-test-suite under a separate PR. --------- Co-authored-by: Kevin Wyatt <kwyatt@hpe.com>
342 lines
15 KiB
C++
342 lines
15 KiB
C++
//===-- Character.cpp -- runtime for CHARACTER type entities --------------===//
|
|
//
|
|
// 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 "flang/Optimizer/Builder/Runtime/Character.h"
|
|
#include "flang/Optimizer/Builder/BoxValue.h"
|
|
#include "flang/Optimizer/Builder/Character.h"
|
|
#include "flang/Optimizer/Builder/FIRBuilder.h"
|
|
#include "flang/Optimizer/Builder/Runtime/RTBuilder.h"
|
|
#include "flang/Optimizer/Builder/Todo.h"
|
|
#include "flang/Runtime/character.h"
|
|
#include "mlir/Dialect/Func/IR/FuncOps.h"
|
|
|
|
using namespace Fortran::runtime;
|
|
|
|
/// Generate calls to string handling intrinsics such as index, scan, and
|
|
/// verify. These are the descriptor based implementations that take four
|
|
/// arguments (string1, string2, back, kind).
|
|
template <typename FN>
|
|
static void genCharacterSearch(FN func, fir::FirOpBuilder &builder,
|
|
mlir::Location loc, mlir::Value resultBox,
|
|
mlir::Value string1Box, mlir::Value string2Box,
|
|
mlir::Value backBox, mlir::Value kind) {
|
|
|
|
auto fTy = func.getFunctionType();
|
|
auto sourceFile = fir::factory::locationToFilename(builder, loc);
|
|
auto sourceLine =
|
|
fir::factory::locationToLineNo(builder, loc, fTy.getInput(6));
|
|
|
|
auto args = fir::runtime::createArguments(builder, loc, fTy, resultBox,
|
|
string1Box, string2Box, backBox,
|
|
kind, sourceFile, sourceLine);
|
|
fir::CallOp::create(builder, loc, func, args);
|
|
}
|
|
|
|
/// Helper function to recover the KIND from the FIR type.
|
|
static int discoverKind(mlir::Type ty) {
|
|
if (auto charTy = mlir::dyn_cast<fir::CharacterType>(ty))
|
|
return charTy.getFKind();
|
|
if (auto eleTy = fir::dyn_cast_ptrEleTy(ty))
|
|
return discoverKind(eleTy);
|
|
if (auto arrTy = mlir::dyn_cast<fir::SequenceType>(ty))
|
|
return discoverKind(arrTy.getEleTy());
|
|
if (auto boxTy = mlir::dyn_cast<fir::BoxCharType>(ty))
|
|
return discoverKind(boxTy.getEleTy());
|
|
if (auto boxTy = mlir::dyn_cast<fir::BoxType>(ty))
|
|
return discoverKind(boxTy.getEleTy());
|
|
llvm_unreachable("unexpected character type");
|
|
}
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Lower character operations
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// Generate a call to the `ADJUST[L|R]` runtime.
|
|
///
|
|
/// \p resultBox must be an unallocated allocatable used for the temporary
|
|
/// result. \p StringBox must be a fir.box describing the adjustr string
|
|
/// argument. The \p adjustFunc should be a mlir::func::FuncOp for the
|
|
/// appropriate runtime entry function.
|
|
static void genAdjust(fir::FirOpBuilder &builder, mlir::Location loc,
|
|
mlir::Value resultBox, mlir::Value stringBox,
|
|
mlir::func::FuncOp &adjustFunc) {
|
|
|
|
auto fTy = adjustFunc.getFunctionType();
|
|
auto sourceLine =
|
|
fir::factory::locationToLineNo(builder, loc, fTy.getInput(3));
|
|
auto sourceFile = fir::factory::locationToFilename(builder, loc);
|
|
auto args = fir::runtime::createArguments(builder, loc, fTy, resultBox,
|
|
stringBox, sourceFile, sourceLine);
|
|
fir::CallOp::create(builder, loc, adjustFunc, args);
|
|
}
|
|
|
|
void fir::runtime::genAdjustL(fir::FirOpBuilder &builder, mlir::Location loc,
|
|
mlir::Value resultBox, mlir::Value stringBox) {
|
|
auto adjustFunc =
|
|
fir::runtime::getRuntimeFunc<mkRTKey(Adjustl)>(loc, builder);
|
|
genAdjust(builder, loc, resultBox, stringBox, adjustFunc);
|
|
}
|
|
|
|
void fir::runtime::genAdjustR(fir::FirOpBuilder &builder, mlir::Location loc,
|
|
mlir::Value resultBox, mlir::Value stringBox) {
|
|
auto adjustFunc =
|
|
fir::runtime::getRuntimeFunc<mkRTKey(Adjustr)>(loc, builder);
|
|
genAdjust(builder, loc, resultBox, stringBox, adjustFunc);
|
|
}
|
|
|
|
mlir::Value
|
|
fir::runtime::genCharCompare(fir::FirOpBuilder &builder, mlir::Location loc,
|
|
mlir::arith::CmpIPredicate cmp,
|
|
mlir::Value lhsBuff, mlir::Value lhsLen,
|
|
mlir::Value rhsBuff, mlir::Value rhsLen) {
|
|
int lhsKind = discoverKind(lhsBuff.getType());
|
|
int rhsKind = discoverKind(rhsBuff.getType());
|
|
if (lhsKind != rhsKind) {
|
|
fir::emitFatalError(loc, "runtime does not support comparison of different "
|
|
"CHARACTER kind values");
|
|
}
|
|
mlir::func::FuncOp func;
|
|
switch (lhsKind) {
|
|
case 1:
|
|
func = fir::runtime::getRuntimeFunc<mkRTKey(CharacterCompareScalar1)>(
|
|
loc, builder);
|
|
break;
|
|
case 2:
|
|
func = fir::runtime::getRuntimeFunc<mkRTKey(CharacterCompareScalar2)>(
|
|
loc, builder);
|
|
break;
|
|
case 4:
|
|
func = fir::runtime::getRuntimeFunc<mkRTKey(CharacterCompareScalar4)>(
|
|
loc, builder);
|
|
break;
|
|
default:
|
|
fir::emitFatalError(
|
|
loc, "unsupported CHARACTER kind value. Runtime expects 1, 2, or 4.");
|
|
}
|
|
auto fTy = func.getFunctionType();
|
|
auto args = fir::runtime::createArguments(builder, loc, fTy, lhsBuff, rhsBuff,
|
|
lhsLen, rhsLen);
|
|
auto tri = fir::CallOp::create(builder, loc, func, args).getResult(0);
|
|
auto zero = builder.createIntegerConstant(loc, tri.getType(), 0);
|
|
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) {
|
|
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));
|
|
}
|
|
|
|
void fir::runtime::genFCString(fir::FirOpBuilder &builder, mlir::Location loc,
|
|
mlir::Value resultBox, mlir::Value stringBox,
|
|
mlir::Value asis) {
|
|
auto func = fir::runtime::getRuntimeFunc<mkRTKey(FCString)>(loc, builder);
|
|
auto fTy = func.getFunctionType();
|
|
auto sourceFile = fir::factory::locationToFilename(builder, loc);
|
|
auto sourceLine =
|
|
fir::factory::locationToLineNo(builder, loc, fTy.getInput(4));
|
|
auto args = fir::runtime::createArguments(
|
|
builder, loc, fTy, resultBox, stringBox, asis, sourceFile, sourceLine);
|
|
fir::CallOp::create(builder, loc, func, args);
|
|
}
|
|
|
|
mlir::Value fir::runtime::genIndex(fir::FirOpBuilder &builder,
|
|
mlir::Location loc, int kind,
|
|
mlir::Value stringBase,
|
|
mlir::Value stringLen,
|
|
mlir::Value substringBase,
|
|
mlir::Value substringLen, mlir::Value back) {
|
|
mlir::func::FuncOp indexFunc;
|
|
switch (kind) {
|
|
case 1:
|
|
indexFunc = fir::runtime::getRuntimeFunc<mkRTKey(Index1)>(loc, builder);
|
|
break;
|
|
case 2:
|
|
indexFunc = fir::runtime::getRuntimeFunc<mkRTKey(Index2)>(loc, builder);
|
|
break;
|
|
case 4:
|
|
indexFunc = fir::runtime::getRuntimeFunc<mkRTKey(Index4)>(loc, builder);
|
|
break;
|
|
default:
|
|
fir::emitFatalError(
|
|
loc, "unsupported CHARACTER kind value. Runtime expects 1, 2, or 4.");
|
|
}
|
|
auto fTy = indexFunc.getFunctionType();
|
|
auto args =
|
|
fir::runtime::createArguments(builder, loc, fTy, stringBase, stringLen,
|
|
substringBase, substringLen, back);
|
|
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,
|
|
mlir::Value substringBox,
|
|
mlir::Value backOpt, mlir::Value kind) {
|
|
auto indexFunc = fir::runtime::getRuntimeFunc<mkRTKey(Index)>(loc, builder);
|
|
genCharacterSearch(indexFunc, builder, loc, resultBox, stringBox,
|
|
substringBox, backOpt, kind);
|
|
}
|
|
|
|
void fir::runtime::genRepeat(fir::FirOpBuilder &builder, mlir::Location loc,
|
|
mlir::Value resultBox, mlir::Value stringBox,
|
|
mlir::Value ncopies) {
|
|
auto repeatFunc = fir::runtime::getRuntimeFunc<mkRTKey(Repeat)>(loc, builder);
|
|
auto fTy = repeatFunc.getFunctionType();
|
|
auto sourceFile = fir::factory::locationToFilename(builder, loc);
|
|
auto sourceLine =
|
|
fir::factory::locationToLineNo(builder, loc, fTy.getInput(4));
|
|
|
|
auto args = fir::runtime::createArguments(
|
|
builder, loc, fTy, resultBox, stringBox, ncopies, sourceFile, sourceLine);
|
|
fir::CallOp::create(builder, loc, repeatFunc, args);
|
|
}
|
|
|
|
void fir::runtime::genTrim(fir::FirOpBuilder &builder, mlir::Location loc,
|
|
mlir::Value resultBox, mlir::Value stringBox) {
|
|
auto trimFunc = fir::runtime::getRuntimeFunc<mkRTKey(Trim)>(loc, builder);
|
|
auto fTy = trimFunc.getFunctionType();
|
|
auto sourceFile = fir::factory::locationToFilename(builder, loc);
|
|
auto sourceLine =
|
|
fir::factory::locationToLineNo(builder, loc, fTy.getInput(3));
|
|
|
|
auto args = fir::runtime::createArguments(builder, loc, fTy, resultBox,
|
|
stringBox, sourceFile, sourceLine);
|
|
fir::CallOp::create(builder, loc, trimFunc, args);
|
|
}
|
|
|
|
void fir::runtime::genScanDescriptor(fir::FirOpBuilder &builder,
|
|
mlir::Location loc, mlir::Value resultBox,
|
|
mlir::Value stringBox, mlir::Value setBox,
|
|
mlir::Value backBox, mlir::Value kind) {
|
|
auto func = fir::runtime::getRuntimeFunc<mkRTKey(Scan)>(loc, builder);
|
|
genCharacterSearch(func, builder, loc, resultBox, stringBox, setBox, backBox,
|
|
kind);
|
|
}
|
|
|
|
mlir::Value fir::runtime::genScan(fir::FirOpBuilder &builder,
|
|
mlir::Location loc, int kind,
|
|
mlir::Value stringBase, mlir::Value stringLen,
|
|
mlir::Value setBase, mlir::Value setLen,
|
|
mlir::Value back) {
|
|
mlir::func::FuncOp func;
|
|
switch (kind) {
|
|
case 1:
|
|
func = fir::runtime::getRuntimeFunc<mkRTKey(Scan1)>(loc, builder);
|
|
break;
|
|
case 2:
|
|
func = fir::runtime::getRuntimeFunc<mkRTKey(Scan2)>(loc, builder);
|
|
break;
|
|
case 4:
|
|
func = fir::runtime::getRuntimeFunc<mkRTKey(Scan4)>(loc, builder);
|
|
break;
|
|
default:
|
|
fir::emitFatalError(
|
|
loc, "unsupported CHARACTER kind value. Runtime expects 1, 2, or 4.");
|
|
}
|
|
auto fTy = func.getFunctionType();
|
|
auto args = fir::runtime::createArguments(builder, loc, fTy, stringBase,
|
|
stringLen, setBase, setLen, back);
|
|
return fir::CallOp::create(builder, loc, func, args).getResult(0);
|
|
}
|
|
|
|
void fir::runtime::genVerifyDescriptor(fir::FirOpBuilder &builder,
|
|
mlir::Location loc,
|
|
mlir::Value resultBox,
|
|
mlir::Value stringBox,
|
|
mlir::Value setBox, mlir::Value backBox,
|
|
mlir::Value kind) {
|
|
auto func = fir::runtime::getRuntimeFunc<mkRTKey(Verify)>(loc, builder);
|
|
genCharacterSearch(func, builder, loc, resultBox, stringBox, setBox, backBox,
|
|
kind);
|
|
}
|
|
|
|
void fir::runtime::genTokenize(fir::FirOpBuilder &builder, mlir::Location loc,
|
|
mlir::Value tokensBox, mlir::Value separatorBox,
|
|
mlir::Value stringBox, mlir::Value setBox) {
|
|
auto func = fir::runtime::getRuntimeFunc<mkRTKey(Tokenize)>(loc, builder);
|
|
auto fTy = func.getFunctionType();
|
|
auto sourceFile = fir::factory::locationToFilename(builder, loc);
|
|
auto sourceLine =
|
|
fir::factory::locationToLineNo(builder, loc, fTy.getInput(5));
|
|
auto args =
|
|
fir::runtime::createArguments(builder, loc, fTy, tokensBox, separatorBox,
|
|
stringBox, setBox, sourceFile, sourceLine);
|
|
fir::CallOp::create(builder, loc, func, args);
|
|
}
|
|
|
|
void fir::runtime::genTokenizePositions(
|
|
fir::FirOpBuilder &builder, mlir::Location loc, mlir::Value firstBox,
|
|
mlir::Value lastBox, mlir::Value stringBox, mlir::Value setBox) {
|
|
auto func =
|
|
fir::runtime::getRuntimeFunc<mkRTKey(TokenizePositions)>(loc, builder);
|
|
auto fTy = func.getFunctionType();
|
|
auto sourceFile = fir::factory::locationToFilename(builder, loc);
|
|
auto sourceLine =
|
|
fir::factory::locationToLineNo(builder, loc, fTy.getInput(5));
|
|
auto args =
|
|
fir::runtime::createArguments(builder, loc, fTy, firstBox, lastBox,
|
|
stringBox, setBox, sourceFile, sourceLine);
|
|
fir::CallOp::create(builder, loc, func, args);
|
|
}
|
|
|
|
mlir::Value fir::runtime::genVerify(fir::FirOpBuilder &builder,
|
|
mlir::Location loc, int kind,
|
|
mlir::Value stringBase,
|
|
mlir::Value stringLen, mlir::Value setBase,
|
|
mlir::Value setLen, mlir::Value back) {
|
|
mlir::func::FuncOp func;
|
|
switch (kind) {
|
|
case 1:
|
|
func = fir::runtime::getRuntimeFunc<mkRTKey(Verify1)>(loc, builder);
|
|
break;
|
|
case 2:
|
|
func = fir::runtime::getRuntimeFunc<mkRTKey(Verify2)>(loc, builder);
|
|
break;
|
|
case 4:
|
|
func = fir::runtime::getRuntimeFunc<mkRTKey(Verify4)>(loc, builder);
|
|
break;
|
|
default:
|
|
fir::emitFatalError(
|
|
loc, "unsupported CHARACTER kind value. Runtime expects 1, 2, or 4.");
|
|
}
|
|
auto fTy = func.getFunctionType();
|
|
auto args = fir::runtime::createArguments(builder, loc, fTy, stringBase,
|
|
stringLen, setBase, setLen, back);
|
|
return fir::CallOp::create(builder, loc, func, args).getResult(0);
|
|
}
|