brintos

brintos / llvm-project-archived public Read only

0
0
Text · 20.8 KiB · 71e3f88 Raw
533 lines · cpp
1//===- SCFToEmitC.cpp - SCF to EmitC conversion ---------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements a pass to convert scf.if ops into emitc ops.10//11//===----------------------------------------------------------------------===//12 13#include "mlir/Conversion/SCFToEmitC/SCFToEmitC.h"14 15#include "mlir/Conversion/ConvertToEmitC/ToEmitCInterface.h"16#include "mlir/Dialect/EmitC/IR/EmitC.h"17#include "mlir/Dialect/EmitC/Transforms/TypeConversions.h"18#include "mlir/Dialect/SCF/IR/SCF.h"19#include "mlir/IR/Builders.h"20#include "mlir/IR/MLIRContext.h"21#include "mlir/IR/PatternMatch.h"22#include "mlir/Transforms/DialectConversion.h"23#include "mlir/Transforms/Passes.h"24#include "llvm/Support/LogicalResult.h"25 26namespace mlir {27#define GEN_PASS_DEF_SCFTOEMITC28#include "mlir/Conversion/Passes.h.inc"29} // namespace mlir30 31using namespace mlir;32using namespace mlir::scf;33 34namespace {35 36/// Implement the interface to convert SCF to EmitC.37struct SCFToEmitCDialectInterface : public ConvertToEmitCPatternInterface {38  using ConvertToEmitCPatternInterface::ConvertToEmitCPatternInterface;39 40  /// Hook for derived dialect interface to provide conversion patterns41  /// and mark dialect legal for the conversion target.42  void populateConvertToEmitCConversionPatterns(43      ConversionTarget &target, TypeConverter &typeConverter,44      RewritePatternSet &patterns) const final {45    populateEmitCSizeTTypeConversions(typeConverter);46    populateSCFToEmitCConversionPatterns(patterns, typeConverter);47  }48};49} // namespace50 51void mlir::registerConvertSCFToEmitCInterface(DialectRegistry &registry) {52  registry.addExtension(+[](MLIRContext *ctx, scf::SCFDialect *dialect) {53    dialect->addInterfaces<SCFToEmitCDialectInterface>();54  });55}56 57namespace {58 59struct SCFToEmitCPass : public impl::SCFToEmitCBase<SCFToEmitCPass> {60  void runOnOperation() override;61};62 63// Lower scf::for to emitc::for, implementing result values using64// emitc::variable's updated within the loop body.65struct ForLowering : public OpConversionPattern<ForOp> {66  using OpConversionPattern<ForOp>::OpConversionPattern;67 68  LogicalResult69  matchAndRewrite(ForOp forOp, OpAdaptor adaptor,70                  ConversionPatternRewriter &rewriter) const override;71};72 73// Create an uninitialized emitc::variable op for each result of the given op.74template <typename T>75static LogicalResult76createVariablesForResults(T op, const TypeConverter *typeConverter,77                          ConversionPatternRewriter &rewriter,78                          SmallVector<Value> &resultVariables) {79  if (!op.getNumResults())80    return success();81 82  Location loc = op->getLoc();83  MLIRContext *context = op.getContext();84 85  OpBuilder::InsertionGuard guard(rewriter);86  rewriter.setInsertionPoint(op);87 88  for (OpResult result : op.getResults()) {89    Type resultType = typeConverter->convertType(result.getType());90    if (!resultType)91      return rewriter.notifyMatchFailure(op, "result type conversion failed");92    Type varType = emitc::LValueType::get(resultType);93    emitc::OpaqueAttr noInit = emitc::OpaqueAttr::get(context, "");94    emitc::VariableOp var =95        emitc::VariableOp::create(rewriter, loc, varType, noInit);96    resultVariables.push_back(var);97  }98 99  return success();100}101 102// Create a series of assign ops assigning given values to given variables at103// the current insertion point of given rewriter.104static void assignValues(ValueRange values, ValueRange variables,105                         ConversionPatternRewriter &rewriter, Location loc) {106  for (auto [value, var] : llvm::zip(values, variables))107    emitc::AssignOp::create(rewriter, loc, var, value);108}109 110SmallVector<Value> loadValues(ArrayRef<Value> variables,111                              PatternRewriter &rewriter, Location loc) {112  return llvm::map_to_vector<>(variables, [&](Value var) {113    Type type = cast<emitc::LValueType>(var.getType()).getValueType();114    return emitc::LoadOp::create(rewriter, loc, type, var).getResult();115  });116}117 118static LogicalResult lowerYield(Operation *op, ValueRange resultVariables,119                                ConversionPatternRewriter &rewriter,120                                scf::YieldOp yield, bool createYield = true) {121  Location loc = yield.getLoc();122 123  OpBuilder::InsertionGuard guard(rewriter);124  rewriter.setInsertionPoint(yield);125 126  SmallVector<Value> yieldOperands;127  if (failed(rewriter.getRemappedValues(yield.getOperands(), yieldOperands)))128    return rewriter.notifyMatchFailure(op, "failed to lower yield operands");129 130  assignValues(yieldOperands, resultVariables, rewriter, loc);131 132  emitc::YieldOp::create(rewriter, loc);133  rewriter.eraseOp(yield);134 135  return success();136}137 138// Lower the contents of an scf::if/scf::index_switch regions to an139// emitc::if/emitc::switch region. The contents of the lowering region is140// moved into the respective lowered region, but the scf::yield is replaced not141// only with an emitc::yield, but also with a sequence of emitc::assign ops that142// set the yielded values into the result variables.143static LogicalResult lowerRegion(Operation *op, ValueRange resultVariables,144                                 ConversionPatternRewriter &rewriter,145                                 Region &region, Region &loweredRegion) {146  rewriter.inlineRegionBefore(region, loweredRegion, loweredRegion.end());147  Operation *terminator = loweredRegion.back().getTerminator();148  return lowerYield(op, resultVariables, rewriter,149                    cast<scf::YieldOp>(terminator));150}151 152LogicalResult153ForLowering::matchAndRewrite(ForOp forOp, OpAdaptor adaptor,154                             ConversionPatternRewriter &rewriter) const {155  Location loc = forOp.getLoc();156 157  if (forOp.getUnsignedCmp())158    return rewriter.notifyMatchFailure(forOp,159                                       "unsigned loops are not supported");160 161  // Create an emitc::variable op for each result. These variables will be162  // assigned to by emitc::assign ops within the loop body.163  SmallVector<Value> resultVariables;164  if (failed(createVariablesForResults(forOp, getTypeConverter(), rewriter,165                                       resultVariables)))166    return rewriter.notifyMatchFailure(forOp,167                                       "create variables for results failed");168 169  assignValues(adaptor.getInitArgs(), resultVariables, rewriter, loc);170 171  emitc::ForOp loweredFor =172      emitc::ForOp::create(rewriter, loc, adaptor.getLowerBound(),173                           adaptor.getUpperBound(), adaptor.getStep());174 175  Block *loweredBody = loweredFor.getBody();176 177  // Erase the auto-generated terminator for the lowered for op.178  rewriter.eraseOp(loweredBody->getTerminator());179 180  IRRewriter::InsertPoint ip = rewriter.saveInsertionPoint();181  rewriter.setInsertionPointToEnd(loweredBody);182 183  SmallVector<Value> iterArgsValues =184      loadValues(resultVariables, rewriter, loc);185 186  rewriter.restoreInsertionPoint(ip);187 188  // Convert the original region types into the new types by adding unrealized189  // casts in the beginning of the loop. This performs the conversion in place.190  if (failed(rewriter.convertRegionTypes(&forOp.getRegion(),191                                         *getTypeConverter(), nullptr))) {192    return rewriter.notifyMatchFailure(forOp, "region types conversion failed");193  }194 195  // Register the replacements for the block arguments and inline the body of196  // the scf.for loop into the body of the emitc::for loop.197  Block *scfBody = &(forOp.getRegion().front());198  SmallVector<Value> replacingValues;199  replacingValues.push_back(loweredFor.getInductionVar());200  replacingValues.append(iterArgsValues.begin(), iterArgsValues.end());201  rewriter.mergeBlocks(scfBody, loweredBody, replacingValues);202 203  auto result = lowerYield(forOp, resultVariables, rewriter,204                           cast<scf::YieldOp>(loweredBody->getTerminator()));205 206  if (failed(result)) {207    return result;208  }209 210  // Load variables into SSA values after the for loop.211  SmallVector<Value> resultValues = loadValues(resultVariables, rewriter, loc);212 213  rewriter.replaceOp(forOp, resultValues);214  return success();215}216 217// Lower scf::if to emitc::if, implementing result values as emitc::variable's218// updated within the then and else regions.219struct IfLowering : public OpConversionPattern<IfOp> {220  using OpConversionPattern<IfOp>::OpConversionPattern;221 222  LogicalResult223  matchAndRewrite(IfOp ifOp, OpAdaptor adaptor,224                  ConversionPatternRewriter &rewriter) const override;225};226 227} // namespace228 229LogicalResult230IfLowering::matchAndRewrite(IfOp ifOp, OpAdaptor adaptor,231                            ConversionPatternRewriter &rewriter) const {232  Location loc = ifOp.getLoc();233 234  // Create an emitc::variable op for each result. These variables will be235  // assigned to by emitc::assign ops within the then & else regions.236  SmallVector<Value> resultVariables;237  if (failed(createVariablesForResults(ifOp, getTypeConverter(), rewriter,238                                       resultVariables)))239    return rewriter.notifyMatchFailure(ifOp,240                                       "create variables for results failed");241 242  // Utility function to lower the contents of an scf::if region to an emitc::if243  // region. The contents of the scf::if regions is moved into the respective244  // emitc::if regions, but the scf::yield is replaced not only with an245  // emitc::yield, but also with a sequence of emitc::assign ops that set the246  // yielded values into the result variables.247  auto lowerRegion = [&resultVariables, &rewriter,248                      &ifOp](Region &region, Region &loweredRegion) {249    rewriter.inlineRegionBefore(region, loweredRegion, loweredRegion.end());250    Operation *terminator = loweredRegion.back().getTerminator();251    auto result = lowerYield(ifOp, resultVariables, rewriter,252                             cast<scf::YieldOp>(terminator));253    if (failed(result)) {254      return result;255    }256    return success();257  };258 259  Region &thenRegion = adaptor.getThenRegion();260  Region &elseRegion = adaptor.getElseRegion();261 262  bool hasElseBlock = !elseRegion.empty();263 264  auto loweredIf =265      emitc::IfOp::create(rewriter, loc, adaptor.getCondition(), false, false);266 267  Region &loweredThenRegion = loweredIf.getThenRegion();268  auto result = lowerRegion(thenRegion, loweredThenRegion);269  if (failed(result)) {270    return result;271  }272 273  if (hasElseBlock) {274    Region &loweredElseRegion = loweredIf.getElseRegion();275    auto result = lowerRegion(elseRegion, loweredElseRegion);276    if (failed(result)) {277      return result;278    }279  }280 281  rewriter.setInsertionPointAfter(ifOp);282  SmallVector<Value> results = loadValues(resultVariables, rewriter, loc);283 284  rewriter.replaceOp(ifOp, results);285  return success();286}287 288// Lower scf::index_switch to emitc::switch, implementing result values as289// emitc::variable's updated within the case and default regions.290struct IndexSwitchOpLowering : public OpConversionPattern<IndexSwitchOp> {291  using OpConversionPattern::OpConversionPattern;292 293  LogicalResult294  matchAndRewrite(IndexSwitchOp indexSwitchOp, OpAdaptor adaptor,295                  ConversionPatternRewriter &rewriter) const override;296};297 298LogicalResult IndexSwitchOpLowering::matchAndRewrite(299    IndexSwitchOp indexSwitchOp, OpAdaptor adaptor,300    ConversionPatternRewriter &rewriter) const {301  Location loc = indexSwitchOp.getLoc();302 303  // Create an emitc::variable op for each result. These variables will be304  // assigned to by emitc::assign ops within the case and default regions.305  SmallVector<Value> resultVariables;306  if (failed(createVariablesForResults(indexSwitchOp, getTypeConverter(),307                                       rewriter, resultVariables))) {308    return rewriter.notifyMatchFailure(indexSwitchOp,309                                       "create variables for results failed");310  }311 312  auto loweredSwitch =313      emitc::SwitchOp::create(rewriter, loc, adaptor.getArg(),314                              adaptor.getCases(), indexSwitchOp.getNumCases());315 316  // Lowering all case regions.317  for (auto pair :318       llvm::zip(adaptor.getCaseRegions(), loweredSwitch.getCaseRegions())) {319    if (failed(lowerRegion(indexSwitchOp, resultVariables, rewriter,320                           *std::get<0>(pair), std::get<1>(pair)))) {321      return failure();322    }323  }324 325  // Lowering default region.326  if (failed(lowerRegion(indexSwitchOp, resultVariables, rewriter,327                         adaptor.getDefaultRegion(),328                         loweredSwitch.getDefaultRegion()))) {329    return failure();330  }331 332  rewriter.setInsertionPointAfter(indexSwitchOp);333  SmallVector<Value> results = loadValues(resultVariables, rewriter, loc);334 335  rewriter.replaceOp(indexSwitchOp, results);336  return success();337}338 339// Lower scf::while to emitc::do using mutable variables to maintain loop state340// across iterations. The do-while structure ensures the condition is evaluated341// after each iteration, matching SCF while semantics.342struct WhileLowering : public OpConversionPattern<WhileOp> {343  using OpConversionPattern::OpConversionPattern;344 345  LogicalResult346  matchAndRewrite(WhileOp whileOp, OpAdaptor adaptor,347                  ConversionPatternRewriter &rewriter) const override {348    Location loc = whileOp.getLoc();349    MLIRContext *context = loc.getContext();350 351    // Create an emitc::variable op for each result. These variables will be352    // assigned to by emitc::assign ops within the loop body.353    SmallVector<Value> resultVariables;354    if (failed(createVariablesForResults(whileOp, getTypeConverter(), rewriter,355                                         resultVariables)))356      return rewriter.notifyMatchFailure(whileOp,357                                         "Failed to create result variables");358 359    // Create variable storage for loop-carried values to enable imperative360    // updates while maintaining SSA semantics at conversion boundaries.361    SmallVector<Value> loopVariables;362    if (failed(createVariablesForLoopCarriedValues(363            whileOp, rewriter, loopVariables, loc, context)))364      return failure();365 366    if (failed(lowerDoWhile(whileOp, loopVariables, resultVariables, context,367                            rewriter, loc)))368      return failure();369 370    rewriter.setInsertionPointAfter(whileOp);371 372    // Load the final result values from result variables.373    SmallVector<Value> finalResults =374        loadValues(resultVariables, rewriter, loc);375    rewriter.replaceOp(whileOp, finalResults);376 377    return success();378  }379 380private:381  // Initialize variables for loop-carried values to enable state updates382  // across iterations without SSA argument passing.383  LogicalResult createVariablesForLoopCarriedValues(384      WhileOp whileOp, ConversionPatternRewriter &rewriter,385      SmallVectorImpl<Value> &loopVars, Location loc,386      MLIRContext *context) const {387    OpBuilder::InsertionGuard guard(rewriter);388    rewriter.setInsertionPoint(whileOp);389 390    emitc::OpaqueAttr noInit = emitc::OpaqueAttr::get(context, "");391 392    for (Value init : whileOp.getInits()) {393      Type convertedType = getTypeConverter()->convertType(init.getType());394      if (!convertedType)395        return rewriter.notifyMatchFailure(whileOp, "type conversion failed");396 397      auto var = emitc::VariableOp::create(398          rewriter, loc, emitc::LValueType::get(convertedType), noInit);399      emitc::AssignOp::create(rewriter, loc, var.getResult(), init);400      loopVars.push_back(var);401    }402 403    return success();404  }405 406  // Lower scf.while to emitc.do.407  LogicalResult lowerDoWhile(WhileOp whileOp, ArrayRef<Value> loopVars,408                             ArrayRef<Value> resultVars, MLIRContext *context,409                             ConversionPatternRewriter &rewriter,410                             Location loc) const {411    // Create a global boolean variable to store the loop condition state.412    Type i1Type = IntegerType::get(context, 1);413    auto globalCondition =414        emitc::VariableOp::create(rewriter, loc, emitc::LValueType::get(i1Type),415                                  emitc::OpaqueAttr::get(context, ""));416    Value conditionVal = globalCondition.getResult();417 418    auto loweredDo = emitc::DoOp::create(rewriter, loc);419 420    // Convert region types to match the target dialect type system.421    if (failed(rewriter.convertRegionTypes(&whileOp.getBefore(),422                                           *getTypeConverter(), nullptr)) ||423        failed(rewriter.convertRegionTypes(&whileOp.getAfter(),424                                           *getTypeConverter(), nullptr))) {425      return rewriter.notifyMatchFailure(whileOp,426                                         "region types conversion failed");427    }428 429    // Prepare the before region (condition evaluation) for merging.430    Block *beforeBlock = &whileOp.getBefore().front();431    Block *bodyBlock = rewriter.createBlock(&loweredDo.getBodyRegion());432    rewriter.setInsertionPointToStart(bodyBlock);433 434    // Load current variable values to use as initial arguments for the435    // condition block.436    SmallVector<Value> replacingValues = loadValues(loopVars, rewriter, loc);437    rewriter.mergeBlocks(beforeBlock, bodyBlock, replacingValues);438 439    Operation *condTerminator =440        loweredDo.getBodyRegion().back().getTerminator();441    scf::ConditionOp condOp = cast<scf::ConditionOp>(condTerminator);442    rewriter.setInsertionPoint(condOp);443 444    // Update result variables with values from scf::condition.445    SmallVector<Value> conditionArgs;446    for (Value arg : condOp.getArgs()) {447      conditionArgs.push_back(rewriter.getRemappedValue(arg));448    }449    assignValues(conditionArgs, resultVars, rewriter, loc);450 451    // Convert scf.condition to condition variable assignment.452    Value condition = rewriter.getRemappedValue(condOp.getCondition());453    emitc::AssignOp::create(rewriter, loc, conditionVal, condition);454 455    // Wrap body region in conditional to preserve scf semantics. Only create456    // ifOp if after-region is non-empty.457    if (whileOp.getAfterBody()->getOperations().size() > 1) {458      auto ifOp = emitc::IfOp::create(rewriter, loc, condition, false, false);459 460      // Prepare the after region (loop body) for merging.461      Block *afterBlock = &whileOp.getAfter().front();462      Block *ifBodyBlock = rewriter.createBlock(&ifOp.getBodyRegion());463 464      // Replacement values for after block using condition op arguments.465      SmallVector<Value> afterReplacingValues;466      for (Value arg : condOp.getArgs())467        afterReplacingValues.push_back(rewriter.getRemappedValue(arg));468 469      rewriter.mergeBlocks(afterBlock, ifBodyBlock, afterReplacingValues);470 471      if (failed(lowerYield(whileOp, loopVars, rewriter,472                            cast<scf::YieldOp>(ifBodyBlock->getTerminator()))))473        return failure();474    }475 476    rewriter.eraseOp(condOp);477 478    // Create condition region that loads from the flag variable.479    Region &condRegion = loweredDo.getConditionRegion();480    Block *condBlock = rewriter.createBlock(&condRegion);481    rewriter.setInsertionPointToStart(condBlock);482 483    auto exprOp = emitc::ExpressionOp::create(484        rewriter, loc, i1Type, conditionVal, /*do_not_inline=*/false);485    Block *exprBlock = rewriter.createBlock(&exprOp.getBodyRegion());486 487    // Set up the expression block to load the condition variable.488    exprBlock->addArgument(conditionVal.getType(), loc);489    rewriter.setInsertionPointToStart(exprBlock);490 491    // Load the condition value and yield it as the expression result.492    Value cond =493        emitc::LoadOp::create(rewriter, loc, i1Type, exprBlock->getArgument(0));494    emitc::YieldOp::create(rewriter, loc, cond);495 496    // Yield the expression as the condition region result.497    rewriter.setInsertionPointToEnd(condBlock);498    emitc::YieldOp::create(rewriter, loc, exprOp);499 500    return success();501  }502};503 504void mlir::populateSCFToEmitCConversionPatterns(RewritePatternSet &patterns,505                                                TypeConverter &typeConverter) {506  patterns.add<ForLowering>(typeConverter, patterns.getContext());507  patterns.add<IfLowering>(typeConverter, patterns.getContext());508  patterns.add<IndexSwitchOpLowering>(typeConverter, patterns.getContext());509  patterns.add<WhileLowering>(typeConverter, patterns.getContext());510}511 512void SCFToEmitCPass::runOnOperation() {513  RewritePatternSet patterns(&getContext());514  TypeConverter typeConverter;515  // Fallback for other types.516  typeConverter.addConversion([](Type type) -> std::optional<Type> {517    if (!emitc::isSupportedEmitCType(type))518      return {};519    return type;520  });521  populateEmitCSizeTTypeConversions(typeConverter);522  populateSCFToEmitCConversionPatterns(patterns, typeConverter);523 524  // Configure conversion to lower out SCF operations.525  ConversionTarget target(getContext());526  target527      .addIllegalOp<scf::ForOp, scf::IfOp, scf::IndexSwitchOp, scf::WhileOp>();528  target.markUnknownOpDynamicallyLegal([](Operation *) { return true; });529  if (failed(530          applyPartialConversion(getOperation(), target, std::move(patterns))))531    signalPassFailure();532}533