ir3: remove OPC_B and brtype from cat0
We currently have a bit of a confusing situation where we have both opcodes for the different branches (OPC_BR, OPC_BRAA,...) and branch types which are supposed to be used with OPC_B (BRANCH_PLAIN, BRANCH_AND,...). However, not every kind of branch has a corresponding type. For example, getone is represented by OPC_GETONE instead of a branch type. This patch proposes to get rid of the branch types and use opcodes everywhere. I think this makes the representation of branches more consistent. It also removes the for the encoder to translate branch types into opcodes. Signed-off-by: Job Noorman <jnoorman@igalia.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/27411>
This commit is contained in:
@@ -153,8 +153,8 @@ static const struct opc_info {
|
||||
/* clang-format off */
|
||||
/* category 0: */
|
||||
OPC(0, OPC_NOP, nop),
|
||||
OPC(0, OPC_B, b),
|
||||
OPC(0, OPC_BR, br),
|
||||
OPC(0, OPC_BRAC, brac),
|
||||
OPC(0, OPC_BRAA, braa),
|
||||
OPC(0, OPC_BRAO, brao),
|
||||
OPC(0, OPC_BALL, ball),
|
||||
|
||||
@@ -59,7 +59,6 @@ void ir3_assert_handler(const char *expr, const char *file, int line,
|
||||
typedef enum {
|
||||
/* category 0: */
|
||||
OPC_NOP = _OPC(0, 0),
|
||||
OPC_B = _OPC(0, 1),
|
||||
OPC_JUMP = _OPC(0, 2),
|
||||
OPC_CALL = _OPC(0, 3),
|
||||
OPC_RET = _OPC(0, 4),
|
||||
@@ -525,16 +524,6 @@ regid(int num, int comp)
|
||||
#define REG_A0 61 /* address register */
|
||||
#define REG_P0 62 /* predicate register */
|
||||
|
||||
typedef enum {
|
||||
BRANCH_PLAIN = 0, /* br */
|
||||
BRANCH_OR = 1, /* brao */
|
||||
BRANCH_AND = 2, /* braa */
|
||||
BRANCH_CONST = 3, /* brac */
|
||||
BRANCH_ANY = 4, /* bany */
|
||||
BRANCH_ALL = 5, /* ball */
|
||||
BRANCH_X = 6, /* brax ??? */
|
||||
} brtype_t;
|
||||
|
||||
/* With is_bindless_s2en = 1, this determines whether bindless is enabled and
|
||||
* if so, how to get the (base, index) pair for both sampler and texture.
|
||||
* There is a single base embedded in the instruction, which is always used
|
||||
|
||||
@@ -364,7 +364,6 @@ struct ir3_instruction {
|
||||
int immed;
|
||||
struct ir3_block *target;
|
||||
const char *target_label;
|
||||
brtype_t brtype;
|
||||
unsigned idx; /* for brac.N */
|
||||
} cat0;
|
||||
struct {
|
||||
@@ -870,7 +869,6 @@ static inline bool
|
||||
is_terminator(struct ir3_instruction *instr)
|
||||
{
|
||||
switch (instr->opc) {
|
||||
case OPC_B:
|
||||
case OPC_BR:
|
||||
case OPC_JUMP:
|
||||
case OPC_BANY:
|
||||
@@ -2336,7 +2334,6 @@ static inline struct ir3_instruction *ir3_##name( \
|
||||
#define INSTR6NODST(name) __INSTR6((ir3_instruction_flags)0, 0, name, OPC_##name)
|
||||
|
||||
/* cat0 instructions: */
|
||||
INSTR1NODST(B)
|
||||
INSTR1NODST(BR)
|
||||
INSTR1NODST(BALL)
|
||||
INSTR1NODST(BANY)
|
||||
|
||||
@@ -103,7 +103,7 @@ count_instruction(struct ir3_instruction *n)
|
||||
* earlier so we don't have this constraint.
|
||||
*/
|
||||
return is_alu(n) ||
|
||||
(is_flow(n) && (n->opc != OPC_JUMP) && (n->opc != OPC_B));
|
||||
(is_flow(n) && (n->opc != OPC_JUMP) && (n->opc != OPC_BR));
|
||||
}
|
||||
|
||||
/* Post-RA, we don't have arrays any more, so we have to be a bit careful here
|
||||
|
||||
@@ -575,6 +575,21 @@ retarget_jump(struct ir3_instruction *instr, struct ir3_block *new_target)
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
is_invertible_branch(struct ir3_instruction *instr)
|
||||
{
|
||||
switch (instr->opc) {
|
||||
case OPC_BR:
|
||||
case OPC_BRAA:
|
||||
case OPC_BRAO:
|
||||
case OPC_BANY:
|
||||
case OPC_BALL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
opt_jump(struct ir3 *ir)
|
||||
{
|
||||
@@ -625,13 +640,14 @@ opt_jump(struct ir3 *ir)
|
||||
|
||||
if (jumps[0]->opc == OPC_JUMP)
|
||||
jumps[1] = NULL;
|
||||
else if (jumps[0]->opc != OPC_B || !jumps[1] || jumps[1]->opc != OPC_B)
|
||||
else if (!is_invertible_branch(jumps[0]) || !jumps[1] ||
|
||||
!is_invertible_branch(jumps[1])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < 2; i++) {
|
||||
if (!jumps[i])
|
||||
continue;
|
||||
|
||||
struct ir3_block *tblock = jumps[i]->cat0.target;
|
||||
if (&tblock->node == block->node.next) {
|
||||
list_delinit(&jumps[i]->node);
|
||||
@@ -686,6 +702,27 @@ mark_xvergence_points(struct ir3 *ir)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
invert_branch(struct ir3_instruction *branch)
|
||||
{
|
||||
switch (branch->opc) {
|
||||
case OPC_BR:
|
||||
break;
|
||||
case OPC_BALL:
|
||||
branch->opc = OPC_BANY;
|
||||
break;
|
||||
case OPC_BANY:
|
||||
branch->opc = OPC_BALL;
|
||||
break;
|
||||
default:
|
||||
unreachable("can't get here");
|
||||
}
|
||||
|
||||
branch->cat0.inv1 = !branch->cat0.inv1;
|
||||
branch->cat0.inv2 = !branch->cat0.inv2;
|
||||
branch->cat0.target = branch->block->successors[1];
|
||||
}
|
||||
|
||||
/* Insert the branch/jump instructions for flow control between blocks.
|
||||
* Initially this is done naively, without considering if the successor
|
||||
* block immediately follows the current block (ie. so no jump required),
|
||||
@@ -725,33 +762,9 @@ block_sched(struct ir3 *ir)
|
||||
* frequently/always end up being a fall-thru):
|
||||
*/
|
||||
br1 = terminator;
|
||||
br1->opc = OPC_B;
|
||||
br1->srcs[0]->num = regid(REG_P0, 0);
|
||||
br1->cat0.inv1 = true;
|
||||
br1->cat0.target = block->successors[1];
|
||||
|
||||
/* "then" branch: */
|
||||
br2 = ir3_instr_create(block, OPC_B, 0, 1);
|
||||
ir3_src_create(br2, regid(REG_P0, 0), 0)->def =
|
||||
terminator->srcs[0]->def;
|
||||
br2 = ir3_instr_clone(br1);
|
||||
invert_branch(br1);
|
||||
br2->cat0.target = block->successors[0];
|
||||
|
||||
switch (opc) {
|
||||
case OPC_B:
|
||||
case OPC_BR:
|
||||
br1->cat0.brtype = br2->cat0.brtype = BRANCH_PLAIN;
|
||||
break;
|
||||
case OPC_BALL:
|
||||
br1->cat0.brtype = BRANCH_ANY;
|
||||
br2->cat0.brtype = BRANCH_ALL;
|
||||
break;
|
||||
case OPC_BANY:
|
||||
br1->cat0.brtype = BRANCH_ALL;
|
||||
br2->cat0.brtype = BRANCH_ANY;
|
||||
break;
|
||||
default:
|
||||
unreachable("can't get here");
|
||||
}
|
||||
}
|
||||
|
||||
/* Creating br2 caused it to be moved before the terminator b1, move it
|
||||
@@ -812,7 +825,7 @@ kill_sched(struct ir3 *ir, struct ir3_shader_variant *so)
|
||||
if (instr->opc != OPC_KILL)
|
||||
continue;
|
||||
|
||||
struct ir3_instruction *br = ir3_instr_create(block, OPC_B, 0, 1);
|
||||
struct ir3_instruction *br = ir3_instr_create(block, OPC_BR, 0, 1);
|
||||
ir3_src_create(br, instr->srcs[0]->num, instr->srcs[0]->flags)->wrmask =
|
||||
1;
|
||||
br->cat0.target =
|
||||
|
||||
@@ -441,7 +441,7 @@ lower_instr(struct ir3 *ir, struct ir3_block **block, struct ir3_instruction *in
|
||||
case OPC_BALLOT_MACRO:
|
||||
case OPC_READ_COND_MACRO:
|
||||
after_block->reconvergence_point = true;
|
||||
branch_opc = OPC_B;
|
||||
branch_opc = OPC_BR;
|
||||
break;
|
||||
case OPC_ANY_MACRO:
|
||||
branch_opc = OPC_BANY;
|
||||
|
||||
@@ -855,13 +855,13 @@ cat0_immed: '#' integer { instr->cat0.immed = $2; }
|
||||
| '#' T_IDENTIFIER { ralloc_steal(instr, (void *)$2); instr->cat0.target_label = $2; }
|
||||
|
||||
cat0_instr: T_OP_NOP { new_instr(OPC_NOP); }
|
||||
| T_OP_BR { new_instr(OPC_B)->cat0.brtype = BRANCH_PLAIN; } cat0_src1 ',' cat0_immed
|
||||
| T_OP_BRAO { new_instr(OPC_B)->cat0.brtype = BRANCH_OR; } cat0_src1 ',' cat0_src2 ',' cat0_immed
|
||||
| T_OP_BRAA { new_instr(OPC_B)->cat0.brtype = BRANCH_AND; } cat0_src1 ',' cat0_src2 ',' cat0_immed
|
||||
| T_OP_BRAC '.' integer { new_instr(OPC_B)->cat0.brtype = BRANCH_CONST; instr->cat0.idx = $3; } cat0_immed
|
||||
| T_OP_BANY { new_instr(OPC_B)->cat0.brtype = BRANCH_ANY; } cat0_src1 ',' cat0_immed
|
||||
| T_OP_BALL { new_instr(OPC_B)->cat0.brtype = BRANCH_ALL; } cat0_src1 ',' cat0_immed
|
||||
| T_OP_BRAX { new_instr(OPC_B)->cat0.brtype = BRANCH_X; } cat0_immed
|
||||
| T_OP_BR { new_instr(OPC_BR); } cat0_src1 ',' cat0_immed
|
||||
| T_OP_BRAO { new_instr(OPC_BRAO); } cat0_src1 ',' cat0_src2 ',' cat0_immed
|
||||
| T_OP_BRAA { new_instr(OPC_BRAA); } cat0_src1 ',' cat0_src2 ',' cat0_immed
|
||||
| T_OP_BRAC '.' integer { new_instr(OPC_BRAC)->cat0.idx = $3; } cat0_immed
|
||||
| T_OP_BANY { new_instr(OPC_BANY); } cat0_src1 ',' cat0_immed
|
||||
| T_OP_BALL { new_instr(OPC_BALL); } cat0_src1 ',' cat0_immed
|
||||
| T_OP_BRAX { new_instr(OPC_BRAX); } cat0_immed
|
||||
| T_OP_JUMP { new_instr(OPC_JUMP); } cat0_immed
|
||||
| T_OP_CALL { new_instr(OPC_CALL); } cat0_immed
|
||||
| T_OP_RET { new_instr(OPC_RET); }
|
||||
|
||||
@@ -669,8 +669,6 @@ sched_block(struct ir3_postsched_ctx *ctx, struct ir3_block *block)
|
||||
foreach_instr_safe (instr, &ctx->unscheduled_list) {
|
||||
switch (instr->opc) {
|
||||
case OPC_NOP:
|
||||
case OPC_B:
|
||||
case OPC_JUMP:
|
||||
list_delinit(&instr->node);
|
||||
break;
|
||||
default:
|
||||
|
||||
@@ -188,19 +188,6 @@ print_instr_name(struct log_stream *stream, struct ir3_instruction *instr,
|
||||
type_name(instr->cat1.src_type),
|
||||
type_name(instr->cat1.dst_type));
|
||||
}
|
||||
} else if (instr->opc == OPC_B) {
|
||||
const char *name[8] = {
|
||||
/* clang-format off */
|
||||
[BRANCH_PLAIN] = "br",
|
||||
[BRANCH_OR] = "brao",
|
||||
[BRANCH_AND] = "braa",
|
||||
[BRANCH_CONST] = "brac",
|
||||
[BRANCH_ANY] = "bany",
|
||||
[BRANCH_ALL] = "ball",
|
||||
[BRANCH_X] = "brax",
|
||||
/* clang-format on */
|
||||
};
|
||||
mesa_log_stream_printf(stream, "%s", name[instr->cat0.brtype]);
|
||||
} else {
|
||||
mesa_log_stream_printf(stream, "%s", disasm_a3xx_instr_name(instr->opc));
|
||||
if (instr->flags & IR3_INSTR_3D)
|
||||
@@ -241,6 +228,9 @@ print_instr_name(struct log_stream *stream, struct ir3_instruction *instr,
|
||||
mesa_log_stream_printf(stream, ".%s",
|
||||
cond[instr->cat2.condition & 0x7]);
|
||||
break;
|
||||
case OPC_BRAC:
|
||||
mesa_log_stream_printf(stream, ".%u", instr->cat0.idx);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -366,8 +356,7 @@ print_instr(struct log_stream *stream, struct ir3_instruction *instr, int lvl)
|
||||
if (instr->dsts[0]->wrmask & (1 << i))
|
||||
mesa_log_stream_printf(stream, "%c", "xyzw"[i]);
|
||||
mesa_log_stream_printf(stream, ")");
|
||||
} else if ((instr->srcs_count > 0 || instr->dsts_count > 0) &&
|
||||
(instr->opc != OPC_B)) {
|
||||
} else if ((instr->srcs_count > 0 || instr->dsts_count > 0)) {
|
||||
/* NOTE the b(ranch) instruction has a suffix, which is
|
||||
* handled below
|
||||
*/
|
||||
@@ -416,41 +405,6 @@ print_instr(struct log_stream *stream, struct ir3_instruction *instr, int lvl)
|
||||
}
|
||||
|
||||
if (is_flow(instr) && instr->cat0.target) {
|
||||
/* the predicate register src is implied: */
|
||||
if (instr->opc == OPC_B) {
|
||||
static const struct {
|
||||
int nsrc;
|
||||
bool idx;
|
||||
} brinfo[7] = {
|
||||
/* clang-format off */
|
||||
[BRANCH_PLAIN] = {1, false},
|
||||
[BRANCH_OR] = {2, false},
|
||||
[BRANCH_AND] = {2, false},
|
||||
[BRANCH_CONST] = {0, true},
|
||||
[BRANCH_ANY] = {1, false},
|
||||
[BRANCH_ALL] = {1, false},
|
||||
[BRANCH_X] = {0, false},
|
||||
/* clang-format on */
|
||||
};
|
||||
|
||||
if (brinfo[instr->cat0.brtype].idx) {
|
||||
mesa_log_stream_printf(stream, ".%u", instr->cat0.idx);
|
||||
}
|
||||
if (brinfo[instr->cat0.brtype].nsrc >= 1) {
|
||||
mesa_log_stream_printf(stream, " %sp0.%c (",
|
||||
instr->cat0.inv1 ? "!" : "",
|
||||
"xyzw"[instr->cat0.comp1 & 0x3]);
|
||||
print_reg_name(stream, instr, instr->srcs[0], false);
|
||||
mesa_log_stream_printf(stream, "), ");
|
||||
}
|
||||
if (brinfo[instr->cat0.brtype].nsrc >= 2) {
|
||||
mesa_log_stream_printf(stream, " %sp0.%c (",
|
||||
instr->cat0.inv2 ? "!" : "",
|
||||
"xyzw"[instr->cat0.comp2 & 0x3]);
|
||||
print_reg_name(stream, instr, instr->srcs[1], false);
|
||||
mesa_log_stream_printf(stream, "), ");
|
||||
}
|
||||
}
|
||||
mesa_log_stream_printf(stream, " target=block%u",
|
||||
block_id(instr->cat0.target));
|
||||
}
|
||||
|
||||
@@ -82,24 +82,7 @@ __instruction_case(struct encode_state *s, struct ir3_instruction *instr)
|
||||
* decoding and split up things which are logically different
|
||||
* instructions
|
||||
*/
|
||||
if (instr->opc == OPC_B) {
|
||||
switch (instr->cat0.brtype) {
|
||||
case BRANCH_PLAIN:
|
||||
return OPC_BR;
|
||||
case BRANCH_OR:
|
||||
return OPC_BRAO;
|
||||
case BRANCH_AND:
|
||||
return OPC_BRAA;
|
||||
case BRANCH_CONST:
|
||||
return OPC_BRAC;
|
||||
case BRANCH_ANY:
|
||||
return OPC_BANY;
|
||||
case BRANCH_ALL:
|
||||
return OPC_BALL;
|
||||
case BRANCH_X:
|
||||
return OPC_BRAX;
|
||||
}
|
||||
} else if (instr->opc == OPC_MOV) {
|
||||
if (instr->opc == OPC_MOV) {
|
||||
struct ir3_register *src = instr->srcs[0];
|
||||
if (src->flags & IR3_REG_IMMED) {
|
||||
return OPC_MOV_IMMED;
|
||||
|
||||
Reference in New Issue
Block a user