Skip to content

Commit abf9729

Browse files
committed
[FIRRTL] When replacing register with reset value, attemp coercion
We have a canonicalization which can replace a register with its reset value, if the reset signal is constantly high. However, the reset-value of a register does not have to exactly match the type of the register, it only has to be equivalent. Previously, this canonicalization would fail if the reset value's type doesn't exactly match the register's type. This commit makes the canonicalization attempt to coerce the reset value to the type of the register. The coercion code will perform sign extension or truncation for integers, and will recursively attempt to coerce the elements of a bundle or vector.
1 parent b0c80f4 commit abf9729

File tree

2 files changed

+196
-13
lines changed

2 files changed

+196
-13
lines changed

Diff for: lib/Dialect/FIRRTL/FIRRTLFolds.cpp

+138-3
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,140 @@ static APInt getMaxSignedValue(unsigned bitWidth) {
352352
return bitWidth > 0 ? APInt::getSignedMaxValue(bitWidth) : APInt();
353353
}
354354

355+
// NOLINTNEXTLINE(misc-no-recursion)
356+
static Value coerceSource(PatternRewriter &rewriter, Location &loc,
357+
FIRRTLBaseType targetType, FIRRTLBaseType sourceType,
358+
Value source) {
359+
if (sourceType == targetType)
360+
return source;
361+
362+
auto srcType = sourceType.getAnonymousType();
363+
auto tgtType = targetType.getAnonymousType();
364+
if (srcType == tgtType)
365+
return source;
366+
367+
auto srcBundleType = dyn_cast<BundleType>(srcType);
368+
auto tgtBundleType = dyn_cast<BundleType>(tgtType);
369+
if (srcBundleType && tgtBundleType) {
370+
auto n = tgtBundleType.getNumElements();
371+
SmallVector<Value> elems;
372+
elems.reserve(n);
373+
for (unsigned i = 0; i < n; ++i) {
374+
auto srcElemType = srcBundleType.getElementType(i);
375+
auto tgtElemType = tgtBundleType.getElementType(i);
376+
auto srcElem = rewriter.create<SubfieldOp>(loc, source, i);
377+
auto elem =
378+
coerceSource(rewriter, loc, tgtElemType, srcElemType, srcElem);
379+
elems.push_back(elem);
380+
}
381+
return rewriter.create<BundleCreateOp>(loc, tgtBundleType, elems);
382+
}
383+
384+
auto srcVectorType = dyn_cast<FVectorType>(srcType);
385+
auto tgtVectorType = dyn_cast<FVectorType>(tgtType);
386+
if (srcVectorType && tgtVectorType) {
387+
auto srcElemType = srcVectorType.getElementType();
388+
auto tgtElemType = tgtVectorType.getElementType();
389+
auto n = tgtVectorType.getNumElements();
390+
SmallVector<Value> elems;
391+
elems.reserve(n);
392+
for (unsigned i = 0; i < n; ++i) {
393+
auto srcElem = rewriter.create<SubindexOp>(loc, source, i);
394+
auto elem =
395+
coerceSource(rewriter, loc, tgtElemType, srcElemType, srcElem);
396+
elems.push_back(elem);
397+
}
398+
return rewriter.create<VectorCreateOp>(loc, tgtVectorType, elems);
399+
}
400+
401+
auto srcIntType = dyn_cast<IntType>(srcType);
402+
auto tgtIntType = dyn_cast<IntType>(tgtType);
403+
if (srcIntType && tgtIntType) {
404+
auto srcWidth = srcIntType.getBitWidthOrSentinel();
405+
auto tgtWidth = tgtIntType.getBitWidthOrSentinel();
406+
if (tgtWidth < srcWidth) {
407+
auto delta = srcWidth - tgtWidth;
408+
Value value = rewriter.create<TailPrimOp>(loc, source, delta);
409+
if (tgtIntType.isSigned())
410+
value = rewriter.create<AsSIntPrimOp>(loc, value);
411+
return value;
412+
}
413+
414+
if (tgtWidth > srcWidth)
415+
source = rewriter.create<PadPrimOp>(loc, source, tgtWidth);
416+
if (tgtIntType.isSigned() && !srcIntType.isSigned())
417+
return rewriter.create<AsSIntPrimOp>(loc, source);
418+
if (!tgtIntType.isSigned() && srcIntType.isSigned())
419+
return rewriter.create<AsUIntPrimOp>(loc, source);
420+
return source;
421+
}
422+
423+
return nullptr;
424+
}
425+
426+
/// Emit a coercion from a value to a target type. Returns nullptr if the
427+
/// coercion is not possible. The resulting value is a non-aliasing source
428+
/// value. As such, we can only emit coercions for passive types.
429+
static Value coerceSource(PatternRewriter &rewriter, Location loc,
430+
Type targetType, Value source) {
431+
Type sourceType = source.getType();
432+
433+
// If the types are syntactically equal, no action is needed.
434+
if (sourceType == targetType)
435+
return source;
436+
437+
// If either of the types are not FIRRTL base types, we cannot coerce.
438+
auto sourceFType = type_cast<FIRRTLBaseType>(sourceType);
439+
auto targetFType = type_cast<FIRRTLBaseType>(targetType);
440+
if (!sourceFType || !targetFType)
441+
return nullptr;
442+
443+
// After type_cast resolves type-aliases, the underlying types may be the
444+
// same. If they are, no action is needed.
445+
if (sourceFType == targetFType)
446+
return source;
447+
448+
// One last shot at avoiding coercion: recursively unfold type-aliases and
449+
// check again for syntactic equality. If they are, no action is needed.
450+
if (sourceFType.getAnonymousType() == targetFType.getAnonymousType())
451+
return source;
452+
453+
// OK, some coercion is necessary. Check if it's possible.
454+
455+
// Give up if either side contains const. Eventually, const will be removed
456+
// from the compiler.
457+
if (sourceFType.containsConst() || targetFType.containsConst())
458+
return nullptr;
459+
460+
// We can only coerce when all the involved widths are known. We can usually
461+
// truncate or extend the source value to match the destination, but if either
462+
// src or dst has an uninferred width, we don't know which way to go.
463+
if (sourceFType.hasUninferredWidth() || targetFType.hasUninferredWidth())
464+
return nullptr;
465+
466+
// Similar story for resets...
467+
if (sourceFType.hasUninferredReset() || targetFType.hasUninferredReset())
468+
return nullptr;
469+
470+
// Give up if the target is not passive. If we have to coerce the source
471+
// value, the coercion ops will produce a nonaliasing source value, which
472+
// prevents us from properly coercing to a correct non-passive value.
473+
if (!targetFType.isPassive() || targetFType.containsAnalog())
474+
return nullptr;
475+
476+
// After the earlier recursive checks, we can defer to equivalence checking.
477+
if (!areTypesEquivalent(targetFType, sourceFType))
478+
return nullptr;
479+
480+
auto result = coerceSource(rewriter, loc, targetFType, sourceFType, source);
481+
482+
// Final sanity check: ensure the result will make matchingconnect happy.
483+
if (result)
484+
assert(areTypesAnonymousEquivalent(targetType, result.getType()));
485+
486+
return result;
487+
}
488+
355489
//===----------------------------------------------------------------------===//
356490
// Fold Hooks
357491
//===----------------------------------------------------------------------===//
@@ -2269,14 +2403,15 @@ canonicalizeRegResetWithOneReset(RegResetOp reg, PatternRewriter &rewriter) {
22692403
if (!isDefinedByOneConstantOp(reg.getResetSignal()))
22702404
return failure();
22712405

2272-
auto resetValue = reg.getResetValue();
2273-
if (reg.getType(0) != resetValue.getType())
2406+
auto value =
2407+
coerceSource(rewriter, reg.getLoc(), reg.getType(0), reg.getResetValue());
2408+
if (!value)
22742409
return failure();
22752410

22762411
// Ignore 'passthrough'.
22772412
(void)dropWrite(rewriter, reg->getResult(0), {});
22782413
replaceOpWithNewOpAndCopyName<NodeOp>(
2279-
rewriter, reg, resetValue, reg.getNameAttr(), reg.getNameKind(),
2414+
rewriter, reg, value, reg.getNameAttr(), reg.getNameKind(),
22802415
reg.getAnnotationsAttr(), reg.getInnerSymAttr(), reg.getForceable());
22812416
return success();
22822417
}

