#include "gpu/intel/jit/pass/strength_reduce.hpp"
#include "gemmstone/../../dsl/ir/pass/simplify.hpp"
#include "gemmstone/../../dsl/ir/pass/trace.hpp"
namespace dnnl {
namespace impl {
namespace gpu {
namespace intel {
namespace jit {
class loop_strength_reducer_t : public ir_mutator_t {
public:
loop_strength_reducer_t() {
loops_.emplace_back();
}
~loop_strength_reducer_t() override {
gpu_assert(post_inc_stores_.empty());
}
object_t _mutate(const for_t &obj) override {
loops_.emplace_back(obj);
auto new_obj = ir_mutator_t::_mutate(obj);
return inject_stores_and_pop_loop(new_obj);
}
object_t _mutate(const let_t &obj) override {
int loop_level = int(loops_.size()) - 1;
auto ret = lets_.insert(
{obj.var, let_info_t(obj.var, obj.value, loop_level)});
gpu_assert(ret.second);
MAYBE_UNUSED(ret);
auto new_obj = ir_mutator_t::_mutate(obj);
lets_.erase(obj.var);
return new_obj;
}
object_t _mutate(const stmt_group_t &obj) override {
gpu_assert(post_inc_stores_.empty());
if ((compute_loop_level_ == -1)
&& (obj.label == stmt_label_t::compute_loop()))
compute_loop_level_ = int(loops_.size());
stmt_t new_obj;
if (obj.body.is<for_t>()) {
loops_.emplace_back(obj.body);
auto body = ir_mutator_t::_mutate(obj.body.as<for_t>());
if (body.is_same(obj.body)) return obj;
new_obj = stmt_group_t::make(
obj.label, inject_post_inc_stores(body));
new_obj = inject_stores_and_pop_loop(new_obj);
} else {
new_obj = ir_mutator_t::_mutate(obj);
auto body = new_obj.as<stmt_group_t>().body;
body = inject_post_inc_stores(body);
new_obj = replace_stmt_body(new_obj, body);
}
post_inc_stores_.clear();
return std::move(new_obj);
}
object_t _mutate(const store_t &obj) override {
if (loops_.size() == 1) return ir_mutator_t::_mutate(obj);
int init_store_level = -1;
stmt_t init_store_stmt = obj;
post_inc_store_info_t post_inc_store(obj);
for (int level = int(loops_.size()) - 1; level >= 1; level--) {
auto &loop_info = loops_[level];
int refs = count_object(loop_info.loop, obj.buf);
if (refs != 2) break;
auto &store = init_store_stmt.as<store_t>();
auto &store_value = store.value;
auto &loop_var = loop_info.loop_var();
auto cur_value = substitute_let(store_value, level);
auto next_value = substitute(cur_value, loop_var, loop_var + 1);
auto inc = simplify(next_value - cur_value);
if (contains_object(inc, loop_var)) break;
if (!store_value.type().is_scalar()) break;
init_store_level = level;
auto new_store_value
= substitute(cur_value, loop_var, loop_info.loop_init());
init_store_stmt = store_t::make(store.buf, store.off,
simplify(new_store_value), store.stride);
post_inc_store.update(loop_info, inc);
}
if ((init_store_level <= 0) || (init_store_level > compute_loop_level_))
return ir_mutator_t::_mutate(obj);
loops_[init_store_level].init_stores.push_back(init_store_stmt);
if (!post_inc_store.is_empty()) {
post_inc_stores_.push_back(std::move(post_inc_store));
}
return stmt_t();
}
private:
struct loop_info_t {
loop_info_t(const stmt_t &loop = {}) : loop(loop) {}
const expr_t &loop_var() const { return loop.as<for_t>().var; }
const expr_t &loop_init() const { return loop.as<for_t>().init; }
const expr_t &loop_bound() const { return loop.as<for_t>().bound; }
expr_t loop_extent() const { return loop_bound() - loop_init(); }
stmt_t loop;
std::vector<stmt_t> init_stores;
std::vector<stmt_t> lets;
};
struct let_info_t {
let_info_t(const expr_t &var, const expr_t &value, int loop_level)
: var(var), value(value), loop_level(loop_level) {}
expr_t var;
expr_t value;
int loop_level;
};
struct post_inc_store_info_t {
post_inc_store_info_t(const store_t &obj)
: store(&obj), inc(0), last_iter_cond(true), compensation(0) {}
stmt_t stmt() const {
auto load
= load_t::make(store->value.type(), store->buf, store->off);
return store_t::make(store->buf, store->off, load + inc);
}
bool is_empty() const { return inc.is(0); }
void update(const loop_info_t &loop, const expr_t &loop_inc) {
inc = simplify(iif_t::make(
last_iter_cond, inc - compensation + loop_inc, inc));
if (last_iter_cond.is_equal(expr_t(true))) {
last_iter_cond = (loop.loop_var() == loop.loop_bound() - 1);
} else {
last_iter_cond = last_iter_cond
& (loop.loop_var() == loop.loop_bound() - 1);
}
compensation = simplify(loop.loop_extent() * loop_inc);
}
const store_t *store;
expr_t inc;
expr_t last_iter_cond;
expr_t compensation;
};
expr_t substitute_let(const expr_t &_e, int loop_level) const {
auto e = _e;
for (;;) {
bool found = false;
auto vars = find_unique_objects<var_t>(e);
for (auto &v : vars) {
auto it = lets_.find(v);
if (it == lets_.end()) continue;
auto &let_info = it->second;
if (let_info.loop_level < loop_level) continue;
found = true;
e = substitute(e, v, let_info.value);
}
if (!found) break;
}
return e;
}
object_t inject_stores_and_pop_loop(const stmt_t &_s) {
stmt_t s = _s;
auto &stores = loops_.back().init_stores;
for (auto it = stores.rbegin(); it != stores.rend(); ++it) {
s = stmt_seq_t::make(*it, s);
}
loops_.pop_back();
gpu_assert(!loops_.empty());
return std::move(s);
}
object_t inject_post_inc_stores(const stmt_t &_s) {
stmt_t s = _s;
for (auto &inc_store : post_inc_stores_) {
s = s.append(inc_store.stmt());
}
return std::move(s);
}
int compute_loop_level_ = -1;
std::vector<loop_info_t> loops_;
std::vector<post_inc_store_info_t> post_inc_stores_;
object_map_t<expr_t, let_info_t> lets_;
};
stmt_t loop_strength_reduce(const stmt_t &s, ir_context_t &ir_ctx) {
ir::trace_start();
auto ret = loop_strength_reducer_t().mutate(s);
ir::trace_pass("loop_strength_reduce", ret, ir_ctx);
return ret;
}
} } } } }