*/
#include "nir.h"
-#include "nir_control_flow_private.h"
+#include "nir_control_flow.h"
/* Secret Decoder Ring:
* clone_foo():
/* True if we are cloning an entire shader. */
bool global_clone;
+ /* If true allows the clone operation to fall back to the original pointer
+ * if no clone pointer is found in the remap table. This allows us to
+ * clone a loop body without having to add srcs from outside the loop to
+ * the remap table. This is useful for loop unrolling.
+ */
+ bool allow_remap_fallback;
+
/* maps orig ptr -> cloned ptr: */
struct hash_table *remap_table;
} clone_state;
static void
-init_clone_state(clone_state *state, bool global)
+init_clone_state(clone_state *state, struct hash_table *remap_table,
+ bool global, bool allow_remap_fallback)
{
state->global_clone = global;
- state->remap_table = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
- _mesa_key_pointer_equal);
+ state->allow_remap_fallback = allow_remap_fallback;
+
+ if (remap_table) {
+ state->remap_table = remap_table;
+ } else {
+ state->remap_table = _mesa_pointer_hash_table_create(NULL);
+ }
+
list_inithead(&state->phi_srcs);
}
if (!state->global_clone && global)
return (void *)ptr;
+ if (unlikely(!state->remap_table)) {
+ assert(state->allow_remap_fallback);
+ return (void *)ptr;
+ }
+
entry = _mesa_hash_table_search(state->remap_table, ptr);
- assert(entry && "Failed to find pointer!");
- if (!entry)
- return NULL;
+ if (!entry) {
+ assert(state->allow_remap_fallback);
+ return (void *)ptr;
+ }
return entry->data;
}
static nir_register *
remap_reg(clone_state *state, const nir_register *reg)
{
- return _lookup_ptr(state, reg, reg->is_global);
+ return _lookup_ptr(state, reg, false);
}
static nir_variable *
return _lookup_ptr(state, var, nir_variable_is_global(var));
}
-static nir_constant *
-clone_constant(clone_state *state, const nir_constant *c, nir_variable *nvar)
+nir_constant *
+nir_constant_clone(const nir_constant *c, nir_variable *nvar)
{
nir_constant *nc = ralloc(nvar, nir_constant);
- nc->value = c->value;
+ memcpy(nc->values, c->values, sizeof(nc->values));
nc->num_elements = c->num_elements;
nc->elements = ralloc_array(nvar, nir_constant *, c->num_elements);
for (unsigned i = 0; i < c->num_elements; i++) {
- nc->elements[i] = clone_constant(state, c->elements[i], nvar);
+ nc->elements[i] = nir_constant_clone(c->elements[i], nvar);
}
return nc;
}
-/* NOTE: for cloning nir_variable's, bypass nir_variable_create to avoid
+/* NOTE: for cloning nir_variables, bypass nir_variable_create to avoid
* having to deal with locals and globals separately:
*/
-static nir_variable *
-clone_variable(clone_state *state, const nir_variable *var)
+nir_variable *
+nir_variable_clone(const nir_variable *var, nir_shader *shader)
{
- nir_variable *nvar = rzalloc(state->ns, nir_variable);
- add_remap(state, nvar, var);
+ nir_variable *nvar = rzalloc(shader, nir_variable);
nvar->type = var->type;
nvar->name = ralloc_strdup(nvar, var->name);
nvar->data = var->data;
nvar->num_state_slots = var->num_state_slots;
- nvar->state_slots = ralloc_array(nvar, nir_state_slot, var->num_state_slots);
- memcpy(nvar->state_slots, var->state_slots,
- var->num_state_slots * sizeof(nir_state_slot));
+ if (var->num_state_slots) {
+ nvar->state_slots = ralloc_array(nvar, nir_state_slot, var->num_state_slots);
+ memcpy(nvar->state_slots, var->state_slots,
+ var->num_state_slots * sizeof(nir_state_slot));
+ }
if (var->constant_initializer) {
nvar->constant_initializer =
- clone_constant(state, var->constant_initializer, nvar);
+ nir_constant_clone(var->constant_initializer, nvar);
}
nvar->interface_type = var->interface_type;
+ nvar->num_members = var->num_members;
+ if (var->num_members) {
+ nvar->members = ralloc_array(nvar, struct nir_variable_data,
+ var->num_members);
+ memcpy(nvar->members, var->members,
+ var->num_members * sizeof(*var->members));
+ }
+
+ return nvar;
+}
+
+static nir_variable *
+clone_variable(clone_state *state, const nir_variable *var)
+{
+ nir_variable *nvar = nir_variable_clone(var, state->ns);
+ add_remap(state, nvar, var);
+
return nvar;
}
}
}
-/* NOTE: for cloning nir_register's, bypass nir_global/local_reg_create()
+/* NOTE: for cloning nir_registers, bypass nir_global/local_reg_create()
* to avoid having to deal with locals and globals separately:
*/
static nir_register *
add_remap(state, nreg, reg);
nreg->num_components = reg->num_components;
+ nreg->bit_size = reg->bit_size;
nreg->num_array_elems = reg->num_array_elems;
nreg->index = reg->index;
nreg->name = ralloc_strdup(nreg, reg->name);
- nreg->is_global = reg->is_global;
- nreg->is_packed = reg->is_packed;
/* reconstructing uses/defs/if_uses handled by nir_instr_insert() */
list_inithead(&nreg->uses);
if (dst->is_ssa) {
nir_ssa_dest_init(ninstr, ndst, dst->ssa.num_components,
dst->ssa.bit_size, dst->ssa.name);
- add_remap(state, &ndst->ssa, &dst->ssa);
+ if (likely(state->remap_table))
+ add_remap(state, &ndst->ssa, &dst->ssa);
} else {
ndst->reg.reg = remap_reg(state, dst->reg.reg);
if (dst->reg.indirect) {
}
}
-static nir_deref *clone_deref(clone_state *state, const nir_deref *deref,
- nir_instr *ninstr, nir_deref *parent);
-
-static nir_deref_var *
-clone_deref_var(clone_state *state, const nir_deref_var *dvar,
- nir_instr *ninstr)
+static nir_alu_instr *
+clone_alu(clone_state *state, const nir_alu_instr *alu)
{
- nir_variable *nvar = remap_var(state, dvar->var);
- nir_deref_var *ndvar = nir_deref_var_create(ninstr, nvar);
+ nir_alu_instr *nalu = nir_alu_instr_create(state->ns, alu->op);
+ nalu->exact = alu->exact;
+ nalu->no_signed_wrap = alu->no_signed_wrap;
+ nalu->no_unsigned_wrap = alu->no_unsigned_wrap;
- if (dvar->deref.child)
- ndvar->deref.child = clone_deref(state, dvar->deref.child,
- ninstr, &ndvar->deref);
+ __clone_dst(state, &nalu->instr, &nalu->dest.dest, &alu->dest.dest);
+ nalu->dest.saturate = alu->dest.saturate;
+ nalu->dest.write_mask = alu->dest.write_mask;
+
+ for (unsigned i = 0; i < nir_op_infos[alu->op].num_inputs; i++) {
+ __clone_src(state, &nalu->instr, &nalu->src[i].src, &alu->src[i].src);
+ nalu->src[i].negate = alu->src[i].negate;
+ nalu->src[i].abs = alu->src[i].abs;
+ memcpy(nalu->src[i].swizzle, alu->src[i].swizzle,
+ sizeof(nalu->src[i].swizzle));
+ }
- return ndvar;
+ return nalu;
}
-static nir_deref_array *
-clone_deref_array(clone_state *state, const nir_deref_array *darr,
- nir_instr *ninstr, nir_deref *parent)
+nir_alu_instr *
+nir_alu_instr_clone(nir_shader *shader, const nir_alu_instr *orig)
{
- nir_deref_array *ndarr = nir_deref_array_create(parent);
+ clone_state state = {
+ .allow_remap_fallback = true,
+ .ns = shader,
+ };
+ return clone_alu(&state, orig);
+}
- ndarr->deref.type = darr->deref.type;
- if (darr->deref.child)
- ndarr->deref.child = clone_deref(state, darr->deref.child,
- ninstr, &ndarr->deref);
+static nir_deref_instr *
+clone_deref_instr(clone_state *state, const nir_deref_instr *deref)
+{
+ nir_deref_instr *nderef =
+ nir_deref_instr_create(state->ns, deref->deref_type);
- ndarr->deref_array_type = darr->deref_array_type;
- ndarr->base_offset = darr->base_offset;
- if (ndarr->deref_array_type == nir_deref_array_type_indirect)
- __clone_src(state, ninstr, &ndarr->indirect, &darr->indirect);
+ __clone_dst(state, &nderef->instr, &nderef->dest, &deref->dest);
- return ndarr;
-}
+ nderef->mode = deref->mode;
+ nderef->type = deref->type;
-static nir_deref_struct *
-clone_deref_struct(clone_state *state, const nir_deref_struct *dstr,
- nir_instr *ninstr, nir_deref *parent)
-{
- nir_deref_struct *ndstr = nir_deref_struct_create(parent, dstr->index);
+ if (deref->deref_type == nir_deref_type_var) {
+ nderef->var = remap_var(state, deref->var);
+ return nderef;
+ }
- ndstr->deref.type = dstr->deref.type;
- if (dstr->deref.child)
- ndstr->deref.child = clone_deref(state, dstr->deref.child,
- ninstr, &ndstr->deref);
+ __clone_src(state, &nderef->instr, &nderef->parent, &deref->parent);
- return ndstr;
-}
+ switch (deref->deref_type) {
+ case nir_deref_type_struct:
+ nderef->strct.index = deref->strct.index;
+ break;
-static nir_deref *
-clone_deref(clone_state *state, const nir_deref *dref,
- nir_instr *ninstr, nir_deref *parent)
-{
- switch (dref->deref_type) {
case nir_deref_type_array:
- return &clone_deref_array(state, nir_deref_as_array(dref),
- ninstr, parent)->deref;
- case nir_deref_type_struct:
- return &clone_deref_struct(state, nir_deref_as_struct(dref),
- ninstr, parent)->deref;
- default:
- unreachable("bad deref type");
- return NULL;
- }
-}
+ case nir_deref_type_ptr_as_array:
+ __clone_src(state, &nderef->instr,
+ &nderef->arr.index, &deref->arr.index);
+ break;
-static nir_alu_instr *
-clone_alu(clone_state *state, const nir_alu_instr *alu)
-{
- nir_alu_instr *nalu = nir_alu_instr_create(state->ns, alu->op);
+ case nir_deref_type_array_wildcard:
+ /* Nothing to do */
+ break;
- __clone_dst(state, &nalu->instr, &nalu->dest.dest, &alu->dest.dest);
- nalu->dest.saturate = alu->dest.saturate;
- nalu->dest.write_mask = alu->dest.write_mask;
+ case nir_deref_type_cast:
+ nderef->cast.ptr_stride = deref->cast.ptr_stride;
+ break;
- for (unsigned i = 0; i < nir_op_infos[alu->op].num_inputs; i++) {
- __clone_src(state, &nalu->instr, &nalu->src[i].src, &alu->src[i].src);
- nalu->src[i].negate = alu->src[i].negate;
- nalu->src[i].abs = alu->src[i].abs;
- memcpy(nalu->src[i].swizzle, alu->src[i].swizzle,
- sizeof(nalu->src[i].swizzle));
+ default:
+ unreachable("Invalid instruction deref type");
}
- return nalu;
+ return nderef;
}
static nir_intrinsic_instr *
nir_intrinsic_instr *nitr =
nir_intrinsic_instr_create(state->ns, itr->intrinsic);
- unsigned num_variables = nir_intrinsic_infos[itr->intrinsic].num_variables;
unsigned num_srcs = nir_intrinsic_infos[itr->intrinsic].num_srcs;
if (nir_intrinsic_infos[itr->intrinsic].has_dest)
nitr->num_components = itr->num_components;
memcpy(nitr->const_index, itr->const_index, sizeof(nitr->const_index));
- for (unsigned i = 0; i < num_variables; i++) {
- nitr->variables[i] = clone_deref_var(state, itr->variables[i],
- &nitr->instr);
- }
-
for (unsigned i = 0; i < num_srcs; i++)
__clone_src(state, &nitr->instr, &nitr->src[i], &itr->src[i]);
clone_load_const(clone_state *state, const nir_load_const_instr *lc)
{
nir_load_const_instr *nlc =
- nir_load_const_instr_create(state->ns, lc->def.num_components);
+ nir_load_const_instr_create(state->ns, lc->def.num_components,
+ lc->def.bit_size);
- memcpy(&nlc->value, &lc->value, sizeof(nlc->value));
+ memcpy(&nlc->value, &lc->value, sizeof(*nlc->value) * lc->def.num_components);
add_remap(state, &nlc->def, &lc->def);
clone_ssa_undef(clone_state *state, const nir_ssa_undef_instr *sa)
{
nir_ssa_undef_instr *nsa =
- nir_ssa_undef_instr_create(state->ns, sa->def.num_components);
+ nir_ssa_undef_instr_create(state->ns, sa->def.num_components,
+ sa->def.bit_size);
add_remap(state, &nsa->def, &sa->def);
ntex->is_shadow = tex->is_shadow;
ntex->is_new_style_shadow = tex->is_new_style_shadow;
ntex->component = tex->component;
+ memcpy(ntex->tg4_offsets, tex->tg4_offsets, sizeof(tex->tg4_offsets));
ntex->texture_index = tex->texture_index;
- if (tex->texture)
- ntex->texture = clone_deref_var(state, tex->texture, &ntex->instr);
- ntex->texture_array_size = tex->texture_array_size;
-
ntex->sampler_index = tex->sampler_index;
- if (tex->sampler)
- ntex->sampler = clone_deref_var(state, tex->sampler, &ntex->instr);
+
+ ntex->texture_non_uniform = tex->texture_non_uniform;
+ ntex->sampler_non_uniform = tex->sampler_non_uniform;
return ntex;
}
nir_call_instr *ncall = nir_call_instr_create(state->ns, ncallee);
for (unsigned i = 0; i < ncall->num_params; i++)
- ncall->params[i] = clone_deref_var(state, call->params[i], &ncall->instr);
-
- ncall->return_deref = clone_deref_var(state, call->return_deref,
- &ncall->instr);
+ __clone_src(state, ncall, &ncall->params[i], &call->params[i]);
return ncall;
}
switch (instr->type) {
case nir_instr_type_alu:
return &clone_alu(state, nir_instr_as_alu(instr))->instr;
+ case nir_instr_type_deref:
+ return &clone_deref_instr(state, nir_instr_as_deref(instr))->instr;
case nir_instr_type_intrinsic:
return &clone_intrinsic(state, nir_instr_as_intrinsic(instr))->instr;
case nir_instr_type_load_const:
/* We need this for phi sources */
add_remap(state, nblk, blk);
- nir_foreach_instr(blk, instr) {
+ nir_foreach_instr(instr, blk) {
if (instr->type == nir_instr_type_phi) {
/* Phi instructions are a bit of a special case when cloning because
* we don't want inserting the instruction to automatically handle
clone_if(clone_state *state, struct exec_list *cf_list, const nir_if *i)
{
nir_if *ni = nir_if_create(state->ns);
+ ni->control = i->control;
__clone_src(state, ni, &ni->condition, &i->condition);
clone_loop(clone_state *state, struct exec_list *cf_list, const nir_loop *loop)
{
nir_loop *nloop = nir_loop_create(state->ns);
+ nloop->control = loop->control;
+ nloop->partially_unrolled = loop->partially_unrolled;
nir_cf_node_insert_end(cf_list, &nloop->cf_node);
}
}
+/* After we've cloned almost everything, we have to walk the list of phi
+ * sources and fix them up. Thanks to loops, the block and SSA value for a
+ * phi source may not be defined when we first encounter it. Instead, we
+ * add it to the phi_srcs list and we fix it up here.
+ */
+static void
+fixup_phi_srcs(clone_state *state)
+{
+ list_for_each_entry_safe(nir_phi_src, src, &state->phi_srcs, src.use_link) {
+ src->pred = remap_local(state, src->pred);
+
+ /* Remove from this list */
+ list_del(&src->src.use_link);
+
+ if (src->src.is_ssa) {
+ src->src.ssa = remap_local(state, src->src.ssa);
+ list_addtail(&src->src.use_link, &src->src.ssa->uses);
+ } else {
+ src->src.reg.reg = remap_reg(state, src->src.reg.reg);
+ list_addtail(&src->src.use_link, &src->src.reg.reg->uses);
+ }
+ }
+ assert(list_is_empty(&state->phi_srcs));
+}
+
+void
+nir_cf_list_clone(nir_cf_list *dst, nir_cf_list *src, nir_cf_node *parent,
+ struct hash_table *remap_table)
+{
+ exec_list_make_empty(&dst->list);
+ dst->impl = src->impl;
+
+ if (exec_list_is_empty(&src->list))
+ return;
+
+ clone_state state;
+ init_clone_state(&state, remap_table, false, true);
+
+ /* We use the same shader */
+ state.ns = src->impl->function->shader;
+
+ /* The control-flow code assumes that the list of cf_nodes always starts
+ * and ends with a block. We start by adding an empty block.
+ */
+ nir_block *nblk = nir_block_create(state.ns);
+ nblk->cf_node.parent = parent;
+ exec_list_push_tail(&dst->list, &nblk->cf_node.node);
+
+ clone_cf_list(&state, &dst->list, &src->list);
+
+ fixup_phi_srcs(&state);
+}
+
static nir_function_impl *
clone_function_impl(clone_state *state, const nir_function_impl *fi)
{
clone_reg_list(state, &nfi->registers, &fi->registers);
nfi->reg_alloc = fi->reg_alloc;
- nfi->num_params = fi->num_params;
- nfi->params = ralloc_array(state->ns, nir_variable *, fi->num_params);
- for (unsigned i = 0; i < fi->num_params; i++) {
- nfi->params[i] = clone_variable(state, fi->params[i]);
- }
- if (fi->return_var)
- nfi->return_var = clone_variable(state, fi->return_var);
-
- assert(list_empty(&state->phi_srcs));
+ assert(list_is_empty(&state->phi_srcs));
clone_cf_list(state, &nfi->body, &fi->body);
- /* After we've cloned almost everything, we have to walk the list of phi
- * sources and fix them up. Thanks to loops, the block and SSA value for a
- * phi source may not be defined when we first encounter it. Instead, we
- * add it to the phi_srcs list and we fix it up here.
- */
- list_for_each_entry_safe(nir_phi_src, src, &state->phi_srcs, src.use_link) {
- src->pred = remap_local(state, src->pred);
- assert(src->src.is_ssa);
- src->src.ssa = remap_local(state, src->src.ssa);
-
- /* Remove from this list and place in the uses of the SSA def */
- list_del(&src->src.use_link);
- list_addtail(&src->src.use_link, &src->src.ssa->uses);
- }
- assert(list_empty(&state->phi_srcs));
+ fixup_phi_srcs(state);
/* All metadata is invalidated in the cloning process */
nfi->valid_metadata = 0;
}
nir_function_impl *
-nir_function_impl_clone(const nir_function_impl *fi)
+nir_function_impl_clone(nir_shader *shader, const nir_function_impl *fi)
{
clone_state state;
- init_clone_state(&state, false);
+ init_clone_state(&state, NULL, false, false);
- /* We use the same shader */
- state.ns = fi->function->shader;
+ state.ns = shader;
nir_function_impl *nfi = clone_function_impl(&state, fi);
add_remap(state, nfxn, fxn);
nfxn->num_params = fxn->num_params;
- nfxn->params = ralloc_array(state->ns, nir_parameter, fxn->num_params);
- memcpy(nfxn->params, fxn->params, sizeof(nir_parameter) * fxn->num_params);
-
- nfxn->return_type = fxn->return_type;
+ if (fxn->num_params) {
+ nfxn->params = ralloc_array(state->ns, nir_parameter, fxn->num_params);
+ memcpy(nfxn->params, fxn->params, sizeof(nir_parameter) * fxn->num_params);
+ }
+ nfxn->is_entrypoint = fxn->is_entrypoint;
/* At first glance, it looks like we should clone the function_impl here.
* However, call instructions need to be able to reference at least the
- * function and those will get processed as we clone the function_impl's.
+ * function and those will get processed as we clone the function_impls.
* We stop here and do function_impls as a second pass.
*/
nir_shader_clone(void *mem_ctx, const nir_shader *s)
{
clone_state state;
- init_clone_state(&state, true);
+ init_clone_state(&state, NULL, true, false);
- nir_shader *ns = nir_shader_create(mem_ctx, s->stage, s->options);
+ nir_shader *ns = nir_shader_create(mem_ctx, s->info.stage, s->options, NULL);
state.ns = ns;
- clone_var_list(&state, &ns->uniforms, &s->uniforms);
- clone_var_list(&state, &ns->inputs, &s->inputs);
- clone_var_list(&state, &ns->outputs, &s->outputs);
- clone_var_list(&state, &ns->shared, &s->shared);
- clone_var_list(&state, &ns->globals, &s->globals);
- clone_var_list(&state, &ns->system_values, &s->system_values);
+ clone_var_list(&state, &ns->variables, &s->variables);
/* Go through and clone functions */
foreach_list_typed(nir_function, fxn, node, &s->functions)
clone_function(&state, fxn, ns);
/* Only after all functions are cloned can we clone the actual function
- * implementations. This is because nir_call_instr's need to reference the
+ * implementations. This is because nir_call_instrs need to reference the
* functions of other functions and we don't know what order the functions
* will have in the list.
*/
- nir_foreach_function(s, fxn) {
+ nir_foreach_function(fxn, s) {
nir_function *nfxn = remap_global(&state, fxn);
nfxn->impl = clone_function_impl(&state, fxn->impl);
nfxn->impl->function = nfxn;
}
- clone_reg_list(&state, &ns->registers, &s->registers);
- ns->reg_alloc = s->reg_alloc;
-
ns->info = s->info;
ns->info.name = ralloc_strdup(ns, ns->info.name);
if (ns->info.label)
ns->num_uniforms = s->num_uniforms;
ns->num_outputs = s->num_outputs;
ns->num_shared = s->num_shared;
+ ns->scratch_size = s->scratch_size;
+
+ ns->constant_data_size = s->constant_data_size;
+ if (s->constant_data_size > 0) {
+ ns->constant_data = ralloc_size(ns, s->constant_data_size);
+ memcpy(ns->constant_data, s->constant_data, s->constant_data_size);
+ }
free_clone_state(&state);
return ns;
}
+
+/** Overwrites dst and replaces its contents with src
+ *
+ * Everything ralloc parented to dst and src itself (but not its children)
+ * will be freed.
+ *
+ * This should only be used by test code which needs to swap out shaders with
+ * a cloned or deserialized version.
+ */
+void
+nir_shader_replace(nir_shader *dst, nir_shader *src)
+{
+ /* Delete all of dest's ralloc children */
+ void *dead_ctx = ralloc_context(NULL);
+ ralloc_adopt(dead_ctx, dst);
+ ralloc_free(dead_ctx);
+
+ /* Re-parent all of src's ralloc children to dst */
+ ralloc_adopt(dst, src);
+
+ memcpy(dst, src, sizeof(*dst));
+
+ /* We have to move all the linked lists over separately because we need the
+ * pointers in the list elements to point to the lists in dst and not src.
+ */
+ exec_list_move_nodes_to(&src->variables, &dst->variables);
+
+ /* Now move the functions over. This takes a tiny bit more work */
+ exec_list_move_nodes_to(&src->functions, &dst->functions);
+ nir_foreach_function(function, dst)
+ function->shader = dst;
+
+ ralloc_free(src);
+}