/** * lower 64bit addition: an 32bit add for the lower parts, an add with * carry for the higher parts. If the carry's value is known, fold it * into the upper add. */ static void ia32_lower_add64(ir_node *node, ir_mode *mode) { dbg_info *dbg = get_irn_dbg_info(node); ir_node *block = get_nodes_block(node); ir_node *left = get_Add_left(node); ir_node *right = get_Add_right(node); ir_node *left_low = get_lowered_low(left); ir_node *left_high = get_lowered_high(left); ir_node *right_low = get_lowered_low(right); ir_node *right_high = get_lowered_high(right); ir_mode *low_mode = get_irn_mode(left_low); ir_mode *high_mode = get_irn_mode(left_high); carry_result cr = lower_add_carry(left, right, low_mode); assert(get_irn_mode(left_low) == get_irn_mode(right_low)); assert(get_irn_mode(left_high) == get_irn_mode(right_high)); if (cr == no_carry) { ir_node *add_low = new_rd_Add(dbg, block, left_low, right_low, low_mode); ir_node *add_high = new_rd_Add(dbg, block, left_high, right_high, high_mode); ir_set_dw_lowered(node, add_low, add_high); } else if (cr == must_carry && (is_Const(left_high) || is_Const(right_high))) { // We cannot assume that left_high and right_high form a normalized Add. ir_node *constant; ir_node *other; if (is_Const(left_high)) { constant = left_high; other = right_high; } else { constant = right_high; other = left_high; } ir_graph *irg = get_irn_irg(right_high); ir_node *one = new_rd_Const(dbg, irg, get_mode_one(high_mode)); ir_node *const_plus_one = new_rd_Add(dbg, block, constant, one, high_mode); ir_node *add_high = new_rd_Add(dbg, block, other, const_plus_one, high_mode); ir_node *add_low = new_rd_Add(dbg, block, left_low, right_low, low_mode); ir_set_dw_lowered(node, add_low, add_high); } else { /* l_res = a_l + b_l */ ir_node *add_low = new_bd_ia32_l_Add(dbg, block, left_low, right_low); ir_mode *mode_flags = ia32_reg_classes[CLASS_ia32_flags].mode; ir_node *res_low = new_r_Proj(add_low, ia32_mode_gp, pn_ia32_l_Add_res); ir_node *flags = new_r_Proj(add_low, mode_flags, pn_ia32_l_Add_flags); /* h_res = a_h + b_h + carry */ ir_node *add_high = new_bd_ia32_l_Adc(dbg, block, left_high, right_high, flags, mode); ir_set_dw_lowered(node, res_low, add_high); } }
/** * lower 64bit subtraction: a 32bit sub for the lower parts, a sub * with borrow for the higher parts. If the borrow's value is known, * fold it into the upper sub. */ static void ia32_lower_sub64(ir_node *node, ir_mode *mode) { dbg_info *dbg = get_irn_dbg_info(node); ir_node *block = get_nodes_block(node); ir_node *left = get_Sub_left(node); ir_node *right = get_Sub_right(node); ir_node *left_low = get_lowered_low(left); ir_node *left_high = get_lowered_high(left); ir_node *right_low = get_lowered_low(right); ir_node *right_high = get_lowered_high(right); ir_mode *low_mode = get_irn_mode(left_low); ir_mode *high_mode = get_irn_mode(left_high); carry_result cr = lower_sub_borrow(left, right, low_mode); assert(get_irn_mode(left_low) == get_irn_mode(right_low)); assert(get_irn_mode(left_high) == get_irn_mode(right_high)); if (cr == no_carry) { ir_node *sub_low = new_rd_Sub(dbg, block, left_low, right_low, low_mode); ir_node *sub_high = new_rd_Sub(dbg, block, left_high, right_high, high_mode); ir_set_dw_lowered(node, sub_low, sub_high); } else if (cr == must_carry && (is_Const(left_high) || is_Const(right_high))) { ir_node *sub_high; ir_graph *irg = get_irn_irg(right_high); ir_node *one = new_rd_Const(dbg, irg, get_mode_one(high_mode)); if (is_Const(right_high)) { ir_node *new_const = new_rd_Add(dbg, block, right_high, one, high_mode); sub_high = new_rd_Sub(dbg, block, left_high, new_const, high_mode); } else if (is_Const(left_high)) { ir_node *new_const = new_rd_Sub(dbg, block, left_high, one, high_mode); sub_high = new_rd_Sub(dbg, block, new_const, right_high, high_mode); } else { panic("logic error"); } ir_node *sub_low = new_rd_Sub(dbg, block, left_low, right_low, low_mode); ir_set_dw_lowered(node, sub_low, sub_high); } else { /* l_res = a_l - b_l */ ir_node *sub_low = new_bd_ia32_l_Sub(dbg, block, left_low, right_low); ir_mode *mode_flags = ia32_reg_classes[CLASS_ia32_flags].mode; ir_node *res_low = new_r_Proj(sub_low, ia32_mode_gp, pn_ia32_l_Sub_res); ir_node *flags = new_r_Proj(sub_low, mode_flags, pn_ia32_l_Sub_flags); /* h_res = a_h - b_h - carry */ ir_node *sub_high = new_bd_ia32_l_Sbb(dbg, block, left_high, right_high, flags, mode); ir_set_dw_lowered(node, res_low, sub_high); } }
ir_node *copy_const_value(dbg_info *dbg, ir_node *n, ir_node *block) { ir_graph *irg = get_irn_irg(block); /* @@@ GL I think we should implement this using the routines from irgopt * for dead node elimination/inlineing. */ ir_mode *m = get_irn_mode(n); ir_node *nn; switch (get_irn_opcode(n)) { case iro_Const: nn = new_rd_Const(dbg, irg, get_Const_tarval(n)); break; case iro_SymConst: nn = new_rd_SymConst(dbg, irg, get_irn_mode(n), get_SymConst_symbol(n), get_SymConst_kind(n)); break; case iro_Add: nn = new_rd_Add(dbg, block, copy_const_value(dbg, get_Add_left(n), block), copy_const_value(dbg, get_Add_right(n), block), m); break; case iro_Sub: nn = new_rd_Sub(dbg, block, copy_const_value(dbg, get_Sub_left(n), block), copy_const_value(dbg, get_Sub_right(n), block), m); break; case iro_Mul: nn = new_rd_Mul(dbg, block, copy_const_value(dbg, get_Mul_left(n), block), copy_const_value(dbg, get_Mul_right(n), block), m); break; case iro_And: nn = new_rd_And(dbg, block, copy_const_value(dbg, get_And_left(n), block), copy_const_value(dbg, get_And_right(n), block), m); break; case iro_Or: nn = new_rd_Or(dbg, block, copy_const_value(dbg, get_Or_left(n), block), copy_const_value(dbg, get_Or_right(n), block), m); break; case iro_Eor: nn = new_rd_Eor(dbg, block, copy_const_value(dbg, get_Eor_left(n), block), copy_const_value(dbg, get_Eor_right(n), block), m); break; case iro_Conv: nn = new_rd_Conv(dbg, block, copy_const_value(dbg, get_Conv_op(n), block), m); break; case iro_Minus: nn = new_rd_Minus(dbg, block, copy_const_value(dbg, get_Minus_op(n), block), m); break; case iro_Not: nn = new_rd_Not(dbg, block, copy_const_value(dbg, get_Not_op(n), block), m); break; case iro_Unknown: nn = new_r_Unknown(irg, m); break; default: panic("opcode invalid or not implemented %+F", n); } return nn; }
/** * Lower a Sel node. Do not touch Sels accessing entities on the frame type. */ static void lower_sel(ir_node *sel) { ir_graph *irg = get_irn_irg(sel); ir_entity *ent = get_Sel_entity(sel); ir_type *owner = get_entity_owner(ent); dbg_info *dbg = get_irn_dbg_info(sel); ir_mode *mode = get_irn_mode(sel); ir_node *bl = get_nodes_block(sel); ir_node *newn; /* we can only replace Sels when the layout of the owner type is decided. */ if (get_type_state(owner) != layout_fixed) return; if (0 < get_Sel_n_indexs(sel)) { /* an Array access */ ir_type *basetyp = get_entity_type(ent); ir_mode *basemode; ir_node *index; if (is_Primitive_type(basetyp)) basemode = get_type_mode(basetyp); else basemode = mode_P_data; assert(basemode && "no mode for lowering Sel"); assert((get_mode_size_bits(basemode) % 8 == 0) && "can not deal with unorthodox modes"); index = get_Sel_index(sel, 0); if (is_Array_type(owner)) { ir_type *arr_ty = owner; size_t dims = get_array_n_dimensions(arr_ty); size_t *map = ALLOCAN(size_t, dims); ir_mode *mode_Int = get_reference_mode_signed_eq(mode); ir_tarval *tv; ir_node *last_size; size_t i; assert(dims == (size_t)get_Sel_n_indexs(sel) && "array dimension must match number of indices of Sel node"); for (i = 0; i < dims; i++) { size_t order = get_array_order(arr_ty, i); assert(order < dims && "order of a dimension must be smaller than the arrays dim"); map[order] = i; } newn = get_Sel_ptr(sel); /* Size of the array element */ tv = new_tarval_from_long(get_type_size_bytes(basetyp), mode_Int); last_size = new_rd_Const(dbg, irg, tv); /* * We compute the offset part of dimension d_i recursively * with the the offset part of dimension d_{i-1} * * off_0 = sizeof(array_element_type); * off_i = (u_i - l_i) * off_{i-1} ; i >= 1 * * whereas u_i is the upper bound of the current dimension * and l_i the lower bound of the current dimension. */ for (i = dims; i > 0;) { size_t dim = map[--i]; ir_node *lb, *ub, *elms, *n, *ind; elms = NULL; lb = get_array_lower_bound(arr_ty, dim); ub = get_array_upper_bound(arr_ty, dim); if (! is_Unknown(lb)) lb = new_rd_Conv(dbg, bl, copy_const_value(get_irn_dbg_info(sel), lb, bl), mode_Int); else lb = NULL; if (! is_Unknown(ub)) ub = new_rd_Conv(dbg, bl, copy_const_value(get_irn_dbg_info(sel), ub, bl), mode_Int); else ub = NULL; /* * If the array has more than one dimension, lower and upper * bounds have to be set in the non-last dimension. */ if (i > 0) { assert(lb != NULL && "lower bound has to be set in multi-dim array"); assert(ub != NULL && "upper bound has to be set in multi-dim array"); /* Elements in one Dimension */ elms = new_rd_Sub(dbg, bl, ub, lb, mode_Int); } ind = new_rd_Conv(dbg, bl, get_Sel_index(sel, dim), mode_Int); /* * Normalize index, id lower bound is set, also assume * lower bound == 0 */ if (lb != NULL) ind = new_rd_Sub(dbg, bl, ind, lb, mode_Int); n = new_rd_Mul(dbg, bl, ind, last_size, mode_Int); /* * see comment above. */ if (i > 0) last_size = new_rd_Mul(dbg, bl, last_size, elms, mode_Int); newn = new_rd_Add(dbg, bl, newn, n, mode); } } else { /* no array type */ ir_mode *idx_mode = get_irn_mode(index); ir_tarval *tv = new_tarval_from_long(get_mode_size_bytes(basemode), idx_mode); newn = new_rd_Add(dbg, bl, get_Sel_ptr(sel), new_rd_Mul(dbg, bl, index, new_r_Const(irg, tv), idx_mode), mode); } } else if (is_Method_type(get_entity_type(ent)) && is_Class_type(owner)) { /* We need an additional load when accessing methods from a dispatch * table. * Matze TODO: Is this really still used? At least liboo does its own * lowering of Method-Sels... */ ir_mode *ent_mode = get_type_mode(get_entity_type(ent)); int offset = get_entity_offset(ent); ir_mode *mode_Int = get_reference_mode_signed_eq(mode); ir_tarval *tv = new_tarval_from_long(offset, mode_Int); ir_node *cnst = new_rd_Const(dbg, irg, tv); ir_node *add = new_rd_Add(dbg, bl, get_Sel_ptr(sel), cnst, mode); ir_node *mem = get_Sel_mem(sel); newn = new_rd_Load(dbg, bl, mem, add, ent_mode, cons_none); newn = new_r_Proj(newn, ent_mode, pn_Load_res); } else { int offset = get_entity_offset(ent); /* replace Sel by add(obj, const(ent.offset)) */ newn = get_Sel_ptr(sel); if (offset != 0) { ir_mode *mode_UInt = get_reference_mode_unsigned_eq(mode); ir_tarval *tv = new_tarval_from_long(offset, mode_UInt); ir_node *cnst = new_r_Const(irg, tv); newn = new_rd_Add(dbg, bl, newn, cnst, mode); } } /* run the hooks */ hook_lower(sel); exchange(sel, newn); }
ir_node *new_rd_Const_long(dbg_info *db, ir_graph *irg, ir_mode *mode, long value) { return new_rd_Const(db, irg, new_tarval_from_long(value, mode)); }