lower_softfloat.c 23.3 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
22
#include "iredges.h"
#include "irgmod.h"
#include "irnodeset.h"
#include "irgwalk.h"
#include "irmode.h"
#include "iropt_dbg.h"
23
#include "iroptimize.h"
24
25
26
27
28
29
30
#include "irprog_t.h"
#include "lower_softfloat.h"
#include "lowering.h"
#include "pmap.h"
#include "type_t.h"
#include "tv_t.h"

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

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;

61
static ir_nodeset_t created_mux_nodes;
62
63
64
65
66
67

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

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

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

/**
 * Adapts the mode of the given node.
 */
static void lower_mode(ir_node *n, void *env)
{
Matthias Braun's avatar
Matthias Braun committed
84
	(void)env;
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
	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;
103
104
105
106
	if (lower_func != NULL) {
		bool *changed = (bool*)env;
		*changed |= lower_func(n);
	}
107
108
109
110
111
112
113
114
115
116
}

/**
 * @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
117
	switch (get_irn_opcode(n)) {
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
	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
135
136
137

	case iro_Conv: {
		ir_mode *const mode = get_irn_mode(n);
138
139
140
		if (operand_mode == mode_D) {
			if (mode == mode_F)
				return unop_tp_d_f;
141
142
143
144
145
146
			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
147
		} else if (operand_mode == mode_F) {
148
149
			if (mode == mode_D)
				return unop_tp_f_d;
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
			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;
				}
			}
182
183
		}
		break;
Christoph Mallon's avatar
Christoph Mallon committed
184
185
	}

186
187
188
189
190
191
192
193
194
	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;
	}

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

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

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

Christoph Mallon's avatar
Christoph Mallon committed
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
	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";

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

266
267
268
	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);
269

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

Matthias Braun's avatar
Matthias Braun committed
275
276
277
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
278
279
280
281
282
283
284
285
{
	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
286
287
	ir_node  *const call     = new_rd_Call(dbgi, block, nomem, callee, arity,
	                                       in, type);
Christoph Mallon's avatar
Christoph Mallon committed
288
289
290
291
292
	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;
}

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

Christoph Mallon's avatar
Christoph Mallon committed
302
303
304
305
306
	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);
307
	return true;
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
}

/**
 * @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
325
	ir_type *res = pmap_get(ir_type, lowered_type, mtp);
326
327
328
	if (res != NULL)
		return res;

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

	/* set param types and result types */
Christoph Mallon's avatar
Christoph Mallon committed
334
	for (size_t i = 0; i < n_param; ++i) {
335
336
337
338
339
340
341
342
343
		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
344
	for (size_t i = 0; i < n_res; ++i) {
345
346
347
348
349
350
351
352
353
354
355
356
357
358
		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);
	}

	set_method_variadicity(res, get_method_variadicity(mtp));
	set_method_calling_convention(res, get_method_calling_convention(mtp));
	set_method_additional_properties(res, get_method_additional_properties(mtp));

359
	set_higher_type(res, mtp);
360
361
362
363
364
365
366
367

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

/**
 * Adapts the method type of a Call.
 */
368
static bool lower_Call(ir_node *node)
369
370
{
	bool     need_lower = false;
Christoph Mallon's avatar
Christoph Mallon committed
371
	ir_type *tp         = get_Call_type(node);
372

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

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

Christoph Mallon's avatar
Christoph Mallon committed
393
	if (!need_lower)
394
		return false;
395
396
397

	tp = lower_method_type(tp);
	set_Call_type(node, tp);
398
	return true;
399
400
401
402
403
}

/**
 * Transforms a Cmp into the appropriate soft float function.
 */
404
static bool lower_Cmp(ir_node *const n)
405
{
Christoph Mallon's avatar
Christoph Mallon committed
406
407
408
	ir_node *const left    = get_Cmp_left(n);
	ir_mode *const op_mode = get_irn_mode(left);
	if (!mode_is_float(op_mode))
409
		return false;
410

Christoph Mallon's avatar
Christoph Mallon committed
411
412
	dbg_info *const dbgi = get_irn_dbg_info(n);
	ir_graph *const irg  = get_irn_irg(n);
413
	ir_node  *const zero = new_rd_Const_null(dbgi, irg, mode_Is);
Christoph Mallon's avatar
Christoph Mallon committed
414
415
416
417
418

	char const  *name     = NULL;
	char const  *name2    = NULL;
	ir_node     *result   = NULL;
	ir_relation  relation = get_Cmp_relation(n);
419
420
	switch (relation) {
	case ir_relation_false:
Christoph Mallon's avatar
Christoph Mallon committed
421
		result = zero;
422
423
		break;
	case ir_relation_equal:
Christoph Mallon's avatar
Christoph Mallon committed
424
		name = "eq";
425
426
		break;
	case ir_relation_less:
Christoph Mallon's avatar
Christoph Mallon committed
427
		name = "lt";
428
429
		break;
	case ir_relation_greater:
Christoph Mallon's avatar
Christoph Mallon committed
430
		name = "gt";
431
432
		break;
	case ir_relation_unordered:
Christoph Mallon's avatar
Christoph Mallon committed
433
		name     = "unord";
434
435
436
		relation = ir_relation_less_greater;
		break;
	case ir_relation_less_equal:
Christoph Mallon's avatar
Christoph Mallon committed
437
		name = "le";
438
439
		break;
	case ir_relation_greater_equal:
Christoph Mallon's avatar
Christoph Mallon committed
440
		name = "ge";
441
442
		break;
	case ir_relation_less_greater:
Christoph Mallon's avatar
Christoph Mallon committed
443
444
		name  = "unord";
		name2 = "ne";
445
446
		break;
	case ir_relation_less_equal_greater:
Christoph Mallon's avatar
Christoph Mallon committed
447
		name     = "unord";
448
449
450
		relation = ir_relation_equal;
		break;
	case ir_relation_unordered_equal:
Christoph Mallon's avatar
Christoph Mallon committed
451
452
453
		name     = "unord";
		relation = ir_relation_less_greater;
		name2    = "ne";
454
455
		break;
	case ir_relation_unordered_less:
Christoph Mallon's avatar
Christoph Mallon committed
456
		name     = "ge";
457
458
459
		relation = ir_relation_less;
		break;
	case ir_relation_unordered_less_equal:
Christoph Mallon's avatar
Christoph Mallon committed
460
		name     = "gt";
461
462
463
		relation = ir_relation_less_equal;
		break;
	case ir_relation_unordered_greater:
Christoph Mallon's avatar
Christoph Mallon committed
464
		name     = "le";
465
466
467
		relation = ir_relation_greater;
		break;
	case ir_relation_unordered_greater_equal:
Christoph Mallon's avatar
Christoph Mallon committed
468
		name     = "lt";
469
470
471
		relation = ir_relation_greater_equal;
		break;
	case ir_relation_unordered_less_greater:
Christoph Mallon's avatar
Christoph Mallon committed
472
		name     = "eq";
473
474
475
		relation = ir_relation_less_greater;
		break;
	case ir_relation_true:
Christoph Mallon's avatar
Christoph Mallon committed
476
		result = zero;
477
478
479
		break;
	}

Christoph Mallon's avatar
Christoph Mallon committed
480
481
	ir_node *const block = get_nodes_block(n);
	ir_node *const right = get_Cmp_right(n);
482

Christoph Mallon's avatar
Christoph Mallon committed
483
484
485
	if (result == NULL) {
		ir_node *const in[] = { left, right };
		result = make_softfloat_call(n, name, ARRAY_SIZE(in), in);
486
487
	}

Christoph Mallon's avatar
Christoph Mallon committed
488
	ir_node *cmp = new_r_Cmp(block, result, zero, relation);
489
490

	/* We need two calls into the softfloat library */
Christoph Mallon's avatar
Christoph Mallon committed
491
492
493
494
495
496
497
	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
498
499
		arch_allow_ifconv_func const allow_ifconv
			= be_get_backend_param()->allow_ifconv;
Christoph Mallon's avatar
Christoph Mallon committed
500
		if (!allow_ifconv(cmp, result, zero))
501
502
503
504
505
506
			ir_nodeset_insert(&created_mux_nodes, mux);

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

	exchange(n, cmp);
507
	return true;
508
509
510
511
512
}

/**
 * Adapts floating point constants.
 */
513
static bool lower_Const(ir_node *const n)
514
{
515
516
	ir_mode *mode = get_irn_mode(n);
	if (!mode_is_float(mode))
517
		return false;
518

519
	ir_tarval *float_tv     = get_Const_tarval(n);
520
	ir_mode   *lowered_mode = get_lowered_mode(mode);
521
	ir_tarval *int_tv       = tarval_bitcast(float_tv, lowered_mode);
522

523
524
	set_irn_mode(n, lowered_mode);
	set_Const_tarval(n, int_tv);
525
	return true;
526
527
528
529
530
}

/**
 * Transforms a Conv into the appropriate soft float function.
 */
531
static bool lower_Conv(ir_node *const n)
532
{
Christoph Mallon's avatar
Christoph Mallon committed
533
534
535
536
537
538
539
540
541
	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))
542
			return false;
543
		if (mode_is_signed(mode))
Christoph Mallon's avatar
Christoph Mallon committed
544
			name = "fix";
545
		else
Christoph Mallon's avatar
Christoph Mallon committed
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
			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);
564
			return true;
Christoph Mallon's avatar
Christoph Mallon committed
565
566
567
		}
		if (get_mode_size_bits(op_mode) > get_mode_size_bits(mode))
			name = "trunc";
568
		else
Christoph Mallon's avatar
Christoph Mallon committed
569
			name = "extend";
570
571
	}

Christoph Mallon's avatar
Christoph Mallon committed
572
573
	ir_node *const in[]   = { op };
	ir_node       *result = make_softfloat_call(n, name, ARRAY_SIZE(in), in);
574
575

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

Christoph Mallon's avatar
Christoph Mallon committed
579
	exchange(n, result);
580
	return true;
581
582
}

583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
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;
}

604
605
606
/**
 * Transforms a Div into the appropriate soft float function.
 */
607
static bool lower_Div(ir_node *const n)
608
{
Christoph Mallon's avatar
Christoph Mallon committed
609
610
	ir_mode *const mode = get_Div_resmode(n);
	if (!mode_is_float(mode))
611
		return false;
612

Christoph Mallon's avatar
Christoph Mallon committed
613
614
615
616
	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);
yb9976's avatar
yb9976 committed
617
	ir_node *const call   = skip_Proj(skip_Proj(result));
618
619
	set_irn_pinned(call, get_irn_pinned(n));

620
	foreach_out_edge_safe(n, edge) {
621
		ir_node *proj = get_edge_src_irn(edge);
Christoph Mallon's avatar
Christoph Mallon committed
622
		if (!is_Proj(proj))
623
624
			continue;

625
		switch ((pn_Div)get_Proj_num(proj)) {
626
627
		case pn_Div_M:
			set_Proj_pred(proj, call);
628
			set_Proj_num(proj, pn_Call_M);
Matthias Braun's avatar
Matthias Braun committed
629
			continue;
630
631
		case pn_Div_X_regular:
			set_Proj_pred(proj, call);
632
			set_Proj_num(proj, pn_Call_X_regular);
Matthias Braun's avatar
Matthias Braun committed
633
			continue;
634
635
		case pn_Div_X_except:
			set_Proj_pred(proj, call);
636
			set_Proj_num(proj, pn_Call_X_except);
Matthias Braun's avatar
Matthias Braun committed
637
			continue;
638
		case pn_Div_res:
Christoph Mallon's avatar
Christoph Mallon committed
639
			exchange(proj, result);
Matthias Braun's avatar
Matthias Braun committed
640
			continue;
641
		}
Matthias Braun's avatar
Matthias Braun committed
642
		panic("unexpected Proj number");
643
	}
644
	return true;
645
646
647
648
649
}

/**
 * Adapts the resmode of a Div.
 */
650
static bool lower_Div_mode(ir_node *n)
651
652
653
654
655
{
	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);
656
657
658
	if (lowered_mode == mode && lowered_res_mode == res_mode)
		return false;

659
660
	set_irn_mode(n, lowered_mode);
	set_Div_resmode(n, lowered_res_mode);
661
	return true;
662
663
664
665
666
}

/**
 * Adapts the ls_mode of a Load.
 */
667
static bool lower_Load(ir_node *n)
668
669
670
671
672
{
	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);
673
674
675
	if (ls_mode == lowered_ls_mode && mode == lowered_mode)
		return false;

676
677
	set_irn_mode(n, lowered_mode);
	set_Load_mode(n, lowered_ls_mode);
678
	return true;
679
680
681
682
683
}

/**
 * Transforms a Minus into the appropriate soft float function.
 */
684
static bool lower_Minus(ir_node *n)
685
{
Christoph Mallon's avatar
Christoph Mallon committed
686
687
	ir_mode *const mode = get_irn_mode(n);
	if (!mode_is_float(mode))
688
		return false;
689

Matthias Braun's avatar
Matthias Braun committed
690
691
692
	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
693
	exchange(n, result);
694
	return true;
695
696
697
698
699
}

/**
 * Transforms a Mul into the appropriate soft float function.
 */
700
static bool lower_Mul(ir_node *n)
701
{
Christoph Mallon's avatar
Christoph Mallon committed
702
703
	ir_mode *const mode = get_irn_mode(n);
	if (!mode_is_float(mode))
704
		return false;
705

Christoph Mallon's avatar
Christoph Mallon committed
706
707
708
709
710
	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);
711
	return true;
712
713
714
715
716
}

/**
 * Transforms a Sub into the appropriate soft float function.
 */
717
static bool lower_Sub(ir_node *n)
718
{
Christoph Mallon's avatar
Christoph Mallon committed
719
720
	ir_mode *const mode = get_irn_mode(n);
	if (!mode_is_float(mode))
721
		return false;
722

Christoph Mallon's avatar
Christoph Mallon committed
723
724
725
726
727
	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);
728
	return true;
729
730
731
732
733
}

/**
 * Enter a lowering function into an ir_op.
 */
Matthias Braun's avatar
Matthias Braun committed
734
735
static void ir_register_softloat_lower_function(ir_op *op,
                                                lower_softfloat_func func)
736
737
738
739
{
	op->ops.generic = (op_func)func;
}

Matthias Braun's avatar
Matthias Braun committed
740
741
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
742
743
744
745
746
747
748
749
750
{
	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
751
752
static void make_unop_type(ir_type **const memoized, ir_type *const op,
                           ir_type *const res)
Christoph Mallon's avatar
Christoph Mallon committed
753
754
755
756
757
758
759
760
{
	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);
	}
}

761
762
763
764
765
/*
 * Initializes softfloat lowering.
 */
static void ir_prepare_softfloat_lowering(void)
{
Christoph Mallon's avatar
Christoph Mallon committed
766
	if (!lowered_type)
767
768
		lowered_type = pmap_create();

Christoph Mallon's avatar
Christoph Mallon committed
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
	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);
yb9976's avatar
yb9976 committed
786
787
	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
788
789
790
791
792
793
794
795
796
797
798
799
800
	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);
801
802
803
804
805
806
807
808
809
810
811
812
813
814
}

/**
 * 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
815
	ir_clear_opcodes_generic_func();
816
817
818
819
820
821
822
823
	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);

824
825
	bool *const changed_irgs = XMALLOCNZ(bool, get_irp_n_irgs());
	foreach_irp_irg(i, irg) {
826
827
		ir_nodeset_init(&created_mux_nodes);

828
		assure_irg_properties(irg, IR_GRAPH_PROPERTY_CONSISTENT_OUT_EDGES);
829

830
		irg_walk_graph(irg, NULL, lower_node, &changed_irgs[i]);
831
832
833
834
835
836
837

		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
838
	ir_clear_opcodes_generic_func();
839
840
841
842
843
	ir_register_softloat_lower_function(op_Bitcast, lower_Bitcast);
	ir_register_softloat_lower_function(op_Call,    lower_Call);
	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);
844

845
	foreach_irp_irg(i, irg) {
Christoph Mallon's avatar
Christoph Mallon committed
846
847
848
		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);
849
850
851
		if (lowered_mtp != mtp)
			set_entity_type(ent, lowered_mtp);

852
		irg_walk_graph(irg, NULL, lower_mode, &changed_irgs[i]);
853
854

		/* fixup parameter entities */
Christoph Mallon's avatar
Christoph Mallon committed
855
856
857
858
859
		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);
860
			if (is_Primitive_type(type)) {
Christoph Mallon's avatar
Christoph Mallon committed
861
				ir_type *const lowered = lower_type(type);
862
863
864
				set_entity_type(member, lowered);
			}
		}
865
866
867

		confirm_irg_properties(irg, changed_irgs[i] ? IR_GRAPH_PROPERTIES_NONE
		                                            : IR_GRAPH_PROPERTIES_ALL);
868
	}
869
	free(changed_irgs);
870
}