/* * 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