Skip to content

Commit e801c74

Browse files
committed
implement split for f2023
1 parent 912a92a commit e801c74

File tree

10 files changed

+259
-0
lines changed

10 files changed

+259
-0
lines changed

flang-rt/lib/runtime/character.cpp

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,35 @@ static RT_API_ATTRS void MaxMin(Descriptor &accumulator, const Descriptor &x,
570570
}
571571
}
572572

573+
template <typename CHAR>
574+
inline RT_API_ATTRS std::size_t Split(const CHAR *x, std::size_t xLen,
575+
const CHAR *set, std::size_t setLen, std::size_t pos, bool back,
576+
const char *sourceFile, int sourceLine) {
577+
Terminator terminator{sourceFile, sourceLine};
578+
579+
if (!back) {
580+
RUNTIME_CHECK(terminator, pos <= xLen);
581+
for (std::size_t i{pos + 1}; i <= xLen; ++i) {
582+
for (std::size_t j{0}; j < setLen; ++j) {
583+
if (x[i - 1] == set[j]) {
584+
return i;
585+
}
586+
}
587+
}
588+
return xLen + 1;
589+
} else {
590+
RUNTIME_CHECK(terminator, pos >= 1 && pos <= xLen + 1);
591+
for (std::size_t i{pos - 1}; i != 0; --i) {
592+
for (std::size_t j{0}; j < setLen; ++j) {
593+
if (x[i - 1] == set[j]) {
594+
return i;
595+
}
596+
}
597+
}
598+
return 0;
599+
}
600+
}
601+
573602
extern "C" {
574603
RT_EXT_API_GROUP_BEGIN
575604

@@ -917,6 +946,24 @@ void RTDEF(CharacterMin)(Descriptor &accumulator, const Descriptor &x,
917946
MaxMin<true>(accumulator, x, sourceFile, sourceLine);
918947
}
919948

949+
std::size_t RTDEF(Split1)(const char *x, std::size_t xLen, const char *set,
950+
std::size_t setLen, std::size_t pos, bool back, const char *sourceFile,
951+
int sourceLine) {
952+
return Split<char>(x, xLen, set, setLen, pos, back, sourceFile, sourceLine);
953+
}
954+
std::size_t RTDEF(Split2)(const char16_t *x, std::size_t xLen,
955+
const char16_t *set, std::size_t setLen, std::size_t pos, bool back,
956+
const char *sourceFile, int sourceLine) {
957+
return Split<char16_t>(
958+
x, xLen, set, setLen, pos, back, sourceFile, sourceLine);
959+
}
960+
std::size_t RTDEF(Split4)(const char32_t *x, std::size_t xLen,
961+
const char32_t *set, std::size_t setLen, std::size_t pos, bool back,
962+
const char *sourceFile, int sourceLine) {
963+
return Split<char32_t>(
964+
x, xLen, set, setLen, pos, back, sourceFile, sourceLine);
965+
}
966+
920967
RT_EXT_API_GROUP_END
921968
}
922969
} // namespace Fortran::runtime

