• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2024 Valve Corporation
3  * SPDX-License-Identifier: MIT
4  */
5 
6 #include "nir.h"
7 #include "nir_builder.h"
8 
9 /*
10  * Lower calls to functions prefixed "nir_*" to the NIR ALU instruction or
11  * intrinsic represented. This matches functions of the form:
12  *
13  *    nir_[op name](__optional mangling suffix)
14  *
15  * These functions return a value if the instruction has a destination. They
16  * take all instruction sources as parameters, followed by parameters for each
17  * ordered intrinsic index if any.
18  *
19  * Mangling allows for multiple definitions of the same instruction with
20  * different vector lengths and bit sizes. This could be combined with
21  * __attribute_((overloadable)) for seamless overloads.
22  *
23  * In effect, this pass re-implements nir_builder dynamically. This exposes
24  * low-level hardware intrinsics to internal driver programs. It is intended for
25  * use with internal OpenCL but should theoretically work for GLSL too.
26  */
27 
28 static void
lower_builtin_alu(nir_builder * b,nir_call_instr * call,nir_op op)29 lower_builtin_alu(nir_builder *b, nir_call_instr *call, nir_op op)
30 {
31    const nir_op_info info = nir_op_infos[op];
32    nir_def *srcs[NIR_ALU_MAX_INPUTS];
33 
34    for (unsigned s = 0; s < info.num_inputs; ++s) {
35       srcs[s] = call->params[1 + s].ssa;
36    }
37 
38    nir_def *res = nir_build_alu_src_arr(b, op, srcs);
39    nir_store_deref(b, nir_src_as_deref(call->params[0]), res,
40                    nir_component_mask(res->num_components));
41 }
42 
43 static void
lower_builtin_intr(nir_builder * b,nir_call_instr * call,nir_intrinsic_op op)44 lower_builtin_intr(nir_builder *b, nir_call_instr *call, nir_intrinsic_op op)
45 {
46    nir_intrinsic_instr *intr = nir_intrinsic_instr_create(b->shader, op);
47    const nir_intrinsic_info info = nir_intrinsic_infos[op];
48 
49    /* If there is a destination, the first parameter is the return deref */
50    unsigned src = info.has_dest ? 1 : 0;
51    assert(call->num_params == (src + info.num_srcs + info.num_indices));
52 
53    /* The next parameters are the intrinsic sources */
54    for (unsigned s = 0; s < info.num_srcs; ++s) {
55       intr->src[s] = nir_src_for_ssa(call->params[src++].ssa);
56    }
57 
58    /* The remaining parameters are the intrinsic indices */
59    for (unsigned s = 0; s < info.num_indices; ++s) {
60       uint64_t val = nir_src_as_uint(call->params[src++]);
61       intr->const_index[info.index_map[info.indices[s]] - 1] = val;
62    }
63 
64    /* Some intrinsics must infer num_components from a particular source. */
65    for (unsigned s = 0; s < info.num_srcs; ++s) {
66       if (info.src_components[s] == 0) {
67          intr->num_components = intr->src[s].ssa->num_components;
68          break;
69       }
70    }
71 
72    /* Insert the instruction before any store_deref */
73    nir_builder_instr_insert(b, &intr->instr);
74 
75    /* If there is a destination, plumb it through the return deref */
76    if (info.has_dest) {
77       nir_deref_instr *deref = nir_src_as_deref(call->params[0]);
78 
79       unsigned bit_size = glsl_get_bit_size(deref->type);
80       unsigned num_components = MAX2(glsl_get_length(deref->type), 1);
81 
82       nir_def_init(&intr->instr, &intr->def, num_components, bit_size);
83       nir_store_deref(b, deref, &intr->def, nir_component_mask(num_components));
84 
85       if (info.dest_components == 0 && intr->num_components == 0) {
86          intr->num_components = num_components;
87       }
88    }
89 }
90 
91 static bool
lower(nir_builder * b,nir_instr * instr,void * data)92 lower(nir_builder *b, nir_instr *instr, void *data)
93 {
94    /* All builtins are exposed as function calls */
95    if (instr->type != nir_instr_type_call)
96       return false;
97 
98    nir_call_instr *call = nir_instr_as_call(instr);
99    nir_function *func = call->callee;
100 
101    /* We reserve all functions prefixed nir_* as builtins needing lowering. */
102    if (strncmp("nir_", func->name, strlen("nir_")) != 0)
103       return false;
104 
105    /* Strip the nir_ prefix to give the name of an ALU opcode or intrinsic. Also
106     * strip the __* suffix if present: we don't need mangling information, we
107     * can recover vector lengths / bit sizes from the NIR.  This implements a
108     * crude form of function overloading.
109     */
110    const char *intr_name = func->name + strlen("nir_");
111    const char *suffix = strstr(intr_name, "__");
112    unsigned len = (suffix != NULL) ? (suffix - intr_name) : strlen(intr_name);
113 
114    /* From this point on, we must not fail. Remove the call. */
115    b->cursor = nir_instr_remove(&call->instr);
116 
117    /* Look for an ALU opcode */
118    for (unsigned i = 0; i < ARRAY_SIZE(nir_op_infos); ++i) {
119       if (strncmp(intr_name, nir_op_infos[i].name, len) == 0 &&
120           strlen(nir_op_infos[i].name) == len) {
121 
122          lower_builtin_alu(b, call, i);
123          return true;
124       }
125    }
126 
127    /* Look for an intrinsic */
128    for (unsigned i = 0; i < ARRAY_SIZE(nir_intrinsic_infos); ++i) {
129       if (strncmp(intr_name, nir_intrinsic_infos[i].name, len) == 0 &&
130           strlen(nir_intrinsic_infos[i].name) == len) {
131 
132          lower_builtin_intr(b, call, i);
133          return true;
134       }
135    }
136 
137    /* We must have matched something! */
138    fprintf(stderr, "unknown opcode %s\n", func->name);
139    unreachable("invalid nir opcode/intrinsic");
140 }
141 
142 bool
nir_lower_calls_to_builtins(nir_shader * s)143 nir_lower_calls_to_builtins(nir_shader *s)
144 {
145    return nir_shader_instructions_pass(s, lower, nir_metadata_none, NULL);
146 }
147