lower_softfloat.c 23.6 KB
Newer Older
1
2
/*
 * This file is part of libFirm.
3
 * Copyright (C) 2012 University of Karlsruhe.
4
5
6
7
8
9
10
11
12
13
14
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
77
78
79
80
81
82
83
84
		return mode;

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

	panic("Unsupported floating point type");
}

/**
 * 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
526
527
	assert(get_mode_size_bits(mode) % 8 == 0);
	unsigned       size = get_mode_size_bits(mode) / 8;
	unsigned char *buf  = ALLOCAN(unsigned char, size);
528

529
530
531
532
533
534
	ir_tarval *float_tv = get_Const_tarval(n);
	for (unsigned i = 0; i < size; ++i) {
		buf[i] = get_tarval_sub_bits(float_tv, i);
	}
	ir_mode   *lowered_mode = get_lowered_mode(mode);
	ir_tarval *int_tv       = new_tarval_from_bytes(buf, lowered_mode, false);
535

536
537
	set_irn_mode(n, lowered_mode);
	set_Const_tarval(n, int_tv);
538
	return true;
539
540
541
542
543
}

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

Christoph Mallon's avatar
Christoph Mallon committed
585
586
	ir_node *const in[]   = { op };
	ir_node       *result = make_softfloat_call(n, name, ARRAY_SIZE(in), in);
587
588

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

Christoph Mallon's avatar
Christoph Mallon committed
592
	exchange(n, result);
593
	return true;
594
595
}

596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
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;
}

617
618
619
/**
 * Transforms a Div into the appropriate soft float function.
 */
620
static bool lower_Div(ir_node *const n)
621
{
Christoph Mallon's avatar
Christoph Mallon committed
622
623
	ir_mode *const mode = get_Div_resmode(n);
	if (!mode_is_float(mode))
624
		return false;
625

Christoph Mallon's avatar
Christoph Mallon committed
626
627
628
629
	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);
630
	ir_node *const call   = skip_Proj(skip_Proj(result));
631
632
	set_irn_pinned(call, get_irn_pinned(n));

633
	foreach_out_edge_safe(n, edge) {
634
		ir_node *proj = get_edge_src_irn(edge);
Christoph Mallon's avatar
Christoph Mallon committed
635
		if (!is_Proj(proj))
636
637
			continue;

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

/**
 * Adapts the resmode of a Div.
 */
663
static bool lower_Div_mode(ir_node *n)
664
665
666
667
668
{
	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);
669
670
671
	if (lowered_mode == mode && lowered_res_mode == res_mode)
		return false;

672
673
	set_irn_mode(n, lowered_mode);
	set_Div_resmode(n, lowered_res_mode);
674
	return true;
675
676
677
678
679
}

/**
 * Adapts the ls_mode of a Load.
 */
680
static bool lower_Load(ir_node *n)
681
682
683
684
685
{
	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);
686
687
688
	if (ls_mode == lowered_ls_mode && mode == lowered_mode)
		return false;

689
690
	set_irn_mode(n, lowered_mode);
	set_Load_mode(n, lowered_ls_mode);
691
	return true;
692
693
694
695
696
}

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

Matthias Braun's avatar
Matthias Braun committed
703
704
705
	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
706
	exchange(n, result);
707
	return true;
708
709
710
711
712
}

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

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

/**
 * Transforms a Sub into the appropriate soft float function.
 */
730
static bool lower_Sub(ir_node *n)
731
{
Christoph Mallon's avatar
Christoph Mallon committed
732
733
	ir_mode *const mode = get_irn_mode(n);
	if (!mode_is_float(mode))
734
		return false;
735

Christoph Mallon's avatar
Christoph Mallon committed
736
737
738
739
740
	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);
741
	return true;
742
743
744
745
746
}

/**
 * Enter a lowering function into an ir_op.
 */
Matthias Braun's avatar
Matthias Braun committed
747
748
static void ir_register_softloat_lower_function(ir_op *op,
                                                lower_softfloat_func func)
749
750
751
752
{
	op->ops.generic = (op_func)func;
}

Matthias Braun's avatar
Matthias Braun committed
753
754
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
755
756
757
758
759
760
761
762
763
{
	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
764
765
static void make_unop_type(ir_type **const memoized, ir_type *const op,
                           ir_type *const res)
Christoph Mallon's avatar
Christoph Mallon committed
766
767
768
769
770
771
772
773
{
	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);
	}
}

774
775
776
777
778
/*
 * Initializes softfloat lowering.
 */
static void ir_prepare_softfloat_lowering(void)
{
Christoph Mallon's avatar
Christoph Mallon committed
779
	if (!lowered_type)
780
781
		lowered_type = pmap_create();

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

/**
 * 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
828
	ir_clear_opcodes_generic_func();
829
830
831
832
833
834
835
836
	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);

837
838
	bool *const changed_irgs = XMALLOCNZ(bool, get_irp_n_irgs());
	foreach_irp_irg(i, irg) {
839
840
		ir_nodeset_init(&created_mux_nodes);

841
		assure_irg_properties(irg, IR_GRAPH_PROPERTY_CONSISTENT_OUT_EDGES);
842

843
		irg_walk_graph(irg, NULL, lower_node, &changed_irgs[i]);
844
845
846
847
848
849
850

		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
851
	ir_clear_opcodes_generic_func();
852
853
854
855
856
	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);
857

858
	foreach_irp_irg(i, irg) {
Christoph Mallon's avatar
Christoph Mallon committed
859
860
861
		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);
862
863
864
		if (lowered_mtp != mtp)
			set_entity_type(ent, lowered_mtp);

865
		irg_walk_graph(irg, NULL, lower_mode, &changed_irgs[i]);
866
867

		/* fixup parameter entities */
Christoph Mallon's avatar
Christoph Mallon committed
868
869
870
871
872
		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);
873
			if (is_Primitive_type(type)) {
Christoph Mallon's avatar
Christoph Mallon committed
874
				ir_type *const lowered = lower_type(type);
875
876
877
				set_entity_type(member, lowered);
			}
		}
878
879
880

		confirm_irg_properties(irg, changed_irgs[i] ? IR_GRAPH_PROPERTIES_NONE
		                                            : IR_GRAPH_PROPERTIES_ALL);
881
	}
882
	free(changed_irgs);
883
}