Diff for: test/Dialect/FIRRTL/canonicalization.mlir

+58-10
Original file line numberDiff line numberDiff line change
@@ -2294,17 +2294,65 @@ firrtl.module @ForceableRegResetToNode(in %clock: !firrtl.clock, in %dummy : !fi
22942294
}
22952295

22962296
// https://github.com/llvm/circt/issues/8348
2297-
// CHECK-LABEL: firrtl.module @RegResetInvalidResetValueType
2298-
// We cannot replace a regreset with its reset value, when the reset value's type does not match.
2299-
firrtl.module @RegResetInvalidResetValueType(in %c : !firrtl.clock, out %out : !firrtl.uint<2>) {
2300-
%c0_ui1 = firrtl.constant 0 : !firrtl.uint<1>
2301-
%c0_ui2 = firrtl.constant 0 : !firrtl.uint<2>
2297+
// CHECK-LABEL: firrtl.module @RegResetCoerceIntResetValue
2298+
// When we replace a regreset with its reset value, we must ensure the reset-value is the correct type.
2299+
firrtl.module @RegResetCoerceIntResetValue(in %c : !firrtl.clock,
2300+
out %out_si1 : !firrtl.sint<1>, out %out_si2 : !firrtl.sint<2>,
2301+
out %out_ui1 : !firrtl.uint<1>, out %out_ui2 : !firrtl.uint<2>
2302+
) {
2303+
%c1_asyncreset = firrtl.specialconstant 1 : !firrtl.asyncreset
2304+
2305+
%c1_si1 = firrtl.constant 1 : !firrtl.sint<1>
2306+
%c1_si2 = firrtl.constant 1 : !firrtl.sint<2>
2307+
2308+
%c1_ui1 = firrtl.constant 1 : !firrtl.uint<1>
2309+
%c1_ui2 = firrtl.constant 1 : !firrtl.uint<2>
2310+
2311+
// SInt Extension.
2312+
// CHECK: firrtl.matchingconnect %out_si2, %c-1_si2 : !firrtl.sint<2>
2313+
%reg_si2 = firrtl.regreset %c, %c1_asyncreset, %c1_si1 : !firrtl.clock, !firrtl.asyncreset, !firrtl.sint<1>, !firrtl.sint<2>
2314+
firrtl.matchingconnect %out_si2, %reg_si2 : !firrtl.sint<2>
2315+
2316+
// SInt Truncation.
2317+
// CHECK: firrtl.matchingconnect %out_si1, %c-1_si1 : !firrtl.sint<1>
2318+
%reg_si1 = firrtl.regreset %c, %c1_asyncreset, %c1_si2 : !firrtl.clock, !firrtl.asyncreset, !firrtl.sint<2>, !firrtl.sint<1>
2319+
firrtl.matchingconnect %out_si1, %reg_si1 : !firrtl.sint<1>
2320+
2321+
// UInt Extension.
2322+
// CHECK: firrtl.matchingconnect %out_ui2, %c1_ui2 : !firrtl.uint<2>
2323+
%reg_ui2 = firrtl.regreset %c, %c1_asyncreset, %c1_ui1 : !firrtl.clock, !firrtl.asyncreset, !firrtl.uint<1>, !firrtl.uint<2>
2324+
firrtl.matchingconnect %out_ui2, %reg_ui2 : !firrtl.uint<2>
2325+
2326+
// UInt Truncation.
2327+
// CHECK: firrtl.matchingconnect %out_ui1, %c1_ui1 : !firrtl.uint<1>
2328+
%reg_ui1 = firrtl.regreset %c, %c1_asyncreset, %c1_ui2 : !firrtl.clock, !firrtl.asyncreset, !firrtl.uint<2>, !firrtl.uint<1>
2329+
firrtl.matchingconnect %out_ui1, %reg_ui1 : !firrtl.uint<1>
2330+
}
2331+
2332+
// CHECK-LABEL: firrtl.module @RegResetCoerceBundleResetValue
2333+
firrtl.module @RegResetCoerceBundleResetValue(in %c : !firrtl.clock, out %out : !firrtl.bundle<a: sint<2>, b: sint<1>>) {
2334+
// CHECK: %0 = firrtl.aggregateconstant [1 : si2, -1 : si1] : !firrtl.bundle<a: sint<2>, b: sint<1>>
2335+
// CHECK: firrtl.matchingconnect %out, %0 : !firrtl.bundle<a: sint<2>, b: sint<1>>
2336+
%c1_asyncreset = firrtl.specialconstant 1 : !firrtl.asyncreset
2337+
%v = firrtl.aggregateconstant [1, 1] : !firrtl.bundle<a: sint<1>, b: sint<2>>
2338+
%r = firrtl.regreset %c, %c1_asyncreset, %v :
2339+
!firrtl.clock, !firrtl.asyncreset,
2340+
!firrtl.bundle<a: sint<1>, b: sint<2>>,
2341+
!firrtl.bundle<a: sint<2>, b: sint<1>>
2342+
firrtl.matchingconnect %out, %r : !firrtl.bundle<a: sint<2>, b: sint<1>>
2343+
}
2344+
2345+
// CHECK-LABEL: firrtl.module @RegResetCoerceVectorResetValue
2346+
firrtl.module @RegResetCoerceVectorResetValue(in %c : !firrtl.clock, out %out : !firrtl.vector<sint<2>, 1>) {
2347+
// CHECK: %0 = firrtl.aggregateconstant [1 : si2] : !firrtl.vector<sint<2>, 1>
2348+
// CHECK: firrtl.matchingconnect %out, %0 : !firrtl.vector<sint<2>, 1>
23022349
%c1_asyncreset = firrtl.specialconstant 1 : !firrtl.asyncreset
2303-
// CHECK: %reg = firrtl.regreset
2304-
%reg = firrtl.regreset %c, %c1_asyncreset, %c0_ui1 : !firrtl.clock, !firrtl.asyncreset, !firrtl.uint<1>, !firrtl.uint<2>
2305-
// CHECK: firrtl.matchingconnect %out, %reg : !firrtl.uint<2>
2306-
firrtl.matchingconnect %out, %reg : !firrtl.uint<2>
2307-
firrtl.matchingconnect %reg, %c0_ui2 : !firrtl.uint<2>
2350+
%v = firrtl.aggregateconstant [1] : !firrtl.vector<sint<1>, 1>
2351+
%r = firrtl.regreset %c, %c1_asyncreset, %v :
2352+
!firrtl.clock, !firrtl.asyncreset,
2353+
!firrtl.vector<sint<1>, 1>,
2354+
!firrtl.vector<sint<2>, 1>
2355+
firrtl.matchingconnect %out, %r : !firrtl.vector<sint<2>, 1>
23082356
}
23092357

23102358
// https://github.com/llvm/circt/issues/929

0 commit comments

Comments
 (0)