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

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

#include "be.h"
#include "dbginfo_t.h"
Matthias Braun's avatar
Matthias Braun committed
15
#include "panic.h"
16
#include "ircons_t.h"
17
18
19
20
21
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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	/* We need two calls into the softfloat library */
Christoph Mallon's avatar
Christoph Mallon committed
513
514
515
516
517
518
519
	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
520
521
		arch_allow_ifconv_func const allow_ifconv
			= be_get_backend_param()->allow_ifconv;
Christoph Mallon's avatar
Christoph Mallon committed
522
		if (!allow_ifconv(cmp, result, zero))
523
524
525
526
527
528
			ir_nodeset_insert(&created_mux_nodes, mux);

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

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

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

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

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

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

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

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

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

605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
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;
}

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

Christoph Mallon's avatar
Christoph Mallon committed
635
636
637
638
	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);
639
	ir_node *const call   = skip_Proj(skip_Proj(result));
640
641
	set_irn_pinned(call, get_irn_pinned(n));

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

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

/**
 * Adapts the resmode of a Div.
 */
672
static bool lower_Div_mode(ir_node *n)
673
674
675
676
677
{
	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);
678
679
680
	if (lowered_mode == mode && lowered_res_mode == res_mode)
		return false;

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

/**
 * Adapts the ls_mode of a Load.
 */
689
static bool lower_Load(ir_node *n)
690
691
692
693
694
{
	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);
695
696
697
	if (ls_mode == lowered_ls_mode && mode == lowered_mode)
		return false;

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

/**
 * Transforms a Minus into the appropriate soft float function.
 */
706
static bool lower_Minus(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

Matthias Braun's avatar
Matthias Braun committed
712
713
714
	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
715
	exchange(n, result);
716
	return true;
717
718
719
720
721
}

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

Christoph Mallon's avatar
Christoph Mallon committed
728
729
730
731
732
	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);
733
	return true;
734
735
736
737
738
}

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

Christoph Mallon's avatar
Christoph Mallon committed
745
746
747
748
749
	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);
750
	return true;
751
752
753
754
755
}

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

Matthias Braun's avatar
Matthias Braun committed
762
763
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
764
765
766
767
768
769
770
771
772
{
	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
773
774
static void make_unop_type(ir_type **const memoized, ir_type *const op,
                           ir_type *const res)
Christoph Mallon's avatar
Christoph Mallon committed
775
776
777
778
779
780
781
782
{
	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);
	}
}

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

Christoph Mallon's avatar
Christoph Mallon committed
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
	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
808
809
	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
810
811
812
813
814
815
816
817
818
819
820
821
822
	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);
823
824
825
826
827
828
829
830
831
832
833
834
835
836
}

/**
 * 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
837
	ir_clear_opcodes_generic_func();
838
839
840
841
842
843
844
845
	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);

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

850
		assure_irg_properties(irg, IR_GRAPH_PROPERTY_CONSISTENT_OUT_EDGES);
851

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

		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
860
	ir_clear_opcodes_generic_func();
861
862
	ir_register_softloat_lower_function(op_Bitcast, lower_Bitcast);
	ir_register_softloat_lower_function(op_Call,    lower_Call);
863
	ir_register_softloat_lower_function(op_Builtin, lower_Builtin);
864
865
866
	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);
867

868
	foreach_irp_irg(i, irg) {
Christoph Mallon's avatar
Christoph Mallon committed
869
870
871
		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);
872
873
874
		if (lowered_mtp != mtp)
			set_entity_type(ent, lowered_mtp);

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

		/* fixup parameter entities */
Christoph Mallon's avatar
Christoph Mallon committed
878
879
880
881
882
		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);
883
			if (is_Primitive_type(type)) {
Christoph Mallon's avatar
Christoph Mallon committed
884
				ir_type *const lowered = lower_type(type);
885
886
887
				set_entity_type(member, lowered);
			}
		}
888
889
890

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