Skip to content

Commit 594787f

Browse files
authored
[Verif] handle self-referencing operations (llvm#8972)
When cloning self-referencing operations like %reg = seq.firreg %reg clock %clock reset sync %reset, %c0_i4 : i4 lower-contracts would previously produce something like: ^bb0(%arg0: !seq.clock, %arg1: i1): ... %5 = seq.firreg(%5, %arg0, %arg1, %4) : i4 ... verif.formal(): ... %3 = seq.firreg(%5, %0, %1, %2) : i4 ... Here we can see that the verif.formal test refers to a value from `bb0`, namely `%5`, which results in an error. The problem appears to be that when cloning the operation, we haven't yet created a mapping for the operation's result (`%5 -> %3`), so `%5` is left in place. This change makes us go through all operands of the cloned operations once more, _after_ adding the mappings for the operations' results, which means that the `%5` in verif.formal is remapped to `%3`. This is done after cloning all operations to also handle cycles longer than one operation. Fixes llvm#8952.
1 parent e698309 commit 594787f

File tree

2 files changed

+55
-4
lines changed

2 files changed

+55
-4
lines changed

lib/Dialect/Verif/Transforms/LowerContracts.cpp

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ Operation *replaceContractOp(OpBuilder &builder, RequireLike op,
5656
return nullptr;
5757
}
5858

59-
LogicalResult cloneOp(OpBuilder &builder, Operation *opToClone,
60-
IRMapping &mapping, bool assumeContract) {
59+
FailureOr<Operation *> cloneOp(OpBuilder &builder, Operation *opToClone,
60+
IRMapping &mapping, bool assumeContract) {
6161
Operation *clonedOp;
6262
// Replace ensure/require ops, otherwise clone
6363
if (auto requireLike = dyn_cast<RequireLike>(*opToClone)) {
@@ -74,7 +74,7 @@ LogicalResult cloneOp(OpBuilder &builder, Operation *opToClone,
7474
llvm::zip(opToClone->getResults(), clonedOp->getResults())) {
7575
mapping.map(x, y);
7676
}
77-
return llvm::success();
77+
return clonedOp;
7878
}
7979

8080
void assumeContractHolds(OpBuilder &builder, IRMapping &mapping,
@@ -118,6 +118,7 @@ LogicalResult cloneFanIn(OpBuilder &builder, Operation *contractToClone,
118118
IRMapping &mapping) {
119119
DenseSet<Operation *> seen;
120120
SmallVector<Operation *> opsToClone;
121+
SmallVector<Operation *> clonedOps;
121122
std::queue<Operation *> workList;
122123
workList.push(contractToClone);
123124

@@ -154,9 +155,24 @@ LogicalResult cloneFanIn(OpBuilder &builder, Operation *contractToClone,
154155
// Sort ops so mapping is available for all operands when cloning an op
155156
computeTopologicalSorting(opsToClone);
156157

158+
clonedOps.reserve(opsToClone.size());
157159
for (auto *op : opsToClone) {
158-
if (failed(cloneOp(builder, op, mapping, true)))
160+
if (auto clonedOp = cloneOp(builder, op, mapping, true);
161+
succeeded(clonedOp)) {
162+
clonedOps.push_back(*clonedOp);
163+
} else {
159164
return failure();
165+
}
166+
}
167+
168+
// Remap self-referencing uses of cloned results in the cloned ops
169+
for (auto *clonedOp : clonedOps) {
170+
for (unsigned i = 0, e = clonedOp->getNumOperands(); i < e; i++) {
171+
auto operand = clonedOp->getOperand(i);
172+
if (mapping.contains(operand)) {
173+
clonedOp->setOperand(i, mapping.lookup(operand));
174+
}
175+
}
160176
}
161177
return success();
162178
}

test/Dialect/Verif/lower-contracts.mlir

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,3 +313,38 @@ hw.module @Mul3(in %a: i2, in %b: i2, out z: i4) {
313313
}
314314
hw.output %z : i4
315315
}
316+
317+
// CHECK-LABEL: verif.formal @Counter_CheckContract_0
318+
// CHECK-NEXT: [[TMP0:%.+]] = hw.constant -1 : i2
319+
// CHECK-NEXT: [[TMP1:%.+]] = hw.constant 1 : i2
320+
// CHECK-NEXT: [[TMP2:%.+]] = hw.constant -2 : i2
321+
// CHECK-NEXT: [[TMP3:%.+]] = hw.constant 0 : i2
322+
// CHECK-NEXT: [[TMP4:%.+]] = verif.symbolic_value : i1
323+
// CHECK-NEXT: [[TMP5:%.+]] = verif.symbolic_value : !seq.clock
324+
// CHECK-NEXT: [[TMP6:%.+]] = seq.from_clock [[TMP5]]
325+
// CHECK-NEXT: [[TMP7:%.+]] = comb.icmp bin eq [[REG:%.+]], [[TMP2]] : i2
326+
// CHECK-NEXT: [[TMP8:%.+]] = comb.add bin [[REG]], [[TMP1]] : i2
327+
// CHECK-NEXT: [[TMP9:%.+]] = comb.mux bin [[TMP7]], [[TMP3]], [[TMP8]] : i2
328+
// CHECK-NEXT: [[REG]] = seq.firreg [[TMP9]] clock [[TMP5]] reset sync [[TMP4]], [[TMP3]] {firrtl.random_init_start = 0 : ui64} : i2
329+
// CHECK-NEXT: [[TMP10:%.+]] = comb.icmp bin ne [[REG]], [[TMP0]] : i2
330+
// CHECK-NEXT: verif.clocked_assert [[TMP10]], posedge [[TMP6]] : i1
331+
// CHECK-NEXT: }
332+
333+
hw.module @Counter(in %in : i2, out out : i2, in %clock : !seq.clock, in %reset : i1) {
334+
%zero = hw.constant 0 : i2
335+
%one = hw.constant 1 : i2
336+
%max = hw.constant -2 : i2
337+
%0 = seq.from_clock %clock
338+
%reg = seq.firreg %next clock %clock reset sync %reset, %zero {firrtl.random_init_start = 0 : ui64} : i2
339+
%eq = comb.icmp bin eq %reg, %max : i2
340+
%added = comb.add bin %reg, %one : i2
341+
%next = comb.mux bin %eq, %zero, %added : i2
342+
%4 = verif.contract %reg : i2 {
343+
%never = hw.constant -1 : i2
344+
%ne = comb.icmp bin ne %4, %never : i2
345+
%6 = verif.has_been_reset %0, sync %reset
346+
%7 = ltl.clock %ne, posedge %0 : i1
347+
verif.ensure %7 if %6 : !ltl.sequence
348+
}
349+
hw.output %4 : i2
350+
}

0 commit comments

Comments
 (0)