|
@@ -83,6 +83,13 @@ struct vc4_shader_validation_state {
|
|
* basic blocks.
|
|
* basic blocks.
|
|
*/
|
|
*/
|
|
bool needs_uniform_address_for_loop;
|
|
bool needs_uniform_address_for_loop;
|
|
|
|
+
|
|
|
|
+ /* Set when we find an instruction writing the top half of the
|
|
|
|
+ * register files. If we allowed writing the unusable regs in
|
|
|
|
+ * a threaded shader, then the other shader running on our
|
|
|
|
+ * QPU's clamp validation would be invalid.
|
|
|
|
+ */
|
|
|
|
+ bool all_registers_used;
|
|
};
|
|
};
|
|
|
|
|
|
static uint32_t
|
|
static uint32_t
|
|
@@ -118,6 +125,13 @@ raddr_add_a_to_live_reg_index(uint64_t inst)
|
|
return ~0;
|
|
return ~0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static bool
|
|
|
|
+live_reg_is_upper_half(uint32_t lri)
|
|
|
|
+{
|
|
|
|
+ return (lri >= 16 && lri < 32) ||
|
|
|
|
+ (lri >= 32 + 16 && lri < 32 + 32);
|
|
|
|
+}
|
|
|
|
+
|
|
static bool
|
|
static bool
|
|
is_tmu_submit(uint32_t waddr)
|
|
is_tmu_submit(uint32_t waddr)
|
|
{
|
|
{
|
|
@@ -390,6 +404,9 @@ check_reg_write(struct vc4_validated_shader_info *validated_shader,
|
|
} else {
|
|
} else {
|
|
validation_state->live_immediates[lri] = ~0;
|
|
validation_state->live_immediates[lri] = ~0;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ if (live_reg_is_upper_half(lri))
|
|
|
|
+ validation_state->all_registers_used = true;
|
|
}
|
|
}
|
|
|
|
|
|
switch (waddr) {
|
|
switch (waddr) {
|
|
@@ -598,6 +615,11 @@ check_instruction_reads(struct vc4_validated_shader_info *validated_shader,
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if ((raddr_a >= 16 && raddr_a < 32) ||
|
|
|
|
+ (raddr_b >= 16 && raddr_b < 32 && sig != QPU_SIG_SMALL_IMM)) {
|
|
|
|
+ validation_state->all_registers_used = true;
|
|
|
|
+ }
|
|
|
|
+
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -753,6 +775,7 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj)
|
|
{
|
|
{
|
|
bool found_shader_end = false;
|
|
bool found_shader_end = false;
|
|
int shader_end_ip = 0;
|
|
int shader_end_ip = 0;
|
|
|
|
+ uint32_t last_thread_switch_ip = -3;
|
|
uint32_t ip;
|
|
uint32_t ip;
|
|
struct vc4_validated_shader_info *validated_shader = NULL;
|
|
struct vc4_validated_shader_info *validated_shader = NULL;
|
|
struct vc4_shader_validation_state validation_state;
|
|
struct vc4_shader_validation_state validation_state;
|
|
@@ -785,6 +808,17 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj)
|
|
if (!vc4_handle_branch_target(&validation_state))
|
|
if (!vc4_handle_branch_target(&validation_state))
|
|
goto fail;
|
|
goto fail;
|
|
|
|
|
|
|
|
+ if (ip == last_thread_switch_ip + 3) {
|
|
|
|
+ /* Reset r0-r3 live clamp data */
|
|
|
|
+ int i;
|
|
|
|
+
|
|
|
|
+ for (i = 64; i < LIVE_REG_COUNT; i++) {
|
|
|
|
+ validation_state.live_min_clamp_offsets[i] = ~0;
|
|
|
|
+ validation_state.live_max_clamp_regs[i] = false;
|
|
|
|
+ validation_state.live_immediates[i] = ~0;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
switch (sig) {
|
|
switch (sig) {
|
|
case QPU_SIG_NONE:
|
|
case QPU_SIG_NONE:
|
|
case QPU_SIG_WAIT_FOR_SCOREBOARD:
|
|
case QPU_SIG_WAIT_FOR_SCOREBOARD:
|
|
@@ -794,6 +828,8 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj)
|
|
case QPU_SIG_LOAD_TMU1:
|
|
case QPU_SIG_LOAD_TMU1:
|
|
case QPU_SIG_PROG_END:
|
|
case QPU_SIG_PROG_END:
|
|
case QPU_SIG_SMALL_IMM:
|
|
case QPU_SIG_SMALL_IMM:
|
|
|
|
+ case QPU_SIG_THREAD_SWITCH:
|
|
|
|
+ case QPU_SIG_LAST_THREAD_SWITCH:
|
|
if (!check_instruction_writes(validated_shader,
|
|
if (!check_instruction_writes(validated_shader,
|
|
&validation_state)) {
|
|
&validation_state)) {
|
|
DRM_ERROR("Bad write at ip %d\n", ip);
|
|
DRM_ERROR("Bad write at ip %d\n", ip);
|
|
@@ -809,6 +845,18 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj)
|
|
shader_end_ip = ip;
|
|
shader_end_ip = ip;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (sig == QPU_SIG_THREAD_SWITCH ||
|
|
|
|
+ sig == QPU_SIG_LAST_THREAD_SWITCH) {
|
|
|
|
+ validated_shader->is_threaded = true;
|
|
|
|
+
|
|
|
|
+ if (ip < last_thread_switch_ip + 3) {
|
|
|
|
+ DRM_ERROR("Thread switch too soon after "
|
|
|
|
+ "last switch at ip %d\n", ip);
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+ last_thread_switch_ip = ip;
|
|
|
|
+ }
|
|
|
|
+
|
|
break;
|
|
break;
|
|
|
|
|
|
case QPU_SIG_LOAD_IMM:
|
|
case QPU_SIG_LOAD_IMM:
|
|
@@ -823,6 +871,13 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj)
|
|
if (!check_branch(inst, validated_shader,
|
|
if (!check_branch(inst, validated_shader,
|
|
&validation_state, ip))
|
|
&validation_state, ip))
|
|
goto fail;
|
|
goto fail;
|
|
|
|
+
|
|
|
|
+ if (ip < last_thread_switch_ip + 3) {
|
|
|
|
+ DRM_ERROR("Branch in thread switch at ip %d",
|
|
|
|
+ ip);
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
break;
|
|
break;
|
|
default:
|
|
default:
|
|
DRM_ERROR("Unsupported QPU signal %d at "
|
|
DRM_ERROR("Unsupported QPU signal %d at "
|
|
@@ -844,6 +899,14 @@ vc4_validate_shader(struct drm_gem_cma_object *shader_obj)
|
|
goto fail;
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /* Might corrupt other thread */
|
|
|
|
+ if (validated_shader->is_threaded &&
|
|
|
|
+ validation_state.all_registers_used) {
|
|
|
|
+ DRM_ERROR("Shader uses threading, but uses the upper "
|
|
|
|
+ "half of the registers, too\n");
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
/* If we did a backwards branch and we haven't emitted a uniforms
|
|
/* If we did a backwards branch and we haven't emitted a uniforms
|
|
* reset since then, we still need the uniforms stream to have the
|
|
* reset since then, we still need the uniforms stream to have the
|
|
* uniforms address available so that the backwards branch can do its
|
|
* uniforms address available so that the backwards branch can do its
|