ir_print.c (9547B)
1 #include "core/slice.h" 2 #include "core/strbuf.h" 3 #include "opt/opt.h" 4 5 static void dump_write(Writer* w, const char* s) { 6 kit_writer_write(w, s, slice_from_cstr(s).len); 7 } 8 9 static void dump_sb(Writer* w, const StrBuf* sb) { 10 kit_writer_write(w, strbuf_cstr(sb), strbuf_len(sb)); 11 } 12 13 /* Append a signed value with an explicit leading sign, matching printf's 14 * "%+lld"/"%+d" formatting (forced '+' on non-negative values). */ 15 static void strbuf_put_i64_plus(StrBuf* sb, i64 v) { 16 if (v < 0) { 17 strbuf_putc(sb, '-'); 18 strbuf_put_u64(sb, (u64)(-(v + 1)) + 1u); 19 } else { 20 strbuf_putc(sb, '+'); 21 strbuf_put_u64(sb, (u64)v); 22 } 23 } 24 25 static const char* op_name(IROp op) { 26 switch (op) { 27 case IR_NOP: 28 return "nop"; 29 case IR_CONST_I: 30 return "const_i"; 31 case IR_CONST_BYTES: 32 return "const_bytes"; 33 case IR_PARAM_DECL: 34 return "param_decl"; 35 case IR_LOAD_IMM: 36 return "load_imm"; 37 case IR_LOAD_CONST: 38 return "load_const"; 39 case IR_COPY: 40 return "copy"; 41 case IR_LOAD: 42 return "load"; 43 case IR_STORE: 44 return "store"; 45 case IR_ADDR_OF: 46 return "addr_of"; 47 case IR_TLS_ADDR_OF: 48 return "tls_addr_of"; 49 case IR_AGG_COPY: 50 return "agg_copy"; 51 case IR_AGG_SET: 52 return "agg_set"; 53 case IR_BITFIELD_LOAD: 54 return "bitfield_load"; 55 case IR_BITFIELD_STORE: 56 return "bitfield_store"; 57 case IR_BINOP: 58 return "binop"; 59 case IR_UNOP: 60 return "unop"; 61 case IR_CMP: 62 return "cmp"; 63 case IR_CONVERT: 64 return "convert"; 65 case IR_CALL: 66 return "call"; 67 case IR_PHI: 68 return "phi"; 69 case IR_BR: 70 return "br"; 71 case IR_CONDBR: 72 return "condbr"; 73 case IR_CMP_BRANCH: 74 return "cmp_branch"; 75 case IR_SWITCH: 76 return "switch"; 77 case IR_INDIRECT_BRANCH: 78 return "indirect_branch"; 79 case IR_LOAD_LABEL_ADDR: 80 return "load_label_addr"; 81 case IR_LOCAL_STATIC_DATA_BEGIN: 82 return "local_static_data_begin"; 83 case IR_LOCAL_STATIC_DATA_WRITE: 84 return "local_static_data_write"; 85 case IR_LOCAL_STATIC_DATA_LABEL_ADDR: 86 return "local_static_data_label_addr"; 87 case IR_LOCAL_STATIC_DATA_END: 88 return "local_static_data_end"; 89 case IR_RET: 90 return "ret"; 91 case IR_UNREACHABLE: 92 return "unreachable"; 93 case IR_SCOPE_BEGIN: 94 return "scope_begin"; 95 case IR_SCOPE_END: 96 return "scope_end"; 97 case IR_BREAK_TO: 98 return "break_to"; 99 case IR_CONTINUE_TO: 100 return "continue_to"; 101 case IR_ALLOCA: 102 return "alloca"; 103 case IR_VA_START: 104 return "va_start"; 105 case IR_VA_ARG: 106 return "va_arg"; 107 case IR_VA_END: 108 return "va_end"; 109 case IR_VA_COPY: 110 return "va_copy"; 111 case IR_ATOMIC_LOAD: 112 return "atomic_load"; 113 case IR_ATOMIC_STORE: 114 return "atomic_store"; 115 case IR_ATOMIC_RMW: 116 return "atomic_rmw"; 117 case IR_ATOMIC_CAS: 118 return "atomic_cas"; 119 case IR_FENCE: 120 return "fence"; 121 case IR_ASM_BLOCK: 122 return "asm_block"; 123 case IR_INTRINSIC: 124 return "intrinsic"; 125 default: 126 return "unknown"; 127 } 128 } 129 130 static const char* alias_name(u8 kind) { 131 switch ((AliasKind)kind) { 132 case ALIAS_UNKNOWN: 133 return "unknown"; 134 case ALIAS_LOCAL: 135 return "local"; 136 case ALIAS_GLOBAL: 137 return "global"; 138 case ALIAS_PARAM: 139 return "param"; 140 case ALIAS_HEAP: 141 return "heap"; 142 case ALIAS_STRING: 143 return "string"; 144 default: 145 return "alias?"; 146 } 147 } 148 149 static void dump_alias(Writer* w, const AliasRoot* a) { 150 char buf[64]; 151 StrBuf sb; 152 dump_write(w, alias_name(a->kind)); 153 strbuf_init(&sb, buf, sizeof buf); 154 switch ((AliasKind)a->kind) { 155 case ALIAS_LOCAL: 156 strbuf_putc(&sb, '#'); 157 strbuf_put_i64(&sb, (i64)(int)a->v.local_id); 158 dump_sb(w, &sb); 159 break; 160 case ALIAS_GLOBAL: 161 strbuf_putc(&sb, '#'); 162 strbuf_put_u64(&sb, (u64)(unsigned)a->v.global); 163 dump_sb(w, &sb); 164 break; 165 case ALIAS_PARAM: 166 strbuf_putc(&sb, '#'); 167 strbuf_put_u64(&sb, (u64)(unsigned)a->v.param_idx); 168 dump_sb(w, &sb); 169 break; 170 case ALIAS_STRING: 171 strbuf_putc(&sb, '#'); 172 strbuf_put_u64(&sb, (u64)(unsigned)a->v.string_id); 173 dump_sb(w, &sb); 174 break; 175 default: 176 break; 177 } 178 } 179 180 static void dump_operand(Writer* w, const Operand* op) { 181 char buf[96]; 182 StrBuf sb; 183 if (!op) { 184 dump_write(w, "-"); 185 return; 186 } 187 strbuf_init(&sb, buf, sizeof buf); 188 switch ((OptOperandKind)op->kind) { 189 case OPK_IMM: 190 strbuf_puts(&sb, "imm:"); 191 strbuf_put_i64(&sb, (i64)op->v.imm); 192 dump_sb(w, &sb); 193 break; 194 case OPK_REG: 195 strbuf_putc(&sb, 'v'); 196 strbuf_put_u64(&sb, (u64)(unsigned)op->v.reg); 197 dump_sb(w, &sb); 198 break; 199 case OPK_LOCAL: 200 strbuf_puts(&sb, "local#"); 201 strbuf_put_u64(&sb, (u64)(unsigned)op->v.frame_slot); 202 dump_sb(w, &sb); 203 break; 204 case OPK_GLOBAL: 205 strbuf_puts(&sb, "global#"); 206 strbuf_put_u64(&sb, (u64)(unsigned)op->v.global.sym); 207 strbuf_put_i64_plus(&sb, (i64)op->v.global.addend); 208 dump_sb(w, &sb); 209 break; 210 case OPK_INDIRECT: 211 strbuf_putc(&sb, '['); 212 strbuf_putc(&sb, 'v'); 213 strbuf_put_u64(&sb, (u64)(unsigned)op->v.ind.base); 214 strbuf_put_i64_plus(&sb, (i64)(int)op->v.ind.ofs); 215 strbuf_putc(&sb, ']'); 216 dump_sb(w, &sb); 217 break; 218 default: 219 dump_write(w, "op?"); 220 break; 221 } 222 } 223 224 static void dump_operands(Writer* w, const Inst* in) { 225 dump_write(w, " opnds=["); 226 for (u32 i = 0; i < in->nopnds; ++i) { 227 if (i) dump_write(w, ","); 228 dump_operand(w, &in->opnds[i]); 229 } 230 dump_write(w, "]"); 231 } 232 233 static void dump_mem(Writer* w, const MemAccess* m) { 234 char buf[128]; 235 StrBuf sb; 236 strbuf_init(&sb, buf, sizeof buf); 237 strbuf_puts(&sb, " mem=size"); 238 strbuf_put_u64(&sb, (u64)(unsigned)m->size); 239 strbuf_puts(&sb, " align"); 240 strbuf_put_u64(&sb, (u64)(unsigned)m->align); 241 strbuf_puts(&sb, " flags="); 242 strbuf_put_hex_u64(&sb, (u64)(unsigned)m->flags); 243 strbuf_puts(&sb, " alias="); 244 dump_sb(w, &sb); 245 dump_alias(w, &m->alias); 246 } 247 248 static void dump_phi(Writer* w, const Inst* in) { 249 char buf[96]; 250 StrBuf sb; 251 IRPhiAux* aux = (IRPhiAux*)in->extra.aux; 252 strbuf_init(&sb, buf, sizeof buf); 253 strbuf_puts(&sb, " slot="); 254 strbuf_put_u64(&sb, aux ? (u64)(unsigned)aux->slot_id : 0u); 255 strbuf_puts(&sb, " preds=["); 256 dump_sb(w, &sb); 257 if (aux) { 258 for (u32 p = 0; p < aux->npreds; ++p) { 259 strbuf_reset(&sb); 260 if (p) strbuf_putc(&sb, ','); 261 strbuf_putc(&sb, 'b'); 262 strbuf_put_u64(&sb, (u64)(unsigned)aux->pred_blocks[p]); 263 strbuf_puts(&sb, ":v"); 264 strbuf_put_u64(&sb, (u64)(unsigned)aux->pred_vals[p]); 265 dump_sb(w, &sb); 266 } 267 } 268 dump_write(w, "]"); 269 } 270 271 static void dump_ret(Writer* w, const Inst* in) { 272 IRRetAux* aux = (IRRetAux*)in->extra.aux; 273 if (!aux || !aux->present) { 274 dump_write(w, " ret=void"); 275 return; 276 } 277 dump_write(w, " ret="); 278 dump_operand(w, &aux->val.storage); 279 } 280 281 void opt_ir_dump(Func* f, Writer* w) { 282 if (!f || !w) return; 283 char buf[160]; 284 StrBuf sb; 285 strbuf_init(&sb, buf, sizeof buf); 286 strbuf_puts(&sb, "ir blocks="); 287 strbuf_put_u64(&sb, (u64)(unsigned)f->nblocks); 288 strbuf_puts(&sb, " vals="); 289 strbuf_put_u64(&sb, (u64)(unsigned)f->nvals); 290 strbuf_putc(&sb, '\n'); 291 dump_sb(w, &sb); 292 for (u32 b = 0; b < f->nblocks; ++b) { 293 Block* bl = &f->blocks[b]; 294 strbuf_reset(&sb); 295 strbuf_puts(&sb, "block "); 296 strbuf_put_u64(&sb, (u64)(unsigned)b); 297 strbuf_puts(&sb, " preds=["); 298 dump_sb(w, &sb); 299 for (u32 p = 0; p < bl->npreds; ++p) { 300 strbuf_reset(&sb); 301 if (p) strbuf_putc(&sb, ','); 302 strbuf_putc(&sb, 'b'); 303 strbuf_put_u64(&sb, (u64)(unsigned)bl->preds[p]); 304 dump_sb(w, &sb); 305 } 306 dump_write(w, "] succs=["); 307 for (u32 s = 0; s < bl->nsucc; ++s) { 308 strbuf_reset(&sb); 309 if (s) strbuf_putc(&sb, ','); 310 strbuf_putc(&sb, 'b'); 311 strbuf_put_u64(&sb, (u64)(unsigned)bl->succ[s]); 312 dump_sb(w, &sb); 313 } 314 strbuf_reset(&sb); 315 strbuf_puts(&sb, "] insts="); 316 strbuf_put_u64(&sb, (u64)(unsigned)bl->ninsts); 317 strbuf_putc(&sb, '\n'); 318 dump_sb(w, &sb); 319 320 for (u32 i = 0; i < bl->ninsts; ++i) { 321 Inst* in = &bl->insts[i]; 322 strbuf_reset(&sb); 323 strbuf_puts(&sb, " "); 324 strbuf_put_u64(&sb, (u64)(unsigned)i); 325 strbuf_putc(&sb, ' '); 326 strbuf_puts(&sb, op_name((IROp)in->op)); 327 dump_sb(w, &sb); 328 if (in->def != VAL_NONE) { 329 strbuf_reset(&sb); 330 strbuf_puts(&sb, " def=v"); 331 strbuf_put_u64(&sb, (u64)(unsigned)in->def); 332 dump_sb(w, &sb); 333 } 334 if (in->ndefs) { 335 dump_write(w, " defs=["); 336 for (u32 d = 0; d < in->ndefs; ++d) { 337 strbuf_reset(&sb); 338 if (d) strbuf_putc(&sb, ','); 339 strbuf_putc(&sb, 'v'); 340 strbuf_put_u64(&sb, (u64)(unsigned)in->defs[d]); 341 dump_sb(w, &sb); 342 } 343 dump_write(w, "]"); 344 } 345 if (in->nopnds) dump_operands(w, in); 346 if ((IROp)in->op == IR_LOAD || (IROp)in->op == IR_STORE) 347 dump_mem(w, &in->extra.mem); 348 if ((IROp)in->op == IR_LOAD_IMM || (IROp)in->op == IR_CONST_I) { 349 strbuf_reset(&sb); 350 strbuf_puts(&sb, " imm="); 351 strbuf_put_i64(&sb, (i64)in->extra.imm); 352 dump_sb(w, &sb); 353 } 354 if ((IROp)in->op == IR_PHI) dump_phi(w, in); 355 if ((IROp)in->op == IR_RET) dump_ret(w, in); 356 dump_write(w, "\n"); 357 } 358 } 359 }