brw: Add basic infrastructure for load_reg pseudo op

load_reg is something like load_payload except it has a single
source. It copies the entire source to the destination. Its purpose is
to convert a non-SSA VGRF into an SSA value. This copy is marked as
volatile so that it will act as a scheduling barrier.

v2: Fix some typos in the commit message. Eliminate the
brw_builder::LOAD_REG overload that returns a brw_inst*. This is
unlikely to ever be used. Add some checks to brw_validate. All
suggested by Caio.

v3: Force the source and destination types of the LOAD_REG to by
integer. This will (eventually) simplify the creating of unit tests for
the pass that adds LOAD_REG instructions.

Reviewed-by: Caio Oliveira <caio.oliveira@intel.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/31497>
This commit is contained in:
Ian Romanick 2024-09-18 18:11:13 -07:00 committed by Marge Bot
parent b9656d51c0
commit cfc50390fb
6 changed files with 72 additions and 6 deletions

View file

@ -85,7 +85,8 @@ brw_def_analysis::update_for_reads(const brw_idom_tree &idom,
/* Additionally, if one of our sources is not a def, then our
* destination may have multiple dynamic assignments.
*/
if (!def_insts[nr] && inst->dst.file == VGRF)
if (inst->opcode != SHADER_OPCODE_LOAD_REG &&
!def_insts[nr] && inst->dst.file == VGRF)
mark_invalid(inst->dst.nr);
}
}
@ -158,7 +159,8 @@ brw_def_analysis::brw_def_analysis(const brw_shader *v)
const int nr = def->src[i].nr;
/* If our "def" reads a non-SSA source, then it isn't a def. */
if (!def_insts[nr] || def_insts[nr] == UNSEEN) {
if (def->opcode != SHADER_OPCODE_LOAD_REG &&
(!def_insts[nr] || def_insts[nr] == UNSEEN)) {
mark_invalid(def->dst.nr);
iterate = true;
break;

View file

@ -859,6 +859,35 @@ public:
return component(dst, 0);
}
brw_reg
LOAD_REG(const brw_reg &src0, brw_inst **out = NULL) const
{
/* LOAD_REG is a raw, bulk copy of one VGRF to another. The type is
* irrelevant. The pass that inserts LOAD_REG to encourage results to be
* defs will force all types to be integer types. Forcing the type to
* always be integer here helps with uniformity, and it will also help
* implement unit tests that want to compare two shaders for equality.
*/
brw_reg_type t = brw_type_with_size(BRW_TYPE_UD,
brw_type_size_bits(src0.type));
brw_reg dst = retype(brw_allocate_vgrf_units(*shader,
shader->alloc.sizes[src0.nr]),
t);
assert(src0.file == VGRF);
assert(shader->alloc.sizes[dst.nr] == shader->alloc.sizes[src0.nr]);
brw_inst *inst = emit(SHADER_OPCODE_LOAD_REG, dst, retype(src0, t));
inst->size_written = REG_SIZE * shader->alloc.sizes[src0.nr];
assert(shader->alloc.sizes[inst->dst.nr] * REG_SIZE == inst->size_written);
assert(!inst->is_partial_write());
if (out) *out = inst;
return retype(inst->dst, src0.type);
}
brw_shader *shader;
brw_inst *BREAK() const { return emit(BRW_OPCODE_BREAK); }

View file

@ -557,6 +557,13 @@ enum opcode {
/* Ends a block moving to the next one. See brw_cfg for details. */
SHADER_OPCODE_FLOW,
/**
* Load a VGRF to generate an SSA value.
*
* Acts as a scheduling barrier.
*/
SHADER_OPCODE_LOAD_REG,
};
enum fb_write_logical_srcs {

View file

@ -303,6 +303,7 @@ brw_inst::can_do_source_mods(const struct intel_device_info *devinfo) const
case SHADER_OPCODE_REDUCE:
case SHADER_OPCODE_INCLUSIVE_SCAN:
case SHADER_OPCODE_EXCLUSIVE_SCAN:
case SHADER_OPCODE_LOAD_REG:
case SHADER_OPCODE_VOTE_ANY:
case SHADER_OPCODE_VOTE_ALL:
case SHADER_OPCODE_VOTE_EQUAL:
@ -587,6 +588,11 @@ brw_inst::size_read(const struct intel_device_info *devinfo, int arg) const
break;
}
case SHADER_OPCODE_LOAD_REG:
return is_uniform(src[arg]) ?
components_read(arg) * brw_type_size_bytes(src[arg].type) :
size_written;
default:
break;
}
@ -952,6 +958,7 @@ bool
brw_inst::is_volatile() const
{
return opcode == SHADER_OPCODE_MEMORY_LOAD_LOGICAL ||
opcode == SHADER_OPCODE_LOAD_REG ||
((opcode == SHADER_OPCODE_SEND ||
opcode == SHADER_OPCODE_SEND_GATHER) && send_is_volatile);
}

View file

@ -286,6 +286,8 @@ brw_instruction_name(const struct brw_isa_info *isa, enum opcode op)
return "inclusive_scan";
case SHADER_OPCODE_EXCLUSIVE_SCAN:
return "exclusive_scan";
case SHADER_OPCODE_LOAD_REG:
return "load_reg";
case SHADER_OPCODE_VOTE_ANY:
return "vote_any";
case SHADER_OPCODE_VOTE_ALL:

View file

@ -44,15 +44,15 @@
#define fsv_assert_eq(A, B) \
{ \
unsigned a = (A); \
unsigned b = (B); \
uintptr_t a = uintptr_t(A); \
uintptr_t b = uintptr_t(B); \
if (a != b) { \
fprintf(stderr, "ASSERT: Scalar %s validation failed!\n", \
_mesa_shader_stage_to_abbrev(s.stage)); \
brw_print_instruction(s, inst, stderr); \
fprintf(stderr, "%s:%d: A == B failed\n", __FILE__, __LINE__); \
fprintf(stderr, " A = %s = %u\n", #A, a); \
fprintf(stderr, " B = %s = %u\n", #B, b); \
fprintf(stderr, " A = %s = %" PRIuPTR "\n", #A, a); \
fprintf(stderr, " B = %s = %" PRIuPTR "\n", #B, b); \
abort(); \
} \
}
@ -250,6 +250,7 @@ brw_validate_instruction_phase(const brw_shader &s, brw_inst *inst)
case SHADER_OPCODE_QUAD_SWAP:
case SHADER_OPCODE_READ_FROM_LIVE_CHANNEL:
case SHADER_OPCODE_READ_FROM_CHANNEL:
case SHADER_OPCODE_LOAD_REG:
invalid_from = BRW_SHADER_PHASE_AFTER_EARLY_LOWERING;
break;
@ -324,6 +325,24 @@ brw_validate(const brw_shader &s)
fsv_assert(is_ud_imm(inst->src[1])); /* commit enable */
break;
case SHADER_OPCODE_LOAD_REG: {
fsv_assert_eq(inst->sources, 1);
fsv_assert_eq(s.alloc.sizes[inst->dst.nr] * REG_SIZE, inst->size_written);
fsv_assert(!inst->is_partial_write());
fsv_assert_lte(inst->src[0].stride, 1);
/* For example, if file == UNIFORM, stride will be zero and offset
* may be non-zero.
*/
if (inst->src[0].stride != 0)
fsv_assert_eq(inst->src[0].offset, 0);
const brw_def_analysis &defs = s.def_analysis.require();
fsv_assert_eq(inst, defs.get(inst->dst));
break;
}
default:
break;
}