From 731402ab0ea7ad1ab975f484cf6465323ed6ce95 Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 6 Nov 2026 17:46:46 -0800 Subject: [PATCH 1/2] [NFC] Move larger bits.h functions into bits.cpp Move makeSignExt, makePackedFieldGet, getSignExtValue, getAlmostSignExt, getZeroExtValue, Provider::getMaxBits, and Provider::getSignExtKeptBits out of src/ir/bits.h into src/ir/bits.cpp, and add missing direct #includes to files that previously relied on transitive includes from bits.h. --- src/cfg/cfg-traversal.h | 1 + src/ir/CMakeLists.txt | 1 + src/ir/bits.cpp | 489 ++++++++++++++++++++++++++++++++++ src/ir/bits.h | 475 +-------------------------------- src/passes/CoalesceLocals.cpp | 1 + src/passes/call-utils.h | 2 +- test/gtest/stringify.cpp | 1 + test/gtest/subtype-exprs.cpp | 1 + 8 files changed, 509 insertions(+), 462 deletions(-) create mode 100644 src/ir/bits.cpp diff --git a/src/cfg/cfg-traversal.h b/src/cfg/cfg-traversal.h index 43e2c188d48..7f5b46ce0b4 100644 --- a/src/cfg/cfg-traversal.h +++ b/src/cfg/cfg-traversal.h @@ -31,6 +31,7 @@ #define cfg_traversal_h #include "ir/branch-utils.h" +#include "shared-constants.h" #include "wasm-traversal.h" #include "wasm.h" diff --git a/src/ir/CMakeLists.txt b/src/ir/CMakeLists.txt index 4bedfc886ec..abf1bbc0d4d 100644 --- a/src/ir/CMakeLists.txt +++ b/src/ir/CMakeLists.txt @@ -1,6 +1,7 @@ FILE(GLOB ir_HEADERS *.h) set(ir_SOURCES abstract.cpp + bits.cpp ExpressionAnalyzer.cpp ExpressionManipulator.cpp constraint.cpp diff --git a/src/ir/bits.cpp b/src/ir/bits.cpp new file mode 100644 index 00000000000..10b6cdbb290 --- /dev/null +++ b/src/ir/bits.cpp @@ -0,0 +1,489 @@ +/* + * Copyright 2026 WebAssembly Community Group participants + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ir/bits.h" +#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" + +namespace wasm::Bits { + +Expression* makeSignExt(Expression* value, Index bytes, Module& wasm) { + if (value->type == Type::i32) { + if (bytes == 1 || bytes == 2) { + auto shifts = bytes == 1 ? 24 : 16; + Builder builder(wasm); + return builder.makeBinary( + ShrSInt32, + builder.makeBinary( + ShlInt32, + value, + LiteralUtils::makeFromInt32(shifts, Type::i32, wasm)), + LiteralUtils::makeFromInt32(shifts, Type::i32, wasm)); + } + assert(bytes == 4); + return value; // nothing to do + } else { + assert(value->type == Type::i64); + if (bytes == 1 || bytes == 2 || bytes == 4) { + auto shifts = bytes == 1 ? 56 : (bytes == 2 ? 48 : 32); + Builder builder(wasm); + return builder.makeBinary( + ShrSInt64, + builder.makeBinary( + ShlInt64, + value, + LiteralUtils::makeFromInt32(shifts, Type::i64, wasm)), + LiteralUtils::makeFromInt32(shifts, Type::i64, wasm)); + } + assert(bytes == 8); + return value; // nothing to do + } +} + +Expression* makePackedFieldGet(Expression* value, + const Field& field, + bool signed_, + Module& wasm) { + if (!field.isPacked()) { + return value; + } + + if (signed_) { + return makeSignExt(value, field.getByteSize(), wasm); + } + + Builder builder(wasm); + auto mask = Bits::lowBitMask(field.getByteSize() * 8); + return builder.makeBinary(AndInt32, value, builder.makeConst(int32_t(mask))); +} + +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; +} + +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; +} + +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; +} + +Index Provider::getMaxBits(Expression* curr) const { + 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"); + } + } 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)); + } + return 32; + } + 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; + } + 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); + } + return 32; + } + 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; + } + case AndInt32: { + return std::min(getMaxBits(binary->left), getMaxBits(binary->right)); + } + case OrInt32: + case XorInt32: { + return std::max(getMaxBits(binary->left), getMaxBits(binary->right)); + } + case ShlInt32: { + if (auto* shifts = binary->right->dynCast()) { + return std::min( + Index(32), getMaxBits(binary->left) + getEffectiveShifts(shifts)); + } + return 32; + } + 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); + } + return 32; + } + 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; + } + 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)); + } + return 64; + } + 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; + } + 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; + } + 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; + } + 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); + } + 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: { + } + } + } 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: { + } + } + } 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; + } + } else if (auto* block = curr->dynCast()) { + if (!block->name.is() && !block->list.empty() && block->type.isConcrete()) { + return getMaxBits(block->list.back()); + } + } + 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"); + } +} + +Index Provider::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; +} + +} // namespace wasm::Bits diff --git a/src/ir/bits.h b/src/ir/bits.h index a894fb75bab..39d4def9636 100644 --- a/src/ir/bits.h +++ b/src/ir/bits.h @@ -17,12 +17,11 @@ #ifndef wasm_ir_bits_h #define wasm_ir_bits_h -#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" +#include "support/index.h" +#include "support/utilities.h" +#include "wasm-type.h" +#include "wasm.h" namespace wasm::Bits { @@ -89,38 +88,7 @@ inline Index getEffectiveShifts(Expression* expr) { WASM_UNREACHABLE("unexpected type"); } -inline Expression* makeSignExt(Expression* value, Index bytes, Module& wasm) { - if (value->type == Type::i32) { - if (bytes == 1 || bytes == 2) { - auto shifts = bytes == 1 ? 24 : 16; - Builder builder(wasm); - return builder.makeBinary( - ShrSInt32, - builder.makeBinary( - ShlInt32, - value, - LiteralUtils::makeFromInt32(shifts, Type::i32, wasm)), - LiteralUtils::makeFromInt32(shifts, Type::i32, wasm)); - } - assert(bytes == 4); - return value; // nothing to do - } else { - assert(value->type == Type::i64); - if (bytes == 1 || bytes == 2 || bytes == 4) { - auto shifts = bytes == 1 ? 56 : (bytes == 2 ? 48 : 32); - Builder builder(wasm); - return builder.makeBinary( - ShrSInt64, - builder.makeBinary( - ShlInt64, - value, - LiteralUtils::makeFromInt32(shifts, Type::i64, wasm)), - LiteralUtils::makeFromInt32(shifts, Type::i64, wasm)); - } - assert(bytes == 8); - return value; // nothing to do - } -} +Expression* makeSignExt(Expression* value, Index bytes, Module& wasm); // Given a value that is read from a field, as a replacement for a // StructGet/ArrayGet that we inferred the value of, and given the signedness of @@ -129,81 +97,19 @@ inline Expression* makeSignExt(Expression* value, Index bytes, Module& wasm) { // where we can replace the StructGet/ArrayGet with the value we know must be // there (without making any assumptions about |value|, that is, we do not // assume it has been truncated already). -inline Expression* makePackedFieldGet(Expression* value, - const Field& field, - bool signed_, - Module& wasm) { - if (!field.isPacked()) { - return value; - } - - if (signed_) { - return makeSignExt(value, field.getByteSize(), wasm); - } - - Builder builder(wasm); - auto mask = Bits::lowBitMask(field.getByteSize() * 8); - return builder.makeBinary(AndInt32, value, builder.makeConst(int32_t(mask))); -} +Expression* makePackedFieldGet(Expression* value, + const Field& field, + bool signed_, + Module& wasm); // 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; -} +Expression* getSignExtValue(Expression* curr); // 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; -} +Expression* getAlmostSignExt(Expression* curr); // 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 @@ -219,20 +125,7 @@ inline Index getAlmostSignExtKeptBits(Expression* 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))) && - getMaskedBits(mask) != 0) { - return extended; - } - return nullptr; -} +Expression* getZeroExtValue(Expression* curr); // Returns the number of low bits kept by a zero-extend operation. inline Index getZeroExtKeptBits(Expression* curr) { @@ -275,323 +168,7 @@ class Provider { // 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; - } - 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"); - } - } 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)); - } - return 32; - } - 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; - } - 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); - } - return 32; - } - 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; - } - case AndInt32: { - return std::min(getMaxBits(binary->left), getMaxBits(binary->right)); - } - case OrInt32: - case XorInt32: { - return std::max(getMaxBits(binary->left), getMaxBits(binary->right)); - } - case ShlInt32: { - if (auto* shifts = binary->right->dynCast()) { - return std::min( - Index(32), getMaxBits(binary->left) + getEffectiveShifts(shifts)); - } - return 32; - } - 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); - } - return 32; - } - 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; - } - 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)); - } - return 64; - } - 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; - } - 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; - } - 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; - } - 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); - } - 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: { - } - } - } 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: { - } - } - } 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; - } - } else if (auto* block = curr->dynCast()) { - if (!block->name.is() && !block->list.empty() && - block->type.isConcrete()) { - return getMaxBits(block->list.back()); - } - } - 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"); - } - } + Index getMaxBits(Expression* curr) const; // As getMaxBits, but returns the minimum amount of bits. Index getMinBits(Expression* curr) const { @@ -606,31 +183,7 @@ class Provider { // 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; - } + Index getSignExtKeptBits(Expression* curr) const; }; inline Index getMaxBits(Expression* curr) { diff --git a/src/passes/CoalesceLocals.cpp b/src/passes/CoalesceLocals.cpp index 438528008fb..0d37c6410f2 100644 --- a/src/passes/CoalesceLocals.cpp +++ b/src/passes/CoalesceLocals.cpp @@ -30,6 +30,7 @@ #include #include "cfg/liveness-traversal.h" +#include "ir/literal-utils.h" #include "ir/numbering.h" #include "ir/utils.h" #include "pass.h" diff --git a/src/passes/call-utils.h b/src/passes/call-utils.h index 650c6a4e3ac..83fe087a27d 100644 --- a/src/passes/call-utils.h +++ b/src/passes/call-utils.h @@ -20,7 +20,7 @@ #include #include "ir/debuginfo.h" -#include "ir/type-updating.h" +#include "wasm-builder.h" #include "wasm.h" namespace wasm::CallUtils { diff --git a/test/gtest/stringify.cpp b/test/gtest/stringify.cpp index f464b5fe0f1..f4808a12301 100644 --- a/test/gtest/stringify.cpp +++ b/test/gtest/stringify.cpp @@ -17,6 +17,7 @@ #include #include "passes/stringify-walker.h" +#include "wasm-builder.h" #include "wasm-type.h" #include "wasm.h" #include "gtest/gtest.h" diff --git a/test/gtest/subtype-exprs.cpp b/test/gtest/subtype-exprs.cpp index e3e87ec2d6a..2cf2877f07c 100644 --- a/test/gtest/subtype-exprs.cpp +++ b/test/gtest/subtype-exprs.cpp @@ -15,6 +15,7 @@ */ #include "ir/subtype-exprs.h" +#include "wasm-builder.h" #include "wasm-traversal.h" #include "wasm.h" #include "gtest/gtest.h" From b1ce7b8405c1b6777323332ae693543c98d93d5f Mon Sep 17 00:00:00 2001 From: Thomas Lively Date: Fri, 6 Nov 2026 18:08:54 -0800 Subject: [PATCH 2/2] more iwyu --- src/cfg/cfg-traversal.h | 12 ++++++++++++ src/ir/bits.cpp | 9 +++++++++ src/ir/bits.h | 3 +++ src/passes/CoalesceLocals.cpp | 9 +++++++++ src/passes/call-utils.h | 4 ++++ test/gtest/stringify.cpp | 5 +++++ test/gtest/subtype-exprs.cpp | 6 ++++++ 7 files changed, 48 insertions(+) diff --git a/src/cfg/cfg-traversal.h b/src/cfg/cfg-traversal.h index 7f5b46ce0b4..290220cc56a 100644 --- a/src/cfg/cfg-traversal.h +++ b/src/cfg/cfg-traversal.h @@ -30,8 +30,20 @@ #ifndef cfg_traversal_h #define cfg_traversal_h +#include +#include +#include +#include +#include +#include +#include +#include + #include "ir/branch-utils.h" +#include "ir/properties.h" #include "shared-constants.h" +#include "support/name.h" +#include "support/utilities.h" #include "wasm-traversal.h" #include "wasm.h" diff --git a/src/ir/bits.cpp b/src/ir/bits.cpp index 10b6cdbb290..abce4c4cb60 100644 --- a/src/ir/bits.cpp +++ b/src/ir/bits.cpp @@ -14,13 +14,22 @@ * limitations under the License. */ +#include +#include +#include + +#include "ir/abstract.h" #include "ir/bits.h" #include "ir/boolean.h" #include "ir/literal-utils.h" #include "ir/load-utils.h" #include "ir/match.h" #include "support/bits.h" +#include "support/index.h" +#include "support/utilities.h" #include "wasm-builder.h" +#include "wasm-type.h" +#include "wasm.h" namespace wasm::Bits { diff --git a/src/ir/bits.h b/src/ir/bits.h index 39d4def9636..b70b0ad2bb6 100644 --- a/src/ir/bits.h +++ b/src/ir/bits.h @@ -17,6 +17,9 @@ #ifndef wasm_ir_bits_h #define wasm_ir_bits_h +#include +#include + #include "support/bits.h" #include "support/index.h" #include "support/utilities.h" diff --git a/src/passes/CoalesceLocals.cpp b/src/passes/CoalesceLocals.cpp index 0d37c6410f2..95637a4d766 100644 --- a/src/passes/CoalesceLocals.cpp +++ b/src/passes/CoalesceLocals.cpp @@ -26,17 +26,26 @@ // #include +#include +#include +#include +#include #include +#include #include +#include #include "cfg/liveness-traversal.h" #include "ir/literal-utils.h" +#include "ir/manipulation.h" #include "ir/numbering.h" #include "ir/utils.h" +#include "literal.h" #include "pass.h" #include "support/learning.h" #include "support/permutations.h" #include "support/sparse_square_matrix.h" +#include "wasm-traversal.h" #include "wasm.h" #ifndef CFG_PROFILE diff --git a/src/passes/call-utils.h b/src/passes/call-utils.h index 83fe087a27d..9fb08687f4c 100644 --- a/src/passes/call-utils.h +++ b/src/passes/call-utils.h @@ -17,10 +17,14 @@ #ifndef wasm_passes_call_utils_h #define wasm_passes_call_utils_h +#include #include +#include #include "ir/debuginfo.h" +#include "support/name.h" #include "wasm-builder.h" +#include "wasm-type.h" #include "wasm.h" namespace wasm::CallUtils { diff --git a/test/gtest/stringify.cpp b/test/gtest/stringify.cpp index f4808a12301..3c25511daf6 100644 --- a/test/gtest/stringify.cpp +++ b/test/gtest/stringify.cpp @@ -14,9 +14,14 @@ * limitations under the License. */ +#include +#include +#include #include +#include #include "passes/stringify-walker.h" +#include "support/name.h" #include "wasm-builder.h" #include "wasm-type.h" #include "wasm.h" diff --git a/test/gtest/subtype-exprs.cpp b/test/gtest/subtype-exprs.cpp index 2cf2877f07c..6a4f5f3651c 100644 --- a/test/gtest/subtype-exprs.cpp +++ b/test/gtest/subtype-exprs.cpp @@ -14,9 +14,15 @@ * limitations under the License. */ +#include +#include +#include + #include "ir/subtype-exprs.h" +#include "literal.h" #include "wasm-builder.h" #include "wasm-traversal.h" +#include "wasm-type.h" #include "wasm.h" #include "gtest/gtest.h"