123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200 |
- From a2be1b8d84ab4ad9a1721fd67824f1e164d5862b Mon Sep 17 00:00:00 2001
- From: Eric Anholt <eric@anholt.net>
- Date: Sat, 2 Jul 2016 10:10:24 -0700
- Subject: [PATCH] drm/vc4: Add a bitmap of branch targets during shader
- validation.
- This isn't used yet, it's just a first step toward loop validation.
- During the main parsing of instructions, we need to know when we hit a
- new basic block so that we can reset validated state.
- v2: Fix a stray semicolon after an if block. (caught by kbuild test).
- Signed-off-by: Eric Anholt <eric@anholt.net>
- (cherry picked from commit 93aa9ae3e5523e49e4e5abacd4dbee0e4ab2d931)
- ---
- drivers/gpu/drm/vc4/vc4_qpu_defines.h | 12 +++
- drivers/gpu/drm/vc4/vc4_validate_shaders.c | 114 ++++++++++++++++++++++++++++-
- 2 files changed, 124 insertions(+), 2 deletions(-)
- --- a/drivers/gpu/drm/vc4/vc4_qpu_defines.h
- +++ b/drivers/gpu/drm/vc4/vc4_qpu_defines.h
- @@ -230,6 +230,15 @@ enum qpu_unpack_r4 {
- #define QPU_COND_MUL_SHIFT 46
- #define QPU_COND_MUL_MASK QPU_MASK(48, 46)
-
- +#define QPU_BRANCH_COND_SHIFT 52
- +#define QPU_BRANCH_COND_MASK QPU_MASK(55, 52)
- +
- +#define QPU_BRANCH_REL ((uint64_t)1 << 51)
- +#define QPU_BRANCH_REG ((uint64_t)1 << 50)
- +
- +#define QPU_BRANCH_RADDR_A_SHIFT 45
- +#define QPU_BRANCH_RADDR_A_MASK QPU_MASK(49, 45)
- +
- #define QPU_SF ((uint64_t)1 << 45)
-
- #define QPU_WADDR_ADD_SHIFT 38
- @@ -261,4 +270,7 @@ enum qpu_unpack_r4 {
- #define QPU_OP_ADD_SHIFT 24
- #define QPU_OP_ADD_MASK QPU_MASK(28, 24)
-
- +#define QPU_BRANCH_TARGET_SHIFT 0
- +#define QPU_BRANCH_TARGET_MASK QPU_MASK(31, 0)
- +
- #endif /* VC4_QPU_DEFINES_H */
- --- a/drivers/gpu/drm/vc4/vc4_validate_shaders.c
- +++ b/drivers/gpu/drm/vc4/vc4_validate_shaders.c
- @@ -59,6 +59,13 @@ struct vc4_shader_validation_state {
- */
- uint32_t live_min_clamp_offsets[32 + 32 + 4];
- bool live_max_clamp_regs[32 + 32 + 4];
- +
- + /* Bitfield of which IPs are used as branch targets.
- + *
- + * Used for validation that the uniform stream is updated at the right
- + * points and clearing the texturing/clamping state.
- + */
- + unsigned long *branch_targets;
- };
-
- static uint32_t
- @@ -418,13 +425,104 @@ check_instruction_reads(uint64_t inst,
- return true;
- }
-
- +/* Make sure that all branches are absolute and point within the shader, and
- + * note their targets for later.
- + */
- +static bool
- +vc4_validate_branches(struct vc4_shader_validation_state *validation_state)
- +{
- + uint32_t max_branch_target = 0;
- + bool found_shader_end = false;
- + int ip;
- + int shader_end_ip = 0;
- + int last_branch = -2;
- +
- + for (ip = 0; ip < validation_state->max_ip; ip++) {
- + uint64_t inst = validation_state->shader[ip];
- + int32_t branch_imm = QPU_GET_FIELD(inst, QPU_BRANCH_TARGET);
- + uint32_t sig = QPU_GET_FIELD(inst, QPU_SIG);
- + uint32_t after_delay_ip = ip + 4;
- + uint32_t branch_target_ip;
- +
- + if (sig == QPU_SIG_PROG_END) {
- + shader_end_ip = ip;
- + found_shader_end = true;
- + continue;
- + }
- +
- + if (sig != QPU_SIG_BRANCH)
- + continue;
- +
- + if (ip - last_branch < 4) {
- + DRM_ERROR("Branch at %d during delay slots\n", ip);
- + return false;
- + }
- + last_branch = ip;
- +
- + if (inst & QPU_BRANCH_REG) {
- + DRM_ERROR("branching from register relative "
- + "not supported\n");
- + return false;
- + }
- +
- + if (!(inst & QPU_BRANCH_REL)) {
- + DRM_ERROR("relative branching required\n");
- + return false;
- + }
- +
- + /* The actual branch target is the instruction after the delay
- + * slots, plus whatever byte offset is in the low 32 bits of
- + * the instruction. Make sure we're not branching beyond the
- + * end of the shader object.
- + */
- + if (branch_imm % sizeof(inst) != 0) {
- + DRM_ERROR("branch target not aligned\n");
- + return false;
- + }
- +
- + branch_target_ip = after_delay_ip + (branch_imm >> 3);
- + if (branch_target_ip >= validation_state->max_ip) {
- + DRM_ERROR("Branch at %d outside of shader (ip %d/%d)\n",
- + ip, branch_target_ip,
- + validation_state->max_ip);
- + return false;
- + }
- + set_bit(branch_target_ip, validation_state->branch_targets);
- +
- + /* Make sure that the non-branching path is also not outside
- + * the shader.
- + */
- + if (after_delay_ip >= validation_state->max_ip) {
- + DRM_ERROR("Branch at %d continues past shader end "
- + "(%d/%d)\n",
- + ip, after_delay_ip, validation_state->max_ip);
- + return false;
- + }
- + set_bit(after_delay_ip, validation_state->branch_targets);
- + max_branch_target = max(max_branch_target, after_delay_ip);
- +
- + /* There are two delay slots after program end is signaled
- + * that are still executed, then we're finished.
- + */
- + if (found_shader_end && ip == shader_end_ip + 2)
- + break;
- + }
- +
- + if (max_branch_target > shader_end_ip) {
- + DRM_ERROR("Branch landed after QPU_SIG_PROG_END");
- + return false;
- + }
- +
- + return true;
- +}
- +
- struct vc4_validated_shader_info *
- vc4_validate_shader(struct drm_gem_cma_object *shader_obj)
- {
- bool found_shader_end = false;
- int shader_end_ip = 0;
- uint32_t ip;
- - struct vc4_validated_shader_info *validated_shader;
- + struct vc4_validated_shader_info *validated_shader = NULL;
- struct vc4_shader_validation_state validation_state;
- int i;
-
- @@ -437,9 +535,18 @@ vc4_validate_shader(struct drm_gem_cma_o
- for (i = 0; i < ARRAY_SIZE(validation_state.live_min_clamp_offsets); i++)
- validation_state.live_min_clamp_offsets[i] = ~0;
-
- + validation_state.branch_targets =
- + kcalloc(BITS_TO_LONGS(validation_state.max_ip),
- + sizeof(unsigned long), GFP_KERNEL);
- + if (!validation_state.branch_targets)
- + goto fail;
- +
- validated_shader = kcalloc(1, sizeof(*validated_shader), GFP_KERNEL);
- if (!validated_shader)
- - return NULL;
- + goto fail;
- +
- + if (!vc4_validate_branches(&validation_state))
- + goto fail;
-
- for (ip = 0; ip < validation_state.max_ip; ip++) {
- uint64_t inst = validation_state.shader[ip];
- @@ -508,9 +615,12 @@ vc4_validate_shader(struct drm_gem_cma_o
- (validated_shader->uniforms_size +
- 4 * validated_shader->num_texture_samples);
-
- + kfree(validation_state.branch_targets);
- +
- return validated_shader;
-
- fail:
- + kfree(validation_state.branch_targets);
- if (validated_shader) {
- kfree(validated_shader->texture_samples);
- kfree(validated_shader);
|