bessadestr.c 13.5 KB
Newer Older
Christian Würdig's avatar
Christian Würdig committed
1
/*
Michael Beck's avatar
Michael Beck committed
2
 * Copyright (C) 1995-2008 University of Karlsruhe.  All right reserved.
Christian Würdig's avatar
Christian Würdig committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *
 * This file is part of libFirm.
 *
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation and appearing in the file LICENSE.GPL included in the
 * packaging of this file.
 *
 * Licensees holding valid libFirm Professional Edition licenses may use
 * this file in accordance with the libFirm Commercial License.
 * Agreement provided with the Software.
 *
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE.
 */

20
/**
Christian Würdig's avatar
Christian Würdig committed
21
22
23
24
25
 * @file
 * @brief       Performs SSA-Destruction.
 * @author      Daniel Grund
 * @date        25.05.2005
 * @version     $Id$
26
 */
Michael Beck's avatar
Michael Beck committed
27
#include "config.h"
28
29
30
31

#include "debug.h"
#include "set.h"
#include "pmap.h"
Sebastian Hack's avatar
Sebastian Hack committed
32
33
#include "irnode_t.h"
#include "ircons_t.h"
34
#include "iredges_t.h"
Michael Beck's avatar
Michael Beck committed
35
#include "irgwalk.h"
Sebastian Hack's avatar
Sebastian Hack committed
36
#include "irgmod.h"
Sebastian Hack's avatar
Sebastian Hack committed
37
#include "irdump.h"
Sebastian Hack's avatar
Sebastian Hack committed
38
39
#include "irprintf.h"

40
#include "be_t.h"
Sebastian Hack's avatar
Sebastian Hack committed
41
#include "beutil.h"
42
#include "bechordal_t.h"
43
#include "bearch_t.h"
Daniel Grund's avatar
Bugfix    
Daniel Grund committed
44
#include "belive_t.h"
45
46
#include "benode_t.h"
#include "besched_t.h"
47
#include "bestatevent.h"
Christian Würdig's avatar
Christian Würdig committed
48
#include "beirg_t.h"
49
#include "beintlive_t.h"
50

51
DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)
52

53
#define get_reg(irn)         arch_get_irn_register(irn)
54
#define set_reg(irn, reg)    arch_set_irn_register(irn, reg)
55

56
static void clear_link(ir_node *irn, void *data) {
57
	(void) data;
58
	set_irn_link(irn, NULL);
Sebastian Hack's avatar
Sebastian Hack committed
59
}
60

Sebastian Hack's avatar
Sebastian Hack committed
61
/**
62
63
64
65
 * For each block build a linked list of phis that
 *  - are in that block
 *  - have the current register class
 * The list is rooted at get_irn_link(BB).
Sebastian Hack's avatar
Sebastian Hack committed
66
 */
67
68
static void collect_phis_walker(ir_node *irn, void *data) {
	be_chordal_env_t *env = data;
Christian Würdig's avatar
Christian Würdig committed
69
	if (is_Phi(irn) && chordal_has_class(env, irn)) {
70
71
72
73
		ir_node *bl = get_nodes_block(irn);
		set_irn_link(irn, get_irn_link(bl));
		set_irn_link(bl, irn);
	}
Sebastian Hack's avatar
Sebastian Hack committed
74
}
75

76
77
78
79
80
81
82
83
84
85
86
87
88
/**
 * This struct represents a Proj for a Perm.
 * It records the argument in the Perm and the corresponding Proj of the
 * Perm.
 */
typedef struct {
	ir_node *arg;  /**< The phi argument to make the Proj for. */
	int pos;       /**< The proj number the Proj will get.
									 This also denotes the position of @p arg
									 in the in array of the Perm. */
	ir_node *proj; /**< The proj created for @p arg. */
} perm_proj_t;

89
static int cmp_perm_proj(const void *a, const void *b, size_t n) {
90
91
	const perm_proj_t *p = a;
	const perm_proj_t *q = b;
92
93
	(void) n;

94
95
96
	return !(p->arg == q->arg);
}

97
98
99
100
101
typedef struct insert_all_perms_env_t {
	be_chordal_env_t *chordal_env;
	pmap *perm_map;
} insert_all_perms_env_t;

102
103
104
105
/**
 * Insert Perms in all predecessors of a block containing a phi
 */
static void insert_all_perms_walker(ir_node *bl, void *data) {
106
107
108
	insert_all_perms_env_t *env = data;
	be_chordal_env_t *chordal_env = env->chordal_env;
	pmap *perm_map = env->perm_map;
109
	be_lv_t *lv = chordal_env->birg->lv;
110
	int i, n;
111

112
	assert(is_Block(bl));
113

114
115
116
	/* If the link flag is NULL, this block has no phis. */
	if(!get_irn_link(bl))
		return;
117

118
119
120
121
122
123
124
	/* Look at all predecessors of the phi block */
	for(i = 0, n = get_irn_arity(bl); i < n; ++i) {
		ir_node *phi, *perm, *insert_after, **in;
		perm_proj_t *pp;
		set *arg_set     = new_set(cmp_perm_proj, chordal_env->cls->n_regs);
		ir_node *pred_bl = get_Block_cfgpred_block(bl, i);
		int n_projs      = 0;
125

126
		assert(!pmap_contains(perm_map, pred_bl) && "Already permed that block");
127

128
129
130
131
132
		/*
		 * Note that all phis in the list are in the same
		 * register class by construction.
		 */
		for(phi = get_irn_link(bl); phi; phi = get_irn_link(phi)) {
133
134
135
136
			ir_node                   *arg = get_irn_n(phi, i);
			const arch_register_req_t *req = arch_get_register_req_out(arg);
			unsigned                   hash;
			perm_proj_t                templ;
137

138
			if (req->type & arch_register_req_type_ignore)
Christoph Mallon's avatar
Christoph Mallon committed
139
140
				continue;

141
			hash = hash_irn(arg);
142
143
			templ.arg  = arg;
			pp         = set_find(arg_set, &templ, sizeof(templ), hash);
144
145

			/*
146
147
148
149
150
			 * If a proj_perm_t entry has not been made in the argument set,
			 * create one. The only restriction is, that the phi argument
			 * may not be live in at the current block, since this argument
			 * interferes with the phi and must thus not be member of a
			 * Perm. A copy will be inserted for this argument alter on.
151
			 */
152
			if(!pp && !be_is_live_in(lv, bl, arg)) {
153
154
				templ.pos = n_projs++;
				set_insert(arg_set, &templ, sizeof(templ), hash);
155
			}
156
157
		}

158

159
		if (n_projs) {
160
			/*
161
162
			 * Create a new Perm with the arguments just collected
			 * above in the arg_set and insert it into the schedule.
163
			 */
164
			in = XMALLOCN(ir_node*, n_projs);
165
166
167
			for(pp = set_first(arg_set); pp; pp = set_next(arg_set))
				in[pp->pos] = pp->arg;

168
			perm = be_new_Perm(chordal_env->cls, pred_bl, n_projs, in);
Sebastian Hack's avatar
Sebastian Hack committed
169
			be_stat_ev("phi_perm", n_projs);
170

171
			insert_after = sched_skip(sched_last(pred_bl), 0, sched_skip_cf_predicator, NULL);
172
173
174
			sched_add_after(insert_after, perm);

			/*
175
176
177
			 * Make the Projs for the Perm and insert into schedule.
			 * Register allocation is copied from the former phi
			 * arguments to the projs (new phi arguments).
178
			 */
179
			insert_after = perm;
180
			for(pp = set_first(arg_set); pp; pp = set_next(arg_set)) {
181
				ir_node *proj = new_r_Proj(pred_bl, perm, get_irn_mode(pp->arg), pp->pos);
182
183
184
185
186
				pp->proj = proj;
				assert(get_reg(pp->arg));
				set_reg(proj, get_reg(pp->arg));
				insert_after = proj;
				DBG((dbg, LEVEL_2, "Copy register assignment %s from %+F to %+F\n", get_reg(pp->arg)->name, pp->arg, pp->proj));
187
188
189
190
191
192
193
194
195
			}

			/*
			 * Set the phi nodes to their new arguments: The Projs of the Perm
			 */
			for(phi = get_irn_link(bl); phi; phi = get_irn_link(phi)) {
				perm_proj_t templ;

				templ.arg = get_irn_n(phi, i);
196
				pp        = set_find(arg_set, &templ, sizeof(templ), hash_irn(templ.arg));
197

198
				/* If not found, it was an interfering argument */
Sebastian Hack's avatar
Sebastian Hack committed
199
				if (pp) {
200
					set_irn_n(phi, i, pp->proj);
Sebastian Hack's avatar
Sebastian Hack committed
201
202
203
204
205
206
207
208
209
					be_liveness_introduce(lv, pp->proj);
				}
			}

			/* update the liveness of the Perm's operands. It might be changed. */
			{
				int i;
				for (i = 0; i < n_projs; ++i)
					be_liveness_update(lv, in[i]);
210
			}
Sebastian Hack's avatar
Sebastian Hack committed
211
			free(in);
212
213
214

			/* register in perm map */
			pmap_insert(perm_map, pred_bl, perm);
215
		}
216

217
218
		del_set(arg_set);
	}
219
220
}

