1/*
2 * Copyright © Microsoft Corporation
3 *
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:
10 *
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
13 * Software.
14 *
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
21 * IN THE SOFTWARE.
22 */
23
24#include "dxil_function.h"
25#include "dxil_module.h"
26
27#define MAX_FUNC_PARAMS 17
28
29struct predefined_func_descr {
30   const char *base_name;
31   const char *retval_descr;
32   const char *param_descr;
33   enum dxil_attr_kind attr;
34};
35
36static struct  predefined_func_descr predefined_funcs[] = {
37{"dx.op.atomicBinOp", "O", "i@iiiii", DXIL_ATTR_KIND_READ_NONE},
38{"dx.op.cbufferLoad", "O", "i@ii", DXIL_ATTR_KIND_READ_ONLY},
39{"dx.op.cbufferLoadLegacy", "B", "i@i", DXIL_ATTR_KIND_READ_ONLY},
40{"dx.op.createHandle", "@", "iciib", DXIL_ATTR_KIND_READ_ONLY},
41{"dx.op.storeOutput", "v", "iiicO", DXIL_ATTR_KIND_NO_UNWIND},
42{"dx.op.loadInput", "O", "iiici", DXIL_ATTR_KIND_READ_NONE},
43{"dx.op.tertiary", "O", "iOOO", DXIL_ATTR_KIND_READ_NONE},
44{"dx.op.quaternary", "O", "iOOOO", DXIL_ATTR_KIND_READ_NONE},
45{"dx.op.threadId", "i", "ii", DXIL_ATTR_KIND_READ_NONE},
46{"dx.op.threadIdInGroup", "i", "ii", DXIL_ATTR_KIND_READ_NONE},
47{"dx.op.flattenedThreadIdInGroup", "i", "i", DXIL_ATTR_KIND_READ_NONE},
48{"dx.op.groupId", "i", "ii", DXIL_ATTR_KIND_READ_NONE},
49{"dx.op.unary", "O", "iO", DXIL_ATTR_KIND_READ_NONE},
50{"dx.op.unaryBits", "i", "iO", DXIL_ATTR_KIND_READ_NONE},
51{"dx.op.isSpecialFloat", "b", "iO", DXIL_ATTR_KIND_READ_NONE},
52{"dx.op.binary", "O", "iOO", DXIL_ATTR_KIND_READ_NONE},
53{"dx.op.bufferStore", "v", "i@iiOOOOc", DXIL_ATTR_KIND_NONE},
54{"dx.op.bufferLoad", "R", "i@ii", DXIL_ATTR_KIND_READ_ONLY},
55{"dx.op.attributeAtVertex", "O", "iiicc", DXIL_ATTR_KIND_READ_NONE},
56{"dx.op.sample", "R", "i@@ffffiiif", DXIL_ATTR_KIND_READ_ONLY},
57{"dx.op.sampleBias", "R", "i@@ffffiiiff", DXIL_ATTR_KIND_READ_ONLY},
58{"dx.op.sampleLevel", "R", "i@@ffffiiif", DXIL_ATTR_KIND_READ_ONLY},
59{"dx.op.sampleGrad", "R", "i@@ffffiiifffffff", DXIL_ATTR_KIND_READ_ONLY},
60{"dx.op.sampleCmp", "R", "i@@ffffiiiff", DXIL_ATTR_KIND_READ_ONLY},
61{"dx.op.sampleCmpLevelZero", "R", "i@@ffffiiif", DXIL_ATTR_KIND_READ_ONLY},
62{"dx.op.textureLoad", "R", "i@iiiiiii", DXIL_ATTR_KIND_READ_ONLY},
63{"dx.op.textureGather", "R", "i@@ffffiii", DXIL_ATTR_KIND_READ_ONLY},
64{"dx.op.textureGatherCmp", "R", "i@@ffffiiif", DXIL_ATTR_KIND_READ_ONLY},
65{"dx.op.discard", "v", "ib", DXIL_ATTR_KIND_NO_UNWIND},
66{"dx.op.sampleIndex", "i", "i", DXIL_ATTR_KIND_READ_NONE},
67{"dx.op.emitStream", "v", "ic", DXIL_ATTR_KIND_NONE},
68{"dx.op.cutStream", "v", "ic", DXIL_ATTR_KIND_NONE},
69{"dx.op.getDimensions", "D", "i@i", DXIL_ATTR_KIND_READ_ONLY},
70{"dx.op.calculateLOD", "f", "i@@fffb", DXIL_ATTR_KIND_READ_ONLY},
71{"dx.op.barrier", "v", "ii", DXIL_ATTR_KIND_NO_DUPLICATE},
72{"dx.op.atomicCompareExchange", "O", "i@iiiii", DXIL_ATTR_KIND_READ_NONE},
73{"dx.op.textureStore", "v", "i@iiiOOOOc", DXIL_ATTR_KIND_NONE},
74{"dx.op.primitiveID", "i", "i", DXIL_ATTR_KIND_READ_NONE},
75{"dx.op.outputControlPointID", "i", "i", DXIL_ATTR_KIND_READ_NONE},
76{"dx.op.gsInstanceID", "i", "i", DXIL_ATTR_KIND_READ_NONE},
77{"dx.op.domainLocation", "f", "ii", DXIL_ATTR_KIND_READ_NONE},
78{"dx.op.legacyF16ToF32", "f", "ii", DXIL_ATTR_KIND_READ_ONLY},
79{"dx.op.legacyF32ToF16", "i", "if", DXIL_ATTR_KIND_READ_ONLY},
80{"dx.op.makeDouble", "g", "iii", DXIL_ATTR_KIND_READ_NONE},
81{"dx.op.splitDouble", "G", "ig", DXIL_ATTR_KIND_READ_NONE},
82{"dx.op.texture2DMSGetSamplePosition", "S", "i@i", DXIL_ATTR_KIND_READ_ONLY},
83{"dx.op.renderTargetGetSamplePosition", "S", "ii", DXIL_ATTR_KIND_READ_ONLY},
84{"dx.op.evalSnapped", "O", "iiicii", DXIL_ATTR_KIND_READ_NONE},
85{"dx.op.evalCentroid", "O", "iiic", DXIL_ATTR_KIND_READ_NONE},
86{"dx.op.evalSampleIndex", "O", "iiici", DXIL_ATTR_KIND_READ_NONE},
87{"dx.op.coverage", "i", "i", DXIL_ATTR_KIND_READ_NONE},
88{"dx.op.storePatchConstant", "v", "iiicO", DXIL_ATTR_KIND_NO_UNWIND},
89{"dx.op.loadPatchConstant", "O", "iiic", DXIL_ATTR_KIND_READ_NONE},
90{"dx.op.loadOutputControlPoint", "O", "iiici", DXIL_ATTR_KIND_READ_NONE},
91};
92
93struct func_descr {
94   const char *name;
95   enum overload_type overload;
96};
97
98struct func_rb_node {
99   struct rb_node node;
100   const struct dxil_func *func;
101   struct func_descr descr;
102};
103
104static inline
105const struct func_rb_node *
106func_rb_node(const struct rb_node *n)
107{
108   return (const struct func_rb_node *)n;
109}
110
111static int
112func_compare_to_name_and_overload(const struct rb_node *node, const void *data)
113{
114   const struct func_descr *descr = (const struct func_descr *)data;
115   const struct func_rb_node *f = func_rb_node(node);
116   if (f->descr.overload < descr->overload)
117      return -1;
118   if (f->descr.overload > descr->overload)
119      return 1;
120
121   return strcmp(f->descr.name, descr->name);
122}
123
124static const struct dxil_func *
125allocate_function_from_predefined(struct dxil_module *mod,
126                                       const char *name,
127                                       enum overload_type overload)
128{
129   for (unsigned i = 0; i < ARRAY_SIZE(predefined_funcs); ++i) {
130      if (!strcmp(predefined_funcs[i].base_name, name)) {
131         return dxil_alloc_func(mod, name, overload,
132                                predefined_funcs[i].retval_descr,
133                                predefined_funcs[i].param_descr,
134                                predefined_funcs[i].attr);
135      }
136   }
137   unreachable("Invalid function name");
138}
139
140const struct dxil_func *
141dxil_get_function(struct dxil_module *mod,
142                  const char *name, enum overload_type overload)
143{
144   struct func_descr descr = { name, overload };
145   const struct rb_node *node = rb_tree_search(mod->functions, &descr,
146                                               func_compare_to_name_and_overload);
147   if (node)
148      return func_rb_node(node)->func;
149
150   return allocate_function_from_predefined(mod, name, overload);
151}
152
153static int func_compare_name(const struct rb_node *lhs, const struct rb_node *rhs)
154{
155   const struct func_rb_node *node = func_rb_node(rhs);
156   return func_compare_to_name_and_overload(lhs, &node->descr);
157}
158
159static void
160dxil_add_function(struct rb_tree *functions, const struct dxil_func *func,
161                  const char *name, enum overload_type overload)
162{
163   struct func_rb_node *f = rzalloc(functions, struct func_rb_node);
164   f->func = func;
165   f->descr.name = name;
166   f->descr.overload = overload;
167   rb_tree_insert(functions, &f->node, func_compare_name);
168}
169
170static const struct dxil_type *
171get_type_from_string(struct dxil_module *mod, const char *param_descr,
172                     enum overload_type overload,  int *idx)
173{
174   assert(param_descr);
175   char type_id = param_descr[(*idx)++];
176   assert(*idx <= (int)strlen(param_descr));
177
178   switch (type_id) {
179   case DXIL_FUNC_PARAM_INT64: return dxil_module_get_int_type(mod, 64);
180   case DXIL_FUNC_PARAM_INT32: return dxil_module_get_int_type(mod, 32);
181   case DXIL_FUNC_PARAM_INT16: return dxil_module_get_int_type(mod, 16);
182   case DXIL_FUNC_PARAM_INT8: return dxil_module_get_int_type(mod, 8);
183   case DXIL_FUNC_PARAM_BOOL: return dxil_module_get_int_type(mod, 1);
184   case DXIL_FUNC_PARAM_FLOAT64: return dxil_module_get_float_type(mod, 64);
185   case DXIL_FUNC_PARAM_FLOAT32: return dxil_module_get_float_type(mod, 32);
186   case DXIL_FUNC_PARAM_FLOAT16: return dxil_module_get_float_type(mod, 16);
187   case DXIL_FUNC_PARAM_HANDLE: return dxil_module_get_handle_type(mod);
188   case DXIL_FUNC_PARAM_VOID: return dxil_module_get_void_type(mod);
189   case DXIL_FUNC_PARAM_FROM_OVERLOAD:  return dxil_get_overload_type(mod, overload);
190   case DXIL_FUNC_PARAM_RESRET: return dxil_module_get_resret_type(mod, overload);
191   case DXIL_FUNC_PARAM_DIM: return dxil_module_get_dimret_type(mod);
192   case DXIL_FUNC_PARAM_SAMPLE_POS: return dxil_module_get_samplepos_type(mod);
193   case DXIL_FUNC_PARAM_CBUF_RET: return dxil_module_get_cbuf_ret_type(mod, overload);
194   case DXIL_FUNC_PARAM_SPLIT_DOUBLE: return dxil_module_get_split_double_ret_type(mod);
195   case DXIL_FUNC_PARAM_POINTER: {
196         const struct dxil_type *target = get_type_from_string(mod, param_descr, overload, idx);
197         return dxil_module_get_pointer_type(mod, target);
198      }
199   default:
200      assert(0 && "unknown type identifier");
201   }
202   return NULL;
203}
204
205const struct dxil_func *
206dxil_alloc_func_with_rettype(struct dxil_module *mod, const char *name,
207                             enum overload_type overload,
208                             const struct dxil_type *retval_type,
209                             const char *param_descr,
210                             enum dxil_attr_kind attr)
211{
212   assert(param_descr);
213   const struct dxil_type *arg_types[MAX_FUNC_PARAMS];
214
215   int index = 0;
216   unsigned num_params = 0;
217
218   while (param_descr[num_params]) {
219      const struct dxil_type *t = get_type_from_string(mod, param_descr, overload, &index);
220      if (!t)
221         return false;
222      assert(num_params < MAX_FUNC_PARAMS);
223      arg_types[num_params++] = t;
224   }
225
226   const struct dxil_type *func_type =
227      dxil_module_add_function_type(mod, retval_type,
228                                    arg_types, num_params);
229   if (!func_type) {
230      fprintf(stderr, "%s: Func type allocation failed\n", __func__);
231      return false;
232   }
233
234   char full_name[100];
235   snprintf(full_name, sizeof (full_name), "%s%s%s", name,
236            overload == DXIL_NONE ? "" : ".", dxil_overload_suffix(overload));
237   const struct dxil_func *func = dxil_add_function_decl(mod, full_name, func_type, attr);
238
239   if (func)
240      dxil_add_function(mod->functions, func, name, overload);
241
242   return func;
243}
244
245const struct dxil_func *
246dxil_alloc_func(struct dxil_module *mod, const char *name, enum overload_type overload,
247                const char *retval_type_descr,
248                const char *param_descr, enum dxil_attr_kind attr)
249{
250
251   int index = 0;
252   const struct dxil_type *retval_type = get_type_from_string(mod, retval_type_descr, overload, &index);
253   assert(retval_type_descr[index] == 0);
254
255   return dxil_alloc_func_with_rettype(mod, name, overload, retval_type,
256                                       param_descr, attr);
257}
258