lower_softfloat.c 23.8 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
#include "iredges_t.h"
18
19
20
#include "irgmod.h"
#include "irnodeset.h"
#include "irgwalk.h"
21
#include "irmode_t.h"
22
#include "irop_t.h"
23
#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
	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++;
sebastian.buchwald1's avatar
sebastian.buchwald1 committed
258
259
	} else if (mode == mode_Iu || mode == mode_Hu || mode == mode_Bu ||
	           mode == mode_Is || mode == mode_Hs || mode == mode_Bs)
Christoph Mallon's avatar
Christoph Mallon committed
260
261
262
263
		result = "si";
	else if (mode == mode_Lu || mode == mode_Ls)
		result = "di";

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

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

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

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

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

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

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

330
331
332
333
334
	size_t   const n_param     = get_method_n_params(mtp);
	size_t   const n_res       = get_method_n_ress(mtp);
	bool     const is_variadic = is_method_variadic(mtp);
	unsigned const cc_mask     = get_method_calling_convention(mtp);
	res = new_type_method(n_param, n_res, is_variadic, cc_mask);
335
336

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

358
	copy_method_properties(res, mtp);
359

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

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

366
static ir_type *lower_type_if_needed(ir_type *tp)
367
{
368
	bool need_lower = false;
369

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

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

390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
	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)
416
		return false;
417

418
419
420
421
422
	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);
	}
423
	return true;
424
425
426
427
428
}

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

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

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

Christoph Mallon's avatar
Christoph Mallon committed
505
506
	ir_node *const block = get_nodes_block(n);
	ir_node *const right = get_Cmp_right(n);
507

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

Christoph Mallon's avatar
Christoph Mallon committed
513
	ir_node *cmp = new_r_Cmp(block, result, zero, relation);
514
515

	/* We need two calls into the softfloat library */
Christoph Mallon's avatar
Christoph Mallon committed
516
517
518
519
520
	if (name2 != NULL) {
		ir_node *const in[] = { left, right };
		result   = make_softfloat_call(n, name2, ARRAY_SIZE(in), in);
		relation = get_Cmp_relation(n);

521
		ir_node *const mux = new_rd_Mux(dbgi, block, cmp, result, zero);
Christoph Mallon's avatar
Christoph Mallon committed
522

Matthias Braun's avatar
Matthias Braun committed
523
524
		arch_allow_ifconv_func const allow_ifconv
			= be_get_backend_param()->allow_ifconv;
Christoph Mallon's avatar
Christoph Mallon committed
525
		if (!allow_ifconv(cmp, result, zero))
526
527
528
529
530
531
			ir_nodeset_insert(&created_mux_nodes, mux);

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

	exchange(n, cmp);
532
	return true;
533
534
535
536
537
}

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

544
	ir_tarval *float_tv     = get_Const_tarval(n);
545
	ir_mode   *lowered_mode = get_lowered_mode(mode);
546
	ir_tarval *int_tv       = tarval_bitcast(float_tv, lowered_mode);
547

548
549
	set_irn_mode(n, lowered_mode);
	set_Const_tarval(n, int_tv);
550
	return true;
551
552
553
554
555
}

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

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

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

Christoph Mallon's avatar
Christoph Mallon committed
604
	exchange(n, result);
605
	return true;
606
607
}

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

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

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

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

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

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

684
685
	set_irn_mode(n, lowered_mode);
	set_Div_resmode(n, lowered_res_mode);
686
	return true;
687
688
689
690
691
}

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

701
702
	set_irn_mode(n, lowered_mode);
	set_Load_mode(n, lowered_ls_mode);
703
	return true;
704
705
706
707
708
}

/**
 * Transforms a Minus into the appropriate soft float function.
 */
709
static bool lower_Minus(ir_node *n)
710
{
Christoph Mallon's avatar
Christoph Mallon committed
711
712
	ir_mode *const mode = get_irn_mode(n);
	if (!mode_is_float(mode))
713
		return false;
714

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

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

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

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

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

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

Matthias Braun's avatar
Matthias Braun committed
765
766
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
767
768
{
	if (!*memoized) {
769
		ir_type *const type = *memoized = new_type_method(2, 1, false, cc_cdecl_set);
Christoph Mallon's avatar
Christoph Mallon committed
770
771
772
773
774
775
		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
776
777
static void make_unop_type(ir_type **const memoized, ir_type *const op,
                           ir_type *const res)
Christoph Mallon's avatar
Christoph Mallon committed
778
779
{
	if (!*memoized) {
780
		ir_type *const type = *memoized = new_type_method(1, 1, false, cc_cdecl_set);
Christoph Mallon's avatar
Christoph Mallon committed
781
782
783
784
785
		set_method_param_type(type, 0, op);
		set_method_res_type(  type, 0, res);
	}
}

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

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

/**
 * 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
840
	ir_clear_opcodes_generic_func();
841
842
843
844
845
846
847
848
	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);

849
850
	bool *const changed_irgs = XMALLOCNZ(bool, get_irp_n_irgs());
	foreach_irp_irg(i, irg) {
851
852
		ir_nodeset_init(&created_mux_nodes);

853
		assure_irg_properties(irg, IR_GRAPH_PROPERTY_CONSISTENT_OUT_EDGES);
854

855
		irg_walk_graph(irg, NULL, lower_node, &changed_irgs[i]);
856
857
858
859
860
861
862

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

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

878
		irg_walk_graph(irg, NULL, lower_mode, &changed_irgs[i]);
879
880

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

		confirm_irg_properties(irg, changed_irgs[i] ? IR_GRAPH_PROPERTIES_NONE
		                                            : IR_GRAPH_PROPERTIES_ALL);
894
	}
895
	free(changed_irgs);
896
}