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 static bool inline_function_impl(nir_function_impl
*impl
, struct set
*inlined
);
31 convert_deref_to_param_deref(nir_instr
*instr
, nir_deref_var
**deref
,
34 /* This isn't a parameter, just return the deref */
35 if ((*deref
)->var
->data
.mode
!= nir_var_param
)
38 int param_idx
= (*deref
)->var
->data
.location
;
40 nir_deref_var
*call_deref
;
42 assert(param_idx
< call
->callee
->num_params
);
43 call_deref
= call
->params
[param_idx
];
45 call_deref
= call
->return_deref
;
49 /* Now we make a new deref by concatenating the deref in the call's
50 * parameter with the deref we were given.
52 nir_deref_var
*new_deref
= nir_deref_var_clone(call_deref
, instr
);
53 nir_deref
*new_tail
= nir_deref_tail(&new_deref
->deref
);
54 new_tail
->child
= (*deref
)->deref
.child
;
55 ralloc_steal(new_tail
, new_tail
->child
);
60 rewrite_param_derefs(nir_instr
*instr
, nir_call_instr
*call
)
62 switch (instr
->type
) {
63 case nir_instr_type_intrinsic
: {
64 nir_intrinsic_instr
*intrin
= nir_instr_as_intrinsic(instr
);
67 i
< nir_intrinsic_infos
[intrin
->intrinsic
].num_variables
; i
++) {
68 convert_deref_to_param_deref(instr
, &intrin
->variables
[i
], call
);
73 case nir_instr_type_tex
: {
74 nir_tex_instr
*tex
= nir_instr_as_tex(instr
);
76 convert_deref_to_param_deref(&tex
->instr
, &tex
->texture
, call
);
78 convert_deref_to_param_deref(&tex
->instr
, &tex
->sampler
, call
);
83 break; /* Nothing else has derefs */
88 lower_param_to_local(nir_variable
*param
, nir_function_impl
*impl
, bool write
)
90 if (param
->data
.mode
!= nir_var_param
)
93 nir_parameter_type param_type
;
94 if (param
->data
.location
>= 0) {
95 assert(param
->data
.location
< impl
->num_params
);
96 param_type
= impl
->function
->params
[param
->data
.location
].param_type
;
99 param_type
= nir_parameter_out
;
102 if ((write
&& param_type
== nir_parameter_in
) ||
103 (!write
&& param_type
== nir_parameter_out
)) {
104 /* In this case, we need a shadow copy. Turn it into a local */
105 param
->data
.mode
= nir_var_local
;
106 exec_list_push_tail(&impl
->locals
, ¶m
->node
);
111 lower_params_to_locals_block(nir_block
*block
, nir_function_impl
*impl
)
113 nir_foreach_instr(instr
, block
) {
114 if (instr
->type
!= nir_instr_type_intrinsic
)
117 nir_intrinsic_instr
*intrin
= nir_instr_as_intrinsic(instr
);
119 switch (intrin
->intrinsic
) {
120 case nir_intrinsic_store_var
:
121 lower_param_to_local(intrin
->variables
[0]->var
, impl
, true);
124 case nir_intrinsic_copy_var
:
125 lower_param_to_local(intrin
->variables
[0]->var
, impl
, true);
126 lower_param_to_local(intrin
->variables
[1]->var
, impl
, false);
129 case nir_intrinsic_load_var
:
130 /* All other intrinsics which access variables (image_load_store)
131 * do so in a read-only fasion.
134 i
< nir_intrinsic_infos
[intrin
->intrinsic
].num_variables
; i
++) {
135 lower_param_to_local(intrin
->variables
[i
]->var
, impl
, false);
148 inline_functions_block(nir_block
*block
, nir_builder
*b
,
151 bool progress
= false;
152 /* This is tricky. We're iterating over instructions in a block but, as
153 * we go, the block and its instruction list are being split into
154 * pieces. However, this *should* be safe since foreach_safe always
155 * stashes the next thing in the iteration. That next thing will
156 * properly get moved to the next block when it gets split, and we
157 * continue iterating there.
159 nir_foreach_instr_safe(instr
, block
) {
160 if (instr
->type
!= nir_instr_type_call
)
165 nir_call_instr
*call
= nir_instr_as_call(instr
);
166 assert(call
->callee
->impl
);
168 inline_function_impl(call
->callee
->impl
, inlined
);
170 nir_function_impl
*callee_copy
=
171 nir_function_impl_clone(call
->callee
->impl
);
172 callee_copy
->function
= call
->callee
;
174 /* Add copies of all in parameters */
175 assert(call
->num_params
== callee_copy
->num_params
);
177 exec_list_append(&b
->impl
->locals
, &callee_copy
->locals
);
178 exec_list_append(&b
->impl
->registers
, &callee_copy
->registers
);
180 b
->cursor
= nir_before_instr(&call
->instr
);
182 /* We now need to tie the two functions together using the
183 * parameters. There are two ways we do this: One is to turn the
184 * parameter into a local variable and do a shadow-copy. The other
185 * is to treat the parameter as a "proxy" and rewrite derefs to use
186 * the actual variable that comes from the call instruction. We
187 * implement both schemes. The first is needed in the case where we
188 * have an in parameter that we write or similar. The second case is
189 * needed for handling things such as images and uniforms properly.
192 /* Figure out when we need to lower to a shadow local */
193 nir_foreach_block(block
, callee_copy
) {
194 lower_params_to_locals_block(block
, callee_copy
);
197 for (unsigned i
= 0; i
< callee_copy
->num_params
; i
++) {
198 nir_variable
*param
= callee_copy
->params
[i
];
200 if (param
->data
.mode
== nir_var_local
&&
201 call
->callee
->params
[i
].param_type
!= nir_parameter_out
) {
202 nir_copy_deref_var(b
, nir_deref_var_create(b
->shader
, param
),
207 nir_foreach_block(block
, callee_copy
) {
208 nir_foreach_instr(instr
, block
)
209 rewrite_param_derefs(instr
, call
);
212 /* Pluck the body out of the function and place it here */
214 nir_cf_list_extract(&body
, &callee_copy
->body
);
215 nir_cf_reinsert(&body
, b
->cursor
);
217 b
->cursor
= nir_before_instr(&call
->instr
);
219 /* Add copies of all out parameters and the return */
220 assert(call
->num_params
== callee_copy
->num_params
);
221 for (unsigned i
= 0; i
< callee_copy
->num_params
; i
++) {
222 nir_variable
*param
= callee_copy
->params
[i
];
224 if (param
->data
.mode
== nir_var_local
&&
225 call
->callee
->params
[i
].param_type
!= nir_parameter_in
) {
226 nir_copy_deref_var(b
, call
->params
[i
],
227 nir_deref_var_create(b
->shader
, param
));
230 if (!glsl_type_is_void(call
->callee
->return_type
) &&
231 callee_copy
->return_var
->data
.mode
== nir_var_local
) {
232 nir_copy_deref_var(b
, call
->return_deref
,
233 nir_deref_var_create(b
->shader
,
234 callee_copy
->return_var
));
237 nir_instr_remove(&call
->instr
);
244 inline_function_impl(nir_function_impl
*impl
, struct set
*inlined
)
246 if (_mesa_set_search(inlined
, impl
))
247 return false; /* Already inlined */
250 nir_builder_init(&b
, impl
);
252 bool progress
= false;
253 nir_foreach_block_safe(block
, impl
) {
254 progress
|= inline_functions_block(block
, &b
, inlined
);
258 /* SSA and register indices are completely messed up now */
259 nir_index_ssa_defs(impl
);
260 nir_index_local_regs(impl
);
262 nir_metadata_preserve(impl
, nir_metadata_none
);
265 _mesa_set_add(inlined
, impl
);
271 nir_inline_functions(nir_shader
*shader
)
273 struct set
*inlined
= _mesa_set_create(NULL
, _mesa_hash_pointer
,
274 _mesa_key_pointer_equal
);
275 bool progress
= false;
277 nir_foreach_function(function
, shader
) {
279 progress
= inline_function_impl(function
->impl
, inlined
) || progress
;
282 _mesa_set_destroy(inlined
, NULL
);