21#include "mlir/IR/AttrTypeSubElements.h"
22#include "mlir/Pass/Pass.h"
24#include "mlir/Transforms/DialectConversion.h"
25#include "llvm/ADT/TypeSwitch.h"
28#define GEN_PASS_DEF_HWARITHTOHW
29#include "circt/Conversion/Passes.h.inc"
46 llvm::function_ref<std::string(StringRef)> namehintCallback) {
47 if (
auto *sourceOp = oldValue.getDefiningOp()) {
49 sourceOp->getAttrOfType<mlir::StringAttr>(
"sv.namehint")) {
50 auto newNamehint = namehintCallback(namehint.strref());
51 newOp->setAttr(
"sv.namehint",
52 StringAttr::get(oldValue.getContext(), newNamehint));
58static Value
extractBits(OpBuilder &builder, Location loc, Value value,
59 unsigned startBit,
unsigned bitWidth) {
60 Value extractedValue =
62 Operation *definingOp = extractedValue.getDefiningOp();
63 if (extractedValue != value && definingOp) {
66 return (oldNamehint +
"_" + std::to_string(startBit) +
"_to_" +
67 std::to_string(startBit + bitWidth))
71 return extractedValue;
77 unsigned targetWidth,
bool signExtension) {
78 unsigned sourceWidth = value.getType().getIntOrFloatBitWidth();
79 unsigned extensionLength = targetWidth - sourceWidth;
81 if (extensionLength == 0)
91 builder.createOrFold<comb::ReplicateOp>(loc, highBit, extensionLength);
96 builder.getIntegerType(extensionLength), 0)
100 auto extOp = comb::ConcatOp::create(builder, loc, extensionBits, value);
102 return (oldNamehint +
"_" + (signExtension ?
"sext_" :
"zext_") +
103 std::to_string(targetWidth))
107 return extOp->getOpResult(0);
112 llvm::TypeSwitch<Type, bool>(type)
113 .Case<IntegerType>([](
auto type) {
return !type.isSignless(); })
114 .Case<hw::ArrayType>(
116 .Case<hw::UnpackedArrayType>(
118 .Case<hw::StructType>([](
auto type) {
119 return llvm::any_of(type.getElements(), [](
auto element) {
120 return isSignednessType(element.type);
123 .Case<hw::UnionType>([](
auto type) {
124 return llvm::any_of(type.getElements(), [](
auto element) {
125 return isSignednessType(element.type);
128 .Case<hw::InOutType>(
130 .Case<hw::TypeAliasType>(
132 .Default([](
auto type) {
return false; });
138 if (
auto typeAttr = dyn_cast<TypeAttr>(attr))
146 if (
auto funcOp = dyn_cast<FunctionOpInterface>(op)) {
149 llvm::none_of(funcOp.getFunctionBody().getArgumentTypes(),
153 if (
auto modOp = dyn_cast<hw::HWModuleLike>(op)) {
155 llvm::none_of(modOp.getModuleBody().getArgumentTypes(),
159 auto attrs = llvm::map_range(op->getAttrs(), [](
const NamedAttribute &attr) {
160 return attr.getValue();
166 return operandsOK && resultsOK && attrsOK;
178 matchAndRewrite(
ConstantOp constOp, OpAdaptor adaptor,
179 ConversionPatternRewriter &rewriter)
const override {
182 Type resultType = getTypeConverter()->convertType(constOp.getType());
185 auto valueAttr = rewriter.getIntegerAttr(
186 hw::type_cast<IntegerType>(resultType), constOp.getConstantValue());
187 rewriter.replaceOpWithNewOp<
hw::ConstantOp>(constOp, resultType, valueAttr);
192struct AggregateConstantOpLowering
197 matchAndRewrite(hw::AggregateConstantOp op, OpAdaptor adaptor,
198 ConversionPatternRewriter &rewriter)
const override {
199 auto fields = op.getFieldsAttr().replace([&](IntegerType type) {
200 return getTypeConverter()->convertType(type);
203 return rewriter.notifyMatchFailure(op,
204 "attribute type conversion failed");
205 rewriter.modifyOpInPlace(
206 op, [&] { op.setFieldsAttr(cast<ArrayAttr>(fields)); });
216 matchAndRewrite(
DivOp op, OpAdaptor adaptor,
217 ConversionPatternRewriter &rewriter)
const override {
218 auto loc = op.getLoc();
219 auto isLhsTypeSigned =
220 cast<IntegerType>(op.getOperand(0).getType()).isSigned();
221 auto rhsType = cast<IntegerType>(op.getOperand(1).getType());
222 auto targetType = cast<IntegerType>(op.getResult().getType());
233 bool signedDivision = targetType.isSigned();
234 unsigned extendSize = std::max(
235 targetType.getWidth(),
236 rhsType.getWidth() + (signedDivision && !rhsType.isSigned() ? 1 : 0));
239 Value lhsValue =
extendTypeWidth(rewriter, loc, adaptor.getInputs()[0],
240 extendSize, isLhsTypeSigned);
241 Value rhsValue =
extendTypeWidth(rewriter, loc, adaptor.getInputs()[1],
242 extendSize, rhsType.isSigned());
246 divResult = comb::DivSOp::create(rewriter, loc, lhsValue, rhsValue,
false)
249 divResult = comb::DivUOp::create(rewriter, loc, lhsValue, rhsValue,
false)
253 auto *divOp = divResult.getDefiningOp();
254 rewriter.modifyOpInPlace(
255 divOp, [&]() { divOp->setDialectAttrs(op->getDialectAttrs()); });
258 Value truncateResult =
extractBits(rewriter, loc, divResult, 0,
259 targetType.getWidth());
260 rewriter.replaceOp(op, truncateResult);
272 matchAndRewrite(
CastOp op, OpAdaptor adaptor,
273 ConversionPatternRewriter &rewriter)
const override {
274 auto sourceType = cast<IntegerType>(op.getIn().getType());
275 auto sourceWidth = sourceType.getWidth();
276 bool isSourceTypeSigned = sourceType.isSigned();
277 auto targetWidth = cast<IntegerType>(op.getOut().getType()).getWidth();
280 if (sourceWidth == targetWidth) {
283 replaceValue = adaptor.getIn();
284 }
else if (sourceWidth < targetWidth) {
288 targetWidth, isSourceTypeSigned);
291 replaceValue =
extractBits(rewriter, op.getLoc(), adaptor.getIn(),
294 rewriter.replaceOp(op, replaceValue);
306static comb::ICmpPredicate lowerPredicate(ICmpPredicate pred,
bool isSigned) {
308 case ICmpPredicate::eq:
309 return comb::ICmpPredicate::eq;
310 case ICmpPredicate::ne:
311 return comb::ICmpPredicate::ne;
312 case ICmpPredicate::lt:
313 return isSigned ? comb::ICmpPredicate::slt : comb::ICmpPredicate::ult;
314 case ICmpPredicate::ge:
315 return isSigned ? comb::ICmpPredicate::sge : comb::ICmpPredicate::uge;
316 case ICmpPredicate::le:
317 return isSigned ? comb::ICmpPredicate::sle : comb::ICmpPredicate::ule;
318 case ICmpPredicate::gt:
319 return isSigned ? comb::ICmpPredicate::sgt : comb::ICmpPredicate::ugt;
323 "Missing hwarith::ICmpPredicate to comb::ICmpPredicate lowering");
324 return comb::ICmpPredicate::eq;
331 matchAndRewrite(
ICmpOp op, OpAdaptor adaptor,
332 ConversionPatternRewriter &rewriter)
const override {
333 auto lhsType = cast<IntegerType>(op.getLhs().getType());
334 auto rhsType = cast<IntegerType>(op.getRhs().getType());
335 IntegerType::SignednessSemantics cmpSignedness;
336 const unsigned cmpWidth =
339 ICmpPredicate pred = op.getPredicate();
340 comb::ICmpPredicate combPred = lowerPredicate(
341 pred, cmpSignedness == IntegerType::SignednessSemantics::Signed);
343 const auto loc = op.getLoc();
344 Value lhsValue =
extendTypeWidth(rewriter, loc, adaptor.getLhs(), cmpWidth,
346 Value rhsValue =
extendTypeWidth(rewriter, loc, adaptor.getRhs(), cmpWidth,
349 auto newOp = comb::ICmpOp::create(rewriter, op->getLoc(), combPred,
350 lhsValue, rhsValue,
false);
351 rewriter.modifyOpInPlace(
352 newOp, [&]() { newOp->setDialectAttrs(op->getDialectAttrs()); });
353 rewriter.replaceOp(op, newOp);
359template <
class BinOp,
class ReplaceOp>
365 matchAndRewrite(BinOp op, OpAdaptor adaptor,
366 ConversionPatternRewriter &rewriter)
const override {
367 auto loc = op.getLoc();
368 auto isLhsTypeSigned =
369 cast<IntegerType>(op.getOperand(0).getType()).isSigned();
370 auto isRhsTypeSigned =
371 cast<IntegerType>(op.getOperand(1).getType()).isSigned();
372 auto targetWidth = cast<IntegerType>(op.getResult().getType()).getWidth();
374 Value lhsValue =
extendTypeWidth(rewriter, loc, adaptor.getInputs()[0],
375 targetWidth, isLhsTypeSigned);
376 Value rhsValue =
extendTypeWidth(rewriter, loc, adaptor.getInputs()[1],
377 targetWidth, isRhsTypeSigned);
379 ReplaceOp::create(rewriter, op.getLoc(), lhsValue, rhsValue,
false);
380 rewriter.modifyOpInPlace(
381 newOp, [&]() { newOp->setDialectAttrs(op->getDialectAttrs()); });
382 rewriter.replaceOp(op, newOp);
393 return it->second.type;
396 llvm::TypeSwitch<Type, Type>(type)
397 .Case<IntegerType>([](
auto type) {
398 if (type.isSignless())
400 return IntegerType::get(type.getContext(), type.getWidth());
402 .Case<hw::ArrayType>([
this](
auto type) {
404 type.getNumElements());
406 .Case<hw::UnpackedArrayType>([
this](
auto type) {
407 return hw::UnpackedArrayType::get(
410 .Case<hw::StructType>([
this](
auto type) {
412 llvm::SmallVector<hw::StructType::FieldInfo> convertedElements;
413 for (
auto element : type.getElements()) {
414 convertedElements.push_back(
417 return hw::StructType::get(type.getContext(), convertedElements);
419 .Case<hw::UnionType>([
this](
auto type) {
420 llvm::SmallVector<hw::UnionType::FieldInfo> convertedElements;
421 for (
auto element : type.getElements()) {
422 convertedElements.push_back({element.name,
426 return hw::UnionType::get(type.getContext(), convertedElements);
428 .Case<hw::InOutType>([
this](
auto type) {
431 .Case<hw::TypeAliasType>([
this](
auto type) {
432 return hw::TypeAliasType::get(
435 .Default([](
auto type) {
return type; });
437 return convertedType;
444 addTargetMaterialization([&](mlir::OpBuilder &builder, mlir::Type resultType,
445 mlir::ValueRange inputs,
446 mlir::Location loc) -> mlir::Value {
447 if (inputs.size() != 1)
449 return UnrealizedConversionCastOp::create(builder, loc, resultType,
454 addSourceMaterialization([&](mlir::OpBuilder &builder, mlir::Type resultType,
455 mlir::ValueRange inputs,
456 mlir::Location loc) -> mlir::Value {
457 if (inputs.size() != 1)
459 return UnrealizedConversionCastOp::create(builder, loc, resultType,
471 patterns.add<ConstantOpLowering, AggregateConstantOpLowering, CastOpLowering,
472 ICmpOpLowering, BinaryOpLowering<AddOp, comb::AddOp>,
473 BinaryOpLowering<SubOp, comb::SubOp>,
474 BinaryOpLowering<MulOp, comb::MulOp>, DivOpLowering>(
475 typeConverter,
patterns.getContext());
480class HWArithToHWPass :
public circt::impl::HWArithToHWBase<HWArithToHWPass> {
482 void runOnOperation()
override {
483 ModuleOp
module = getOperation();
485 ConversionTarget target(getContext());
486 target.markUnknownOpDynamicallyLegal(
isLegalOp);
487 RewritePatternSet
patterns(&getContext());
489 target.addIllegalDialect<HWArithDialect>();
502 if (failed(applyFullConversion(module, target, std::move(
patterns))))
503 return signalPassFailure();
513 return std::make_unique<HWArithToHWPass>();
static SmallVector< Value > extractBits(OpBuilder &builder, Value val)
static bool isLegalOp(Operation *op)
Returns true if the given op is considered as legal - i.e.
static bool isSignednessAttr(Attribute attr)
static bool isLegalOp(Operation *op)
Returns true if the given op is considered as legal for HWArith conversion.
static void improveNamehint(Value oldValue, Operation *newOp, llvm::function_ref< std::string(StringRef)> namehintCallback)
static Value extractBits(OpBuilder &builder, Location loc, Value value, unsigned startBit, unsigned bitWidth)
static bool isSignednessType(Type type)
static Value extendTypeWidth(OpBuilder &builder, Location loc, Value value, unsigned targetWidth, bool signExtension)
A helper type converter class that automatically populates the relevant materializations and type con...
mlir::Type removeSignedness(mlir::Type type)
llvm::DenseMap< mlir::Type, ConvertedType > conversionCache
HWArithToHWTypeConverter()
unsigned inferAddResultType(IntegerType::SignednessSemantics &signedness, IntegerType lhs, IntegerType rhs)
The InstanceGraph op interface, see InstanceGraphInterface.td for more details.
void populateHWArithToHWConversionPatterns(HWArithToHWTypeConverter &typeConverter, RewritePatternSet &patterns)
Get the HWArith to HW conversion patterns.
std::unique_ptr< mlir::Pass > createHWArithToHWPass()
LogicalResult doTypeConversion(Operation *op, ValueRange operands, ConversionPatternRewriter &rewriter, const TypeConverter *typeConverter)
Generic pattern which replaces an operation by one of the same operation name, but with converted att...