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