16 if (v8_flags.turboshaft_trace_unrolling) StdoutStream() << x << std::endl; \
30 TRACE(
"LoopUnrollingAnalyzer: loop at "
31 <<
start->index() <<
" ==> iter_count=" << iter_count);
65 if (if_true_header == if_false_header) {
72 bool loop_if_cond_is = if_true_header ==
start;
81 OpIndex* phi, uint64_t* cst)
const {
94 if (
matcher_.MatchUnsignedIntegralConstant(right, cst)) {
98 }
else if (
matcher_.MatchPhi(right, 2)) {
99 if (
matcher_.MatchUnsignedIntegralConstant(left, cst)) {
115 *left = binop->left();
116 *right = binop->right();
118 *binop_rep = binop->rep;
139 const Block* header,
OpIndex cond_idx,
bool loop_if_cond_is)
const {
157 if (
matcher_.MatchUnsignedIntegralConstant(phi.input(0), &phi_cst)) {
163 if (
MatchWordBinop(phi.input(1), &left, &right, &binop_op, &binop_rep) ||
168 if (left == phi_idx) {
172 if (
matcher_.MatchUnsignedIntegralConstant(right, &binop_cst)) {
176 binop_rep, loop_if_cond_is);
178 }
else if (right == phi_idx) {
182 if (
matcher_.MatchUnsignedIntegralConstant(left, &binop_cst)) {
186 binop_rep, loop_if_cond_is);
198 switch (binop_kind) {
223 return BinOp::kBitwiseAnd;
225 return BinOp::kBitwiseOr;
227 return BinOp::kBitwiseXor;
238 return BinOp::kOverflowCheckedAdd;
240 return BinOp::kOverflowCheckedMul;
242 return BinOp::kOverflowCheckedSub;
247 if (
count.IsExact()) {
248 return os <<
"Exact[" <<
count.exact_count() <<
"]";
249 }
else if (
count.IsApprox()) {
250 return os <<
"Approx[" <<
count.approx_count() <<
"]";
253 return os <<
"Unknown";
261 case CmpOp::kSignedLessThan:
263 case CmpOp::kSignedLessThanOrEqual:
265 case CmpOp::kUnsignedLessThan:
267 case CmpOp::kUnsignedLessThanOrEqual:
269 case CmpOp::kSignedGreaterThan:
271 case CmpOp::kSignedGreaterThanOrEqual:
273 case CmpOp::kUnsignedGreaterThan:
275 case CmpOp::kUnsignedGreaterThanOrEqual:
288 case BinOp::kBitwiseAnd:
290 case BinOp::kBitwiseOr:
292 case BinOp::kBitwiseXor:
294 case BinOp::kOverflowCheckedAdd:
296 case BinOp::kOverflowCheckedMul:
298 case BinOp::kOverflowCheckedSub:
306std::optional<Int> Next(Int val, Int incr,
310 case BinOp::kBitwiseAnd:
312 case BinOp::kBitwiseOr:
314 case BinOp::kBitwiseXor:
319#define CASE_ARITH(op) \
321 case BinOp::kOverflowChecked##op: { \
322 if (binop_rep == WordRepresentation::Word32()) { \
324 if (base::bits::Signed##op##Overflow32( \
325 static_cast<int32_t>(val), static_cast<int32_t>(incr), &res)) { \
326 return std::nullopt; \
328 return static_cast<Int>(res); \
330 DCHECK_EQ(binop_rep, WordRepresentation::Word64()); \
332 if (base::bits::Signed##op##Overflow64(val, incr, &res)) { \
333 return std::nullopt; \
335 return static_cast<Int>(res); \
346bool Cmp(Int val, Int max,
CmpOp cmp_op) {
348 case CmpOp::kSignedLessThan:
349 case CmpOp::kUnsignedLessThan:
351 case CmpOp::kSignedLessThanOrEqual:
352 case CmpOp::kUnsignedLessThanOrEqual:
354 case CmpOp::kSignedGreaterThan:
355 case CmpOp::kUnsignedGreaterThan:
357 case CmpOp::kSignedGreaterThanOrEqual:
358 case CmpOp::kUnsignedGreaterThanOrEqual:
366bool SubWillOverflow(Int lhs, Int rhs) {
367 if constexpr (std::is_same_v<Int, int32_t> || std::is_same_v<Int, uint32_t>) {
371 static_assert(std::is_same_v<Int, int64_t> ||
372 std::is_same_v<Int, uint64_t>);
379bool DivWillOverflow(Int dividend, Int divisor) {
380 if constexpr (std::is_unsigned_v<Int>) {
383 return dividend == std::numeric_limits<Int>::min() && divisor == -1;
394 Int init, Int max,
CmpOp cmp_op, Int binop_cst,
BinOp binop_op,
396 static_assert(std::is_integral_v<Int>);
398 (cmp_op == CmpOp::kUnsignedLessThan ||
399 cmp_op == CmpOp::kUnsignedLessThanOrEqual ||
400 cmp_op == CmpOp::kUnsignedGreaterThan ||
401 cmp_op == CmpOp::kUnsignedGreaterThanOrEqual));
412 size_t iter_count = 0;
414 if (Cmp(curr, max, cmp_op) != loop_if_cond_is) {
417 if (
auto next = Next(curr, binop_cst, binop_op, binop_rep)) {
425 if (binop_cst == 0) {
434 if (binop_op == StaticCanonicalForLoopMatcher::BinOp::kAdd) {
436 any_of(CmpOp::kUnsignedLessThan, CmpOp::kUnsignedLessThanOrEqual,
437 CmpOp::kSignedLessThan, CmpOp::kSignedLessThanOrEqual) &&
438 init < max && !SubWillOverflow(max, init) && loop_if_cond_is) {
445 DCHECK(!DivWillOverflow(max - init, binop_cst));
446 Int quotient = (max - init) / binop_cst;
450 if (cmp_op ==
any_of(CmpOp::kUnsignedGreaterThan,
451 CmpOp::kUnsignedGreaterThanOrEqual,
452 CmpOp::kSignedGreaterThan,
453 CmpOp::kSignedGreaterThanOrEqual) &&
454 init > max && !SubWillOverflow(max, init) && loop_if_cond_is) {
461 if (DivWillOverflow(max - init, binop_cst))
return {};
462 Int quotient = (max - init) / binop_cst;
466 if (cmp_op == CmpOp::kEqual && !SubWillOverflow(max, init) &&
470 if (init < max && binop_cst < 0) {
475 if (init > max && binop_cst > 0) {
481 Int
remainder = (max - init) % binop_cst;
488 Int quotient = (max - init) / binop_cst;
501 uint64_t cmp_cst,
CmpOp cmp_op, uint64_t initial_input, uint64_t binop_cst,
504 case CmpOp::kSignedLessThan:
505 case CmpOp::kSignedLessThanOrEqual:
506 case CmpOp::kSignedGreaterThan:
507 case CmpOp::kSignedGreaterThanOrEqual:
511 static_cast<int32_t
>(initial_input),
static_cast<int32_t
>(cmp_cst),
512 cmp_op,
static_cast<int32_t
>(binop_cst), binop_op, binop_rep,
517 static_cast<int64_t
>(initial_input),
static_cast<int64_t
>(cmp_cst),
518 cmp_op,
static_cast<int64_t
>(binop_cst), binop_op, binop_rep,
521 case CmpOp::kUnsignedLessThan:
522 case CmpOp::kUnsignedLessThanOrEqual:
523 case CmpOp::kUnsignedGreaterThan:
524 case CmpOp::kUnsignedGreaterThanOrEqual:
527 static_cast<uint32_t
>(initial_input),
528 static_cast<uint32_t
>(cmp_cst), cmp_op,
529 static_cast<uint32_t
>(binop_cst), binop_op, binop_rep,
534 binop_cst, binop_op, binop_rep,
544 return CmpOp::kEqual;
546 return CmpOp::kSignedLessThan;
548 return CmpOp::kSignedLessThanOrEqual;
550 return CmpOp::kUnsignedLessThan;
552 return CmpOp::kUnsignedLessThanOrEqual;
559 return CmpOp::kEqual;
560 case CmpOp::kSignedLessThan:
561 return CmpOp::kSignedGreaterThan;
562 case CmpOp::kSignedLessThanOrEqual:
563 return CmpOp::kSignedGreaterThanOrEqual;
564 case CmpOp::kUnsignedLessThan:
565 return CmpOp::kUnsignedGreaterThan;
566 case CmpOp::kUnsignedLessThanOrEqual:
567 return CmpOp::kUnsignedGreaterThanOrEqual;
568 case CmpOp::kSignedGreaterThan:
569 return CmpOp::kSignedLessThan;
570 case CmpOp::kSignedGreaterThanOrEqual:
571 return CmpOp::kSignedLessThanOrEqual;
572 case CmpOp::kUnsignedGreaterThan:
573 return CmpOp::kUnsignedLessThan;
574 case CmpOp::kUnsignedGreaterThanOrEqual:
575 return CmpOp::kUnsignedLessThanOrEqual;
bool Contains(OpIndex op_idx) const
static IterationCount Approx(size_t count)
bool IsSmallerThan(size_t max)
static IterationCount Exact(size_t count)
const Block * GetLoopHeader(const Block *block) const
const ZoneUnorderedMap< const Block *, LoopInfo > & LoopHeaders() const
ZoneAbslFlatHashSet< uint32_t > & stack_checks_to_remove_
bool ShouldPartiallyUnrollLoop(const Block *loop_header) const
static constexpr size_t kMaxIterForStackCheckRemoval
void DetectUnrollableLoops()
bool ShouldFullyUnrollLoop(const Block *loop_header) const
bool can_unroll_at_least_one_loop_
IterationCount GetLoopIterationCount(const LoopFinder::LoopInfo &info) const
const StaticCanonicalForLoopMatcher canonical_loop_matcher_
ZoneUnorderedMap< const Block *, IterationCount > loop_iteration_count_
IterationCount GetIterCountIfStaticCanonicalForLoop(const Block *header, OpIndex cond_idx, bool loop_if_cond_is) const
static constexpr bool BinopKindIsSupported(WordBinopOp::Kind binop_kind)
const OperationMatcher & matcher_
bool MatchPhiCompareCst(OpIndex cond_idx, StaticCanonicalForLoopMatcher::CmpOp *cmp_op, OpIndex *phi, uint64_t *cst) const
bool MatchCheckedOverflowBinop(OpIndex idx, V< Word > *left, V< Word > *right, BinOp *binop_op, WordRepresentation *binop_rep) const
IterationCount CountIterationsImpl(Int init, Int max, CmpOp cmp_op, Int binop_cst, StaticCanonicalForLoopMatcher::BinOp binop_op, WordRepresentation binop_rep, bool loop_if_cond_is) const
IterationCount CountIterations(uint64_t equal_cst, CmpOp cmp_op, uint64_t initial_input, uint64_t binop_cst, BinOp binop_op, WordRepresentation binop_rep, bool loop_if_cond_is) const
static constexpr CmpOp ComparisonKindToCmpOp(ComparisonOp::Kind kind)
static constexpr CmpOp InvertComparisonOp(CmpOp op)
static constexpr size_t kMaxExactIter
static constexpr BinOp BinopFromOverflowCheckedBinopKind(OverflowCheckedBinopOp::Kind kind)
bool MatchWordBinop(OpIndex idx, V< Word > *left, V< Word > *right, BinOp *binop_op, WordRepresentation *binop_rep) const
static constexpr BinOp BinopFromWordBinopKind(WordBinopOp::Kind kind)
static constexpr WordRepresentation Word32()
static constexpr WordRepresentation Word64()
bool SignedSubOverflow64(int64_t lhs, int64_t rhs, int64_t *val)
bool SignedSubOverflow32(int32_t lhs, int32_t rhs, int32_t *val)
std::ostream & operator<<(std::ostream &os, PaddingSpace padding)
any_of(const Args &...) -> any_of< Args... >
StaticCanonicalForLoopMatcher::CmpOp CmpOp
#define DCHECK_LE(v1, v2)
#define DCHECK_GE(v1, v2)
#define DCHECK(condition)
#define DCHECK_EQ(v1, v2)
V< Word32 > condition() const
@ kUnsignedLessThanOrEqual
V8_INLINE OpIndex input(size_t i) const
const underlying_operation_t< Op > * TryCast() const
static constexpr int kValueIndex