2 * Copyright © 2015 Intel Corporation
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
25 #include "nir_builder.h"
26 #include "nir_control_flow.h"
28 struct lower_returns_state
{
30 struct exec_list
*cf_list
;
32 nir_variable
*return_flag
;
35 static bool lower_returns_in_cf_list(struct exec_list
*cf_list
,
36 struct lower_returns_state
*state
);
39 predicate_following(nir_cf_node
*node
, struct lower_returns_state
*state
)
41 nir_builder
*b
= &state
->builder
;
42 b
->cursor
= nir_after_cf_node_and_phis(node
);
44 if (nir_cursors_equal(b
->cursor
, nir_after_cf_list(state
->cf_list
)))
45 return; /* Nothing to predicate */
47 assert(state
->return_flag
);
49 nir_if
*if_stmt
= nir_if_create(b
->shader
);
50 if_stmt
->condition
= nir_src_for_ssa(nir_load_var(b
, state
->return_flag
));
51 nir_cf_node_insert(b
->cursor
, &if_stmt
->cf_node
);
54 /* If we're inside of a loop, then all we need to do is insert a
58 nir_jump_instr_create(state
->builder
.shader
, nir_jump_break
);
59 nir_instr_insert(nir_before_cf_list(&if_stmt
->then_list
), &brk
->instr
);
61 /* Otherwise, we need to actually move everything into the else case
62 * of the if statement.
65 nir_cf_extract(&list
, nir_after_cf_node(&if_stmt
->cf_node
),
66 nir_after_cf_list(state
->cf_list
));
67 assert(!exec_list_is_empty(&list
.list
));
68 nir_cf_reinsert(&list
, nir_before_cf_list(&if_stmt
->else_list
));
73 lower_returns_in_loop(nir_loop
*loop
, struct lower_returns_state
*state
)
75 nir_loop
*parent
= state
->loop
;
77 bool progress
= lower_returns_in_cf_list(&loop
->body
, state
);
80 /* If the recursive call made progress, then there were returns inside
81 * of the loop. These would have been lowered to breaks with the return
82 * flag set to true. We need to predicate everything following the loop
86 predicate_following(&loop
->cf_node
, state
);
92 lower_returns_in_if(nir_if
*if_stmt
, struct lower_returns_state
*state
)
96 progress
= lower_returns_in_cf_list(&if_stmt
->then_list
, state
);
97 progress
= lower_returns_in_cf_list(&if_stmt
->else_list
, state
) || progress
;
99 /* If either of the recursive calls made progress, then there were
100 * returns inside of the body of the if. If we're in a loop, then these
101 * were lowered to breaks which automatically skip to the end of the
102 * loop so we don't have to do anything. If we're not in a loop, then
103 * all we know is that the return flag is set appropreately and that the
104 * recursive calls ensured that nothing gets executed *inside* the if
105 * after a return. In order to ensure nothing outside gets executed
106 * after a return, we need to predicate everything following on the
109 if (progress
&& !state
->loop
)
110 predicate_following(&if_stmt
->cf_node
, state
);
116 lower_returns_in_block(nir_block
*block
, struct lower_returns_state
*state
)
118 if (block
->predecessors
->entries
== 0 &&
119 block
!= nir_start_block(state
->builder
.impl
)) {
120 /* This block is unreachable. Delete it and everything after it. */
122 nir_cf_extract(&list
, nir_before_cf_node(&block
->cf_node
),
123 nir_after_cf_list(state
->cf_list
));
125 if (exec_list_is_empty(&list
.list
)) {
126 /* There's nothing here, which also means there's nothing in this
127 * block so we have nothing to do.
131 nir_cf_delete(&list
);
136 nir_instr
*last_instr
= nir_block_last_instr(block
);
137 if (last_instr
== NULL
)
140 if (last_instr
->type
!= nir_instr_type_jump
)
143 nir_jump_instr
*jump
= nir_instr_as_jump(last_instr
);
144 if (jump
->type
!= nir_jump_return
)
147 nir_builder
*b
= &state
->builder
;
148 b
->cursor
= nir_before_instr(&jump
->instr
);
150 /* Set the return flag */
151 if (state
->return_flag
== NULL
) {
153 nir_local_variable_create(b
->impl
, glsl_bool_type(), "return");
155 /* Set a default value of false */
156 state
->return_flag
->constant_initializer
=
157 rzalloc(state
->return_flag
, nir_constant
);
159 nir_store_var(b
, state
->return_flag
, nir_imm_int(b
, NIR_TRUE
), 1);
162 /* We're in a loop. Make the return a break. */
163 jump
->type
= nir_jump_break
;
165 /* Not in a loop. Just delete the return; we'll deal with
168 assert(nir_cf_node_next(&block
->cf_node
) == NULL
);
169 nir_instr_remove(&jump
->instr
);
176 lower_returns_in_cf_list(struct exec_list
*cf_list
,
177 struct lower_returns_state
*state
)
179 bool progress
= false;
181 struct exec_list
*parent_list
= state
->cf_list
;
182 state
->cf_list
= cf_list
;
184 /* We iterate over the list backwards because any given lower call may
185 * take everything following the given CF node and predicate it. In
186 * order to avoid recursion/iteration problems, we want everything after
187 * a given node to already be lowered before this happens.
189 foreach_list_typed_reverse_safe(nir_cf_node
, node
, node
, cf_list
) {
190 switch (node
->type
) {
191 case nir_cf_node_block
:
192 if (lower_returns_in_block(nir_cf_node_as_block(node
), state
))
197 if (lower_returns_in_if(nir_cf_node_as_if(node
), state
))
201 case nir_cf_node_loop
:
202 if (lower_returns_in_loop(nir_cf_node_as_loop(node
), state
))
207 unreachable("Invalid inner CF node type");
211 state
->cf_list
= parent_list
;
217 nir_lower_returns_impl(nir_function_impl
*impl
)
219 struct lower_returns_state state
;
221 state
.cf_list
= &impl
->body
;
223 state
.return_flag
= NULL
;
224 nir_builder_init(&state
.builder
, impl
);
226 bool progress
= lower_returns_in_cf_list(&impl
->body
, &state
);
229 nir_metadata_preserve(impl
, nir_metadata_none
);
235 nir_lower_returns(nir_shader
*shader
)
237 bool progress
= false;
239 nir_foreach_function(shader
, function
) {
241 progress
= nir_lower_returns_impl(function
->impl
) || progress
;