summaryrefslogtreecommitdiffhomepage
path: root/ir/be/ia32/ia32_finish.c
blob: 1632bba6750fa7cca324fa20f7a2a17d2c1cec8a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
/*
 * This file is part of libFirm.
 * Copyright (C) 2012 University of Karlsruhe.
 */

/**
 * @file
 * @brief   This file implements functions to finalize the irg for emit.
 * @author  Christian Wuerdig
 */
#include "be2addr.h"
#include "bearch.h"
#include "besched.h"
#include "gen_ia32_regalloc_if.h"
#include "ia32_bearch_t.h"
#include "ia32_new_nodes.h"
#include "ia32_transform.h"
#include "iredges_t.h"
#include "irgmod.h"
#include "irgwalk.h"
#include "irnode_t.h"
#include "panic.h"

static bool reads_carry(x86_condition_code_t code)
{
	x86_condition_code_t c2 = code & ~x86_cc_negated;
	return c2 == x86_cc_below || c2 == x86_cc_below_equal
	    || c2 == x86_cc_float_below || c2 == x86_cc_float_below_equal
	    || c2 == x86_cc_float_unordered_below_equal
	    || c2 == x86_cc_float_unordered_below;
}

/**
 * After register allocation, transforms a Sub or xSub into Neg--Add iff
 *   OUT_REG != SRC1_REG && OUT_REG == SRC2_REG.
 */
static bool ia32_transform_sub_to_neg_add(ir_node *const irn,
                                          arch_register_t const *const out_reg)
{
	/* in case of sub and OUT == SRC2 we can transform the sequence into neg
	 * src2 -- add */
	ir_node *const in2 = get_irn_n(irn, n_ia32_binary_right);
	if (out_reg != arch_get_irn_register(in2))
		return false;

	dbg_info *const dbgi  = get_irn_dbg_info(irn);
	ir_node  *const block = get_nodes_block(irn);
	ir_graph *const irg   = get_irn_irg(irn);
	ir_node  *const noreg = ia32_new_NoReg_gp(irg);
	ir_node  *const nomem = get_irg_no_mem(irg);
	ir_node  *const in1   = get_irn_n(irn, n_ia32_binary_left);

	/* generate the neg src2 */
	ir_node *res;
	if (is_ia32_Subs(irn)) {
		x86_insn_size_t const size = get_ia32_attr_const(irn)->size;
		assert(get_irn_mode(irn) != mode_T);

		ir_node *const noreg_fp = ia32_new_NoReg_xmm(irg);
		res = new_bd_ia32_Xorp(dbgi, block, noreg, noreg, nomem, in2, noreg_fp,
		                       size);
		ir_entity *entity = ia32_gen_fp_known_const(size == X86_SIZE_32
		                                            ? ia32_SSIGN : ia32_DSIGN);
		ia32_attr_t *const attr = get_ia32_attr(res);
		attr->addr.immediate.entity = entity;
		attr->addr.variant          = X86_ADDR_JUST_IMM;
		set_ia32_op_type(res, ia32_AddrModeS);

		arch_set_irn_register(res, out_reg);

		/* add to schedule */
		sched_add_before(irn, res);

		/* generate the add */
		res = new_bd_ia32_Adds(dbgi, block, noreg, noreg, nomem, res, in1,
		                       size);
	} else {
		ir_node *flags_proj  = NULL;
		bool     needs_carry = false;
		/** See if someone is interested in a correctly set carry flag */
		if (get_irn_mode(irn) == mode_T) {
			flags_proj = get_Proj_for_pn(irn, pn_ia32_flags);
			if (flags_proj) {
				foreach_out_edge(flags_proj, edge) {
					ir_node *user = get_edge_src_irn(edge);
					x86_condition_code_t cc = get_ia32_condcode(user);
					if (reads_carry(cc)) {
						needs_carry = true;
						break;
					}
				}
			}
		}

		ir_node *carry;
		if (is_ia32_Sbb(irn)) {
			/* Feed borrow (in CF) as carry (via CMC) into NOT+ADC. */
			carry = get_irn_n(irn, n_ia32_Sbb_eflags);
			carry = new_bd_ia32_Cmc(dbgi, block, carry);
			goto carry;
		} else if (flags_proj != NULL && needs_carry) {
			/*
			 * ARG, the above technique does NOT set the flags right.
			 * So, we must produce the following code:
			 * t1 = ~b
			 * t2 = a + ~b + Carry
			 * Complement Carry
			 *
			 * a + -b = a + (~b + 1)  would set the carry flag wrong IFF both a and b are zero.
			 */
			carry = new_bd_ia32_Stc(dbgi, block);

carry:;
			ir_node *nnot = new_bd_ia32_Not(dbgi, block, in2, X86_SIZE_32);
			arch_set_irn_register(nnot, out_reg);
			sched_add_before(irn, nnot);

			arch_set_irn_register(carry, &ia32_registers[REG_EFLAGS]);
			sched_add_before(irn, carry);

			ir_node *adc = new_bd_ia32_Adc(dbgi, block, noreg, noreg, nomem,
			                               nnot, in1, carry, X86_SIZE_32);
			arch_set_irn_register(adc, out_reg);
			set_ia32_commutative(adc);

			if (flags_proj != NULL) {
				set_irn_mode(adc, mode_T);
				arch_register_t const *const reg_flags = &ia32_registers[REG_EFLAGS];
				ir_node               *const adc_flags = be_new_Proj_reg(adc, pn_ia32_Adc_flags, reg_flags);

				ir_node *cmc = new_bd_ia32_Cmc(dbgi, block, adc_flags);
				arch_set_irn_register(cmc, reg_flags);
				sched_add_after(irn, cmc);
				exchange(flags_proj, cmc);
			}

			res = adc;
		} else {
			res = new_bd_ia32_Neg(dbgi, block, in2, X86_SIZE_32);
			arch_set_irn_register(res, out_reg);

			/* add to schedule */
			sched_add_before(irn, res);

			/* generate the add */
			res = new_bd_ia32_Add(dbgi, block, noreg, noreg, nomem, res, in1,
			                      X86_SIZE_32);
			arch_set_irn_register_out(res, pn_ia32_res,   out_reg);
			arch_set_irn_register_out(res, pn_ia32_flags, &ia32_registers[REG_EFLAGS]);
			set_ia32_commutative(res);
		}
	}

	set_irn_mode(res, get_irn_mode(irn));

	/* exchange the add and the sub */
	sched_replace(irn, res);
	exchange(irn, res);
	return true;
}

