From 6eb7634f301a0ef6465f514ae8cc4634602470a2 Mon Sep 17 00:00:00 2001 From: Jean Perier Date: Sun, 10 Oct 2021 20:18:45 +0200 Subject: [PATCH] [fir] Add character conversion pass Upstream the character conversion pass. Translates entities of one CHARACTER KIND to another. By default the translation is to naively zero-extend or truncate a code point to fit the destination size. This patch is part of the upstreaming effort from fir-dev branch. Co-authored-by: Eric Schweitz Co-authored-by: Valentin Clement Reviewed By: schweitz Differential Revision: https://reviews.llvm.org/D111405 --- flang/include/flang/Optimizer/Transforms/Passes.h | 1 + flang/include/flang/Optimizer/Transforms/Passes.td | 18 +++ flang/lib/Optimizer/Transforms/CMakeLists.txt | 1 + .../Optimizer/Transforms/CharacterConversion.cpp | 128 +++++++++++++++++++++ flang/test/Fir/char-conversion.fir | 29 +++++ 5 files changed, 177 insertions(+) create mode 100644 flang/lib/Optimizer/Transforms/CharacterConversion.cpp create mode 100644 flang/test/Fir/char-conversion.fir diff --git a/flang/include/flang/Optimizer/Transforms/Passes.h b/flang/include/flang/Optimizer/Transforms/Passes.h index f89e80c..fc689b0 100644 --- a/flang/include/flang/Optimizer/Transforms/Passes.h +++ b/flang/include/flang/Optimizer/Transforms/Passes.h @@ -27,6 +27,7 @@ namespace fir { //===----------------------------------------------------------------------===// std::unique_ptr createAffineDemotionPass(); +std::unique_ptr createCharacterConversionPass(); std::unique_ptr createExternalNameConversionPass(); std::unique_ptr createPromoteToAffinePass(); diff --git a/flang/include/flang/Optimizer/Transforms/Passes.td b/flang/include/flang/Optimizer/Transforms/Passes.td index 1929480..b207ad7 100644 --- a/flang/include/flang/Optimizer/Transforms/Passes.td +++ b/flang/include/flang/Optimizer/Transforms/Passes.td @@ -55,6 +55,24 @@ def AffineDialectDemotion : FunctionPass<"demote-affine"> { ]; } +def CharacterConversion : Pass<"character-conversion"> { + let summary = "Convert CHARACTER entities with different KINDs"; + let description = [{ + Translates entities of one CHARACTER KIND to another. + + By default the translation is to naively zero-extend or truncate a code + point to fit the destination size. + }]; + let constructor = "::fir::createCharacterConversionPass()"; + let dependentDialects = [ "fir::FIROpsDialect" ]; + let options = [ + Option<"useRuntimeCalls", "use-runtime-calls", + "std::string", /*default=*/"std::string{}", + "Generate runtime calls to a named set of conversion routines. " + "By default, the conversions may produce unexpected results."> + ]; +} + def ExternalNameConversion : Pass<"external-name-interop", "mlir::ModuleOp"> { let summary = "Convert name for external interoperability"; let description = [{ diff --git a/flang/lib/Optimizer/Transforms/CMakeLists.txt b/flang/lib/Optimizer/Transforms/CMakeLists.txt index dbb0d46..6465ba8 100644 --- a/flang/lib/Optimizer/Transforms/CMakeLists.txt +++ b/flang/lib/Optimizer/Transforms/CMakeLists.txt @@ -1,6 +1,7 @@ add_flang_library(FIRTransforms AffinePromotion.cpp AffineDemotion.cpp + CharacterConversion.cpp Inliner.cpp ExternalNameConversion.cpp diff --git a/flang/lib/Optimizer/Transforms/CharacterConversion.cpp b/flang/lib/Optimizer/Transforms/CharacterConversion.cpp new file mode 100644 index 0000000..90c5193 --- /dev/null +++ b/flang/lib/Optimizer/Transforms/CharacterConversion.cpp @@ -0,0 +1,128 @@ +//===- CharacterConversion.cpp -- convert between character encodings -----===// +// +// 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 "PassDetail.h" +#include "flang/Optimizer/Dialect/FIRDialect.h" +#include "flang/Optimizer/Dialect/FIROps.h" +#include "flang/Optimizer/Dialect/FIRType.h" +#include "flang/Optimizer/Support/FIRContext.h" +#include "flang/Optimizer/Support/KindMapping.h" +#include "flang/Optimizer/Transforms/Passes.h" +#include "mlir/Dialect/StandardOps/IR/Ops.h" +#include "mlir/IR/Diagnostics.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Transforms/DialectConversion.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "flang-character-conversion" + +namespace { + +// TODO: Future hook to select some set of runtime calls. +struct CharacterConversionOptions { + std::string runtimeName; +}; + +class CharacterConvertConversion + : public mlir::OpRewritePattern { +public: + using OpRewritePattern::OpRewritePattern; + + mlir::LogicalResult + matchAndRewrite(fir::CharConvertOp conv, + mlir::PatternRewriter &rewriter) const override { + auto kindMap = fir::getKindMapping(conv->getParentOfType()); + auto loc = conv.getLoc(); + + LLVM_DEBUG(llvm::dbgs() + << "running character conversion on " << conv << '\n'); + + // Establish a loop that executes count iterations. + auto zero = rewriter.create(loc, 0); + auto one = rewriter.create(loc, 1); + auto idxTy = rewriter.getIndexType(); + auto castCnt = rewriter.create(loc, idxTy, conv.count()); + auto countm1 = rewriter.create(loc, castCnt, one); + auto loop = rewriter.create(loc, zero, countm1, one); + auto insPt = rewriter.saveInsertionPoint(); + rewriter.setInsertionPointToStart(loop.getBody()); + + // For each code point in the `from` string, convert naively to the `to` + // string code point. Conversion is done blindly on size only, not value. + auto getCharBits = [&](mlir::Type t) { + auto chrTy = fir::unwrapSequenceType(fir::dyn_cast_ptrEleTy(t)) + .cast(); + return kindMap.getCharacterBitsize(chrTy.getFKind()); + }; + auto fromBits = getCharBits(conv.from().getType()); + auto toBits = getCharBits(conv.to().getType()); + auto pointerType = [&](unsigned bits) { + return fir::ReferenceType::get(fir::SequenceType::get( + fir::SequenceType::ShapeRef{fir::SequenceType::getUnknownExtent()}, + rewriter.getIntegerType(bits))); + }; + auto fromPtrTy = pointerType(fromBits); + auto toTy = rewriter.getIntegerType(toBits); + auto toPtrTy = pointerType(toBits); + auto fromPtr = rewriter.create(loc, fromPtrTy, conv.from()); + auto toPtr = rewriter.create(loc, toPtrTy, conv.to()); + auto getEleTy = [&](unsigned bits) { + return fir::ReferenceType::get(rewriter.getIntegerType(bits)); + }; + auto fromi = rewriter.create( + loc, getEleTy(fromBits), fromPtr, + mlir::ValueRange{loop.getInductionVar()}); + auto toi = rewriter.create( + loc, getEleTy(toBits), toPtr, mlir::ValueRange{loop.getInductionVar()}); + auto load = rewriter.create(loc, fromi); + mlir::Value icast = + (fromBits >= toBits) + ? rewriter.create(loc, toTy, load).getResult() + : rewriter.create(loc, toTy, load).getResult(); + rewriter.replaceOpWithNewOp(conv, icast, toi); + rewriter.restoreInsertionPoint(insPt); + return mlir::success(); + } +}; + +/// Rewrite the `fir.char_convert` op into a loop. This pass must be run only on +/// fir::CharConvertOp. +class CharacterConversion + : public fir::CharacterConversionBase { +public: + void runOnOperation() override { + CharacterConversionOptions clOpts{useRuntimeCalls.getValue()}; + if (clOpts.runtimeName.empty()) { + auto *context = &getContext(); + auto *func = getOperation(); + mlir::OwningRewritePatternList patterns(context); + patterns.insert(context); + mlir::ConversionTarget target(*context); + target.addLegalDialect(); + + // apply the patterns + target.addIllegalOp(); + if (mlir::failed(mlir::applyPartialConversion(func, target, + std::move(patterns)))) { + mlir::emitError(mlir::UnknownLoc::get(context), + "error in rewriting character convert op"); + signalPassFailure(); + } + return; + } + + // TODO: some sort of runtime supported conversion? + signalPassFailure(); + } +}; +} // end anonymous namespace + +std::unique_ptr fir::createCharacterConversionPass() { + return std::make_unique(); +} diff --git a/flang/test/Fir/char-conversion.fir b/flang/test/Fir/char-conversion.fir new file mode 100644 index 0000000..e47096d --- /dev/null +++ b/flang/test/Fir/char-conversion.fir @@ -0,0 +1,29 @@ +// RUN: fir-opt --character-conversion %s | FileCheck %s + +func @char_convert() { + %1 = fir.undefined i32 + %2 = fir.undefined !fir.ref> + %3 = fir.undefined !fir.ref>> + fir.char_convert %2 for %1 to %3 : !fir.ref>, i32, !fir.ref>> + return +} + +// CHECK-LABEL: func @char_convert() { +// CHECK: %[[VAL_0:.*]] = fir.undefined i32 +// CHECK: %[[VAL_1:.*]] = fir.undefined !fir.ref> +// CHECK: %[[VAL_2:.*]] = fir.undefined !fir.ref>> +// CHECK: %[[VAL_3:.*]] = constant 0 : index +// CHECK: %[[VAL_4:.*]] = constant 1 : index +// CHECK: %[[VAL_5:.*]] = fir.convert %[[VAL_0]] : (i32) -> index +// CHECK: %[[VAL_6:.*]] = subi %[[VAL_5]], %[[VAL_4]] : index +// CHECK: fir.do_loop %[[VAL_7:.*]] = %[[VAL_3]] to %[[VAL_6]] step %[[VAL_4]] { +// CHECK: %[[VAL_8:.*]] = fir.convert %[[VAL_1]] : (!fir.ref>) -> !fir.ref> +// CHECK: %[[VAL_9:.*]] = fir.convert %[[VAL_2]] : (!fir.ref>>) -> !fir.ref> +// CHECK: %[[VAL_10:.*]] = fir.coordinate_of %[[VAL_8]], %[[VAL_7]] : (!fir.ref>, index) -> !fir.ref +// CHECK: %[[VAL_11:.*]] = fir.coordinate_of %[[VAL_9]], %[[VAL_7]] : (!fir.ref>, index) -> !fir.ref +// CHECK: %[[VAL_12:.*]] = fir.load %[[VAL_10]] : !fir.ref +// CHECK: %[[VAL_13:.*]] = zexti %[[VAL_12]] : i8 to i16 +// CHECK: fir.store %[[VAL_13]] to %[[VAL_11]] : !fir.ref +// CHECK: } +// CHECK: return +// CHECK: } -- 2.7.4