Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Moore] Add explicit truncation and zero/sign-extension #7783

Merged
merged 1 commit into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 61 additions & 1 deletion include/circt/Dialect/Moore/MooreOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ class ResultIsSingleBitMatchingInputDomain<string result, string input> :
llvm::cast<UnpackedType>($_self).getDomain())
}]>;

class TypeDomainsMatch<list<string> values> :
AllMatchSameOperatorTrait<values, [{
cast<moore::UnpackedType>($_self.getType()).getDomain()
}], "domain">;

//===----------------------------------------------------------------------===//
// Structure
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -504,7 +509,7 @@ def StringConstantOp : MooreOp<"string_constant", [Pure]> {
}

//===----------------------------------------------------------------------===//
// Expressions
// Casting and Resizing
//===----------------------------------------------------------------------===//

def ConversionOp : MooreOp<"conversion", [Pure]> {
Expand Down Expand Up @@ -538,6 +543,61 @@ def ConversionOp : MooreOp<"conversion", [Pure]> {
let hasFolder = 1;
}

def TruncOp : MooreOp<"trunc", [
Pure,
TypeDomainsMatch<["input", "result"]>,
PredOpTrait<"result width must be smaller than input width", CPred<[{
cast<moore::IntType>($result.getType()).getBitSize() <
cast<moore::IntType>($input.getType()).getBitSize()
}]>>,
]> {
let summary = "Truncate a value";
let description = [{
Reduce the bit width of a value by removing some of its most significant
bits. This can only change the bit width of an integer type.
}];
let arguments = (ins IntType:$input);
let results = (outs IntType:$result);
let assemblyFormat = [{
$input attr-dict `:` type($input) `->` type($result)
}];
}

class ExtOpBase<string mnemonic> : MooreOp<mnemonic, [
Pure,
TypeDomainsMatch<["input", "result"]>,
PredOpTrait<"result width must be larger than input width", CPred<[{
cast<moore::IntType>($result.getType()).getBitSize() >
cast<moore::IntType>($input.getType()).getBitSize()
}]>>,
]> {
let arguments = (ins IntType:$input);
let results = (outs IntType:$result);
let assemblyFormat = [{
$input attr-dict `:` type($input) `->` type($result)
}];
}

def ZExtOp : ExtOpBase<"zext"> {
let summary = "Zero-extend a value";
let description = [{
Increase the bit width of a value by inserting additional zero most
significant bits. This keeps the unsigned value constant.
}];
}

def SExtOp : ExtOpBase<"sext"> {
let summary = "Sign-extend a value";
let description = [{
Increase the bit width of a value by replicating its most significant bit.
This keeps the signed value constant.
}];
}

//===----------------------------------------------------------------------===//
// Expressions
//===----------------------------------------------------------------------===//

def NegOp : MooreOp<"neg", [Pure, SameOperandsAndResultType]> {
let summary = "Arithmetic negation";
let description = [{
Expand Down
53 changes: 45 additions & 8 deletions lib/Conversion/ImportVerilog/Expressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -610,20 +610,16 @@ struct RvalueExprVisitor {

// Handle left expression.
builder.setInsertionPointToStart(&trueBlock);
auto trueValue = context.convertRvalueExpression(expr.left());
auto trueValue = context.convertRvalueExpression(expr.left(), type);
if (!trueValue)
return {};
if (trueValue.getType() != type)
trueValue = builder.create<moore::ConversionOp>(loc, type, trueValue);
builder.create<moore::YieldOp>(loc, trueValue);

// Handle right expression.
builder.setInsertionPointToStart(&falseBlock);
auto falseValue = context.convertRvalueExpression(expr.right());
auto falseValue = context.convertRvalueExpression(expr.right(), type);
if (!falseValue)
return {};
if (falseValue.getType() != type)
falseValue = builder.create<moore::ConversionOp>(loc, type, falseValue);
builder.create<moore::YieldOp>(loc, falseValue);

return conditionalOp.getResult();
Expand Down Expand Up @@ -967,8 +963,9 @@ Value Context::convertRvalueExpression(const slang::ast::Expression &expr,
Type requiredType) {
auto loc = convertLocation(expr.sourceRange);
auto value = expr.visit(RvalueExprVisitor(*this, loc));
if (value && requiredType && value.getType() != requiredType)
value = builder.create<moore::ConversionOp>(loc, requiredType, value);
if (value && requiredType)
value =
materializeConversion(requiredType, value, expr.type->isSigned(), loc);
return value;
}

Expand Down Expand Up @@ -1064,3 +1061,43 @@ Value Context::convertToSimpleBitVector(Value value) {
<< " cannot be cast to a simple bit vector";
return {};
}

Value Context::materializeConversion(Type type, Value value, bool isSigned,
Location loc) {
if (type == value.getType())
return value;
auto dstPacked = dyn_cast<moore::PackedType>(type);
auto srcPacked = dyn_cast<moore::PackedType>(value.getType());

// Resize the value if needed.
if (dstPacked && srcPacked && dstPacked.getBitSize() &&
srcPacked.getBitSize() &&
*dstPacked.getBitSize() != *srcPacked.getBitSize()) {
auto dstWidth = *dstPacked.getBitSize();
auto srcWidth = *srcPacked.getBitSize();

// Convert the value to a simple bit vector which we can extend or truncate.
auto srcWidthType = moore::IntType::get(value.getContext(), srcWidth,
srcPacked.getDomain());
if (value.getType() != srcWidthType)
value = builder.create<moore::ConversionOp>(value.getLoc(), srcWidthType,
value);
Comment on lines +1082 to +1084
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So cool 😎!


// Create truncation or sign/zero extension ops depending on the source and
// destination width.
auto dstWidthType = moore::IntType::get(value.getContext(), dstWidth,
srcPacked.getDomain());
if (dstWidth < srcWidth) {
value = builder.create<moore::TruncOp>(loc, dstWidthType, value);
} else if (dstWidth > srcWidth) {
if (isSigned)
value = builder.create<moore::SExtOp>(loc, dstWidthType, value);
else
value = builder.create<moore::ZExtOp>(loc, dstWidthType, value);
}
}

if (value.getType() != type)
value = builder.create<moore::ConversionOp>(loc, type, value);
return value;
}
5 changes: 5 additions & 0 deletions lib/Conversion/ImportVerilog/ImportVerilogInternals.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ struct Context {
/// if the given value is null.
Value convertToSimpleBitVector(Value value);

/// Helper function to insert the necessary operations to cast a value from
/// one type to another.
Value materializeConversion(Type type, Value value, bool isSigned,
Location loc);

/// Helper function to materialize an `SVInt` as an SSA value.
Value materializeSVInt(const slang::SVInt &svint,
const slang::ast::Type &type, Location loc);
Expand Down
56 changes: 55 additions & 1 deletion lib/Conversion/MooreToCore/MooreToCore.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -967,6 +967,10 @@ struct CaseXZEqOpConversion : public OpConversionPattern<SourceOp> {
}
};

//===----------------------------------------------------------------------===//
// Conversions
//===----------------------------------------------------------------------===//

struct ConversionOpConversion : public OpConversionPattern<ConversionOp> {
using OpConversionPattern::OpConversionPattern;

Expand All @@ -991,6 +995,50 @@ struct ConversionOpConversion : public OpConversionPattern<ConversionOp> {
}
};

struct TruncOpConversion : public OpConversionPattern<TruncOp> {
using OpConversionPattern::OpConversionPattern;

LogicalResult
matchAndRewrite(TruncOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
rewriter.replaceOpWithNewOp<comb::ExtractOp>(op, adaptor.getInput(), 0,
op.getType().getWidth());
return success();
}
};

struct ZExtOpConversion : public OpConversionPattern<ZExtOp> {
using OpConversionPattern::OpConversionPattern;

LogicalResult
matchAndRewrite(ZExtOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
auto targetWidth = op.getType().getWidth();
auto inputWidth = op.getInput().getType().getWidth();

auto zeroExt = rewriter.create<hw::ConstantOp>(
op.getLoc(), rewriter.getIntegerType(targetWidth - inputWidth), 0);

rewriter.replaceOpWithNewOp<comb::ConcatOp>(
op, ValueRange{zeroExt, adaptor.getInput()});
return success();
}
};

struct SExtOpConversion : public OpConversionPattern<SExtOp> {
using OpConversionPattern::OpConversionPattern;

LogicalResult
matchAndRewrite(SExtOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
auto type = typeConverter->convertType(op.getType());
auto value =
comb::createOrFoldSExt(op.getLoc(), adaptor.getInput(), type, rewriter);
rewriter.replaceOp(op, value);
return success();
}
};

//===----------------------------------------------------------------------===//
// Statement Conversion
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -1498,10 +1546,16 @@ static void populateOpConversion(RewritePatternSet &patterns,
VariableOpConversion,
NetOpConversion,

// Patterns for conversion operations.
ConversionOpConversion,
TruncOpConversion,
ZExtOpConversion,
SExtOpConversion,

// Patterns of miscellaneous operations.
ConstantOpConv, ConcatOpConversion, ReplicateOpConversion,
ExtractOpConversion, DynExtractOpConversion, DynExtractRefOpConversion,
ConversionOpConversion, ReadOpConversion,
ReadOpConversion,
StructExtractOpConversion, StructExtractRefOpConversion,
ExtractRefOpConversion, StructCreateOpConversion, ConditionalOpConversion,
YieldOpConversion, OutputOpConversion, StringConstantOpConv,
Expand Down
39 changes: 31 additions & 8 deletions test/Conversion/ImportVerilog/basic.sv
Original file line number Diff line number Diff line change
Expand Up @@ -759,8 +759,9 @@ module Expressions;
// CHECK: moore.neg [[TMP1]] : i32
c = -a;
// CHECK: [[TMP1:%.+]] = moore.read %v
// CHECK: [[TMP2:%.+]] = moore.conversion [[TMP1]] : !moore.array<2 x i4> -> !moore.i32
// CHECK: moore.neg [[TMP2]] : i32
// CHECK: [[TMP2:%.+]] = moore.conversion [[TMP1]] : !moore.array<2 x i4> -> !moore.i8
// CHECK: [[TMP3:%.+]] = moore.zext [[TMP2]] : i8 -> i32
// CHECK: moore.neg [[TMP3]] : i32
c = -v;
// CHECK: [[TMP1:%.+]] = moore.read %a
// CHECK: moore.not [[TMP1]] : i32
Expand Down Expand Up @@ -831,8 +832,9 @@ module Expressions;
c = a + b;
// CHECK: [[TMP1:%.+]] = moore.read %a
// CHECK: [[TMP2:%.+]] = moore.read %v
// CHECK: [[TMP3:%.+]] = moore.conversion [[TMP2]] : !moore.array<2 x i4> -> !moore.i32
// CHECK: moore.add [[TMP1]], [[TMP3]] : i32
// CHECK: [[TMP3:%.+]] = moore.conversion [[TMP2]] : !moore.array<2 x i4> -> !moore.i8
// CHECK: [[TMP4:%.+]] = moore.zext [[TMP3]] : i8 -> i32
// CHECK: moore.add [[TMP1]], [[TMP4]] : i32
c = a + v;
// CHECK: [[TMP1:%.+]] = moore.read %a
// CHECK: [[TMP2:%.+]] = moore.read %b
Expand Down Expand Up @@ -1305,15 +1307,15 @@ module Conversion;
// Implicit conversion.
// CHECK: %a = moore.variable
// CHECK: [[TMP1:%.+]] = moore.read %a
// CHECK: [[TMP2:%.+]] = moore.conversion [[TMP1]] : !moore.i16 -> !moore.i32
// CHECK: [[TMP2:%.+]] = moore.sext [[TMP1]] : i16 -> i32
// CHECK: %b = moore.variable [[TMP2]]
shortint a;
int b = a;

// Explicit conversion.
// CHECK: [[TMP1:%.+]] = moore.read %a
// CHECK: [[TMP2:%.+]] = moore.conversion [[TMP1]] : !moore.i16 -> !moore.i8
// CHECK: [[TMP3:%.+]] = moore.conversion [[TMP2]] : !moore.i8 -> !moore.i32
// CHECK: [[TMP2:%.+]] = moore.trunc [[TMP1]] : i16 -> i8
// CHECK: [[TMP3:%.+]] = moore.sext [[TMP2]] : i8 -> i32
// CHECK: %c = moore.variable [[TMP3]]
int c = byte'(a);

Expand All @@ -1327,7 +1329,7 @@ module Conversion;

// Width conversion.
// CHECK: [[TMP1:%.+]] = moore.read %b
// CHECK: [[TMP2:%.+]] = moore.conversion [[TMP1]] : !moore.i32 -> !moore.i19
// CHECK: [[TMP2:%.+]] = moore.trunc [[TMP1]] : i32 -> i19
// CHECK: %e = moore.variable [[TMP2]]
bit signed [18:0] e = 19'(b);

Expand Down Expand Up @@ -2049,3 +2051,24 @@ endmodule
module PortCastB (input bit [0:0][31:0] a, output bit [0:0][31:0] b);
assign b = a;
endmodule

// CHECK-LABEL: func.func private @SignCastsA(
// CHECK-SAME: %arg0: !moore.l16
function void SignCastsA(logic [15:0] value);
// CHECK: [[TMP:%.+]] = moore.zext %arg0 : l16 -> l32
// CHECK: call @SignCastsB([[TMP]])
SignCastsB($unsigned(value));
// CHECK: [[TMP:%.+]] = moore.sext %arg0 : l16 -> l32
// CHECK: call @SignCastsB([[TMP]])
SignCastsB($signed(value));

// CHECK: [[TMP:%.+]] = moore.zext %arg0 : l16 -> l32
// CHECK: call @SignCastsB([[TMP]])
SignCastsB(unsigned'(value));
// CHECK: [[TMP:%.+]] = moore.sext %arg0 : l16 -> l32
// CHECK: call @SignCastsB([[TMP]])
SignCastsB(signed'(value));
endfunction

function void SignCastsB(logic [31:0] value);
endfunction
43 changes: 31 additions & 12 deletions test/Conversion/MooreToCore/basic.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -65,18 +65,6 @@ func.func @Expressions(%arg0: !moore.i1, %arg1: !moore.l1, %arg2: !moore.i6, %ar
moore.constant 12 : !moore.i32
moore.constant 3 : !moore.i6

moore.conversion %arg0 : !moore.i1 -> !moore.l1
// CHECK-NEXT: [[V0:%.+]] = hw.constant 0 : i2
// CHECK-NEXT: comb.concat [[V0]], %arg2 : i2, i6
moore.conversion %arg2 : !moore.i6 -> !moore.l8
// CHECK-NEXT: [[V0:%.+]] = comb.extract %arg2 from 4 : (i6) -> i2
// CHECK-NEXT: [[V1:%.+]] = hw.constant 0 : i2
// CHECK-NEXT: [[V2:%.+]] = comb.icmp eq [[V0]], [[V1]] : i2
// CHECK-NEXT: [[V3:%.+]] = comb.extract %arg2 from 0 : (i6) -> i4
// CHECK-NEXT: [[V4:%.+]] = hw.constant -1 : i4
// CHECK-NEXT: comb.mux [[V2]], [[V3]], [[V4]] : i4
moore.conversion %arg2 : !moore.i6 -> !moore.l4

// CHECK-NEXT: [[V0:%.+]] = hw.constant 0 : i5
// CHECK-NEXT: [[V1:%.+]] = comb.concat [[V0]], %arg0 : i5, i1
// CHECK-NEXT: comb.shl %arg2, [[V1]] : i6
Expand Down Expand Up @@ -879,3 +867,34 @@ moore.module @StringConstant() {
moore.return
}
}

// CHECK-LABEL: func.func @Conversions
func.func @Conversions(%arg0: !moore.i16, %arg1: !moore.l16) {
// CHECK: [[TMP:%.+]] = comb.extract %arg0 from 0 : (i16) -> i8
// CHECK: dbg.variable "trunc", [[TMP]]
%0 = moore.trunc %arg0 : i16 -> i8
dbg.variable "trunc", %0 : !moore.i8

// CHECK: [[ZEXT:%.+]] = hw.constant 0 : i16
// CHECK: [[TMP:%.+]] = comb.concat [[ZEXT]], %arg0 : i16, i16
// CHECK: dbg.variable "zext", [[TMP]]
%1 = moore.zext %arg0 : i16 -> i32
dbg.variable "zext", %1 : !moore.i32

// CHECK: [[SIGN:%.+]] = comb.extract %arg0 from 15 : (i16) -> i1
// CHECK: [[SEXT:%.+]] = comb.replicate [[SIGN]] : (i1) -> i16
// CHECK: [[TMP:%.+]] = comb.concat [[SEXT]], %arg0 : i16, i16
// CHECK: dbg.variable "sext", [[TMP]]
%2 = moore.sext %arg0 : i16 -> i32
dbg.variable "sext", %2 : !moore.i32

// CHECK: dbg.variable "i2l", %arg0 : i16
%3 = moore.conversion %arg0 : !moore.i16 -> !moore.l16
dbg.variable "i2l", %3 : !moore.l16

// CHECK: dbg.variable "l2i", %arg1 : i16
%4 = moore.conversion %arg1 : !moore.l16 -> !moore.i16
dbg.variable "l2i", %4 : !moore.i16

return
}
Loading