MLIR 24.0.0git
RaiseWasmMLIR.cpp
Go to the documentation of this file.
1//===- RaiseWasmMLIR.cpp - Convert Wasm to less abstract dialects ---*- C++
2//-*-===//
3//
4// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5// See https://llvm.org/LICENSE.txt for license information.
6// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7//
8//===----------------------------------------------------------------------===//
9//
10// This file implements lowering of wasm operations to standard dialects ops.
11//
12//===----------------------------------------------------------------------===//
13
15
26#include "mlir/IR/ValueRange.h"
29#include "llvm/Support/LogicalResult.h"
30#include <optional>
31
32#define DEBUG_TYPE "wasm-convert"
33
34namespace mlir {
35#define GEN_PASS_DEF_RAISEWASMMLIR
36#include "mlir/Conversion/Passes.h.inc"
37} // namespace mlir
38
39using namespace mlir;
40using namespace mlir::wasmssa;
41namespace {
42
43template <typename SourceOp, typename TargetIntOp, typename TargetFPOp>
44struct IntFPDispatchMappingConversion : OpConversionPattern<SourceOp> {
45 using OpConversionPattern<SourceOp>::OpConversionPattern;
46
47 LogicalResult
48 matchAndRewrite(SourceOp srcOp, typename SourceOp::Adaptor adaptor,
49 ConversionPatternRewriter &rewriter) const override {
50 Type type = srcOp.getRhs().getType();
51 if (type.isInteger()) {
52 rewriter.replaceOpWithNewOp<TargetIntOp>(srcOp, srcOp->getResultTypes(),
53 adaptor.getOperands());
54 return success();
55 }
56 if (!type.isFloat())
57 return failure();
58 rewriter.replaceOpWithNewOp<TargetFPOp>(srcOp, srcOp->getResultTypes(),
59 adaptor.getOperands());
60 return success();
61 }
62};
63
64using WasmAddOpConversion =
65 IntFPDispatchMappingConversion<AddOp, arith::AddIOp, arith::AddFOp>;
66using WasmMulOpConversion =
67 IntFPDispatchMappingConversion<MulOp, arith::MulIOp, arith::MulFOp>;
68using WasmSubOpConversion =
69 IntFPDispatchMappingConversion<SubOp, arith::SubIOp, arith::SubFOp>;
70
71/// Convert a k-ary source operation \p SourceOp into an operation \p TargetOp.
72/// Both \p SourceOp and \p TargetOp must have the same number of operands.
73template <typename SourceOp, typename TargetOp>
74struct OpMappingConversion : OpConversionPattern<SourceOp> {
75 using OpConversionPattern<SourceOp>::OpConversionPattern;
76
77 LogicalResult
78 matchAndRewrite(SourceOp srcOp, typename SourceOp::Adaptor adaptor,
79 ConversionPatternRewriter &rewriter) const override {
80 rewriter.replaceOpWithNewOp<TargetOp>(srcOp, srcOp->getResultTypes(),
81 adaptor.getOperands());
82 return success();
83 }
84};
85
86using WasmAndOpConversion = OpMappingConversion<AndOp, arith::AndIOp>;
87using WasmCeilOpConversion = OpMappingConversion<CeilOp, math::CeilOp>;
88/// TODO: SIToFP and UIToFP don't allow specification of the floating point
89/// rounding mode
90using WasmConvertSOpConversion =
91 OpMappingConversion<ConvertSOp, arith::SIToFPOp>;
92using WasmConvertUOpConversion =
93 OpMappingConversion<ConvertUOp, arith::UIToFPOp>;
94using WasmDemoteOpConversion = OpMappingConversion<DemoteOp, arith::TruncFOp>;
95using WasmDivFPOpConversion = OpMappingConversion<DivOp, arith::DivFOp>;
96using WasmDivSIOpConversion = OpMappingConversion<DivSIOp, arith::DivSIOp>;
97using WasmDivUIOpConversion = OpMappingConversion<DivUIOp, arith::DivUIOp>;
98using WasmExtendSOpConversion =
99 OpMappingConversion<ExtendSI32Op, arith::ExtSIOp>;
100using WasmExtendUOpConversion =
101 OpMappingConversion<ExtendUI32Op, arith::ExtUIOp>;
102using WasmFloorOpConversion = OpMappingConversion<FloorOp, math::FloorOp>;
103using WasmMaxOpConversion = OpMappingConversion<MaxOp, arith::MaximumFOp>;
104using WasmMinOpConversion = OpMappingConversion<MinOp, arith::MinimumFOp>;
105using WasmOrOpConversion = OpMappingConversion<OrOp, arith::OrIOp>;
106using WasmPromoteOpConversion = OpMappingConversion<PromoteOp, arith::ExtFOp>;
107using WasmRemSIOpConversion = OpMappingConversion<RemSIOp, arith::RemSIOp>;
108using WasmRemUIOpConversion = OpMappingConversion<RemUIOp, arith::RemUIOp>;
109using WasmReinterpretOpConversion =
110 OpMappingConversion<ReinterpretOp, arith::BitcastOp>;
111using WasmShLOpConversion = OpMappingConversion<ShLOp, arith::ShLIOp>;
112using WasmShRSOpConversion = OpMappingConversion<ShRSOp, arith::ShRSIOp>;
113using WasmShRUOpConversion = OpMappingConversion<ShRUOp, arith::ShRUIOp>;
114using WasmXOrOpConversion = OpMappingConversion<XOrOp, arith::XOrIOp>;
115using WasmNegOpConversion = OpMappingConversion<NegOp, arith::NegFOp>;
116using WasmCopySignOpConversion =
117 OpMappingConversion<CopySignOp, math::CopySignOp>;
118using WasmClzOpConversion =
119 OpMappingConversion<ClzOp, math::CountLeadingZerosOp>;
120using WasmCtzOpConversion =
121 OpMappingConversion<CtzOp, math::CountTrailingZerosOp>;
122using WasmPopCntOpConversion = OpMappingConversion<PopCntOp, math::CtPopOp>;
123using WasmAbsOpConversion = OpMappingConversion<AbsOp, math::AbsFOp>;
124using WasmTruncOpConversion = OpMappingConversion<TruncOp, math::TruncOp>;
125using WasmSqrtOpConversion = OpMappingConversion<SqrtOp, math::SqrtOp>;
126using WasmWrapOpConversion = OpMappingConversion<WrapOp, arith::TruncIOp>;
127
128/// Lower a rotate to a series of bitwise operations. Intended for us
129/// in dialects that do not natively support rotate operations.
130///
131/// Result stays in the wasm dialect. It will then subsequently be lowered to
132/// the target dialect.
133///
134/// The rotate will be lowered to a pattern like so:
135///
136/// (val LHSShiftOp (bits & (width-1))) | (val RHSShiftOp (-bits & (width-1)))
137///
138/// Where LHSShiftOp and RHSShiftOp are shift operations. Concretely,
139///
140/// rotr = (val >> (bits & (width - 1))) | (val << (-bits & (width - 1)))
141/// rotl = (val << (bits & (width - 1))) | (val >> (-bits & (width - 1)))
142///
143/// Using this variant ensures that our rotate is defined in the target dialect.
144///
145/// \p SourceOp - Rotate operation to replace.
146/// \p LHSShiftOp - Shift operation to use on the left-hand side of the OR.
147/// \p RHSShiftOp - Shift operation to use on the right-hand side of the OR.
148template <typename SourceOp, typename LHSShiftOp, typename RHSShiftOp>
149struct RotateOpConversion : OpConversionPattern<SourceOp> {
150 using OpConversionPattern<SourceOp>::OpConversionPattern;
151
152 LogicalResult
153 matchAndRewrite(SourceOp srcOp, typename SourceOp::Adaptor adaptor,
154 ConversionPatternRewriter &rewriter) const override {
155 const Type ty = srcOp->getResultTypes()[0];
156 const Location loc = srcOp->getLoc();
157 const Value val = adaptor.getVal();
158 const Value bits = adaptor.getBits();
159 const unsigned width = ty.getIntOrFloatBitWidth();
160
161 // Materialize (width - 1) for use in both sides of the expression.
162 auto cstWidthMinusOne =
163 ConstOp::create(rewriter, loc, IntegerAttr::get(ty, width - 1));
164
165 // Form the left-hand side of the OR:
166 // (val (lhs shift op) (bits & (width - 1)))
167 auto orLHS = LHSShiftOp::create(
168 rewriter, loc, val,
169 AndOp::create(rewriter, loc, bits, cstWidthMinusOne));
170
171 // Form the right-hand side of the OR:
172 // (val (rhs shift op) (-bits & (width - 1)))
173 auto orRHS = RHSShiftOp::create(
174 rewriter, loc, val,
175 // (-bits & (width - 1))
176 AndOp::create(rewriter, loc,
177 // 0 - bits == -bits
178 SubOp::create(rewriter, loc,
179 ConstOp::create(rewriter, loc,
180 IntegerAttr::get(ty, 0)),
181 bits),
182 cstWidthMinusOne));
183
184 // OR together the two shifts and replace the rotate with the new
185 // expression.
186 rewriter.replaceOpWithNewOp<OrOp>(srcOp, orLHS, orRHS);
187 return success();
188 }
189};
190
191using WasmRotrOpConversion = RotateOpConversion<RotrOp, ShRUOp, ShLOp>;
192using WasmRotlOpConversion = RotateOpConversion<RotlOp, ShLOp, ShRUOp>;
193
194template <typename SourceOp, typename TargetOp, typename AttrType,
195 typename ValType, ValType flag>
196struct ComparisonOpConversion : OpConversionPattern<SourceOp> {
197 using OpConversionPattern<SourceOp>::OpConversionPattern;
198
199 LogicalResult
200 matchAndRewrite(SourceOp srcOp, typename SourceOp::Adaptor adaptor,
201 ConversionPatternRewriter &rewriter) const override {
202 auto cmpRes =
203 TargetOp::create(rewriter, srcOp.getLoc(), rewriter.getI1Type(),
204 AttrType::get(rewriter.getContext(), flag),
205 adaptor.getLhs(), adaptor.getRhs())
206 .getResult();
207 rewriter.replaceOpWithNewOp<arith::ExtUIOp>(srcOp, rewriter.getI32Type(),
208 cmpRes);
209
210 return success();
211 }
212};
213
214template <typename SourceOp, arith::CmpFPredicate compFlag>
215using FPComparisonConversion =
216 ComparisonOpConversion<SourceOp, arith::CmpFOp, arith::CmpFPredicateAttr,
217 arith::CmpFPredicate, compFlag>;
218
219template <typename SourceOp, arith::CmpIPredicate compFlag>
220using IntComparisonConversion =
221 ComparisonOpConversion<SourceOp, arith::CmpIOp, arith::CmpIPredicateAttr,
222 arith::CmpIPredicate, compFlag>;
223
224using WasmLtSIOpConversion =
225 IntComparisonConversion<LtSIOp, arith::CmpIPredicate::slt>;
226using WasmLeSIOpConversion =
227 IntComparisonConversion<LeSIOp, arith::CmpIPredicate::sle>;
228using WasmGtSIOpConversion =
229 IntComparisonConversion<GtSIOp, arith::CmpIPredicate::sgt>;
230using WasmGeSIOpConversion =
231 IntComparisonConversion<GeSIOp, arith::CmpIPredicate::sge>;
232using WasmLtUIOpConversion =
233 IntComparisonConversion<LtUIOp, arith::CmpIPredicate::ult>;
234using WasmLeUIOpConversion =
235 IntComparisonConversion<LeUIOp, arith::CmpIPredicate::ule>;
236using WasmGtUIOpConversion =
237 IntComparisonConversion<GtUIOp, arith::CmpIPredicate::ugt>;
238using WasmGeUIOpConversion =
239 IntComparisonConversion<GeUIOp, arith::CmpIPredicate::uge>;
240using WasmLtOpConversion =
241 FPComparisonConversion<LtOp, arith::CmpFPredicate::OLT>;
242using WasmLeOpConversion =
243 FPComparisonConversion<LeOp, arith::CmpFPredicate::OLE>;
244using WasmGtOpConversion =
245 FPComparisonConversion<GtOp, arith::CmpFPredicate::OGT>;
246using WasmGeOpConversion =
247 FPComparisonConversion<GeOp, arith::CmpFPredicate::OGE>;
248
249template <typename SourceOp, arith::CmpIPredicate IntFlag,
250 arith::CmpFPredicate FloatFlag>
251struct IntFpComparisonOpConversion : OpConversionPattern<SourceOp> {
252 using OpConversionPattern<SourceOp>::OpConversionPattern;
253
254 LogicalResult
255 matchAndRewrite(SourceOp srcOp, typename SourceOp::Adaptor adaptor,
256 ConversionPatternRewriter &rewriter) const override {
257 Value comparisonResult;
258 if (srcOp.getLhs().getType().isInteger())
259 comparisonResult =
260 arith::CmpIOp::create(
261 rewriter, srcOp.getLoc(), rewriter.getI1Type(),
262 arith::CmpIPredicateAttr::get(rewriter.getContext(), IntFlag),
263 adaptor.getLhs(), adaptor.getRhs())
264 .getResult();
265 else if (srcOp.getLhs().getType().isFloat())
266 comparisonResult =
267 arith::CmpFOp::create(
268 rewriter, srcOp.getLoc(), rewriter.getI1Type(),
269 arith::CmpFPredicateAttr::get(rewriter.getContext(), FloatFlag),
270 adaptor.getLhs(), adaptor.getRhs())
271 .getResult();
272 else
273 return rewriter.notifyMatchFailure(
274 srcOp.getLoc(), "Unsupported datatype for comparison OP.");
275
276 rewriter.replaceOpWithNewOp<arith::ExtUIOp>(srcOp, rewriter.getI32Type(),
277 comparisonResult);
278 return success();
279 }
280};
281
282using WasmEqOpConversion =
283 IntFpComparisonOpConversion<EqOp, arith::CmpIPredicate::eq,
284 arith::CmpFPredicate::OEQ>;
285using WasmNeOpConversion =
286 IntFpComparisonOpConversion<NeOp, arith::CmpIPredicate::ne,
287 arith::CmpFPredicate::ONE>;
288
289struct WasmCallOpConversion : OpConversionPattern<FuncCallOp> {
290 using OpConversionPattern::OpConversionPattern;
291
292 LogicalResult
293 matchAndRewrite(FuncCallOp funcCallOp, FuncCallOp::Adaptor adaptor,
294 ConversionPatternRewriter &rewriter) const override {
295 rewriter.replaceOpWithNewOp<func::CallOp>(
296 funcCallOp, funcCallOp.getCallee(), funcCallOp.getResults().getTypes(),
297 funcCallOp.getOperands());
298 return success();
299 }
300};
301
302struct WasmConstOpConversion : OpConversionPattern<ConstOp> {
303 using OpConversionPattern::OpConversionPattern;
304
305 LogicalResult
306 matchAndRewrite(ConstOp constOp, ConstOp::Adaptor adaptor,
307 ConversionPatternRewriter &rewriter) const override {
308 rewriter.replaceOpWithNewOp<arith::ConstantOp>(constOp, constOp.getValue());
309 return success();
310 }
311};
312
313struct WasmEqzOpConversion : OpConversionPattern<EqzOp> {
314 using OpConversionPattern::OpConversionPattern;
315
316 LogicalResult
317 matchAndRewrite(EqzOp eqzOp, EqzOp::Adaptor adaptor,
318 ConversionPatternRewriter &rewriter) const override {
319 auto loc = eqzOp->getLoc();
320 auto zero = arith::ConstantOp::create(
321 rewriter, loc,
322 rewriter.getIntegerAttr(adaptor.getInput().getType(), 0))
323 .getResult();
324 auto cmpRes = arith::CmpIOp::create(
325 rewriter, loc, rewriter.getI1Type(),
326 arith::CmpIPredicateAttr::get(rewriter.getContext(),
327 arith::CmpIPredicate::eq),
328 adaptor.getInput(), zero)
329 .getResult();
330 rewriter.replaceOpWithNewOp<arith::ExtUIOp>(eqzOp, rewriter.getI32Type(),
331 cmpRes);
332
333 return success();
334 }
335};
336
337struct WasmExtendLowBitsOpConversion : OpConversionPattern<ExtendLowBitsSOp> {
338 using OpConversionPattern::OpConversionPattern;
339
340 LogicalResult
341 matchAndRewrite(ExtendLowBitsSOp extendLowBytesSOp,
342 ExtendLowBitsSOp::Adaptor adaptor,
343 ConversionPatternRewriter &rewriter) const override {
344 auto truncWidth = extendLowBytesSOp.getBitsToTake().getInt();
345 auto truncation = arith::TruncIOp::create(
346 rewriter, extendLowBytesSOp->getLoc(),
347 rewriter.getIntegerType(truncWidth), adaptor.getInput());
348 rewriter.replaceOpWithNewOp<arith::ExtSIOp>(
349 extendLowBytesSOp, extendLowBytesSOp.getResult().getType(),
350 truncation.getResult());
351 return success();
352 }
353};
354
355struct WasmFuncImportOpConversion : OpConversionPattern<FuncImportOp> {
356 using OpConversionPattern::OpConversionPattern;
357
358 LogicalResult
359 matchAndRewrite(FuncImportOp funcImportOp, FuncImportOp::Adaptor,
360 ConversionPatternRewriter &rewriter) const override {
361 auto nFunc = rewriter.replaceOpWithNewOp<func::FuncOp>(
362 funcImportOp, funcImportOp.getSymName(), funcImportOp.getType());
363 nFunc.setVisibility(SymbolTable::Visibility::Private);
364 return success();
365 }
366};
367
368struct WasmFuncOpConversion : OpConversionPattern<FuncOp> {
369 using OpConversionPattern::OpConversionPattern;
370 ///
371 /// Control flow conversion needs shared state for tracking which block
372 /// corresponds to which operation at which level.
373 ///
374 /// This class handles such tracking and performs the conversion of control
375 /// flow related ops contained in a function.
376 class CFRewriterVisitor {
377 private:
378 using branch_to_dest_t = llvm::DenseMap<LabelBranchingOpInterface, Block *>;
379 Value getCompResultAsI1(Value compResult,
380 ConversionPatternRewriter &rewriter) {
381 auto testValue = arith::ConstantOp::create(rewriter, compResult.getLoc(),
382 rewriter.getI32IntegerAttr(0));
383 auto flag = arith::CmpIOp::create(
384 rewriter, compResult.getLoc(), rewriter.getIntegerType(1),
385 arith::CmpIPredicate::ne, compResult, testValue)
386 .getResult();
387 return flag;
388 }
389
390 void replaceNestLevelWithBranch(BlockOp blockOp,
391 llvm::ArrayRef<Block *> regionsToEntry,
392 ConversionPatternRewriter &rewriter) {
393 rewriter.replaceOpWithNewOp<cf::BranchOp>(blockOp, regionsToEntry[0],
394 blockOp->getOperands());
395 }
396
397 void replaceNestLevelWithBranch(LoopOp loopOp,
398 llvm::ArrayRef<Block *> regionsToEntry,
399 ConversionPatternRewriter &rewriter) {
400 rewriter.replaceOpWithNewOp<cf::BranchOp>(loopOp, regionsToEntry[0],
401 loopOp->getOperands());
402 }
403
404 void replaceNestLevelWithBranch(IfOp ifOp,
405 llvm::ArrayRef<Block *> regionsToEntry,
406 ConversionPatternRewriter &rewriter) {
407 Block *falseDest =
408 regionsToEntry.size() == 2 ? regionsToEntry[1] : ifOp.getTarget();
409 auto flag = getCompResultAsI1(ifOp.getCondition(), rewriter);
410 rewriter.replaceOpWithNewOp<cf::CondBranchOp>(
411 ifOp, flag, regionsToEntry[0], ifOp.getInputs(), falseDest,
412 ifOp.getInputs());
413 }
414
415 template <typename LevelType>
416 LogicalResult
417 replaceNestLevelWithBranchWrapper(LabelLevelOpInterface nestingOp,
418 llvm::ArrayRef<Block *> regionsToEntry,
419 ConversionPatternRewriter &rewriter) {
420 auto cast = dyn_cast<LevelType>(nestingOp.getOperation());
421 if (!cast)
422 return failure();
423 replaceNestLevelWithBranch(cast, regionsToEntry, rewriter);
424 return success();
425 }
426
427 template <typename... LevelTypes>
428 LogicalResult inlineNestDispatcher(LabelLevelOpInterface nestingOp,
429 ConversionPatternRewriter &rewriter) {
430 auto sip = rewriter.saveInsertionPoint();
431 Block *blockSuccessor = nestingOp->getSuccessor(0);
432 llvm::SmallVector<Block *, 2> regionEntries;
433 LLVM_DEBUG(llvm::dbgs()
434 << "Starting inlining blocks for " << nestingOp << "\n";);
435 for (auto &region : nestingOp->getRegions()) {
436 if (region.empty())
437 continue;
438 regionEntries.push_back(&region.front());
439 /// Inline blocks of nested ops
440 llvm::SmallVector<LabelLevelOpInterface> nestedOps{
441 region.getOps<LabelLevelOpInterface>()};
442 for (auto nestedOp : nestedOps) {
443 LLVM_DEBUG(llvm::dbgs() << " Found nested op: " << nestedOp);
444 if (failed(inlineBlocks(nestedOp, rewriter)))
445 return failure();
446 }
447 rewriter.inlineRegionBefore(region, blockSuccessor);
448 }
449 LLVM_DEBUG(llvm::dbgs() << "End of region inlining\n");
450 LLVM_DEBUG(llvm::dbgs() << "Replacing initial op with branching\n");
451 rewriter.setInsertionPoint(nestingOp);
452 auto res = success(
453 (... || succeeded(replaceNestLevelWithBranchWrapper<LevelTypes>(
454 nestingOp, regionEntries, rewriter))));
455 rewriter.restoreInsertionPoint(sip);
456 if (failed(res))
457 return emitError(nestingOp->getLoc(),
458 "Unable to inline the operation regions.");
459 return success();
460 }
461
462 /// Take a nesting level defining op and inline it in the parent region.
463 LogicalResult inlineBlocks(LabelLevelOpInterface nestingOp,
464 ConversionPatternRewriter &rewriter) {
465 return inlineNestDispatcher<BlockOp, IfOp, LoopOp>(nestingOp, rewriter);
466 }
467
468 llvm::FailureOr<Block *> getBlockFor(LabelBranchingOpInterface branchOp) {
469 auto destIter = branchToDest.find(branchOp);
470 if (destIter == branchToDest.end())
471 return branchOp->emitError("No indexed label op for this operation: ")
472 << branchOp;
473 return destIter->second;
474 }
475
476 inline void convertBranch(BranchIfOp brOp, Block *dest,
477 ConversionPatternRewriter &rewriter) {
478 auto flag = getCompResultAsI1(brOp.getCondition(), rewriter);
479 rewriter.replaceOpWithNewOp<cf::CondBranchOp>(
480 brOp, flag, dest, brOp.getInputs(), brOp.getElseSuccessor(),
481 ValueRange{});
482 }
483
484 inline void convertBranch(BlockReturnOp brOp, Block *dest,
485 ConversionPatternRewriter &rewriter) {
486 rewriter.replaceOpWithNewOp<cf::BranchOp>(brOp, dest, brOp.getInputs());
487 }
488
489 template <typename LevelInterfaceT>
490 inline LogicalResult
491 convertBranchWrapper(LabelBranchingOpInterface branchOp, Block *dest,
492 ConversionPatternRewriter &rewriter) {
493 auto cast = dyn_cast<LevelInterfaceT>(branchOp.getOperation());
494 if (!cast)
495 return failure();
496 auto sip = rewriter.saveInsertionPoint();
497 rewriter.setInsertionPoint(branchOp);
498 convertBranch(cast, dest, rewriter);
499 rewriter.restoreInsertionPoint(sip);
500 return success();
501 }
502
503 template <typename... BranchInterfaceT>
504 LogicalResult convertBranchDispatch(LabelBranchingOpInterface branchOp,
505 ConversionPatternRewriter &rewriter) {
506 auto dest = getBlockFor(branchOp);
507 if (failed(dest))
508 return failure();
509 auto res =
510 success((... || succeeded(convertBranchWrapper<BranchInterfaceT>(
511 branchOp, *dest, rewriter))));
512 if (failed(res))
513 return emitError(branchOp->getLoc(), "No known converter for op ")
514 << branchOp;
515 return res;
516 }
517
518 LogicalResult convertBranch(LabelBranchingOpInterface branchOp,
519 ConversionPatternRewriter &rewriter) {
520 return convertBranchDispatch<BlockReturnOp, BranchIfOp>(branchOp,
521 rewriter);
522 }
523
524 func::FuncOp func;
525 branch_to_dest_t branchToDest;
526
527 public:
528 CFRewriterVisitor(func::FuncOp func) : func{func} {
529 func.walk([this](LabelBranchingOpInterface branchOp) {
530 branchToDest.insert({branchOp, branchOp.getTarget()});
531 });
532 }
533 LogicalResult rewrite(ConversionPatternRewriter &rewriter) {
534 llvm::SmallVector<LabelLevelOpInterface> nestingOps{
535 func.getOps<LabelLevelOpInterface>()};
536 for (auto nestingOp : nestingOps)
537 if (failed(inlineBlocks(nestingOp, rewriter)))
538 return failure();
539
540 auto res =
541 func->walk([this, &rewriter](LabelBranchingOpInterface branchOp) {
542 if (failed(convertBranch(branchOp, rewriter)))
543 return WalkResult::interrupt();
544 return WalkResult::advance();
545 });
546 return failure(res.wasInterrupted());
547 }
548 };
549
550 LogicalResult
551 matchAndRewrite(FuncOp funcOp, FuncOp::Adaptor adaptor,
552 ConversionPatternRewriter &rewriter) const override {
553 auto newFunc =
554 func::FuncOp::create(rewriter, funcOp->getLoc(), funcOp.getSymName(),
555 funcOp.getFunctionType());
556 rewriter.cloneRegionBefore(funcOp.getBody(), newFunc.getBody(),
557 newFunc.getBody().end());
558 Block *oldEntryBlock = &newFunc.getBody().front();
559 auto blockArgTypes = oldEntryBlock->getArgumentTypes();
560 TypeConverter::SignatureConversion sC{oldEntryBlock->getNumArguments()};
561 auto numArgs = blockArgTypes.size();
562 for (size_t i = 0; i < numArgs; ++i) {
563 auto argType = dyn_cast<LocalRefType>(blockArgTypes[i]);
564 if (!argType)
565 return failure();
566 sC.addInputs(i, argType.getElementType());
567 }
568
569 rewriter.applySignatureConversion(oldEntryBlock, sC, getTypeConverter());
570 rewriter.replaceOp(funcOp, newFunc);
571 CFRewriterVisitor cfRewriter{newFunc};
572 return cfRewriter.rewrite(rewriter);
573 }
574};
575
576struct WasmGlobalImportOpConverter : OpConversionPattern<GlobalImportOp> {
577 using OpConversionPattern::OpConversionPattern;
578 LogicalResult
579 matchAndRewrite(GlobalImportOp gIOp, GlobalImportOp::Adaptor adaptor,
580 ConversionPatternRewriter &rewriter) const override {
581 auto memrefGOp = rewriter.replaceOpWithNewOp<memref::GlobalOp>(
582 gIOp, gIOp.getSymNameAttr(), rewriter.getStringAttr("nested"),
583 TypeAttr::get(MemRefType::get({1}, gIOp.getType())), Attribute{},
584 /*constant*/ UnitAttr{},
585 /*alignment*/ IntegerAttr{});
586 memrefGOp.setConstant(!gIOp.getIsMutable());
587 return success();
588 }
589};
590
591template <typename CRTP, typename OriginOpType>
592struct GlobalOpConverter : OpConversionPattern<GlobalOp> {
593 using OpConversionPattern::OpConversionPattern;
594 LogicalResult
595 matchAndRewrite(GlobalOp globalOp, GlobalOp::Adaptor adaptor,
596 ConversionPatternRewriter &rewriter) const override {
597 ReturnOp rop = globalOp.getInitTerminator();
598
599 if (rop->getNumOperands() != 1)
600 return rewriter.notifyMatchFailure(
601 globalOp, "globalOp initializer should return one value exactly");
602
603 auto initializerOp =
604 dyn_cast<OriginOpType>(rop->getOperand(0).getDefiningOp());
605
606 if (!initializerOp)
607 return rewriter.notifyMatchFailure(
608 globalOp, "invalid initializer op type for this pattern");
609
610 return static_cast<CRTP const *>(this)->handleInitializer(
611 globalOp, rewriter, initializerOp);
612 }
613};
614
615struct WasmGlobalWithConstInitConversion
616 : GlobalOpConverter<WasmGlobalWithConstInitConversion, ConstOp> {
617 using GlobalOpConverter::GlobalOpConverter;
618 LogicalResult handleInitializer(GlobalOp globalOp,
619 ConversionPatternRewriter &rewriter,
620 ConstOp constInit) const {
621 auto initializer =
622 DenseElementsAttr::get(RankedTensorType::get({1}, globalOp.getType()),
623 ArrayRef<Attribute>{constInit.getValueAttr()});
624 auto globalReplacement = rewriter.replaceOpWithNewOp<memref::GlobalOp>(
625 globalOp, globalOp.getSymNameAttr(), rewriter.getStringAttr("private"),
626 TypeAttr::get(MemRefType::get({1}, globalOp.getType())), initializer,
627 /*constant*/ UnitAttr{},
628 /*alignment*/ IntegerAttr{});
629 globalReplacement.setConstant(!globalOp.getIsMutable());
630 return success();
631 }
632};
633
634struct WasmGlobalWithGetGlobalInitConversion
635 : GlobalOpConverter<WasmGlobalWithGetGlobalInitConversion, GlobalGetOp> {
636 using GlobalOpConverter::GlobalOpConverter;
637 LogicalResult handleInitializer(GlobalOp globalOp,
638 ConversionPatternRewriter &rewriter,
639 GlobalGetOp constInit) const {
640 auto globalReplacement = rewriter.replaceOpWithNewOp<memref::GlobalOp>(
641 globalOp, globalOp.getSymNameAttr(), rewriter.getStringAttr("private"),
642 TypeAttr::get(MemRefType::get({1}, globalOp.getType())),
643 rewriter.getUnitAttr(),
644 /*constant*/ UnitAttr{},
645 /*alignment*/ IntegerAttr{});
646 globalReplacement.setConstant(!globalOp.getIsMutable());
647 auto loc = globalOp.getLoc();
648 auto initializerName = (globalOp.getSymName() + "::initializer").str();
649 auto globalInitializer =
650 func::FuncOp::create(rewriter, loc, initializerName,
651 FunctionType::get(getContext(), {}, {}));
652 globalInitializer->setAttr(rewriter.getStringAttr("initializer"),
653 rewriter.getUnitAttr());
654 auto *initializerBody = globalInitializer.addEntryBlock();
655 auto sip = rewriter.saveInsertionPoint();
656 rewriter.setInsertionPointToStart(initializerBody);
657 auto srcGlobalPtr = memref::GetGlobalOp::create(
658 rewriter, loc, MemRefType::get({1}, constInit.getType()),
659 constInit.getGlobal());
660 auto destGlobalPtr =
661 memref::GetGlobalOp::create(rewriter, loc, globalReplacement.getType(),
662 globalReplacement.getSymName());
663 auto idx = arith::ConstantIndexOp::create(rewriter, loc, 0).getResult();
664 auto loadSrc =
665 memref::LoadOp::create(rewriter, loc, srcGlobalPtr, ValueRange{idx});
666 memref::StoreOp::create(rewriter, loc, loadSrc.getResult(),
667 destGlobalPtr.getResult(), ValueRange{idx});
668 func::ReturnOp::create(rewriter, loc);
669 rewriter.restoreInsertionPoint(sip);
670 return success();
671 }
672};
673
674struct WasmGlobalSetOpConversion : OpConversionPattern<GlobalSetOp> {
675 using OpConversionPattern::OpConversionPattern;
676 LogicalResult
677 matchAndRewrite(GlobalSetOp globalSetOp, GlobalSetOp::Adaptor adaptor,
678 ConversionPatternRewriter &rewriter) const override {
679 auto loc = globalSetOp.getLoc();
680 auto globalPtr = memref::GetGlobalOp::create(
681 rewriter, loc, MemRefType::get({1}, adaptor.getValue().getType()),
682 globalSetOp.getGlobal());
683 auto idx = arith::ConstantIndexOp::create(rewriter, loc, 0);
684 rewriter.replaceOpWithNewOp<memref::StoreOp>(
685 globalSetOp, adaptor.getValue(), globalPtr.getResult(),
686 ValueRange{idx.getResult()});
687 return success();
688 }
689};
690
691struct WasmMemoryOpConversion : OpConversionPattern<MemOp> {
692 using OpConversionPattern::OpConversionPattern;
693
694 LogicalResult
695 matchAndRewrite(MemOp memOp, MemOp::Adaptor adaptor,
696 ConversionPatternRewriter &rewriter) const override {
697 auto loc = memOp.getLoc();
698 auto bufferType =
699 MemRefType::get({ShapedType::kDynamic}, rewriter.getI8Type());
700 auto bufferPtrType = MemRefType::get({1}, bufferType);
701 auto memVisibility = memOp.getVisibility();
702 // Convert to StringAttr since memref::GlobalOp expects visibility as a
703 // string attribute.
704 mlir::StringAttr visAttr;
705 if (memVisibility == mlir::SymbolTable::Visibility::Public)
706 visAttr = mlir::StringAttr::get(memOp->getContext(), "public");
707 else if (memVisibility == mlir::SymbolTable::Visibility::Private)
708 visAttr = mlir::StringAttr::get(memOp->getContext(), "private");
709 else
710 visAttr = mlir::StringAttr::get(memOp->getContext(), "nested");
711
712 auto memPtr = rewriter.replaceOpWithNewOp<memref::GlobalOp>(
713 memOp, memOp.getSymNameAttr(), visAttr, TypeAttr::get(bufferPtrType),
714 /*initialValue*/ rewriter.getUnitAttr(),
715 /*constant*/ UnitAttr{}, /*alignment*/ IntegerAttr{});
716 auto initializerName = (memPtr.getSymName() + "::initializer").str();
717 auto memInitializer =
718 func::FuncOp::create(rewriter, loc, initializerName,
719 FunctionType::get(getContext(), {}, {}));
720 memInitializer->setAttr(rewriter.getStringAttr("initializer"),
721 rewriter.getUnitAttr());
722 auto *initializerBody = memInitializer.addEntryBlock();
723 auto sip = rewriter.saveInsertionPoint();
724 rewriter.setInsertionPointToStart(initializerBody);
725 auto memRefPtr = memref::GetGlobalOp::create(
726 rewriter, loc, MemRefType::get({1}, bufferType), memPtr.getSymName());
727 auto alloc = memref::AllocOp::create(
728 rewriter, loc,
729 MemRefType::get({memOp.getLimits().getMin()}, rewriter.getI8Type()));
730 auto castOp =
731 memref::CastOp::create(rewriter, loc, bufferType, alloc.getResult());
732 auto idx = arith::ConstantIndexOp::create(rewriter, loc, 0);
733 memref::StoreOp::create(rewriter, loc, castOp.getResult(),
734 memRefPtr.getResult(), ValueRange{idx.getResult()});
735 func::ReturnOp::create(rewriter, loc);
736 rewriter.restoreInsertionPoint(sip);
737 func::CallOp::create(rewriter, loc, memInitializer);
738 return success();
739 }
740};
741
742inline TypedAttr getInitializerAttr(Type t) {
743 assert(t.isIntOrFloat() &&
744 "This helper is intended to use with int and float types");
745 if (t.isInteger())
746 return IntegerAttr::get(t, 0);
747 if (t.isFloat())
748 return FloatAttr::get(t, 0.);
749 return TypedAttr{};
750}
751
752struct WasmLocalConversion : OpConversionPattern<LocalOp> {
753 using OpConversionPattern::OpConversionPattern;
754 LogicalResult
755 matchAndRewrite(LocalOp localOp, LocalOp::Adaptor adaptor,
756 ConversionPatternRewriter &rewriter) const override {
757 auto alloca = rewriter.replaceOpWithNewOp<memref::AllocaOp>(
758 localOp,
759 MemRefType::get({}, localOp.getResult().getType().getElementType()));
760 auto initializer = arith::ConstantOp::create(
761 rewriter, localOp->getLoc(),
762 getInitializerAttr(localOp.getResult().getType().getElementType()));
763 memref::StoreOp::create(rewriter, localOp->getLoc(),
764 initializer.getResult(), alloca.getResult());
765 return success();
766 }
767};
768
769struct WasmLocalGetConversion : OpConversionPattern<LocalGetOp> {
770 using OpConversionPattern::OpConversionPattern;
771 LogicalResult
772 matchAndRewrite(LocalGetOp localGetOp, LocalGetOp::Adaptor adaptor,
773 ConversionPatternRewriter &rewriter) const override {
774 rewriter.replaceOpWithNewOp<memref::LoadOp>(
775 localGetOp, localGetOp.getResult().getType(), adaptor.getLocalVar(),
776 ValueRange{});
777 return success();
778 }
779};
780
781struct WasmLocalSetConversion : OpConversionPattern<LocalSetOp> {
782 using OpConversionPattern::OpConversionPattern;
783 LogicalResult
784 matchAndRewrite(LocalSetOp localSetOp, LocalSetOp::Adaptor adaptor,
785 ConversionPatternRewriter &rewriter) const override {
786 rewriter.replaceOpWithNewOp<memref::StoreOp>(
787 localSetOp, adaptor.getValue(), adaptor.getLocalVar(), ValueRange{});
788 return success();
789 }
790};
791
792struct WasmLocalTeeConversion : OpConversionPattern<LocalTeeOp> {
793 using OpConversionPattern::OpConversionPattern;
794 LogicalResult
795 matchAndRewrite(LocalTeeOp localTeeOp, LocalTeeOp::Adaptor adaptor,
796 ConversionPatternRewriter &rewriter) const override {
797 memref::StoreOp::create(rewriter, localTeeOp->getLoc(), adaptor.getValue(),
798 adaptor.getLocalVar());
799 rewriter.replaceOp(localTeeOp, adaptor.getValue());
800 return success();
801 }
802};
803
804struct WasmReturnOpConversion : OpConversionPattern<ReturnOp> {
805 using OpConversionPattern::OpConversionPattern;
806
807 LogicalResult
808 matchAndRewrite(ReturnOp returnOp, ReturnOp::Adaptor adaptor,
809 ConversionPatternRewriter &rewriter) const override {
810 rewriter.replaceOpWithNewOp<func::ReturnOp>(returnOp,
811 adaptor.getOperands());
812 return success();
813 }
814};
815
816struct WasmSelectOpConversion : OpConversionPattern<SelectOp> {
817 using OpConversionPattern::OpConversionPattern;
818
819 LogicalResult
820 matchAndRewrite(SelectOp selectOp, SelectOp::Adaptor adaptor,
821 ConversionPatternRewriter &rewriter) const override {
822 auto loc = selectOp.getLoc();
823 auto zero =
824 arith::ConstantOp::create(rewriter, loc, rewriter.getI32IntegerAttr(0));
825 auto flag = arith::CmpIOp::create(rewriter, loc, arith::CmpIPredicate::ne,
826 adaptor.getCondition(), zero.getResult());
827 rewriter.replaceOpWithNewOp<arith::SelectOp>(selectOp, flag.getResult(),
828 adaptor.getTrueValue(),
829 adaptor.getFalseValue());
830 return success();
831 }
832};
833
834struct RaiseWasmMLIRPass : public impl::RaiseWasmMLIRBase<RaiseWasmMLIRPass> {
835 void runOnOperation() override {
836 ConversionTarget target{getContext()};
837 target.addIllegalDialect<WasmSSADialect>();
838 target.addLegalDialect<arith::ArithDialect, BuiltinDialect,
839 cf::ControlFlowDialect, func::FuncDialect,
840 memref::MemRefDialect, math::MathDialect>();
841 RewritePatternSet patterns(&getContext());
842 TypeConverter tc{};
843 tc.addConversion([](Type type) -> std::optional<Type> { return type; });
844 tc.addConversion([](LocalRefType type) -> std::optional<Type> {
845 return MemRefType::get({}, type.getElementType());
846 });
847 tc.addTargetMaterialization([](OpBuilder &builder, MemRefType destType,
848 ValueRange values, Location loc) -> Value {
849 if (values.size() != 1 ||
850 values.front().getType() != destType.getElementType())
851 return {};
852 auto localVar = memref::AllocaOp::create(builder, loc, destType);
853 memref::StoreOp::create(builder, loc, values.front(),
854 localVar.getResult());
855 return localVar.getResult();
856 });
858
859 llvm::DenseMap<StringAttr, StringAttr> idxSymToImportSym{};
860 auto *topOp = getOperation();
861 topOp->walk([&idxSymToImportSym, this](ImportOpInterface importOp) {
862 auto const qualifiedImportName = importOp.getQualifiedImportName();
863 auto qualNameAttr = StringAttr::get(&getContext(), qualifiedImportName);
864 idxSymToImportSym.insert(
865 std::make_pair(importOp.getSymbolName(), qualNameAttr));
866 });
867
868 if (failed(applyFullConversion(topOp, target, std::move(patterns))))
869 return signalPassFailure();
870
871 auto symTable = SymbolTable{topOp};
872 for (auto &[oldName, newName] : idxSymToImportSym) {
873 if (failed(symTable.rename(oldName, newName)))
874 return signalPassFailure();
875 }
876 }
877};
878} // namespace
879
881 TypeConverter &tc, RewritePatternSet &patternSet) {
882 auto *ctx = patternSet.getContext();
883 // Disable clang-format in patternSet for readability + small diffs.
884 // clang-format off
885 patternSet
886 .add<
887 WasmAbsOpConversion,
888 WasmAddOpConversion,
889 WasmAndOpConversion,
890 WasmCallOpConversion,
891 WasmCeilOpConversion,
892 WasmClzOpConversion,
893 WasmConstOpConversion,
894 WasmConvertSOpConversion,
895 WasmConvertUOpConversion,
896 WasmCopySignOpConversion,
897 WasmCtzOpConversion,
898 WasmDemoteOpConversion,
899 WasmDivFPOpConversion,
900 WasmDivSIOpConversion,
901 WasmDivUIOpConversion,
902 WasmEqOpConversion,
903 WasmEqzOpConversion,
904 WasmExtendLowBitsOpConversion,
905 WasmExtendSOpConversion,
906 WasmExtendUOpConversion,
907 WasmFloorOpConversion,
908 WasmFuncImportOpConversion,
909 WasmFuncOpConversion,
910 WasmGeOpConversion,
911 WasmGeSIOpConversion,
912 WasmGeUIOpConversion,
913 WasmGlobalImportOpConverter,
914 WasmGlobalSetOpConversion,
915 WasmGlobalWithConstInitConversion,
916 WasmGlobalWithGetGlobalInitConversion,
917 WasmGtOpConversion,
918 WasmGtSIOpConversion,
919 WasmGtUIOpConversion,
920 WasmLeOpConversion,
921 WasmLeSIOpConversion,
922 WasmLeUIOpConversion,
923 WasmLocalConversion,
924 WasmLocalGetConversion,
925 WasmLocalSetConversion,
926 WasmLocalTeeConversion,
927 WasmLtOpConversion,
928 WasmLtSIOpConversion,
929 WasmLtUIOpConversion,
930 WasmMaxOpConversion,
931 WasmMemoryOpConversion,
932 WasmMinOpConversion,
933 WasmMulOpConversion,
934 WasmNeOpConversion,
935 WasmNegOpConversion,
936 WasmOrOpConversion,
937 WasmPopCntOpConversion,
938 WasmPromoteOpConversion,
939 WasmReinterpretOpConversion,
940 WasmRemSIOpConversion,
941 WasmRemUIOpConversion,
942 WasmReturnOpConversion,
943 WasmRotlOpConversion,
944 WasmRotrOpConversion,
945 WasmSelectOpConversion,
946 WasmShLOpConversion,
947 WasmShRSOpConversion,
948 WasmShRUOpConversion,
949 WasmSqrtOpConversion,
950 WasmSubOpConversion,
951 WasmTruncOpConversion,
952 WasmWrapOpConversion,
953 WasmXOrOpConversion
954 >(tc, ctx);
955 // clang-format on
956}
957
958std::unique_ptr<Pass> createRaiseWasmMLIRPass() {
959 return std::make_unique<RaiseWasmMLIRPass>();
960}
return success()
static Type getElementType(Type type)
Determine the element type of type.
b getContext())
std::unique_ptr< Pass > createRaiseWasmMLIRPass()
static void rewrite(DataFlowSolver &solver, MLIRContext *context, MutableArrayRef< Region > initialRegions)
Rewrite the given regions using the computing analysis.
Definition SCCP.cpp:67
ValueTypeRange< BlockArgListType > getArgumentTypes()
Return a range containing the types of the arguments for this block.
Definition Block.cpp:154
unsigned getNumArguments()
Definition Block.h:152
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
MLIRContext * getContext() const
RewritePatternSet & add(ConstructorArg &&arg, ConstructorArgs &&...args)
Add an instance of each of the pattern types 'Ts' to the pattern list with the given arguments.
@ Public
The symbol is public and may be referenced anywhere internal or external to the visible references in...
Definition SymbolTable.h:93
@ Private
The symbol is private and may only be referenced by SymbolRefAttrs local to the operations within the...
Definition SymbolTable.h:97
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isFloat() const
Return true if this is an float type (with the specified width).
Definition Types.cpp:47
bool isInteger() const
Return true if this is an integer type (with the specified width).
Definition Types.cpp:58
bool isIntOrFloat() const
Return true if this is an integer (of any signedness) or a float type.
Definition Types.cpp:118
unsigned getIntOrFloatBitWidth() const
Return the bit width of an integer or a float type, assert failure on other types.
Definition Types.cpp:124
type_range getType() const
Location getLoc() const
Return the location of this value.
Definition Value.cpp:24
static WalkResult advance()
Definition WalkResult.h:47
static WalkResult interrupt()
Definition WalkResult.h:46
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:397
detail::InFlightRemark failed(Location loc, RemarkOpts opts)
Report an optimization remark that failed.
Definition Remarks.h:717
Include the generated interface declarations.
void populateRaiseWasmMLIRConversionPatterns(TypeConverter &, RewritePatternSet &)
Collect a set of patterns to convert from the Wasm dialect to standard dialects.
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:307
InFlightDiagnostic emitError(Location loc)
Utility method to emit an error message using this location.