1/*
2 * Copyright 2013 Vadim Girlin <vadimgirlin@gmail.com>
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * on the rights to use, copy, modify, merge, publish, distribute, sub
8 * license, and/or sell copies of the Software, and to permit persons to whom
9 * the Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21 * USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *
23 * Authors:
24 *      Vadim Girlin
25 */
26
27#include "sb_shader.h"
28#include "sb_pass.h"
29
30namespace r600_sb {
31
32int ra_checker::run() {
33
34	rm_stack.clear();
35	rm_stack.resize(1);
36	rm_stk_level = 0;
37
38	process_op_dst(sh.root);
39
40	run_on(sh.root);
41
42	assert(rm_stk_level == 0);
43
44	dump_all_errors();
45
46	assert(sh.errors.empty());
47
48	return 0;
49}
50
51void ra_checker::dump_error(const error_info &e) {
52
53	sblog << "error at : ";
54	dump::dump_op(e.n);
55
56	sblog << "\n";
57	sblog << "  : " << e.message << "\n";
58}
59
60void ra_checker::dump_all_errors() {
61	for (error_map::iterator I = sh.errors.begin(), E = sh.errors.end();
62			I != E; ++I) {
63		dump_error(I->second);
64	}
65}
66
67
68void ra_checker::error(node *n, unsigned id, std::string msg) {
69	error_info e;
70	e.n = n;
71	e.arg_index = id;
72	e.message = msg;
73	sh.errors.insert(std::make_pair(n, e));
74}
75
76void ra_checker::push_stack() {
77	++rm_stk_level;
78	if (rm_stack.size() == rm_stk_level)
79		rm_stack.push_back(rm_stack.back());
80	else
81		rm_stack[rm_stk_level] = rm_stack[rm_stk_level - 1];
82}
83
84void ra_checker::pop_stack() {
85	--rm_stk_level;
86}
87
88void ra_checker::kill_alu_only_regs() {
89	// TODO
90}
91
92void ra_checker::check_value_gpr(node *n, unsigned id, value *v) {
93	sel_chan gpr = v->gpr;
94	if (!gpr) {
95		sb_ostringstream o;
96		o << "operand value " << *v << " is not allocated";
97		error(n, id, o.str());
98		return;
99	}
100	reg_value_map::iterator F = rmap().find(v->gpr);
101	if (F == rmap().end()) {
102		sb_ostringstream o;
103		o << "operand value " << *v << " was not previously written to its gpr";
104		error(n, id, o.str());
105		return;
106	}
107	if (!F->second->v_equal(v)) {
108		sb_ostringstream o;
109		o << "expected operand value " << *v
110				<< ", gpr contains " << *(F->second);
111		error(n, id, o.str());
112		return;
113	}
114
115
116}
117
118void ra_checker::check_src_vec(node *n, unsigned id, vvec &vv, bool src) {
119
120	for (vvec::iterator I = vv.begin(), E = vv.end(); I != E; ++I) {
121		value *v = *I;
122		if (!v || !v->is_sgpr())
123			continue;
124
125		if (v->is_rel()) {
126			if (!v->rel) {
127				sb_ostringstream o;
128				o << "expected relative offset in " << *v;
129				error(n, id, o.str());
130				return;
131			}
132		} else if (src) {
133			check_value_gpr(n, id, v);
134		}
135	}
136}
137
138void ra_checker::check_op_src(node *n) {
139	check_src_vec(n, 0, n->dst, false);
140	check_src_vec(n, 100, n->src, true);
141}
142
143void ra_checker::process_op_dst(node *n) {
144
145	unsigned id = 0;
146
147	for (vvec::iterator I = n->dst.begin(), E = n->dst.end(); I != E; ++I) {
148		value *v = *I;
149
150		++id;
151
152		if (!v)
153			continue;
154
155		if (v->is_sgpr()) {
156
157			if (!v->gpr) {
158				sb_ostringstream o;
159				o << "destination operand " << *v << " is not allocated";
160				error(n, id, o.str());
161				return;
162			}
163
164			rmap()[v->gpr] = v;
165		} else if (v->is_rel()) {
166			if (v->rel->is_const()) {
167				rmap()[v->get_final_gpr()] = v;
168			} else {
169				unsigned sz = v->array->array_size;
170				unsigned start = v->array->gpr;
171				for (unsigned i = 0; i < sz; ++i) {
172					rmap()[start + (i << 2)] = v;
173				}
174			}
175		}
176	}
177}
178
179void ra_checker::check_phi_src(container_node *p, unsigned id) {
180	for (node_iterator I = p->begin(), E = p->end(); I != E; ++I) {
181		node *n = *I;
182		value *s = n->src[id];
183		if (s->is_sgpr())
184			check_value_gpr(n, id, s);
185	}
186}
187
188void ra_checker::process_phi_dst(container_node *p) {
189	for (node_iterator I = p->begin(), E = p->end(); I != E; ++I) {
190		node *n = *I;
191		process_op_dst(n);
192	}
193}
194
195void ra_checker::check_alu_group(alu_group_node *g) {
196
197	for (node_iterator I = g->begin(), E = g->end(); I != E; ++I) {
198		node *a = *I;
199		if (!a->is_alu_inst()) {
200			sb_ostringstream o;
201			o << "non-alu node inside alu group";
202			error(a, 0, o.str());
203			return;
204		}
205
206		check_op_src(a);
207	}
208
209	std::fill(prev_dst, prev_dst + 5, (value*)NULL);
210
211	for (node_iterator I = g->begin(), E = g->end(); I != E; ++I) {
212		alu_node *a = static_cast<alu_node*>(*I);
213
214		process_op_dst(a);
215
216		unsigned slot = a->bc.slot;
217		prev_dst[slot] = a->dst[0];
218	}
219}
220
221void ra_checker::run_on(container_node* c) {
222
223	if (c->is_region()) {
224		region_node *r = static_cast<region_node*>(c);
225		if (r->loop_phi) {
226			check_phi_src(r->loop_phi, 0);
227			process_phi_dst(r->loop_phi);
228		}
229	} else if (c->is_depart()) {
230
231		push_stack();
232
233	} else if (c->is_repeat()) {
234
235		push_stack();
236
237	}
238
239	for (node_iterator I = c->begin(), E = c->end(); I != E; ++I) {
240		node *n = *I;
241
242		if(n->is_cf_inst() || n->is_fetch_inst()) {
243			check_op_src(n);
244			process_op_dst(n);
245		}
246
247		if (n->is_container()) {
248			if (n->is_alu_group()) {
249				check_alu_group(static_cast<alu_group_node*>(n));
250			} else {
251				container_node *nc = static_cast<container_node*>(n);
252				run_on(nc);
253			}
254		}
255	}
256
257	if (c->is_depart()) {
258		depart_node *r = static_cast<depart_node*>(c);
259		check_phi_src(r->target->phi, r->dep_id);
260		pop_stack();
261	} else if (c->is_repeat()) {
262		repeat_node *r = static_cast<repeat_node*>(c);
263		assert (r->target->loop_phi);
264
265		pop_stack();
266	} else if (c->is_region()) {
267		region_node *r = static_cast<region_node*>(c);
268		if (r->phi)
269			process_phi_dst(r->phi);
270	}
271}
272
273} // namespace r600_sb
274