[opt] Simplify carry extraction pattern (prepending zero bit, adding, then slicing above the original MSb)#3731
Conversation
cd10c2e to
8c2d439
Compare
26418bf to
0f31205
Compare
… slicing above the MSb)
0f31205 to
1f881c4
Compare
…ing zero bit, adding, slicing above the MSb)
allight
left a comment
There was a problem hiding this comment.
FWIW actually concat is currently considered the 'canonical' representation of zero extend (
xls/xls/passes/canonicalization_pass.cc
Line 257 in c891969
Anyway mostly minor things and looks good otherwise.
| // A uniform view of a bits-typed node that represents a zero-extension of a | ||
| // narrower bits-typed `base` value. | ||
| // | ||
| // This matches either: | ||
| // - `zero_ext(base, new_bit_count=W)` where base has width N < W | ||
| // - `concat(0..., base)` where all leading operands are literal zeros and base | ||
| // is the final operand. | ||
| struct ZeroExtendedBitsView { |
There was a problem hiding this comment.
Probably want to just move the ZeroExtLike from reassociation_pass.cc into node-util and use that instead
xls/xls/passes/reassociation_pass.cc
Line 74 in c891969
| // | ||
| // This is useful for matching commutative patterns while populating additional | ||
| // context via captures in `on_match`. | ||
| inline bool MatchNodesInAnyOrder( |
There was a problem hiding this comment.
nit:
IMO something more general like:
template <typename T, typename Func0, typename... Funcs>
requires(std::is_swappable_v<T> && std::is_invocable_r_v<bool, Func0, T>)
bool MatchingOrderSpan(absl::Span<T> arr, Func0 f0, Funcs... funcs) {
for (int i = 0; i < arr.size(); ++i) {
std::swap(arr[0], arr[i]);
if (f0(arr[0]) &&
MatchingOrderSpan(arr.subspan(1), std::forward<Funcs>(funcs)...)) {
return true;
}
std::swap(arr[0], arr[i]);
}
return false;
}
template <typename T, typename Func0>
requires(std::is_invocable_r_v<bool, Func0, T>)
bool MatchingOrderSpan(absl::Span<T> arr, Func0 f0) {
if (arr.size() != 1) {
return false;
}
return f0(arr[0]);
}
template <typename T>
bool MatchingOrderSpan(absl::Span<T> arr) {
if (arr.size() != 0) {
return false;
}
return true;
}
template <typename T, typename... Funcs>
std::optional<std::array<T, std::tuple_size_v<std::tuple<Funcs...>>>>
MatchingOrder(std::array<T, std::tuple_size_v<std::tuple<Funcs...>>> arr,
Funcs... funcs) {
if (MatchingOrderSpan(absl::MakeSpan(arr), std::forward<Funcs>(funcs)...)) {
return arr;
}
return std::nullopt;
}TEST(MatchingOrderTest, Works) {
std::array<int, 3> arr = {1, 2, 3};
auto v = MatchingOrder(
arr, [](int i) { return i == 3; }, [](int i) { return i == 2; },
[](int i) { return i == 1; });
EXPECT_THAT(v, testing::Optional(testing::_));
EXPECT_EQ(*v, (std::array<int, 3>{3, 2, 1}));
}
TEST(MatchingOrderTest, Works2) {
auto v = MatchingOrder<int>(
{4, 2, 3}, [](int i) { return i == 3; }, [](int i) { return i == 2; },
[](int i) { return i == 1; });
EXPECT_FALSE(v);
}in xls/common would be a bit preferable.
In any event I think its probably a good idea to move away from callbacks like on_match since they make the control flow hard to follow.
| // Include extra boolean context to mirror the motivating pattern. | ||
| BValue eq_x_ff = fb.Eq(x, fb.Literal(UBits(255, 8))); | ||
| BValue leaf_237 = fb.Param("leaf_237", u1); | ||
| BValue leaf_466 = fb.Param("leaf_466", u1); |
There was a problem hiding this comment.
I don't understand what this is trying to show. AFAICT it just makes the test longer? Remove.
| int64_t{16})); | ||
|
|
||
| TEST_F(BitSliceSimplificationPassTest, | ||
| CarryOutOfAddWithZeroExtendedOperand_MatchesSwappedAddOperands) { |
There was a problem hiding this comment.
Don't give tests names with _. CarryOutOfAddWithZeroExtendedOperandMatchesSwappedAddOperands
This and all of the other tests.
| } | ||
|
|
||
| TEST_F(BitSliceSimplificationPassTest, | ||
| CarryOutOfAddWithZeroExtendedOperand_MatchesZeroExtNode) { |
…ing zero bit, adding, slicing above the MSb)
…ing zero bit, adding, slicing above the MSb)
…ing zero bit, adding, slicing above the MSb)
…ing zero bit, adding, slicing above the MSb)
…ing zero bit, adding, slicing above the MSb)
…ing zero bit, adding, slicing above the MSb)
…ing zero bit, adding, slicing above the MSb)
…ing zero bit, adding, slicing above the MSb)
…ing zero bit, adding, slicing above the MSb)
…ing zero bit, adding, slicing above the MSb)
…ing zero bit, adding, slicing above the MSb)
Note the "ZeroExtendedBitsView" in this change seems useful until we come to stronger conclusions on canonical forms (i.e. zero_ext I think should be more canonical than concat as it's a more precise operator, but wouldn't want to make that a prereq).
bit_slice(add(zext(v), k), start=N, width=1)(carry-out ofv + k).bit_slice(...)becomes(k[N] ? ult(v, (2^N - (k mod 2^N))) : uge(v, (2^N - (k mod 2^N))))(and if(k mod 2^N) == 0, becomesliteral(k[N])).before:
which is:
after: