1 /*
2 * Copyright (C) 2010-2011 Marcin Kościelnicki <koriakin@0x04.net>
3 * Copyright (C) 2010 Francisco Jerez <currojerez@riseup.net>
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the "Software"),
8 * to deal in the Software without restriction, including without limitation
9 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 * and/or sell copies of the Software, and to permit persons to whom the
11 * Software is furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice (including the next
14 * paragraph) shall be included in all copies or substantial portions of the
15 * Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 * OTHER DEALINGS IN THE SOFTWARE.
24 */
25
26 #include "rnndec.h"
27 #include <assert.h>
28 #include <stdio.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <inttypes.h>
32 #include "util.h"
33
rnndec_newcontext(struct rnndb * db)34 struct rnndeccontext *rnndec_newcontext(struct rnndb *db) {
35 struct rnndeccontext *res = calloc (sizeof *res, 1);
36 res->db = db;
37 res->colors = &envy_null_colors;
38 return res;
39 }
40
rnndec_varadd(struct rnndeccontext * ctx,char * varset,const char * variant)41 int rnndec_varadd(struct rnndeccontext *ctx, char *varset, const char *variant) {
42 struct rnnenum *en = rnn_findenum(ctx->db, varset);
43 if (!en) {
44 fprintf (stderr, "Enum %s doesn't exist in database!\n", varset);
45 return 0;
46 }
47 int i, j;
48 for (i = 0; i < en->valsnum; i++)
49 if (!strcasecmp(en->vals[i]->name, variant)) {
50 struct rnndecvariant *ci = calloc (sizeof *ci, 1);
51 ci->en = en;
52 ci->variant = i;
53 ADDARRAY(ctx->vars, ci);
54 return 1;
55 }
56
57 if (i == en->valsnum) {
58 fprintf (stderr, "Variant %s doesn't exist in enum %s!\n", variant, varset);
59 return 0;
60 }
61
62 for (j = 0; j < ctx->varsnum; j++) {
63 if (ctx->vars[j]->en == en) {
64 ctx->vars[j]->variant = i;
65 break;
66 }
67 }
68
69 if (i == ctx->varsnum) {
70 struct rnndecvariant *ci = calloc (sizeof *ci, 1);
71 ci->en = en;
72 ci->variant = i;
73 ADDARRAY(ctx->vars, ci);
74 }
75
76 return 1;
77 }
78
rnndec_varmatch(struct rnndeccontext * ctx,struct rnnvarinfo * vi)79 int rnndec_varmatch(struct rnndeccontext *ctx, struct rnnvarinfo *vi) {
80 if (vi->dead)
81 return 0;
82 int i;
83 for (i = 0; i < vi->varsetsnum; i++) {
84 int j;
85 for (j = 0; j < ctx->varsnum; j++)
86 if (vi->varsets[i]->venum == ctx->vars[j]->en)
87 break;
88 if (j == ctx->varsnum) {
89 fprintf (stderr, "I don't know which %s variant to use!\n", vi->varsets[i]->venum->name);
90 } else {
91 if (!vi->varsets[i]->variants[ctx->vars[j]->variant])
92 return 0;
93 }
94 }
95 return 1;
96 }
97
98 /* see https://en.wikipedia.org/wiki/Half-precision_floating-point_format */
float16i(uint16_t val)99 static uint32_t float16i(uint16_t val)
100 {
101 uint32_t sign = ((uint32_t)(val & 0x8000)) << 16;
102 uint32_t frac = val & 0x3ff;
103 int32_t expn = (val >> 10) & 0x1f;
104
105 if (expn == 0) {
106 if (frac) {
107 /* denormalized number: */
108 int shift = __builtin_clz(frac) - 21;
109 frac <<= shift;
110 expn = -shift;
111 } else {
112 /* +/- zero: */
113 return sign;
114 }
115 } else if (expn == 0x1f) {
116 /* Inf/NaN: */
117 return sign | 0x7f800000 | (frac << 13);
118 }
119
120 return sign | ((expn + 127 - 15) << 23) | (frac << 13);
121 }
float16(uint16_t val)122 static float float16(uint16_t val)
123 {
124 union { uint32_t i; float f; } u;
125 u.i = float16i(val);
126 return u.f;
127 }
128
rnndec_decode_enum_val(struct rnndeccontext * ctx,struct rnnvalue ** vals,int valsnum,uint64_t value)129 static const char *rnndec_decode_enum_val(struct rnndeccontext *ctx,
130 struct rnnvalue **vals, int valsnum, uint64_t value)
131 {
132 int i;
133 for (i = 0; i < valsnum; i++)
134 if (rnndec_varmatch(ctx, &vals[i]->varinfo) &&
135 vals[i]->valvalid && vals[i]->value == value)
136 return vals[i]->name;
137 return NULL;
138 }
139
rnndec_decode_enum(struct rnndeccontext * ctx,const char * enumname,uint64_t enumval)140 const char *rnndec_decode_enum(struct rnndeccontext *ctx, const char *enumname, uint64_t enumval)
141 {
142 struct rnnenum *en = rnn_findenum (ctx->db, enumname);
143 if (en) {
144 return rnndec_decode_enum_val(ctx, en->vals, en->valsnum, enumval);
145 }
146 return NULL;
147 }
148
149 /* The name UNK%u is used as a placeholder for bitfields that exist but
150 * have an unknown function.
151 */
is_unknown(const char * name)152 static int is_unknown(const char *name)
153 {
154 unsigned u;
155 return sscanf(name, "UNK%u", &u) == 1;
156 }
157
rnndec_decodeval(struct rnndeccontext * ctx,struct rnntypeinfo * ti,uint64_t value)158 char *rnndec_decodeval(struct rnndeccontext *ctx, struct rnntypeinfo *ti, uint64_t value) {
159 int width = ti->high - ti->low + 1;
160 char *res = 0;
161 int i;
162 struct rnnvalue **vals;
163 int valsnum;
164 struct rnnbitfield **bitfields;
165 int bitfieldsnum;
166 char *tmp;
167 const char *ctmp;
168 uint64_t mask, value_orig;
169 if (!ti)
170 goto failhex;
171 value_orig = value;
172 value = (value & typeinfo_mask(ti)) >> ti->low;
173 value <<= ti->shr;
174
175 switch (ti->type) {
176 case RNN_TTYPE_ENUM:
177 vals = ti->eenum->vals;
178 valsnum = ti->eenum->valsnum;
179 goto doenum;
180 case RNN_TTYPE_INLINE_ENUM:
181 vals = ti->vals;
182 valsnum = ti->valsnum;
183 goto doenum;
184 doenum:
185 ctmp = rnndec_decode_enum_val(ctx, vals, valsnum, value);
186 if (ctmp) {
187 asprintf (&res, "%s%s%s", ctx->colors->eval, ctmp, ctx->colors->reset);
188 if (ti->addvariant) {
189 rnndec_varadd(ctx, ti->eenum->name, ctmp);
190 }
191 break;
192 }
193 goto failhex;
194 case RNN_TTYPE_BITSET:
195 bitfields = ti->ebitset->bitfields;
196 bitfieldsnum = ti->ebitset->bitfieldsnum;
197 goto dobitset;
198 case RNN_TTYPE_INLINE_BITSET:
199 bitfields = ti->bitfields;
200 bitfieldsnum = ti->bitfieldsnum;
201 goto dobitset;
202 dobitset:
203 mask = 0;
204 for (i = 0; i < bitfieldsnum; i++) {
205 if (!rnndec_varmatch(ctx, &bitfields[i]->varinfo))
206 continue;
207 uint64_t type_mask = typeinfo_mask(&bitfields[i]->typeinfo);
208 if (((value & type_mask) == 0) && is_unknown(bitfields[i]->name))
209 continue;
210 mask |= type_mask;
211 if (bitfields[i]->typeinfo.type == RNN_TTYPE_BOOLEAN) {
212 const char *color = is_unknown(bitfields[i]->name) ?
213 ctx->colors->err : ctx->colors->mod;
214 if (value & type_mask) {
215 if (!res)
216 asprintf (&res, "%s%s%s", color, bitfields[i]->name, ctx->colors->reset);
217 else {
218 asprintf (&tmp, "%s | %s%s%s", res, color, bitfields[i]->name, ctx->colors->reset);
219 free(res);
220 res = tmp;
221 }
222 }
223 continue;
224 }
225 char *subval;
226 if (is_unknown(bitfields[i]->name) && (bitfields[i]->typeinfo.type != RNN_TTYPE_A3XX_REGID)) {
227 uint64_t field_val = value & type_mask;
228 field_val = (field_val & typeinfo_mask(&bitfields[i]->typeinfo)) >> bitfields[i]->typeinfo.low;
229 field_val <<= bitfields[i]->typeinfo.shr;
230 asprintf (&subval, "%s%#"PRIx64"%s", ctx->colors->err, field_val, ctx->colors->reset);
231 } else {
232 subval = rnndec_decodeval(ctx, &bitfields[i]->typeinfo, value & type_mask);
233 }
234 if (!res)
235 asprintf (&res, "%s%s%s = %s", ctx->colors->rname, bitfields[i]->name, ctx->colors->reset, subval);
236 else {
237 asprintf (&tmp, "%s | %s%s%s = %s", res, ctx->colors->rname, bitfields[i]->name, ctx->colors->reset, subval);
238 free(res);
239 res = tmp;
240 }
241 free(subval);
242 }
243 if (value & ~mask) {
244 if (!res)
245 asprintf (&res, "%s%#"PRIx64"%s", ctx->colors->err, value & ~mask, ctx->colors->reset);
246 else {
247 asprintf (&tmp, "%s | %s%#"PRIx64"%s", res, ctx->colors->err, value & ~mask, ctx->colors->reset);
248 free(res);
249 res = tmp;
250 }
251 }
252 if (!res)
253 asprintf (&res, "%s0%s", ctx->colors->num, ctx->colors->reset);
254 asprintf (&tmp, "{ %s }", res);
255 free(res);
256 return tmp;
257 case RNN_TTYPE_SPECTYPE:
258 return rnndec_decodeval(ctx, &ti->spectype->typeinfo, value);
259 case RNN_TTYPE_HEX:
260 asprintf (&res, "%s%#"PRIx64"%s", ctx->colors->num, value, ctx->colors->reset);
261 break;
262 case RNN_TTYPE_FIXED:
263 if (value & UINT64_C(1) << (width-1)) {
264 asprintf (&res, "%s-%lf%s", ctx->colors->num,
265 ((double)((UINT64_C(1) << width) - value)) / ((double)(1 << ti->radix)),
266 ctx->colors->reset);
267 break;
268 }
269 /* fallthrough */
270 case RNN_TTYPE_UFIXED:
271 asprintf (&res, "%s%lf%s", ctx->colors->num,
272 ((double)value) / ((double)(1LL << ti->radix)),
273 ctx->colors->reset);
274 break;
275 case RNN_TTYPE_A3XX_REGID:
276 asprintf (&res, "%sr%"PRIu64".%c%s", ctx->colors->num, (value >> 2), "xyzw"[value & 0x3], ctx->colors->reset);
277 break;
278 case RNN_TTYPE_UINT:
279 asprintf (&res, "%s%"PRIu64"%s", ctx->colors->num, value, ctx->colors->reset);
280 break;
281 case RNN_TTYPE_INT:
282 if (value & UINT64_C(1) << (width-1))
283 asprintf (&res, "%s-%"PRIi64"%s", ctx->colors->num, (UINT64_C(1) << width) - value, ctx->colors->reset);
284 else
285 asprintf (&res, "%s%"PRIi64"%s", ctx->colors->num, value, ctx->colors->reset);
286 break;
287 case RNN_TTYPE_BOOLEAN:
288 if (value == 0) {
289 asprintf (&res, "%sFALSE%s", ctx->colors->eval, ctx->colors->reset);
290 } else if (value == 1) {
291 asprintf (&res, "%sTRUE%s", ctx->colors->eval, ctx->colors->reset);
292 }
293 break;
294 case RNN_TTYPE_FLOAT: {
295 union { uint64_t i; float f; double d; } val;
296 val.i = value;
297 if (width == 64)
298 asprintf(&res, "%s%f%s", ctx->colors->num,
299 val.d, ctx->colors->reset);
300 else if (width == 32)
301 asprintf(&res, "%s%f%s", ctx->colors->num,
302 val.f, ctx->colors->reset);
303 else if (width == 16)
304 asprintf(&res, "%s%f%s", ctx->colors->num,
305 float16(value), ctx->colors->reset);
306 else
307 goto failhex;
308
309 break;
310 }
311 failhex:
312 default:
313 asprintf (&res, "%s%#"PRIx64"%s", ctx->colors->num, value, ctx->colors->reset);
314 break;
315 }
316 if (value_orig & ~typeinfo_mask(ti)) {
317 asprintf (&tmp, "%s | %s%#"PRIx64"%s", res, ctx->colors->err, value_orig & ~typeinfo_mask(ti), ctx->colors->reset);
318 free(res);
319 res = tmp;
320 }
321 return res;
322 }
323
appendidx(struct rnndeccontext * ctx,char * name,uint64_t idx,struct rnnenum * index)324 static char *appendidx (struct rnndeccontext *ctx, char *name, uint64_t idx, struct rnnenum *index) {
325 char *res;
326 const char *index_name = NULL;
327
328 if (index)
329 index_name = rnndec_decode_enum_val(ctx, index->vals, index->valsnum, idx);
330
331 if (index_name)
332 asprintf (&res, "%s[%s%s%s]", name, ctx->colors->eval, index_name, ctx->colors->reset);
333 else
334 asprintf (&res, "%s[%s%#"PRIx64"%s]", name, ctx->colors->num, idx, ctx->colors->reset);
335
336 free (name);
337 return res;
338 }
339
340 /* This could probably be made to work for stripes too.. */
get_array_idx_offset(struct rnndelem * elem,uint64_t addr,uint64_t * idx,uint64_t * offset)341 static int get_array_idx_offset(struct rnndelem *elem, uint64_t addr, uint64_t *idx, uint64_t *offset)
342 {
343 if (elem->offsets) {
344 int i;
345 for (i = 0; i < elem->offsetsnum; i++) {
346 uint64_t o = elem->offsets[i];
347 if ((o <= addr) && (addr < (o + elem->stride))) {
348 *idx = i;
349 *offset = addr - o;
350 return 0;
351 }
352 }
353 return -1;
354 } else {
355 if (addr < elem->offset)
356 return -1;
357
358 *idx = (addr - elem->offset) / elem->stride;
359 *offset = (addr - elem->offset) % elem->stride;
360
361 if (elem->length && (*idx >= elem->length))
362 return -1;
363
364 return 0;
365 }
366 }
367
trymatch(struct rnndeccontext * ctx,struct rnndelem ** elems,int elemsnum,uint64_t addr,int write,int dwidth,uint64_t * indices,int indicesnum)368 static struct rnndecaddrinfo *trymatch (struct rnndeccontext *ctx, struct rnndelem **elems, int elemsnum, uint64_t addr, int write, int dwidth, uint64_t *indices, int indicesnum) {
369 struct rnndecaddrinfo *res;
370 int i, j;
371 for (i = 0; i < elemsnum; i++) {
372 if (!rnndec_varmatch(ctx, &elems[i]->varinfo))
373 continue;
374 uint64_t offset, idx;
375 char *tmp, *name;
376 switch (elems[i]->type) {
377 case RNN_ETYPE_REG:
378 if (addr < elems[i]->offset)
379 break;
380 if (elems[i]->stride) {
381 idx = (addr-elems[i]->offset)/elems[i]->stride;
382 offset = (addr-elems[i]->offset)%elems[i]->stride;
383 } else {
384 idx = 0;
385 offset = addr-elems[i]->offset;
386 }
387 if (offset >= elems[i]->width/dwidth)
388 break;
389 if (elems[i]->length && idx >= elems[i]->length)
390 break;
391 res = calloc (sizeof *res, 1);
392 res->typeinfo = &elems[i]->typeinfo;
393 res->width = elems[i]->width;
394 asprintf (&res->name, "%s%s%s", ctx->colors->rname, elems[i]->name, ctx->colors->reset);
395 for (j = 0; j < indicesnum; j++)
396 res->name = appendidx(ctx, res->name, indices[j], NULL);
397 if (elems[i]->length != 1)
398 res->name = appendidx(ctx, res->name, idx, elems[i]->index);
399 if (offset) {
400 asprintf (&tmp, "%s+%s%#"PRIx64"%s", res->name, ctx->colors->err, offset, ctx->colors->reset);
401 free(res->name);
402 res->name = tmp;
403 }
404 return res;
405 case RNN_ETYPE_STRIPE:
406 for (idx = 0; idx < elems[i]->length || !elems[i]->length; idx++) {
407 if (addr < elems[i]->offset + elems[i]->stride * idx)
408 break;
409 offset = addr - (elems[i]->offset + elems[i]->stride * idx);
410 int extraidx = (elems[i]->length != 1);
411 int nindnum = (elems[i]->name ? 0 : indicesnum + extraidx);
412 uint64_t nind[nindnum];
413 if (!elems[i]->name) {
414 for (j = 0; j < indicesnum; j++)
415 nind[j] = indices[j];
416 if (extraidx)
417 nind[indicesnum] = idx;
418 }
419 res = trymatch (ctx, elems[i]->subelems, elems[i]->subelemsnum, offset, write, dwidth, nind, nindnum);
420 if (!res)
421 continue;
422 if (!elems[i]->name)
423 return res;
424 asprintf (&name, "%s%s%s", ctx->colors->rname, elems[i]->name, ctx->colors->reset);
425 for (j = 0; j < indicesnum; j++)
426 name = appendidx(ctx, name, indices[j], NULL);
427 if (elems[i]->length != 1)
428 name = appendidx(ctx, name, idx, elems[i]->index);
429 asprintf (&tmp, "%s.%s", name, res->name);
430 free(name);
431 free(res->name);
432 res->name = tmp;
433 return res;
434 }
435 break;
436 case RNN_ETYPE_ARRAY:
437 if (get_array_idx_offset(elems[i], addr, &idx, &offset))
438 break;
439 asprintf (&name, "%s%s%s", ctx->colors->rname, elems[i]->name, ctx->colors->reset);
440 for (j = 0; j < indicesnum; j++)
441 name = appendidx(ctx, name, indices[j], NULL);
442 if (elems[i]->length != 1)
443 name = appendidx(ctx, name, idx, elems[i]->index);
444 if ((res = trymatch (ctx, elems[i]->subelems, elems[i]->subelemsnum, offset, write, dwidth, 0, 0))) {
445 asprintf (&tmp, "%s.%s", name, res->name);
446 free(name);
447 free(res->name);
448 res->name = tmp;
449 return res;
450 }
451 res = calloc (sizeof *res, 1);
452 asprintf (&tmp, "%s+%s%#"PRIx64"%s", name, ctx->colors->err, offset, ctx->colors->reset);
453 free(name);
454 res->name = tmp;
455 return res;
456 default:
457 break;
458 }
459 }
460 return 0;
461 }
462
rnndec_checkaddr(struct rnndeccontext * ctx,struct rnndomain * domain,uint64_t addr,int write)463 int rnndec_checkaddr(struct rnndeccontext *ctx, struct rnndomain *domain, uint64_t addr, int write) {
464 struct rnndecaddrinfo *res = trymatch(ctx, domain->subelems, domain->subelemsnum, addr, write, domain->width, 0, 0);
465 if (res) {
466 free(res->name);
467 free(res);
468 }
469 return res != NULL;
470 }
471
rnndec_decodeaddr(struct rnndeccontext * ctx,struct rnndomain * domain,uint64_t addr,int write)472 struct rnndecaddrinfo *rnndec_decodeaddr(struct rnndeccontext *ctx, struct rnndomain *domain, uint64_t addr, int write) {
473 struct rnndecaddrinfo *res = trymatch(ctx, domain->subelems, domain->subelemsnum, addr, write, domain->width, 0, 0);
474 if (res)
475 return res;
476 res = calloc (sizeof *res, 1);
477 asprintf (&res->name, "%s%#"PRIx64"%s", ctx->colors->err, addr, ctx->colors->reset);
478 return res;
479 }
480
tryreg(struct rnndeccontext * ctx,struct rnndelem ** elems,int elemsnum,int dwidth,const char * name,uint64_t * offset)481 static unsigned tryreg(struct rnndeccontext *ctx, struct rnndelem **elems, int elemsnum,
482 int dwidth, const char *name, uint64_t *offset)
483 {
484 int i;
485 unsigned ret;
486 const char *suffix = strchr(name, '[');
487 unsigned n = suffix ? (suffix - name) : strlen(name);
488 const char *dotsuffix = strchr(name, '.');
489 unsigned dotn = dotsuffix ? (dotsuffix - name) : strlen(name);
490
491 const char *child = NULL;
492 unsigned idx = 0;
493
494 if (suffix) {
495 const char *tmp = strchr(suffix, ']');
496 idx = strtol(suffix+1, NULL, 0);
497 child = tmp+2;
498 }
499
500 for (i = 0; i < elemsnum; i++) {
501 struct rnndelem *elem = elems[i];
502 if (!rnndec_varmatch(ctx, &elem->varinfo))
503 continue;
504 int match = elem->name && (strlen(elem->name) == n) && !strncmp(elem->name, name, n);
505 switch (elem->type) {
506 case RNN_ETYPE_REG:
507 if (match) {
508 assert(!suffix);
509 *offset = elem->offset;
510 return 1;
511 }
512 break;
513 case RNN_ETYPE_STRIPE:
514 if (elem->name) {
515 if (!dotsuffix)
516 break;
517 if (strlen(elem->name) != dotn || strncmp(elem->name, name, dotn))
518 break;
519 }
520 ret = tryreg(ctx, elem->subelems, elem->subelemsnum, dwidth,
521 elem->name ? dotsuffix : name, offset);
522 if (ret)
523 return 1;
524 break;
525 case RNN_ETYPE_ARRAY:
526 if (match) {
527 assert(suffix);
528 ret = tryreg(ctx, elem->subelems, elem->subelemsnum, dwidth, child, offset);
529 if (ret) {
530 *offset += elem->offset + (idx * elem->stride);
531 return 1;
532 }
533 }
534 break;
535 default:
536 break;
537 }
538 }
539 return 0;
540 }
541
rnndec_decodereg(struct rnndeccontext * ctx,struct rnndomain * domain,const char * name)542 uint64_t rnndec_decodereg(struct rnndeccontext *ctx, struct rnndomain *domain, const char *name)
543 {
544 uint64_t offset;
545 if (tryreg(ctx, domain->subelems, domain->subelemsnum, domain->width, name, &offset)) {
546 return offset;
547 } else {
548 return 0;
549 }
550 }
551