mirror of https://github.com/llvm/circt.git
[MooreToCore] Support ConditionalOp (#7501)
Add a lowering pattern from `moore.conditional` to `scf.if`. This currently relies on the condition being a two-valued integer after lowering. Once we support four-valued integers at the core dialect level, the lowering of `moore.conditional` will become a lot more complicated.
This commit is contained in:
parent
43608bea24
commit
42401036d8
|
@ -516,7 +516,8 @@ def ConvertMooreToCore : Pass<"convert-moore-to-core", "mlir::ModuleOp"> {
|
|||
}];
|
||||
let constructor = "circt::createConvertMooreToCorePass()";
|
||||
let dependentDialects = ["comb::CombDialect", "hw::HWDialect",
|
||||
"llhd::LLHDDialect", "mlir::cf::ControlFlowDialect"];
|
||||
"llhd::LLHDDialect", "mlir::cf::ControlFlowDialect",
|
||||
"mlir::scf::SCFDialect"];
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -1224,24 +1224,21 @@ def UnionExtractRefOp : MooreOp<"union_extract_ref"> {
|
|||
def ConditionalOp : MooreOp<"conditional",[
|
||||
RecursiveMemoryEffects,
|
||||
NoRegionArguments,
|
||||
SingleBlockImplicitTerminator<"moore::YieldOp">
|
||||
]> {
|
||||
let summary = "Conditional operation";
|
||||
let description = [{
|
||||
If cond_predicate is true, the operator returns the value of the first
|
||||
expression without evaluating the second expression; if false, it returns
|
||||
the value of the second expression without evaluating the first expression.
|
||||
If cond_predicate evaluates to an ambiguous value (x or z), then both the
|
||||
first expression and the second expression shall be evaluated, and compared
|
||||
for logical equivalence. If that comparison is true (1), the operator shall
|
||||
return either the first or second expression. Otherwise the operator returns
|
||||
a result based on the data types of the expressions.
|
||||
If the condition is true, this op evaluates the first region and returns its
|
||||
result without evaluating the second region. If the the condition is false,
|
||||
this op evaluates the second region and returns its result without
|
||||
evaluating the first region.
|
||||
|
||||
When both the first and second expressions are of integral types, if the
|
||||
cond_predicate evaluates to an ambiguous value and the expressions are not
|
||||
logically equivalent, their results shall be combined bit by bit using the
|
||||
table below to calculate the final result. The first and second expressions
|
||||
are extended to the same width.
|
||||
If the condition is unknown (X or Z), _both_ regions are evaluated. If both
|
||||
results are equal as per `case_eq`, one of the results is returned. If the
|
||||
results are not equal, this op returns a value based on the data types of
|
||||
the results.
|
||||
|
||||
In case the results of the first and second region are of an integral type,
|
||||
they are merged by applying the following bit-wise truth table:
|
||||
|
||||
|?: | 0 | 1 | X | Z |
|
||||
|---|---|---|---|---|
|
||||
|
@ -1250,6 +1247,7 @@ def ConditionalOp : MooreOp<"conditional",[
|
|||
| X | X | X | X | X |
|
||||
| Z | X | X | X | X |
|
||||
|
||||
Non-integral data types define other rules which are not yet implemented.
|
||||
See IEEE 1800-2017 § 11.4.11 "Conditional operator".
|
||||
}];
|
||||
let arguments = (ins AnySingleBitType:$condition);
|
||||
|
@ -1280,7 +1278,6 @@ def YieldOp : MooreOp<"yield", [
|
|||
yielded.
|
||||
}];
|
||||
let arguments = (ins UnpackedType:$result);
|
||||
let builders = [OpBuilder<(ins), [{ /* nothing to do */ }]>];
|
||||
let assemblyFormat = [{
|
||||
attr-dict $result `:` type($result)
|
||||
}];
|
||||
|
|
|
@ -677,13 +677,13 @@ struct RvalueExprVisitor {
|
|||
auto conditionalOp = builder.create<moore::ConditionalOp>(loc, type, value);
|
||||
|
||||
// Create blocks for true region and false region.
|
||||
conditionalOp.getTrueRegion().emplaceBlock();
|
||||
conditionalOp.getFalseRegion().emplaceBlock();
|
||||
auto &trueBlock = conditionalOp.getTrueRegion().emplaceBlock();
|
||||
auto &falseBlock = conditionalOp.getFalseRegion().emplaceBlock();
|
||||
|
||||
OpBuilder::InsertionGuard g(builder);
|
||||
|
||||
// Handle left expression.
|
||||
builder.setInsertionPointToStart(conditionalOp.getBody(0));
|
||||
builder.setInsertionPointToStart(&trueBlock);
|
||||
auto trueValue = context.convertRvalueExpression(expr.left());
|
||||
if (!trueValue)
|
||||
return {};
|
||||
|
@ -692,7 +692,7 @@ struct RvalueExprVisitor {
|
|||
builder.create<moore::YieldOp>(loc, trueValue);
|
||||
|
||||
// Handle right expression.
|
||||
builder.setInsertionPointToStart(conditionalOp.getBody(1));
|
||||
builder.setInsertionPointToStart(&falseBlock);
|
||||
auto falseValue = context.convertRvalueExpression(expr.right());
|
||||
if (!falseValue)
|
||||
return {};
|
||||
|
|
|
@ -8,12 +8,13 @@ add_circt_conversion_library(CIRCTMooreToCore
|
|||
Core
|
||||
|
||||
LINK_LIBS PUBLIC
|
||||
CIRCTMoore
|
||||
CIRCTLLHD
|
||||
CIRCTHW
|
||||
CIRCTComb
|
||||
CIRCTHW
|
||||
CIRCTLLHD
|
||||
CIRCTMoore
|
||||
MLIRControlFlowDialect
|
||||
MLIRFuncDialect
|
||||
MLIRTransforms
|
||||
MLIRSCFDialect
|
||||
MLIRSideEffectInterfaces
|
||||
MLIRTransforms
|
||||
)
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "circt/Dialect/Moore/MooreOps.h"
|
||||
#include "mlir/Dialect/ControlFlow/IR/ControlFlowOps.h"
|
||||
#include "mlir/Dialect/Func/IR/FuncOps.h"
|
||||
#include "mlir/Dialect/SCF/IR/SCF.h"
|
||||
#include "mlir/IR/BuiltinDialect.h"
|
||||
#include "mlir/Interfaces/SideEffectInterfaces.h"
|
||||
#include "mlir/Pass/Pass.h"
|
||||
|
@ -960,6 +961,39 @@ struct AssignOpConversion : public OpConversionPattern<OpTy> {
|
|||
}
|
||||
};
|
||||
|
||||
struct ConditionalOpConversion : public OpConversionPattern<ConditionalOp> {
|
||||
using OpConversionPattern::OpConversionPattern;
|
||||
|
||||
LogicalResult
|
||||
matchAndRewrite(ConditionalOp op, OpAdaptor adaptor,
|
||||
ConversionPatternRewriter &rewriter) const override {
|
||||
// TODO: This lowering is only correct if the condition is two-valued. If
|
||||
// the condition is X or Z, both branches of the conditional must be
|
||||
// evaluated and merged with the appropriate lookup table. See documentation
|
||||
// for `ConditionalOp`.
|
||||
auto type = typeConverter->convertType(op.getType());
|
||||
auto ifOp =
|
||||
rewriter.create<scf::IfOp>(op.getLoc(), type, adaptor.getCondition());
|
||||
rewriter.inlineRegionBefore(op.getTrueRegion(), ifOp.getThenRegion(),
|
||||
ifOp.getThenRegion().end());
|
||||
rewriter.inlineRegionBefore(op.getFalseRegion(), ifOp.getElseRegion(),
|
||||
ifOp.getElseRegion().end());
|
||||
rewriter.replaceOp(op, ifOp);
|
||||
return success();
|
||||
}
|
||||
};
|
||||
|
||||
struct YieldOpConversion : public OpConversionPattern<YieldOp> {
|
||||
using OpConversionPattern::OpConversionPattern;
|
||||
|
||||
LogicalResult
|
||||
matchAndRewrite(YieldOp op, OpAdaptor adaptor,
|
||||
ConversionPatternRewriter &rewriter) const override {
|
||||
rewriter.replaceOpWithNewOp<scf::YieldOp>(op, adaptor.getResult());
|
||||
return success();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -992,6 +1026,8 @@ static void populateLegality(ConversionTarget &target) {
|
|||
|
||||
addGenericLegality<cf::CondBranchOp>(target);
|
||||
addGenericLegality<cf::BranchOp>(target);
|
||||
addGenericLegality<scf::IfOp>(target);
|
||||
addGenericLegality<scf::YieldOp>(target);
|
||||
addGenericLegality<func::CallOp>(target);
|
||||
addGenericLegality<func::ReturnOp>(target);
|
||||
addGenericLegality<UnrealizedConversionCastOp>(target);
|
||||
|
@ -1081,7 +1117,8 @@ static void populateOpConversion(RewritePatternSet &patterns,
|
|||
ExtractOpConversion, DynExtractOpConversion, DynExtractRefOpConversion,
|
||||
ConversionOpConversion, ReadOpConversion, NamedConstantOpConv,
|
||||
StructExtractOpConversion, StructExtractRefOpConversion,
|
||||
ExtractRefOpConversion, StructCreateOpConversion,
|
||||
ExtractRefOpConversion, StructCreateOpConversion, ConditionalOpConversion,
|
||||
YieldOpConversion,
|
||||
|
||||
// Patterns of unary operations.
|
||||
ReduceAndOpConversion, ReduceOrOpConversion, ReduceXorOpConversion,
|
||||
|
|
|
@ -258,6 +258,38 @@ func.func @Expressions(%arg0: !moore.i1, %arg1: !moore.l1, %arg2: !moore.i6, %ar
|
|||
moore.wildcard_eq %arg0, %arg0 : !moore.i1 -> !moore.i1
|
||||
moore.wildcard_ne %arg0, %arg0 : !moore.i1 -> !moore.i1
|
||||
|
||||
// CHECK-NEXT: [[RES:%.+]] = scf.if %arg0 -> (i6) {
|
||||
// CHECK-NEXT: scf.yield %arg2 : i6
|
||||
// CHECK-NEXT: } else {
|
||||
// CHECK-NEXT: [[TMP:%.+]] = hw.constant 19 : i6
|
||||
// CHECK-NEXT: scf.yield [[TMP]] : i6
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: comb.parity [[RES]] : i6
|
||||
%k0 = moore.conditional %arg0 : i1 -> i6 {
|
||||
moore.yield %arg2 : i6
|
||||
} {
|
||||
%0 = moore.constant 19 : i6
|
||||
moore.yield %0 : i6
|
||||
}
|
||||
moore.reduce_xor %k0 : i6 -> i1
|
||||
|
||||
// CHECK-NEXT: [[RES:%.+]] = scf.if %arg1 -> (i6) {
|
||||
// CHECK-NEXT: [[TMP:%.+]] = hw.constant 0 : i6
|
||||
// CHECK-NEXT: scf.yield [[TMP]] : i6
|
||||
// CHECK-NEXT: } else {
|
||||
// CHECK-NEXT: [[TMP:%.+]] = hw.constant 19 : i6
|
||||
// CHECK-NEXT: scf.yield [[TMP]] : i6
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: comb.parity [[RES]] : i6
|
||||
%k1 = moore.conditional %arg1 : l1 -> l6 {
|
||||
%0 = moore.constant bXXXXXX : l6
|
||||
moore.yield %0 : l6
|
||||
} {
|
||||
%0 = moore.constant 19 : l6
|
||||
moore.yield %0 : l6
|
||||
}
|
||||
moore.reduce_xor %k1 : l6 -> l1
|
||||
|
||||
// CHECK-NEXT: return
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue