From f52dfa6a1da5c58361e5fa229b98c7a7d0e031cc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 13:07:19 +0000 Subject: [PATCH 01/11] Initial plan From c0052042df60d2d68cbaa482eef73b6f1317dddf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 13:15:16 +0000 Subject: [PATCH 02/11] Expand fuzz_simplify shuffle generation Co-authored-by: alexreinking <169273+alexreinking@users.noreply.github.com> --- test/correctness/fuzz_simplify.cpp | 68 ++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/test/correctness/fuzz_simplify.cpp b/test/correctness/fuzz_simplify.cpp index 4154773dd1ec..d674f500ed4f 100644 --- a/test/correctness/fuzz_simplify.cpp +++ b/test/correctness/fuzz_simplify.cpp @@ -53,6 +53,16 @@ int get_random_divisor(RandomEngine &rng, Type t) { return random_choice(rng, divisors); } +int random_vector_width(RandomEngine &rng, int min_lanes = 2) { + std::vector widths; + for (int width : {2, 3, 4, 6, 8}) { + if (width >= min_lanes) { + widths.push_back(width); + } + } + return random_choice(rng, widths); +} + Expr random_leaf(RandomEngine &rng, Type t, bool overflow_undef = false, bool imm_only = false) { if (t.is_int() && t.bits() == 32) { overflow_undef = true; @@ -85,6 +95,61 @@ Expr random_leaf(RandomEngine &rng, Type t, bool overflow_undef = false, bool im Expr random_expr(RandomEngine &rng, Type t, int depth, bool overflow_undef = false); +Expr random_shuffle_expr(RandomEngine &rng, Type t, int depth, bool overflow_undef) { + if (t.is_scalar()) { + int lanes = random_vector_width(rng); + Expr vector = random_expr(rng, t.with_lanes(lanes), depth, overflow_undef); + std::uniform_int_distribution dist(0, lanes - 1); + return Shuffle::make_extract_element(vector, dist(rng)); + } + + std::vector> shuffles = { + [&]() { + int vectors = get_random_divisor(rng, t); + Type subtype = t.with_lanes(t.lanes() / vectors); + std::vector exprs; + exprs.reserve(vectors); + for (int i = 0; i < vectors; i++) { + exprs.push_back(random_expr(rng, subtype, depth, overflow_undef)); + } + return Shuffle::make_concat(exprs); + }, + [&]() { + int vectors = get_random_divisor(rng, t); + Type subtype = t.with_lanes(t.lanes() / vectors); + std::vector exprs; + exprs.reserve(vectors); + for (int i = 0; i < vectors; i++) { + exprs.push_back(random_expr(rng, subtype, depth, overflow_undef)); + } + return Shuffle::make_interleave(exprs); + }, + [&]() { + Expr vector = random_expr(rng, t, depth, overflow_undef); + std::vector indices(t.lanes()); + for (int i = 0; i < t.lanes(); i++) { + indices[i] = i; + if (i & 1) { + int tmp = indices[i]; + indices[i] = indices[i / 2]; + indices[i / 2] = tmp; + } + } + return Shuffle::make({vector}, indices); + }, + }; + + if (t.lanes() * 2 <= 8) { + shuffles.push_back([&]() { + Expr vector = random_expr(rng, t.with_lanes(t.lanes() * 2), depth, overflow_undef); + std::uniform_int_distribution dist(0, 1); + return Shuffle::make_slice(vector, dist(rng), 2, t.lanes()); + }); + } + + return random_choice(rng, shuffles)(); +} + Expr random_condition(RandomEngine &rng, Type t, int depth, bool maybe_scalar) { static make_bin_op_fn make_bin_op[] = { EQ::make, @@ -174,6 +239,9 @@ Expr random_expr(RandomEngine &rng, Type t, int depth, bool overflow_undef) { } return random_expr(rng, t, depth, overflow_undef); }, + [&]() { + return random_shuffle_expr(rng, t, depth, overflow_undef); + }, [&]() { if (t.is_bool()) { auto e1 = random_expr(rng, t, depth); From f58e21d439d327d95c5fc61d27f0a09b5e05df3a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 13:16:18 +0000 Subject: [PATCH 03/11] Harden fuzz_simplify shuffle helper Co-authored-by: alexreinking <169273+alexreinking@users.noreply.github.com> --- test/correctness/fuzz_simplify.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/correctness/fuzz_simplify.cpp b/test/correctness/fuzz_simplify.cpp index d674f500ed4f..aa2048b33219 100644 --- a/test/correctness/fuzz_simplify.cpp +++ b/test/correctness/fuzz_simplify.cpp @@ -60,6 +60,7 @@ int random_vector_width(RandomEngine &rng, int min_lanes = 2) { widths.push_back(width); } } + internal_assert(!widths.empty()); return random_choice(rng, widths); } From 1ffb5053fa0adc415baefff78167e864da81a0b2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 13:20:51 +0000 Subject: [PATCH 04/11] Add VectorReduce generation to fuzz_simplify Co-authored-by: alexreinking <169273+alexreinking@users.noreply.github.com> --- test/correctness/fuzz_simplify.cpp | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/correctness/fuzz_simplify.cpp b/test/correctness/fuzz_simplify.cpp index aa2048b33219..d63473c2e836 100644 --- a/test/correctness/fuzz_simplify.cpp +++ b/test/correctness/fuzz_simplify.cpp @@ -151,6 +151,32 @@ Expr random_shuffle_expr(RandomEngine &rng, Type t, int depth, bool overflow_und return random_choice(rng, shuffles)(); } +Expr random_vector_reduce_expr(RandomEngine &rng, Type t, int depth, bool overflow_undef) { + int input_lanes = (t.is_scalar() ? random_vector_width(rng) : t.lanes()); + Type input_type = t.with_lanes(input_lanes); + Expr vec = random_expr(rng, input_type, depth, overflow_undef); + int output_lanes = get_random_divisor(rng, input_type); + + if (input_type.is_bool()) { + VectorReduce::Operator reduce_ops[] = { + VectorReduce::And, + VectorReduce::Or, + }; + return VectorReduce::make(random_choice(rng, reduce_ops), vec, output_lanes); + } + + VectorReduce::Operator reduce_ops[] = { + VectorReduce::Add, + VectorReduce::SaturatingAdd, + VectorReduce::Mul, + VectorReduce::Min, + VectorReduce::Max, + VectorReduce::And, + VectorReduce::Or, + }; + return VectorReduce::make(random_choice(rng, reduce_ops), vec, output_lanes); +} + Expr random_condition(RandomEngine &rng, Type t, int depth, bool maybe_scalar) { static make_bin_op_fn make_bin_op[] = { EQ::make, @@ -243,6 +269,9 @@ Expr random_expr(RandomEngine &rng, Type t, int depth, bool overflow_undef) { [&]() { return random_shuffle_expr(rng, t, depth, overflow_undef); }, + [&]() { + return random_vector_reduce_expr(rng, t, depth, overflow_undef); + }, [&]() { if (t.is_bool()) { auto e1 = random_expr(rng, t, depth); From 73a0e55b8c695eaba273edc75414fcc1f8e5ccc4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 13:55:54 +0000 Subject: [PATCH 05/11] Fix invalid VectorReduce fuzz generation Co-authored-by: alexreinking <169273+alexreinking@users.noreply.github.com> --- test/correctness/fuzz_simplify.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/correctness/fuzz_simplify.cpp b/test/correctness/fuzz_simplify.cpp index d63473c2e836..77e66bddaf3a 100644 --- a/test/correctness/fuzz_simplify.cpp +++ b/test/correctness/fuzz_simplify.cpp @@ -53,10 +53,10 @@ int get_random_divisor(RandomEngine &rng, Type t) { return random_choice(rng, divisors); } -int random_vector_width(RandomEngine &rng, int min_lanes = 2) { +int random_vector_width(RandomEngine &rng, int min_lanes = 2, int multiple_of = 1) { std::vector widths; for (int width : {2, 3, 4, 6, 8}) { - if (width >= min_lanes) { + if (width >= min_lanes && (width % multiple_of) == 0) { widths.push_back(width); } } @@ -152,10 +152,10 @@ Expr random_shuffle_expr(RandomEngine &rng, Type t, int depth, bool overflow_und } Expr random_vector_reduce_expr(RandomEngine &rng, Type t, int depth, bool overflow_undef) { - int input_lanes = (t.is_scalar() ? random_vector_width(rng) : t.lanes()); + int input_lanes = t.is_scalar() ? random_vector_width(rng) : random_vector_width(rng, t.lanes(), t.lanes()); Type input_type = t.with_lanes(input_lanes); Expr vec = random_expr(rng, input_type, depth, overflow_undef); - int output_lanes = get_random_divisor(rng, input_type); + int output_lanes = t.lanes(); if (input_type.is_bool()) { VectorReduce::Operator reduce_ops[] = { From 76a29cd2781eb045ee1aa1788c8a27184af4154f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 15:19:31 +0000 Subject: [PATCH 06/11] Pivot to regression fix plan Co-authored-by: alexreinking <169273+alexreinking@users.noreply.github.com> --- test/correctness/fuzz_simplify.cpp | 81 +++++++----------------------- 1 file changed, 18 insertions(+), 63 deletions(-) diff --git a/test/correctness/fuzz_simplify.cpp b/test/correctness/fuzz_simplify.cpp index 77e66bddaf3a..a88ba1c2c4e5 100644 --- a/test/correctness/fuzz_simplify.cpp +++ b/test/correctness/fuzz_simplify.cpp @@ -97,65 +97,20 @@ Expr random_leaf(RandomEngine &rng, Type t, bool overflow_undef = false, bool im Expr random_expr(RandomEngine &rng, Type t, int depth, bool overflow_undef = false); Expr random_shuffle_expr(RandomEngine &rng, Type t, int depth, bool overflow_undef) { - if (t.is_scalar()) { - int lanes = random_vector_width(rng); - Expr vector = random_expr(rng, t.with_lanes(lanes), depth, overflow_undef); - std::uniform_int_distribution dist(0, lanes - 1); - return Shuffle::make_extract_element(vector, dist(rng)); - } - - std::vector> shuffles = { - [&]() { - int vectors = get_random_divisor(rng, t); - Type subtype = t.with_lanes(t.lanes() / vectors); - std::vector exprs; - exprs.reserve(vectors); - for (int i = 0; i < vectors; i++) { - exprs.push_back(random_expr(rng, subtype, depth, overflow_undef)); - } - return Shuffle::make_concat(exprs); - }, - [&]() { - int vectors = get_random_divisor(rng, t); - Type subtype = t.with_lanes(t.lanes() / vectors); - std::vector exprs; - exprs.reserve(vectors); - for (int i = 0; i < vectors; i++) { - exprs.push_back(random_expr(rng, subtype, depth, overflow_undef)); - } - return Shuffle::make_interleave(exprs); - }, - [&]() { - Expr vector = random_expr(rng, t, depth, overflow_undef); - std::vector indices(t.lanes()); - for (int i = 0; i < t.lanes(); i++) { - indices[i] = i; - if (i & 1) { - int tmp = indices[i]; - indices[i] = indices[i / 2]; - indices[i / 2] = tmp; - } - } - return Shuffle::make({vector}, indices); - }, - }; - - if (t.lanes() * 2 <= 8) { - shuffles.push_back([&]() { - Expr vector = random_expr(rng, t.with_lanes(t.lanes() * 2), depth, overflow_undef); - std::uniform_int_distribution dist(0, 1); - return Shuffle::make_slice(vector, dist(rng), 2, t.lanes()); - }); - } - - return random_choice(rng, shuffles)(); + internal_assert(t.is_scalar()); + internal_assert(!t.is_bool()); + int lanes = random_vector_width(rng); + Expr vector = Broadcast::make(random_expr(rng, t, depth, overflow_undef), lanes); + std::uniform_int_distribution dist(0, lanes - 1); + return Shuffle::make_extract_element(vector, dist(rng)); } Expr random_vector_reduce_expr(RandomEngine &rng, Type t, int depth, bool overflow_undef) { - int input_lanes = t.is_scalar() ? random_vector_width(rng) : random_vector_width(rng, t.lanes(), t.lanes()); + internal_assert(t.is_scalar()); + int input_lanes = random_vector_width(rng); Type input_type = t.with_lanes(input_lanes); Expr vec = random_expr(rng, input_type, depth, overflow_undef); - int output_lanes = t.lanes(); + int output_lanes = 1; if (input_type.is_bool()) { VectorReduce::Operator reduce_ops[] = { @@ -167,12 +122,6 @@ Expr random_vector_reduce_expr(RandomEngine &rng, Type t, int depth, bool overfl VectorReduce::Operator reduce_ops[] = { VectorReduce::Add, - VectorReduce::SaturatingAdd, - VectorReduce::Mul, - VectorReduce::Min, - VectorReduce::Max, - VectorReduce::And, - VectorReduce::Or, }; return VectorReduce::make(random_choice(rng, reduce_ops), vec, output_lanes); } @@ -227,7 +176,7 @@ Expr make_bitwise_not(Expr a, Expr) { } Expr make_shift_right(Expr a, Expr b) { - return a >> (b % a.type().bits()); + return a >> (b % make_const(b.type(), a.type().bits())); } Expr random_expr(RandomEngine &rng, Type t, int depth, bool overflow_undef) { @@ -267,10 +216,16 @@ Expr random_expr(RandomEngine &rng, Type t, int depth, bool overflow_undef) { return random_expr(rng, t, depth, overflow_undef); }, [&]() { - return random_shuffle_expr(rng, t, depth, overflow_undef); + if (t.is_scalar() && !t.is_bool()) { + return random_shuffle_expr(rng, t, depth, overflow_undef); + } + return random_expr(rng, t, depth, overflow_undef); }, [&]() { - return random_vector_reduce_expr(rng, t, depth, overflow_undef); + if (t.is_scalar()) { + return random_vector_reduce_expr(rng, t, depth, overflow_undef); + } + return random_expr(rng, t, depth, overflow_undef); }, [&]() { if (t.is_bool()) { From 215bbe82bdc981283ee9ea113cf6ae29ec595797 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 15:32:24 +0000 Subject: [PATCH 07/11] Co-authored-by: alexreinking <169273+alexreinking@users.noreply.github.com> --- src/Simplify_Cast.cpp | 9 ++-- src/Simplify_Exprs.cpp | 36 +++++++------- src/Simplify_Shuffle.cpp | 4 +- test/correctness/fuzz_simplify.cpp | 79 +++++++++++++++++++++++------- test/correctness/simplify.cpp | 14 ++++-- 5 files changed, 97 insertions(+), 45 deletions(-) diff --git a/src/Simplify_Cast.cpp b/src/Simplify_Cast.cpp index 8738c26aeb97..d217f5968de4 100644 --- a/src/Simplify_Cast.cpp +++ b/src/Simplify_Cast.cpp @@ -83,7 +83,8 @@ Expr Simplify::visit(const Cast *op, ExprInfo *info) { return make_const(op->type, safe_numeric_cast(*u), info); } else if (cast && op->type.code() == cast->type.code() && - op->type.bits() < cast->type.bits()) { + op->type.bits() < cast->type.bits() && + op->type.lanes() == cast->value.type().lanes()) { // If this is a cast of a cast of the same type, where the // outer cast is narrower, the inner cast can be // eliminated. @@ -93,7 +94,8 @@ Expr Simplify::visit(const Cast *op, ExprInfo *info) { cast->type.is_int() && cast->value.type().is_int() && op->type.bits() >= cast->type.bits() && - cast->type.bits() >= cast->value.type().bits()) { + cast->type.bits() >= cast->value.type().bits() && + op->type.lanes() == cast->value.type().lanes()) { // Casting from a signed type always sign-extends, so widening // partway to a signed type and the rest of the way to some other // integer type is the same as just widening to that integer type @@ -103,7 +105,8 @@ Expr Simplify::visit(const Cast *op, ExprInfo *info) { op->type.is_int_or_uint() && cast->type.is_int_or_uint() && op->type.bits() <= cast->type.bits() && - op->type.bits() <= op->value.type().bits()) { + op->type.bits() <= op->value.type().bits() && + op->type.lanes() == cast->value.type().lanes()) { // If this is a cast between integer types, where the // outer cast is narrower than the inner cast and the // inner cast's argument, the inner cast can be diff --git a/src/Simplify_Exprs.cpp b/src/Simplify_Exprs.cpp index bbd67a5bace0..0b167f8ab0a1 100644 --- a/src/Simplify_Exprs.cpp +++ b/src/Simplify_Exprs.cpp @@ -137,7 +137,7 @@ Expr Simplify::visit(const VectorReduce *op, ExprInfo *info) { rewrite(h_min(max(broadcast(x, arg_lanes), y), lanes), max(h_min(y, lanes), broadcast(x, lanes))) || rewrite(h_min(broadcast(x, arg_lanes), lanes), broadcast(x, lanes)) || rewrite(h_min(broadcast(x, c0), lanes), h_min(x, lanes), factor % c0 == 0) || - rewrite(h_min(ramp(x, y, arg_lanes), lanes), x + min(y * (arg_lanes - 1), 0)) || + ((lanes == 1) && rewrite(h_min(ramp(x, y, arg_lanes), lanes), x + min(y * (arg_lanes - 1), 0))) || false) { return mutate(rewrite.result, info); } @@ -151,7 +151,7 @@ Expr Simplify::visit(const VectorReduce *op, ExprInfo *info) { rewrite(h_max(max(broadcast(x, arg_lanes), y), lanes), max(h_max(y, lanes), broadcast(x, lanes))) || rewrite(h_max(broadcast(x, arg_lanes), lanes), broadcast(x, lanes)) || rewrite(h_max(broadcast(x, c0), lanes), h_max(x, lanes), factor % c0 == 0) || - rewrite(h_max(ramp(x, y, arg_lanes), lanes), x + max(y * (arg_lanes - 1), 0)) || + ((lanes == 1) && rewrite(h_max(ramp(x, y, arg_lanes), lanes), x + max(y * (arg_lanes - 1), 0))) || false) { return mutate(rewrite.result, info); } @@ -165,14 +165,14 @@ Expr Simplify::visit(const VectorReduce *op, ExprInfo *info) { rewrite(h_and(broadcast(x, arg_lanes) && y, lanes), h_and(y, lanes) && broadcast(x, lanes)) || rewrite(h_and(broadcast(x, arg_lanes), lanes), broadcast(x, lanes)) || rewrite(h_and(broadcast(x, c0), lanes), h_and(x, lanes), factor % c0 == 0) || - rewrite(h_and(ramp(x, y, arg_lanes) < broadcast(z, arg_lanes), lanes), - x + max(y * (arg_lanes - 1), 0) < z) || - rewrite(h_and(ramp(x, y, arg_lanes) <= broadcast(z, arg_lanes), lanes), - x + max(y * (arg_lanes - 1), 0) <= z) || - rewrite(h_and(broadcast(x, arg_lanes) < ramp(y, z, arg_lanes), lanes), - x < y + min(z * (arg_lanes - 1), 0)) || - rewrite(h_and(broadcast(x, arg_lanes) < ramp(y, z, arg_lanes), lanes), - x <= y + min(z * (arg_lanes - 1), 0)) || + ((lanes == 1) && rewrite(h_and(ramp(x, y, arg_lanes) < broadcast(z, arg_lanes), lanes), + x + max(y * (arg_lanes - 1), 0) < z)) || + ((lanes == 1) && rewrite(h_and(ramp(x, y, arg_lanes) <= broadcast(z, arg_lanes), lanes), + x + max(y * (arg_lanes - 1), 0) <= z)) || + ((lanes == 1) && rewrite(h_and(broadcast(x, arg_lanes) < ramp(y, z, arg_lanes), lanes), + x < y + min(z * (arg_lanes - 1), 0))) || + ((lanes == 1) && rewrite(h_and(broadcast(x, arg_lanes) < ramp(y, z, arg_lanes), lanes), + x <= y + min(z * (arg_lanes - 1), 0))) || false) { return mutate(rewrite.result, info); } @@ -187,14 +187,14 @@ Expr Simplify::visit(const VectorReduce *op, ExprInfo *info) { rewrite(h_or(broadcast(x, arg_lanes), lanes), broadcast(x, lanes)) || rewrite(h_or(broadcast(x, c0), lanes), h_or(x, lanes), factor % c0 == 0) || // type of arg_lanes is somewhat indeterminate - rewrite(h_or(ramp(x, y, arg_lanes) < broadcast(z, arg_lanes), lanes), - x + min(y * (arg_lanes - 1), 0) < z) || - rewrite(h_or(ramp(x, y, arg_lanes) <= broadcast(z, arg_lanes), lanes), - x + min(y * (arg_lanes - 1), 0) <= z) || - rewrite(h_or(broadcast(x, arg_lanes) < ramp(y, z, arg_lanes), lanes), - x < y + max(z * (arg_lanes - 1), 0)) || - rewrite(h_or(broadcast(x, arg_lanes) < ramp(y, z, arg_lanes), lanes), - x <= y + max(z * (arg_lanes - 1), 0)) || + ((lanes == 1) && rewrite(h_or(ramp(x, y, arg_lanes) < broadcast(z, arg_lanes), lanes), + x + min(y * (arg_lanes - 1), 0) < z)) || + ((lanes == 1) && rewrite(h_or(ramp(x, y, arg_lanes) <= broadcast(z, arg_lanes), lanes), + x + min(y * (arg_lanes - 1), 0) <= z)) || + ((lanes == 1) && rewrite(h_or(broadcast(x, arg_lanes) < ramp(y, z, arg_lanes), lanes), + x < y + max(z * (arg_lanes - 1), 0))) || + ((lanes == 1) && rewrite(h_or(broadcast(x, arg_lanes) < ramp(y, z, arg_lanes), lanes), + x <= y + max(z * (arg_lanes - 1), 0))) || false) { return mutate(rewrite.result, info); } diff --git a/src/Simplify_Shuffle.cpp b/src/Simplify_Shuffle.cpp index aecb4c6fc99a..5ea02bd2170e 100644 --- a/src/Simplify_Shuffle.cpp +++ b/src/Simplify_Shuffle.cpp @@ -236,7 +236,7 @@ Expr Simplify::visit(const Shuffle *op, ExprInfo *info) { } } if (can_collapse) { - return Ramp::make(r->base, r->stride, op->indices.size()); + return mutate(Ramp::make(r->base, r->stride, op->indices.size()), info); } } @@ -257,7 +257,7 @@ Expr Simplify::visit(const Shuffle *op, ExprInfo *info) { } if (can_collapse) { - return Ramp::make(new_vectors[0], stride, op->indices.size()); + return mutate(Ramp::make(new_vectors[0], stride, op->indices.size()), info); } } } diff --git a/test/correctness/fuzz_simplify.cpp b/test/correctness/fuzz_simplify.cpp index a88ba1c2c4e5..802325f2ddb4 100644 --- a/test/correctness/fuzz_simplify.cpp +++ b/test/correctness/fuzz_simplify.cpp @@ -97,20 +97,65 @@ Expr random_leaf(RandomEngine &rng, Type t, bool overflow_undef = false, bool im Expr random_expr(RandomEngine &rng, Type t, int depth, bool overflow_undef = false); Expr random_shuffle_expr(RandomEngine &rng, Type t, int depth, bool overflow_undef) { - internal_assert(t.is_scalar()); - internal_assert(!t.is_bool()); - int lanes = random_vector_width(rng); - Expr vector = Broadcast::make(random_expr(rng, t, depth, overflow_undef), lanes); - std::uniform_int_distribution dist(0, lanes - 1); - return Shuffle::make_extract_element(vector, dist(rng)); + if (t.is_scalar()) { + int lanes = random_vector_width(rng); + Expr vector = random_expr(rng, t.with_lanes(lanes), depth, overflow_undef); + std::uniform_int_distribution dist(0, lanes - 1); + return Shuffle::make_extract_element(vector, dist(rng)); + } + + std::vector> shuffles = { + [&]() { + int vectors = get_random_divisor(rng, t); + Type subtype = t.with_lanes(t.lanes() / vectors); + std::vector exprs; + exprs.reserve(vectors); + for (int i = 0; i < vectors; i++) { + exprs.push_back(random_expr(rng, subtype, depth, overflow_undef)); + } + return Shuffle::make_concat(exprs); + }, + [&]() { + int vectors = get_random_divisor(rng, t); + Type subtype = t.with_lanes(t.lanes() / vectors); + std::vector exprs; + exprs.reserve(vectors); + for (int i = 0; i < vectors; i++) { + exprs.push_back(random_expr(rng, subtype, depth, overflow_undef)); + } + return Shuffle::make_interleave(exprs); + }, + [&]() { + Expr vector = random_expr(rng, t, depth, overflow_undef); + std::vector indices(t.lanes()); + for (int i = 0; i < t.lanes(); i++) { + indices[i] = i; + if (i & 1) { + int tmp = indices[i]; + indices[i] = indices[i / 2]; + indices[i / 2] = tmp; + } + } + return Shuffle::make({vector}, indices); + }, + }; + + if (t.lanes() * 2 <= 8) { + shuffles.push_back([&]() { + Expr vector = random_expr(rng, t.with_lanes(t.lanes() * 2), depth, overflow_undef); + std::uniform_int_distribution dist(0, 1); + return Shuffle::make_slice(vector, dist(rng), 2, t.lanes()); + }); + } + + return random_choice(rng, shuffles)(); } Expr random_vector_reduce_expr(RandomEngine &rng, Type t, int depth, bool overflow_undef) { - internal_assert(t.is_scalar()); - int input_lanes = random_vector_width(rng); + int input_lanes = t.is_scalar() ? random_vector_width(rng) : random_vector_width(rng, t.lanes(), t.lanes()); Type input_type = t.with_lanes(input_lanes); Expr vec = random_expr(rng, input_type, depth, overflow_undef); - int output_lanes = 1; + int output_lanes = t.lanes(); if (input_type.is_bool()) { VectorReduce::Operator reduce_ops[] = { @@ -122,6 +167,12 @@ Expr random_vector_reduce_expr(RandomEngine &rng, Type t, int depth, bool overfl VectorReduce::Operator reduce_ops[] = { VectorReduce::Add, + VectorReduce::SaturatingAdd, + VectorReduce::Mul, + VectorReduce::Min, + VectorReduce::Max, + VectorReduce::And, + VectorReduce::Or, }; return VectorReduce::make(random_choice(rng, reduce_ops), vec, output_lanes); } @@ -216,16 +267,10 @@ Expr random_expr(RandomEngine &rng, Type t, int depth, bool overflow_undef) { return random_expr(rng, t, depth, overflow_undef); }, [&]() { - if (t.is_scalar() && !t.is_bool()) { - return random_shuffle_expr(rng, t, depth, overflow_undef); - } - return random_expr(rng, t, depth, overflow_undef); + return random_shuffle_expr(rng, t, depth, overflow_undef); }, [&]() { - if (t.is_scalar()) { - return random_vector_reduce_expr(rng, t, depth, overflow_undef); - } - return random_expr(rng, t, depth, overflow_undef); + return random_vector_reduce_expr(rng, t, depth, overflow_undef); }, [&]() { if (t.is_bool()) { diff --git a/test/correctness/simplify.cpp b/test/correctness/simplify.cpp index de10bde5a1b9..3cc7330371c4 100644 --- a/test/correctness/simplify.cpp +++ b/test/correctness/simplify.cpp @@ -672,6 +672,7 @@ void check_vectors() { // Collapse some vector concats check(concat_vectors({ramp(x, 2, 4), ramp(x + 8, 2, 4)}), ramp(x, 2, 8)); check(concat_vectors({ramp(x, 3, 2), ramp(x + 6, 3, 2), ramp(x + 12, 3, 2)}), ramp(x, 3, 6)); + check(concat_vectors({x, x}), Broadcast::make(x, 2)); // Now some ones that can't work { @@ -810,23 +811,26 @@ void check_vectors() { int_vector); check(VectorReduce::make(VectorReduce::Max, Broadcast::make(int_vector, 4), 8), VectorReduce::make(VectorReduce::Max, Broadcast::make(int_vector, 4), 8)); + check(cast(UInt(32, 2), + VectorReduce::make(VectorReduce::Max, Ramp::make(cast(UInt(8), x), cast(UInt(8), y), 8), 2)), + cast(UInt(32, 2), + VectorReduce::make(VectorReduce::Max, Ramp::make(cast(UInt(8), x), cast(UInt(8), y), 8), 2))); { - // h_add(broadcast(x, 8), 4) should simplify to broadcast(x * 2, 4) check(VectorReduce::make(VectorReduce::Add, broadcast(x, 8), 4), broadcast(x * 2, 4)); } { Expr const_u8 = cast(UInt(8), 3); - check(VectorReduce::make(VectorReduce::Add, broadcast(const_u8, 9), 3), broadcast(cast(UInt(8), 9), 3)); + check(VectorReduce::make(VectorReduce::Add, broadcast(const_u8, 9), 3), + broadcast(cast(UInt(8), 9), 3)); } { - // Test VectorReduce::Add on a variable of unsigned type to ensure the multiplied factor - // keeps the correct type and avoids type-mismatch assertion failures. Expr u8_x = Variable::make(UInt(8), "u8_x"); - check(VectorReduce::make(VectorReduce::Add, broadcast(u8_x, 9), 3), broadcast(u8_x * cast(UInt(8), 3), 3)); + check(VectorReduce::make(VectorReduce::Add, broadcast(u8_x, 9), 3), + broadcast(u8_x * cast(UInt(8), 3), 3)); } } From ce8cf94144056454ed588b69accd9f9dabcf147a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 16:50:51 +0000 Subject: [PATCH 08/11] Merge main into branch Co-authored-by: alexreinking <169273+alexreinking@users.noreply.github.com> --- test/correctness/simplify.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/correctness/simplify.cpp b/test/correctness/simplify.cpp index 3cc7330371c4..555186d25b27 100644 --- a/test/correctness/simplify.cpp +++ b/test/correctness/simplify.cpp @@ -817,6 +817,7 @@ void check_vectors() { VectorReduce::make(VectorReduce::Max, Ramp::make(cast(UInt(8), x), cast(UInt(8), y), 8), 2))); { + // h_add(broadcast(x, 8), 4) should simplify to broadcast(x * 2, 4) check(VectorReduce::make(VectorReduce::Add, broadcast(x, 8), 4), broadcast(x * 2, 4)); } @@ -828,6 +829,8 @@ void check_vectors() { } { + // Test VectorReduce::Add on a variable of unsigned type to ensure the multiplied factor + // keeps the correct type and avoids type-mismatch assertion failures. Expr u8_x = Variable::make(UInt(8), "u8_x"); check(VectorReduce::make(VectorReduce::Add, broadcast(u8_x, 9), 3), broadcast(u8_x * cast(UInt(8), 3), 3)); From 329e7b13db581f25376cac8734ba1a7d45c6a22c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 16:54:30 +0000 Subject: [PATCH 09/11] Harden merged pre-commit auto-fix workflow Co-authored-by: alexreinking <169273+alexreinking@users.noreply.github.com> --- .github/workflows/pre-commit-push-fixes.yml | 35 ++++++++++++++------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/.github/workflows/pre-commit-push-fixes.yml b/.github/workflows/pre-commit-push-fixes.yml index c5cfe118d929..a91b3f636d25 100644 --- a/.github/workflows/pre-commit-push-fixes.yml +++ b/.github/workflows/pre-commit-push-fixes.yml @@ -12,7 +12,9 @@ jobs: push-fixes: name: Push auto-fixes to PR branch runs-on: ubuntu-slim - if: github.event.workflow_run.conclusion == 'failure' + if: >- + github.event.workflow_run.event == 'pull_request' + && github.event.workflow_run.conclusion == 'failure' steps: - name: Download auto-fix artifacts id: download @@ -27,12 +29,16 @@ jobs: - name: Read PR metadata if: steps.download.outcome == 'success' id: pr + env: + GH_TOKEN: ${{ github.token }} + PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }} run: | + pr_json="$(gh api repos/${{ github.repository }}/pulls/${PR_NUMBER})" { - echo "number=$(jq -r '.number' /tmp/pre-commit-fixes/pr-metadata.json)" - echo "head-repo=$(jq -r '.head_repo' /tmp/pre-commit-fixes/pr-metadata.json)" - echo "head-ref=$(jq -r '.head_ref' /tmp/pre-commit-fixes/pr-metadata.json)" - echo "maintainer-can-modify=$(jq -r '.maintainer_can_modify' /tmp/pre-commit-fixes/pr-metadata.json)" + echo "number=${PR_NUMBER}" + echo "head-repo=$(jq -r '.head.repo.full_name' <<<"${pr_json}")" + echo "head-ref=$(jq -r '.head.ref' <<<"${pr_json}")" + echo "maintainer-can-modify=$(jq -r '.maintainer_can_modify' <<<"${pr_json}")" } >> "$GITHUB_OUTPUT" - name: Abort if maintainer edits not allowed @@ -58,15 +64,21 @@ jobs: env: GH_TOKEN: ${{ steps.app-token.outputs.token }} - - uses: actions/checkout@v4 + - name: Fetch PR branch if: steps.download.outcome == 'success' && env.skip != 'true' - with: - repository: ${{ steps.pr.outputs.head-repo }} - ref: ${{ steps.pr.outputs.head-ref }} - token: ${{ steps.app-token.outputs.token }} + env: + HEAD_REPO: ${{ steps.pr.outputs.head-repo }} + HEAD_REF: ${{ steps.pr.outputs.head-ref }} + run: | + git init . + git remote add origin "https://github.com/${HEAD_REPO}.git" + git -c advice.detachedHead=false fetch --depth=1 origin "refs/heads/${HEAD_REF}" + git checkout --detach FETCH_HEAD - name: Apply and push fixes if: steps.download.outcome == 'success' && env.skip != 'true' + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} run: | git config user.name "${{ steps.app-token.outputs.app-slug }}[bot]" git config user.email "${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com" @@ -79,4 +91,5 @@ jobs: git apply /tmp/pre-commit-fixes/pre-commit-fixes.patch git add -A git commit -m "Apply pre-commit auto-fixes" - git push + git remote set-url --push origin "https://x-access-token:${GH_TOKEN}@github.com/${{ steps.pr.outputs.head-repo }}.git" + git push origin HEAD:${{ steps.pr.outputs.head-ref }} From e73ab4cc5db0e79d7c59c9ff45dafb193223eb66 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 16:58:51 +0000 Subject: [PATCH 10/11] Resolve remaining PR merge conflicts Co-authored-by: alexreinking <169273+alexreinking@users.noreply.github.com> --- .github/workflows/pre-commit-push-fixes.yml | 35 +++++++-------------- test/correctness/simplify.cpp | 15 +++++---- 2 files changed, 18 insertions(+), 32 deletions(-) diff --git a/.github/workflows/pre-commit-push-fixes.yml b/.github/workflows/pre-commit-push-fixes.yml index a91b3f636d25..c5cfe118d929 100644 --- a/.github/workflows/pre-commit-push-fixes.yml +++ b/.github/workflows/pre-commit-push-fixes.yml @@ -12,9 +12,7 @@ jobs: push-fixes: name: Push auto-fixes to PR branch runs-on: ubuntu-slim - if: >- - github.event.workflow_run.event == 'pull_request' - && github.event.workflow_run.conclusion == 'failure' + if: github.event.workflow_run.conclusion == 'failure' steps: - name: Download auto-fix artifacts id: download @@ -29,16 +27,12 @@ jobs: - name: Read PR metadata if: steps.download.outcome == 'success' id: pr - env: - GH_TOKEN: ${{ github.token }} - PR_NUMBER: ${{ github.event.workflow_run.pull_requests[0].number }} run: | - pr_json="$(gh api repos/${{ github.repository }}/pulls/${PR_NUMBER})" { - echo "number=${PR_NUMBER}" - echo "head-repo=$(jq -r '.head.repo.full_name' <<<"${pr_json}")" - echo "head-ref=$(jq -r '.head.ref' <<<"${pr_json}")" - echo "maintainer-can-modify=$(jq -r '.maintainer_can_modify' <<<"${pr_json}")" + echo "number=$(jq -r '.number' /tmp/pre-commit-fixes/pr-metadata.json)" + echo "head-repo=$(jq -r '.head_repo' /tmp/pre-commit-fixes/pr-metadata.json)" + echo "head-ref=$(jq -r '.head_ref' /tmp/pre-commit-fixes/pr-metadata.json)" + echo "maintainer-can-modify=$(jq -r '.maintainer_can_modify' /tmp/pre-commit-fixes/pr-metadata.json)" } >> "$GITHUB_OUTPUT" - name: Abort if maintainer edits not allowed @@ -64,21 +58,15 @@ jobs: env: GH_TOKEN: ${{ steps.app-token.outputs.token }} - - name: Fetch PR branch + - uses: actions/checkout@v4 if: steps.download.outcome == 'success' && env.skip != 'true' - env: - HEAD_REPO: ${{ steps.pr.outputs.head-repo }} - HEAD_REF: ${{ steps.pr.outputs.head-ref }} - run: | - git init . - git remote add origin "https://github.com/${HEAD_REPO}.git" - git -c advice.detachedHead=false fetch --depth=1 origin "refs/heads/${HEAD_REF}" - git checkout --detach FETCH_HEAD + with: + repository: ${{ steps.pr.outputs.head-repo }} + ref: ${{ steps.pr.outputs.head-ref }} + token: ${{ steps.app-token.outputs.token }} - name: Apply and push fixes if: steps.download.outcome == 'success' && env.skip != 'true' - env: - GH_TOKEN: ${{ steps.app-token.outputs.token }} run: | git config user.name "${{ steps.app-token.outputs.app-slug }}[bot]" git config user.email "${{ steps.get-user-id.outputs.user-id }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com" @@ -91,5 +79,4 @@ jobs: git apply /tmp/pre-commit-fixes/pre-commit-fixes.patch git add -A git commit -m "Apply pre-commit auto-fixes" - git remote set-url --push origin "https://x-access-token:${GH_TOKEN}@github.com/${{ steps.pr.outputs.head-repo }}.git" - git push origin HEAD:${{ steps.pr.outputs.head-ref }} + git push diff --git a/test/correctness/simplify.cpp b/test/correctness/simplify.cpp index 555186d25b27..8616bfa024aa 100644 --- a/test/correctness/simplify.cpp +++ b/test/correctness/simplify.cpp @@ -811,10 +811,6 @@ void check_vectors() { int_vector); check(VectorReduce::make(VectorReduce::Max, Broadcast::make(int_vector, 4), 8), VectorReduce::make(VectorReduce::Max, Broadcast::make(int_vector, 4), 8)); - check(cast(UInt(32, 2), - VectorReduce::make(VectorReduce::Max, Ramp::make(cast(UInt(8), x), cast(UInt(8), y), 8), 2)), - cast(UInt(32, 2), - VectorReduce::make(VectorReduce::Max, Ramp::make(cast(UInt(8), x), cast(UInt(8), y), 8), 2))); { // h_add(broadcast(x, 8), 4) should simplify to broadcast(x * 2, 4) @@ -824,17 +820,20 @@ void check_vectors() { { Expr const_u8 = cast(UInt(8), 3); - check(VectorReduce::make(VectorReduce::Add, broadcast(const_u8, 9), 3), - broadcast(cast(UInt(8), 9), 3)); + check(VectorReduce::make(VectorReduce::Add, broadcast(const_u8, 9), 3), broadcast(cast(UInt(8), 9), 3)); } { // Test VectorReduce::Add on a variable of unsigned type to ensure the multiplied factor // keeps the correct type and avoids type-mismatch assertion failures. Expr u8_x = Variable::make(UInt(8), "u8_x"); - check(VectorReduce::make(VectorReduce::Add, broadcast(u8_x, 9), 3), - broadcast(u8_x * cast(UInt(8), 3), 3)); + check(VectorReduce::make(VectorReduce::Add, broadcast(u8_x, 9), 3), broadcast(u8_x * cast(UInt(8), 3), 3)); } + + check(cast(UInt(32, 2), + VectorReduce::make(VectorReduce::Max, Ramp::make(cast(UInt(8), x), cast(UInt(8), y), 8), 2)), + cast(UInt(32, 2), + VectorReduce::make(VectorReduce::Max, Ramp::make(cast(UInt(8), x), cast(UInt(8), y), 8), 2))); } void check_bounds() { From 088466a9e1be16ef1e5da9ac0a13c7965c1c5179 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 15 Mar 2026 18:26:13 +0000 Subject: [PATCH 11/11] Preserve lane counts in random conditions Co-authored-by: alexreinking <169273+alexreinking@users.noreply.github.com> --- test/correctness/fuzz_simplify.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/test/correctness/fuzz_simplify.cpp b/test/correctness/fuzz_simplify.cpp index 802325f2ddb4..235427eab330 100644 --- a/test/correctness/fuzz_simplify.cpp +++ b/test/correctness/fuzz_simplify.cpp @@ -187,13 +187,18 @@ Expr random_condition(RandomEngine &rng, Type t, int depth, bool maybe_scalar) { GE::make, }; + int lanes = t.lanes(); if (maybe_scalar && (rng() & 1)) { t = t.element_of(); } Expr a = random_expr(rng, t, depth); Expr b = random_expr(rng, t, depth); - return random_choice(rng, make_bin_op)(a, b); + Expr result = random_choice(rng, make_bin_op)(a, b); + if (result.type().lanes() != lanes) { + result = Broadcast::make(result, lanes); + } + return result; } Expr make_absd(Expr a, Expr b) {