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