spirv: extract switch parsing into its own function
v2 (Jason Ekstrand): - Construct a list of vtn_case objects Signed-off-by: Karol Herbst <kherbst@redhat.com> Reviewed-by: Jason Ekstrand <jason@jlekstrand.net> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/2401>
This commit is contained in:
@@ -476,6 +476,74 @@ vtn_add_cfg_work_item(struct vtn_builder *b,
|
|||||||
list_addtail(&work->link, work_list);
|
list_addtail(&work->link, work_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* returns the default block */
|
||||||
|
static void
|
||||||
|
vtn_parse_switch(struct vtn_builder *b,
|
||||||
|
struct vtn_switch *swtch,
|
||||||
|
const uint32_t *branch,
|
||||||
|
struct list_head *case_list)
|
||||||
|
{
|
||||||
|
const uint32_t *branch_end = branch + (branch[0] >> SpvWordCountShift);
|
||||||
|
|
||||||
|
struct vtn_value *sel_val = vtn_untyped_value(b, branch[1]);
|
||||||
|
vtn_fail_if(!sel_val->type ||
|
||||||
|
sel_val->type->base_type != vtn_base_type_scalar,
|
||||||
|
"Selector of OpSwitch must have a type of OpTypeInt");
|
||||||
|
|
||||||
|
nir_alu_type sel_type =
|
||||||
|
nir_get_nir_type_for_glsl_type(sel_val->type->type);
|
||||||
|
vtn_fail_if(nir_alu_type_get_base_type(sel_type) != nir_type_int &&
|
||||||
|
nir_alu_type_get_base_type(sel_type) != nir_type_uint,
|
||||||
|
"Selector of OpSwitch must have a type of OpTypeInt");
|
||||||
|
|
||||||
|
struct hash_table *block_to_case = _mesa_pointer_hash_table_create(b);
|
||||||
|
|
||||||
|
bool is_default = true;
|
||||||
|
const unsigned bitsize = nir_alu_type_get_type_size(sel_type);
|
||||||
|
for (const uint32_t *w = branch + 2; w < branch_end;) {
|
||||||
|
uint64_t literal = 0;
|
||||||
|
if (!is_default) {
|
||||||
|
if (bitsize <= 32) {
|
||||||
|
literal = *(w++);
|
||||||
|
} else {
|
||||||
|
assert(bitsize == 64);
|
||||||
|
literal = vtn_u64_literal(w);
|
||||||
|
w += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct vtn_block *case_block = vtn_block(b, *(w++));
|
||||||
|
|
||||||
|
struct hash_entry *case_entry =
|
||||||
|
_mesa_hash_table_search(block_to_case, case_block);
|
||||||
|
|
||||||
|
struct vtn_case *cse;
|
||||||
|
if (case_entry) {
|
||||||
|
cse = case_entry->data;
|
||||||
|
} else {
|
||||||
|
cse = rzalloc(b, struct vtn_case);
|
||||||
|
|
||||||
|
cse->node.type = vtn_cf_node_type_case;
|
||||||
|
cse->node.parent = swtch ? &swtch->node : NULL;
|
||||||
|
cse->block = case_block;
|
||||||
|
list_inithead(&cse->body);
|
||||||
|
util_dynarray_init(&cse->values, b);
|
||||||
|
|
||||||
|
list_addtail(&cse->node.link, case_list);
|
||||||
|
_mesa_hash_table_insert(block_to_case, case_block, cse);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_default) {
|
||||||
|
cse->is_default = true;
|
||||||
|
} else {
|
||||||
|
util_dynarray_append(&cse->values, uint64_t, literal);
|
||||||
|
}
|
||||||
|
|
||||||
|
is_default = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_mesa_hash_table_destroy(block_to_case, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/* Processes a block and returns the next block to process or NULL if we've
|
/* Processes a block and returns the next block to process or NULL if we've
|
||||||
* reached the end of the construct.
|
* reached the end of the construct.
|
||||||
*/
|
*/
|
||||||
@@ -679,17 +747,6 @@ vtn_process_block(struct vtn_builder *b,
|
|||||||
}
|
}
|
||||||
|
|
||||||
case SpvOpSwitch: {
|
case SpvOpSwitch: {
|
||||||
struct vtn_value *sel_val = vtn_untyped_value(b, block->branch[1]);
|
|
||||||
vtn_fail_if(!sel_val->type ||
|
|
||||||
sel_val->type->base_type != vtn_base_type_scalar,
|
|
||||||
"Selector of OpSwitch must have a type of OpTypeInt");
|
|
||||||
|
|
||||||
nir_alu_type sel_type =
|
|
||||||
nir_get_nir_type_for_glsl_type(sel_val->type->type);
|
|
||||||
vtn_fail_if(nir_alu_type_get_base_type(sel_type) != nir_type_int &&
|
|
||||||
nir_alu_type_get_base_type(sel_type) != nir_type_uint,
|
|
||||||
"Selector of OpSwitch must have a type of OpTypeInt");
|
|
||||||
|
|
||||||
struct vtn_switch *swtch = rzalloc(b, struct vtn_switch);
|
struct vtn_switch *swtch = rzalloc(b, struct vtn_switch);
|
||||||
|
|
||||||
swtch->node.type = vtn_cf_node_type_switch;
|
swtch->node.type = vtn_cf_node_type_switch;
|
||||||
@@ -710,82 +767,39 @@ vtn_process_block(struct vtn_builder *b,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* First, we go through and record all of the cases. */
|
/* First, we go through and record all of the cases. */
|
||||||
const uint32_t *branch_end =
|
vtn_parse_switch(b, swtch, block->branch, &swtch->cases);
|
||||||
block->branch + (block->branch[0] >> SpvWordCountShift);
|
|
||||||
|
|
||||||
struct hash_table *block_to_case = _mesa_pointer_hash_table_create(b);
|
/* Gather the branch types for the switch */
|
||||||
|
vtn_foreach_cf_node(case_node, &swtch->cases) {
|
||||||
|
struct vtn_case *cse = vtn_cf_node_as_case(case_node);
|
||||||
|
|
||||||
bool is_default = true;
|
cse->type = vtn_handle_branch(b, &swtch->node, cse->block);
|
||||||
const unsigned bitsize = nir_alu_type_get_type_size(sel_type);
|
switch (cse->type) {
|
||||||
for (const uint32_t *w = block->branch + 2; w < branch_end;) {
|
case vtn_branch_type_none:
|
||||||
uint64_t literal = 0;
|
/* This is a "real" cases which has stuff in it */
|
||||||
if (!is_default) {
|
vtn_fail_if(cse->block->switch_case != NULL,
|
||||||
if (bitsize <= 32) {
|
"OpSwitch has a case which is also in another "
|
||||||
literal = *(w++);
|
"OpSwitch construct");
|
||||||
} else {
|
cse->block->switch_case = cse;
|
||||||
assert(bitsize == 64);
|
vtn_add_cfg_work_item(b, work_list, &cse->node,
|
||||||
literal = vtn_u64_literal(w);
|
&cse->body, cse->block);
|
||||||
w += 2;
|
break;
|
||||||
}
|
|
||||||
|
case vtn_branch_type_switch_break:
|
||||||
|
case vtn_branch_type_loop_break:
|
||||||
|
case vtn_branch_type_loop_continue:
|
||||||
|
/* Switch breaks as well as loop breaks and continues can be
|
||||||
|
* used to break out of a switch construct or as direct targets
|
||||||
|
* of the OpSwitch.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
vtn_fail("Target of OpSwitch is not a valid structured exit "
|
||||||
|
"from the switch construct.");
|
||||||
}
|
}
|
||||||
struct vtn_block *case_block = vtn_block(b, *(w++));
|
|
||||||
|
|
||||||
struct hash_entry *case_entry =
|
|
||||||
_mesa_hash_table_search(block_to_case, case_block);
|
|
||||||
|
|
||||||
struct vtn_case *cse;
|
|
||||||
if (case_entry) {
|
|
||||||
cse = case_entry->data;
|
|
||||||
} else {
|
|
||||||
cse = rzalloc(b, struct vtn_case);
|
|
||||||
|
|
||||||
cse->node.type = vtn_cf_node_type_case;
|
|
||||||
cse->node.parent = &swtch->node;
|
|
||||||
list_inithead(&cse->body);
|
|
||||||
util_dynarray_init(&cse->values, b);
|
|
||||||
|
|
||||||
cse->type = vtn_handle_branch(b, &swtch->node, case_block);
|
|
||||||
switch (cse->type) {
|
|
||||||
case vtn_branch_type_none:
|
|
||||||
/* This is a "real" cases which has stuff in it */
|
|
||||||
vtn_fail_if(case_block->switch_case != NULL,
|
|
||||||
"OpSwitch has a case which is also in another "
|
|
||||||
"OpSwitch construct");
|
|
||||||
case_block->switch_case = cse;
|
|
||||||
vtn_add_cfg_work_item(b, work_list, &cse->node,
|
|
||||||
&cse->body, case_block);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case vtn_branch_type_switch_break:
|
|
||||||
case vtn_branch_type_loop_break:
|
|
||||||
case vtn_branch_type_loop_continue:
|
|
||||||
/* Switch breaks as well as loop breaks and continues can be
|
|
||||||
* used to break out of a switch construct or as direct targets
|
|
||||||
* of the OpSwitch.
|
|
||||||
*/
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
vtn_fail("Target of OpSwitch is not a valid structured exit "
|
|
||||||
"from the switch construct.");
|
|
||||||
}
|
|
||||||
|
|
||||||
list_addtail(&cse->node.link, &swtch->cases);
|
|
||||||
|
|
||||||
_mesa_hash_table_insert(block_to_case, case_block, cse);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_default) {
|
|
||||||
cse->is_default = true;
|
|
||||||
} else {
|
|
||||||
util_dynarray_append(&cse->values, uint64_t, literal);
|
|
||||||
}
|
|
||||||
|
|
||||||
is_default = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_mesa_hash_table_destroy(block_to_case, NULL);
|
|
||||||
|
|
||||||
return swtch->break_block;
|
return swtch->break_block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -184,6 +184,8 @@ struct vtn_if {
|
|||||||
struct vtn_case {
|
struct vtn_case {
|
||||||
struct vtn_cf_node node;
|
struct vtn_cf_node node;
|
||||||
|
|
||||||
|
struct vtn_block *block;
|
||||||
|
|
||||||
enum vtn_branch_type type;
|
enum vtn_branch_type type;
|
||||||
struct list_head body;
|
struct list_head body;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user