Skip to content

Implement more cases for getMaxBits #2879

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

Merged
merged 88 commits into from
Sep 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
ee08839
implement 64-bit cases for getMaxBits
MaxGraey May 27, 2020
374c9b6
add ExtendSInt32 & ExtendUInt32 for unary cases
MaxGraey May 27, 2020
3a0f72e
fix ExtendSInt32
MaxGraey May 27, 2020
091e15e
implement RemUInt32 / RemUInt64 for getMaxBits
MaxGraey May 27, 2020
18a2aa2
implement DivUInt(32/64) / DivSInt(32/64)
MaxGraey May 27, 2020
168fed4
Implement RemSInt32 / RemSInt64
MaxGraey May 27, 2020
d04ae7d
update according review
MaxGraey May 27, 2020
c08376c
Merge branch 'master' into more-getmaxbits
MaxGraey May 30, 2020
2dc792c
fix missing returns for rest cases
MaxGraey May 30, 2020
56146d3
Merge branch 'master' into more-getmaxbits
MaxGraey Jun 2, 2020
3f68768
Merge branch 'master' into more-getmaxbits
MaxGraey Jun 2, 2020
36f73e5
rearrange cases
MaxGraey Jun 2, 2020
c77d5d4
calc getMaxBits also for multiply
MaxGraey Jun 5, 2020
d60b53e
Merge branch 'master' into more-getmaxbits
MaxGraey Jun 5, 2020
0bba5b6
lint
MaxGraey Jun 5, 2020
f43c8db
generalize div / rem for all type dividers
MaxGraey Jun 5, 2020
a6415e1
add fast paths when divider or multiplier is zero
MaxGraey Jun 5, 2020
d979620
more optimizations
MaxGraey Jun 5, 2020
6685e6d
lint
MaxGraey Jun 5, 2020
7726f18
revert generalizations for div / rem
MaxGraey Jun 5, 2020
a39091a
return max bits of left expr for udiv as default case
MaxGraey Jun 5, 2020
908eb96
Merge branch 'master' into more-getmaxbits
MaxGraey Jun 5, 2020
df767f4
return zero when lhs also zero for mul
MaxGraey Jun 6, 2020
73ad57d
Merge branch 'master' into more-getmaxbits
MaxGraey Jul 22, 2020
6b8d232
wip
MaxGraey Jul 22, 2020
f387148
fix using geti32 instead geti64 for 64-bit consts
MaxGraey Jul 22, 2020
38ee4e7
cleanups
MaxGraey Jul 22, 2020
74f10d5
Merge branch 'master' into more-getmaxbits
MaxGraey Jul 24, 2020
fa13337
Merge branch 'master' into more-getmaxbits
MaxGraey Aug 4, 2020
2e9ab76
resolve conflicts
MaxGraey Aug 4, 2020
3ea4530
cleanups
MaxGraey Aug 4, 2020
252ce01
more fixes after resolving conflicts
MaxGraey Aug 4, 2020
fd4e8cf
more fancy unit test error output
MaxGraey Aug 4, 2020
60b7b55
lint
MaxGraey Aug 4, 2020
d93f65d
fixture refresh
MaxGraey Aug 4, 2020
c9fe2cb
add basic tests
MaxGraey Aug 4, 2020
14ae90c
print line and file for assertion errors
MaxGraey Aug 4, 2020
90f39d4
more
MaxGraey Aug 4, 2020
5a99214
refactor
MaxGraey Aug 4, 2020
64aded9
-> failCount
MaxGraey Aug 4, 2020
4b2061a
-> failsCount
MaxGraey Aug 5, 2020
216b6fa
simplify output to cout as well
MaxGraey Aug 5, 2020
c631b94
add basic tests fo divs
MaxGraey Aug 5, 2020
d0a4938
finish div tests
MaxGraey Aug 5, 2020
543d239
more
MaxGraey Aug 5, 2020
5673101
more
MaxGraey Aug 5, 2020
ae827a8
Merge branch 'master' into more-getmaxbits
MaxGraey Aug 5, 2020
8567487
reduce to two consts
MaxGraey Aug 5, 2020
957e945
reorder color consts
MaxGraey Aug 5, 2020
d5d6d2b
use Bits ns
MaxGraey Aug 5, 2020
709b8ed
add rem_s / rem_u tests
MaxGraey Aug 5, 2020
569362f
add and / or / xor tests
MaxGraey Aug 5, 2020
9545f9e
add unary ops
MaxGraey Aug 5, 2020
9085d8e
remove check to zero for rhs consts
MaxGraey Aug 7, 2020
422e46f
Merge branch 'master' into more-getmaxbits
MaxGraey Aug 11, 2020
551a675
changes according review
MaxGraey Aug 11, 2020
d7335a8
also ahndling sub32/sub64
MaxGraey Aug 12, 2020
3a27ca3
linter
MaxGraey Aug 12, 2020
e0739c3
add early returns for negative or zero lhs
MaxGraey Aug 12, 2020
74e568c
remove redundant upper bound limit
MaxGraey Aug 12, 2020
ae75346
Merge remote-tracking branch 'WebAssembly/master' into more-getmaxbits
MaxGraey Aug 13, 2020
6883ce7
cleanup tests
MaxGraey Aug 13, 2020
0cc2e85
remove naeg checks for MulInt32/MulInt64
MaxGraey Aug 14, 2020
acd6518
simplify SubInt32/SubInt64
MaxGraey Aug 14, 2020
df40a08
more tests
MaxGraey Aug 14, 2020
da177ae
add antiexample
MaxGraey Aug 16, 2020
d84f9ba
larger value (255 -> 1023)
MaxGraey Aug 16, 2020
5c1d1c4
add example with global
MaxGraey Aug 16, 2020
f319270
make blobal exportable
MaxGraey Aug 16, 2020
55d9824
more tests
MaxGraey Aug 16, 2020
f924008
revert getMaxBits for subtractions + remove fixtures
MaxGraey Aug 16, 2020
790a05c
Merge branch 'master' into more-getmaxbits
MaxGraey Aug 18, 2020
30b1842
remove some zero checks
MaxGraey Aug 18, 2020
341a3ef
Merge branch 'master' into more-getmaxbits
MaxGraey Aug 22, 2020
b91ee8c
add comments + refactorings
MaxGraey Aug 22, 2020
719d8c3
Merge branch 'master' into more-getmaxbits
MaxGraey Aug 27, 2020
146c966
refactor DivSInt32|64 / DivUInt32|64
MaxGraey Aug 28, 2020
a74f10e
more tests and fixes
MaxGraey Aug 28, 2020
b4d614d
refactor
MaxGraey Aug 28, 2020
9b2fe4d
lint
MaxGraey Aug 28, 2020
162c94d
more tests
MaxGraey Aug 28, 2020
8404d91
indent
MaxGraey Aug 28, 2020
4be0956
simplify
MaxGraey Aug 28, 2020
ec62420
Merge branch 'master' into more-getmaxbits
MaxGraey Sep 16, 2020
5fc687a
Update src/ir/bits.h
MaxGraey Sep 16, 2020
0be9076
Update src/ir/bits.h
MaxGraey Sep 16, 2020
33c9eab
add CeilLog2 util
MaxGraey Sep 16, 2020
277d159
lint
MaxGraey Sep 16, 2020
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
187 changes: 172 additions & 15 deletions src/ir/bits.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,35 +128,85 @@ struct DummyLocalInfoProvider {
template<typename LocalInfoProvider = DummyLocalInfoProvider>
Index getMaxBits(Expression* curr,
LocalInfoProvider* localInfoProvider = nullptr) {
if (auto* const_ = curr->dynCast<Const>()) {
if (auto* c = curr->dynCast<Const>()) {
switch (curr->type.getBasic()) {
case Type::i32:
return 32 - const_->value.countLeadingZeroes().geti32();
return 32 - c->value.countLeadingZeroes().geti32();
case Type::i64:
return 64 - const_->value.countLeadingZeroes().geti64();
return 64 - c->value.countLeadingZeroes().geti64();
default:
WASM_UNREACHABLE("invalid type");
}
} else if (auto* binary = curr->dynCast<Binary>()) {
switch (binary->op) {
// 32-bit
case AddInt32:
case SubInt32:
case MulInt32:
case DivSInt32:
case DivUInt32:
case RemSInt32:
case RemUInt32:
case RotLInt32:
case RotRInt32:
case SubInt32:
return 32;
case AddInt32: {
auto maxBitsLeft = getMaxBits(binary->left, localInfoProvider);
auto maxBitsRight = getMaxBits(binary->right, localInfoProvider);
return std::min(Index(32), std::max(maxBitsLeft, maxBitsRight) + 1);
}
case MulInt32: {
auto maxBitsRight = getMaxBits(binary->right, localInfoProvider);
auto maxBitsLeft = getMaxBits(binary->left, localInfoProvider);
return std::min(Index(32), maxBitsLeft + maxBitsRight);
}
case DivSInt32: {
if (auto* c = binary->right->dynCast<Const>()) {
int32_t maxBitsLeft = getMaxBits(binary->left, localInfoProvider);
// If either side might be negative, then the result will be negative
if (maxBitsLeft == 32 || c->value.geti32() < 0) {
return 32;
}
int32_t bitsRight = getMaxBits(c);
return std::max(0, maxBitsLeft - bitsRight + 1);
}
return 32;
}
case DivUInt32: {
int32_t maxBitsLeft = getMaxBits(binary->left, localInfoProvider);
if (auto* c = binary->right->dynCast<Const>()) {
int32_t bitsRight = getMaxBits(c);
return std::max(0, maxBitsLeft - bitsRight + 1);
}
return maxBitsLeft;
}
case RemSInt32: {
if (auto* c = binary->right->dynCast<Const>()) {
auto maxBitsLeft = getMaxBits(binary->left, localInfoProvider);
// if maxBitsLeft is negative
if (maxBitsLeft == 32) {
return 32;
}
auto bitsRight = Index(CeilLog2(c->value.geti32()));
return std::min(maxBitsLeft, bitsRight);
}
return 32;
}
case RemUInt32: {
if (auto* c = binary->right->dynCast<Const>()) {
auto maxBitsLeft = getMaxBits(binary->left, localInfoProvider);
auto bitsRight = Index(CeilLog2(c->value.geti32()));
return std::min(maxBitsLeft, bitsRight);
}
return 32;
case AndInt32:
}
case AndInt32: {
return std::min(getMaxBits(binary->left, localInfoProvider),
getMaxBits(binary->right, localInfoProvider));
}
case OrInt32:
case XorInt32:
return std::max(getMaxBits(binary->left, localInfoProvider),
getMaxBits(binary->right, localInfoProvider));
case XorInt32: {
auto maxBits = getMaxBits(binary->right, localInfoProvider);
Copy link
Member

Choose a reason for hiding this comment

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

Is there reason to believe that checking the right side first will save more work than checking the left side first, or is it an arbitrary choice?

Copy link
Contributor Author

@MaxGraey MaxGraey Sep 16, 2020

Choose a reason for hiding this comment

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

Yes, right side could be cheaper due to canonization which always force constant on the right. Also x ^ -1 is quite often case.

// if maxBits is negative
if (maxBits == 32) {
return 32;
}
return std::max(getMaxBits(binary->left, localInfoProvider), maxBits);
}
case ShlInt32: {
if (auto* shifts = binary->right->dynCast<Const>()) {
return std::min(Index(32),
Expand All @@ -178,6 +228,7 @@ Index getMaxBits(Expression* curr,
case ShrSInt32: {
if (auto* shift = binary->right->dynCast<Const>()) {
auto maxBits = getMaxBits(binary->left, localInfoProvider);
// if maxBits is negative
if (maxBits == 32) {
return 32;
}
Expand All @@ -188,7 +239,105 @@ Index getMaxBits(Expression* curr,
}
return 32;
}
// 64-bit TODO
case RotLInt64:
case RotRInt64:
case SubInt64:
return 64;
case AddInt64: {
auto maxBitsLeft = getMaxBits(binary->left, localInfoProvider);
auto maxBitsRight = getMaxBits(binary->right, localInfoProvider);
return std::min(Index(64), std::max(maxBitsLeft, maxBitsRight) + 1);
}
case MulInt64: {
auto maxBitsRight = getMaxBits(binary->right, localInfoProvider);
auto maxBitsLeft = getMaxBits(binary->left, localInfoProvider);
return std::min(Index(64), maxBitsLeft + maxBitsRight);
}
case DivSInt64: {
if (auto* c = binary->right->dynCast<Const>()) {
int32_t maxBitsLeft = getMaxBits(binary->left, localInfoProvider);
// if maxBitsLeft or right const value is negative
if (maxBitsLeft == 64 || c->value.geti64() < 0) {
return 64;
}
int32_t bitsRight = getMaxBits(c);
return std::max(0, maxBitsLeft - bitsRight + 1);
}
return 64;
}
case DivUInt64: {
int32_t maxBitsLeft = getMaxBits(binary->left, localInfoProvider);
if (auto* c = binary->right->dynCast<Const>()) {
int32_t bitsRight = getMaxBits(c);
return std::max(0, maxBitsLeft - bitsRight + 1);
}
return maxBitsLeft;
}
case RemSInt64: {
if (auto* c = binary->right->dynCast<Const>()) {
auto maxBitsLeft = getMaxBits(binary->left, localInfoProvider);
// if maxBitsLeft is negative
if (maxBitsLeft == 64) {
return 64;
}
auto bitsRight = Index(CeilLog2(c->value.geti64()));
return std::min(maxBitsLeft, bitsRight);
}
return 64;
}
case RemUInt64: {
if (auto* c = binary->right->dynCast<Const>()) {
auto maxBitsLeft = getMaxBits(binary->left, localInfoProvider);
auto bitsRight = Index(CeilLog2(c->value.geti64()));
return std::min(maxBitsLeft, bitsRight);
}
return 64;
}
case AndInt64: {
auto maxBits = getMaxBits(binary->right, localInfoProvider);
return std::min(getMaxBits(binary->left, localInfoProvider), maxBits);
}
case OrInt64:
case XorInt64: {
auto maxBits = getMaxBits(binary->right, localInfoProvider);
// if maxBits is negative
if (maxBits == 64) {
return 64;
}
return std::max(getMaxBits(binary->left, localInfoProvider), maxBits);
}
case ShlInt64: {
if (auto* shifts = binary->right->dynCast<Const>()) {
auto maxBits = getMaxBits(binary->left, localInfoProvider);
return std::min(Index(64),
Bits::getEffectiveShifts(shifts) + maxBits);
}
return 64;
}
case ShrUInt64: {
if (auto* shift = binary->right->dynCast<Const>()) {
auto maxBits = getMaxBits(binary->left, localInfoProvider);
auto shifts =
std::min(Index(Bits::getEffectiveShifts(shift)),
maxBits); // can ignore more shifts than zero us out
return std::max(Index(0), maxBits - shifts);
}
return 64;
}
case ShrSInt64: {
if (auto* shift = binary->right->dynCast<Const>()) {
auto maxBits = getMaxBits(binary->left, localInfoProvider);
// if maxBits is negative
if (maxBits == 64) {
return 64;
}
auto shifts =
std::min(Index(Bits::getEffectiveShifts(shift)),
maxBits); // can ignore more shifts than zero us out
return std::max(Index(0), maxBits - shifts);
}
return 64;
}
// comparisons
case EqInt32:
case NeInt32:
Expand All @@ -200,6 +349,7 @@ Index getMaxBits(Expression* curr,
case GtUInt32:
case GeSInt32:
case GeUInt32:

case EqInt64:
case NeInt64:
case LtSInt64:
Expand All @@ -210,12 +360,14 @@ Index getMaxBits(Expression* curr,
case GtUInt64:
case GeSInt64:
case GeUInt64:

case EqFloat32:
case NeFloat32:
case LtFloat32:
case LeFloat32:
case GtFloat32:
case GeFloat32:

case EqFloat64:
case NeFloat64:
case LtFloat64:
Expand All @@ -240,7 +392,12 @@ Index getMaxBits(Expression* curr,
case EqZInt64:
return 1;
case WrapInt64:
case ExtendUInt32:
return std::min(Index(32), getMaxBits(unary->value, localInfoProvider));
case ExtendSInt32: {
auto maxBits = getMaxBits(unary->value, localInfoProvider);
return maxBits == 32 ? Index(64) : maxBits;
}
default: {
}
}
Expand Down
8 changes: 8 additions & 0 deletions src/support/bits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,14 @@ template<> int CountLeadingZeroes<uint64_t>(uint64_t v) {
#endif
}

template<> int CeilLog2<uint32_t>(uint32_t v) {
return 32 - CountLeadingZeroes(v - 1);
}

template<> int CeilLog2<uint64_t>(uint64_t v) {
return 64 - CountLeadingZeroes(v - 1);
}

uint32_t Log2(uint32_t v) {
switch (v) {
default:
Expand Down
6 changes: 6 additions & 0 deletions src/support/bits.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ template<typename T> int PopCount(T);
template<typename T> uint32_t BitReverse(T);
template<typename T> int CountTrailingZeroes(T);
template<typename T> int CountLeadingZeroes(T);
template<typename T> int CeilLog2(T);

#ifndef wasm_support_bits_definitions
// The template specializations are provided elsewhere.
Expand All @@ -52,6 +53,8 @@ extern template int CountTrailingZeroes(uint32_t);
extern template int CountTrailingZeroes(uint64_t);
extern template int CountLeadingZeroes(uint32_t);
extern template int CountLeadingZeroes(uint64_t);
extern template int CeilLog2(uint32_t);
extern template int CeilLog2(uint64_t);
Comment on lines +56 to +57
Copy link
Member

Choose a reason for hiding this comment

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

It looks like it would be a lot simpler to just overload the CeilLog2 for uint32_t and uint64_t without using any templates. That probably applies to all these functions, but since they're already like that, this can be left for a follow up.

Copy link
Contributor Author

@MaxGraey MaxGraey Sep 16, 2020

Choose a reason for hiding this comment

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

It was my first attempt, but templates in C++ still a mystery to me sometimes=) I didn’t manage to make friends with bits.h header this bits.cpp part:

template<typename T>
int CeilLog2(T v) {
  return sizeof(T) * 8 - CountLeadingZeroes(v - 1);
}

Also it quite hard to restrict T to unsigned integer types only.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, no problem, let's leave cleaning that up to a follow-up PR.

#endif

// Convenience signed -> unsigned. It usually doesn't make much sense to use bit
Expand All @@ -65,6 +68,9 @@ template<typename T> int CountTrailingZeroes(T v) {
template<typename T> int CountLeadingZeroes(T v) {
return CountLeadingZeroes(typename std::make_unsigned<T>::type(v));
}
template<typename T> int CeilLog2(T v) {
return CeilLog2(typename std::make_unsigned<T>::type(v));
}
template<typename T> bool IsPowerOf2(T v) {
return v != 0 && (v & (v - 1)) == 0;
}
Expand Down
Loading