1 //===- CharacterConversion.cpp -- convert between character encodings -----===//
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 #include "PassDetail.h"
10 #include "flang/Optimizer/Dialect/FIRDialect.h"
11 #include "flang/Optimizer/Dialect/FIROps.h"
12 #include "flang/Optimizer/Dialect/FIRType.h"
13 #include "flang/Optimizer/Support/FIRContext.h"
14 #include "flang/Optimizer/Support/KindMapping.h"
15 #include "flang/Optimizer/Transforms/Passes.h"
16 #include "mlir/Dialect/StandardOps/IR/Ops.h"
17 #include "mlir/IR/Diagnostics.h"
18 #include "mlir/Pass/Pass.h"
19 #include "mlir/Transforms/DialectConversion.h"
20 #include "llvm/Support/Debug.h"
21 
22 #define DEBUG_TYPE "flang-character-conversion"
23 
24 namespace {
25 
26 // TODO: Future hook to select some set of runtime calls.
27 struct CharacterConversionOptions {
28   std::string runtimeName;
29 };
30 
31 class CharacterConvertConversion
32     : public mlir::OpRewritePattern<fir::CharConvertOp> {
33 public:
34   using OpRewritePattern::OpRewritePattern;
35 
36   mlir::LogicalResult
37   matchAndRewrite(fir::CharConvertOp conv,
38                   mlir::PatternRewriter &rewriter) const override {
39     auto kindMap = fir::getKindMapping(conv->getParentOfType<mlir::ModuleOp>());
40     auto loc = conv.getLoc();
41 
42     LLVM_DEBUG(llvm::dbgs()
43                << "running character conversion on " << conv << '\n');
44 
45     // Establish a loop that executes count iterations.
46     auto zero = rewriter.create<mlir::arith::ConstantIndexOp>(loc, 0);
47     auto one = rewriter.create<mlir::arith::ConstantIndexOp>(loc, 1);
48     auto idxTy = rewriter.getIndexType();
49     auto castCnt = rewriter.create<fir::ConvertOp>(loc, idxTy, conv.count());
50     auto countm1 = rewriter.create<mlir::arith::SubIOp>(loc, castCnt, one);
51     auto loop = rewriter.create<fir::DoLoopOp>(loc, zero, countm1, one);
52     auto insPt = rewriter.saveInsertionPoint();
53     rewriter.setInsertionPointToStart(loop.getBody());
54 
55     // For each code point in the `from` string, convert naively to the `to`
56     // string code point. Conversion is done blindly on size only, not value.
57     auto getCharBits = [&](mlir::Type t) {
58       auto chrTy = fir::unwrapSequenceType(fir::dyn_cast_ptrEleTy(t))
59                        .cast<fir::CharacterType>();
60       return kindMap.getCharacterBitsize(chrTy.getFKind());
61     };
62     auto fromBits = getCharBits(conv.from().getType());
63     auto toBits = getCharBits(conv.to().getType());
64     auto pointerType = [&](unsigned bits) {
65       return fir::ReferenceType::get(fir::SequenceType::get(
66           fir::SequenceType::ShapeRef{fir::SequenceType::getUnknownExtent()},
67           rewriter.getIntegerType(bits)));
68     };
69     auto fromPtrTy = pointerType(fromBits);
70     auto toTy = rewriter.getIntegerType(toBits);
71     auto toPtrTy = pointerType(toBits);
72     auto fromPtr = rewriter.create<fir::ConvertOp>(loc, fromPtrTy, conv.from());
73     auto toPtr = rewriter.create<fir::ConvertOp>(loc, toPtrTy, conv.to());
74     auto getEleTy = [&](unsigned bits) {
75       return fir::ReferenceType::get(rewriter.getIntegerType(bits));
76     };
77     auto fromi = rewriter.create<fir::CoordinateOp>(
78         loc, getEleTy(fromBits), fromPtr,
79         mlir::ValueRange{loop.getInductionVar()});
80     auto toi = rewriter.create<fir::CoordinateOp>(
81         loc, getEleTy(toBits), toPtr, mlir::ValueRange{loop.getInductionVar()});
82     auto load = rewriter.create<fir::LoadOp>(loc, fromi);
83     mlir::Value icast =
84         (fromBits >= toBits)
85             ? rewriter.create<fir::ConvertOp>(loc, toTy, load).getResult()
86             : rewriter.create<mlir::arith::ExtUIOp>(loc, toTy, load)
87                   .getResult();
88     rewriter.replaceOpWithNewOp<fir::StoreOp>(conv, icast, toi);
89     rewriter.restoreInsertionPoint(insPt);
90     return mlir::success();
91   }
92 };
93 
94 /// Rewrite the `fir.char_convert` op into a loop. This pass must be run only on
95 /// fir::CharConvertOp.
96 class CharacterConversion
97     : public fir::CharacterConversionBase<CharacterConversion> {
98 public:
99   void runOnOperation() override {
100     CharacterConversionOptions clOpts{useRuntimeCalls.getValue()};
101     if (clOpts.runtimeName.empty()) {
102       auto *context = &getContext();
103       auto *func = getOperation();
104       mlir::OwningRewritePatternList patterns(context);
105       patterns.insert<CharacterConvertConversion>(context);
106       mlir::ConversionTarget target(*context);
107       target.addLegalDialect<mlir::AffineDialect, fir::FIROpsDialect,
108                              mlir::arith::ArithmeticDialect,
109                              mlir::StandardOpsDialect>();
110 
111       // apply the patterns
112       target.addIllegalOp<fir::CharConvertOp>();
113       if (mlir::failed(mlir::applyPartialConversion(func, target,
114                                                     std::move(patterns)))) {
115         mlir::emitError(mlir::UnknownLoc::get(context),
116                         "error in rewriting character convert op");
117         signalPassFailure();
118       }
119       return;
120     }
121 
122     // TODO: some sort of runtime supported conversion?
123     signalPassFailure();
124   }
125 };
126 } // end anonymous namespace
127 
128 std::unique_ptr<mlir::Pass> fir::createCharacterConversionPass() {
129   return std::make_unique<CharacterConversion>();
130 }
131