* IN THE SOFTWARE.
*/
-#include "nir.h"
+#include "nir_schedule.h"
#include "util/dag.h"
#include "util/u_dynarray.h"
*/
int pressure;
- /* Number of channels that may be in use before we switch to the
- * pressure-prioritizing scheduling heuristic.
- */
- int threshold;
+ /* Options specified by the backend */
+ const nir_schedule_options *options;
} nir_schedule_scoreboard;
/* When walking the instructions in reverse, we use this flag to swap
*/
enum direction { F, R };
+struct nir_schedule_class_dep {
+ int klass;
+ nir_schedule_node *node;
+ struct nir_schedule_class_dep *next;
+};
+
typedef struct {
- nir_shader *shader;
+ nir_schedule_scoreboard *scoreboard;
- /* Map from nir_instr to nir_schedule_node * */
- struct hash_table *instr_map;
/* Map from nir_register to nir_schedule_node * */
struct hash_table *reg_map;
nir_schedule_node *discard;
nir_schedule_node *jump;
+ struct nir_schedule_class_dep *class_deps;
+
enum direction dir;
} nir_deps_state;
return true;
nir_schedule_node *dst_n = entry->data;
- nir_schedule_node *src_n = nir_schedule_get_node(state->instr_map,
- src->parent_instr);
+ nir_schedule_node *src_n =
+ nir_schedule_get_node(state->scoreboard->instr_map,
+ src->parent_instr);
add_dep(state, dst_n, src_n);
if (dest->is_ssa)
return true;
- nir_schedule_node *dest_n = nir_schedule_get_node(state->instr_map,
- dest->reg.parent_instr);
+ nir_schedule_node *dest_n =
+ nir_schedule_get_node(state->scoreboard->instr_map,
+ dest->reg.parent_instr);
struct hash_entry *entry = _mesa_hash_table_search(state->reg_map,
dest->reg.reg);
nir_schedule_ssa_deps(nir_ssa_def *def, void *in_state)
{
nir_deps_state *state = in_state;
- nir_schedule_node *def_n = nir_schedule_get_node(state->instr_map, def->parent_instr);
+ struct hash_table *instr_map = state->scoreboard->instr_map;
+ nir_schedule_node *def_n = nir_schedule_get_node(instr_map, def->parent_instr);
nir_foreach_use(src, def) {
- nir_schedule_node *use_n = nir_schedule_get_node(state->instr_map,
+ nir_schedule_node *use_n = nir_schedule_get_node(instr_map,
src->parent_instr);
add_read_dep(state, def_n, use_n);
return true;
}
+static struct nir_schedule_class_dep *
+nir_schedule_get_class_dep(nir_deps_state *state,
+ int klass)
+{
+ for (struct nir_schedule_class_dep *class_dep = state->class_deps;
+ class_dep != NULL;
+ class_dep = class_dep->next) {
+ if (class_dep->klass == klass)
+ return class_dep;
+ }
+
+ struct nir_schedule_class_dep *class_dep =
+ ralloc(state->reg_map, struct nir_schedule_class_dep);
+
+ class_dep->klass = klass;
+ class_dep->node = NULL;
+ class_dep->next = state->class_deps;
+
+ state->class_deps = class_dep;
+
+ return class_dep;
+}
+
static void
nir_schedule_intrinsic_deps(nir_deps_state *state,
nir_intrinsic_instr *instr)
{
- nir_schedule_node *n = nir_schedule_get_node(state->instr_map, &instr->instr);
+ nir_schedule_node *n = nir_schedule_get_node(state->scoreboard->instr_map,
+ &instr->instr);
+ const nir_schedule_options *options = state->scoreboard->options;
+ nir_schedule_dependency dep;
+
+ if (options->intrinsic_cb &&
+ options->intrinsic_cb(instr, &dep, options->intrinsic_cb_data)) {
+ struct nir_schedule_class_dep *class_dep =
+ nir_schedule_get_class_dep(state, dep.klass);
+
+ switch (dep.type) {
+ case NIR_SCHEDULE_READ_DEPENDENCY:
+ add_read_dep(state, class_dep->node, n);
+ break;
+ case NIR_SCHEDULE_WRITE_DEPENDENCY:
+ add_write_dep(state, &class_dep->node, n);
+ break;
+ }
+ }
switch (instr->intrinsic) {
case nir_intrinsic_load_uniform:
break;
case nir_intrinsic_store_output:
- /* For some non-FS shader stages, or for some hardware, output stores
- * affect the same shared memory as input loads.
+ /* For some hardware and stages, output stores affect the same shared
+ * memory as input loads.
*/
- if (state->shader->info.stage != MESA_SHADER_FRAGMENT)
+ if ((state->scoreboard->options->stages_with_shared_io_memory &
+ (1 << state->scoreboard->shader->info.stage)))
add_write_dep(state, &state->load_input, n);
/* Make sure that preceding discards stay before the store_output */
break;
case nir_intrinsic_load_input:
+ case nir_intrinsic_load_per_vertex_input:
add_read_dep(state, state->load_input, n);
break;
calculate_forward_deps(nir_schedule_scoreboard *scoreboard, nir_block *block)
{
nir_deps_state state = {
- .shader = scoreboard->shader,
+ .scoreboard = scoreboard,
.dir = F,
- .instr_map = scoreboard->instr_map,
.reg_map = _mesa_pointer_hash_table_create(NULL),
};
calculate_reverse_deps(nir_schedule_scoreboard *scoreboard, nir_block *block)
{
nir_deps_state state = {
- .shader = scoreboard->shader,
+ .scoreboard = scoreboard,
.dir = R,
- .instr_map = scoreboard->instr_map,
.reg_map = _mesa_pointer_hash_table_create(NULL),
};
return state.regs_freed;
}
+/**
+ * Chooses an instruction that will minimise the register pressure as much as
+ * possible. This should only be used as a fallback when the regular scheduling
+ * generates a shader whose register allocation fails.
+ */
+static nir_schedule_node *
+nir_schedule_choose_instruction_fallback(nir_schedule_scoreboard *scoreboard)
+{
+ nir_schedule_node *chosen = NULL;
+
+ /* Find the leader in the ready (shouldn't-stall) set with the mininum
+ * cost.
+ */
+ list_for_each_entry(nir_schedule_node, n, &scoreboard->dag->heads, dag.link) {
+ if (scoreboard->time < n->ready_time)
+ continue;
+
+ if (!chosen || chosen->max_delay > n->max_delay)
+ chosen = n;
+ }
+ if (chosen) {
+ if (debug) {
+ fprintf(stderr, "chose (ready fallback): ");
+ nir_print_instr(chosen->instr, stderr);
+ fprintf(stderr, "\n");
+ }
+
+ return chosen;
+ }
+
+ /* Otherwise, choose the leader with the minimum cost. */
+ list_for_each_entry(nir_schedule_node, n, &scoreboard->dag->heads, dag.link) {
+ if (!chosen || chosen->max_delay > n->max_delay)
+ chosen = n;
+ }
+ if (debug) {
+ fprintf(stderr, "chose (leader fallback): ");
+ nir_print_instr(chosen->instr, stderr);
+ fprintf(stderr, "\n");
+ }
+
+ return chosen;
+}
+
/**
* Chooses an instruction to schedule using the Goodman/Hsu (1988) CSP (Code
* Scheduling for Parallelism) heuristic.
}
nir_schedule_node *chosen;
- if (scoreboard->pressure < scoreboard->threshold)
+ if (scoreboard->options->fallback)
+ chosen = nir_schedule_choose_instruction_fallback(scoreboard);
+ else if (scoreboard->pressure < scoreboard->options->threshold)
chosen = nir_schedule_choose_instruction_csp(scoreboard);
else
chosen = nir_schedule_choose_instruction_csr(scoreboard);
}
static nir_schedule_scoreboard *
-nir_schedule_get_scoreboard(nir_shader *shader, int threshold)
+nir_schedule_get_scoreboard(nir_shader *shader,
+ const nir_schedule_options *options)
{
nir_schedule_scoreboard *scoreboard = rzalloc(NULL, nir_schedule_scoreboard);
scoreboard->shader = shader;
scoreboard->live_values = _mesa_pointer_set_create(scoreboard);
scoreboard->remaining_uses = _mesa_pointer_hash_table_create(scoreboard);
- scoreboard->threshold = threshold;
+ scoreboard->options = options;
scoreboard->pressure = 0;
nir_foreach_function(function, shader) {
* tune.
*/
void
-nir_schedule(nir_shader *shader, int threshold)
+nir_schedule(nir_shader *shader,
+ const nir_schedule_options *options)
{
nir_schedule_scoreboard *scoreboard = nir_schedule_get_scoreboard(shader,
- threshold);
+ options);
if (debug) {
fprintf(stderr, "NIR shader before scheduling:\n");