static bool ia32_transform_ShlD_to_ShrD_imm(ir_node *const irn,
                                            arch_register_t const *const out_reg)
{
	ir_node *const in1 = get_irn_n(irn, n_ia32_ShlD_val_low);
	if (arch_get_irn_register(in1) != out_reg)
		return false;

	/* a = ShlD(b, a, c) -> a = ShrD(a, b, 32 - c) */
	ir_node                     *const lcount = get_irn_n(irn, n_ia32_ShlD_count);
	ia32_immediate_attr_t const *const attr   = get_ia32_immediate_attr_const(lcount);
	ir_graph                    *const irg    = get_irn_irg(irn);
	ir_node                     *const count  = ia32_create_Immediate(irg, 32 - attr->imm.offset);
	dbg_info                    *const dbgi   = get_irn_dbg_info(irn);
	ir_node                     *const block  = get_nodes_block(irn);
	ir_node                     *const in0    = get_irn_n(irn, n_ia32_ShlD_val_high);
	ir_node                     *const res    = new_bd_ia32_ShrD_imm(dbgi, block, in1, in0, count);
	arch_set_irn_register_out(res, pn_ia32_ShrD_res, out_reg);
	sched_replace(irn, res);
	exchange(irn, res);
	return true;
}

/**
 * Following Problem:
 * We have a source address mode node with base or index register equal to
 * result register and unfulfilled should_be_same requirement. The constraint
 * handler will insert a copy from the remaining input operand to the result
 * register -> base or index is broken then.
 * Solution: Turn back this address mode into explicit Load + Operation.
 */
static void fix_am_source(ir_node *const irn, arch_register_t const *const out_reg)
{
	/* Only need to fix operations with source address mode. */
	if (get_ia32_op_type(irn) != ia32_AddrModeS)
		return;
	/* Only need to fix if the out reg is the same as base or index register. */
	if (out_reg != arch_get_irn_register_in(irn, n_ia32_base) &&
	    out_reg != arch_get_irn_register_in(irn, n_ia32_index))
		return;

	ir_node *const load_res = ia32_turn_back_am(irn);
	arch_set_irn_register(load_res, out_reg);
}

static bool ia32_handle_2addr(ir_node *const node, arch_register_req_t const *const req, arch_register_t const *const reg)
{
	/* Some nodes are just a bit less efficient, but need no fixing if the
	 * should_be_same requirement is not fulfilled. */
	if (is_ia32_Lea(node) || is_ia32_Minus64(node))
		return true;
	fix_am_source(node, reg);
	if (req->should_be_same == (1U << n_ia32_binary_left | 1U << n_ia32_binary_right)) {
		if (reg == arch_get_irn_register_in(node, n_ia32_binary_right)) {
			ia32_swap_left_right(node);
			return true;
		}
	} else if (is_ia32_ShlD(node)) {
		return ia32_transform_ShlD_to_ShrD_imm(node, reg);
	} else if (is_ia32_Sub(node) || is_ia32_Sbb(node) || is_ia32_Subs(node)) {
		return ia32_transform_sub_to_neg_add(node, reg);
	}
	return false;
}

/**
 * Add Copy nodes for not fulfilled should_be_equal constraints
 */
void ia32_finish_irg(ir_graph *irg)
{
	be_handle_2addr(irg, &ia32_handle_2addr);
}