552 lines
12 KiB
C++
552 lines
12 KiB
C++
/*
|
|
* Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* on the rights to use, copy, modify, merge, publish, distribute, sub
|
|
* license, and/or sell copies of the Software, and to permit persons to whom
|
|
* the Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the next
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
* Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
|
|
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
* USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
*
|
|
* Authors:
|
|
* Vadim Girlin
|
|
*/
|
|
|
|
#include "sb_bc.h"
|
|
#include "sb_shader.h"
|
|
#include "sb_pass.h"
|
|
|
|
namespace r600_sb {
|
|
|
|
bool node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
|
bool container_node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
|
bool alu_group_node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
|
bool alu_node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
|
bool cf_node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
|
bool fetch_node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
|
bool region_node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
|
|
|
bool repeat_node::accept(vpass& p, bool enter) {
|
|
return p.visit(*this, enter);
|
|
}
|
|
|
|
bool depart_node::accept(vpass& p, bool enter) {
|
|
return p.visit(*this, enter);
|
|
}
|
|
bool if_node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
|
bool bb_node::accept(vpass& p, bool enter) { return p.visit(*this, enter); }
|
|
bool alu_packed_node::accept(vpass& p, bool enter) {
|
|
return p.visit(*this, enter);
|
|
}
|
|
|
|
void alu_packed_node::init_args(bool repl) {
|
|
alu_node *p = static_cast<alu_node*>(first);
|
|
assert(p->is_valid());
|
|
while (p) {
|
|
dst.insert(dst.end(), p->dst.begin(), p->dst.end());
|
|
src.insert(src.end(), p->src.begin(), p->src.end());
|
|
p = static_cast<alu_node*>(p->next);
|
|
}
|
|
|
|
value *replicated_value = NULL;
|
|
|
|
for (vvec::iterator I = dst.begin(), E = dst.end(); I != E; ++I) {
|
|
value *v = *I;
|
|
if (v) {
|
|
if (repl) {
|
|
if (replicated_value)
|
|
v->assign_source(replicated_value);
|
|
else
|
|
replicated_value = v;
|
|
}
|
|
|
|
v->def = this;
|
|
}
|
|
}
|
|
}
|
|
|
|
void container_node::insert_node_before(node* s, node* n) {
|
|
if (s->prev) {
|
|
node *sp = s->prev;
|
|
sp->next = n;
|
|
n->prev = sp;
|
|
n->next = s;
|
|
s->prev = n;
|
|
} else {
|
|
n->next = s;
|
|
s->prev = n;
|
|
first = n;
|
|
}
|
|
n->parent = this;
|
|
}
|
|
|
|
void container_node::insert_node_after(node* s, node* n) {
|
|
if (s->next) {
|
|
node *sn = s->next;
|
|
sn->prev = n;
|
|
n->next = sn;
|
|
n->prev = s;
|
|
s->next = n;
|
|
} else {
|
|
n->prev = s;
|
|
s->next = n;
|
|
last = n;
|
|
}
|
|
n->parent = this;
|
|
}
|
|
|
|
void container_node::move(iterator b, iterator e) {
|
|
assert(b != e);
|
|
|
|
container_node *source_container = b->parent;
|
|
node *l = source_container->cut(b, e);
|
|
|
|
first = last = l;
|
|
first->parent = this;
|
|
|
|
while (last->next) {
|
|
last = last->next;
|
|
last->parent = this;
|
|
}
|
|
}
|
|
|
|
node* container_node::cut(iterator b, iterator e) {
|
|
assert(!*b || b->parent == this);
|
|
assert(!*e || e->parent == this);
|
|
assert(b != e);
|
|
|
|
if (b->prev) {
|
|
b->prev->next = *e;
|
|
} else {
|
|
first = *e;
|
|
}
|
|
|
|
if (*e) {
|
|
e->prev->next = NULL;
|
|
e->prev = b->prev;
|
|
} else {
|
|
last->next = NULL;
|
|
last = b->prev;
|
|
}
|
|
|
|
b->prev = NULL;
|
|
|
|
return *b;
|
|
}
|
|
|
|
unsigned container_node::count() {
|
|
unsigned c = 0;
|
|
node *t = first;
|
|
while (t) {
|
|
t = t->next;
|
|
c++;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
void container_node::remove_node(node *n) {
|
|
if (n->prev)
|
|
n->prev->next = n->next;
|
|
else
|
|
first = n->next;
|
|
if (n->next)
|
|
n->next->prev = n->prev;
|
|
else
|
|
last = n->prev;
|
|
n->parent = NULL;
|
|
}
|
|
|
|
void container_node::expand(container_node *n) {
|
|
if (!n->empty()) {
|
|
node *e0 = n->first;
|
|
node *e1 = n->last;
|
|
|
|
e0->prev = n->prev;
|
|
if (e0->prev) {
|
|
e0->prev->next = e0;
|
|
} else {
|
|
first = e0;
|
|
}
|
|
|
|
e1->next = n->next;
|
|
if (e1->next)
|
|
e1->next->prev = e1;
|
|
else
|
|
last = e1;
|
|
|
|
do {
|
|
e0->parent = this;
|
|
e0 = e0->next;
|
|
} while (e0 != e1->next);
|
|
} else
|
|
remove_node(n);
|
|
}
|
|
|
|
void container_node::push_back(node *n) {
|
|
if (last) {
|
|
last->next = n;
|
|
n->next = NULL;
|
|
n->prev = last;
|
|
last = n;
|
|
} else {
|
|
assert(!first);
|
|
first = last = n;
|
|
n->prev = n->next = NULL;
|
|
}
|
|
n->parent = this;
|
|
}
|
|
void container_node::push_front(node *n) {
|
|
if (first) {
|
|
first->prev = n;
|
|
n->prev = NULL;
|
|
n->next = first;
|
|
first = n;
|
|
} else {
|
|
assert(!last);
|
|
first = last = n;
|
|
n->prev = n->next = NULL;
|
|
}
|
|
n->parent = this;
|
|
}
|
|
|
|
void node::insert_before(node* n) {
|
|
parent->insert_node_before(this, n);
|
|
}
|
|
|
|
void node::insert_after(node* n) {
|
|
parent->insert_node_after(this, n);
|
|
}
|
|
|
|
void node::replace_with(node* n) {
|
|
n->prev = prev;
|
|
n->next = next;
|
|
n->parent = parent;
|
|
if (prev)
|
|
prev->next = n;
|
|
if (next)
|
|
next->prev = n;
|
|
|
|
if (parent->first == this)
|
|
parent->first = n;
|
|
|
|
if (parent->last == this)
|
|
parent->last = n;
|
|
|
|
parent = NULL;
|
|
next = prev = NULL;
|
|
}
|
|
|
|
void container_node::expand() {
|
|
parent->expand(this);
|
|
}
|
|
|
|
void node::remove() {parent->remove_node(this);
|
|
}
|
|
|
|
value_hash node::hash_src() const {
|
|
|
|
value_hash h = 12345;
|
|
|
|
for (int k = 0, e = src.size(); k < e; ++k) {
|
|
value *s = src[k];
|
|
if (s)
|
|
h ^= (s->hash());
|
|
}
|
|
|
|
return h;
|
|
}
|
|
|
|
|
|
value_hash node::hash() const {
|
|
|
|
if (parent && parent->subtype == NST_LOOP_PHI_CONTAINER)
|
|
return 47451;
|
|
|
|
return hash_src() ^ (subtype << 13) ^ (type << 3);
|
|
}
|
|
|
|
void r600_sb::container_node::append_from(container_node* c) {
|
|
if (!c->first)
|
|
return;
|
|
|
|
node *b = c->first;
|
|
|
|
if (last) {
|
|
last->next = c->first;
|
|
last->next->prev = last;
|
|
} else {
|
|
first = c->first;
|
|
}
|
|
|
|
last = c->last;
|
|
c->first = NULL;
|
|
c->last = NULL;
|
|
|
|
while (b) {
|
|
b->parent = this;
|
|
b = b->next;
|
|
}
|
|
}
|
|
|
|
bool node::fold_dispatch(expr_handler* ex) { return ex->fold(*this); }
|
|
bool container_node::fold_dispatch(expr_handler* ex) { return ex->fold(*this); }
|
|
bool alu_node::fold_dispatch(expr_handler* ex) { return ex->fold(*this); }
|
|
bool alu_packed_node::fold_dispatch(expr_handler* ex) { return ex->fold(*this); }
|
|
bool fetch_node::fold_dispatch(expr_handler* ex) { return ex->fold(*this); }
|
|
bool cf_node::fold_dispatch(expr_handler* ex) { return ex->fold(*this); }
|
|
|
|
unsigned alu_packed_node::get_slot_mask() {
|
|
unsigned mask = 0;
|
|
for (node_iterator I = begin(), E = end(); I != E; ++I)
|
|
mask |= 1 << static_cast<alu_node*>(*I)->bc.slot;
|
|
return mask;
|
|
}
|
|
|
|
void alu_packed_node::update_packed_items(sb_context &ctx) {
|
|
|
|
vvec::iterator SI(src.begin()), DI(dst.begin());
|
|
|
|
assert(first);
|
|
|
|
alu_node *c = static_cast<alu_node*>(first);
|
|
unsigned flags = c->bc.op_ptr->flags;
|
|
unsigned slot_flags = c->bc.slot_flags;
|
|
|
|
// fixup dst for instructions that replicate output
|
|
if (((flags & AF_REPL) && slot_flags == AF_4V) ||
|
|
(ctx.is_cayman() && slot_flags == AF_S)) {
|
|
|
|
value *swp[4] = {};
|
|
|
|
unsigned chan;
|
|
|
|
for (vvec::iterator I2 = dst.begin(), E2 = dst.end();
|
|
I2 != E2; ++I2) {
|
|
value *v = *I2;
|
|
if (v) {
|
|
chan = v->get_final_chan();
|
|
assert(!swp[chan] || swp[chan] == v);
|
|
swp[chan] = v;
|
|
}
|
|
}
|
|
|
|
chan = 0;
|
|
for (vvec::iterator I2 = dst.begin(), E2 = dst.end();
|
|
I2 != E2; ++I2, ++chan) {
|
|
*I2 = swp[chan];
|
|
}
|
|
}
|
|
|
|
for (node_iterator I = begin(), E = end(); I != E; ++I) {
|
|
alu_node *n = static_cast<alu_node*>(*I);
|
|
assert(n);
|
|
|
|
for (vvec::iterator I2 = n->src.begin(), E2 = n->src.end();
|
|
I2 != E2; ++I2, ++SI) {
|
|
*I2 = *SI;
|
|
}
|
|
for (vvec::iterator I2 = n->dst.begin(), E2 = n->dst.end();
|
|
I2 != E2; ++I2, ++DI) {
|
|
*I2 = *DI;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool node::is_cf_op(unsigned op) {
|
|
if (!is_cf_inst())
|
|
return false;
|
|
cf_node *c = static_cast<cf_node*>(this);
|
|
return c->bc.op == op;
|
|
}
|
|
|
|
bool node::is_alu_op(unsigned op) {
|
|
if (!is_alu_inst())
|
|
return false;
|
|
alu_node *c = static_cast<alu_node*>(this);
|
|
return c->bc.op == op;
|
|
}
|
|
|
|
bool node::is_fetch_op(unsigned op) {
|
|
if (!is_fetch_inst())
|
|
return false;
|
|
fetch_node *c = static_cast<fetch_node*>(this);
|
|
return c->bc.op == op;
|
|
}
|
|
|
|
|
|
|
|
bool node::is_mova() {
|
|
if (!is_alu_inst())
|
|
return false;
|
|
alu_node *a = static_cast<alu_node*>(this);
|
|
return (a->bc.op_ptr->flags & AF_MOVA);
|
|
}
|
|
|
|
bool node::is_pred_set() {
|
|
if (!is_alu_inst())
|
|
return false;
|
|
alu_node *a = static_cast<alu_node*>(this);
|
|
return (a->bc.op_ptr->flags & AF_ANY_PRED);
|
|
}
|
|
|
|
unsigned node::cf_op_flags() {
|
|
assert(is_cf_inst());
|
|
cf_node *c = static_cast<cf_node*>(this);
|
|
return c->bc.op_ptr->flags;
|
|
}
|
|
|
|
unsigned node::alu_op_flags() {
|
|
assert(is_alu_inst());
|
|
alu_node *c = static_cast<alu_node*>(this);
|
|
return c->bc.op_ptr->flags;
|
|
}
|
|
|
|
unsigned node::fetch_op_flags() {
|
|
assert(is_fetch_inst());
|
|
fetch_node *c = static_cast<fetch_node*>(this);
|
|
return c->bc.op_ptr->flags;
|
|
}
|
|
|
|
unsigned node::alu_op_slot_flags() {
|
|
assert(is_alu_inst());
|
|
alu_node *c = static_cast<alu_node*>(this);
|
|
return c->bc.slot_flags;
|
|
}
|
|
|
|
region_node* node::get_parent_region() {
|
|
node *p = this;
|
|
while ((p = p->parent))
|
|
if (p->is_region())
|
|
return static_cast<region_node*>(p);
|
|
return NULL;
|
|
}
|
|
|
|
unsigned container_node::real_alu_count() {
|
|
unsigned c = 0;
|
|
node *t = first;
|
|
while (t) {
|
|
if (t->is_alu_inst())
|
|
++c;
|
|
else if (t->is_alu_packed())
|
|
c += static_cast<container_node*>(t)->count();
|
|
t = t->next;
|
|
}
|
|
return c;
|
|
}
|
|
|
|
void container_node::collect_stats(node_stats& s) {
|
|
|
|
for (node_iterator I = begin(), E = end(); I != E; ++I) {
|
|
node *n = *I;
|
|
if (n->is_container()) {
|
|
static_cast<container_node*>(n)->collect_stats(s);
|
|
}
|
|
|
|
if (n->is_alu_inst()) {
|
|
++s.alu_count;
|
|
alu_node *a = static_cast<alu_node*>(n);
|
|
if (a->bc.op_ptr->flags & AF_KILL)
|
|
++s.alu_kill_count;
|
|
else if (a->is_copy_mov())
|
|
++s.alu_copy_mov_count;
|
|
if (a->uses_ar())
|
|
s.uses_ar = true;
|
|
} else if (n->is_fetch_inst())
|
|
++s.fetch_count;
|
|
else if (n->is_cf_inst())
|
|
++s.cf_count;
|
|
else if (n->is_region()) {
|
|
++s.region_count;
|
|
region_node *r = static_cast<region_node*>(n);
|
|
if(r->is_loop())
|
|
++s.loop_count;
|
|
|
|
if (r->phi)
|
|
s.phi_count += r->phi->count();
|
|
if (r->loop_phi)
|
|
s.loop_phi_count += r->loop_phi->count();
|
|
}
|
|
else if (n->is_depart())
|
|
++s.depart_count;
|
|
else if (n->is_repeat())
|
|
++s.repeat_count;
|
|
else if (n->is_if())
|
|
++s.if_count;
|
|
}
|
|
}
|
|
|
|
void region_node::expand_depart(depart_node *d) {
|
|
depart_vec::iterator I = departs.begin() + d->dep_id, E;
|
|
I = departs.erase(I);
|
|
E = departs.end();
|
|
while (I != E) {
|
|
--(*I)->dep_id;
|
|
++I;
|
|
}
|
|
d->expand();
|
|
}
|
|
|
|
void region_node::expand_repeat(repeat_node *r) {
|
|
repeat_vec::iterator I = repeats.begin() + r->rep_id - 1, E;
|
|
I = repeats.erase(I);
|
|
E = repeats.end();
|
|
while (I != E) {
|
|
--(*I)->rep_id;
|
|
++I;
|
|
}
|
|
r->expand();
|
|
}
|
|
|
|
void node_stats::dump() {
|
|
sblog << " alu_count : " << alu_count << "\n";
|
|
sblog << " alu_kill_count : " << alu_kill_count << "\n";
|
|
sblog << " alu_copy_mov_count : " << alu_copy_mov_count << "\n";
|
|
sblog << " cf_count : " << cf_count << "\n";
|
|
sblog << " fetch_count : " << fetch_count << "\n";
|
|
sblog << " region_count : " << region_count << "\n";
|
|
sblog << " loop_count : " << loop_count << "\n";
|
|
sblog << " phi_count : " << phi_count << "\n";
|
|
sblog << " loop_phi_count : " << loop_phi_count << "\n";
|
|
sblog << " depart_count : " << depart_count << "\n";
|
|
sblog << " repeat_count : " << repeat_count << "\n";
|
|
sblog << " if_count : " << if_count << "\n";
|
|
}
|
|
|
|
unsigned alu_node::interp_param() {
|
|
if (!(bc.op_ptr->flags & AF_INTERP))
|
|
return 0;
|
|
unsigned param;
|
|
if (bc.op_ptr->src_count == 2) {
|
|
param = src[1]->select.sel();
|
|
} else {
|
|
param = src[0]->select.sel();
|
|
}
|
|
return param + 1;
|
|
}
|
|
|
|
alu_group_node* alu_node::get_alu_group_node() {
|
|
node *p = parent;
|
|
if (p) {
|
|
if (p->subtype == NST_ALU_PACKED_INST) {
|
|
assert(p->parent && p->parent->subtype == NST_ALU_GROUP);
|
|
p = p->parent;
|
|
}
|
|
return static_cast<alu_group_node*>(p);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
} // namespace r600_sb
|