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