lower_softfloat.c 23.6 KB
Newer Older
1
2
/*
 * This file is part of libFirm.
3
 * Copyright (C) 2012 University of Karlsruhe.
4
5
6
7
8
9
10
11
12
13
14
 */

/**
 * @file
 * @brief   Lower floating point operations to function calls
 * @author  Sebastian Buchwald
 */
#include <stdbool.h>

#include "be.h"
#include "dbginfo_t.h"
Matthias Braun's avatar
Matthias Braun committed
15
#include "panic.h"
16
#include "ircons_t.h"
17
18
19
20
21
#include "iredges.h"
#include "irgmod.h"
#include "irnodeset.h"
#include "irgwalk.h"
#include "irmode.h"
22
#include "irop_t.h"
23
#include "iropt_dbg.h"
24
#include "iroptimize.h"
25
26
27
28
29
30
31
#include "irprog_t.h"
#include "lower_softfloat.h"
#include "lowering.h"
#include "pmap.h"
#include "type_t.h"
#include "tv_t.h"

32
typedef bool (*lower_softfloat_func)(ir_node *node);
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

static ir_type *binop_tp_d;
static ir_type *binop_tp_f;
static ir_type *cmp_tp_d;
static ir_type *cmp_tp_f;
static ir_type *unop_tp_d;
static ir_type *unop_tp_f;
static ir_type *unop_tp_d_f;
static ir_type *unop_tp_d_is;
static ir_type *unop_tp_d_iu;
static ir_type *unop_tp_d_ls;
static ir_type *unop_tp_d_lu;
static ir_type *unop_tp_f_d;
static ir_type *unop_tp_f_is;
static ir_type *unop_tp_f_iu;
static ir_type *unop_tp_f_ls;
static ir_type *unop_tp_f_lu;
static ir_type *unop_tp_is_d;
static ir_type *unop_tp_is_f;
static ir_type *unop_tp_iu_d;
static ir_type *unop_tp_iu_f;
static ir_type *unop_tp_ls_d;
static ir_type *unop_tp_ls_f;
static ir_type *unop_tp_lu_d;
static ir_type *unop_tp_lu_f;

/** A map from a method type to its lowered type. */
static pmap *lowered_type;

62
static ir_nodeset_t created_mux_nodes;
63
64
65
66
67
68

/**
 * @return The lowered (floating point) mode.
 */
static ir_mode *get_lowered_mode(ir_mode *mode)
{
Matthias Braun's avatar
Matthias Braun committed
69
	if (!mode_is_float(mode))
70
71
72
73
74
75
76
		return mode;

	if (mode == mode_F)
		return mode_Iu;
	else if (mode == mode_D)
		return mode_Lu;

77
	panic("unsupported floating point type");
78
79
80
81
82
83
84
}

/**
 * Adapts the mode of the given node.
 */
static void lower_mode(ir_node *n, void *env)
{
Matthias Braun's avatar
Matthias Braun committed
85
	(void)env;
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
	ir_op                *op         = get_irn_op(n);
	lower_softfloat_func  lower_func = (lower_softfloat_func) op->ops.generic;
	ir_mode              *mode       = get_irn_mode(n);
	if (lower_func != NULL) {
		lower_func(n);
		return;
	}

	set_irn_mode(n, get_lowered_mode(mode));
}

/**
 * Wrapper for specific lower function.
 */
static void lower_node(ir_node *n, void *env)
{
	ir_op                *op         = get_irn_op(n);
	lower_softfloat_func  lower_func = (lower_softfloat_func) op->ops.generic;
104
105
106
107
	if (lower_func != NULL) {
		bool *changed = (bool*)env;
		*changed |= lower_func(n);
	}
108
109
110
111
112
113
114
115
116
117
}

/**
 * @return The type of the function replacing the given node.
 */
static ir_type *get_softfloat_type(const ir_node *n)
{
	ir_node *operand      = get_irn_n(n, 0);
	ir_mode *operand_mode = get_irn_mode(operand);

Christoph Mallon's avatar
Christoph Mallon committed
118
	switch (get_irn_opcode(n)) {
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
	case iro_Div:
		operand_mode = get_irn_mode(get_Div_left(n));
		/* fall through */
	case iro_Add:
	case iro_Mul:
	case iro_Sub:
		if (operand_mode == mode_F)
			return binop_tp_f;
		else if (operand_mode == mode_D)
			return binop_tp_d;
		break;
	case iro_Cmp:
		if (operand_mode == mode_F)
			return cmp_tp_f;
		else if (operand_mode == mode_D)
			return cmp_tp_d;
		break;
Christoph Mallon's avatar
Christoph Mallon committed
136
137
138

	case iro_Conv: {
		ir_mode *const mode = get_irn_mode(n);
139
140
141
		if (operand_mode == mode_D) {
			if (mode == mode_F)
				return unop_tp_d_f;
142
143
144
145
146
147
			else if (get_mode_arithmetic(mode) == irma_twos_complement) {
				if (get_mode_size_bits(mode) <= 32)
					return mode_is_signed(mode) ? unop_tp_d_is : unop_tp_d_iu;
				else if (get_mode_size_bits(mode) == 64)
					return mode_is_signed(mode) ? unop_tp_d_ls : unop_tp_d_lu;
			}
Christoph Mallon's avatar
Christoph Mallon committed
148
		} else if (operand_mode == mode_F) {
149
150
			if (mode == mode_D)
				return unop_tp_f_d;
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
			else if (get_mode_arithmetic(mode) == irma_twos_complement) {
				if (get_mode_size_bits(mode) <= 32)
					return mode_is_signed(mode) ? unop_tp_f_is : unop_tp_f_iu;
				else if (get_mode_size_bits(mode) == 64)
					return mode_is_signed(mode) ? unop_tp_f_ls : unop_tp_f_lu;
			}
		} else if (get_mode_arithmetic(operand_mode) == irma_twos_complement) {
			if (mode_is_signed(operand_mode)) {
				if (get_mode_size_bits(operand_mode) <= 32) {
					if (mode == mode_D)
						return unop_tp_is_d;
					else if (mode == mode_F)
						return unop_tp_is_f;
				} else if (get_mode_size_bits(operand_mode) == 64) {
					if (mode == mode_D)
						return unop_tp_ls_d;
					else if (mode == mode_F)
						return unop_tp_ls_f;
				}
			} else {
				if (get_mode_size_bits(operand_mode) <= 32) {
					if (mode == mode_D)
						return unop_tp_iu_d;
					else if (mode == mode_F)
						return unop_tp_iu_f;
				} else if (get_mode_size_bits(operand_mode) == 64) {
					if (mode == mode_D)
						return unop_tp_lu_d;
					else if (mode == mode_F)
						return unop_tp_lu_f;
				}
			}
183
184
		}
		break;
Christoph Mallon's avatar
Christoph Mallon committed
185
186
	}

187
188
189
190
191
192
193
194
195
	case iro_Minus:
		if (operand_mode == mode_F)
			return unop_tp_f;
		else if (operand_mode == mode_D)
			return unop_tp_d;
		break;
	default: break;
	}

196
	panic("could not determine a suitable type");
197
198
199
}

/**
200
 * @return An Address representing the function that replaces the given node.
201
 */
Christoph Mallon's avatar
Christoph Mallon committed
202
static ir_node *create_softfloat_address(const ir_node *n, const char *name)
203
{
Christoph Mallon's avatar
Christoph Mallon committed
204
	ir_type *const method = get_softfloat_type(n);
205
206

	/* Parameter types. */
Christoph Mallon's avatar
Christoph Mallon committed
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
	char const *first_param  = "";
	char const *second_param = "";
	unsigned    float_types  = 0;
	unsigned    double_types = 0;
	switch (get_method_n_params(method)) {
	case 2: {
		ir_type *const param_type = get_method_param_type(method, 1);
		ir_mode *const mode       = get_type_mode(param_type);
		if (mode == mode_F) {
			second_param = "sf";
			float_types++;
		} else if (mode == mode_D) {
			second_param = "df";
			double_types++;
		} else if (mode == mode_Iu || mode == mode_Is) {
			second_param = "si";
		} else if (mode == mode_Lu || mode == mode_Ls) {
			second_param = "di";
225
226
		}
	}
Christoph Mallon's avatar
Christoph Mallon committed
227
228
229
230
		/* FALLTHROUGH */
	case 1: {
		ir_type *const param_type = get_method_param_type(method, 0);
		ir_mode *const mode       = get_type_mode(param_type);
231
		if (mode == mode_F) {
Christoph Mallon's avatar
Christoph Mallon committed
232
			first_param = float_types > 0 ? "" : "sf";
233
			float_types++;
Christoph Mallon's avatar
Christoph Mallon committed
234
235
		} else if (mode == mode_D) {
			first_param = double_types > 0 ? "" : "df";
236
			double_types++;
Christoph Mallon's avatar
Christoph Mallon committed
237
238
239
240
		} else if (mode == mode_Iu || mode == mode_Is) {
			first_param = "si";
		} else if (mode == mode_Lu || mode == mode_Ls) {
			first_param = "di";
241
		}
Christoph Mallon's avatar
Christoph Mallon committed
242
		break;
243
244
	}

Christoph Mallon's avatar
Christoph Mallon committed
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
	default:
		break;
	}

	/* Result type. */
	char     const *result = "";
	ir_mode *const  mode   = is_Div(n) ? get_Div_resmode(n) : get_irn_mode(n);
	if (mode == mode_F) {
		result = float_types > 0 ? "" : "sf";
		float_types++;
	} else if (mode == mode_D) {
		result = double_types > 0 ? "" : "df";
		double_types++;
	} else if (mode == mode_Iu || mode == mode_Hu || mode == mode_Bu
			|| mode == mode_Is || mode == mode_Hs || mode == mode_Bs)
		result = "si";
	else if (mode == mode_Lu || mode == mode_Ls)
		result = "di";

264
265
266
	assert(float_types <= 3);
	assert(double_types <= 3);

267
268
269
	ident *const id = float_types + double_types > 1 ?
		new_id_fmt("__%s%s%s%s%u", name, first_param, second_param, result, float_types + double_types) :
		new_id_fmt("__%s%s%s%s",   name, first_param, second_param, result);
270

Christoph Mallon's avatar
Christoph Mallon committed
271
272
	ir_graph  *const irg = get_irn_irg(n);
	ir_entity *const ent = create_compilerlib_entity(id, method);
273
	return new_r_Address(irg, ent);
274
275
}

