/*
* 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_shader.h"
#include "sb_pass.h"
namespace r600_sb {
bool dump::visit(node& n, bool enter) {
if (enter) {
indent();
dump_flags(n);
switch (n.subtype) {
case NST_PHI:
dump_op(n, "* phi");
break;
case NST_PSI:
dump_op(n, "* psi");
break;
case NST_COPY:
dump_op(n, "* copy");
break;
default:
assert(!"invalid node subtype");
break;
}
sblog << "\n";
}
return false;
}
bool dump::visit(container_node& n, bool enter) {
if (enter) {
if (!n.empty()) {
indent();
dump_flags(n);
sblog << "{ ";
if (!n.dst.empty()) {
sblog << " preloaded inputs [";
dump_vec(n.dst);
sblog << "] ";
}
dump_live_values(n, true);
}
++level;
} else {
--level;
if (!n.empty()) {
indent();
sblog << "} ";
if (!n.src.empty()) {
sblog << " results [";
dump_vec(n.src);
sblog << "] ";
}
dump_live_values(n, false);
}
}
return true;
}
bool dump::visit(bb_node& n, bool enter) {
if (enter) {
indent();
dump_flags(n);
sblog << "{ BB_" << n.id << " loop_level = " << n.loop_level << " ";
dump_live_values(n, true);
++level;
} else {
--level;
indent();
sblog << "} end BB_" << n.id << " ";
dump_live_values(n, false);
}
return true;
}
bool dump::visit(alu_group_node& n, bool enter) {
if (enter) {
indent();
dump_flags(n);
sblog << "[ ";
dump_live_values(n, true);
++level;
} else {
--level;
indent();
sblog << "] ";
dump_live_values(n, false);
}
return true;
}
bool dump::visit(cf_node& n, bool enter) {
if (enter) {
indent();
dump_flags(n);
dump_op(n, n.bc.op_ptr->name);
if (n.bc.op_ptr->flags & CF_BRANCH) {
sblog << " @" << (n.bc.addr << 1);
}
dump_common(n);
sblog << "\n";
if (!n.empty()) {
indent();
sblog << "< ";
dump_live_values(n, true);
}
++level;
} else {
--level;
if (!n.empty()) {
indent();
sblog << "> ";
dump_live_values(n, false);
}
}
return true;
}
bool dump::visit(alu_node& n, bool enter) {
if (enter) {
indent();
dump_flags(n);
dump_alu(&n);
dump_common(n);
sblog << "\n";
++level;
} else {
--level;
}
return true;
}
bool dump::visit(alu_packed_node& n, bool enter) {
if (enter) {
indent();
dump_flags(n);
dump_op(n, n.op_ptr()->name);
sblog << " ";
dump_live_values(n, true);
++level;
} else {
--level;
if (!n.live_after.empty()) {
indent();
dump_live_values(n, false);
}
}
// proccess children only if their src/dst aren't moved to this node yet
return n.src.empty();
}
bool dump::visit(fetch_node& n, bool enter) {
if (enter) {
indent();
dump_flags(n);
dump_op(n, n.bc.op_ptr->name);
sblog << "\n";
++level;
} else {
--level;
}
return true;
}
bool dump::visit(region_node& n, bool enter) {
if (enter) {
indent();
dump_flags(n);
sblog << "region #" << n.region_id << " ";
dump_common(n);
if (!n.vars_defined.empty()) {
sblog << "vars_defined: ";
dump_set(sh, n.vars_defined);
}
dump_live_values(n, true);
++level;
if (n.loop_phi)
run_on(*n.loop_phi);
} else {
--level;
if (n.phi)
run_on(*n.phi);
indent();
dump_live_values(n, false);
}
return true;
}
bool dump::visit(repeat_node& n, bool enter) {
if (enter) {
indent();
dump_flags(n);
sblog << "repeat region #" << n.target->region_id;
sblog << (n.empty() ? " " : " after { ");
dump_common(n);
sblog << " ";
dump_live_values(n, true);
++level;
} else {
--level;
if (!n.empty()) {
indent();
sblog << "} end_repeat ";
dump_live_values(n, false);
}
}
return true;
}
bool dump::visit(depart_node& n, bool enter) {
if (enter) {
indent();
dump_flags(n);
sblog << "depart region #" << n.target->region_id;
sblog << (n.empty() ? " " : " after { ");
dump_common(n);
sblog << " ";
dump_live_values(n, true);
++level;
} else {
--level;
if (!n.empty()) {
indent();
sblog << "} end_depart ";
dump_live_values(n, false);
}
}
return true;
}
bool dump::visit(if_node& n, bool enter) {
if (enter) {
indent();
dump_flags(n);
sblog << "if " << *n.cond << " ";
dump_common(n);
sblog << " ";
dump_live_values(n, true);
indent();
sblog <<"{\n";
++level;
} else {
--level;
indent();
sblog << "} endif ";
dump_live_values(n, false);
}
return true;
}
void dump::indent() {
sblog.print_wl("", level * 4);
}
void dump::dump_vec(const vvec & vv) {
bool first = true;
for(vvec::const_iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
value *v = *I;
if (!first)
sblog << ", ";
else
first = false;
if (v) {
sblog << *v;
} else {
sblog << "__";
}
}
}
void dump::dump_rels(vvec & vv) {
for(vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
value *v = *I;
if (!v || !v->is_rel())
continue;
sblog << "\n\t\t\t\t\t";
sblog << " rels: " << *v << " : ";
dump_vec(v->mdef);
sblog << " <= ";
dump_vec(v->muse);
}
}
void dump::dump_op(node &n, const char *name) {
if (n.pred) {
alu_node &a = static_cast<alu_node&>(n);
sblog << (a.bc.pred_sel-2) << " [" << *a.pred << "] ";
}
sblog << name;
bool has_dst = !n.dst.empty();
if (n.subtype == NST_CF_INST) {
cf_node *c = static_cast<cf_node*>(&n);
if (c->bc.op_ptr->flags & CF_EXP) {
static const char *exp_type[] = {"PIXEL", "POS ", "PARAM"};
sblog << " " << exp_type[c->bc.type] << " " << c->bc.array_base;
has_dst = false;
} else if (c->bc.op_ptr->flags & (CF_MEM)) {
static const char *exp_type[] = {"WRITE", "WRITE_IND", "WRITE_ACK",
"WRITE_IND_ACK"};
sblog << " " << exp_type[c->bc.type] << " " << c->bc.array_base
<< " ES:" << c->bc.elem_size;
if (!(c->bc.op_ptr->flags & CF_EMIT)) {
has_dst = false;
}
}
}
sblog << " ";
if (has_dst) {
dump_vec(n.dst);
sblog << ", ";
}
dump_vec(n.src);
}
void dump::dump_set(shader &sh, val_set& v) {
sblog << "[";
for(val_set::iterator I = v.begin(sh), E = v.end(sh); I != E; ++I) {
value *val = *I;
sblog << *val << " ";
}
sblog << "]";
}
void dump::dump_common(node& n) {
}
void dump::dump_flags(node &n) {
if (n.flags & NF_DEAD)
sblog << "### DEAD ";
if (n.flags & NF_REG_CONSTRAINT)
sblog << "R_CONS ";
if (n.flags & NF_CHAN_CONSTRAINT)
sblog << "CH_CONS ";
if (n.flags & NF_ALU_4SLOT)
sblog << "4S ";
}
void dump::dump_val(value* v) {
sblog << *v;
}
void dump::dump_alu(alu_node *n) {
if (n->is_copy_mov())
sblog << "(copy) ";
if (n->pred) {
sblog << (n->bc.pred_sel-2) << " [" << *n->pred << "] ";
}
sblog << n->bc.op_ptr->name;
if (n->bc.omod) {
static const char *omod_str[] = {"", "*2", "*4", "/2"};
sblog << omod_str[n->bc.omod];
}
if (n->bc.clamp) {
sblog << "_sat";
}
bool has_dst = !n->dst.empty();
sblog << " ";
if (has_dst) {
dump_vec(n->dst);
sblog << ", ";
}
unsigned s = 0;
for (vvec::iterator I = n->src.begin(), E = n->src.end(); I != E;
++I, ++s) {
bc_alu_src &src = n->bc.src[s];
if (src.neg)
sblog << "-";
if (src.abs)
sblog << "|";
dump_val(*I);
if (src.abs)
sblog << "|";
if (I + 1 != E)
sblog << ", ";
}
dump_rels(n->dst);
dump_rels(n->src);
}
void dump::dump_op(node* n) {
if (n->type == NT_IF) {
dump_op(*n, "IF ");
return;
}
switch(n->subtype) {
case NST_ALU_INST:
dump_alu(static_cast<alu_node*>(n));
break;
case NST_FETCH_INST:
dump_op(*n, static_cast<fetch_node*>(n)->bc.op_ptr->name);
break;
case NST_CF_INST:
case NST_ALU_CLAUSE:
case NST_TEX_CLAUSE:
case NST_VTX_CLAUSE:
case NST_GDS_CLAUSE:
dump_op(*n, static_cast<cf_node*>(n)->bc.op_ptr->name);
break;
case NST_ALU_PACKED_INST:
dump_op(*n, static_cast<alu_packed_node*>(n)->op_ptr()->name);
break;
case NST_PHI:
dump_op(*n, "PHI");
break;
case NST_PSI:
dump_op(*n, "PSI");
break;
case NST_COPY:
dump_op(*n, "COPY");
break;
default:
dump_op(*n, "??unknown_op");
}
}
void dump::dump_op_list(container_node* c) {
for (node_iterator I = c->begin(), E = c->end(); I != E; ++I) {
dump_op(*I);
sblog << "\n";
}
}
void dump::dump_queue(sched_queue& q) {
for (sched_queue::iterator I = q.begin(), E = q.end(); I != E; ++I) {
dump_op(*I);
sblog << "\n";
}
}
void dump::dump_live_values(container_node &n, bool before) {
if (before) {
if (!n.live_before.empty()) {
sblog << "live_before: ";
dump_set(sh, n.live_before);
}
} else {
if (!n.live_after.empty()) {
sblog << "live_after: ";
dump_set(sh, n.live_after);
}
}
sblog << "\n";
}
} // namespace r600_sb