Daniel Grund's avatar
Daniel Grund committed
221
222
223
#define is_pinned(irn) (get_irn_link(irn))
#define get_pinning_block(irn) ((ir_node *)get_irn_link(irn))
#define pin_irn(irn, lock) (set_irn_link(irn, lock))
224

225
/**
226
227
 * Adjusts the register allocation for the (new) phi-operands
 * and insert duplicates iff necessary.
228
 */
229
230
static void	set_regs_or_place_dupls_walker(ir_node *bl, void *data) {
	be_chordal_env_t *chordal_env = data;
Sebastian Hack's avatar
Sebastian Hack committed
231
	be_lv_t *lv = chordal_env->birg->lv;
232
233
234
	ir_node *phi;

	/* Consider all phis of this block */
235
	for (phi = get_irn_link(bl); phi; phi = get_irn_link(phi)) {
236
237
238
239
240
		ir_node                     *phi_block = get_nodes_block(phi);
		const arch_register_t       *phi_reg   = get_reg(phi);
		const arch_register_class_t *cls       = phi_reg->reg_class;
		int                          max;
		int                          i;
241
242

		assert(is_Phi(phi) && "Can only handle phi-destruction :)");
243

244
		/* process all arguments of the phi */
245
		for (i = 0, max = get_irn_arity(phi); i < max; ++i) {
246
247
248
249
			ir_node                   *arg = get_irn_n(phi, i);
			const arch_register_req_t *req = arch_get_register_req_out(arg);
			const arch_register_t     *arg_reg;
			ir_node                   *arg_block;
250

251
			if (req->type & arch_register_req_type_ignore)
Christoph Mallon's avatar
Christoph Mallon committed
252
253
				continue;

254
255
256
			arg_block = get_Block_cfgpred_block(phi_block, i);
			arg_reg   = get_reg(arg);

257
258
259
260
			assert(arg_reg && "Register must be set while placing perms");

			DBG((dbg, LEVEL_1, "  for %+F(%s) -- %+F(%s)\n", phi, phi_reg->name, arg, arg_reg->name));

261
			if (be_values_interfere(lv, phi, arg)) {
262
263
264
265
266
267
268
				/*
					Insert a duplicate in arguments block,
					make it the new phi arg,
					set its register,
					insert it into schedule,
					pin it
				*/
269
				ir_node *dupl  = be_new_Copy(cls, arg_block, arg);
Christian Würdig's avatar
Christian Würdig committed
270
271
272

				/* this is commented out because it will fail in case of unknown float */
#if 0
Christian Würdig's avatar
Christian Würdig committed
273
274
				ir_mode *m_phi = get_irn_mode(phi), *m_dupl = get_irn_mode(dupl);

275
276
277
278
				/*
					Conv signed <-> unsigned is killed on ia32
					check for: (both int OR both float) AND equal mode sizes
				*/
Christian Würdig's avatar
Christian Würdig committed
279
280
281
				assert(((mode_is_int(m_phi) && mode_is_int(m_dupl)) ||
					(mode_is_float(m_phi) && mode_is_float(m_dupl))) &&
					(get_mode_size_bits(m_phi) == get_mode_size_bits(m_dupl)));
Christian Würdig's avatar
Christian Würdig committed
282
#endif /* if 0 */
Christian Würdig's avatar
Christian Würdig committed
283

284
285
				set_irn_n(phi, i, dupl);
				set_reg(dupl, phi_reg);
286
				sched_add_after(sched_skip(sched_last(arg_block), 0, sched_skip_cf_predicator, NULL), dupl);
287
				pin_irn(dupl, phi_block);
Sebastian Hack's avatar
Sebastian Hack committed
288
289
				be_liveness_introduce(lv, dupl);
				be_liveness_update(lv, arg);
290
				DBG((dbg, LEVEL_1, "    they do interfere: insert %+F(%s)\n", dupl, get_reg(dupl)->name));
Daniel Grund's avatar
Daniel Grund committed
291
292
293
294
				continue; /* with next argument */
			}

			if (phi_reg == arg_reg) {
295
				/* Phi and arg have the same register, so pin and continue */
Daniel Grund's avatar
Daniel Grund committed
296
297
298
299
				pin_irn(arg, phi_block);
				DBG((dbg, LEVEL_1, "      arg has same reg: pin %+F(%s)\n", arg, get_reg(arg)->name));
				continue;
			}
300

Daniel Grund's avatar
Daniel Grund committed
301
302
303
			DBG((dbg, LEVEL_1, "    they do not interfere\n"));
			assert(is_Proj(arg));
			/*
304
305
306
307
308
309
310
311
				First check if there is an other phi
				- in the same block
				- having arg at the current pos in its arg-list
				- having the same color as arg

				If found, then pin the arg (for that phi)
			*/
			if (! is_pinned(arg)) {
Daniel Grund's avatar
Daniel Grund committed
312
				ir_node *other_phi;
313

Daniel Grund's avatar
Daniel Grund committed
314
				DBG((dbg, LEVEL_1, "      searching for phi with same arg having args register\n"));
315

Daniel Grund's avatar
Daniel Grund committed
316
				for(other_phi = get_irn_link(phi_block); other_phi; other_phi = get_irn_link(other_phi)) {
317
318
319
320
321

					assert(is_Phi(other_phi)                               &&
						get_nodes_block(phi) == get_nodes_block(other_phi) &&
						"link fields are screwed up");

Daniel Grund's avatar
Daniel Grund committed
322
323
					if (get_irn_n(other_phi, i) == arg && get_reg(other_phi) == arg_reg) {
						DBG((dbg, LEVEL_1, "        found %+F(%s)\n", other_phi, get_reg(other_phi)->name));
Christian Würdig's avatar
Christian Würdig committed
324
						pin_irn(arg, phi_block);
Daniel Grund's avatar
Daniel Grund committed
325
						break;
Christian Würdig's avatar
Christian Würdig committed
326
					}
327
				}
328
			}
Daniel Grund's avatar
Daniel Grund committed
329
330

			if (is_pinned(arg)) {
331
332
333
334
335
336
337
				/*
					Insert a duplicate of the original value in arguments block,
					make it the new phi arg,
					set its register,
					insert it into schedule,
					pin it
				*/
338
339
				ir_node *perm = get_Proj_pred(arg);
				ir_node *dupl = be_new_Copy(cls, arg_block, arg);
Matthias Braun's avatar
Matthias Braun committed
340
				ir_node *ins;
Christian Würdig's avatar
Christian Würdig committed
341
342
343

				/* this is commented out because it will fail in case of unknown float */
#if 0
344
345
346
347
348
349
350
351
352
353
				ir_mode *m_phi    = get_irn_mode(phi);
				ir_mode *m_dupl   = get_irn_mode(dupl);

				/*
					Conv signed <-> unsigned is killed on ia32
					check for: (both int OR both float) AND equal mode sizes
				*/
				assert(((mode_is_int(m_phi) && mode_is_int(m_dupl)) ||
					(mode_is_float(m_phi) && mode_is_float(m_dupl))) &&
					(get_mode_size_bits(m_phi) == get_mode_size_bits(m_dupl)));
Christian Würdig's avatar
Christian Würdig committed
354
#endif /* if 0 */
355

Daniel Grund's avatar
Daniel Grund committed
356
357
				set_irn_n(phi, i, dupl);
				set_reg(dupl, phi_reg);
Matthias Braun's avatar
Matthias Braun committed
358
359
360
				/* skip the Perm's Projs and insert the copies behind. */
				for(ins = sched_next(perm); is_Proj(ins); ins = sched_next(ins));
				sched_add_before(ins, dupl);
Daniel Grund's avatar
Daniel Grund committed
361
				pin_irn(dupl, phi_block);
Sebastian Hack's avatar
Sebastian Hack committed
362
363
				be_liveness_introduce(lv, dupl);
				be_liveness_update(lv, arg);
Daniel Grund's avatar
Daniel Grund committed
364
365
				DBG((dbg, LEVEL_1, "      arg is pinned: insert %+F(%s)\n", dupl, get_reg(dupl)->name));
			} else {
366
367
368
369
				/*
					No other phi has the same color (else arg would have been pinned),
					so just set the register and pin
				*/
Daniel Grund's avatar
Daniel Grund committed
370
371
372
373
				set_reg(arg, phi_reg);
				pin_irn(arg, phi_block);
				DBG((dbg, LEVEL_1, "      arg is not pinned: so pin %+F(%s)\n", arg, get_reg(arg)->name));
			}
374
375
376
377
		}
	}
}