Matthias Braun's avatar
Matthias Braun committed
276
277
278
static ir_node *make_softfloat_call(ir_node *const n, char const *const name,
                                    size_t const arity,
                                    ir_node *const *const in)
Christoph Mallon's avatar
Christoph Mallon committed
279
280
281
282
283
284
285
286
{
	dbg_info *const dbgi     = get_irn_dbg_info(n);
	ir_node  *const block    = get_nodes_block(n);
	ir_graph *const irg      = get_irn_irg(n);
	ir_node  *const nomem    = get_irg_no_mem(irg);
	ir_node  *const callee   = create_softfloat_address(n, name);
	ir_type  *const type     = get_softfloat_type(n);
	ir_mode  *const res_mode = get_type_mode(get_method_res_type(type, 0));
Matthias Braun's avatar
Matthias Braun committed
287
288
	ir_node  *const call     = new_rd_Call(dbgi, block, nomem, callee, arity,
	                                       in, type);
Christoph Mallon's avatar
Christoph Mallon committed
289
290
291
292
293
	ir_node  *const results  = new_r_Proj(call, mode_T, pn_Call_T_result);
	ir_node  *const result   = new_r_Proj(results, res_mode, 0);
	return result;
}

294
295
296
/**
 * Transforms an Add into the appropriate soft float function.
 */
297
static bool lower_Add(ir_node *const n)
298
{
Christoph Mallon's avatar
Christoph Mallon committed
299
300
	ir_mode *const mode = get_irn_mode(n);
	if (!mode_is_float(mode))
301
		return false;
302

Christoph Mallon's avatar
Christoph Mallon committed
303
304
305
306
307
	ir_node *const left   = get_Add_left(n);
	ir_node *const right  = get_Add_right(n);
	ir_node *const in[]   = { left, right };
	ir_node *const result = make_softfloat_call(n, "add", ARRAY_SIZE(in), in);
	exchange(n, result);
308
	return true;
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
}

/**
 * @return The lowered (floating point) type.
 */
static ir_type *lower_type(ir_type *tp)
{
	ir_mode *mode         = get_type_mode(tp);
	ir_mode *lowered_mode = get_lowered_mode(mode);
	return get_type_for_mode(lowered_mode);
}

/**
 * @return The lowered method type.
 */
static ir_type *lower_method_type(ir_type *mtp)
{
Christoph Mallon's avatar
Christoph Mallon committed
326
	ir_type *res = pmap_get(ir_type, lowered_type, mtp);
327
328
329
	if (res != NULL)
		return res;

Christoph Mallon's avatar
Christoph Mallon committed
330
331
	size_t const n_param = get_method_n_params(mtp);
	size_t const n_res   = get_method_n_ress(mtp);
332
333
334
	res = new_type_method(n_param, n_res);

	/* set param types and result types */
Christoph Mallon's avatar
Christoph Mallon committed
335
	for (size_t i = 0; i < n_param; ++i) {
336
337
338
339
340
341
342
343
344
		ir_type *ptp   = get_method_param_type(mtp, i);
		ir_mode *pmode = get_type_mode(ptp);

		if (pmode != NULL && mode_is_float(pmode)) {
			ptp = lower_type(ptp);
		}

		set_method_param_type(res, i, ptp);
	}
Christoph Mallon's avatar
Christoph Mallon committed
345
	for (size_t i = 0; i < n_res; ++i) {
346
347
348
349
350
351
352
353
354
355
		ir_type *rtp   = get_method_res_type(mtp, i);
		ir_mode *rmode = get_type_mode(rtp);

		if (rmode != NULL && mode_is_float(rmode)) {
			rtp = lower_type(rtp);
		}

		set_method_res_type(res, i, rtp);
	}

356
	copy_method_properties(res, mtp);
357

358
	set_higher_type(res, mtp);
359
360
361
362
363

	pmap_insert(lowered_type, mtp, res);
	return res;
}

364
static ir_type *lower_type_if_needed(ir_type *tp)
365
{
366
	bool need_lower = false;
367

Christoph Mallon's avatar
Christoph Mallon committed
368
369
	size_t const n_params = get_method_n_params(tp);
	for (size_t p = 0; p < n_params; ++p) {
370
371
		ir_type *ptp   = get_method_param_type(tp, p);
		ir_mode *pmode = get_type_mode(ptp);
Christoph Mallon's avatar
Christoph Mallon committed
372
		if (pmode && mode_is_float(pmode)) {
373
374
375
376
377
			need_lower = true;
			break;
		}
	}

Christoph Mallon's avatar
Christoph Mallon committed
378
379
	size_t const n_res = get_method_n_ress(tp);
	for (size_t i = 0; i < n_res; ++i) {
380
381
		ir_type *rtp   = get_method_res_type(tp, i);
		ir_mode *rmode = get_type_mode(rtp);
Christoph Mallon's avatar
Christoph Mallon committed
382
		if (rmode && mode_is_float(rmode)) {
383
384
385
386
387
			need_lower = true;
			break;
		}
	}

388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
	if (need_lower) {
		return lower_method_type(tp);
	} else {
		return tp;
	}
}

/**
 * Adapts the method type of a Call.
 */
static bool lower_Call(ir_node *node)
{
	ir_type *tp       = get_Call_type(node);
	ir_type *lower_tp = lower_type_if_needed(tp);
	if (lower_tp != tp) {
		set_Call_type(node, lower_tp);
	}
	return true;
}

/**
 * Adapts the method type of a va_arg Builtin.
 */
static bool lower_Builtin(ir_node *node)
{
	if (get_Builtin_kind(node) != ir_bk_va_arg)
414
		return false;
415

416
417
418
419
420
	ir_type *tp       = get_Builtin_type(node);
	ir_type *lower_tp = lower_type_if_needed(tp);
	if (lower_tp != tp) {
		set_Builtin_type(node, lower_tp);
	}
421
	return true;
422
423
424
425
426
}

/**
 * Transforms a Cmp into the appropriate soft float function.
 */
427
static bool lower_Cmp(ir_node *const n)
428
{
Christoph Mallon's avatar
Christoph Mallon committed
429
430
431
	ir_node *const left    = get_Cmp_left(n);
	ir_mode *const op_mode = get_irn_mode(left);
	if (!mode_is_float(op_mode))
432
		return false;
433

Christoph Mallon's avatar
Christoph Mallon committed
434
435
	dbg_info *const dbgi = get_irn_dbg_info(n);
	ir_graph *const irg  = get_irn_irg(n);
436
	ir_node  *const zero = new_rd_Const_null(dbgi, irg, mode_Is);
Christoph Mallon's avatar
Christoph Mallon committed
437
438
439
440
441

	char const  *name     = NULL;
	char const  *name2    = NULL;
	ir_node     *result   = NULL;
	ir_relation  relation = get_Cmp_relation(n);
442
443
	switch (relation) {
	case ir_relation_false:
Christoph Mallon's avatar
Christoph Mallon committed
444
		result = zero;
445
446
		break;
	case ir_relation_equal:
Christoph Mallon's avatar
Christoph Mallon committed
447
		name = "eq";
448
449
		break;
	case ir_relation_less:
Christoph Mallon's avatar
Christoph Mallon committed
450
		name = "lt";
451
452
		break;
	case ir_relation_greater:
Christoph Mallon's avatar
Christoph Mallon committed
453
		name = "gt";
454
455
		break;
	case ir_relation_unordered:
Christoph Mallon's avatar
Christoph Mallon committed
456
		name     = "unord";
457
458
459
		relation = ir_relation_less_greater;
		break;
	case ir_relation_less_equal:
Christoph Mallon's avatar
Christoph Mallon committed
460
		name = "le";
461
462
		break;
	case ir_relation_greater_equal:
Christoph Mallon's avatar
Christoph Mallon committed
463
		name = "ge";
464
465
		break;
	case ir_relation_less_greater:
Christoph Mallon's avatar
Christoph Mallon committed
466
467
		name  = "unord";
		name2 = "ne";
468
469
		break;
	case ir_relation_less_equal_greater:
Christoph Mallon's avatar
Christoph Mallon committed
470
		name     = "unord";
471
472
473
		relation = ir_relation_equal;
		break;
	case ir_relation_unordered_equal:
Christoph Mallon's avatar
Christoph Mallon committed
474
475
476
		name     = "unord";
		relation = ir_relation_less_greater;
		name2    = "ne";
477
478
		break;
	case ir_relation_unordered_less:
Christoph Mallon's avatar
Christoph Mallon committed
479
		name     = "ge";
480
481
482
		relation = ir_relation_less;
		break;
	case ir_relation_unordered_less_equal:
Christoph Mallon's avatar
Christoph Mallon committed
483
		name     = "gt";
484
485
486
		relation = ir_relation_less_equal;
		break;
	case ir_relation_unordered_greater:
Christoph Mallon's avatar
Christoph Mallon committed
487
		name     = "le";
488
489
490
		relation = ir_relation_greater;
		break;
	case ir_relation_unordered_greater_equal:
Christoph Mallon's avatar
Christoph Mallon committed
491
		name     = "lt";
492
493
494
		relation = ir_relation_greater_equal;
		break;
	case ir_relation_unordered_less_greater:
Christoph Mallon's avatar
Christoph Mallon committed
495
		name     = "eq";
496
497
498
		relation = ir_relation_less_greater;
		break;
	case ir_relation_true:
Christoph Mallon's avatar
Christoph Mallon committed
499
		result = zero;
500
501
502
		break;
	}

Christoph Mallon's avatar
Christoph Mallon committed
503
504
	ir_node *const block = get_nodes_block(n);
	ir_node *const right = get_Cmp_right(n);
505

Christoph Mallon's avatar
Christoph Mallon committed
506
507
508
	if (result == NULL) {
		ir_node *const in[] = { left, right };
		result = make_softfloat_call(n, name, ARRAY_SIZE(in), in);
509
510
	}

Christoph Mallon's avatar
Christoph Mallon committed
511
	ir_node *cmp = new_r_Cmp(block, result, zero, relation);
512
513

	/* We need two calls into the softfloat library */
Christoph Mallon's avatar
Christoph Mallon committed
514
515
516
517
518
519
520
	if (name2 != NULL) {
		ir_node *const in[] = { left, right };
		result   = make_softfloat_call(n, name2, ARRAY_SIZE(in), in);
		relation = get_Cmp_relation(n);

		ir_node *const mux = new_rd_Mux(dbgi, block, cmp, result, zero, mode_Is);

Matthias Braun's avatar
Matthias Braun committed
521
522
		arch_allow_ifconv_func const allow_ifconv
			= be_get_backend_param()->allow_ifconv;
Christoph Mallon's avatar
Christoph Mallon committed
523
		if (!allow_ifconv(cmp, result, zero))
524
525
526
527
528
529
			ir_nodeset_insert(&created_mux_nodes, mux);

		cmp = new_r_Cmp(block, mux, zero, relation);
	}

	exchange(n, cmp);
530
	return true;
531
532
533
534
535
}

/**
 * Adapts floating point constants.
 */
536
static bool lower_Const(ir_node *const n)
537
{
538
539
	ir_mode *mode = get_irn_mode(n);
	if (!mode_is_float(mode))
540
		return false;
541

542
	ir_tarval *float_tv     = get_Const_tarval(n);
543
	ir_mode   *lowered_mode = get_lowered_mode(mode);
544
	ir_tarval *int_tv       = tarval_bitcast(float_tv, lowered_mode);
545

546
547
	set_irn_mode(n, lowered_mode);
	set_Const_tarval(n, int_tv);
548
	return true;
549
550
551
552
553
}

/**
 * Transforms a Conv into the appropriate soft float function.
 */
554
static bool lower_Conv(ir_node *const n)
555
{
Christoph Mallon's avatar
Christoph Mallon committed
556
557
558
559
560
561
562
563
564
	dbg_info *const dbgi    = get_irn_dbg_info(n);
	ir_node  *const block   = get_nodes_block(n);
	ir_mode  *const mode    = get_irn_mode(n);
	ir_node        *op      = get_Conv_op(n);
	ir_mode        *op_mode = get_irn_mode(op);

	char const *name;
	if (!mode_is_float(mode)) {
		if (!mode_is_float(op_mode))
565
			return false;
566
		if (mode_is_signed(mode))
Christoph Mallon's avatar
Christoph Mallon committed
567
			name = "fix";
568
		else
Christoph Mallon's avatar
Christoph Mallon committed
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
			name = "fixuns";
	} else if (!mode_is_float(op_mode)) {
		ir_mode *min_mode;
		if (mode_is_signed(op_mode)) {
			name     = "float";
			min_mode = mode_Is;
		} else {
			name     = "floatun";
			min_mode = mode_Iu;
		}
		if (get_mode_size_bits(op_mode) < get_mode_size_bits(min_mode)) {
			op_mode = min_mode;
			op      = new_rd_Conv(dbgi, block, op, op_mode);
		}
	} else {
		/* Remove unnecessary Convs. */
		if (op_mode == mode) {
			exchange(n, op);
587
			return true;
Christoph Mallon's avatar
Christoph Mallon committed
588
589
590
		}
		if (get_mode_size_bits(op_mode) > get_mode_size_bits(mode))
			name = "trunc";
591
		else
Christoph Mallon's avatar
Christoph Mallon committed
592
			name = "extend";
593
594
	}

Christoph Mallon's avatar
Christoph Mallon committed
595
596
	ir_node *const in[]   = { op };
	ir_node       *result = make_softfloat_call(n, name, ARRAY_SIZE(in), in);
597
598

	/* Check whether we need a Conv for the result. */
Christoph Mallon's avatar
Christoph Mallon committed
599
600
	if (get_irn_mode(result) != mode)
		result = new_rd_Conv(dbgi, block, result, mode);
601

Christoph Mallon's avatar
Christoph Mallon committed
602
	exchange(n, result);
603
	return true;
604
605
}

