lower_softfloat.c 23.4 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
15
 */

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

#include "be.h"
#include "dbginfo_t.h"
#include "debug.h"
Matthias Braun's avatar
Matthias Braun committed
16
#include "panic.h"
17
#include "ircons_t.h"
18
19
20
21
22
23
#include "iredges.h"
#include "irgmod.h"
#include "irnodeset.h"
#include "irgwalk.h"
#include "irmode.h"
#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);

Christoph Mallon's avatar
Christoph Mallon committed
267
	char buf[16];
268
	if (float_types + double_types > 1)
Matthias Braun's avatar
Matthias Braun committed
269
270
		snprintf(buf, sizeof(buf), "__%s%s%s%s%u", name, first_param,
		         second_param, result, float_types + double_types);
271
	else
Matthias Braun's avatar
Matthias Braun committed
272
273
		snprintf(buf, sizeof(buf), "__%s%s%s%s", name, first_param,
		         second_param, result);
274

Christoph Mallon's avatar
Christoph Mallon committed
275
276
277
	ir_graph  *const irg = get_irn_irg(n);
	ident     *const id  = new_id_from_str(buf);
	ir_entity *const ent = create_compilerlib_entity(id, method);
278
	return new_r_Address(irg, ent);
279
280
}

Matthias Braun's avatar
Matthias Braun committed
281
282
283
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
284
285
286
287
288
289
290
291
{
	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
292
293
	ir_node  *const call     = new_rd_Call(dbgi, block, nomem, callee, arity,
	                                       in, type);
Christoph Mallon's avatar
Christoph Mallon committed
294
295
296
297
298
	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;
}

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

Christoph Mallon's avatar
Christoph Mallon committed
308
309
310
311
312
	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);
313
	return true;
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
}

/**
 * @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
331
	ir_type *res = pmap_get(ir_type, lowered_type, mtp);
332
333
334
	if (res != NULL)
		return res;

Christoph Mallon's avatar
Christoph Mallon committed
335
336
	size_t const n_param = get_method_n_params(mtp);
	size_t const n_res   = get_method_n_ress(mtp);
337
338
339
	res = new_type_method(n_param, n_res);

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

365
	set_higher_type(res, mtp);
366
367
368
369
370
371
372
373

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

/**
 * Adapts the method type of a Call.
 */
374
static bool lower_Call(ir_node *node)
375
376
{
	bool     need_lower = false;
Christoph Mallon's avatar
Christoph Mallon committed
377
	ir_type *tp         = get_Call_type(node);
378

Christoph Mallon's avatar
Christoph Mallon committed
379
380
	size_t const n_params = get_method_n_params(tp);
	for (size_t p = 0; p < n_params; ++p) {
381
382
		ir_type *ptp   = get_method_param_type(tp, p);
		ir_mode *pmode = get_type_mode(ptp);
Christoph Mallon's avatar
Christoph Mallon committed
383
		if (pmode && mode_is_float(pmode)) {
384
385
386
387
388
			need_lower = true;
			break;
		}
	}

Christoph Mallon's avatar
Christoph Mallon committed
389
390
	size_t const n_res = get_method_n_ress(tp);
	for (size_t i = 0; i < n_res; ++i) {
391
392
		ir_type *rtp   = get_method_res_type(tp, i);
		ir_mode *rmode = get_type_mode(rtp);
Christoph Mallon's avatar
Christoph Mallon committed
393
		if (rmode && mode_is_float(rmode)) {
394
395
396
397
398
			need_lower = true;
			break;
		}
	}

Christoph Mallon's avatar
Christoph Mallon committed
399
	if (!need_lower)
400
		return false;
401
402
403

	tp = lower_method_type(tp);
	set_Call_type(node, tp);
404
	return true;
405
406
407
408
409
}

/**
 * Transforms a Cmp into the appropriate soft float function.
 */
410
static bool lower_Cmp(ir_node *const n)
411
{
Christoph Mallon's avatar
Christoph Mallon committed
412
413
414
	ir_node *const left    = get_Cmp_left(n);
	ir_mode *const op_mode = get_irn_mode(left);
	if (!mode_is_float(op_mode))
415
		return false;
416

Christoph Mallon's avatar
Christoph Mallon committed
417
418
	dbg_info *const dbgi = get_irn_dbg_info(n);
	ir_graph *const irg  = get_irn_irg(n);
419
	ir_node  *const zero = new_rd_Const_null(dbgi, irg, mode_Is);
Christoph Mallon's avatar
Christoph Mallon committed
420
421
422
423
424

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

Christoph Mallon's avatar
Christoph Mallon committed
486
487
	ir_node *const block = get_nodes_block(n);
	ir_node *const right = get_Cmp_right(n);
488

Christoph Mallon's avatar
Christoph Mallon committed
489
490
491
	if (result == NULL) {
		ir_node *const in[] = { left, right };
		result = make_softfloat_call(n, name, ARRAY_SIZE(in), in);
492
493
	}

Christoph Mallon's avatar
Christoph Mallon committed
494
	ir_node *cmp = new_r_Cmp(block, result, zero, relation);
495
496

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

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

	exchange(n, cmp);
513
	return true;
514
515
516
517
518
}

/**
 * Adapts floating point constants.
 */
519
static bool lower_Const(ir_node *const n)
520
{
521
522
	ir_mode *mode = get_irn_mode(n);
	if (!mode_is_float(mode))
523
		return false;
524

525
	ir_tarval *float_tv     = get_Const_tarval(n);
526
	ir_mode   *lowered_mode = get_lowered_mode(mode);
527
	ir_tarval *int_tv       = tarval_bitcast(float_tv, lowered_mode);
528

529
530
	set_irn_mode(n, lowered_mode);
	set_Const_tarval(n, int_tv);
531
	return true;
532
533
534
535
536
}

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

Christoph Mallon's avatar
Christoph Mallon committed
578
579
	ir_node *const in[]   = { op };
	ir_node       *result = make_softfloat_call(n, name, ARRAY_SIZE(in), in);
580
581

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

Christoph Mallon's avatar
Christoph Mallon committed
585
	exchange(n, result);
586
	return true;
587
588
}

589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
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;
}

610
611
612
/**
 * Transforms a Div into the appropriate soft float function.
 */
613
static bool lower_Div(ir_node *const n)
614
{
Christoph Mallon's avatar
Christoph Mallon committed
615
616
	ir_mode *const mode = get_Div_resmode(n);
	if (!mode_is_float(mode))
617
		return false;
618

Christoph Mallon's avatar
Christoph Mallon committed
619
620
621
622
	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);
623
	ir_node *const call   = skip_Proj(skip_Proj(result));
624
625
	set_irn_pinned(call, get_irn_pinned(n));

626
	foreach_out_edge_safe(n, edge) {
627
		ir_node *proj = get_edge_src_irn(edge);
Christoph Mallon's avatar
Christoph Mallon committed
628
		if (!is_Proj(proj))
629
630
			continue;

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

/**
 * Adapts the resmode of a Div.
 */
656
static bool lower_Div_mode(ir_node *n)
657
658
659
660
661
{
	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);
662
663
664
	if (lowered_mode == mode && lowered_res_mode == res_mode)
		return false;

665
666
	set_irn_mode(n, lowered_mode);
	set_Div_resmode(n, lowered_res_mode);
667
	return true;
668
669
670
671
672
}

/**
 * Adapts the ls_mode of a Load.
 */
673
static bool lower_Load(ir_node *n)
674
675
676
677
678
{
	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);
679
680
681
	if (ls_mode == lowered_ls_mode && mode == lowered_mode)
		return false;

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

/**
 * Transforms a Minus into the appropriate soft float function.
 */
690
static bool lower_Minus(ir_node *n)
691
{
Christoph Mallon's avatar
Christoph Mallon committed
692
693
	ir_mode *const mode = get_irn_mode(n);
	if (!mode_is_float(mode))
694
		return false;
695

Matthias Braun's avatar
Matthias Braun committed
696
697
698
	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
699
	exchange(n, result);
700
	return true;
701
702
703
704
705
}

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

Christoph Mallon's avatar
Christoph Mallon committed
712
713
714
715
716
	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);
717
	return true;
718
719
720
721
722
}

/**
 * Transforms a Sub into the appropriate soft float function.
 */
723
static bool lower_Sub(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_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);
734
	return true;
735
736
737
738
739
}

/**
 * Enter a lowering function into an ir_op.
 */
Matthias Braun's avatar
Matthias Braun committed
740
741
static void ir_register_softloat_lower_function(ir_op *op,
                                                lower_softfloat_func func)
742
743
744
745
{
	op->ops.generic = (op_func)func;
}

Matthias Braun's avatar
Matthias Braun committed
746
747
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
748
749
750
751
752
753
754
755
756
{
	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
757
758
static void make_unop_type(ir_type **const memoized, ir_type *const op,
                           ir_type *const res)
Christoph Mallon's avatar
Christoph Mallon committed
759
760
761
762
763
764
765
766
{
	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);
	}
}

767
768
769
770
771
/*
 * Initializes softfloat lowering.
 */
static void ir_prepare_softfloat_lowering(void)
{
Christoph Mallon's avatar
Christoph Mallon committed
772
	if (!lowered_type)
773
774
		lowered_type = pmap_create();

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

/**
 * 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
821
	ir_clear_opcodes_generic_func();
822
823
824
825
826
827
828
829
	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);

830
831
	bool *const changed_irgs = XMALLOCNZ(bool, get_irp_n_irgs());
	foreach_irp_irg(i, irg) {
832
833
		ir_nodeset_init(&created_mux_nodes);

834
		assure_irg_properties(irg, IR_GRAPH_PROPERTY_CONSISTENT_OUT_EDGES);
835

836
		irg_walk_graph(irg, NULL, lower_node, &changed_irgs[i]);
837
838
839
840
841
842
843

		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
844
	ir_clear_opcodes_generic_func();
845
846
847
848
849
	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);
850

851
	foreach_irp_irg(i, irg) {
Christoph Mallon's avatar
Christoph Mallon committed
852
853
854
		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);
855
856
857
		if (lowered_mtp != mtp)
			set_entity_type(ent, lowered_mtp);

858
		irg_walk_graph(irg, NULL, lower_mode, &changed_irgs[i]);
859
860

		/* fixup parameter entities */
Christoph Mallon's avatar
Christoph Mallon committed
861
862
863
864
865
		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);
866
			if (is_Primitive_type(type)) {
Christoph Mallon's avatar
Christoph Mallon committed
867
				ir_type *const lowered = lower_type(type);
868
869
870
				set_entity_type(member, lowered);
			}
		}
871
872
873

		confirm_irg_properties(irg, changed_irgs[i] ? IR_GRAPH_PROPERTIES_NONE
		                                            : IR_GRAPH_PROPERTIES_ALL);
874
	}
875
	free(changed_irgs);
876
}