1 //===-- Character.cpp -----------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // Coding style: https://mlir.llvm.org/getting_started/DeveloperGuide/
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "flang/Optimizer/Builder/Character.h"
14 #include "flang/Optimizer/Builder/DoLoopHelper.h"
15 #include "flang/Optimizer/Builder/FIRBuilder.h"
16 #include "flang/Optimizer/Builder/Todo.h"
17 #include "llvm/Support/Debug.h"
18 #include <optional>
19 
20 #define DEBUG_TYPE "flang-lower-character"
21 
22 //===----------------------------------------------------------------------===//
23 // CharacterExprHelper implementation
24 //===----------------------------------------------------------------------===//
25 
26 /// Unwrap all the ref and box types and return the inner element type.
unwrapBoxAndRef(mlir::Type type)27 static mlir::Type unwrapBoxAndRef(mlir::Type type) {
28   if (auto boxType = type.dyn_cast<fir::BoxCharType>())
29     return boxType.getEleTy();
30   while (true) {
31     type = fir::unwrapRefType(type);
32     if (auto boxTy = type.dyn_cast<fir::BoxType>())
33       type = boxTy.getEleTy();
34     else
35       break;
36   }
37   return type;
38 }
39 
40 /// Unwrap base fir.char<kind,len> type.
recoverCharacterType(mlir::Type type)41 static fir::CharacterType recoverCharacterType(mlir::Type type) {
42   type = fir::unwrapSequenceType(unwrapBoxAndRef(type));
43   if (auto charTy = type.dyn_cast<fir::CharacterType>())
44     return charTy;
45   llvm::report_fatal_error("expected a character type");
46 }
47 
isCharacterScalar(mlir::Type type)48 bool fir::factory::CharacterExprHelper::isCharacterScalar(mlir::Type type) {
49   type = unwrapBoxAndRef(type);
50   return !type.isa<fir::SequenceType>() && fir::isa_char(type);
51 }
52 
isArray(mlir::Type type)53 bool fir::factory::CharacterExprHelper::isArray(mlir::Type type) {
54   type = unwrapBoxAndRef(type);
55   if (auto seqTy = type.dyn_cast<fir::SequenceType>())
56     return fir::isa_char(seqTy.getEleTy());
57   return false;
58 }
59 
60 fir::CharacterType
getCharacterType(mlir::Type type)61 fir::factory::CharacterExprHelper::getCharacterType(mlir::Type type) {
62   assert(isCharacterScalar(type) && "expected scalar character");
63   return recoverCharacterType(type);
64 }
65 
66 fir::CharacterType
getCharType(mlir::Type type)67 fir::factory::CharacterExprHelper::getCharType(mlir::Type type) {
68   return recoverCharacterType(type);
69 }
70 
getCharacterType(const fir::CharBoxValue & box)71 fir::CharacterType fir::factory::CharacterExprHelper::getCharacterType(
72     const fir::CharBoxValue &box) {
73   return getCharacterType(box.getBuffer().getType());
74 }
75 
76 fir::CharacterType
getCharacterType(mlir::Value str)77 fir::factory::CharacterExprHelper::getCharacterType(mlir::Value str) {
78   return getCharacterType(str.getType());
79 }
80 
81 /// Determine the static size of the character. Returns the computed size, not
82 /// an IR Value.
83 static std::optional<fir::CharacterType::LenType>
getCompileTimeLength(const fir::CharBoxValue & box)84 getCompileTimeLength(const fir::CharBoxValue &box) {
85   auto len = recoverCharacterType(box.getBuffer().getType()).getLen();
86   if (len == fir::CharacterType::unknownLen())
87     return {};
88   return len;
89 }
90 
91 /// Detect the precondition that the value `str` does not reside in memory. Such
92 /// values will have a type `!fir.array<...x!fir.char<N>>` or `!fir.char<N>`.
needToMaterialize(mlir::Value str)93 LLVM_ATTRIBUTE_UNUSED static bool needToMaterialize(mlir::Value str) {
94   return str.getType().isa<fir::SequenceType>() || fir::isa_char(str.getType());
95 }
96 
97 /// This is called only if `str` does not reside in memory. Such a bare string
98 /// value will be converted into a memory-based temporary and an extended
99 /// boxchar value returned.
100 fir::CharBoxValue
materializeValue(mlir::Value str)101 fir::factory::CharacterExprHelper::materializeValue(mlir::Value str) {
102   assert(needToMaterialize(str));
103   auto ty = str.getType();
104   assert(isCharacterScalar(ty) && "expected scalar character");
105   auto charTy = ty.dyn_cast<fir::CharacterType>();
106   if (!charTy || charTy.getLen() == fir::CharacterType::unknownLen()) {
107     LLVM_DEBUG(llvm::dbgs() << "cannot materialize: " << str << '\n');
108     llvm_unreachable("must be a !fir.char<N> type");
109   }
110   auto len = builder.createIntegerConstant(
111       loc, builder.getCharacterLengthType(), charTy.getLen());
112   auto temp = builder.create<fir::AllocaOp>(loc, charTy);
113   builder.create<fir::StoreOp>(loc, str, temp);
114   LLVM_DEBUG(llvm::dbgs() << "materialized as local: " << str << " -> (" << temp
115                           << ", " << len << ")\n");
116   return {temp, len};
117 }
118 
119 fir::ExtendedValue
toExtendedValue(mlir::Value character,mlir::Value len)120 fir::factory::CharacterExprHelper::toExtendedValue(mlir::Value character,
121                                                    mlir::Value len) {
122   auto lenType = builder.getCharacterLengthType();
123   auto type = character.getType();
124   auto base = fir::isa_passbyref_type(type) ? character : mlir::Value{};
125   auto resultLen = len;
126   llvm::SmallVector<mlir::Value> extents;
127 
128   if (auto eleType = fir::dyn_cast_ptrEleTy(type))
129     type = eleType;
130 
131   if (auto arrayType = type.dyn_cast<fir::SequenceType>()) {
132     type = arrayType.getEleTy();
133     auto indexType = builder.getIndexType();
134     for (auto extent : arrayType.getShape()) {
135       if (extent == fir::SequenceType::getUnknownExtent())
136         break;
137       extents.emplace_back(
138           builder.createIntegerConstant(loc, indexType, extent));
139     }
140     // Last extent might be missing in case of assumed-size. If more extents
141     // could not be deduced from type, that's an error (a fir.box should
142     // have been used in the interface).
143     if (extents.size() + 1 < arrayType.getShape().size())
144       mlir::emitError(loc, "cannot retrieve array extents from type");
145   }
146 
147   if (auto charTy = type.dyn_cast<fir::CharacterType>()) {
148     if (!resultLen && charTy.getLen() != fir::CharacterType::unknownLen())
149       resultLen = builder.createIntegerConstant(loc, lenType, charTy.getLen());
150   } else if (auto boxCharType = type.dyn_cast<fir::BoxCharType>()) {
151     auto refType = builder.getRefType(boxCharType.getEleTy());
152     // If the embox is accessible, use its operand to avoid filling
153     // the generated fir with embox/unbox.
154     mlir::Value boxCharLen;
155     if (auto definingOp = character.getDefiningOp()) {
156       if (auto box = mlir::dyn_cast<fir::EmboxCharOp>(definingOp)) {
157         base = box.getMemref();
158         boxCharLen = box.getLen();
159       }
160     }
161     if (!boxCharLen) {
162       auto unboxed =
163           builder.create<fir::UnboxCharOp>(loc, refType, lenType, character);
164       base = builder.createConvert(loc, refType, unboxed.getResult(0));
165       boxCharLen = unboxed.getResult(1);
166     }
167     if (!resultLen) {
168       resultLen = boxCharLen;
169     }
170   } else if (type.isa<fir::BoxType>()) {
171     mlir::emitError(loc, "descriptor or derived type not yet handled");
172   } else {
173     llvm_unreachable("Cannot translate mlir::Value to character ExtendedValue");
174   }
175 
176   if (!base) {
177     if (auto load =
178             mlir::dyn_cast_or_null<fir::LoadOp>(character.getDefiningOp())) {
179       base = load.getOperand();
180     } else {
181       return materializeValue(fir::getBase(character));
182     }
183   }
184   if (!resultLen)
185     llvm::report_fatal_error("no dynamic length found for character");
186   if (!extents.empty())
187     return fir::CharArrayBoxValue{base, resultLen, extents};
188   return fir::CharBoxValue{base, resultLen};
189 }
190 
getSingletonCharType(mlir::MLIRContext * ctxt,int kind)191 static mlir::Type getSingletonCharType(mlir::MLIRContext *ctxt, int kind) {
192   return fir::CharacterType::getSingleton(ctxt, kind);
193 }
194 
195 mlir::Value
createEmbox(const fir::CharBoxValue & box)196 fir::factory::CharacterExprHelper::createEmbox(const fir::CharBoxValue &box) {
197   // Base CharBoxValue of CharArrayBoxValue are ok here (do not require a scalar
198   // type)
199   auto charTy = recoverCharacterType(box.getBuffer().getType());
200   auto boxCharType =
201       fir::BoxCharType::get(builder.getContext(), charTy.getFKind());
202   auto refType = fir::ReferenceType::get(boxCharType.getEleTy());
203   mlir::Value buff = box.getBuffer();
204   // fir.boxchar requires a memory reference. Allocate temp if the character is
205   // not in memory.
206   if (!fir::isa_ref_type(buff.getType())) {
207     auto temp = builder.createTemporary(loc, buff.getType());
208     builder.create<fir::StoreOp>(loc, buff, temp);
209     buff = temp;
210   }
211   buff = builder.createConvert(loc, refType, buff);
212   // Convert in case the provided length is not of the integer type that must
213   // be used in boxchar.
214   auto len = builder.createConvert(loc, builder.getCharacterLengthType(),
215                                    box.getLen());
216   return builder.create<fir::EmboxCharOp>(loc, boxCharType, buff, len);
217 }
218 
toScalarCharacter(const fir::CharArrayBoxValue & box)219 fir::CharBoxValue fir::factory::CharacterExprHelper::toScalarCharacter(
220     const fir::CharArrayBoxValue &box) {
221   if (box.getBuffer().getType().isa<fir::PointerType>())
222     TODO(loc, "concatenating non contiguous character array into a scalar");
223 
224   // TODO: add a fast path multiplying new length at compile time if the info is
225   // in the array type.
226   auto lenType = builder.getCharacterLengthType();
227   auto len = builder.createConvert(loc, lenType, box.getLen());
228   for (auto extent : box.getExtents())
229     len = builder.create<mlir::arith::MulIOp>(
230         loc, len, builder.createConvert(loc, lenType, extent));
231 
232   // TODO: typeLen can be improved in compiled constant cases
233   // TODO: allow bare fir.array<> (no ref) conversion here ?
234   auto typeLen = fir::CharacterType::unknownLen();
235   auto kind = recoverCharacterType(box.getBuffer().getType()).getFKind();
236   auto charTy = fir::CharacterType::get(builder.getContext(), kind, typeLen);
237   auto type = fir::ReferenceType::get(charTy);
238   auto buffer = builder.createConvert(loc, type, box.getBuffer());
239   return {buffer, len};
240 }
241 
createEmbox(const fir::CharArrayBoxValue & box)242 mlir::Value fir::factory::CharacterExprHelper::createEmbox(
243     const fir::CharArrayBoxValue &box) {
244   // Use same embox as for scalar. It's losing the actual data size information
245   // (We do not multiply the length by the array size), but that is what Fortran
246   // call interfaces using boxchar expect.
247   return createEmbox(static_cast<const fir::CharBoxValue &>(box));
248 }
249 
250 /// Get the address of the element at position \p index of the scalar character
251 /// \p buffer.
252 /// \p buffer must be of type !fir.ref<fir.char<k, len>>. The length may be
253 /// unknown. \p index must have any integer type, and is zero based. The return
254 /// value is a singleton address (!fir.ref<!fir.char<kind>>)
255 mlir::Value
createElementAddr(mlir::Value buffer,mlir::Value index)256 fir::factory::CharacterExprHelper::createElementAddr(mlir::Value buffer,
257                                                      mlir::Value index) {
258   // The only way to address an element of a fir.ref<char<kind, len>> is to cast
259   // it to a fir.array<len x fir.char<kind>> and use fir.coordinate_of.
260   auto bufferType = buffer.getType();
261   assert(fir::isa_ref_type(bufferType));
262   assert(isCharacterScalar(bufferType));
263   auto charTy = recoverCharacterType(bufferType);
264   auto singleTy = getSingletonCharType(builder.getContext(), charTy.getFKind());
265   auto singleRefTy = builder.getRefType(singleTy);
266   auto extent = fir::SequenceType::getUnknownExtent();
267   if (charTy.getLen() != fir::CharacterType::unknownLen())
268     extent = charTy.getLen();
269   auto coorTy = builder.getRefType(fir::SequenceType::get({extent}, singleTy));
270 
271   auto coor = builder.createConvert(loc, coorTy, buffer);
272   auto i = builder.createConvert(loc, builder.getIndexType(), index);
273   return builder.create<fir::CoordinateOp>(loc, singleRefTy, coor, i);
274 }
275 
276 /// Load a character out of `buff` from offset `index`.
277 /// `buff` must be a reference to memory.
278 mlir::Value
createLoadCharAt(mlir::Value buff,mlir::Value index)279 fir::factory::CharacterExprHelper::createLoadCharAt(mlir::Value buff,
280                                                     mlir::Value index) {
281   LLVM_DEBUG(llvm::dbgs() << "load a char: " << buff << " type: "
282                           << buff.getType() << " at: " << index << '\n');
283   return builder.create<fir::LoadOp>(loc, createElementAddr(buff, index));
284 }
285 
286 /// Store the singleton character `c` to `str` at offset `index`.
287 /// `str` must be a reference to memory.
createStoreCharAt(mlir::Value str,mlir::Value index,mlir::Value c)288 void fir::factory::CharacterExprHelper::createStoreCharAt(mlir::Value str,
289                                                           mlir::Value index,
290                                                           mlir::Value c) {
291   LLVM_DEBUG(llvm::dbgs() << "store the char: " << c << " into: " << str
292                           << " type: " << str.getType() << " at: " << index
293                           << '\n');
294   auto addr = createElementAddr(str, index);
295   builder.create<fir::StoreOp>(loc, c, addr);
296 }
297 
298 // FIXME: this temp is useless... either fir.coordinate_of needs to
299 // work on "loaded" characters (!fir.array<len x fir.char<kind>>) or
300 // character should never be loaded.
301 // If this is a fir.array<>, allocate and store the value so that
302 // fir.cooridnate_of can be use on the value.
getCharBoxBuffer(const fir::CharBoxValue & box)303 mlir::Value fir::factory::CharacterExprHelper::getCharBoxBuffer(
304     const fir::CharBoxValue &box) {
305   auto buff = box.getBuffer();
306   if (fir::isa_char(buff.getType())) {
307     auto newBuff = builder.create<fir::AllocaOp>(loc, buff.getType());
308     builder.create<fir::StoreOp>(loc, buff, newBuff);
309     return newBuff;
310   }
311   return buff;
312 }
313 
314 /// Create a loop to copy `count` characters from `src` to `dest`. Note that the
315 /// KIND indicates the number of bits in a code point. (ASCII, UCS-2, or UCS-4.)
createCopy(const fir::CharBoxValue & dest,const fir::CharBoxValue & src,mlir::Value count)316 void fir::factory::CharacterExprHelper::createCopy(
317     const fir::CharBoxValue &dest, const fir::CharBoxValue &src,
318     mlir::Value count) {
319   auto fromBuff = getCharBoxBuffer(src);
320   auto toBuff = getCharBoxBuffer(dest);
321   LLVM_DEBUG(llvm::dbgs() << "create char copy from: "; src.dump();
322              llvm::dbgs() << " to: "; dest.dump();
323              llvm::dbgs() << " count: " << count << '\n');
324   auto kind = getCharacterKind(src.getBuffer().getType());
325   // If the src and dest are the same KIND, then use memmove to move the bits.
326   // We don't have to worry about overlapping ranges with memmove.
327   if (getCharacterKind(dest.getBuffer().getType()) == kind) {
328     auto bytes = builder.getKindMap().getCharacterBitsize(kind) / 8;
329     auto i64Ty = builder.getI64Type();
330     auto kindBytes = builder.createIntegerConstant(loc, i64Ty, bytes);
331     auto castCount = builder.createConvert(loc, i64Ty, count);
332     auto totalBytes =
333         builder.create<mlir::arith::MulIOp>(loc, kindBytes, castCount);
334     auto notVolatile = builder.createBool(loc, false);
335     auto memmv = getLlvmMemmove(builder);
336     auto argTys = memmv.getFunctionType().getInputs();
337     auto toPtr = builder.createConvert(loc, argTys[0], toBuff);
338     auto fromPtr = builder.createConvert(loc, argTys[1], fromBuff);
339     builder.create<fir::CallOp>(
340         loc, memmv, mlir::ValueRange{toPtr, fromPtr, totalBytes, notVolatile});
341     return;
342   }
343 
344   // Convert a CHARACTER of one KIND into a CHARACTER of another KIND.
345   builder.create<fir::CharConvertOp>(loc, src.getBuffer(), count,
346                                      dest.getBuffer());
347 }
348 
createPadding(const fir::CharBoxValue & str,mlir::Value lower,mlir::Value upper)349 void fir::factory::CharacterExprHelper::createPadding(
350     const fir::CharBoxValue &str, mlir::Value lower, mlir::Value upper) {
351   auto blank = createBlankConstant(getCharacterType(str));
352   // Always create the loop, if upper < lower, no iteration will be
353   // executed.
354   auto toBuff = getCharBoxBuffer(str);
355   fir::factory::DoLoopHelper{builder, loc}.createLoop(
356       lower, upper, [&](fir::FirOpBuilder &, mlir::Value index) {
357         createStoreCharAt(toBuff, index, blank);
358       });
359 }
360 
361 fir::CharBoxValue
createCharacterTemp(mlir::Type type,mlir::Value len)362 fir::factory::CharacterExprHelper::createCharacterTemp(mlir::Type type,
363                                                        mlir::Value len) {
364   auto kind = recoverCharacterType(type).getFKind();
365   auto typeLen = fir::CharacterType::unknownLen();
366   // If len is a constant, reflect the length in the type.
367   if (auto cstLen = getIntIfConstant(len))
368     typeLen = *cstLen;
369   auto *ctxt = builder.getContext();
370   auto charTy = fir::CharacterType::get(ctxt, kind, typeLen);
371   llvm::SmallVector<mlir::Value> lenParams;
372   if (typeLen == fir::CharacterType::unknownLen())
373     lenParams.push_back(len);
374   auto ref = builder.allocateLocal(loc, charTy, "", ".chrtmp",
375                                    /*shape=*/llvm::None, lenParams);
376   return {ref, len};
377 }
378 
createTempFrom(const fir::ExtendedValue & source)379 fir::CharBoxValue fir::factory::CharacterExprHelper::createTempFrom(
380     const fir::ExtendedValue &source) {
381   const auto *charBox = source.getCharBox();
382   if (!charBox)
383     fir::emitFatalError(loc, "source must be a fir::CharBoxValue");
384   auto len = charBox->getLen();
385   auto sourceTy = charBox->getBuffer().getType();
386   auto temp = createCharacterTemp(sourceTy, len);
387   if (fir::isa_ref_type(sourceTy)) {
388     createCopy(temp, *charBox, len);
389   } else {
390     auto ref = builder.createConvert(loc, builder.getRefType(sourceTy),
391                                      temp.getBuffer());
392     builder.create<fir::StoreOp>(loc, charBox->getBuffer(), ref);
393   }
394   return temp;
395 }
396 
397 // Simple length one character assignment without loops.
createLengthOneAssign(const fir::CharBoxValue & lhs,const fir::CharBoxValue & rhs)398 void fir::factory::CharacterExprHelper::createLengthOneAssign(
399     const fir::CharBoxValue &lhs, const fir::CharBoxValue &rhs) {
400   auto addr = lhs.getBuffer();
401   auto toTy = fir::unwrapRefType(addr.getType());
402   mlir::Value val = rhs.getBuffer();
403   if (fir::isa_ref_type(val.getType()))
404     val = builder.create<fir::LoadOp>(loc, val);
405   val = builder.createConvert(loc, toTy, val);
406   builder.create<fir::StoreOp>(loc, val, addr);
407 }
408 
409 /// Returns the minimum of integer mlir::Value \p a and \b.
genMin(fir::FirOpBuilder & builder,mlir::Location loc,mlir::Value a,mlir::Value b)410 mlir::Value genMin(fir::FirOpBuilder &builder, mlir::Location loc,
411                    mlir::Value a, mlir::Value b) {
412   auto cmp = builder.create<mlir::arith::CmpIOp>(
413       loc, mlir::arith::CmpIPredicate::slt, a, b);
414   return builder.create<mlir::arith::SelectOp>(loc, cmp, a, b);
415 }
416 
createAssign(const fir::CharBoxValue & lhs,const fir::CharBoxValue & rhs)417 void fir::factory::CharacterExprHelper::createAssign(
418     const fir::CharBoxValue &lhs, const fir::CharBoxValue &rhs) {
419   auto rhsCstLen = getCompileTimeLength(rhs);
420   auto lhsCstLen = getCompileTimeLength(lhs);
421   bool compileTimeSameLength =
422       lhsCstLen && rhsCstLen && *lhsCstLen == *rhsCstLen;
423 
424   if (compileTimeSameLength && *lhsCstLen == 1) {
425     createLengthOneAssign(lhs, rhs);
426     return;
427   }
428 
429   // Copy the minimum of the lhs and rhs lengths and pad the lhs remainder
430   // if needed.
431   auto copyCount = lhs.getLen();
432   auto idxTy = builder.getIndexType();
433   if (!compileTimeSameLength) {
434     auto lhsLen = builder.createConvert(loc, idxTy, lhs.getLen());
435     auto rhsLen = builder.createConvert(loc, idxTy, rhs.getLen());
436     copyCount = genMin(builder, loc, lhsLen, rhsLen);
437   }
438 
439   // Actual copy
440   createCopy(lhs, rhs, copyCount);
441 
442   // Pad if needed.
443   if (!compileTimeSameLength) {
444     auto one = builder.createIntegerConstant(loc, lhs.getLen().getType(), 1);
445     auto maxPadding =
446         builder.create<mlir::arith::SubIOp>(loc, lhs.getLen(), one);
447     createPadding(lhs, copyCount, maxPadding);
448   }
449 }
450 
createConcatenate(const fir::CharBoxValue & lhs,const fir::CharBoxValue & rhs)451 fir::CharBoxValue fir::factory::CharacterExprHelper::createConcatenate(
452     const fir::CharBoxValue &lhs, const fir::CharBoxValue &rhs) {
453   auto lhsLen = builder.createConvert(loc, builder.getCharacterLengthType(),
454                                       lhs.getLen());
455   auto rhsLen = builder.createConvert(loc, builder.getCharacterLengthType(),
456                                       rhs.getLen());
457   mlir::Value len = builder.create<mlir::arith::AddIOp>(loc, lhsLen, rhsLen);
458   auto temp = createCharacterTemp(getCharacterType(rhs), len);
459   createCopy(temp, lhs, lhsLen);
460   auto one = builder.createIntegerConstant(loc, len.getType(), 1);
461   auto upperBound = builder.create<mlir::arith::SubIOp>(loc, len, one);
462   auto lhsLenIdx = builder.createConvert(loc, builder.getIndexType(), lhsLen);
463   auto fromBuff = getCharBoxBuffer(rhs);
464   auto toBuff = getCharBoxBuffer(temp);
465   fir::factory::DoLoopHelper{builder, loc}.createLoop(
466       lhsLenIdx, upperBound, one,
467       [&](fir::FirOpBuilder &bldr, mlir::Value index) {
468         auto rhsIndex = bldr.create<mlir::arith::SubIOp>(loc, index, lhsLenIdx);
469         auto charVal = createLoadCharAt(fromBuff, rhsIndex);
470         createStoreCharAt(toBuff, index, charVal);
471       });
472   return temp;
473 }
474 
createSubstring(const fir::CharBoxValue & box,llvm::ArrayRef<mlir::Value> bounds)475 fir::CharBoxValue fir::factory::CharacterExprHelper::createSubstring(
476     const fir::CharBoxValue &box, llvm::ArrayRef<mlir::Value> bounds) {
477   // Constant need to be materialize in memory to use fir.coordinate_of.
478   auto nbounds = bounds.size();
479   if (nbounds < 1 || nbounds > 2) {
480     mlir::emitError(loc, "Incorrect number of bounds in substring");
481     return {mlir::Value{}, mlir::Value{}};
482   }
483   mlir::SmallVector<mlir::Value> castBounds;
484   // Convert bounds to length type to do safe arithmetic on it.
485   for (auto bound : bounds)
486     castBounds.push_back(
487         builder.createConvert(loc, builder.getCharacterLengthType(), bound));
488   auto lowerBound = castBounds[0];
489   // FIR CoordinateOp is zero based but Fortran substring are one based.
490   auto one = builder.createIntegerConstant(loc, lowerBound.getType(), 1);
491   auto offset =
492       builder.create<mlir::arith::SubIOp>(loc, lowerBound, one).getResult();
493   auto addr = createElementAddr(box.getBuffer(), offset);
494   auto kind = getCharacterKind(box.getBuffer().getType());
495   auto charTy = fir::CharacterType::getUnknownLen(builder.getContext(), kind);
496   auto resultType = builder.getRefType(charTy);
497   auto substringRef = builder.createConvert(loc, resultType, addr);
498 
499   // Compute the length.
500   mlir::Value substringLen;
501   if (nbounds < 2) {
502     substringLen =
503         builder.create<mlir::arith::SubIOp>(loc, box.getLen(), castBounds[0]);
504   } else {
505     substringLen =
506         builder.create<mlir::arith::SubIOp>(loc, castBounds[1], castBounds[0]);
507   }
508   substringLen = builder.create<mlir::arith::AddIOp>(loc, substringLen, one);
509 
510   // Set length to zero if bounds were reversed (Fortran 2018 9.4.1)
511   auto zero = builder.createIntegerConstant(loc, substringLen.getType(), 0);
512   auto cdt = builder.create<mlir::arith::CmpIOp>(
513       loc, mlir::arith::CmpIPredicate::slt, substringLen, zero);
514   substringLen =
515       builder.create<mlir::arith::SelectOp>(loc, cdt, zero, substringLen);
516 
517   return {substringRef, substringLen};
518 }
519 
520 mlir::Value
createLenTrim(const fir::CharBoxValue & str)521 fir::factory::CharacterExprHelper::createLenTrim(const fir::CharBoxValue &str) {
522   // Note: Runtime for LEN_TRIM should also be available at some
523   // point. For now use an inlined implementation.
524   auto indexType = builder.getIndexType();
525   auto len = builder.createConvert(loc, indexType, str.getLen());
526   auto one = builder.createIntegerConstant(loc, indexType, 1);
527   auto minusOne = builder.createIntegerConstant(loc, indexType, -1);
528   auto zero = builder.createIntegerConstant(loc, indexType, 0);
529   auto trueVal = builder.createIntegerConstant(loc, builder.getI1Type(), 1);
530   auto blank = createBlankConstantCode(getCharacterType(str));
531   mlir::Value lastChar = builder.create<mlir::arith::SubIOp>(loc, len, one);
532 
533   auto iterWhile =
534       builder.create<fir::IterWhileOp>(loc, lastChar, zero, minusOne, trueVal,
535                                        /*returnFinalCount=*/false, lastChar);
536   auto insPt = builder.saveInsertionPoint();
537   builder.setInsertionPointToStart(iterWhile.getBody());
538   auto index = iterWhile.getInductionVar();
539   // Look for first non-blank from the right of the character.
540   auto fromBuff = getCharBoxBuffer(str);
541   auto elemAddr = createElementAddr(fromBuff, index);
542   auto codeAddr =
543       builder.createConvert(loc, builder.getRefType(blank.getType()), elemAddr);
544   auto c = builder.create<fir::LoadOp>(loc, codeAddr);
545   auto isBlank = builder.create<mlir::arith::CmpIOp>(
546       loc, mlir::arith::CmpIPredicate::eq, blank, c);
547   llvm::SmallVector<mlir::Value> results = {isBlank, index};
548   builder.create<fir::ResultOp>(loc, results);
549   builder.restoreInsertionPoint(insPt);
550   // Compute length after iteration (zero if all blanks)
551   mlir::Value newLen =
552       builder.create<mlir::arith::AddIOp>(loc, iterWhile.getResult(1), one);
553   auto result = builder.create<mlir::arith::SelectOp>(
554       loc, iterWhile.getResult(0), zero, newLen);
555   return builder.createConvert(loc, builder.getCharacterLengthType(), result);
556 }
557 
558 fir::CharBoxValue
createCharacterTemp(mlir::Type type,int len)559 fir::factory::CharacterExprHelper::createCharacterTemp(mlir::Type type,
560                                                        int len) {
561   assert(len >= 0 && "expected positive length");
562   auto kind = recoverCharacterType(type).getFKind();
563   auto charType = fir::CharacterType::get(builder.getContext(), kind, len);
564   auto addr = builder.create<fir::AllocaOp>(loc, charType);
565   auto mlirLen =
566       builder.createIntegerConstant(loc, builder.getCharacterLengthType(), len);
567   return {addr, mlirLen};
568 }
569 
570 // Returns integer with code for blank. The integer has the same
571 // size as the character. Blank has ascii space code for all kinds.
createBlankConstantCode(fir::CharacterType type)572 mlir::Value fir::factory::CharacterExprHelper::createBlankConstantCode(
573     fir::CharacterType type) {
574   auto bits = builder.getKindMap().getCharacterBitsize(type.getFKind());
575   auto intType = builder.getIntegerType(bits);
576   return builder.createIntegerConstant(loc, intType, ' ');
577 }
578 
createBlankConstant(fir::CharacterType type)579 mlir::Value fir::factory::CharacterExprHelper::createBlankConstant(
580     fir::CharacterType type) {
581   return createSingletonFromCode(createBlankConstantCode(type),
582                                  type.getFKind());
583 }
584 
createAssign(const fir::ExtendedValue & lhs,const fir::ExtendedValue & rhs)585 void fir::factory::CharacterExprHelper::createAssign(
586     const fir::ExtendedValue &lhs, const fir::ExtendedValue &rhs) {
587   if (auto *str = rhs.getBoxOf<fir::CharBoxValue>()) {
588     if (auto *to = lhs.getBoxOf<fir::CharBoxValue>()) {
589       createAssign(*to, *str);
590       return;
591     }
592   }
593   TODO(loc, "character array assignment");
594   // Note that it is not sure the array aspect should be handled
595   // by this utility.
596 }
597 
598 mlir::Value
createEmboxChar(mlir::Value addr,mlir::Value len)599 fir::factory::CharacterExprHelper::createEmboxChar(mlir::Value addr,
600                                                    mlir::Value len) {
601   return createEmbox(fir::CharBoxValue{addr, len});
602 }
603 
604 std::pair<mlir::Value, mlir::Value>
createUnboxChar(mlir::Value boxChar)605 fir::factory::CharacterExprHelper::createUnboxChar(mlir::Value boxChar) {
606   using T = std::pair<mlir::Value, mlir::Value>;
607   return toExtendedValue(boxChar).match(
608       [](const fir::CharBoxValue &b) -> T {
609         return {b.getBuffer(), b.getLen()};
610       },
611       [](const fir::CharArrayBoxValue &b) -> T {
612         return {b.getBuffer(), b.getLen()};
613       },
614       [](const auto &) -> T { llvm::report_fatal_error("not a character"); });
615 }
616 
isCharacterLiteral(mlir::Type type)617 bool fir::factory::CharacterExprHelper::isCharacterLiteral(mlir::Type type) {
618   if (auto seqType = type.dyn_cast<fir::SequenceType>())
619     return (seqType.getShape().size() == 1) &&
620            fir::isa_char(seqType.getEleTy());
621   return false;
622 }
623 
624 fir::KindTy
getCharacterKind(mlir::Type type)625 fir::factory::CharacterExprHelper::getCharacterKind(mlir::Type type) {
626   assert(isCharacterScalar(type) && "expected scalar character");
627   return recoverCharacterType(type).getFKind();
628 }
629 
630 fir::KindTy
getCharacterOrSequenceKind(mlir::Type type)631 fir::factory::CharacterExprHelper::getCharacterOrSequenceKind(mlir::Type type) {
632   return recoverCharacterType(type).getFKind();
633 }
634 
hasConstantLengthInType(const fir::ExtendedValue & exv)635 bool fir::factory::CharacterExprHelper::hasConstantLengthInType(
636     const fir::ExtendedValue &exv) {
637   auto charTy = recoverCharacterType(fir::getBase(exv).getType());
638   return charTy.hasConstantLen();
639 }
640 
641 mlir::Value
createSingletonFromCode(mlir::Value code,int kind)642 fir::factory::CharacterExprHelper::createSingletonFromCode(mlir::Value code,
643                                                            int kind) {
644   auto charType = fir::CharacterType::get(builder.getContext(), kind, 1);
645   auto bits = builder.getKindMap().getCharacterBitsize(kind);
646   auto intType = builder.getIntegerType(bits);
647   auto cast = builder.createConvert(loc, intType, code);
648   auto undef = builder.create<fir::UndefOp>(loc, charType);
649   auto zero = builder.getIntegerAttr(builder.getIndexType(), 0);
650   return builder.create<fir::InsertValueOp>(loc, charType, undef, cast,
651                                             builder.getArrayAttr(zero));
652 }
653 
extractCodeFromSingleton(mlir::Value singleton)654 mlir::Value fir::factory::CharacterExprHelper::extractCodeFromSingleton(
655     mlir::Value singleton) {
656   auto type = getCharacterType(singleton);
657   assert(type.getLen() == 1);
658   auto bits = builder.getKindMap().getCharacterBitsize(type.getFKind());
659   auto intType = builder.getIntegerType(bits);
660   auto zero = builder.getIntegerAttr(builder.getIndexType(), 0);
661   return builder.create<fir::ExtractValueOp>(loc, intType, singleton,
662                                              builder.getArrayAttr(zero));
663 }
664 
665 mlir::Value
readLengthFromBox(mlir::Value box)666 fir::factory::CharacterExprHelper::readLengthFromBox(mlir::Value box) {
667   auto lenTy = builder.getCharacterLengthType();
668   auto size = builder.create<fir::BoxEleSizeOp>(loc, lenTy, box);
669   auto charTy = recoverCharacterType(box.getType());
670   auto bits = builder.getKindMap().getCharacterBitsize(charTy.getFKind());
671   auto width = bits / 8;
672   if (width > 1) {
673     auto widthVal = builder.createIntegerConstant(loc, lenTy, width);
674     return builder.create<mlir::arith::DivSIOp>(loc, size, widthVal);
675   }
676   return size;
677 }
678 
getLength(mlir::Value memref)679 mlir::Value fir::factory::CharacterExprHelper::getLength(mlir::Value memref) {
680   auto memrefType = memref.getType();
681   auto charType = recoverCharacterType(memrefType);
682   assert(charType && "must be a character type");
683   if (charType.hasConstantLen())
684     return builder.createIntegerConstant(loc, builder.getCharacterLengthType(),
685                                          charType.getLen());
686   if (memrefType.isa<fir::BoxType>())
687     return readLengthFromBox(memref);
688   if (memrefType.isa<fir::BoxCharType>())
689     return createUnboxChar(memref).second;
690 
691   // Length cannot be deduced from memref.
692   return {};
693 }
694 
695 std::pair<mlir::Value, mlir::Value>
extractCharacterProcedureTuple(fir::FirOpBuilder & builder,mlir::Location loc,mlir::Value tuple)696 fir::factory::extractCharacterProcedureTuple(fir::FirOpBuilder &builder,
697                                              mlir::Location loc,
698                                              mlir::Value tuple) {
699   mlir::TupleType tupleType = tuple.getType().cast<mlir::TupleType>();
700   mlir::Value addr = builder.create<fir::ExtractValueOp>(
701       loc, tupleType.getType(0), tuple,
702       builder.getArrayAttr(
703           {builder.getIntegerAttr(builder.getIndexType(), 0)}));
704   mlir::Value proc = [&]() -> mlir::Value {
705     if (auto addrTy = addr.getType().dyn_cast<fir::BoxProcType>())
706       return builder.create<fir::BoxAddrOp>(loc, addrTy.getEleTy(), addr);
707     return addr;
708   }();
709   mlir::Value len = builder.create<fir::ExtractValueOp>(
710       loc, tupleType.getType(1), tuple,
711       builder.getArrayAttr(
712           {builder.getIntegerAttr(builder.getIndexType(), 1)}));
713   return {proc, len};
714 }
715 
createCharacterProcedureTuple(fir::FirOpBuilder & builder,mlir::Location loc,mlir::Type argTy,mlir::Value addr,mlir::Value len)716 mlir::Value fir::factory::createCharacterProcedureTuple(
717     fir::FirOpBuilder &builder, mlir::Location loc, mlir::Type argTy,
718     mlir::Value addr, mlir::Value len) {
719   mlir::TupleType tupleType = argTy.cast<mlir::TupleType>();
720   addr = builder.createConvert(loc, tupleType.getType(0), addr);
721   if (len)
722     len = builder.createConvert(loc, tupleType.getType(1), len);
723   else
724     len = builder.create<fir::UndefOp>(loc, tupleType.getType(1));
725   mlir::Value tuple = builder.create<fir::UndefOp>(loc, tupleType);
726   tuple = builder.create<fir::InsertValueOp>(
727       loc, tupleType, tuple, addr,
728       builder.getArrayAttr(
729           {builder.getIntegerAttr(builder.getIndexType(), 0)}));
730   tuple = builder.create<fir::InsertValueOp>(
731       loc, tupleType, tuple, len,
732       builder.getArrayAttr(
733           {builder.getIntegerAttr(builder.getIndexType(), 1)}));
734   return tuple;
735 }
736 
737 mlir::Type
getCharacterProcedureTupleType(mlir::Type funcPointerType)738 fir::factory::getCharacterProcedureTupleType(mlir::Type funcPointerType) {
739   mlir::MLIRContext *context = funcPointerType.getContext();
740   mlir::Type lenType = mlir::IntegerType::get(context, 64);
741   return mlir::TupleType::get(context, {funcPointerType, lenType});
742 }
743