Sebastian Hack's avatar
Sebastian Hack committed
378
void be_ssa_destruction(be_chordal_env_t *chordal_env) {
Sebastian Hack's avatar
Sebastian Hack committed
379
	insert_all_perms_env_t insert_perms_env;
Sebastian Hack's avatar
Sebastian Hack committed
380
	pmap *perm_map = pmap_create();
381
	ir_graph *irg  = chordal_env->irg;
Sebastian Hack's avatar
Sebastian Hack committed
382
	be_lv_t *lv    = be_assure_liveness(chordal_env->birg);
383

384
	FIRM_DBG_REGISTER(dbg, "ir.be.ssadestr");
385

Sebastian Hack's avatar
Sebastian Hack committed
386
	be_liveness_invalidate(lv);
387

388
	/* create a map for fast lookup of perms: block --> perm */
389
	irg_walk_graph(irg, clear_link, collect_phis_walker, chordal_env);
390

391
	DBG((dbg, LEVEL_1, "Placing perms...\n"));
392
393
394
	insert_perms_env.chordal_env = chordal_env;
	insert_perms_env.perm_map = perm_map;
	irg_block_walk_graph(irg, insert_all_perms_walker, NULL, &insert_perms_env);
395

Sebastian Hack's avatar
Sebastian Hack committed
396
397
398
399
	// Matze: really needed here?
	// Sebastian: Yes. the walker function uses interference.
	be_liveness_invalidate(lv);

400
401
	if (chordal_env->opts->dump_flags & BE_CH_DUMP_SSADESTR)
		be_dump(irg, "-ssa_destr_perms_placed", dump_ir_block_graph_sched);
402

Sebastian Hack's avatar
Sebastian Hack committed
403
	be_liveness_assure_chk(lv);
404

405
406
	DBG((dbg, LEVEL_1, "Setting regs and placing dupls...\n"));
	irg_block_walk_graph(irg, set_regs_or_place_dupls_walker, NULL, chordal_env);
407

Sebastian Hack's avatar
Sebastian Hack committed
408
409
410
	/* TODO: unfortunaltely updating doesn't work yet. */
	be_liveness_invalidate(lv);

411
412
	if (chordal_env->opts->dump_flags & BE_CH_DUMP_SSADESTR)
		be_dump(irg, "-ssa_destr_regs_set", dump_ir_block_graph_sched);
413

Sebastian Hack's avatar
Sebastian Hack committed
414
	pmap_destroy(perm_map);
415
416
}

417
static void ssa_destruction_check_walker(ir_node *bl, void *data) {
418
	ir_node *phi;
419
	int i, max;
420
	(void)data;
421

422
	for (phi = get_irn_link(bl); phi; phi = get_irn_link(phi)) {
423
424
425
426
		const arch_register_t *phi_reg, *arg_reg;

		phi_reg = get_reg(phi);
		/* iterate over all args of phi */
427
		for (i = 0, max = get_irn_arity(phi); i < max; ++i) {
428
429
			ir_node                   *arg = get_irn_n(phi, i);
			const arch_register_req_t *req = arch_get_register_req_out(arg);
430

431
			if (req->type & arch_register_req_type_ignore)
Christoph Mallon's avatar
Christoph Mallon committed
432
433
				continue;

434
			arg_reg = get_reg(arg);
435
436

			if (phi_reg != arg_reg) {
437
438
439
				DBG((dbg, 0, "Error: Registers of %+F and %+F differ: %s %s\n", phi, arg, phi_reg->name, arg_reg->name));
				assert(0);
			}
440
441

			if (! is_pinned(arg)) {
442
443
444
445
446
				DBG((dbg, 0, "Warning: Phi argument %+F is not pinned.\n", arg));
				assert(0);
			}
		}
	}
Sebastian Hack's avatar
Sebastian Hack committed
447
448
449
}

void be_ssa_destruction_check(be_chordal_env_t *chordal_env) {
450
	irg_block_walk_graph(chordal_env->irg, ssa_destruction_check_walker, NULL, NULL);
451
}