lower_softfloat.c 24 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
335
	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);
	mtp_additional_properties const props       = get_method_additional_properties(mtp);
	res = new_type_method(n_param, n_res, is_variadic, cc_mask, props);
336
337

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

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

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

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

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

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

389
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
	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)
415
		return false;
416

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

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

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

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

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

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

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

	/* We need two calls into the softfloat library */
Christoph Mallon's avatar
Christoph Mallon committed
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);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

852
		assure_irg_properties(irg, IR_GRAPH_PROPERTY_CONSISTENT_OUT_EDGES);
853

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

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

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

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

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

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