1// SPDX-License-Identifier: MIT 2// 3// SSA conversion 4// Copyright (C) 2005 Luc Van Oostenryck 5// 6 7#include <assert.h> 8#include "ssa.h" 9#include "lib.h" 10#include "dominate.h" 11#include "flowgraph.h" 12#include "linearize.h" 13#include "simplify.h" 14#include "flow.h" 15 16 17// Is it possible and desirable for this to be promoted to a pseudo? 18static inline bool is_promotable(struct symbol *type) 19{ 20 struct symbol *member; 21 int bf_seen; 22 int nbr; 23 24 if (type->type == SYM_NODE) 25 type = type->ctype.base_type; 26 switch (type->type) { 27 case SYM_ENUM: 28 case SYM_BITFIELD: 29 case SYM_PTR: 30 case SYM_RESTRICT: // OK, always integer types 31 return 1; 32 case SYM_STRUCT: 33 // we allow a single scalar field 34 // but a run of bitfields count for 1 35 // (and packed bifields are excluded). 36 if (type->packed) 37 return 0; 38 nbr = 0; 39 bf_seen = 0; 40 FOR_EACH_PTR(type->symbol_list, member) { 41 if (is_bitfield_type(member)) { 42 if (bf_seen) 43 continue; 44 bf_seen = 1; 45 } else { 46 bf_seen = 0; 47 } 48 if (!is_scalar_type(member)) 49 return 0; 50 if (nbr++) 51 return 0; 52 } END_FOR_EACH_PTR(member); 53 if (bf_seen && (type->bit_size > long_ctype.bit_size)) 54 return 0; 55 return 1; 56 case SYM_UNION: 57 // FIXME: should be like struct but has problem 58 // when used with/for type cohercion 59 // -----> OK if only same sized integral types 60 FOR_EACH_PTR(type->symbol_list, member) { 61 if (member->bit_size != type->bit_size) 62 return 0; 63 if (!is_integral_type(member)) 64 return 0; 65 } END_FOR_EACH_PTR(member); 66 return 1; 67 default: 68 break; 69 } 70 if (type->ctype.base_type == &int_type) 71 return 1; 72 if (type->ctype.base_type == &fp_type) 73 return 1; 74 return 0; 75} 76 77static void kill_store(struct instruction *insn) 78{ 79 remove_use(&insn->src); 80 remove_use(&insn->target); 81 insn->bb = NULL; 82} 83 84static bool same_memop(struct instruction *a, struct instruction *b) 85{ 86 if (a->size != b->size || a->offset != b->offset) 87 return false; 88 if (is_integral_type(a->type) && is_float_type(b->type)) 89 return false; 90 if (is_float_type(a->type) && is_integral_type(b->type)) 91 return false; 92 return true; 93} 94 95static void rewrite_local_var(struct basic_block *bb, pseudo_t addr, int nbr_stores, int nbr_uses) 96{ 97 struct instruction *store = NULL; 98 struct instruction *insn; 99 bool remove = false; 100 101 if (!bb) 102 return; 103 104 FOR_EACH_PTR(bb->insns, insn) { 105 106 if (!insn->bb || insn->src != addr) 107 continue; 108 switch (insn->opcode) { 109 case OP_LOAD: 110 if (!store) 111 replace_with_pseudo(insn, undef_pseudo()); 112 else if (same_memop(store, insn)) 113 replace_with_pseudo(insn, store->target); 114 else 115 remove = false; 116 break; 117 case OP_STORE: 118 store = insn; 119 remove = true; 120 break; 121 } 122 } END_FOR_EACH_PTR(insn); 123 if (remove) 124 kill_store(store); 125} 126 127// we would like to know: 128// is there one or more stores? 129// are all loads & stores local/done in a single block? 130static void ssa_convert_one_var(struct entrypoint *ep, struct symbol *var) 131{ 132 unsigned long generation = ++bb_generation; 133 struct basic_block_list *alpha = NULL; 134 struct basic_block_list *idf = NULL; 135 struct basic_block *samebb = NULL; 136 struct basic_block *bb; 137 struct pseudo_user *pu; 138 unsigned long mod = var->ctype.modifiers; 139 bool local = true; 140 int nbr_stores = 0; 141 int nbr_uses = 0; 142 pseudo_t addr; 143 144 /* Never used as a symbol? */ 145 addr = var->pseudo; 146 if (!addr) 147 return; 148 149 /* We don't do coverage analysis of volatiles.. */ 150 if (mod & MOD_VOLATILE) 151 return; 152 153 /* ..and symbols with external visibility need more care */ 154 mod &= (MOD_NONLOCAL | MOD_STATIC | MOD_ADDRESSABLE); 155 if (mod) 156 goto external_visibility; 157 158 if (!is_promotable(var)) 159 return; 160 161 // 1) insert in the worklist all BBs that may modify var 162 FOR_EACH_PTR(addr->users, pu) { 163 struct instruction *insn = pu->insn; 164 struct basic_block *bb = insn->bb; 165 166 switch (insn->opcode) { 167 case OP_STORE: 168 nbr_stores++; 169 if (bb->generation != generation) { 170 bb->generation = generation; 171 add_bb(&alpha, bb); 172 } 173 /* fall through */ 174 case OP_LOAD: 175 if (local) { 176 if (!samebb) 177 samebb = bb; 178 else if (samebb != bb) 179 local = false; 180 } 181 nbr_uses++; 182 break; 183 case OP_SYMADDR: 184 mod |= MOD_ADDRESSABLE; 185 goto external_visibility; 186 default: 187 warning(var->pos, "symbol '%s' pseudo used in unexpected way", 188 show_ident(var->ident)); 189 } 190 } END_FOR_EACH_PTR(pu); 191 192 // if all uses are local to a single block 193 // they can easily be rewritten and doesn't need phi-nodes 194 // FIXME: could be done for extended BB too 195 if (local) { 196 rewrite_local_var(samebb, addr, nbr_stores, nbr_uses); 197 return; 198 } 199 200 idf_compute(ep, &idf, alpha); 201 FOR_EACH_PTR(idf, bb) { 202 struct instruction *node = insert_phi_node(bb, var); 203 node->phi_var = var->pseudo; 204 } END_FOR_EACH_PTR(bb); 205 var->torename = 1; 206 207external_visibility: 208 if (mod & (MOD_NONLOCAL | MOD_STATIC)) 209 return; 210 kill_dead_stores(ep, addr, !mod); 211} 212 213static struct instruction *lookup_var(struct basic_block *bb, struct symbol *var) 214{ 215 do { 216 struct instruction *insn = phi_map_lookup(bb->phi_map, var); 217 if (insn) 218 return insn; 219 } while ((bb = bb->idom)); 220 return NULL; 221} 222 223static struct instruction_list *phis_all; 224static struct instruction_list *phis_used; 225static struct instruction_list *stores; 226 227static bool matching_load(struct instruction *def, struct instruction *insn) 228{ 229 if (insn->size != def->size) 230 return false; 231 switch (def->opcode) { 232 case OP_STORE: 233 case OP_LOAD: 234 if (insn->offset != def->offset) 235 return false; 236 case OP_PHI: 237 break; 238 default: 239 return false; 240 } 241 return true; 242} 243 244static void ssa_rename_insn(struct basic_block *bb, struct instruction *insn) 245{ 246 struct instruction *def; 247 struct symbol *var; 248 pseudo_t addr; 249 pseudo_t val; 250 251 switch (insn->opcode) { 252 case OP_STORE: 253 addr = insn->src; 254 if (addr->type != PSEUDO_SYM) 255 break; 256 var = addr->sym; 257 if (!var || !var->torename) 258 break; 259 phi_map_update(&bb->phi_map, var, insn); 260 add_instruction(&stores, insn); 261 break; 262 case OP_LOAD: 263 addr = insn->src; 264 if (addr->type != PSEUDO_SYM) 265 break; 266 var = addr->sym; 267 if (!var || !var->torename) 268 break; 269 def = lookup_var(bb, var); 270 if (!def) { 271 val = undef_pseudo(); 272 } else if (!matching_load(def, insn)) { 273 var->torename = false; 274 break; 275 } else { 276 val = def->target; 277 } 278 replace_with_pseudo(insn, val); 279 break; 280 case OP_PHI: 281 var = insn->type; 282 if (!var || !var->torename) 283 break; 284 phi_map_update(&bb->phi_map, var, insn); 285 add_instruction(&phis_all, insn); 286 break; 287 } 288} 289 290static void ssa_rename_insns(struct entrypoint *ep) 291{ 292 struct basic_block *bb; 293 294 FOR_EACH_PTR(ep->bbs, bb) { 295 struct instruction *insn; 296 FOR_EACH_PTR(bb->insns, insn) { 297 if (!insn->bb) 298 continue; 299 ssa_rename_insn(bb, insn); 300 } END_FOR_EACH_PTR(insn); 301 } END_FOR_EACH_PTR(bb); 302} 303 304static void mark_phi_used(pseudo_t val) 305{ 306 struct instruction *node; 307 308 if (val->type != PSEUDO_REG) 309 return; 310 node = val->def; 311 if (node->opcode != OP_PHI) 312 return; 313 if (node->used) 314 return; 315 node->used = 1; 316 add_instruction(&phis_used, node); 317} 318 319static void ssa_rename_phi(struct instruction *insn) 320{ 321 struct basic_block *par; 322 struct symbol *var; 323 324 if (!insn->phi_var) 325 return; 326 var = insn->phi_var->sym; 327 if (!var->torename) 328 return; 329 FOR_EACH_PTR(insn->bb->parents, par) { 330 struct instruction *def = lookup_var(par, var); 331 pseudo_t val = def ? def->target : undef_pseudo(); 332 struct instruction *phisrc = alloc_phisrc(val, var); 333 pseudo_t phi = phisrc->target; 334 phi->ident = var->ident; 335 insert_last_instruction(par, phisrc); 336 link_phi(insn, phi); 337 mark_phi_used(val); 338 } END_FOR_EACH_PTR(par); 339} 340 341static void ssa_rename_phis(struct entrypoint *ep) 342{ 343 struct instruction *phi; 344 345 phis_used = NULL; 346 FOR_EACH_PTR(phis_all, phi) { 347 if (has_users(phi->target)) { 348 phi->used = 1; 349 add_instruction(&phis_used, phi); 350 } 351 } END_FOR_EACH_PTR(phi); 352 353 FOR_EACH_PTR(phis_used, phi) { 354 if (!phi->bb) 355 continue; 356 ssa_rename_phi(phi); 357 } END_FOR_EACH_PTR(phi); 358} 359 360static void remove_dead_stores(struct instruction_list *stores) 361{ 362 struct instruction *store; 363 364 FOR_EACH_PTR(stores, store) { 365 struct symbol *var = store->addr->sym; 366 367 if (var->torename) 368 kill_store(store); 369 } END_FOR_EACH_PTR(store); 370} 371 372void ssa_convert(struct entrypoint *ep) 373{ 374 struct basic_block *bb; 375 pseudo_t pseudo; 376 int first, last; 377 378 // calculate the number of BBs 379 first = ep->entry->bb->nr; 380 last = first; 381 FOR_EACH_PTR(ep->bbs, bb) { 382 int nr = bb->nr; 383 if (nr > last) 384 last = nr; 385 bb->phi_map = NULL; 386 } END_FOR_EACH_PTR(bb); 387 388 // try to promote memory accesses to pseudos 389 stores = NULL; 390 FOR_EACH_PTR(ep->accesses, pseudo) { 391 ssa_convert_one_var(ep, pseudo->sym); 392 } END_FOR_EACH_PTR(pseudo); 393 394 // rename the converted accesses 395 phis_all = phis_used = NULL; 396 ssa_rename_insns(ep); 397 ssa_rename_phis(ep); 398 399 // remove now dead stores 400 remove_dead_stores(stores); 401} 402