606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
static bool lower_Bitcast(ir_node *const n)
{
	/* bitcast was casting float->int or int->float we can simply replace it
	 * with a conv (between integer modes) now. */
	ir_node *op       = get_Bitcast_op(n);
	ir_mode *src_mode = get_irn_mode(op);
	ir_mode *dst_mode = get_irn_mode(n);
	/* note that the predecessor may already be transformed, so it's
	 * possible that we don't see a float mode anymore. */
	if (mode_is_float(dst_mode))
		dst_mode = get_lowered_mode(dst_mode);
	ir_node *res = op;
	if (src_mode != dst_mode) {
		dbg_info *dbgi  = get_irn_dbg_info(n);
		ir_node  *block = get_nodes_block(n);
		res = new_rd_Conv(dbgi, block, op, dst_mode);
	}
	exchange(n, res);
	return true;
}

627
628
629
/**
 * Transforms a Div into the appropriate soft float function.
 */
630
static bool lower_Div(ir_node *const n)
631
{
Christoph Mallon's avatar
Christoph Mallon committed
632
633
	ir_mode *const mode = get_Div_resmode(n);
	if (!mode_is_float(mode))
634
		return false;
635

Christoph Mallon's avatar
Christoph Mallon committed
636
637
638
639
	ir_node *const left   = get_Div_left(n);
	ir_node *const right  = get_Div_right(n);
	ir_node *const in[]   = { left, right };
	ir_node *const result = make_softfloat_call(n, "div", ARRAY_SIZE(in), in);
640
	ir_node *const call   = skip_Proj(skip_Proj(result));
641
642
	set_irn_pinned(call, get_irn_pinned(n));

643
	foreach_out_edge_safe(n, edge) {
644
		ir_node *proj = get_edge_src_irn(edge);
Christoph Mallon's avatar
Christoph Mallon committed
645
		if (!is_Proj(proj))
646
647
			continue;

648
		switch ((pn_Div)get_Proj_num(proj)) {
649
650
		case pn_Div_M:
			set_Proj_pred(proj, call);
651
			set_Proj_num(proj, pn_Call_M);
Matthias Braun's avatar
Matthias Braun committed
652
			continue;
653
654
		case pn_Div_X_regular:
			set_Proj_pred(proj, call);
655
			set_Proj_num(proj, pn_Call_X_regular);
Matthias Braun's avatar
Matthias Braun committed
656
			continue;
657
658
		case pn_Div_X_except:
			set_Proj_pred(proj, call);
659
			set_Proj_num(proj, pn_Call_X_except);
Matthias Braun's avatar
Matthias Braun committed
660
			continue;
661
		case pn_Div_res:
Christoph Mallon's avatar
Christoph Mallon committed
662
			exchange(proj, result);
Matthias Braun's avatar
Matthias Braun committed
663
			continue;
664
		}
Matthias Braun's avatar
Matthias Braun committed
665
		panic("unexpected Proj number");
666
	}
667
	return true;
668
669
670
671
672
}

/**
 * Adapts the resmode of a Div.
 */
673
static bool lower_Div_mode(ir_node *n)
674
675
676
677
678
{
	ir_mode *res_mode         = get_Div_resmode(n);
	ir_mode *lowered_res_mode = get_lowered_mode(res_mode);
	ir_mode *mode             = get_irn_mode(n);
	ir_mode *lowered_mode     = get_lowered_mode(mode);
679
680
681
	if (lowered_mode == mode && lowered_res_mode == res_mode)
		return false;

682
683
	set_irn_mode(n, lowered_mode);
	set_Div_resmode(n, lowered_res_mode);
684
	return true;
685
686
687
688
689
}

/**
 * Adapts the ls_mode of a Load.
 */
690
static bool lower_Load(ir_node *n)
691
692
693
694
695
{
	ir_mode *ls_mode         = get_Load_mode(n);
	ir_mode *lowered_ls_mode = get_lowered_mode(ls_mode);
	ir_mode *mode            = get_irn_mode(n);
	ir_mode *lowered_mode    = get_lowered_mode(mode);
696
697
698
	if (ls_mode == lowered_ls_mode && mode == lowered_mode)
		return false;

699
700
	set_irn_mode(n, lowered_mode);
	set_Load_mode(n, lowered_ls_mode);
701
	return true;
702
703
704
705
706
}

/**
 * Transforms a Minus into the appropriate soft float function.
 */
