ir_dump.c (12780B)
1 /* Textual dump of the semantic CG IR (the recorded CG-API tape). 2 * 3 * This renders CgIrFunc — the IR as recorded from the CG API, before any 4 * lowering to the optimizer's CFG form. It is the only dumper for the Cg* IR; 5 * the optimizer's Func IR is dumped separately by opt_ir_dump (opt/ir_print.c). 6 * 7 * The format is line-oriented and stable enough for golden-file diffs but 8 * otherwise unspecified. Symbols are rendered by numeric id (sym#N) since the 9 * dumper has no object-builder handle, matching opt_ir_dump's convention. */ 10 11 #include "cg/ir.h" 12 #include "core/core.h" 13 #include "core/pool.h" 14 #include "core/slice.h" 15 #include "core/strbuf.h" 16 17 static void dump_write(Writer* w, const StrBuf* sb) { 18 kit_writer_write(w, strbuf_cstr(sb), strbuf_len(sb)); 19 } 20 21 static const char* cg_ir_op_name(CgIrOp op) { 22 switch (op) { 23 case CG_IR_NOP: 24 return "nop"; 25 case CG_IR_LABEL: 26 return "label"; 27 case CG_IR_LOAD_IMM: 28 return "load_imm"; 29 case CG_IR_LOAD_CONST: 30 return "load_const"; 31 case CG_IR_COPY: 32 return "copy"; 33 case CG_IR_LOAD: 34 return "load"; 35 case CG_IR_STORE: 36 return "store"; 37 case CG_IR_ADDR_OF: 38 return "addr_of"; 39 case CG_IR_TLS_ADDR_OF: 40 return "tls_addr_of"; 41 case CG_IR_AGG_COPY: 42 return "agg_copy"; 43 case CG_IR_AGG_SET: 44 return "agg_set"; 45 case CG_IR_BITFIELD_LOAD: 46 return "bitfield_load"; 47 case CG_IR_BITFIELD_STORE: 48 return "bitfield_store"; 49 case CG_IR_BINOP: 50 return "binop"; 51 case CG_IR_UNOP: 52 return "unop"; 53 case CG_IR_CMP: 54 return "cmp"; 55 case CG_IR_CONVERT: 56 return "convert"; 57 case CG_IR_CALL: 58 return "call"; 59 case CG_IR_RET: 60 return "ret"; 61 case CG_IR_UNREACHABLE: 62 return "unreachable"; 63 case CG_IR_BR: 64 return "br"; 65 case CG_IR_CMP_BRANCH: 66 return "cmp_branch"; 67 case CG_IR_SWITCH: 68 return "switch"; 69 case CG_IR_INDIRECT_BRANCH: 70 return "indirect_branch"; 71 case CG_IR_LOAD_LABEL_ADDR: 72 return "load_label_addr"; 73 case CG_IR_LOCAL_STATIC_DATA_BEGIN: 74 return "local_static_data_begin"; 75 case CG_IR_LOCAL_STATIC_DATA_WRITE: 76 return "local_static_data_write"; 77 case CG_IR_LOCAL_STATIC_DATA_LABEL_ADDR: 78 return "local_static_data_label_addr"; 79 case CG_IR_LOCAL_STATIC_DATA_END: 80 return "local_static_data_end"; 81 case CG_IR_SCOPE_BEGIN: 82 return "scope_begin"; 83 case CG_IR_SCOPE_END: 84 return "scope_end"; 85 case CG_IR_BREAK_TO: 86 return "break_to"; 87 case CG_IR_CONTINUE_TO: 88 return "continue_to"; 89 case CG_IR_ALLOCA: 90 return "alloca"; 91 case CG_IR_VA_START: 92 return "va_start"; 93 case CG_IR_VA_ARG: 94 return "va_arg"; 95 case CG_IR_VA_END: 96 return "va_end"; 97 case CG_IR_VA_COPY: 98 return "va_copy"; 99 case CG_IR_ATOMIC_LOAD: 100 return "atomic_load"; 101 case CG_IR_ATOMIC_STORE: 102 return "atomic_store"; 103 case CG_IR_ATOMIC_RMW: 104 return "atomic_rmw"; 105 case CG_IR_ATOMIC_CAS: 106 return "atomic_cas"; 107 case CG_IR_FENCE: 108 return "fence"; 109 case CG_IR_INTRINSIC: 110 return "intrinsic"; 111 case CG_IR_ASM_BLOCK: 112 return "asm_block"; 113 } 114 return "??"; 115 } 116 117 static const char* cg_ir_binop_name(BinOp op) { 118 switch (op) { 119 case BO_IADD: 120 return "iadd"; 121 case BO_ISUB: 122 return "isub"; 123 case BO_IMUL: 124 return "imul"; 125 case BO_SDIV: 126 return "sdiv"; 127 case BO_UDIV: 128 return "udiv"; 129 case BO_SREM: 130 return "srem"; 131 case BO_UREM: 132 return "urem"; 133 case BO_FADD: 134 return "fadd"; 135 case BO_FSUB: 136 return "fsub"; 137 case BO_FMUL: 138 return "fmul"; 139 case BO_FDIV: 140 return "fdiv"; 141 case BO_AND: 142 return "and"; 143 case BO_OR: 144 return "or"; 145 case BO_XOR: 146 return "xor"; 147 case BO_SHL: 148 return "shl"; 149 case BO_SHR_S: 150 return "shr_s"; 151 case BO_SHR_U: 152 return "shr_u"; 153 } 154 return "??"; 155 } 156 157 static const char* cg_ir_unop_name(UnOp op) { 158 switch (op) { 159 case UO_NEG: 160 return "neg"; 161 case UO_FNEG: 162 return "fneg"; 163 case UO_NOT: 164 return "not"; 165 case UO_BNOT: 166 return "bnot"; 167 } 168 return "??"; 169 } 170 171 static const char* cg_ir_cmp_name(CmpOp op) { 172 switch (op) { 173 case CMP_EQ: 174 return "eq"; 175 case CMP_NE: 176 return "ne"; 177 case CMP_LT_S: 178 return "lt_s"; 179 case CMP_LE_S: 180 return "le_s"; 181 case CMP_GT_S: 182 return "gt_s"; 183 case CMP_GE_S: 184 return "ge_s"; 185 case CMP_LT_U: 186 return "lt_u"; 187 case CMP_LE_U: 188 return "le_u"; 189 case CMP_GT_U: 190 return "gt_u"; 191 case CMP_GE_U: 192 return "ge_u"; 193 case CMP_OEQ_F: 194 return "oeq_f"; 195 case CMP_ONE_F: 196 return "one_f"; 197 case CMP_OLT_F: 198 return "olt_f"; 199 case CMP_OLE_F: 200 return "ole_f"; 201 case CMP_OGT_F: 202 return "ogt_f"; 203 case CMP_OGE_F: 204 return "oge_f"; 205 case CMP_UEQ_F: 206 return "ueq_f"; 207 case CMP_UNE_F: 208 return "une_f"; 209 case CMP_ULT_F: 210 return "ult_f"; 211 case CMP_ULE_F: 212 return "ule_f"; 213 case CMP_UGT_F: 214 return "ugt_f"; 215 case CMP_UGE_F: 216 return "uge_f"; 217 } 218 return "??"; 219 } 220 221 static const char* cg_ir_conv_name(ConvKind op) { 222 switch (op) { 223 case CV_SEXT: 224 return "sext"; 225 case CV_ZEXT: 226 return "zext"; 227 case CV_TRUNC: 228 return "trunc"; 229 case CV_ITOF_S: 230 return "itof_s"; 231 case CV_ITOF_U: 232 return "itof_u"; 233 case CV_FTOI_S: 234 return "ftoi_s"; 235 case CV_FTOI_U: 236 return "ftoi_u"; 237 case CV_FEXT: 238 return "fext"; 239 case CV_FTRUNC: 240 return "ftrunc"; 241 case CV_BITCAST: 242 return "bitcast"; 243 } 244 return "??"; 245 } 246 247 static void put_type(StrBuf* sb, KitCgTypeId type) { 248 strbuf_putc(sb, 't'); 249 strbuf_put_u64(sb, (u64)type); 250 } 251 252 static void put_operand(StrBuf* sb, const Operand* op) { 253 switch (op->kind) { 254 case OPK_IMM: 255 strbuf_put_slice(sb, SLICE_LIT("imm:")); 256 strbuf_put_i64(sb, op->v.imm); 257 break; 258 case OPK_LOCAL: 259 strbuf_putc(sb, 'L'); 260 strbuf_put_u64(sb, (u64)op->v.local); 261 break; 262 case OPK_GLOBAL: 263 strbuf_put_slice(sb, SLICE_LIT("sym#")); 264 strbuf_put_u64(sb, (u64)op->v.global.sym); 265 if (op->v.global.addend) { 266 strbuf_putc(sb, op->v.global.addend < 0 ? '-' : '+'); 267 strbuf_put_u64(sb, op->v.global.addend < 0 268 ? (u64)(-(op->v.global.addend + 1)) + 1u 269 : (u64)op->v.global.addend); 270 } 271 break; 272 case OPK_INDIRECT: 273 strbuf_putc(sb, '['); 274 strbuf_putc(sb, 'L'); 275 strbuf_put_u64(sb, (u64)op->v.ind.base); 276 if (op->v.ind.index != CG_LOCAL_NONE) { 277 strbuf_put_slice(sb, SLICE_LIT("+L")); 278 strbuf_put_u64(sb, (u64)op->v.ind.index); 279 strbuf_putc(sb, '*'); 280 strbuf_put_u64(sb, (u64)(1u << op->v.ind.log2_scale)); 281 } 282 if (op->v.ind.ofs) { 283 strbuf_putc(sb, op->v.ind.ofs < 0 ? '-' : '+'); 284 strbuf_put_u64(sb, op->v.ind.ofs < 0 285 ? (u64)(-((i64)op->v.ind.ofs + 1)) + 1u 286 : (u64)op->v.ind.ofs); 287 } 288 strbuf_putc(sb, ']'); 289 break; 290 default: 291 strbuf_put_slice(sb, SLICE_LIT("?op")); 292 break; 293 } 294 strbuf_putc(sb, ':'); 295 put_type(sb, op->type); 296 } 297 298 static void put_mem(StrBuf* sb, const MemAccess* m) { 299 strbuf_put_slice(sb, SLICE_LIT(" mem{sz=")); 300 strbuf_put_u64(sb, (u64)m->size); 301 strbuf_put_slice(sb, SLICE_LIT(" al=")); 302 strbuf_put_u64(sb, (u64)m->align); 303 if (m->flags) { 304 strbuf_put_slice(sb, SLICE_LIT(" fl=")); 305 strbuf_put_hex_u64(sb, (u64)m->flags); 306 } 307 strbuf_putc(sb, '}'); 308 } 309 310 static void put_inst_extra(StrBuf* sb, const CgIrFunc* f, const CgIrInst* in) { 311 switch (in->op) { 312 case CG_IR_LOAD_IMM: 313 strbuf_put_slice(sb, SLICE_LIT(" = ")); 314 strbuf_put_i64(sb, in->extra.imm); 315 break; 316 case CG_IR_BINOP: 317 strbuf_putc(sb, ' '); 318 strbuf_puts(sb, cg_ir_binop_name((BinOp)in->extra.imm)); 319 break; 320 case CG_IR_UNOP: 321 strbuf_putc(sb, ' '); 322 strbuf_puts(sb, cg_ir_unop_name((UnOp)in->extra.imm)); 323 break; 324 case CG_IR_CMP: 325 strbuf_putc(sb, ' '); 326 strbuf_puts(sb, cg_ir_cmp_name((CmpOp)in->extra.imm)); 327 break; 328 case CG_IR_CONVERT: 329 strbuf_putc(sb, ' '); 330 strbuf_puts(sb, cg_ir_conv_name((ConvKind)in->extra.imm)); 331 break; 332 case CG_IR_BR: 333 case CG_IR_LABEL: 334 strbuf_put_slice(sb, SLICE_LIT(" Lbl")); 335 strbuf_put_u64(sb, (u64)in->extra.imm); 336 break; 337 case CG_IR_SCOPE_END: 338 case CG_IR_BREAK_TO: 339 case CG_IR_CONTINUE_TO: 340 strbuf_put_slice(sb, SLICE_LIT(" scope")); 341 strbuf_put_u64(sb, (u64)in->extra.imm); 342 break; 343 case CG_IR_SCOPE_BEGIN: { 344 const CgIrScopeAux* aux = (const CgIrScopeAux*)in->extra.aux; 345 if (aux) { 346 strbuf_put_slice(sb, SLICE_LIT(" scope")); 347 strbuf_put_u64(sb, (u64)aux->scope); 348 strbuf_put_slice(sb, SLICE_LIT(" kind=")); 349 strbuf_put_u64(sb, (u64)aux->desc.kind); 350 } 351 break; 352 } 353 case CG_IR_CMP_BRANCH: { 354 const CgIrCmpBranchAux* aux = (const CgIrCmpBranchAux*)in->extra.aux; 355 if (aux) { 356 strbuf_putc(sb, ' '); 357 strbuf_puts(sb, cg_ir_cmp_name(aux->op)); 358 strbuf_put_slice(sb, SLICE_LIT(" -> Lbl")); 359 strbuf_put_u64(sb, (u64)aux->target); 360 } 361 break; 362 } 363 case CG_IR_CALL: { 364 const CgIrCallAux* aux = (const CgIrCallAux*)in->extra.aux; 365 if (aux) { 366 strbuf_put_slice(sb, SLICE_LIT(" callee=")); 367 put_operand(sb, &aux->desc.callee); 368 strbuf_put_slice(sb, SLICE_LIT(" args=[")); 369 for (u32 i = 0; i < aux->desc.nargs; ++i) { 370 if (i) strbuf_putc(sb, ' '); 371 strbuf_putc(sb, 'L'); 372 strbuf_put_u64(sb, (u64)aux->desc.args[i]); 373 } 374 strbuf_put_slice(sb, SLICE_LIT("] result=")); 375 if (aux->desc.result != CG_LOCAL_NONE) { 376 strbuf_putc(sb, 'L'); 377 strbuf_put_u64(sb, (u64)aux->desc.result); 378 } else { 379 strbuf_putc(sb, '-'); 380 } 381 if (aux->desc.flags & CG_CALL_TAIL) 382 strbuf_put_slice(sb, SLICE_LIT(" tail")); 383 } 384 break; 385 } 386 case CG_IR_RET: { 387 const CgIrRetAux* aux = (const CgIrRetAux*)in->extra.aux; 388 if (aux) { 389 strbuf_put_slice(sb, SLICE_LIT(" value=")); 390 if (aux->present) { 391 strbuf_putc(sb, 'L'); 392 strbuf_put_u64(sb, (u64)aux->value); 393 } else { 394 strbuf_putc(sb, '-'); 395 } 396 } 397 break; 398 } 399 case CG_IR_LOAD: 400 case CG_IR_STORE: 401 put_mem(sb, &in->extra.mem); 402 break; 403 default: 404 break; 405 } 406 (void)f; 407 } 408 409 void cg_ir_func_dump(const CgIrFunc* f, Writer* w) { 410 char buf[1024]; 411 StrBuf sb; 412 if (!f || !w) return; 413 strbuf_init(&sb, buf, sizeof buf); 414 415 strbuf_put_slice(&sb, SLICE_LIT("func sym#")); 416 strbuf_put_u64(&sb, (u64)f->desc.sym); 417 strbuf_put_slice(&sb, SLICE_LIT(" params=")); 418 strbuf_put_u64(&sb, (u64)f->nparams); 419 strbuf_put_slice(&sb, SLICE_LIT(" locals=")); 420 strbuf_put_u64(&sb, (u64)f->nlocals); 421 strbuf_put_slice(&sb, SLICE_LIT(" labels=")); 422 strbuf_put_u64(&sb, (u64)f->nlabels); 423 strbuf_put_slice(&sb, SLICE_LIT(" scopes=")); 424 strbuf_put_u64(&sb, (u64)f->nscopes); 425 strbuf_put_slice(&sb, SLICE_LIT(" insts=")); 426 strbuf_put_u64(&sb, (u64)f->ninsts); 427 strbuf_putc(&sb, '\n'); 428 dump_write(w, &sb); 429 430 for (u32 i = 0; i < f->nlocals; ++i) { 431 const CgIrLocal* l = &f->locals[i]; 432 strbuf_reset(&sb); 433 strbuf_put_slice(&sb, SLICE_LIT(" local L")); 434 strbuf_put_u64(&sb, (u64)l->id); 435 strbuf_putc(&sb, ' '); 436 put_type(&sb, l->desc.type); 437 strbuf_put_slice(&sb, SLICE_LIT(" sz=")); 438 strbuf_put_u64(&sb, (u64)l->desc.size); 439 strbuf_put_slice(&sb, SLICE_LIT(" al=")); 440 strbuf_put_u64(&sb, (u64)l->desc.align); 441 if (l->desc.name) { 442 Slice nm = pool_slice(f->c->global, l->desc.name); 443 strbuf_put_slice(&sb, SLICE_LIT(" \"")); 444 strbuf_put_slice(&sb, nm); 445 strbuf_putc(&sb, '"'); 446 } 447 if (l->is_param) { 448 strbuf_put_slice(&sb, SLICE_LIT(" param#")); 449 strbuf_put_u64(&sb, (u64)l->param_index); 450 } 451 if (l->address_taken) strbuf_put_slice(&sb, SLICE_LIT(" addr_taken")); 452 strbuf_putc(&sb, '\n'); 453 dump_write(w, &sb); 454 } 455 456 for (u32 i = 0; i < f->ninsts; ++i) { 457 const CgIrInst* in = &f->insts[i]; 458 strbuf_reset(&sb); 459 strbuf_put_slice(&sb, SLICE_LIT(" ")); 460 strbuf_put_u64(&sb, (u64)i); 461 strbuf_put_slice(&sb, SLICE_LIT(": ")); 462 strbuf_puts(&sb, cg_ir_op_name((CgIrOp)in->op)); 463 for (u32 j = 0; j < in->nopnds; ++j) { 464 strbuf_putc(&sb, ' '); 465 put_operand(&sb, &in->opnds[j]); 466 } 467 put_inst_extra(&sb, f, in); 468 strbuf_putc(&sb, '\n'); 469 dump_write(w, &sb); 470 } 471 }