Commit 6772a17a authored by Thomas Helland's avatar Thomas Helland Committed by Timothy Arceri

nir: Add a loop analysis pass

This pass detects induction variables and calculates the
trip count of loops to be used for loop unrolling.

V2: Rebase, adapt to removal of function overloads

V3: (Timothy Arceri)
 - don't try to find trip count if loop terminator conditional is a phi
 - fix trip count for do-while loops
 - replace conditional type != alu assert with return
 - disable unrolling of loops with continues
 - multiple fixes to memory allocation, stop leaking and don't destroy
   structs we want to use for unrolling.
 - fix iteration count bugs when induction var not on RHS of condition
 - add FIXME for && conditions
 - calculate trip count for unsigned induction/limit vars

V4: (Timothy Arceri)
- count instructions in a loop
- set the limiting_terminator even if we can't find the trip count for
 all terminators. This is needed for complex unrolling where we handle
 2 terminators and the trip count is unknown for one of them.
- restruct structs so we don't keep information not required after
 analysis and remove dead fields.
- force unrolling in some cases as per the rules in the GLSL IR pass

V5: (Timothy Arceri)
- fix metadata mask value 0x10 vs 0x16

V6: (Timothy Arceri)
- merge loop_variable and nir_loop_variable structs and lists suggested by Jason
- remove induction var hash table and store pointer to induction information in
  the loop_variable suggested by Jason.
- use lowercase list_addtail() suggested by Jason.
- tidy up init_loop_block() as per Jasons suggestions.
- replace switch with nir_op_infos[alu->op].num_inputs == 2 in
  is_var_basic_induction_var() as suggested by Jason.
- use nir_block_last_instr() in and rename foreach_cf_node_ex_loop() as suggested
  by Jason.
- fix else check for is_trivial_loop_terminator() as per Connors suggetions.
- simplify offset for induction valiables incremented before the exit conditions is
  checked.
- replace nir_op_isub check with assert() as it should have been lowered away.

V7: (Timothy Arceri)
- use rzalloc() on nir_loop struct creation. Worked previously because ralloc()
  was broken and always zeroed the struct.
- fix cf_node_find_loop_jumps() to find jumps when loops contain
  nested if statements. Code is tidier as a result.

V8: (Timothy Arceri)
- move is_trivial_loop_terminator() to nir.h so we can use it to assert is
  the loop unroll pass
- fix analysis to not bail when looking for terminator when the break is in the else
  rather then the if
- added new loop terminator fields: break_block, continue_from_block and
  continue_from_then so we don't have to gather these when doing unrolling.
- get correct array length when forcing unrolling of variables
  indexed arrays that are the same size as the iteration count
- add support for induction variables of type float
- update trival loop terminator check to allow an if containing
  instructions as long as both branches contain only a single
  block.

V9: (Timothy)
 - bunch of tidy ups and simplifications suggested by Jason.
 - rewrote trivial terminator detection, now the only restriction is there
   must be no nested jumps, anything else goes.
 - rewrote the iteration test to use nir_eval_const_opcode().
 - count instruction properly even when forcing an unroll.
 - bunch of other tidy ups and simplifications.

V10: (Timothy)
 - some trivial tidy ups suggested by Jason.
 - conditional fix for break inside continue branch by Jason.
