mirror of https://github.com/llvm/circt.git
[ImportVerilog] Add conditional operator. (#6950)
This commit is contained in:
parent
2189f25c9b
commit
c76eb120d6
|
@ -223,17 +223,18 @@ def NetOp : MooreOp<"net", [
|
|||
]> {
|
||||
let summary = "A net declaration";
|
||||
let description = [{
|
||||
The `moore.net' operation is a net declaration. Net types defines different types
|
||||
of net connection in SV. There are twelve built-in net types defined in the official
|
||||
standard construct of the operation:
|
||||
`supply0`, `supply1`, `tri`, `triand`, `trior`, `trireg`, `tri0`, `tri1`, `uwire`,
|
||||
`wire`, `wand`, `wor`.
|
||||
Optional assignment argument allows net operation to be initialized with specific
|
||||
values as soon as it is created. Only one net declaration assignment can be made for
|
||||
a particular net. See IEEE 1800-2017 § 10.3.1 "The net declaration assignment" for
|
||||
the differences between net declaration assignments and continuous assign statements.
|
||||
It has some features that are not supported: declaring an interconnect net and using
|
||||
user-defined types in the net operation.
|
||||
The `moore.net' operation is a net declaration. Net types defines different
|
||||
types of net connection in SV. There are twelve built-in net types defined
|
||||
in the official standard construct of the operation:
|
||||
`supply0`, `supply1`, `tri`, `triand`, `trior`, `trireg`, `tri0`, `tri1`,
|
||||
`uwire`, `wire`, `wand`, `wor`.
|
||||
Optional assignment argument allows net operation to be initialized with
|
||||
specific values as soon as it is created. Only one net declaration
|
||||
assignment can be made for a particular net. See IEEE 1800-2017 § 10.3.1
|
||||
"The net declaration assignment" for the differences between net declaration
|
||||
assignments and continuous assign statements. It has some features that are
|
||||
not supported: declaring an interconnect net and using user-defined types in
|
||||
the net operation.
|
||||
|
||||
See IEEE 1800-2017 § 6.7 "Net declarations".
|
||||
}];
|
||||
|
@ -961,27 +962,27 @@ def StructInjectOp : MooreOp<"struct_inject"> {
|
|||
def UnionCreateOp : MooreOp<"union_create"> {
|
||||
let summary = "Union Create operation";
|
||||
let description = [{
|
||||
A union is a data type that represents a single piece
|
||||
of storage that can be accessed using one of
|
||||
the named member data types. Only one of the
|
||||
data types in the union can be used at a time.
|
||||
By default, a union is unpacked, meaning there
|
||||
is no required representation for how members
|
||||
of the union are stored. Dynamic types and chandle
|
||||
types can only be used in tagged unions.
|
||||
See IEEE 1800-2017 § 7.3 "Unions"
|
||||
A union is a data type that represents a single piece
|
||||
of storage that can be accessed using one of
|
||||
the named member data types. Only one of the
|
||||
data types in the union can be used at a time.
|
||||
By default, a union is unpacked, meaning there
|
||||
is no required representation for how members
|
||||
of the union are stored. Dynamic types and chandle
|
||||
types can only be used in tagged unions.
|
||||
See IEEE 1800-2017 § 7.3 "Unions"
|
||||
|
||||
Example:
|
||||
```
|
||||
typedef union { int i; shortreal f; } num; // named union type
|
||||
num n;
|
||||
n.f = 0.0; // set n in floating point format
|
||||
typedef struct {
|
||||
bit isfloat;
|
||||
union { int i; shortreal f; } n; // anonymous union type
|
||||
} tagged_st; // named structure
|
||||
```
|
||||
See IEEE 1800-2017 § 7.3 "Unions"
|
||||
Example:
|
||||
```
|
||||
typedef union { int i; shortreal f; } num; // named union type
|
||||
num n;
|
||||
n.f = 0.0; // set n in floating point format
|
||||
typedef struct {
|
||||
bit isfloat;
|
||||
union { int i; shortreal f; } n; // anonymous union type
|
||||
} tagged_st; // named structure
|
||||
```
|
||||
See IEEE 1800-2017 § 7.3 "Unions"
|
||||
}];
|
||||
let arguments = (ins StrAttr:$unionName, UnpackedType:$input);
|
||||
let results = (outs UnpackedType:$result);
|
||||
|
@ -989,30 +990,30 @@ def UnionCreateOp : MooreOp<"union_create"> {
|
|||
$unionName `,` $input attr-dict `:`
|
||||
type($input) `->` type($result)
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
def UnionExtractOp : MooreOp<"union_extract"> {
|
||||
def UnionExtractOp : MooreOp<"union_extract"> {
|
||||
let summary = "Union Extract operation";
|
||||
let description = [{
|
||||
With packed unions, writing one member and reading another is
|
||||
independent of the byte ordering of the machine,
|
||||
unlike an unpacked union of unpacked structures,
|
||||
which are C-compatible and have members in ascending address order.
|
||||
See IEEE 1800-2017 § 7.3.1 "Packed unions"
|
||||
With packed unions, writing one member and reading another is
|
||||
independent of the byte ordering of the machine,
|
||||
unlike an unpacked union of unpacked structures,
|
||||
which are C-compatible and have members in ascending address order.
|
||||
See IEEE 1800-2017 § 7.3.1 "Packed unions"
|
||||
|
||||
Example:
|
||||
```
|
||||
typedef union packed { // default unsigned
|
||||
s_atmcell acell;
|
||||
bit [423:0] bit_slice;
|
||||
bit [52:0][7:0] byte_slice;
|
||||
} u_atmcell;
|
||||
u_atmcell u1;
|
||||
byte b; bit [3:0] nib;
|
||||
b = u1.bit_slice[415:408]; // same as b = u1.byte_slice[51];
|
||||
nib = u1.bit_slice [423:420];
|
||||
```
|
||||
See IEEE 1800-2017 § 7.3.1 "Packed unions"
|
||||
Example:
|
||||
```
|
||||
typedef union packed { // default unsigned
|
||||
s_atmcell acell;
|
||||
bit [423:0] bit_slice;
|
||||
bit [52:0][7:0] byte_slice;
|
||||
} u_atmcell;
|
||||
u_atmcell u1;
|
||||
byte b; bit [3:0] nib;
|
||||
b = u1.bit_slice[415:408]; // same as b = u1.byte_slice[51];
|
||||
nib = u1.bit_slice [423:420];
|
||||
```
|
||||
See IEEE 1800-2017 § 7.3.1 "Packed unions"
|
||||
}];
|
||||
let arguments = (ins StrAttr:$memberName, UnpackedType:$input);
|
||||
let results = (outs UnpackedType:$result);
|
||||
|
@ -1020,5 +1021,71 @@ def UnionCreateOp : MooreOp<"union_create"> {
|
|||
$input `,` $memberName attr-dict `:`
|
||||
type($input) `->` type($result)
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
|?: | 0 | 1 | X | Z |
|
||||
|---|---|---|---|---|
|
||||
| 0 | 0 | X | X | X |
|
||||
| 1 | X | 1 | X | X |
|
||||
| X | X | X | X | X |
|
||||
| Z | X | X | X | X |
|
||||
|
||||
See IEEE 1800-2017 § 11.4.11 "Conditional operator".
|
||||
}];
|
||||
let arguments = (ins AnySingleBitType:$condition);
|
||||
let results = (outs UnpackedType:$result);
|
||||
let regions = (region SizedRegion<1>:$trueRegion,
|
||||
SizedRegion<1>:$falseRegion);
|
||||
let assemblyFormat = [{
|
||||
$condition attr-dict `:` type($condition) `->` type($result)
|
||||
$trueRegion $falseRegion
|
||||
}];
|
||||
}
|
||||
|
||||
def YieldOp : MooreOp<"yield", [
|
||||
Pure,
|
||||
Terminator,
|
||||
HasParent<"ConditionalOp">
|
||||
]> {
|
||||
let summary = "conditional yield and termination operation";
|
||||
let description = [{
|
||||
"moore.yield" yields an SSA value from the Moore dialect op region and
|
||||
terminates the regions. The semantics of how the values are yielded is
|
||||
defined by the parent operation.
|
||||
If "moore.yield" has any operands, the operands must match the parent
|
||||
operation's results.
|
||||
If the parent operation defines no values, then the "moore.yield" may be
|
||||
left out in the custom syntax and the builders will insert one implicitly.
|
||||
Otherwise, it has to be present in the syntax to indicate which values are
|
||||
yielded.
|
||||
}];
|
||||
let arguments = (ins UnpackedType:$result);
|
||||
let builders = [OpBuilder<(ins), [{ /* nothing to do */ }]>];
|
||||
let assemblyFormat = [{
|
||||
attr-dict $result `:` type($result)
|
||||
}];
|
||||
let hasVerifier = 1;
|
||||
}
|
||||
#endif // CIRCT_DIALECT_MOORE_MOOREOPS
|
||||
|
|
|
@ -514,6 +514,41 @@ struct ExprVisitor {
|
|||
return result;
|
||||
}
|
||||
|
||||
// Handle conditional operator `?:`.
|
||||
Value visit(const slang::ast::ConditionalExpression &expr) {
|
||||
auto type = context.convertType(*expr.type);
|
||||
|
||||
// Handle condition.
|
||||
Value cond = convertToSimpleBitVector(
|
||||
context.convertExpression(*expr.conditions.begin()->expr));
|
||||
cond = convertToBool(cond);
|
||||
if (!cond)
|
||||
return {};
|
||||
auto conditionalOp = builder.create<moore::ConditionalOp>(loc, type, cond);
|
||||
|
||||
// Create blocks for true region and false region.
|
||||
conditionalOp.getTrueRegion().emplaceBlock();
|
||||
conditionalOp.getFalseRegion().emplaceBlock();
|
||||
|
||||
OpBuilder::InsertionGuard g(builder);
|
||||
|
||||
// Handle left expression.
|
||||
builder.setInsertionPointToStart(conditionalOp.getBody(0));
|
||||
auto trueValue = context.convertExpression(expr.left());
|
||||
if (!trueValue)
|
||||
return {};
|
||||
builder.create<moore::YieldOp>(loc, trueValue);
|
||||
|
||||
// Handle right expression.
|
||||
builder.setInsertionPointToStart(conditionalOp.getBody(1));
|
||||
auto falseValue = context.convertExpression(expr.right());
|
||||
if (!falseValue)
|
||||
return {};
|
||||
builder.create<moore::YieldOp>(loc, falseValue);
|
||||
|
||||
return conditionalOp.getResult();
|
||||
}
|
||||
|
||||
/// Emit an error for all other expressions.
|
||||
template <typename T>
|
||||
Value visit(T &&node) {
|
||||
|
|
|
@ -353,6 +353,30 @@ LogicalResult ConcatOp::inferReturnTypes(
|
|||
return success();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// YieldOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
LogicalResult YieldOp::verify() {
|
||||
// Check that YieldOp's parent operation is ConditionalOp.
|
||||
auto cond = dyn_cast<ConditionalOp>(*(*this).getParentOp());
|
||||
if (!cond) {
|
||||
emitOpError("must have a conditional parent");
|
||||
return failure();
|
||||
}
|
||||
|
||||
// Check that the operand matches the parent operation's result.
|
||||
auto condType = cond.getType();
|
||||
auto yieldType = getOperand().getType();
|
||||
if (condType != yieldType) {
|
||||
emitOpError("yield type must match conditional. Expected ")
|
||||
<< condType << ", but got " << yieldType << ".";
|
||||
return failure();
|
||||
}
|
||||
|
||||
return success();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// ConversionOp
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
|
|
@ -350,6 +350,9 @@ module Expressions;
|
|||
int a, b;
|
||||
} c, d;
|
||||
} union1;
|
||||
// CHECK: %r1 = moore.variable : real
|
||||
// CHECK: %r2 = moore.variable : real
|
||||
real r1,r2;
|
||||
|
||||
initial begin
|
||||
// CHECK: moore.constant 0 : i32
|
||||
|
@ -600,6 +603,41 @@ module Expressions;
|
|||
// CHECK: moore.or [[TMP1]], [[TMP6]] : i1
|
||||
c = a inside { a, b, [a:b] };
|
||||
|
||||
//===------------------------------------------------------------------===//
|
||||
// Conditional operator
|
||||
|
||||
// CHECK: moore.conditional %x : i1 -> i32 {
|
||||
// CHECK: moore.yield %a : i32
|
||||
// CHECK: } {
|
||||
// CHECK: moore.yield %b : i32
|
||||
// CHECK: }
|
||||
c = x ? a : b;
|
||||
|
||||
// CHECK: moore.conditional %x : i1 -> real {
|
||||
// CHECK: moore.yield %r1 : real
|
||||
// CHECK: } {
|
||||
// CHECK: moore.yield %r2 : real
|
||||
// CHECK: }
|
||||
r1 = x ? r1 : r2;
|
||||
|
||||
// CHECK: [[TMP1:%.+]] = moore.bool_cast %a : i32 -> i1
|
||||
// CHECK: moore.conditional [[TMP1]] : i1 -> i32 {
|
||||
// CHECK: moore.yield %a : i32
|
||||
// CHECK: } {
|
||||
// CHECK: moore.yield %b : i32
|
||||
// CHECK: }
|
||||
c = a ? a : b;
|
||||
|
||||
// CHECK: [[TMP1:%.+]] = moore.sgt %a, %b : i32 -> i1
|
||||
// CHECK: moore.conditional [[TMP1]] : i1 -> i32 {
|
||||
// CHECK: [[TMP2:%.+]] = moore.add %a, %b : i32
|
||||
// CHECK: moore.yield [[TMP2]] : i32
|
||||
// CHECK: } {
|
||||
// CHECK: [[TMP2:%.+]] = moore.sub %a, %b : i32
|
||||
// CHECK: moore.yield [[TMP2]] : i32
|
||||
// CHECK: }
|
||||
c = (a > b) ? (a + b) : (a - b);
|
||||
|
||||
//===------------------------------------------------------------------===//
|
||||
// Assign operators
|
||||
|
||||
|
|
|
@ -217,4 +217,15 @@ moore.module @Expressions() {
|
|||
// CHECK: moore.extract [[VAL4]] from [[VAL5]] : i8, i32 -> i5
|
||||
%5 = moore.constant 2 : i32
|
||||
moore.extract %4 from %5 : i8, i32 -> i5
|
||||
|
||||
// CHECK: moore.conditional %b1 : i1 -> i32 {
|
||||
// CHECK: moore.yield %int : i32
|
||||
// CHECK: } {
|
||||
// CHECK: moore.yield %int2 : i32
|
||||
// CHECK: }
|
||||
moore.conditional %b1 : i1 -> i32 {
|
||||
moore.yield %int : i32
|
||||
} {
|
||||
moore.yield %int2 : i32
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,3 +65,25 @@ moore.constant -2 : !moore.i1
|
|||
|
||||
// expected-error @below {{attribute width 9 does not match return type's width 8}}
|
||||
"moore.constant" () {value = 42 : i9} : () -> !moore.i8
|
||||
|
||||
// -----
|
||||
|
||||
%y = moore.variable : i8
|
||||
|
||||
moore.module @Cond() {
|
||||
// expected-error @below {{'moore.yield' op expects parent op 'moore.conditional'}}
|
||||
moore.yield %y : i8
|
||||
}
|
||||
|
||||
// -----
|
||||
|
||||
%x = moore.variable : i1
|
||||
%t = moore.variable : i8
|
||||
%f = moore.variable : i8
|
||||
|
||||
moore.conditional %x : i1 -> i32 {
|
||||
// expected-error @below {{yield type must match conditional. Expected '!moore.i32', but got '!moore.i8'}}
|
||||
moore.yield %t : i8
|
||||
} {
|
||||
moore.yield %f : i8
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue