[Mesa-dev] [PATCH] nir: Document the function inlining process
Karol Herbst
kherbst at redhat.com
Wed Dec 12 04:48:28 UTC 2018
sorry, totally forgot about that. Seems fine as it is and I have the
same within my CL branch (except the last
nir_lower_constant_initializers).
as I don't really know if all that is actually true, but because it
makes sense to me:
Acked-by: Karol Herbst <kherbst at redhat.com>
I kind of wished we would have a helper function for this though like
"nir_convert_to_single_function(nir_shader *shader, nir_function
*entry_point)" which would do all of that already and we could just
call. Hopefully you can come up with a better name.
On Wed, Dec 12, 2018 at 4:34 AM Jason Ekstrand <jason at jlekstrand.net> wrote:
>
> ping
>
> On Mon, Oct 29, 2018 at 12:14 PM Jason Ekstrand <jason at jlekstrand.net> wrote:
>>
>> This has thrown a few people off recently and it's good to have the
>> process and all the rational for it documented somewhere. A comment at
>> the top of nir_inline_functions seems as good a place as any.
>>
>> Cc: Matt Turner <mattst88 at gmail.com>
>> Cc: Karol Herbst <kherbst at redhat.com>
>> ---
>> src/compiler/nir/nir_inline_functions.c | 68 +++++++++++++++++++++++++
>> 1 file changed, 68 insertions(+)
>>
>> diff --git a/src/compiler/nir/nir_inline_functions.c b/src/compiler/nir/nir_inline_functions.c
>> index 06c90d93956..29474bb417b 100644
>> --- a/src/compiler/nir/nir_inline_functions.c
>> +++ b/src/compiler/nir/nir_inline_functions.c
>> @@ -132,6 +132,74 @@ inline_function_impl(nir_function_impl *impl, struct set *inlined)
>> return progress;
>> }
>>
>> +/** A pass to inline all functions in a shader into their callers
>> + *
>> + * For most use-cases, function inlining is a multi-step process. The general
>> + * pattern employed by SPIR-V consumers and others is as follows:
>> + *
>> + * 1. nir_lower_constant_initializers(shader, nir_var_local)
>> + *
>> + * This is needed because local variables from the callee are simply added
>> + * to the locals list for the caller and the information about where the
>> + * constant initializer logically happens is lost. If the callee is
>> + * called in a loop, this can cause the variable to go from being
>> + * initialized once per loop iteration to being initialized once at the
>> + * top of the caller and values to persist from one invocation of the
>> + * callee to the next. The simple solution to this problem is to get rid
>> + * of constant initializers before function inlining.
>> + *
>> + * 2. nir_lower_returns(shader)
>> + *
>> + * nir_inline_functions assumes that all functions end "naturally" by
>> + * execution reaching the end of the function without any return
>> + * instructions causing instant jumps to the end. Thanks to NIR being
>> + * structured, we can't represent arbitrary jumps to various points in the
>> + * program which is what an early return in the callee would have to turn
>> + * into when we inline it into the caller. Instead, we require returns to
>> + * be lowered which lets us just copy+paste the callee directly into the
>> + * caller.
>> + *
>> + * 3. nir_inline_functions(shader)
>> + *
>> + * This does the actual function inlining and the resulting shader will
>> + * contain no call instructions.
>> + *
>> + * 4. nir_copy_prop(shader)
>> + *
>> + * Most functions contain pointer parameters where the result of a deref
>> + * instruction is passed in as a parameter, loaded via a load_param
>> + * intrinsic, and then turned back into a deref via a cast. Running copy
>> + * propagation gets rid of the intermediate steps and results in a whole
>> + * deref chain again. This is currently required by a number of
>> + * optimizations and lowering passes at least for certain variable modes.
>> + *
>> + * 5. Loop over the functions and delete all but the main entrypoint.
>> + *
>> + * In the Intel Vulkan driver this looks like this:
>> + *
>> + * foreach_list_typed_safe(nir_function, func, node, &nir->functions) {
>> + * if (func != entry_point)
>> + * exec_node_remove(&func->node);
>> + * }
>> + * assert(exec_list_length(&nir->functions) == 1);
>> + *
>> + * While nir_inline_functions does get rid of all call instructions, it
>> + * doesn't get rid of any functions because it doesn't know what the "root
>> + * function" is. Instead, it's up to the individual driver to know how to
>> + * decide on a root function and delete the rest. With SPIR-V,
>> + * spirv_to_nir returns the root function and so we can just use == whereas
>> + * with GL, you may have to look for a function named "main".
>> + *
>> + * 6. nir_lower_constant_initializers(shader, ~nir_var_local)
>> + *
>> + * Lowering constant initializers on inputs, outputs, global variables,
>> + * etc. requires that we know the main entrypoint so that we know where to
>> + * initialize them. Otherwise, we would have to assume that anything
>> + * could be a main entrypoint and initialize them at the start of every
>> + * function but that would clearly be wrong if any of those functions were
>> + * ever called within another function. Simply requiring a single-
>> + * entrypoint function shader is the best way to make it well-defined.
>> + */
>> bool
>> nir_inline_functions(nir_shader *shader)
>> {
>> --
>> 2.19.1
>>
More information about the mesa-dev
mailing list