static void walk_topo_helper(ir_node* irn, std::function<void (ir_node*, void*)> walker, void* env) { if (irn_visited(irn)) return; /* only break loops at phi/block nodes */ const bool is_loop_breaker = is_Phi(irn) || is_Block(irn); if (is_loop_breaker) mark_irn_visited(irn); if (!is_Block(irn)) { ir_node* block = get_nodes_block(irn); if (block != NULL) walk_topo_helper(block, walker, env); } for (int i = 0; i < get_irn_arity(irn); ++i) { ir_node* pred = get_irn_n(irn, i); walk_topo_helper(pred, walker, env); } if (is_loop_breaker || !irn_visited(irn)) walker(irn, env); mark_irn_visited(irn); }
/** * Check, whether a Return can be moved on block upwards. * * In a block with a Return, all live nodes must be linked * with the Return, otherwise they are dead (because the Return leaves * the graph, so no more users of the other nodes can exists. * * We can move a Return, if its predecessors are Phi nodes or * comes from another block. In the later case, it is always possible * to move the Return one block up, because the predecessor block must * dominate the Return block (SSA) and then it dominates the predecessor * block of the Return block as well. * * All predecessors of the Return block must be Jmp's of course, or we * cannot move it up, so we add blocks if needed. */ static bool can_move_ret(ir_node *ret) { ir_node *retbl = get_nodes_block(ret); int i, n = get_irn_arity(ret); for (i = 0; i < n; ++i) { ir_node *pred = get_irn_n(ret, i); if (! is_Phi(pred) && retbl == get_nodes_block(pred)) { /* first condition failed, found a non-Phi predecessor * then is in the Return block */ return false; } } /* check, that predecessors are Jmps */ n = get_Block_n_cfgpreds(retbl); /* we cannot move above a labeled block, as this might kill the block */ if (n <= 1 || get_Block_entity(retbl) != NULL) return false; for (i = 0; i < n; ++i) { ir_node *pred = get_Block_cfgpred(retbl, i); pred = skip_Tuple(pred); if (! is_Jmp(pred) && !is_Bad(pred)) { /* simply place a new block here */ ir_graph *irg = get_irn_irg(retbl); ir_node *block = new_r_Block(irg, 1, &pred); ir_node *jmp = new_r_Jmp(block); set_Block_cfgpred(retbl, i, jmp); } } return true; }
/** * Check, if we can reach a target node from a given node inside one basic block. * @param h The heights object. * @param curr The current node from which we tried to reach the other one. * @param tgt The node we try to reach. * @return 1, one of tgt can be reached from curr, 0 else. */ static bool search(ir_heights_t *h, const ir_node *curr, const ir_node *tgt) { /* if the current node is the one we were looking for, we're done. */ if (curr == tgt) return true; /* If we are in another block or at a phi we won't find our target. */ if (get_nodes_block(curr) != get_nodes_block(tgt)) return false; if (is_Phi(curr)) return false; /* Check, if we have already been here. Coming more often won't help :-) */ irn_height_t *h_curr = maybe_get_height_data(h, curr); if (h_curr->visited >= h->visited) return false; /* If we are too deep into the DAG we won't find the target either. */ irn_height_t *h_tgt = maybe_get_height_data(h, tgt); if (h_curr->height > h_tgt->height) return false; /* Mark this place as visited. */ h_curr->visited = h->visited; /* Start a search from this node. */ foreach_irn_in(curr, i, op) { if (search(h, op, tgt)) return true; } return false; }
ir_node *be_complete_Phi(ir_node *const phi, unsigned const n_ins, ir_node **const ins) { assert(is_Phi(phi) && get_Phi_n_preds(phi) == 0); ir_graph *const irg = get_irn_irg(phi); phi->attr.phi.u.backedge = new_backedge_arr(get_irg_obstack(irg), n_ins); set_irn_in(phi, n_ins, ins); arch_register_req_t const **const in_reqs = be_allocate_in_reqs(irg, n_ins); arch_register_req_t const *const req = arch_get_irn_register_req(phi); for (unsigned i = 0; i < n_ins; ++i) { in_reqs[i] = req; } backend_info_t *const info = be_get_info(phi); info->in_reqs = in_reqs; verify_new_node(phi); return phi; }
/** * Compute the height of a node in a block. * @param h The heights object. * @param irn The node. * @param bl The block. */ static unsigned compute_height(ir_heights_t *h, ir_node *irn, const ir_node *bl) { irn_height_t *ih = get_height_data(h, irn); /* bail out if we already visited that node. */ if (ih->visited >= h->visited) return ih->height; ih->visited = h->visited; ih->height = 0; foreach_out_edge(irn, edge) { ir_node *dep = get_edge_src_irn(edge); if (!is_Block(dep) && !is_Phi(dep) && get_nodes_block(dep) == bl) { unsigned dep_height = compute_height(h, dep, bl); ih->height = MAX(ih->height, dep_height+1); } }
/** * tests whether we can legally move node node after node after * (only works for nodes in same block) */ static bool can_move(ir_node *node, ir_node *after) { ir_node *node_block = get_nodes_block(node); assert(node_block == get_nodes_block(after)); /** all users have to be after the after node */ foreach_out_edge(node, edge) { ir_node *out = get_edge_src_irn(edge); if (get_nodes_block(out) != node_block) continue; /* phi represents a usage at block end */ if (is_Phi(out)) continue; if (arch_is_irn_not_scheduled(out)) { if (!can_move(out, after)) return false; } else { if (sched_get_time_step(out) <= sched_get_time_step(after)) return false; } }
static void try_remove_unnecessary_phi(ir_node *phi) { ir_node *phi_value = NULL; /* See if all inputs are either pointing to a single value or * are self references. */ bool have_self_loop = false; foreach_irn_in(phi, i, in) { if (in == phi) { have_self_loop = true; continue; } if (in == phi_value) continue; /** found a different value from the one we already found, can't remove * the phi (yet) */ if (phi_value != NULL) return; phi_value = in; } if (phi_value == NULL) return; /* Do not remove PhiM with self-loops as potentially endless loops are * observable, see comment in equivalent_node_Phi() to learn more. */ if (have_self_loop && get_irn_mode(phi) == mode_M) return; /* if we're here then all phi inputs have been either phi_value * or self-references, we can replace the phi by phi_value. */ exchange(phi, phi_value); /* recursively check phi_value, because it could be that we were the last * phi-node in a loop-body. Then our argument is an unnecessary phi in * the loop header which can be eliminated now */ if (is_Phi(phi_value)) { try_remove_unnecessary_phi(phi_value); } }
static void infer_typeinfo_walker(ir_node *irn, void *env) { bool *changed = (bool*) env; // A node's type needs only to be calculated once. if (get_irn_typeinfo_type(irn) != initial_type) return; if (is_Alloc(irn)) { // this one is easy, we know the exact dynamic type. ir_type *type = get_Alloc_type(irn); if (! is_Class_type(type)) return; set_irn_typeinfo_type(irn, type); *changed = true; } else if (is_Sel(irn) || is_SymConst_addr_ent(irn)) { // the type we determine here is the one of the entity we select or reference. // the transform_Sel method below will use the type incoming on the Sel_ptr input. ir_type *type = get_Sel_or_SymConst_type(irn); if (! type) return; ir_type *one_alive = get_alive_subclass(type); if (! one_alive) return; set_irn_typeinfo_type(irn, one_alive); *changed = true; } else if (is_Call(irn)) { // the dynamic type of the call result is the return type of the called entity. ir_node *call_pred = get_Call_ptr(irn); ir_type *pred_type = get_irn_typeinfo_type(call_pred); if (pred_type == initial_type) return; set_irn_typeinfo_type(irn, pred_type); *changed = true; } else if (is_Load(irn)) { // the dynamic type of the Load result is the type of the loaded entity. ir_node *load_pred = get_Load_ptr(irn); if (! is_Sel(load_pred) && !is_SymConst_addr_ent(load_pred)) return; ir_type *pred_type = get_irn_typeinfo_type(load_pred); if (pred_type == initial_type) return; set_irn_typeinfo_type(irn, pred_type); *changed = true; } else if (is_Proj(irn)) { // Types have to be propagated through Proj nodes (XXX: and also through Cast and Confirm ir_mode *pmode = get_irn_mode(irn); if (pmode != mode_P) return; ir_node *proj_pred = get_Proj_pred(irn); if (is_Proj(proj_pred) && get_irn_mode(proj_pred) == mode_T && get_Proj_proj(proj_pred) == pn_Call_T_result && is_Call(get_Proj_pred(proj_pred))) proj_pred = get_Proj_pred(proj_pred); // skip the result tuple ir_type *pred_type = get_irn_typeinfo_type(proj_pred); if (pred_type == initial_type) return; set_irn_typeinfo_type(irn, pred_type); *changed = true; } else if (is_Phi(irn)) { // Phi nodes are a special case because the incoming type information must be merged // A Phi node's type is unknown until all inputs are known to be the same dynamic type. ir_mode *pmode = get_irn_mode(irn); if (pmode != mode_P) return; int phi_preds = get_Phi_n_preds(irn); ir_type *last = NULL; for (int p = 0; p < phi_preds; p++) { ir_node *pred = get_Phi_pred(irn, p); ir_type *pred_type = get_irn_typeinfo_type(pred); if (pred_type == initial_type) return; if (p && last != pred_type) return; last = pred_type; } set_irn_typeinfo_type(irn, last); } }
Worklist::Worklist(ir_graph* functionGraph, GraphHandler& handler): functionGraph(functionGraph), handler(handler) { typedef void (*ir_func)(ir_node*, void*); struct envMembers { std::queue<ir_node*>* pQueue; std::unordered_map<ir_node*, bool>* pIsQueued; }; envMembers envInstance; envInstance.pQueue = &this->worklist; envInstance.pIsQueued = &this->isQueued; ir_func addPhis = [](ir_node * node, void* env) { if (is_Phi(node)) { auto envInstance = (envMembers*)env; set_irn_link(node, (void*)tarval_unknown); envInstance->pQueue->push(node); (*envInstance->pIsQueued)[node] = true; } }; ir_func addToWorklist = [](ir_node * node, void* env) { if (is_Phi(node)) return; auto envInstance = (envMembers*)env; ir_tarval* tarval; auto isBadNode = [&] (Node node) -> bool { if (is_Call(node) || is_Load(node) || is_Start(node)) return true; /*else if (Node(node).getChildCount() > 0) { for (Node child : Node(node).getChildren()) if (child.getTarval() == tarval_bad) return true; }*/ return false; }; // TODO: Support other modes such as Bu, Lu if (is_Const(node) && Node(node).getTarval().isNumeric()) tarval = get_Const_tarval(node); else if (isBadNode(Node(node))) tarval = tarval_bad; else tarval = tarval_unknown; set_irn_link(node, (void*)tarval); envInstance->pQueue->push(node); (*envInstance->pIsQueued)[node] = true; }; walk_topological(functionGraph, addPhis, (void*)&envInstance); walk_topological(functionGraph, addToWorklist, (void*)&envInstance); }
/** * lower 64bit conversions */ static void ia32_lower_conv64(ir_node *node, ir_mode *mode) { dbg_info *dbg = get_irn_dbg_info(node); ir_node *op = get_Conv_op(node); ir_mode *mode_from = get_irn_mode(op); ir_mode *mode_to = get_irn_mode(node); if (mode_is_float(mode_from) && get_mode_size_bits(mode_to) == 64 && get_mode_arithmetic(mode_to) == irma_twos_complement) { /* We have a Conv float -> long long here */ ir_node *float_to_ll; ir_node *l_res; ir_node *h_res; if (mode_is_signed(mode)) { /* convert from float to signed 64bit */ ir_node *block = get_nodes_block(node); float_to_ll = new_bd_ia32_l_FloattoLL(dbg, block, op); l_res = new_r_Proj(float_to_ll, ia32_mode_gp, pn_ia32_l_FloattoLL_res_low); h_res = new_r_Proj(float_to_ll, mode, pn_ia32_l_FloattoLL_res_high); } else { /* Convert from float to unsigned 64bit. */ ir_graph *irg = get_irn_irg(node); ir_tarval *flt_tv = new_tarval_from_str("9223372036854775808", 19, x86_mode_E); ir_node *flt_corr = new_r_Const(irg, flt_tv); ir_node *lower_blk = part_block_dw(node); ir_node *upper_blk = get_nodes_block(node); set_dw_control_flow_changed(); ir_node *opc = new_rd_Conv(dbg, upper_blk, op, x86_mode_E); ir_node *cmp = new_rd_Cmp(dbg, upper_blk, opc, flt_corr, ir_relation_less); ir_node *cond = new_rd_Cond(dbg, upper_blk, cmp); ir_node *in[] = { new_r_Proj(cond, mode_X, pn_Cond_true), new_r_Proj(cond, mode_X, pn_Cond_false) }; ir_node *blk = new_r_Block(irg, 1, &in[1]); in[1] = new_r_Jmp(blk); set_irn_in(lower_blk, 2, in); /* create to Phis */ ir_node *phi_in[] = { new_r_Const_null(irg, mode), new_r_Const_long(irg, mode, 0x80000000) }; ir_node *int_phi = new_r_Phi(lower_blk, ARRAY_SIZE(phi_in), phi_in, mode); ir_node *fphi_in[] = { opc, new_rd_Sub(dbg, upper_blk, opc, flt_corr, x86_mode_E) }; ir_node *flt_phi = new_r_Phi(lower_blk, ARRAY_SIZE(fphi_in), fphi_in, x86_mode_E); /* fix Phi links for next part_block() */ if (is_Phi(int_phi)) add_Block_phi(lower_blk, int_phi); if (is_Phi(flt_phi)) add_Block_phi(lower_blk, flt_phi); float_to_ll = new_bd_ia32_l_FloattoLL(dbg, lower_blk, flt_phi); l_res = new_r_Proj(float_to_ll, ia32_mode_gp, pn_ia32_l_FloattoLL_res_low); h_res = new_r_Proj(float_to_ll, mode, pn_ia32_l_FloattoLL_res_high); h_res = new_rd_Add(dbg, lower_blk, h_res, int_phi, mode); /* move the call and its Proj's to the lower block */ set_nodes_block(node, lower_blk); for (ir_node *proj = (ir_node*)get_irn_link(node); proj != NULL; proj = (ir_node*)get_irn_link(proj)) { set_nodes_block(proj, lower_blk); } } ir_set_dw_lowered(node, l_res, h_res); } else if (get_mode_size_bits(mode_from) == 64 && get_mode_arithmetic(mode_from) == irma_twos_complement && mode_is_float(mode_to)) { /* We have a Conv long long -> float here */ ir_node *op_low = get_lowered_low(op); ir_node *op_high = get_lowered_high(op); ir_node *block = get_nodes_block(node); ir_node *ll_to_float = new_bd_ia32_l_LLtoFloat(dbg, block, op_high, op_low, mode_to); exchange(node, ll_to_float); } else { ir_default_lower_dw_Conv(node, mode); } }
/** * Pre-walker for connecting DAGs and counting. */ static void connect_dags(ir_node *node, void *env) { dag_env_t *dag_env = (dag_env_t*)env; int i, arity; ir_node *block; dag_entry_t *entry; ir_mode *mode; if (is_Block(node)) return; block = get_nodes_block(node); /* ignore start end end blocks */ ir_graph *const irg = get_Block_irg(block); if (block == get_irg_start_block(irg) || block == get_irg_end_block(irg)) return; /* ignore Phi nodes */ if (is_Phi(node)) return; if (dag_env->options & FIRMSTAT_ARGS_ARE_ROOTS && is_arg(node)) return; mode = get_irn_mode(node); if (mode == mode_X || mode == mode_M) { /* do NOT count mode_X and mode_M nodes */ return; } /* if this option is set, Loads are always leaves */ if (dag_env->options & FIRMSTAT_LOAD_IS_LEAVE && is_Load(node)) return; if (dag_env->options & FIRMSTAT_CALL_IS_LEAVE && is_Call(node)) return; entry = get_irn_dag_entry(node); if (! entry) { /* found an unassigned node, maybe a new root */ entry = new_dag_entry(dag_env, node); } /* put the predecessors into the same DAG as the current */ for (i = 0, arity = get_irn_arity(node); i < arity; ++i) { ir_node *prev = get_irn_n(node, i); ir_mode *mode = get_irn_mode(prev); if (is_Phi(prev)) continue; if (mode == mode_X || mode == mode_M) continue; /* * copy constants if requested into the DAG's * beware, do NOT add a link, as this will result in * wrong intersections */ if (dag_env->options & FIRMSTAT_COPY_CONSTANTS) { if (is_irn_constlike(prev)) { ++entry->num_nodes; ++entry->num_inner_nodes; } } /* only nodes from the same block goes into the DAG */ if (get_nodes_block(prev) == block) { dag_entry_t *prev_entry = get_irn_dag_entry(prev); if (! prev_entry) { /* not assigned node, put it into the same DAG */ set_irn_dag_entry(prev, entry); ++entry->num_nodes; ++entry->num_inner_nodes; } else { if (prev_entry == entry) { /* We found a node that is already assigned to this DAG. This DAG is not a tree. */ entry->is_tree = 0; } else { /* two DAGs intersect: copy the data to one of them and kill the other */ entry->num_roots += prev_entry->num_roots; entry->num_nodes += prev_entry->num_nodes; entry->num_inner_nodes += prev_entry->num_inner_nodes; entry->is_tree &= prev_entry->is_tree; --dag_env->num_of_dags; prev_entry->is_dead = 1; prev_entry->link = entry; } } } } }
/** * Post-walker to detect DAG roots that are referenced form other blocks */ static void find_dag_roots(ir_node *node, void *env) { dag_env_t *dag_env = (dag_env_t*)env; int i, arity; dag_entry_t *entry; ir_node *block; if (is_Block(node)) return; block = get_nodes_block(node); /* ignore start end end blocks */ ir_graph *const irg = get_Block_irg(block); if (block == get_irg_start_block(irg) || block == get_irg_end_block(irg)) return; /* Phi nodes always references nodes from "other" block */ if (is_Phi(node)) { if (get_irn_mode(node) != mode_M) { for (i = 0, arity = get_irn_arity(node); i < arity; ++i) { ir_node *prev = get_irn_n(node, i); if (is_Phi(prev)) continue; if (dag_env->options & FIRMSTAT_COPY_CONSTANTS) { if (is_irn_constlike(prev)) continue; } entry = get_irn_dag_entry(prev); if (! entry) { /* found an unassigned node, a new root */ entry = new_dag_entry(dag_env, node); entry->is_ext_ref = 1; } } } } else { for (i = 0, arity = get_irn_arity(node); i < arity; ++i) { ir_node *prev = get_irn_n(node, i); ir_mode *mode = get_irn_mode(prev); if (mode == mode_X || mode == mode_M) continue; if (is_Phi(prev)) continue; if (dag_env->options & FIRMSTAT_COPY_CONSTANTS) { if (is_irn_constlike(prev)) continue; } if (get_nodes_block(prev) != block) { /* The predecessor is from another block. It forms a root. */ entry = get_irn_dag_entry(prev); if (! entry) { /* found an unassigned node, a new root */ entry = new_dag_entry(dag_env, node); entry->is_ext_ref = 1; } } } } }