diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td --- a/flang/include/flang/Optimizer/Dialect/FIROps.td +++ b/flang/include/flang/Optimizer/Dialect/FIROps.td @@ -294,6 +294,46 @@ }]; } +def fir_CharConvertOp : fir_Op<"char_convert", []> { + let summary = [{ + Primitive to convert an entity of type CHARACTER from one KIND to a + different KIND. + }]; + + let description = [{ + Copy a CHARACTER (must be in memory) of KIND _k1_ to a CHARACTER (also must + be in memory) of KIND _k2_ where _k1_ != _k2_ and the buffers do not + overlap. This latter restriction is unchecked, as the Fortran language + definition eliminates the overlapping in memory case. + + The number of code points copied is specified explicitly as the second + argument. The length of the !fir.char type is ignored. + + ```mlir + fir.char_convert %1 for %2 to %3 : !fir.ref>, i32, + !fir.ref> + ``` + + Should future support for encodings other than ASCII be supported, codegen + can generate a call to a runtime helper routine which will map the code + points from UTF-8 to UCS-2, for example. Such remappings may not always + be possible as they may involve the creation of more code points than the + `count` limit. These details are left as future to-dos. + }]; + + let arguments = (ins + Arg:$from, + AnyIntegerType:$count, + Arg:$to + ); + + let assemblyFormat = [{ + $from `for` $count `to` $to attr-dict `:` type(operands) + }]; + + let verifier = "return ::verify(*this);"; +} + def fir_StoreOp : fir_Op<"store", []> { let summary = "store an SSA-value to a memory location"; diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp --- a/flang/lib/Optimizer/Dialect/FIROps.cpp +++ b/flang/lib/Optimizer/Dialect/FIROps.cpp @@ -667,6 +667,24 @@ return success(); } +//===----------------------------------------------------------------------===// +// CharConvertOp +//===----------------------------------------------------------------------===// + +static mlir::LogicalResult verify(fir::CharConvertOp op) { + auto unwrap = [&](mlir::Type t) { + t = fir::unwrapSequenceType(fir::dyn_cast_ptrEleTy(t)); + return t.dyn_cast(); + }; + auto inTy = unwrap(op.from().getType()); + auto outTy = unwrap(op.to().getType()); + if (!(inTy && outTy)) + return op.emitOpError("not a reference to a character"); + if (inTy.getFKind() == outTy.getFKind()) + return op.emitOpError("buffers must have different KIND values"); + return mlir::success(); +} + //===----------------------------------------------------------------------===// // CmpcOp //===----------------------------------------------------------------------===// diff --git a/flang/test/Fir/fir-ops.fir b/flang/test/Fir/fir-ops.fir --- a/flang/test/Fir/fir-ops.fir +++ b/flang/test/Fir/fir-ops.fir @@ -682,3 +682,12 @@ fir.save_result %res to %buffer(%shape) typeparams %c50 : !fir.array>, !fir.ref>>, !fir.shape<1>, index return } + +func @char_convert() { + %1 = fir.undefined i32 + %2 = fir.undefined !fir.ref> + %3 = fir.undefined !fir.ref>> + // CHECK: fir.char_convert %{{.*}} for %{{.*}} to %{{.*}} : !fir.ref>, i32, !fir.ref>> + fir.char_convert %2 for %1 to %3 : !fir.ref>, i32, !fir.ref>> + return +} diff --git a/flang/test/Fir/invalid.fir b/flang/test/Fir/invalid.fir --- a/flang/test/Fir/invalid.fir +++ b/flang/test/Fir/invalid.fir @@ -380,6 +380,50 @@ // ----- +func @ugly_char_convert() { + %1 = fir.undefined i32 + %2 = fir.undefined !fir.ref> + %3 = fir.undefined !fir.ref>> + // expected-error@+1 {{'fir.char_convert' op buffers must have different KIND values}} + fir.char_convert %2 for %1 to %3 : !fir.ref>, i32, !fir.ref>> + return +} + +// ----- + +func @ugly_char_convert() { + %1 = fir.undefined i32 + %2 = fir.undefined !fir.ref> + %3 = fir.undefined !fir.ref> + // expected-error@+1 {{'fir.char_convert' op not a reference to a character}} + fir.char_convert %2 for %1 to %3 : !fir.ref>, i32, !fir.ref> + return +} + +// ----- + +func @ugly_char_convert() { + %1 = fir.undefined i32 + %2 = fir.undefined !fir.ref> + %3 = fir.undefined !fir.ref>> + // expected-error@+1 {{'fir.char_convert' op operand #0 must be any reference, but got 'i32'}} + fir.char_convert %1 for %1 to %3 : i32, i32, !fir.ref>> + return +} + +// ----- + +func @ugly_char_convert() { + %1 = fir.undefined i32 + %2 = fir.undefined !fir.ref> + %3 = fir.undefined !fir.ref>> + // expected-error@+1 {{'fir.char_convert' op operand #1 must be any integer, but got '!fir.ref>'}} + fir.char_convert %2 for %2 to %3 : !fir.ref>, !fir.ref>, !fir.ref>> + return +} + +// ----- + fir.global internal @_QEmultiarray : !fir.array<32x32xi32> { %c0_i32 = constant 1 : i32 %0 = fir.undefined !fir.array<32x32xi32>