707
static bool lower_Minus(ir_node *n)
708
{
Christoph Mallon's avatar
Christoph Mallon committed
709
710
	ir_mode *const mode = get_irn_mode(n);
	if (!mode_is_float(mode))
711
		return false;
712

Matthias Braun's avatar
Matthias Braun committed
713
714
715
	ir_node *const op     = get_Minus_op(n);
	ir_node *const in[]   = { op };
	ir_node *const result = make_softfloat_call(n, "neg", ARRAY_SIZE(in), in);
Christoph Mallon's avatar
Christoph Mallon committed
716
	exchange(n, result);
717
	return true;
718
719
720
721
722
}

/**
 * Transforms a Mul into the appropriate soft float function.
 */
723
static bool lower_Mul(ir_node *n)
724
{
Christoph Mallon's avatar
Christoph Mallon committed
725
726
	ir_mode *const mode = get_irn_mode(n);
	if (!mode_is_float(mode))
727
		return false;
728

Christoph Mallon's avatar
Christoph Mallon committed
729
730
731
732
733
	ir_node *const left   = get_Mul_left(n);
	ir_node *const right  = get_Mul_right(n);
	ir_node *const in[]   = { left, right };
	ir_node *const result = make_softfloat_call(n, "mul", ARRAY_SIZE(in), in);
	exchange(n, result);
734
	return true;
735
736
737
738
739
}

/**
 * Transforms a Sub into the appropriate soft float function.
 */
740
static bool lower_Sub(ir_node *n)
741
{
Christoph Mallon's avatar
Christoph Mallon committed
742
743
	ir_mode *const mode = get_irn_mode(n);
	if (!mode_is_float(mode))
744
		return false;
745

Christoph Mallon's avatar
Christoph Mallon committed
746
747
748
749
750
	ir_node *const left   = get_Sub_left(n);
	ir_node *const right  = get_Sub_right(n);
	ir_node *const in[]   = { left, right };
	ir_node *const result = make_softfloat_call(n, "sub", ARRAY_SIZE(in), in);
	exchange(n, result);
751
	return true;
752
753
754
755
756
}

/**
 * Enter a lowering function into an ir_op.
 */
Matthias Braun's avatar
Matthias Braun committed
757
758
static void ir_register_softloat_lower_function(ir_op *op,
                                                lower_softfloat_func func)
759
760
761
762
{
	op->ops.generic = (op_func)func;
}

Matthias Braun's avatar
Matthias Braun committed
763
764
static void make_binop_type(ir_type **const memoized, ir_type *const left,
                            ir_type *const right, ir_type *const res)
Christoph Mallon's avatar
Christoph Mallon committed
765
766
767
768
769
770
771
772
773
{
	if (!*memoized) {
		ir_type *const type = *memoized = new_type_method(2, 1);
		set_method_param_type(type, 0, left);
		set_method_param_type(type, 1, right);
		set_method_res_type(  type, 0, res);
	}
}

Matthias Braun's avatar
Matthias Braun committed
774
775
static void make_unop_type(ir_type **const memoized, ir_type *const op,
                           ir_type *const res)
Christoph Mallon's avatar
Christoph Mallon committed
776
777
778
779
780
781
782
783
{
	if (!*memoized) {
		ir_type *const type = *memoized = new_type_method(1, 1);
		set_method_param_type(type, 0, op);
		set_method_res_type(  type, 0, res);
	}
}

784
785
786
787
788
/*
 * Initializes softfloat lowering.
 */
static void ir_prepare_softfloat_lowering(void)
{
Christoph Mallon's avatar
Christoph Mallon committed
789
	if (!lowered_type)
790
791
		lowered_type = pmap_create();

Christoph Mallon's avatar
Christoph Mallon committed
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
	ir_type *const type_D  = get_type_for_mode(mode_D);
	ir_type *const type_F  = get_type_for_mode(mode_F);
	ir_type *const type_Is = get_type_for_mode(mode_Is);
	ir_type *const type_Iu = get_type_for_mode(mode_Iu);
	ir_type *const type_Ls = get_type_for_mode(mode_Ls);
	ir_type *const type_Lu = get_type_for_mode(mode_Lu);

	make_binop_type(&binop_tp_d, type_D, type_D, type_D);
	make_binop_type(&binop_tp_f, type_F, type_F, type_F);
	make_binop_type(&cmp_tp_d,   type_D, type_D, type_Is);
	make_binop_type(&cmp_tp_f,   type_F, type_F, type_Is);

	make_unop_type(&unop_tp_d,    type_D,  type_D);
	make_unop_type(&unop_tp_f,    type_F,  type_F);
	make_unop_type(&unop_tp_d_f,  type_D,  type_F);
	make_unop_type(&unop_tp_d_is, type_D,  type_Is);
	make_unop_type(&unop_tp_d_iu, type_D,  type_Iu);
sebastian.buchwald1's avatar
sebastian.buchwald1 committed
809
810
	make_unop_type(&unop_tp_d_ls, type_D,  type_Ls);
	make_unop_type(&unop_tp_d_lu, type_D,  type_Lu);
Christoph Mallon's avatar
Christoph Mallon committed
811
812
813
814
815
816
817
818
819
820
821
822
823
	make_unop_type(&unop_tp_f_d,  type_F,  type_D);
	make_unop_type(&unop_tp_f_is, type_F,  type_Is);
	make_unop_type(&unop_tp_f_iu, type_F,  type_Iu);
	make_unop_type(&unop_tp_f_ls, type_F,  type_Ls);
	make_unop_type(&unop_tp_f_lu, type_F,  type_Lu);
	make_unop_type(&unop_tp_is_d, type_Is, type_D);
	make_unop_type(&unop_tp_is_f, type_Is, type_F);
	make_unop_type(&unop_tp_iu_d, type_Iu, type_D);
	make_unop_type(&unop_tp_iu_f, type_Iu, type_F);
	make_unop_type(&unop_tp_ls_d, type_Ls, type_D);
	make_unop_type(&unop_tp_ls_f, type_Ls, type_F);
	make_unop_type(&unop_tp_lu_d, type_Lu, type_D);
	make_unop_type(&unop_tp_lu_f, type_Lu, type_F);
824
825
826
827
828
829
830
831
832
833
834
835
836
837
}

