mirror of https://gitlab.freedesktop.org/mesa/mesa
254 lines
8.1 KiB
C
254 lines
8.1 KiB
C
/*
|
|
* Copyright (C) 2019 Collabora, Ltd.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a
|
|
* copy of this software and associated documentation files (the "Software"),
|
|
* to deal in the Software without restriction, including without limitation
|
|
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
* and/or sell copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice (including the next
|
|
* paragraph) shall be included in all copies or substantial portions of the
|
|
* Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
* Authors (Collabora):
|
|
* Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
|
|
*/
|
|
|
|
#include "compiler.h"
|
|
|
|
/* Midgard texture/derivative operations have a pair of bits controlling the
|
|
* behaviour of helper invocations:
|
|
*
|
|
* - Should a helper invocation terminate after executing this instruction?
|
|
* - Should a helper invocation actually execute this instruction?
|
|
*
|
|
* The terminate bit should be set on the last instruction requiring helper
|
|
* invocations. Without control flow, that's literally the last instruction;
|
|
* with control flow, there may be multiple such instructions (with ifs) or no
|
|
* such instruction (with loops).
|
|
*
|
|
* The execute bit should be set if the value of this instruction is required
|
|
* by a future instruction requiring helper invocations. Consider:
|
|
*
|
|
* 0 = texture ...
|
|
* 1 = fmul 0, #10
|
|
* 2 = dfdx 1
|
|
* store 2
|
|
*
|
|
* Since the derivative calculation 2 requires helper invocations, the value 1
|
|
* must be calculated by helper invocations, and since it depends on 0, 0 must
|
|
* be calculated by helpers. Hence the texture op has the execute bit set, and
|
|
* the derivative op has the terminate bit set.
|
|
*
|
|
* Calculating the terminate bit occurs by forward dataflow analysis to
|
|
* determine which blocks require helper invocations. A block requires
|
|
* invocations in if any of its instructions use helper invocations, or if it
|
|
* depends on a block that requires invocation. With that analysis, the
|
|
* terminate bit is set on the last instruction using invocations within any
|
|
* block that does *not* require invocations out.
|
|
*
|
|
* Likewise, calculating the execute bit requires backward dataflow analysis
|
|
* with union as the join operation and the generating set being the union of
|
|
* sources of instructions writing executed values.
|
|
*/
|
|
|
|
/* Does a block use helpers directly */
|
|
static bool
|
|
mir_block_uses_helpers(gl_shader_stage stage, midgard_block *block)
|
|
{
|
|
mir_foreach_instr_in_block(block, ins) {
|
|
if (ins->type != TAG_TEXTURE_4)
|
|
continue;
|
|
if (mir_op_computes_derivatives(stage, ins->op))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static bool
|
|
mir_block_terminates_helpers(midgard_block *block)
|
|
{
|
|
/* Can't terminate if there are no helpers */
|
|
if (!block->helpers_in)
|
|
return false;
|
|
|
|
/* Can't terminate if a successor needs helpers */
|
|
pan_foreach_successor((&block->base), succ) {
|
|
if (((midgard_block *)succ)->helpers_in)
|
|
return false;
|
|
}
|
|
|
|
/* Otherwise we terminate */
|
|
return true;
|
|
}
|
|
|
|
void
|
|
mir_analyze_helper_terminate(compiler_context *ctx)
|
|
{
|
|
/* Set blocks as directly requiring helpers, and if they do add them to
|
|
* the worklist to propagate to their predecessors */
|
|
|
|
struct set *worklist =
|
|
_mesa_set_create(NULL, _mesa_hash_pointer, _mesa_key_pointer_equal);
|
|
|
|
struct set *visited =
|
|
_mesa_set_create(NULL, _mesa_hash_pointer, _mesa_key_pointer_equal);
|
|
|
|
mir_foreach_block(ctx, _block) {
|
|
midgard_block *block = (midgard_block *)_block;
|
|
block->helpers_in |= mir_block_uses_helpers(ctx->stage, block);
|
|
|
|
if (block->helpers_in)
|
|
_mesa_set_add(worklist, _block);
|
|
}
|
|
|
|
/* Next, propagate back. Since there are a finite number of blocks, the
|
|
* worklist (a subset of all the blocks) is finite. Since a block can
|
|
* only be added to the worklist if it is not on the visited list and
|
|
* the visited list - also a subset of the blocks - grows every
|
|
* iteration, the algorithm must terminate. */
|
|
|
|
struct set_entry *cur;
|
|
|
|
while ((cur = _mesa_set_next_entry(worklist, NULL)) != NULL) {
|
|
/* Pop off a block requiring helpers */
|
|
pan_block *blk = (struct pan_block *)cur->key;
|
|
_mesa_set_remove(worklist, cur);
|
|
|
|
/* Its predecessors also require helpers */
|
|
pan_foreach_predecessor(blk, pred) {
|
|
if (!_mesa_set_search(visited, pred)) {
|
|
((midgard_block *)pred)->helpers_in = true;
|
|
_mesa_set_add(worklist, pred);
|
|
}
|
|
}
|
|
|
|
_mesa_set_add(visited, blk);
|
|
}
|
|
|
|
_mesa_set_destroy(visited, NULL);
|
|
_mesa_set_destroy(worklist, NULL);
|
|
|
|
/* Finally, set helper_terminate on the last derivative-calculating
|
|
* instruction in a block that terminates helpers */
|
|
mir_foreach_block(ctx, _block) {
|
|
midgard_block *block = (midgard_block *)_block;
|
|
|
|
if (!mir_block_terminates_helpers(block))
|
|
continue;
|
|
|
|
mir_foreach_instr_in_block_rev(block, ins) {
|
|
if (ins->type != TAG_TEXTURE_4)
|
|
continue;
|
|
if (!mir_op_computes_derivatives(ctx->stage, ins->op))
|
|
continue;
|
|
|
|
ins->helper_terminate = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool
|
|
mir_helper_block_update(BITSET_WORD *deps, pan_block *_block,
|
|
unsigned temp_count)
|
|
{
|
|
bool progress = false;
|
|
midgard_block *block = (midgard_block *)_block;
|
|
|
|
mir_foreach_instr_in_block_rev(block, ins) {
|
|
/* Ensure we write to a helper dependency */
|
|
if (ins->dest >= temp_count || !BITSET_TEST(deps, ins->dest))
|
|
continue;
|
|
|
|
/* Then add all of our dependencies */
|
|
mir_foreach_src(ins, s) {
|
|
if (ins->src[s] >= temp_count)
|
|
continue;
|
|
|
|
/* Progress if the dependency set changes */
|
|
progress |= !BITSET_TEST(deps, ins->src[s]);
|
|
BITSET_SET(deps, ins->src[s]);
|
|
}
|
|
}
|
|
|
|
return progress;
|
|
}
|
|
|
|
void
|
|
mir_analyze_helper_requirements(compiler_context *ctx)
|
|
{
|
|
mir_compute_temp_count(ctx);
|
|
unsigned temp_count = ctx->temp_count;
|
|
BITSET_WORD *deps = calloc(sizeof(BITSET_WORD), BITSET_WORDS(temp_count));
|
|
|
|
/* Initialize with the sources of instructions consuming
|
|
* derivatives */
|
|
|
|
mir_foreach_instr_global(ctx, ins) {
|
|
if (ins->type != TAG_TEXTURE_4)
|
|
continue;
|
|
if (ins->dest >= ctx->temp_count)
|
|
continue;
|
|
if (!mir_op_computes_derivatives(ctx->stage, ins->op))
|
|
continue;
|
|
|
|
mir_foreach_src(ins, s) {
|
|
if (ins->src[s] < temp_count)
|
|
BITSET_SET(deps, ins->src[s]);
|
|
}
|
|
}
|
|
|
|
/* Propagate that up */
|
|
|
|
struct set *work_list =
|
|
_mesa_set_create(NULL, _mesa_hash_pointer, _mesa_key_pointer_equal);
|
|
|
|
struct set *visited =
|
|
_mesa_set_create(NULL, _mesa_hash_pointer, _mesa_key_pointer_equal);
|
|
|
|
struct set_entry *cur =
|
|
_mesa_set_add(work_list, pan_exit_block(&ctx->blocks));
|
|
|
|
do {
|
|
pan_block *blk = (struct pan_block *)cur->key;
|
|
_mesa_set_remove(work_list, cur);
|
|
|
|
bool progress = mir_helper_block_update(deps, blk, temp_count);
|
|
|
|
if (progress || !_mesa_set_search(visited, blk)) {
|
|
pan_foreach_predecessor(blk, pred)
|
|
_mesa_set_add(work_list, pred);
|
|
}
|
|
|
|
_mesa_set_add(visited, blk);
|
|
} while ((cur = _mesa_set_next_entry(work_list, NULL)) != NULL);
|
|
|
|
_mesa_set_destroy(visited, NULL);
|
|
_mesa_set_destroy(work_list, NULL);
|
|
|
|
/* Set the execute bits */
|
|
|
|
mir_foreach_instr_global(ctx, ins) {
|
|
if (ins->type != TAG_TEXTURE_4)
|
|
continue;
|
|
if (ins->dest >= ctx->temp_count)
|
|
continue;
|
|
|
|
ins->helper_execute = BITSET_TEST(deps, ins->dest);
|
|
}
|
|
|
|
free(deps);
|
|
}
|