Skip to content

Commit

Permalink
Merge branch 'main' into xtensa-codegen
Browse files Browse the repository at this point in the history
  • Loading branch information
steven-johnson committed May 8, 2023
2 parents c153cda + 763d207 commit be1d051
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 69 deletions.
33 changes: 21 additions & 12 deletions test/fuzz/cse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,34 @@
#include <time.h>

using namespace Halide;
using namespace Halide::ConciseCasts;
using namespace Halide::Internal;
using std::vector;

// Note that this deliberately uses int16 values everywhere --
// *not* int32 -- because we want to test CSE, not the simplifier's
// overflow behavior, and using int32 can end up with results
// containing signed_integer_overflow(), which is not helpful here.
Expr random_expr(FuzzedDataProvider &fdp, int depth, vector<Expr> &exprs) {
if (depth <= 0) {
return fdp.ConsumeIntegralInRange<int>(-5, 4);
return i16(fdp.ConsumeIntegralInRange<int>(-5, 4));
}
if (!exprs.empty() && fdp.ConsumeBool()) {
// Reuse an existing expression
return pick_value_in_vector(fdp, exprs);
}
std::function<Expr()> build_next_expr[] = {
[&]() {
return Var("x");
// Can't use Var() here because that would require i32 values,
// which we are avoiding here because we don't want to end
// up with signed_integer_overflow()
return Variable::make(Int(16), unique_name("x"));
},
[&]() {
return Var("y");
return Variable::make(Int(16), unique_name("y"));
},
[&]() {
return Var("z");
return Variable::make(Int(16), unique_name("z"));
},
[&]() {
Expr next = random_expr(fdp, depth - 1, exprs);
Expand All @@ -42,20 +50,20 @@ Expr random_expr(FuzzedDataProvider &fdp, int depth, vector<Expr> &exprs) {
[&]() {
Expr a = random_expr(fdp, depth - 1, exprs);
Expr b = random_expr(fdp, depth - 1, exprs);
return Let::make("x", a, b);
return i16(Let::make("x", a, b));
},
[&]() {
Expr a = random_expr(fdp, depth - 1, exprs);
Expr b = random_expr(fdp, depth - 1, exprs);
return Let::make("y", a, b);
return i16(Let::make("y", a, b));
},
[&]() {
Expr a = random_expr(fdp, depth - 1, exprs);
Expr b = random_expr(fdp, depth - 1, exprs);
return Let::make("z", a, b);
return i16(Let::make("z", a, b));
},
[&]() {
return fdp.ConsumeIntegralInRange<int>(-5, 4);
return i16(fdp.ConsumeIntegralInRange<int>(-5, 4));
},
};
Expr next = fdp.PickValueInArray(build_next_expr)();
Expand All @@ -71,14 +79,15 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
Expr csed = common_subexpression_elimination(orig);

Expr check = (orig == csed);
check = Let::make("x", 1, check);
check = Let::make("y", 2, check);
check = Let::make("z", 3, check);
check = Let::make("x", i16(1), check);
check = Let::make("y", i16(2), check);
check = Let::make("z", i16(3), check);
Stmt check_stmt = uniquify_variable_names(Evaluate::make(check));
check = check_stmt.as<Evaluate>()->value;

// Don't use can_prove, because it recursively calls cse, which just confuses matters.
assert(is_const_one(simplify(check)));
Expr result = simplify(check);
assert(is_const_one(result));

return 0;
}
125 changes: 68 additions & 57 deletions test/fuzz/simplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ using std::string;
using namespace Halide;
using namespace Halide::Internal;

typedef Expr (*make_bin_op_fn)(Expr, Expr);

const int fuzz_var_count = 5;

Type fuzz_types[] = {UInt(1), UInt(8), UInt(16), UInt(32), Int(8), Int(16), Int(32)};
const int fuzz_type_count = sizeof(fuzz_types) / sizeof(fuzz_types[0]);

std::string fuzz_var(int i) {
return std::string(1, 'a' + i);
Expand All @@ -30,12 +31,12 @@ Expr random_var(FuzzedDataProvider &fdp) {
}

Type random_type(FuzzedDataProvider &fdp, int width) {
Type T = fdp.PickValueInArray(fuzz_types);
Type t = fdp.PickValueInArray(fuzz_types);

if (width > 1) {
T = T.with_lanes(width);
t = t.with_lanes(width);
}
return T;
return t;
}

int get_random_divisor(FuzzedDataProvider &fdp, Type t) {
Expand All @@ -49,41 +50,40 @@ int get_random_divisor(FuzzedDataProvider &fdp, Type t) {
return pick_value_in_vector(fdp, divisors);
}

Expr random_leaf(FuzzedDataProvider &fdp, Type T, bool overflow_undef = false, bool imm_only = false) {
if (T.is_int() && T.bits() == 32) {
Expr random_leaf(FuzzedDataProvider &fdp, Type t, bool overflow_undef = false, bool imm_only = false) {
if (t.is_int() && t.bits() == 32) {
overflow_undef = true;
}
if (T.is_scalar()) {
if (t.is_scalar()) {
if (!imm_only && fdp.ConsumeBool()) {
auto v1 = random_var(fdp);
return cast(T, v1);
return cast(t, v1);
} else {
if (overflow_undef) {
// For Int(32), we don't care about correctness during
// overflow, so just use numbers that are unlikely to
// overflow.
return cast(T, fdp.ConsumeIntegralInRange<int>(-128, 127));
return cast(t, fdp.ConsumeIntegralInRange<int>(-128, 127));
} else {
return cast(T, fdp.ConsumeIntegral<int>());
return cast(t, fdp.ConsumeIntegral<int>());
}
}
} else {
int lanes = get_random_divisor(fdp, T);
int lanes = get_random_divisor(fdp, t);
if (fdp.ConsumeBool()) {
auto e1 = random_leaf(fdp, T.with_lanes(T.lanes() / lanes), overflow_undef);
auto e2 = random_leaf(fdp, T.with_lanes(T.lanes() / lanes), overflow_undef);
auto e1 = random_leaf(fdp, t.with_lanes(t.lanes() / lanes), overflow_undef);
auto e2 = random_leaf(fdp, t.with_lanes(t.lanes() / lanes), overflow_undef);
return Ramp::make(e1, e2, lanes);
} else {
auto e1 = random_leaf(fdp, T.with_lanes(T.lanes() / lanes), overflow_undef);
auto e1 = random_leaf(fdp, t.with_lanes(t.lanes() / lanes), overflow_undef);
return Broadcast::make(e1, lanes);
}
}
}

Expr random_expr(FuzzedDataProvider &fdp, Type T, int depth, bool overflow_undef = false);
Expr random_expr(FuzzedDataProvider &fdp, Type t, int depth, bool overflow_undef = false);

Expr random_condition(FuzzedDataProvider &fdp, Type T, int depth, bool maybe_scalar) {
typedef Expr (*make_bin_op_fn)(Expr, Expr);
Expr random_condition(FuzzedDataProvider &fdp, Type t, int depth, bool maybe_scalar) {
static make_bin_op_fn make_bin_op[] = {
EQ::make,
NE::make,
Expand All @@ -92,84 +92,82 @@ Expr random_condition(FuzzedDataProvider &fdp, Type T, int depth, bool maybe_sca
GT::make,
GE::make,
};
const int op_count = sizeof(make_bin_op) / sizeof(make_bin_op[0]);

if (maybe_scalar && fdp.ConsumeBool()) {
T = T.element_of();
t = t.element_of();
}

Expr a = random_expr(fdp, T, depth);
Expr b = random_expr(fdp, T, depth);
Expr a = random_expr(fdp, t, depth);
Expr b = random_expr(fdp, t, depth);
return fdp.PickValueInArray(make_bin_op)(a, b);
}

Expr make_absd(Expr a, Expr b) {
// random_expr() assumes that the result type is the same as the input type,
// random_expr() assumes that the result t is the same as the input t,
// which isn't true for all absd variants, so force the issue.
return cast(a.type(), absd(a, b));
}

Expr random_expr(FuzzedDataProvider &fdp, Type T, int depth, bool overflow_undef) {
if (T.is_int() && T.bits() == 32) {
Expr random_expr(FuzzedDataProvider &fdp, Type t, int depth, bool overflow_undef) {
if (t.is_int() && t.bits() == 32) {
overflow_undef = true;
}

if (depth-- <= 0) {
return random_leaf(fdp, T, overflow_undef);
return random_leaf(fdp, t, overflow_undef);
}

std::function<Expr()> operations[] = {
[&]() {
return random_leaf(fdp, T);
return random_leaf(fdp, t);
},
[&]() {
auto c = random_condition(fdp, T, depth, true);
auto e1 = random_expr(fdp, T, depth, overflow_undef);
auto e2 = random_expr(fdp, T, depth, overflow_undef);
auto c = random_condition(fdp, t, depth, true);
auto e1 = random_expr(fdp, t, depth, overflow_undef);
auto e2 = random_expr(fdp, t, depth, overflow_undef);
return Select::make(c, e1, e2);
},
[&]() {
if (T.lanes() != 1) {
int lanes = get_random_divisor(fdp, T);
auto e1 = random_expr(fdp, T.with_lanes(T.lanes() / lanes), depth, overflow_undef);
if (t.lanes() != 1) {
int lanes = get_random_divisor(fdp, t);
auto e1 = random_expr(fdp, t.with_lanes(t.lanes() / lanes), depth, overflow_undef);
return Broadcast::make(e1, lanes);
}
return random_expr(fdp, T, depth, overflow_undef);
return random_expr(fdp, t, depth, overflow_undef);
},
[&]() {
if (T.lanes() != 1) {
int lanes = get_random_divisor(fdp, T);
auto e1 = random_expr(fdp, T.with_lanes(T.lanes() / lanes), depth, overflow_undef);
auto e2 = random_expr(fdp, T.with_lanes(T.lanes() / lanes), depth, overflow_undef);
if (t.lanes() != 1) {
int lanes = get_random_divisor(fdp, t);
auto e1 = random_expr(fdp, t.with_lanes(t.lanes() / lanes), depth, overflow_undef);
auto e2 = random_expr(fdp, t.with_lanes(t.lanes() / lanes), depth, overflow_undef);
return Ramp::make(e1, e2, lanes);
}
return random_expr(fdp, T, depth, overflow_undef);
return random_expr(fdp, t, depth, overflow_undef);
},
[&]() {
if (T.is_bool()) {
auto e1 = random_expr(fdp, T, depth);
if (t.is_bool()) {
auto e1 = random_expr(fdp, t, depth);
return Not::make(e1);
}
return random_expr(fdp, T, depth, overflow_undef);
return random_expr(fdp, t, depth, overflow_undef);
},
[&]() {
// When generating boolean expressions, maybe throw in a condition on non-bool types.
if (T.is_bool()) {
return random_condition(fdp, random_type(fdp, T.lanes()), depth, false);
if (t.is_bool()) {
return random_condition(fdp, random_type(fdp, t.lanes()), depth, false);
}
return random_expr(fdp, T, depth, overflow_undef);
return random_expr(fdp, t, depth, overflow_undef);
},
[&]() {
// Get a random type that isn't T or int32 (int32 can overflow and we don't care about that).
// Get a random t that isn't t or int32 (int32 can overflow and we don't care about that).
Type subT;
do {
subT = random_type(fdp, T.lanes());
} while (subT == T || (subT.is_int() && subT.bits() == 32));
subT = random_type(fdp, t.lanes());
} while (subT == t || (subT.is_int() && subT.bits() == 32));
auto e1 = random_expr(fdp, subT, depth, overflow_undef);
return Cast::make(T, e1);
return Cast::make(t, e1);
},
[&]() {
typedef Expr (*make_bin_op_fn)(Expr, Expr);
static make_bin_op_fn make_bin_op[] = {
// Arithmetic operations.
Add::make,
Expand All @@ -180,23 +178,32 @@ Expr random_expr(FuzzedDataProvider &fdp, Type T, int depth, bool overflow_undef
Div::make,
Mod::make,
make_absd,
// Boolean operations.
};

Expr a = random_expr(fdp, t, depth, overflow_undef);
Expr b = random_expr(fdp, t, depth, overflow_undef);
return fdp.PickValueInArray(make_bin_op)(a, b);
},
[&]() {
static make_bin_op_fn make_bin_op[] = {
And::make,
Or::make,
};

Expr a = random_expr(fdp, T, depth, overflow_undef);
Expr b = random_expr(fdp, T, depth, overflow_undef);
return fdp.PickValueInArray(make_bin_op)(a, b);
// Boolean operations -- both sides must be cast to booleans,
// and then we must cast the result back to 't'.
Expr a = cast<bool>(random_expr(fdp, t, depth, overflow_undef));
Expr b = cast<bool>(random_expr(fdp, t, depth, overflow_undef));
return cast(t, fdp.PickValueInArray(make_bin_op)(a, b));
}};
return fdp.PickValueInArray(operations)();
}

bool test_simplification(Expr a, Expr b, Type T, const map<string, Expr> &vars) {
for (int j = 0; j < T.lanes(); j++) {
bool test_simplification(Expr a, Expr b, Type t, const map<string, Expr> &vars) {
for (int j = 0; j < t.lanes(); j++) {
Expr a_j = a;
Expr b_j = b;
if (T.lanes() != 1) {
if (t.lanes() != 1) {
a_j = extract_lane(a, j);
b_j = extract_lane(b, j);
}
Expand Down Expand Up @@ -250,7 +257,10 @@ bool test_expression(FuzzedDataProvider &fdp, Expr test, int samples) {
return true;
}

// These are here to enable copy of failed output expressions and pasting them into the test for debugging.
// These are here to enable copy of failed output expressions
// and pasting them into the test for debugging; they are commented out
// to avoid "unused function" warnings in some build environments.
#if 0
Expr ramp(Expr b, Expr s, int w) {
return Ramp::make(b, s, w);
}
Expand Down Expand Up @@ -314,6 +324,7 @@ Expr int16x2(Expr x) {
Expr int32x2(Expr x) {
return Cast::make(Int(32).with_lanes(2), x);
}
#endif

Expr a(Variable::make(Int(0), fuzz_var(0)));
Expr b(Variable::make(Int(0), fuzz_var(1)));
Expand Down

0 comments on commit be1d051

Please sign in to comment.