/**
 * Callback to lower only the Mux nodes we created.
 */
static int lower_mux_cb(ir_node *mux)
{
	return ir_nodeset_contains(&created_mux_nodes, mux);
}

void lower_floating_point(void)
{
	ir_prepare_softfloat_lowering();

Matthias Braun's avatar
Matthias Braun committed
838
	ir_clear_opcodes_generic_func();
839
840
841
842
843
844
845
846
	ir_register_softloat_lower_function(op_Add,   lower_Add);
	ir_register_softloat_lower_function(op_Cmp,   lower_Cmp);
	ir_register_softloat_lower_function(op_Conv,  lower_Conv);
	ir_register_softloat_lower_function(op_Div,   lower_Div);
	ir_register_softloat_lower_function(op_Minus, lower_Minus);
	ir_register_softloat_lower_function(op_Mul,   lower_Mul);
	ir_register_softloat_lower_function(op_Sub,   lower_Sub);

847
848
	bool *const changed_irgs = XMALLOCNZ(bool, get_irp_n_irgs());
	foreach_irp_irg(i, irg) {
849
850
		ir_nodeset_init(&created_mux_nodes);

851
		assure_irg_properties(irg, IR_GRAPH_PROPERTY_CONSISTENT_OUT_EDGES);
852

853
		irg_walk_graph(irg, NULL, lower_node, &changed_irgs[i]);
854
855
856
857
858
859
860

		if (ir_nodeset_size(&created_mux_nodes) > 0)
			lower_mux(irg, lower_mux_cb);

		ir_nodeset_destroy(&created_mux_nodes);
	}

Matthias Braun's avatar
Matthias Braun committed
861
	ir_clear_opcodes_generic_func();
862
863
	ir_register_softloat_lower_function(op_Bitcast, lower_Bitcast);
	ir_register_softloat_lower_function(op_Call,    lower_Call);
864
	ir_register_softloat_lower_function(op_Builtin, lower_Builtin);
865
866
867
	ir_register_softloat_lower_function(op_Const,   lower_Const);
	ir_register_softloat_lower_function(op_Div,     lower_Div_mode);
	ir_register_softloat_lower_function(op_Load,    lower_Load);
868

869
	foreach_irp_irg(i, irg) {
Christoph Mallon's avatar
Christoph Mallon committed
870
871
872
		ir_entity *const ent         = get_irg_entity(irg);
		ir_type   *const mtp         = get_entity_type(ent);
		ir_type   *const lowered_mtp = lower_method_type(mtp);
873
874
875
		if (lowered_mtp != mtp)
			set_entity_type(ent, lowered_mtp);

876
		irg_walk_graph(irg, NULL, lower_mode, &changed_irgs[i]);
877
878

		/* fixup parameter entities */
Christoph Mallon's avatar
Christoph Mallon committed
879
880
881
882
883
		ir_type *const frame_tp  = get_irg_frame_type(irg);
		size_t   const n_members = get_compound_n_members(frame_tp);
		for (size_t j = 0; j < n_members; ++j) {
			ir_entity *const member = get_compound_member(frame_tp, j);
			ir_type   *const type   = get_entity_type(member);
884
			if (is_Primitive_type(type)) {
Christoph Mallon's avatar
Christoph Mallon committed
885
				ir_type *const lowered = lower_type(type);
886
887
888
				set_entity_type(member, lowered);
			}
		}
889
890
891

		confirm_irg_properties(irg, changed_irgs[i] ? IR_GRAPH_PROPERTIES_NONE
		                                            : IR_GRAPH_PROPERTIES_ALL);
892
	}
893
	free(changed_irgs);
894
}