lower_softfloat.c 23.1 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
		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);
	}

355
	copy_method_properties(res, mtp);
356

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	exchange(n, cmp);
505
	return true;
506
507
508
509
510
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

826
		assure_irg_properties(irg, IR_GRAPH_PROPERTY_CONSISTENT_OUT_EDGES);
827

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

		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
836
	ir_clear_opcodes_generic_func();
837
838
839
840
841
	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);
842

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

850
		irg_walk_graph(irg, NULL, lower_mode, &changed_irgs[i]);
851
852

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

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