flang-rt/unittests/Runtime/CharacterTest.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,3 +430,53 @@ TYPED_TEST(RepeatTests, Repeat) {
430430
RunRepeatTest<TypeParam>(t.ncopies, t.input, t.output);
431431
}
432432
}
433+
434+
// Test SPLIT()
435+
template <typename CHAR>
436+
using SplitFunction = std::function<std::size_t(const CHAR *, std::size_t,
437+
const CHAR *, std::size_t, std::size_t, bool, const char *, int)>;
438+
using SplitFunctions = CharTypedFunctions<SplitFunction>;
439+
template <typename CHAR> struct SplitTests : public ::testing::Test {};
440+
TYPED_TEST_SUITE(SplitTests, CharacterTypes, );
441+
442+
struct SplitTestCase {
443+
const char *x, *y;
444+
std::size_t pos;
445+
bool back;
446+
std::size_t expect;
447+
};
448+
449+
template <typename CHAR>
450+
void RunSplitTests(const std::vector<SplitTestCase> &testCases,
451+
const SplitFunction<CHAR> &function) {
452+
for (const auto &t : testCases) {
453+
// Convert default character to desired kind
454+
std::size_t xLen{std::strlen(t.x)}, yLen{std::strlen(t.y)};
455+
std::basic_string<CHAR> x{t.x, t.x + xLen};
456+
std::basic_string<CHAR> y{t.y, t.y + yLen};
457+
auto got{function(x.data(), xLen, y.data(), yLen, t.pos, t.back, "", 0)};
458+
ASSERT_EQ(got, t.expect)
459+
<< "SPLIT('" << t.x << "','" << t.y << "',pos=" << t.pos
460+
<< ",back=" << t.back << ") for CHARACTER(kind=" << sizeof(CHAR)
461+
<< "): got " << got << ", expected " << t.expect;
462+
}
463+
}
464+
465+
TYPED_TEST(SplitTests, Split) {
466+
static SplitFunctions functions{
467+
RTNAME(Split1), RTNAME(Split2), RTNAME(Split4)};
468+
static std::vector<SplitTestCase> testcases{
469+
{" one,last example,", ", ", 0, false, 1},
470+
{" one,last example,", ", ", 1, false, 5},
471+
{" one,last example,", ", ", 5, false, 10},
472+
{" one,last example,", ", ", 10, false, 18},
473+
{" one,last example,", ", ", 18, false, 19},
474+
{" one,last example,", ", ", 19, true, 18},
475+
{" one,last example,", ", ", 18, true, 10},
476+
{" one,last example,", ", ", 10, true, 5},
477+
{" one,last example,", ", ", 5, true, 1},
478+
{" one,last example,", ", ", 1, true, 0},
479+
};
480+
RunSplitTests<TypeParam>(
481+
testcases, std::get<SplitFunction<TypeParam>>(functions));
482+
}

flang/include/flang/Optimizer/Builder/IntrinsicCall.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ struct IntrinsicLibrary {
436436
fir::ExtendedValue genSizeOf(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
437437
mlir::Value genSpacing(mlir::Type resultType,
438438
llvm::ArrayRef<mlir::Value> args);
439+
void genSplit(llvm::ArrayRef<fir::ExtendedValue>);
439440
fir::ExtendedValue genSpread(mlir::Type, llvm::ArrayRef<fir::ExtendedValue>);
440441
fir::ExtendedValue genStorageSize(mlir::Type,
441442
llvm::ArrayRef<fir::ExtendedValue>);

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,14 @@ mlir::Value genVerify(fir::FirOpBuilder &builder, mlir::Location loc, int kind,
128128
mlir::Value setBase, mlir::Value setLen,
129129
mlir::Value back);
130130

131+
/// Generate call to the split runtime routine that is specialized on
132+
/// \param kind.
133+
/// The \param kind represents the kind of the elements in the strings.
134+
mlir::Value genSplit(fir::FirOpBuilder &builder, mlir::Location loc, int kind,
135+
mlir::Value stringBase, mlir::Value stringLen,
136+
mlir::Value setBase, mlir::Value setLen, mlir::Value pos,
137+
mlir::Value back);
138+
131139
} // namespace fir::runtime
132140

133141
#endif // FORTRAN_OPTIMIZER_BUILDER_RUNTIME_CHARACTER_H

flang/include/flang/Runtime/character.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@ std::size_t RTDECL(Verify4)(const char32_t *, std::size_t, const char32_t *set,
127127
void RTDECL(Verify)(Descriptor &result, const Descriptor &string,
128128
const Descriptor &set, const Descriptor *back /*can be null*/, int kind,
129129
const char *sourceFile = nullptr, int sourceLine = 0);
130+
131+
std::size_t RTDECL(Split1)(const char *, std::size_t, const char *set,
132+
std::size_t, std::size_t, bool back = false,
133+
const char *sourceFile = nullptr, int sourceLine = 0);
134+
std::size_t RTDECL(Split2)(const char16_t *, std::size_t, const char16_t *set,
135+
std::size_t, std::size_t, bool back = false,
136+
const char *sourceFile = nullptr, int sourceLine = 0);
137+
std::size_t RTDECL(Split4)(const char32_t *, std::size_t, const char32_t *set,
138+
std::size_t, std::size_t, bool back = false,
139+
const char *sourceFile = nullptr, int sourceLine = 0);
130140
}
131141
} // namespace Fortran::runtime
132142
#endif // FORTRAN_RUNTIME_CHARACTER_H_

