diff --git a/src/ir/bits.h b/src/ir/bits.h index 7f3c1f4b2cf..a894fb75bab 100644 --- a/src/ir/bits.h +++ b/src/ir/bits.h @@ -20,6 +20,7 @@ #include "ir/boolean.h" #include "ir/literal-utils.h" #include "ir/load-utils.h" +#include "ir/match.h" #include "support/bits.h" #include "wasm-builder.h" @@ -145,9 +146,112 @@ inline Expression* makePackedFieldGet(Expression* value, return builder.makeBinary(AndInt32, value, builder.makeConst(int32_t(mask))); } -// getMaxBits() helper that has pessimistic results for the bits used in locals. -struct DummyLocalInfoProvider { - Index getMaxBitsForLocal(LocalGet* get) { +// Check if an expression is a sign-extend, and if so, returns the value +// that is extended, otherwise nullptr +inline Expression* getSignExtValue(Expression* curr) { + // We only care about integers here, and ignore unreachables, etc. + if (!curr->type.isInteger()) { + return nullptr; + } + if (auto* unary = curr->dynCast()) { + switch (unary->op) { + case ExtendS8Int32: + case ExtendS16Int32: + case ExtendS8Int64: + case ExtendS16Int64: + case ExtendS32Int64: + // The operand may have become unreachable before refinalization updates + // `curr->type`. + return unary->value->type.isInteger() ? unary->value : nullptr; + default: + return nullptr; + } + } + using namespace Abstract; + using namespace Match; + int64_t leftShift = 0, rightShift = 0; + Expression* extended = nullptr; + if (matches(curr, + binary(ShrS, + binary(Shl, any(&extended), ival(&leftShift)), + ival(&rightShift)))) { + auto leftShifts = getEffectiveShifts(leftShift, curr->type); + auto rightShifts = getEffectiveShifts(rightShift, curr->type); + if (leftShifts != 0 && leftShifts == rightShifts) { + return extended; + } + } + return nullptr; +} + +// Check if an expression is almost a sign-extend: perhaps the inner shift +// is too large. We can split the shifts in that case, which is sometimes +// useful (e.g. if we can remove the signext) +inline Expression* getAlmostSignExt(Expression* curr) { + using namespace Abstract; + using namespace Match; + int64_t leftShift = 0, rightShift = 0; + Expression* extended = nullptr; + if (matches(curr, + binary(ShrS, + binary(Shl, any(&extended), ival(&leftShift)), + ival(&rightShift)))) { + auto leftShifts = getEffectiveShifts(leftShift, curr->type); + auto rightShifts = getEffectiveShifts(rightShift, curr->type); + if (rightShifts != 0 && rightShifts <= leftShifts) { + return extended; + } + } + return nullptr; +} + +// Returns the number of low bits kept by an almost-sign-extend (including any +// extra left-shifted zero bits), and sets |extraLeftShifts| to the number of +// extra left shifts. +inline Index getAlmostSignExtKeptBits(Expression* curr, + Index& extraLeftShifts) { + auto* leftShift = curr->cast()->left->cast()->right; + auto* rightShift = curr->cast()->right; + extraLeftShifts = + getEffectiveShifts(leftShift) - getEffectiveShifts(rightShift); + return curr->type.getByteSize() * 8 - getEffectiveShifts(rightShift); +} + +// Check if an expression is a zero-extend, and if so, returns the value +// that is extended, otherwise nullptr +inline Expression* getZeroExtValue(Expression* curr) { + // We only care about i32s here, and ignore i64s, unreachables, etc. + if (curr->type != Type::i32) { + return nullptr; + } + using namespace Match; + int32_t mask = 0; + Expression* extended = nullptr; + if (matches(curr, binary(AndInt32, any(&extended), i32(&mask))) && + getMaskedBits(mask) != 0) { + return extended; + } + return nullptr; +} + +// Returns the number of low bits kept by a zero-extend operation. +inline Index getZeroExtKeptBits(Expression* curr) { + assert(curr->type == Type::i32); + int32_t mask = curr->cast()->right->cast()->value.geti32(); + return getMaskedBits(mask); +} + +// Interface for providing known bit properties of locals. +struct LocalInfoProvider { + virtual ~LocalInfoProvider() = default; + virtual Index getMaxBitsForLocal(LocalGet* get) const = 0; + virtual Index getSignExtKeptBitsForLocal(LocalGet* get) const = 0; +}; + +// LocalInfoProvider helper that has pessimistic results for the bits used in +// locals. +struct DummyLocalInfoProvider : LocalInfoProvider { + Index getMaxBitsForLocal(LocalGet* get) const override { if (get->type == Type::i32) { return 32; } else if (get->type == Type::i64) { @@ -155,347 +259,390 @@ struct DummyLocalInfoProvider { } WASM_UNREACHABLE("type has no integer bit size"); } + + Index getSignExtKeptBitsForLocal(LocalGet* get) const override { return 0; } }; -// Returns the maximum amount of bits used in an integer expression -// not extremely precise (doesn't look into add operands, etc.) -// LocalInfoProvider is an optional class that can provide answers about -// local.get. -template -Index getMaxBits(Expression* curr, - LocalInfoProvider* localInfoProvider = nullptr) { - if (Properties::emitsBoolean(curr)) { - return 1; - } - if (auto* c = curr->dynCast()) { - switch (curr->type.getBasic()) { - case Type::i32: - return 32 - c->value.countLeadingZeroes().geti32(); - case Type::i64: - return 64 - c->value.countLeadingZeroes().geti64(); - default: - WASM_UNREACHABLE("invalid type"); +class Provider { + const LocalInfoProvider& localInfoProvider; + + static inline const DummyLocalInfoProvider dummyLocalInfoProvider{}; + +public: + Provider(const LocalInfoProvider* localInfoProvider = nullptr) + : localInfoProvider(localInfoProvider ? *localInfoProvider + : dummyLocalInfoProvider) {} + + // Returns the maximum amount of bits used in an integer expression + // not extremely precise (doesn't look into add operands, etc.) + Index getMaxBits(Expression* curr) const { + if (Properties::emitsBoolean(curr)) { + return 1; } - } else if (auto* binary = curr->dynCast()) { - switch (binary->op) { - // 32-bit - 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); + if (auto* c = curr->dynCast()) { + switch (curr->type.getBasic()) { + case Type::i32: + return 32 - c->value.countLeadingZeroes().geti32(); + case Type::i64: + return 64 - c->value.countLeadingZeroes().geti64(); + default: + WASM_UNREACHABLE("invalid type"); } - case DivSInt32: { - if (auto* c = binary->right->dynCast()) { - 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; + } else if (auto* binary = curr->dynCast()) { + switch (binary->op) { + // 32-bit + case RotLInt32: + case RotRInt32: + case SubInt32: + return 32; + case AddInt32: { + auto maxBitsLeft = getMaxBits(binary->left); + auto maxBitsRight = getMaxBits(binary->right); + return std::min(Index(32), std::max(maxBitsLeft, maxBitsRight) + 1); + } + case MulInt32: { + auto maxBitsRight = getMaxBits(binary->right); + auto maxBitsLeft = getMaxBits(binary->left); + return std::min(Index(32), maxBitsLeft + maxBitsRight); + } + case DivSInt32: { + if (auto* c = binary->right->dynCast()) { + int32_t maxBitsLeft = getMaxBits(binary->left); + // 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); + // Apply std::min: Result bits cannot exceed dividend bits + return std::min(maxBitsLeft, + std::max(0, maxBitsLeft - bitsRight + 1)); } - int32_t bitsRight = getMaxBits(c); - // Apply std::min: Result bits cannot exceed dividend bits - return std::min(maxBitsLeft, - std::max(0, maxBitsLeft - bitsRight + 1)); + return 32; } - return 32; - } - case DivUInt32: { - int32_t maxBitsLeft = getMaxBits(binary->left, localInfoProvider); - if (auto* c = binary->right->dynCast()) { - int32_t bitsRight = getMaxBits(c); - // Apply std::min: Result bits cannot exceed dividend bits - return std::min(maxBitsLeft, - std::max(0, maxBitsLeft - bitsRight + 1)); + case DivUInt32: { + int32_t maxBitsLeft = getMaxBits(binary->left); + if (auto* c = binary->right->dynCast()) { + int32_t bitsRight = getMaxBits(c); + // Apply std::min: Result bits cannot exceed dividend bits + return std::min(maxBitsLeft, + std::max(0, maxBitsLeft - bitsRight + 1)); + } + return maxBitsLeft; } - return maxBitsLeft; - } - case RemSInt32: { - if (auto* c = binary->right->dynCast()) { - auto maxBitsLeft = getMaxBits(binary->left, localInfoProvider); - // if left may be negative, the result may be negative - if (maxBitsLeft == 32) { - return 32; + case RemSInt32: { + if (auto* c = binary->right->dynCast()) { + auto maxBitsLeft = getMaxBits(binary->left); + // if left may be negative, the result may be negative + if (maxBitsLeft == 32) { + return 32; + } + auto bitsRight = Index(ceilLog2(c->value.geti32())); + return std::min(maxBitsLeft, bitsRight); } - auto bitsRight = Index(ceilLog2(c->value.geti32())); - return std::min(maxBitsLeft, bitsRight); + return 32; } - return 32; - } - case RemUInt32: { - if (auto* c = binary->right->dynCast()) { - auto maxBitsLeft = getMaxBits(binary->left, localInfoProvider); - auto bitsRight = Index(ceilLog2(c->value.geti32())); - return std::min(maxBitsLeft, bitsRight); + case RemUInt32: { + if (auto* c = binary->right->dynCast()) { + auto maxBitsLeft = getMaxBits(binary->left); + auto bitsRight = Index(ceilLog2(c->value.geti32())); + return std::min(maxBitsLeft, bitsRight); + } + return 32; } - return 32; - } - 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 ShlInt32: { - if (auto* shifts = binary->right->dynCast()) { - return std::min(Index(32), - getMaxBits(binary->left, localInfoProvider) + - Bits::getEffectiveShifts(shifts)); + case AndInt32: { + return std::min(getMaxBits(binary->left), getMaxBits(binary->right)); } - return 32; - } - case ShrUInt32: { - if (auto* shift = binary->right->dynCast()) { - 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); + case OrInt32: + case XorInt32: { + return std::max(getMaxBits(binary->left), getMaxBits(binary->right)); } - return 32; - } - case ShrSInt32: { - if (auto* shift = binary->right->dynCast()) { - auto maxBits = getMaxBits(binary->left, localInfoProvider); - // if left may be negative, the result may be negative - if (maxBits == 32) { - return 32; + case ShlInt32: { + if (auto* shifts = binary->right->dynCast()) { + return std::min( + Index(32), getMaxBits(binary->left) + getEffectiveShifts(shifts)); } - 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 32; } - return 32; - } - 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()) { - int32_t maxBitsLeft = getMaxBits(binary->left, localInfoProvider); - // if left or right const value is negative - if (maxBitsLeft == 64 || c->value.geti64() < 0) { - return 64; + case ShrUInt32: { + if (auto* shift = binary->right->dynCast()) { + auto maxBits = getMaxBits(binary->left); + auto shifts = + std::min(Index(getEffectiveShifts(shift)), + maxBits); // can ignore more shifts than zero us out + return std::max(Index(0), maxBits - shifts); } - int32_t bitsRight = getMaxBits(c); - // Apply std::min: Result bits cannot exceed dividend bits - return std::min(maxBitsLeft, - std::max(0, maxBitsLeft - bitsRight + 1)); + return 32; } - return 64; - } - case DivUInt64: { - int32_t maxBitsLeft = getMaxBits(binary->left, localInfoProvider); - if (auto* c = binary->right->dynCast()) { - int32_t bitsRight = getMaxBits(c); - // Apply std::min: Result bits cannot exceed dividend bits - return std::min(maxBitsLeft, - std::max(0, maxBitsLeft - bitsRight + 1)); + case ShrSInt32: { + if (auto* shift = binary->right->dynCast()) { + auto maxBits = getMaxBits(binary->left); + // if left may be negative, the result may be negative + if (maxBits == 32) { + return 32; + } + auto shifts = + std::min(Index(getEffectiveShifts(shift)), + maxBits); // can ignore more shifts than zero us out + return std::max(Index(0), maxBits - shifts); + } + return 32; } - return maxBitsLeft; - } - case RemSInt64: { - if (auto* c = binary->right->dynCast()) { - auto maxBitsLeft = getMaxBits(binary->left, localInfoProvider); - // if left may be negative, the result may be negative - if (maxBitsLeft == 64) { - return 64; + case RotLInt64: + case RotRInt64: + case SubInt64: + return 64; + case AddInt64: { + auto maxBitsLeft = getMaxBits(binary->left); + auto maxBitsRight = getMaxBits(binary->right); + return std::min(Index(64), std::max(maxBitsLeft, maxBitsRight) + 1); + } + case MulInt64: { + auto maxBitsRight = getMaxBits(binary->right); + auto maxBitsLeft = getMaxBits(binary->left); + return std::min(Index(64), maxBitsLeft + maxBitsRight); + } + case DivSInt64: { + if (auto* c = binary->right->dynCast()) { + int32_t maxBitsLeft = getMaxBits(binary->left); + // if left or right const value is negative + if (maxBitsLeft == 64 || c->value.geti64() < 0) { + return 64; + } + int32_t bitsRight = getMaxBits(c); + // Apply std::min: Result bits cannot exceed dividend bits + return std::min(maxBitsLeft, + std::max(0, maxBitsLeft - bitsRight + 1)); } - auto bitsRight = Index(ceilLog2(c->value.geti64())); - return std::min(maxBitsLeft, bitsRight); + return 64; } - return 64; - } - case RemUInt64: { - if (auto* c = binary->right->dynCast()) { - auto maxBitsLeft = getMaxBits(binary->left, localInfoProvider); - auto bitsRight = Index(ceilLog2(c->value.geti64())); - return std::min(maxBitsLeft, bitsRight); + case DivUInt64: { + int32_t maxBitsLeft = getMaxBits(binary->left); + if (auto* c = binary->right->dynCast()) { + int32_t bitsRight = getMaxBits(c); + // Apply std::min: Result bits cannot exceed dividend bits + return std::min(maxBitsLeft, + std::max(0, maxBitsLeft - bitsRight + 1)); + } + return maxBitsLeft; } - return 64; - } - case AndInt64: { - return std::min(getMaxBits(binary->left, localInfoProvider), - getMaxBits(binary->right, localInfoProvider)); - } - case OrInt64: - case XorInt64: { - return std::max(getMaxBits(binary->left, localInfoProvider), - getMaxBits(binary->right, localInfoProvider)); - } - case ShlInt64: { - if (auto* shifts = binary->right->dynCast()) { - auto maxBits = getMaxBits(binary->left, localInfoProvider); - return std::min(Index(64), - Bits::getEffectiveShifts(shifts) + maxBits); + case RemSInt64: { + if (auto* c = binary->right->dynCast()) { + auto maxBitsLeft = getMaxBits(binary->left); + // if left may be negative, the result may be negative + if (maxBitsLeft == 64) { + return 64; + } + auto bitsRight = Index(ceilLog2(c->value.geti64())); + return std::min(maxBitsLeft, bitsRight); + } + return 64; } - return 64; - } - case ShrUInt64: { - if (auto* shift = binary->right->dynCast()) { - 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); + case RemUInt64: { + if (auto* c = binary->right->dynCast()) { + auto maxBitsLeft = getMaxBits(binary->left); + auto bitsRight = Index(ceilLog2(c->value.geti64())); + return std::min(maxBitsLeft, bitsRight); + } + return 64; } - return 64; - } - case ShrSInt64: { - if (auto* shift = binary->right->dynCast()) { - auto maxBits = getMaxBits(binary->left, localInfoProvider); - // if left may be negative, the result may be negative - if (maxBits == 64) { - return 64; + case AndInt64: { + return std::min(getMaxBits(binary->left), getMaxBits(binary->right)); + } + case OrInt64: + case XorInt64: { + return std::max(getMaxBits(binary->left), getMaxBits(binary->right)); + } + case ShlInt64: { + if (auto* shifts = binary->right->dynCast()) { + auto maxBits = getMaxBits(binary->left); + return std::min(Index(64), getEffectiveShifts(shifts) + maxBits); } - 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 ShrUInt64: { + if (auto* shift = binary->right->dynCast()) { + auto maxBits = getMaxBits(binary->left); + auto shifts = + std::min(Index(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()) { + auto maxBits = getMaxBits(binary->left); + // if left may be negative, the result may be negative + if (maxBits == 64) { + return 64; + } + auto shifts = + std::min(Index(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: + case LtSInt32: + case LtUInt32: + case LeSInt32: + case LeUInt32: + case GtSInt32: + case GtUInt32: + case GeSInt32: + case GeUInt32: + + case EqInt64: + case NeInt64: + case LtSInt64: + case LtUInt64: + case LeSInt64: + case LeUInt64: + case GtSInt64: + case GtUInt64: + case GeSInt64: + case GeUInt64: + + case EqFloat32: + case NeFloat32: + case LtFloat32: + case LeFloat32: + case GtFloat32: + case GeFloat32: + + case EqFloat64: + case NeFloat64: + case LtFloat64: + case LeFloat64: + case GtFloat64: + case GeFloat64: + WASM_UNREACHABLE("relationals handled before"); + default: { } - return 64; - } - // comparisons - case EqInt32: - case NeInt32: - case LtSInt32: - case LtUInt32: - case LeSInt32: - case LeUInt32: - case GtSInt32: - case GtUInt32: - case GeSInt32: - case GeUInt32: - - case EqInt64: - case NeInt64: - case LtSInt64: - case LtUInt64: - case LeSInt64: - case LeUInt64: - case GtSInt64: - case GtUInt64: - case GeSInt64: - case GeUInt64: - - case EqFloat32: - case NeFloat32: - case LtFloat32: - case LeFloat32: - case GtFloat32: - case GeFloat32: - - case EqFloat64: - case NeFloat64: - case LtFloat64: - case LeFloat64: - case GtFloat64: - case GeFloat64: - WASM_UNREACHABLE("relationals handled before"); - default: { - } - } - } else if (auto* unary = curr->dynCast()) { - switch (unary->op) { - case ClzInt32: - case CtzInt32: - case PopcntInt32: - return 6; - case ClzInt64: - case CtzInt64: - case PopcntInt64: - return 7; - case EqZInt32: - case EqZInt64: - WASM_UNREACHABLE("relationals handled before"); - case WrapInt64: - case ExtendUInt32: - return std::min(Index(32), getMaxBits(unary->value, localInfoProvider)); - case ExtendS8Int32: { - auto maxBits = getMaxBits(unary->value, localInfoProvider); - return maxBits >= 8 ? Index(32) : maxBits; - } - case ExtendS16Int32: { - auto maxBits = getMaxBits(unary->value, localInfoProvider); - return maxBits >= 16 ? Index(32) : maxBits; - } - case ExtendS8Int64: { - auto maxBits = getMaxBits(unary->value, localInfoProvider); - return maxBits >= 8 ? Index(64) : maxBits; } - case ExtendS16Int64: { - auto maxBits = getMaxBits(unary->value, localInfoProvider); - return maxBits >= 16 ? Index(64) : maxBits; + } else if (auto* unary = curr->dynCast()) { + switch (unary->op) { + case ClzInt32: + case CtzInt32: + case PopcntInt32: + return 6; + case ClzInt64: + case CtzInt64: + case PopcntInt64: + return 7; + case EqZInt32: + case EqZInt64: + WASM_UNREACHABLE("relationals handled before"); + case WrapInt64: + case ExtendUInt32: + return std::min(Index(32), getMaxBits(unary->value)); + case ExtendS8Int32: { + auto maxBits = getMaxBits(unary->value); + return maxBits >= 8 ? Index(32) : maxBits; + } + case ExtendS16Int32: { + auto maxBits = getMaxBits(unary->value); + return maxBits >= 16 ? Index(32) : maxBits; + } + case ExtendS8Int64: { + auto maxBits = getMaxBits(unary->value); + return maxBits >= 8 ? Index(64) : maxBits; + } + case ExtendS16Int64: { + auto maxBits = getMaxBits(unary->value); + return maxBits >= 16 ? Index(64) : maxBits; + } + case ExtendS32Int64: + case ExtendSInt32: { + auto maxBits = getMaxBits(unary->value); + return maxBits >= 32 ? Index(64) : maxBits; + } + default: { + } } - case ExtendS32Int64: - case ExtendSInt32: { - auto maxBits = getMaxBits(unary->value, localInfoProvider); - return maxBits >= 32 ? Index(64) : maxBits; + } else if (auto* set = curr->dynCast()) { + // a tee passes through the value + return getMaxBits(set->value); + } else if (auto* get = curr->dynCast()) { + return localInfoProvider.getMaxBitsForLocal(get); + } else if (auto* load = curr->dynCast()) { + // if signed, then the sign-extension might fill all the bits + // if unsigned, then we have a limit + if (LoadUtils::isSignRelevant(load) && !load->signed_) { + return 8 * load->bytes; } - default: { + } else if (auto* block = curr->dynCast()) { + if (!block->name.is() && !block->list.empty() && + block->type.isConcrete()) { + return getMaxBits(block->list.back()); } } - } else if (auto* set = curr->dynCast()) { - // a tee passes through the value - return getMaxBits(set->value, localInfoProvider); - } else if (auto* get = curr->dynCast()) { - // TODO: Should this be optional? - assert(localInfoProvider); - return localInfoProvider->getMaxBitsForLocal(get); - } else if (auto* load = curr->dynCast()) { - // if signed, then the sign-extension might fill all the bits - // if unsigned, then we have a limit - if (LoadUtils::isSignRelevant(load) && !load->signed_) { - return 8 * load->bytes; + switch (curr->type.getBasic()) { + case Type::i32: + return 32; + case Type::i64: + return 64; + case Type::unreachable: + return 64; // not interesting, but don't crash + default: + WASM_UNREACHABLE("invalid type"); } - } else if (auto* block = curr->dynCast()) { - if (!block->name.is() && !block->list.empty() && block->type.isConcrete()) { - return getMaxBits(block->list.back(), localInfoProvider); + } + + // As getMaxBits, but returns the minimum amount of bits. + Index getMinBits(Expression* curr) const { + if (auto* c = curr->dynCast()) { + // Constants are simple: the min and max are identical. + return getMaxBits(c); } + + // TODO: everything else + return 0; } - switch (curr->type.getBasic()) { - case Type::i32: - return 32; - case Type::i64: - return 64; - case Type::unreachable: - return 64; // not interesting, but don't crash - default: - WASM_UNREACHABLE("invalid type"); + + // If |curr| is known to be sign-extended, returns the number of low bits kept + // before sign-extending into the upper bits, or 0 otherwise. + Index getSignExtKeptBits(Expression* curr) const { + if (getSignExtValue(curr)) { + if (auto* unary = curr->dynCast()) { + switch (unary->op) { + case ExtendS8Int32: + case ExtendS8Int64: + return 8; + case ExtendS16Int32: + case ExtendS16Int64: + return 16; + case ExtendS32Int64: + return 32; + default: + WASM_UNREACHABLE("invalid unary operation"); + } + } else { + auto* rightShift = curr->cast()->right; + return curr->type.getByteSize() * 8 - getEffectiveShifts(rightShift); + } + } + if (auto* get = curr->dynCast()) { + return localInfoProvider.getSignExtKeptBitsForLocal(get); + } + return 0; } +}; + +inline Index getMaxBits(Expression* curr) { + return Provider().getMaxBits(curr); } -// As getMaxBits, but returns the minimum amount of bits. inline Index getMinBits(Expression* curr) { - if (auto* c = curr->dynCast()) { - // Constants are simple: the min and max are identical. - return getMaxBits(c); - } + return Provider().getMinBits(curr); +} - // TODO: everything else - return 0; +inline Index getSignExtKeptBits(Expression* curr) { + return Provider().getSignExtKeptBits(curr); } } // namespace wasm::Bits diff --git a/src/ir/properties.h b/src/ir/properties.h index 96d9786960f..3067b57518c 100644 --- a/src/ir/properties.h +++ b/src/ir/properties.h @@ -148,121 +148,6 @@ inline Literals getLiterals(const Expression* curr) { } } -// Check if an expression is a sign-extend, and if so, returns the value -// that is extended, otherwise nullptr -inline Expression* getSignExtValue(Expression* curr) { - // We only care about integers here, and ignore unreachables, etc. - if (!curr->type.isInteger()) { - return nullptr; - } - if (auto* unary = curr->dynCast()) { - switch (unary->op) { - case ExtendS8Int32: - case ExtendS16Int32: - case ExtendS8Int64: - case ExtendS16Int64: - case ExtendS32Int64: - // The operand may have become unreachable before refinalization updates - // `curr->type`. - return unary->value->type.isInteger() ? unary->value : nullptr; - default: - return nullptr; - } - } - using namespace Abstract; - using namespace Match; - int64_t leftShift = 0, rightShift = 0; - Expression* extended = nullptr; - if (matches(curr, - binary(ShrS, - binary(Shl, any(&extended), ival(&leftShift)), - ival(&rightShift)))) { - auto leftShifts = Bits::getEffectiveShifts(leftShift, curr->type); - auto rightShifts = Bits::getEffectiveShifts(rightShift, curr->type); - if (leftShifts != 0 && leftShifts == rightShifts) { - return extended; - } - } - return nullptr; -} - -// gets the size of the sign-extended value -inline Index getSignExtBits(Expression* curr) { - assert(curr->type.isInteger()); - if (auto* unary = curr->dynCast()) { - switch (unary->op) { - case ExtendS8Int32: - case ExtendS8Int64: - return 8; - case ExtendS16Int32: - case ExtendS16Int64: - return 16; - case ExtendS32Int64: - return 32; - default: - WASM_UNREACHABLE("invalid unary operation"); - } - } else { - auto* rightShift = curr->cast()->right; - return curr->type.getByteSize() * 8 - Bits::getEffectiveShifts(rightShift); - } -} - -// Check if an expression is almost a sign-extend: perhaps the inner shift -// is too large. We can split the shifts in that case, which is sometimes -// useful (e.g. if we can remove the signext) -inline Expression* getAlmostSignExt(Expression* curr) { - using namespace Abstract; - using namespace Match; - int64_t leftShift = 0, rightShift = 0; - Expression* extended = nullptr; - if (matches(curr, - binary(ShrS, - binary(Shl, any(&extended), ival(&leftShift)), - ival(&rightShift)))) { - auto leftShifts = Bits::getEffectiveShifts(leftShift, curr->type); - auto rightShifts = Bits::getEffectiveShifts(rightShift, curr->type); - if (rightShifts != 0 && rightShifts <= leftShifts) { - return extended; - } - } - return nullptr; -} - -// gets the size of the almost sign-extended value, as well as the -// extra shifts, if any -inline Index getAlmostSignExtBits(Expression* curr, Index& extraLeftShifts) { - auto* leftShift = curr->cast()->left->cast()->right; - auto* rightShift = curr->cast()->right; - extraLeftShifts = - Bits::getEffectiveShifts(leftShift) - Bits::getEffectiveShifts(rightShift); - return getSignExtBits(curr); -} - -// Check if an expression is a zero-extend, and if so, returns the value -// that is extended, otherwise nullptr -inline Expression* getZeroExtValue(Expression* curr) { - // We only care about i32s here, and ignore i64s, unreachables, etc. - if (curr->type != Type::i32) { - return nullptr; - } - using namespace Match; - int32_t mask = 0; - Expression* extended = nullptr; - if (matches(curr, binary(AndInt32, any(&extended), i32(&mask))) && - Bits::getMaskedBits(mask) != 0) { - return extended; - } - return nullptr; -} - -// gets the size of the sign-extended value -inline Index getZeroExtBits(Expression* curr) { - assert(curr->type == Type::i32); - int32_t mask = curr->cast()->right->cast()->value.geti32(); - return Bits::getMaskedBits(mask); -} - // Returns a falling-through value, that is, it looks through a local.tee // and other operations that receive a value and let it flow through them. If // there is no value falling through, returns the node itself (as that is the diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp index cfeb478acaa..097c2a78159 100644 --- a/src/passes/OptimizeInstructions.cpp +++ b/src/passes/OptimizeInstructions.cpp @@ -83,7 +83,7 @@ struct LocalInfo { static const Index kUnknown = Index(-1); Index maxBits = -1; - Index signExtBits = 0; + Index signExtKeptBits = 0; }; struct LocalScanner : PostWalker { @@ -101,9 +101,10 @@ struct LocalScanner : PostWalker { auto& info = localInfo[i]; if (func->isParam(i)) { info.maxBits = getBitsForType(func->getLocalType(i)); // worst-case - info.signExtBits = LocalInfo::kUnknown; // we will never know anything + // We will never know anything. + info.signExtKeptBits = LocalInfo::kUnknown; } else { - info.maxBits = info.signExtBits = 0; // we are open to learning + info.maxBits = info.signExtKeptBits = 0; // we are open to learning } } // walk @@ -111,8 +112,8 @@ struct LocalScanner : PostWalker { // finalize for (Index i = 0; i < func->getNumLocals(); i++) { auto& info = localInfo[i]; - if (info.signExtBits == LocalInfo::kUnknown) { - info.signExtBits = 0; + if (info.signExtKeptBits == LocalInfo::kUnknown) { + info.signExtKeptBits = 0; } } } @@ -130,27 +131,24 @@ struct LocalScanner : PostWalker { auto* value = Properties::getFallthrough(curr->value, passOptions, *getModule()); auto& info = localInfo[curr->index]; - info.maxBits = std::max(info.maxBits, Bits::getMaxBits(value, this)); + info.maxBits = std::max(info.maxBits, Bits::getMaxBits(value)); info.maxBits = std::min(info.maxBits, getBitsForType(type)); - auto signExtBits = LocalInfo::kUnknown; - if (Properties::getSignExtValue(value)) { - signExtBits = Properties::getSignExtBits(value); - } else if (auto* load = value->dynCast()) { - if (LoadUtils::isSignRelevant(load) && load->signed_) { - signExtBits = load->bytes * 8; + auto keptBits = Bits::getSignExtKeptBits(value); + if (keptBits == 0) { + if (auto* load = value->dynCast(); + load && LoadUtils::isSignRelevant(load) && load->signed_) { + keptBits = load->bytes * 8; + } else { + keptBits = LocalInfo::kUnknown; } } - if (info.signExtBits == 0) { - info.signExtBits = signExtBits; // first info we see - } else if (info.signExtBits != signExtBits) { + if (info.signExtKeptBits == 0) { + info.signExtKeptBits = keptBits; // first info we see + } else if (info.signExtKeptBits != keptBits) { // contradictory information, give up - info.signExtBits = LocalInfo::kUnknown; + info.signExtKeptBits = LocalInfo::kUnknown; } } - - // define this for the templated getMaxBits method. we know nothing here yet - // about locals, so return the maxes - Index getMaxBitsForLocal(LocalGet* get) { return getBitsForType(get->type); } }; namespace { @@ -219,7 +217,8 @@ template struct Match::Internal::MatchSelf> { // Main pass class struct OptimizeInstructions - : public WalkerPass> { + : public WalkerPass>, + public Bits::LocalInfoProvider { bool isFunctionParallel() override { return true; } std::unique_ptr create() override { @@ -334,7 +333,7 @@ struct OptimizeInstructions // We should never be called with a constant. assert(!curr->is()); - if (!curr->type.isInteger() || Bits::getMaxBits(curr, this) != 0) { + if (!curr->type.isInteger() || bits.getMaxBits(curr) != 0) { return nullptr; } @@ -596,29 +595,29 @@ struct OptimizeInstructions } } } - if (auto* ext = Properties::getAlmostSignExt(curr)) { + if (auto* ext = Bits::getAlmostSignExt(curr)) { Index extraLeftShifts; - auto bits = Properties::getAlmostSignExtBits(curr, extraLeftShifts); - if (extraLeftShifts == 0 && trySignExtLoad(ext, bits)) { + auto keptBits = Bits::getAlmostSignExtKeptBits(curr, extraLeftShifts); + if (extraLeftShifts == 0 && trySignExtLoad(ext, keptBits)) { return replaceCurrent(ext); } // We can in some cases remove part of a sign extend, that is, // (x << A) >> B => x << (A - B) // If the sign-extend input cannot have a sign bit, we don't need it. - if (Bits::getMaxBits(ext, this) + extraLeftShifts < bits) { + if (bits.getMaxBits(ext) + extraLeftShifts < keptBits) { return replaceCurrent(removeAlmostSignExt(curr)); } // We also don't need it if it already has an identical-sized sign // extend applied to it. That is, if it is already a sign-extended // value, then another sign extend will do nothing. We do need to be // careful of the extra shifts, though. - if (isSignExted(ext, bits) && extraLeftShifts == 0) { + if (hasSignExtKeptBits(ext, keptBits) && extraLeftShifts == 0) { return replaceCurrent(removeAlmostSignExt(curr)); } } else if (curr->op == EqInt32 || curr->op == NeInt32 || curr->op == EqInt64 || curr->op == NeInt64) { if (auto* c = curr->right->dynCast()) { - if (auto* ext = Properties::getSignExtValue(curr->left)) { + if (auto* ext = Bits::getSignExtValue(curr->left)) { // We are comparing a sign extend to a constant, which means we can // use a cheaper zero-extend in some cases. That is, // (x << S) >> S ==/!= C => x & T ==/!= C @@ -631,7 +630,7 @@ struct OptimizeInstructions // sign-extended, that is, its effective sign bit is spread to all // the upper bits, which means that the relevant bits on the left // side are either all 0, or all 1. - auto bits = Properties::getSignExtBits(curr->left); + auto bits = Bits::getSignExtKeptBits(curr->left); uint64_t right = c->value.getUnsigned(); Index totalBits = c->type.getByteSize() * 8; Index numRelevantBits = totalBits - bits + 1; @@ -668,10 +667,10 @@ struct OptimizeInstructions builder.makeSequence(builder.makeDrop(ext), c)); } } - } else if (auto* left = Properties::getSignExtValue(curr->left)) { - if (auto* right = Properties::getSignExtValue(curr->right)) { - auto bits = Properties::getSignExtBits(curr->left); - if (Properties::getSignExtBits(curr->right) == bits) { + } else if (auto* left = Bits::getSignExtValue(curr->left)) { + if (auto* right = Bits::getSignExtValue(curr->right)) { + auto bits = Bits::getSignExtKeptBits(curr->left); + if (Bits::getSignExtKeptBits(curr->right) == bits) { // we are comparing two sign-exts with the same bits, so we may as // well replace both with cheaper zexts curr->left = makeZeroExt(left, bits); @@ -681,7 +680,7 @@ struct OptimizeInstructions } else if (auto* load = curr->right->dynCast()) { // we are comparing a load to a sign-ext, we may be able to switch // to zext - auto leftBits = Properties::getSignExtBits(curr->left); + auto leftBits = Bits::getSignExtKeptBits(curr->left); if (load->signed_ && leftBits == load->bytes * 8) { load->signed_ = false; curr->left = makeZeroExt(left, leftBits); @@ -689,10 +688,10 @@ struct OptimizeInstructions } } } else if (auto* load = curr->left->dynCast()) { - if (auto* right = Properties::getSignExtValue(curr->right)) { + if (auto* right = Bits::getSignExtValue(curr->right)) { // we are comparing a load to a sign-ext, we may be able to switch // to zext - auto rightBits = Properties::getSignExtBits(curr->right); + auto rightBits = Bits::getSignExtKeptBits(curr->right); if (load->signed_ && rightBits == load->bytes * 8) { load->signed_ = false; curr->right = makeZeroExt(right, rightBits); @@ -740,7 +739,7 @@ struct OptimizeInstructions return replaceCurrent(left); } } - auto lowMask = Bits::lowBitMask64(Bits::getMaxBits(left, this)); + auto lowMask = Bits::lowBitMask64(bits.getMaxBits(left)); if ((mask & lowMask) == lowMask) { // A mask of more bits than can be present is not needed. return replaceCurrent(left); @@ -761,7 +760,7 @@ struct OptimizeInstructions // faster on VMs. Also this opens more possibilities for further // simplifications afterwards. if (c >= 0 && (op = makeUnsignedBinaryOp(curr->op)) != InvalidBinary && - Bits::getMaxBits(curr->left, this) <= 31) { + bits.getMaxBits(curr->left) <= 31) { curr->op = op; } if (c < 0 && c > std::numeric_limits::min() && @@ -791,7 +790,7 @@ struct OptimizeInstructions int64_t c = right->value.geti64(); // See description above for Type::i32 if (c >= 0 && (op = makeUnsignedBinaryOp(curr->op)) != InvalidBinary && - Bits::getMaxBits(curr->left, this) <= 63) { + bits.getMaxBits(curr->left) <= 63) { curr->op = op; } if (getPassOptions().shrinkLevel == 0 && c < 0 && @@ -953,7 +952,7 @@ struct OptimizeInstructions Unary* inner; Expression* x; if (matches(curr, unary(EqZInt32, unary(&inner, WrapInt64, any(&x)))) && - Bits::getMaxBits(x, this) <= 32) { + bits.getMaxBits(x) <= 32) { inner->op = EqZInt64; return replaceCurrent(inner); } @@ -984,7 +983,7 @@ struct OptimizeInstructions UnaryOp unaryOp; if (matches(curr, unary(&unaryOp, unary(WrapInt64, any(&x))))) { if (unaryOp == ExtendSInt32 || unaryOp == ExtendUInt32) { - auto maxBits = Bits::getMaxBits(x, this); + auto maxBits = bits.getMaxBits(x); if ((unaryOp == ExtendSInt32 && maxBits <= 31) || (unaryOp == ExtendUInt32 && maxBits <= 32)) { return replaceCurrent(x); @@ -1044,9 +1043,9 @@ struct OptimizeInstructions } } - if (auto* ext = Properties::getSignExtValue(curr)) { - auto bits = Properties::getSignExtBits(curr); - if (trySignExtLoad(ext, bits)) { + if (auto* ext = Bits::getSignExtValue(curr)) { + auto currBits = Bits::getSignExtKeptBits(curr); + if (trySignExtLoad(ext, currBits)) { return replaceCurrent(ext); } // Simple sign extends can be removed if the value is already @@ -1054,8 +1053,8 @@ struct OptimizeInstructions // larger sign-extend: if we have an 8-bit value in 32-bit, then there are // 24 sign bits, and doing a sign-extend to 16 will only affect 16 of // those 24, and the effect is to leave them as they are. - auto signExtBits = getSignExtBits(ext); - if (signExtBits > 0 && signExtBits <= bits) { + auto extBits = bits.getSignExtKeptBits(ext); + if (extBits > 0 && extBits <= currBits) { return replaceCurrent(ext); } } @@ -1094,10 +1093,10 @@ struct OptimizeInstructions } } // eqz of a sign extension can be of zero-extension - if (auto* ext = Properties::getSignExtValue(curr->value)) { + if (auto* ext = Bits::getSignExtValue(curr->value)) { // we are comparing a sign extend to a constant, which means we can // use a cheaper zext - auto bits = Properties::getSignExtBits(curr->value); + auto bits = Bits::getSignExtKeptBits(curr->value); curr->value = makeZeroExt(ext, bits); return replaceCurrent(curr); } @@ -1309,11 +1308,11 @@ struct OptimizeInstructions } } } - } else if (auto* ext = Properties::getSignExtValue(binary)) { + } else if (auto* ext = Bits::getSignExtValue(binary)) { // if sign extending the exact bit size we store, we can skip the // extension if extending something bigger, then we just alter bits we // don't save anyhow - if (Properties::getSignExtBits(binary) >= Index(bytes) * 8) { + if (Bits::getSignExtKeptBits(binary) >= Index(bytes) * 8) { value = ext; } } @@ -2980,7 +2979,7 @@ struct OptimizeInstructions } } - Index getMaxBitsForLocal(LocalGet* get) { + Index getMaxBitsForLocal(LocalGet* get) const override { // check what we know about the local (we may know nothing, if this local // was added after the pass scanned for locals; in that case, full // optimization may require another cycle) @@ -2990,9 +2989,17 @@ struct OptimizeInstructions return getBitsForType(get->type); } + Index getSignExtKeptBitsForLocal(LocalGet* get) const override { + if (get->index < localInfo.size()) { + return localInfo[get->index].signExtKeptBits; + } + return 0; + } + private: // Information about our locals std::vector localInfo; + Bits::Provider bits{this}; // Checks if the first is a local.tee and the second a local.get of that same // index. This is useful in the methods right below us, as it is a common @@ -3446,10 +3453,10 @@ struct OptimizeInstructions } } } - if (auto* ext = Properties::getSignExtValue(binary)) { + if (auto* ext = Bits::getSignExtValue(binary)) { // use a cheaper zero-extent, we just care about the boolean value // anyhow - return makeZeroExt(ext, Properties::getSignExtBits(binary)); + return makeZeroExt(ext, Bits::getSignExtKeptBits(binary)); } } else if (auto* block = boolean->dynCast()) { if (block->type == Type::i32 && block->list.size() > 0) { @@ -3572,10 +3579,9 @@ struct OptimizeInstructions } } } - if (curr->type == Type::i32 && - Bits::getMaxBits(curr->condition, this) <= 1 && - Bits::getMaxBits(curr->ifTrue, this) <= 1 && - Bits::getMaxBits(curr->ifFalse, this) <= 1) { + if (curr->type == Type::i32 && bits.getMaxBits(curr->condition) <= 1 && + bits.getMaxBits(curr->ifTrue) <= 1 && + bits.getMaxBits(curr->ifFalse) <= 1) { // The condition and both arms are i32 booleans, which allows us to do // boolean optimizations. Expression* x; @@ -4093,7 +4099,7 @@ struct OptimizeInstructions auto* right = curr->right; // Check left's max bits and right is constant. - auto leftMaxBits = Bits::getMaxBits(left, this); + auto leftMaxBits = bits.getMaxBits(left); uint64_t maskLeft; // leftMaxBits may be greater than size if the lhs is unreachable but has // not been refinalized yet. @@ -4443,22 +4449,10 @@ struct OptimizeInstructions return false; } - // Check if an expression is already sign-extended to an exact number of bits. - bool isSignExted(Expression* curr, Index bits) { - return getSignExtBits(curr) == bits; - } - - // Returns the number of bits an expression is sign-extended (or 0 if it is - // not). - Index getSignExtBits(Expression* curr) { - if (Properties::getSignExtValue(curr)) { - return Properties::getSignExtBits(curr); - } - if (auto* get = curr->dynCast()) { - // Check what we know about the local. - return localInfo[get->index].signExtBits; - } - return 0; + // Check if an expression is already sign-extended with an exact number of + // kept bits. + bool hasSignExtKeptBits(Expression* curr, Index keptBits) { + return bits.getSignExtKeptBits(curr) == keptBits; } // optimize trivial math operations, given that the right side of a binary @@ -4532,24 +4526,24 @@ struct OptimizeInstructions // i32(bool(x)) != 0 ==> i32(bool(x)) if ((matches(curr, binary(EqInt32, any(&left), i32(1))) || matches(curr, binary(NeInt32, any(&left), i32(0)))) && - Bits::getMaxBits(left, this) == 1) { + bits.getMaxBits(left) == 1) { return left; } // i64(bool(x)) == 1 ==> i32(bool(x)) // i64(bool(x)) != 0 ==> i32(bool(x)) if ((matches(curr, binary(EqInt64, any(&left), i64(1))) || matches(curr, binary(NeInt64, any(&left), i64(0)))) && - Bits::getMaxBits(left, this) == 1) { + bits.getMaxBits(left) == 1) { return builder.makeUnary(WrapInt64, left); } // bool(x) != 1 ==> !bool(x) if (matches(curr, binary(Ne, any(&left), ival(1))) && - Bits::getMaxBits(left, this) == 1) { + bits.getMaxBits(left) == 1) { return builder.makeUnary(Abstract::getUnary(type, EqZ), left); } // bool(x) ^ 1 ==> !bool(x) if (matches(curr, binary(Xor, any(&left), ival(1))) && - Bits::getMaxBits(left, this) == 1) { + bits.getMaxBits(left) == 1) { auto* result = builder.makeUnary(Abstract::getUnary(type, EqZ), left); if (left->type == Type::i64) { // Xor's result is also an i64 in this case, but EqZ returns i32, so we @@ -4563,7 +4557,7 @@ struct OptimizeInstructions } // bool(x) | 1 ==> 1 if (matches(curr, binary(Or, any(&left), ival(1))) && - Bits::getMaxBits(left, this) == 1) { + bits.getMaxBits(left) == 1) { return getDroppedChildrenAndAppend(curr, right); } @@ -4811,8 +4805,8 @@ struct OptimizeInstructions // If we know nothing about a limit on the amount of bits on either side, // give up. auto typeMaxBits = getBitsForType(binary->type); - auto leftMaxBits = Bits::getMaxBits(binary->left, this); - auto rightMaxBits = Bits::getMaxBits(binary->right, this); + auto leftMaxBits = bits.getMaxBits(binary->left); + auto rightMaxBits = bits.getMaxBits(binary->right); if (std::max(leftMaxBits, rightMaxBits) == typeMaxBits) { return true; } @@ -5251,11 +5245,11 @@ struct OptimizeInstructions { // Check if there is a nontrivial amount of bits on the left, which may // provide enough to optimize. - auto leftMaxBits = Bits::getMaxBits(curr->left, this); + auto leftMaxBits = bits.getMaxBits(curr->left); auto type = curr->left->type; if (leftMaxBits < getBitsForType(type)) { using namespace Abstract; - auto rightMinBits = Bits::getMinBits(curr->right); + auto rightMinBits = bits.getMinBits(curr->right); auto rightIsNegative = rightMinBits == getBitsForType(type); if (leftMaxBits < rightMinBits) { // There are not enough bits on the left for it to be equal to the @@ -5407,7 +5401,7 @@ struct OptimizeInstructions } case EqZInt32: { // eqz(eqz(bool(x))) ==> bool(x) - if (Bits::getMaxBits(unaryInner->value, this) == 1) { + if (bits.getMaxBits(unaryInner->value) == 1) { return unaryInner->value; } break; diff --git a/src/passes/PickLoadSigns.cpp b/src/passes/PickLoadSigns.cpp index f04f8451ca0..1b4feb6d46e 100644 --- a/src/passes/PickLoadSigns.cpp +++ b/src/passes/PickLoadSigns.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include +#include "ir/bits.h" #include #include @@ -73,16 +73,16 @@ struct PickLoadSigns : public WalkerPass> { } auto* parent = expressionStack[expressionStack.size() - i]; - if (Properties::getZeroExtValue(parent) == curr) { - auto bits = Properties::getZeroExtBits(parent); + if (Bits::getZeroExtValue(parent) == curr) { + auto bits = Bits::getZeroExtKeptBits(parent); if (usage.unsignedUsages == 0) { usage.unsignedBits = bits; } else if (usage.unsignedBits != bits) { usage.unsignedBits = 0; } usage.unsignedUsages++; - } else if (Properties::getSignExtValue(parent) == curr) { - auto bits = Properties::getSignExtBits(parent); + } else if (Bits::getSignExtValue(parent) == curr) { + auto bits = Bits::getSignExtKeptBits(parent); if (usage.signedUsages == 0) { usage.signedBits = bits; } else if (usage.signedBits != bits) {