nir: resolve functions: only resolve functions that are reachable from main

Use DFS traversal from main to resolve reachable functions. Avoid spurious
"unresolved reference" linker errors for dead helper functions.
It avoid reporting linking error for following shader test. The shader test used
to pass before merge_requests/31137:

[require]
GLSL >= 1.50

[vertex shader]
/* declared but not defined */
vec4 transform_color(vec3 color, float alpha);

/* calls transform_color — but this function is never called from main */
vec4 apply_transform(vec3 color, float alpha)
{
    return transform_color(color, alpha);
}

[vertex shader]
in vec4 piglit_vertex;

void main()
{
    /* apply_transform is never called here */
    gl_Position = piglit_vertex;
}

Signed-off-by: Xinju Li <xinju.li@broadcom.com>

use pass_flags to mark function as reachable

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/41065>
This commit is contained in:
Xinju Li 2026-04-20 01:01:34 -07:00 committed by Marge Bot
parent a78634ccb0
commit 0319be8b02

View file

@ -23,6 +23,7 @@
#include "gl_nir_linker.h"
#include "linker_util.h"
#include "util/u_dynarray.h"
#include "program/symbol_table.h"
#include "util/hash_table.h"
#include "main/shader_types.h"
@ -318,6 +319,86 @@ find_matching_signature(struct list_head *f_list,
return match;
}
/**
* Resolve the function calls reachable from the shader entrypoint.
* Remove unreachable functions. This is done using DFS traversal from main.
* It avoids spurious "unresolved reference" linker errors for dead helper
* functions. Only calls that are actually reachable from main must be resolved.
*/
static bool
resolve_calls_and_remove_unreachable(struct gl_shader_program *prog,
struct gl_shader *main,
struct gl_linked_shader *linked_sh,
struct hash_table *func_lookup)
{
nir_shader *shader = linked_sh->Program->nir;
nir_function_impl *entry = nir_shader_get_entrypoint(shader);
if (!entry)
return true;
/* use nir_function::pass_flags to check function reachability */
nir_foreach_function(func, shader)
func->pass_flags = 0;
struct util_dynarray stack;
util_dynarray_init(&stack, NULL);
util_dynarray_append(&stack, entry);
bool success = true;
while (util_dynarray_num_elements(&stack, nir_function_impl *) > 0) {
nir_function_impl *impl =
util_dynarray_pop(&stack, nir_function_impl *);
if (impl->function->pass_flags)
continue;
impl->function->pass_flags = 1;
nir_foreach_block(block, impl) {
nir_foreach_instr(instr, block) {
if (instr->type != nir_instr_type_call)
continue;
nir_call_instr *call = nir_instr_as_call(instr);
/* Already resolved */
if (call->callee->impl) {
util_dynarray_append(&stack, call->callee->impl);
continue;
}
struct hash_entry *e = _mesa_hash_table_search(func_lookup, call->callee->name);
if (e) {
struct list_head *f_list = (struct list_head *) e->data;
nir_function *f = find_matching_signature(f_list, call->callee->params,
call->callee->num_params,
main->has_implicit_conversions,
main->has_implicit_int_to_uint_conversion);
if (f)
call->callee = f;
}
if(!call->callee->impl) {
linker_error(prog, "unresolved reference to function `%s'\n",
call->callee->name);
success = false;
goto done;
}
util_dynarray_append(&stack, call->callee->impl);
}
}
}
done:
util_dynarray_fini(&stack);
nir_foreach_function_safe(func, shader) {
if (func->impl && func->pass_flags == 0
&& strstr(func->name, "gl_mesa_tmp") == NULL)
exec_node_remove(&func->node);
}
return success;
}
static nir_function *
clone_function(struct hash_table *remap_table,
const nir_function *fxn, nir_shader *ns)
@ -492,44 +573,10 @@ gl_nir_link_function_calls(struct gl_shader_program *prog,
}
}
/* Now that all shaders have been combined together make sure all function
* calls can be resolved.
*/
nir_foreach_function_impl(impl, linked_sh->Program->nir) {
nir_foreach_block(block, impl) {
nir_foreach_instr(instr, block) {
if (instr->type == nir_instr_type_call) {
nir_call_instr *call = nir_instr_as_call(instr);
/* If this was already set at compile time don't try to set it
* again.
*/
if (call->callee->impl)
continue;
struct hash_entry *e = _mesa_hash_table_search(func_lookup,
call->callee->name);
if (e) {
struct list_head *f_list = (struct list_head *) e->data;
nir_function *f =
find_matching_signature(f_list, call->callee->params,
call->callee->num_params,
main->has_implicit_conversions,
main->has_implicit_int_to_uint_conversion);
if (f)
call->callee = f;
}
if (!call->callee->impl) {
linker_error(prog, "unresolved reference to function `%s'\n",
call->callee->name);
ralloc_free(mem_ctx);
return false;
}
}
}
}
/* Resolve function calls and remove unreachable functions */
if (!resolve_calls_and_remove_unreachable(prog, main, linked_sh, func_lookup)) {
ralloc_free(mem_ctx);
return false;
}
/**