flang/lib/Evaluate/intrinsics.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,6 +1725,10 @@ static const IntrinsicInterface intrinsicSubroutine[]{
17251725
{{"seconds", AnyInt, Rank::scalar, Optionality::required,
17261726
common::Intent::In}},
17271727
{}, Rank::elemental, IntrinsicClass::impureSubroutine},
1728+
{"split",
1729+
{{"string", SameCharNoLen}, {"set", SameCharNoLen}, {"pos", AnyInt},
1730+
{"back", AnyLogical, Rank::elemental, Optionality::optional}},
1731+
{}, Rank::elemental, IntrinsicClass::pureSubroutine},
17281732
{"unlink",
17291733
{{"path", DefaultChar, Rank::scalar, Optionality::required,
17301734
common::Intent::In},

flang/lib/Optimizer/Builder/IntrinsicCall.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,12 @@ static constexpr IntrinsicHandler handlers[]{
958958
/*isElemental=*/false},
959959
{"sleep", &I::genSleep, {{{"seconds", asValue}}}, /*isElemental=*/false},
960960
{"spacing", &I::genSpacing},
961+
{"split",
962+
&I::genSplit,
963+
{{{"string", asAddr},
964+
{"set", asAddr},
965+
{"pos", asAddr},
966+
{"back", asValue, handleDynamicOptional}}}},
961967
{"spread",
962968
&I::genSpread,
963969
{{{"source", asBox}, {"dim", asValue}, {"ncopies", asValue}}},
@@ -8763,6 +8769,42 @@ mlir::Value IntrinsicLibrary::genSpacing(mlir::Type resultType,
87638769
fir::runtime::genSpacing(builder, loc, fir::getBase(args[0])));
87648770
}
87658771

8772+
// SPLIT
8773+
void IntrinsicLibrary::genSplit(llvm::ArrayRef<fir::ExtendedValue> args) {
8774+
assert(args.size() == 4);
8775+
8776+
// Handle required string base arg
8777+
mlir::Value stringBase = fir::getBase(args[0]);
8778+
8779+
// Handle required set string base arg
8780+
mlir::Value setBase = fir::getBase(args[1]);
8781+
8782+
// Handle kind argument; it is the kind of character in this case
8783+
fir::KindTy kind =
8784+
fir::factory::CharacterExprHelper{builder, loc}.getCharacterKind(
8785+
stringBase.getType());
8786+
8787+
// Handle string length argument
8788+
mlir::Value stringLen = fir::getLen(args[0]);
8789+
8790+
// Handle set string length argument
8791+
mlir::Value setLen = fir::getLen(args[1]);
8792+
8793+
// Handle pos argument
8794+
mlir::Value posAddr = fir::getBase(args[2]);
8795+
mlir::Value pos = fir::LoadOp::create(builder, loc, posAddr);
8796+
8797+
// Handle optional back argument
8798+
mlir::Value back =
8799+
isStaticallyAbsent(args[3])
8800+
? builder.createIntegerConstant(loc, builder.getI1Type(), 0)
8801+
: fir::getBase(args[3]);
8802+
8803+
pos = fir::runtime::genSplit(builder, loc, kind, stringBase, stringLen,
8804+
setBase, setLen, pos, back);
8805+
builder.createStoreWithConvert(loc, pos, posAddr);
8806+
}
8807+
87668808
// SPREAD
87678809
fir::ExtendedValue
87688810
IntrinsicLibrary::genSpread(mlir::Type resultType,

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,3 +290,34 @@ mlir::Value fir::runtime::genVerify(fir::FirOpBuilder &builder,
290290
stringLen, setBase, setLen, back);
291291
return fir::CallOp::create(builder, loc, func, args).getResult(0);
292292
}
293+
294+
mlir::Value fir::runtime::genSplit(fir::FirOpBuilder &builder,
295+
mlir::Location loc, int kind,
296+
mlir::Value stringBase,
297+
mlir::Value stringLen, mlir::Value setBase,
298+
mlir::Value setLen, mlir::Value pos,
299+
mlir::Value back) {
300+
mlir::func::FuncOp func;
301+
switch (kind) {
302+
case 1:
303+
func = fir::runtime::getRuntimeFunc<mkRTKey(Split1)>(loc, builder);
304+
break;
305+
case 2:
306+
func = fir::runtime::getRuntimeFunc<mkRTKey(Split2)>(loc, builder);
307+
break;
308+
case 4:
309+
func = fir::runtime::getRuntimeFunc<mkRTKey(Split4)>(loc, builder);
310+
break;
311+
default:
312+
fir::emitFatalError(
313+
loc, "unsupported CHARACTER kind value. Runtime expects 1, 2, or 4.");
314+
}
315+
auto fTy = func.getFunctionType();
316+
auto sourceFile = fir::factory::locationToFilename(builder, loc);
317+
auto sourceLine =
318+
fir::factory::locationToLineNo(builder, loc, fTy.getInput(7));
319+
auto args = fir::runtime::createArguments(builder, loc, fTy, stringBase,
320+
stringLen, setBase, setLen, pos,
321+
back, sourceFile, sourceLine);
322+
return fir::CallOp::create(builder, loc, func, args).getResult(0);
323+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
! RUN: bbc -emit-fir -hlfir=false %s -o - | FileCheck %s
2+
3+
! CHECK-LABEL: func @_QPsplit_test1(
4+
! CHECK-SAME: %[[s1:[^:]+]]: !fir.boxchar<1>{{.*}}, %[[s2:[^:]+]]: !fir.boxchar<1>{{.*}}, %[[p:[^:]+]]: !fir.ref<i32>{{.*}})
5+
subroutine split_test1(s1, s2, p)
6+
character(*) :: s1, s2
7+
integer :: p
8+
! CHECK: %[[c1:.*]]:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
9+
! CHECK: %[[c2:.*]]:2 = fir.unboxchar %arg1 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
10+
! CHECK: %[[pos:.*]] = fir.load %arg2 : !fir.ref<i32>
11+
! CHECK: %false = arith.constant false
12+
! CHECK: %[[c1base:.*]] = fir.convert %[[c1]]#0 : (!fir.ref<!fir.char<1,?>>) -> !fir.ref<i8>
13+
! CHECK: %[[c1len:.*]] = fir.convert %[[c1]]#1 : (index) -> i64
14+
! CHECK: %[[c2base:.*]] = fir.convert %[[c2]]#0 : (!fir.ref<!fir.char<1,?>>) -> !fir.ref<i8>
15+
! CHECK: %[[c2len:.*]] = fir.convert %[[c2]]#1 : (index) -> i64
16+
! CHECK: %[[pos1:.*]] = fir.convert %[[pos]] : (i32) -> i64
17+
! CHECK: %[[pos2:.*]] = fir.call @_FortranASplit1(%[[c1base]], %[[c1len]], %[[c2base]], %[[c2len]], %[[pos1]], %false, {{.*}}) {{.*}}: (!fir.ref<i8>, i64, !fir.ref<i8>, i64, i64, i1, !fir.ref<i8>, i32) -> i64
18+
! CHECK: %[[pos3:.*]] = fir.convert %[[pos2]] : (i64) -> i32
19+
! CHECK: fir.store %[[pos3]] to %[[p]] : !fir.ref<i32>
20+
! CHECK: return
21+
call split(s1, s2, p)
22+
end subroutine split_test1
23+
24+
! CHECK-LABEL: func @_QPsplit_test2(
25+
! CHECK-SAME: %[[s1:[^:]+]]: !fir.boxchar<1>{{.*}}, %[[s2:[^:]+]]: !fir.boxchar<1>{{.*}}, %[[p:[^:]+]]: !fir.ref<i32>{{.*}})
26+
subroutine split_test2(s1, s2, p)
27+
character(*) :: s1, s2
28+
integer :: p
29+
! CHECK: %[[c1:.*]]:2 = fir.unboxchar %arg0 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
30+
! CHECK: %[[c2:.*]]:2 = fir.unboxchar %arg1 : (!fir.boxchar<1>) -> (!fir.ref<!fir.char<1,?>>, index)
31+
! CHECK: %true = arith.constant true
32+
! CHECK: %[[pos:.*]] = fir.load %arg2 : !fir.ref<i32>
33+
! CHECK: %[[c1base:.*]] = fir.convert %[[c1]]#0 : (!fir.ref<!fir.char<1,?>>) -> !fir.ref<i8>
34+
! CHECK: %[[c1len:.*]] = fir.convert %[[c1]]#1 : (index) -> i64
35+
! CHECK: %[[c2base:.*]] = fir.convert %[[c2]]#0 : (!fir.ref<!fir.char<1,?>>) -> !fir.ref<i8>
36+
! CHECK: %[[c2len:.*]] = fir.convert %[[c2]]#1 : (index) -> i64
37+
! CHECK: %[[pos1:.*]] = fir.convert %[[pos]] : (i32) -> i64
38+
! CHECK: %[[pos2:.*]] = fir.call @_FortranASplit1(%[[c1base]], %[[c1len]], %[[c2base]], %[[c2len]], %[[pos1]], %true, {{.*}}) {{.*}}: (!fir.ref<i8>, i64, !fir.ref<i8>, i64, i64, i1, !fir.ref<i8>, i32) -> i64
39+
! CHECK: %[[pos3:.*]] = fir.convert %[[pos2]] : (i64) -> i32
40+
! CHECK: fir.store %[[pos3]] to %[[p]] : !fir.ref<i32>
41+
! CHECK: return
42+
call split(s1, s2, p, .true.)
43+
end subroutine split_test2

flang/unittests/Optimizer/Builder/Runtime/CharacterTest.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,26 @@ TEST_F(RuntimeCallTest, genVerifyTest) {
209209
checkGenVerify(*firBuilder, "_FortranAVerify2", 2);
210210
checkGenVerify(*firBuilder, "_FortranAVerify4", 4);
211211
}
212+
213+
void checkGenSplit(
214+
fir::FirOpBuilder &builder, llvm::StringRef fctName, unsigned kind) {
215+
auto loc = builder.getUnknownLoc();
216+
mlir::Type charTy = fir::CharacterType::get(builder.getContext(), kind, 10);
217+
mlir::Type boxTy = fir::BoxType::get(charTy);
218+
mlir::Type i32Ty = IntegerType::get(builder.getContext(), 32);
219+
mlir::Value stringBase = fir::UndefOp::create(builder, loc, boxTy);
220+
mlir::Value stringLen = fir::UndefOp::create(builder, loc, i32Ty);
221+
mlir::Value setBase = fir::UndefOp::create(builder, loc, boxTy);
222+
mlir::Value setLen = fir::UndefOp::create(builder, loc, i32Ty);
223+
mlir::Value pos = fir::UndefOp::create(builder, loc, i32Ty);
224+
mlir::Value back = fir::UndefOp::create(builder, loc, i32Ty);
225+
mlir::Value res = fir::runtime::genSplit(
226+
builder, loc, kind, stringBase, stringLen, setBase, setLen, pos, back);
227+
checkCallOp(res.getDefiningOp(), fctName, 6);
228+
}
229+
230+
TEST_F(RuntimeCallTest, genSplitTest) {
231+
checkGenSplit(*firBuilder, "_FortranASplit1", 1);
232+
checkGenSplit(*firBuilder, "_FortranASplit2", 2);
233+
checkGenSplit(*firBuilder, "_FortranASplit4", 4);
234+
}

0 commit comments

Comments
 (0)