Reviewed-by: Jason Ekstrand's avatarJason Ekstrand <jason@jlekstrand.net>
parent eda3ec79
......@@ -195,6 +195,8 @@ NIR_FILES = \
nir/nir_intrinsics.c \
nir/nir_intrinsics.h \
nir/nir_liveness.c \
nir/nir_loop_analyze.c \
nir/nir_loop_analyze.h \
nir/nir_lower_alu_to_scalar.c \
nir/nir_lower_atomics.c \
nir/nir_lower_bitmap.c \
......
......@@ -393,7 +393,7 @@ nir_if_create(nir_shader *shader)
nir_loop *
nir_loop_create(nir_shader *shader)
{
nir_loop *loop = ralloc(shader, nir_loop);
nir_loop *loop = rzalloc(shader, nir_loop);
cf_init(&loop->cf_node, nir_cf_node_loop);
......
......@@ -1505,10 +1505,42 @@ typedef struct nir_if {
struct exec_list else_list; /** < list of nir_cf_node */
} nir_if;
typedef struct {
nir_if *nif;
nir_instr *conditional_instr;
nir_block *break_block;
nir_block *continue_from_block;
bool continue_from_then;
struct list_head loop_terminator_link;
} nir_loop_terminator;
typedef struct {
/* Number of instructions in the loop */
unsigned num_instructions;
/* How many times the loop is run (if known) */
unsigned trip_count;
bool is_trip_count_known;
/* Unroll the loop regardless of its size */
bool force_unroll;
nir_loop_terminator *limiting_terminator;
/* A list of loop_terminators terminating this loop. */
struct list_head loop_terminator_list;
} nir_loop_info;
typedef struct {
nir_cf_node cf_node;
struct exec_list body; /** < list of nir_cf_node */
nir_loop_info *info;
} nir_loop;
/**
......@@ -1521,6 +1553,7 @@ typedef enum {
nir_metadata_dominance = 0x2,
nir_metadata_live_ssa_defs = 0x4,
nir_metadata_not_properly_reset = 0x8,
nir_metadata_loop_analysis = 0x10,
} nir_metadata;
typedef struct {
......@@ -1749,6 +1782,8 @@ typedef struct nir_shader_compiler_options {
* information must be inferred from the list of input nir_variables.
*/
bool use_interpolated_input_intrinsics;
unsigned max_unroll_iterations;
} nir_shader_compiler_options;
typedef struct nir_shader {
......@@ -1859,7 +1894,7 @@ nir_loop *nir_loop_create(nir_shader *shader);
nir_function_impl *nir_cf_node_get_function(nir_cf_node *node);
/** requests that the given pieces of metadata be generated */
void nir_metadata_require(nir_function_impl *impl, nir_metadata required);
void nir_metadata_require(nir_function_impl *impl, nir_metadata required, ...);
/** dirties all but the preserved metadata */
void nir_metadata_preserve(nir_function_impl *impl, nir_metadata preserved);
......@@ -2479,6 +2514,10 @@ void nir_lower_double_pack(nir_shader *shader);
bool nir_normalize_cubemap_coords(nir_shader *shader);
void nir_live_ssa_defs_impl(nir_function_impl *impl);
void nir_loop_analyze_impl(nir_function_impl *impl,
nir_variable_mode indirect_mask);
bool nir_ssa_defs_interfere(nir_ssa_def *a, nir_ssa_def *b);
void nir_convert_to_ssa_impl(nir_function_impl *impl);
......
This diff is collapsed.
/*
* Copyright © 2016 Intel Corporation
*
* 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.
*/
#pragma once
#include "nir.h"
/* Returns true if nir_cf_node contains a jump other than the expected_jump
* parameter.
*/
static inline bool
contains_other_jump(nir_cf_node *node, nir_instr *expected_jump)
{
switch (node->type) {
case nir_cf_node_block: {
nir_instr *lst_instr = nir_block_last_instr(nir_cf_node_as_block(node));
/* dead_cf should have eliminated any instruction after the first break
*/
nir_foreach_instr(instr, nir_cf_node_as_block(node))
assert(instr->type != nir_instr_type_jump || instr == lst_instr);
if (lst_instr && lst_instr->type == nir_instr_type_jump &&
lst_instr != expected_jump)
return true;
else
return false;
}
case nir_cf_node_if: {
nir_if *if_stmt = nir_cf_node_as_if(node);
foreach_list_typed_safe(nir_cf_node, node, node, &if_stmt->then_list) {
if (contains_other_jump(node, expected_jump))
return true;
}
foreach_list_typed_safe(nir_cf_node, node, node, &if_stmt->else_list) {
if (contains_other_jump(node, expected_jump))
return true;
}
return false;
}
case nir_cf_node_loop:
return true;
default:
unreachable("Unhandled cf node type");
}
}
/* Here we define a trivial if as containing only a single break that must be
* located at the end of either the then or else branch of the top level if,
* there must be no other breaks or any other type of jump. Or we pass NULL
* to break_block the if must contains no jumps at all.
*/
static inline bool
nir_is_trivial_loop_if(nir_if *nif, nir_block *break_block)
{
nir_instr *last_instr = NULL;
if (break_block) {
last_instr = nir_block_last_instr(break_block);
assert(last_instr && last_instr->type == nir_instr_type_jump &&
nir_instr_as_jump(last_instr)->type == nir_jump_break);
}
if (contains_other_jump(&nif->cf_node, last_instr))
return false;
return true;
}
......@@ -31,7 +31,7 @@
*/
void
nir_metadata_require(nir_function_impl *impl, nir_metadata required)
nir_metadata_require(nir_function_impl *impl, nir_metadata required, ...)
{
#define NEEDS_UPDATE(X) ((required & ~impl->valid_metadata) & (X))
......@@ -41,6 +41,12 @@ nir_metadata_require(nir_function_impl *impl, nir_metadata required)
nir_calc_dominance_impl(impl);
if (NEEDS_UPDATE(nir_metadata_live_ssa_defs))
nir_live_ssa_defs_impl(impl);
if (NEEDS_UPDATE(nir_metadata_loop_analysis)) {
va_list ap;
va_start(ap, required);
nir_loop_analyze_impl(impl, va_arg(ap, nir_variable_mode));
va_end(ap);
}
#undef NEEDS_UPDATE
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment