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

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

#include "be.h"
#include "dbginfo_t.h"
#include "debug.h"
#include "error.h"
17
#include "ircons_t.h"
18
19
20
21
22
23
#include "iredges.h"
#include "irgmod.h"
#include "irnodeset.h"
#include "irgwalk.h"
#include "irmode.h"
#include "iropt_dbg.h"
24
#include "iroptimize.h"
25
26
27
28
29
30
31
#include "irprog_t.h"
#include "lower_softfloat.h"
#include "lowering.h"
#include "pmap.h"
#include "type_t.h"
#include "tv_t.h"

32
typedef bool (*lower_softfloat_func)(ir_node *node);
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61

static ir_type *binop_tp_d;
static ir_type *binop_tp_f;
static ir_type *cmp_tp_d;
static ir_type *cmp_tp_f;
static ir_type *unop_tp_d;
static ir_type *unop_tp_f;
static ir_type *unop_tp_d_f;
static ir_type *unop_tp_d_is;
static ir_type *unop_tp_d_iu;
static ir_type *unop_tp_d_ls;
static ir_type *unop_tp_d_lu;
static ir_type *unop_tp_f_d;
static ir_type *unop_tp_f_is;
static ir_type *unop_tp_f_iu;
static ir_type *unop_tp_f_ls;
static ir_type *unop_tp_f_lu;
static ir_type *unop_tp_is_d;
static ir_type *unop_tp_is_f;
static ir_type *unop_tp_iu_d;
static ir_type *unop_tp_iu_f;
static ir_type *unop_tp_ls_d;
static ir_type *unop_tp_ls_f;
static ir_type *unop_tp_lu_d;
static ir_type *unop_tp_lu_f;

/** A map from a method type to its lowered type. */
static pmap *lowered_type;

62
static ir_nodeset_t created_mux_nodes;
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106

/**
 * @return The lowered (floating point) mode.
 */
static ir_mode *get_lowered_mode(ir_mode *mode)
{
	if (! mode_is_float(mode))
		return mode;

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

	panic("Unsupported floating point type");
}

/**
 * Adapts the mode of the given node.
 */
static void lower_mode(ir_node *n, void *env)
{
	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);

	(void) env;

	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;

107
108
109
110
	if (lower_func != NULL) {
		bool *changed = (bool*)env;
		*changed |= lower_func(n);
	}
111
112
113
114
115
116
117
118
119
120
}

/**
 * @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
121
	switch (get_irn_opcode(n)) {
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
	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
139
140
141

	case iro_Conv: {
		ir_mode *const mode = get_irn_mode(n);
142
143
144
145
146
147
148
149
150
151
152
		if (operand_mode == mode_D) {
			if (mode == mode_F)
				return unop_tp_d_f;
			else if (mode == mode_Is || mode == mode_Hs || mode == mode_Bs)
				return unop_tp_d_is;
			else if (mode == mode_Iu || mode == mode_Hu || mode == mode_Bu)
				return unop_tp_d_iu;
			else if (mode == mode_Ls)
				return unop_tp_d_ls;
			else if (mode == mode_Lu)
				return unop_tp_d_lu;
Christoph Mallon's avatar
Christoph Mallon committed
153
		} else if (operand_mode == mode_F) {
154
155
156
157
158
159
160
161
162
163
			if (mode == mode_D)
				return unop_tp_f_d;
			else if (mode == mode_Is || mode == mode_Hs || mode == mode_Bs)
				return unop_tp_f_is;
			else if (mode == mode_Iu || mode == mode_Hu || mode == mode_Bu)
				return unop_tp_f_iu;
			else if (mode == mode_Ls)
				return unop_tp_f_ls;
			else if (mode == mode_Lu)
				return unop_tp_f_lu;
Christoph Mallon's avatar
Christoph Mallon committed
164
		} else if (operand_mode == mode_Is || operand_mode == mode_Hs || operand_mode == mode_Bs) {
165
166
167
168
			if (mode == mode_D)
				return unop_tp_is_d;
			else if (mode == mode_F)
				return unop_tp_is_f;
Christoph Mallon's avatar
Christoph Mallon committed
169
		} else if (operand_mode == mode_Iu || operand_mode == mode_Hu || operand_mode == mode_Bu) {
170
171
172
173
			if (mode == mode_D)
				return unop_tp_iu_d;
			else if (mode == mode_F)
				return unop_tp_iu_f;
Christoph Mallon's avatar
Christoph Mallon committed
174
		} else if (operand_mode == mode_Ls) {
175
176
177
178
			if (mode == mode_D)
				return unop_tp_ls_d;
			else if (mode == mode_F)
				return unop_tp_ls_f;
Christoph Mallon's avatar
Christoph Mallon committed
179
		} else if (operand_mode == mode_Lu) {
180
181
182
183
184
185
			if (mode == mode_D)
				return unop_tp_lu_d;
			else if (mode == mode_F)
				return unop_tp_lu_f;
		}
		break;
Christoph Mallon's avatar
Christoph Mallon committed
186
187
	}

188
189
190
191
192
193
194
195
196
	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;
	}

197
	panic("Could not determine a suitable type");
198
199
200
}

/**
201
 * @return An Address representing the function that replaces the given node.
202
 */
Christoph Mallon's avatar
Christoph Mallon committed
203
static ir_node *create_softfloat_address(const ir_node *n, const char *name)
204
{
Christoph Mallon's avatar
Christoph Mallon committed
205
	ir_type *const method = get_softfloat_type(n);
206
207

	/* Parameter types. */
Christoph Mallon's avatar
Christoph Mallon committed
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
	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";
227
228
		}
	}
Christoph Mallon's avatar
Christoph Mallon committed
229
230
231
232
		/* FALLTHROUGH */
	case 1: {
		ir_type *const param_type = get_method_param_type(method, 0);
		ir_mode *const mode       = get_type_mode(param_type);
233
234

		if (mode == mode_F) {
Christoph Mallon's avatar
Christoph Mallon committed
235
			first_param = float_types > 0 ? "" : "sf";
236
			float_types++;
Christoph Mallon's avatar
Christoph Mallon committed
237
238
		} else if (mode == mode_D) {
			first_param = double_types > 0 ? "" : "df";
239
			double_types++;
Christoph Mallon's avatar
Christoph Mallon committed
240
241
242
243
		} else if (mode == mode_Iu || mode == mode_Is) {
			first_param = "si";
		} else if (mode == mode_Lu || mode == mode_Ls) {
			first_param = "di";
244
		}
Christoph Mallon's avatar
Christoph Mallon committed
245
		break;
246
247
	}

Christoph Mallon's avatar
Christoph Mallon committed
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
	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";

267
268
269
	assert(float_types <= 3);
	assert(double_types <= 3);

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

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

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

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

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

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

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

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

		if (rmode != NULL && mode_is_float(rmode)) {
			rtp = lower_type(rtp);
		}

		set_method_res_type(res, i, rtp);
	}

	set_method_variadicity(res, get_method_variadicity(mtp));
	set_method_calling_convention(res, get_method_calling_convention(mtp));
	set_method_additional_properties(res, get_method_additional_properties(mtp));

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

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

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

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

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

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

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

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

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

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

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

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

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

	/* We need two calls into the softfloat library */
Christoph Mallon's avatar
Christoph Mallon committed
496
497
498
499
500
501
502
503
504
	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);

		arch_allow_ifconv_func const allow_ifconv = be_get_backend_param()->allow_ifconv;
		if (!allow_ifconv(cmp, result, zero))
505
506
507
508
509
510
			ir_nodeset_insert(&created_mux_nodes, mux);

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

	exchange(n, cmp);
511
	return true;
512
513
514
515
516
}

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

523
524
525
	assert(get_mode_size_bits(mode) % 8 == 0);
	unsigned       size = get_mode_size_bits(mode) / 8;
	unsigned char *buf  = ALLOCAN(unsigned char, size);
526

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

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

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

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

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

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

/**
 * Transforms a Div into the appropriate soft float function.
 */
597
static bool lower_Div(ir_node *const n)
598
{
Christoph Mallon's avatar
Christoph Mallon committed
599
600
	ir_mode *const mode = get_Div_resmode(n);
	if (!mode_is_float(mode))
601
		return false;
602

Christoph Mallon's avatar
Christoph Mallon committed
603
604
605
606
	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);
607
	ir_node *const call   = skip_Proj(skip_Proj(result));
608
609
610

	set_irn_pinned(call, get_irn_pinned(n));

611
	foreach_out_edge_safe(n, edge) {
612
		ir_node *proj = get_edge_src_irn(edge);
Christoph Mallon's avatar
Christoph Mallon committed
613
		if (!is_Proj(proj))
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
			continue;

		switch (get_Proj_proj(proj)) {
		case pn_Div_M:
			set_Proj_pred(proj, call);
			set_Proj_proj(proj, pn_Call_M);
			break;
		case pn_Div_X_regular:
			set_Proj_pred(proj, call);
			set_Proj_proj(proj, pn_Call_X_regular);
			break;
		case pn_Div_X_except:
			set_Proj_pred(proj, call);
			set_Proj_proj(proj, pn_Call_X_except);
			break;
		case pn_Div_res:
Christoph Mallon's avatar
Christoph Mallon committed
630
			exchange(proj, result);
631
632
			break;
		default:
633
			panic("unexpected Proj number");
634
635
		}
	}
636
	return true;
637
638
639
640
641
}

/**
 * Adapts the resmode of a Div.
 */
642
static bool lower_Div_mode(ir_node *n)
643
644
645
646
647
648
{
	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);

649
650
651
	if (lowered_mode == mode && lowered_res_mode == res_mode)
		return false;

652
653
	set_irn_mode(n, lowered_mode);
	set_Div_resmode(n, lowered_res_mode);
654
	return true;
655
656
657
658
659
}

/**
 * Adapts the ls_mode of a Load.
 */
660
static bool lower_Load(ir_node *n)
661
662
663
664
665
666
{
	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);

667
668
669
	if (ls_mode == lowered_ls_mode && mode == lowered_mode)
		return false;

670
671
	set_irn_mode(n, lowered_mode);
	set_Load_mode(n, lowered_ls_mode);
672
	return true;
673
674
675
676
677
}

/**
 * Transforms a Minus into the appropriate soft float function.
 */
678
static bool lower_Minus(ir_node *n)
679
{
Christoph Mallon's avatar
Christoph Mallon committed
680
681
	ir_mode *const mode = get_irn_mode(n);
	if (!mode_is_float(mode))
682
		return false;
683

Christoph Mallon's avatar
Christoph Mallon committed
684
685
686
687
	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);
	exchange(n, result);
688
	return true;
689
690
691
692
693
}

/**
 * Transforms a Mul into the appropriate soft float function.
 */
694
static bool lower_Mul(ir_node *n)
695
{
Christoph Mallon's avatar
Christoph Mallon committed
696
697
	ir_mode *const mode = get_irn_mode(n);
	if (!mode_is_float(mode))
698
		return false;
699

Christoph Mallon's avatar
Christoph Mallon committed
700
701
702
703
704
	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);
705
	return true;
706
707
708
709
710
}

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

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

/**
 * Enter a lowering function into an ir_op.
 */
static void ir_register_softloat_lower_function(ir_op *op, lower_softfloat_func func)
{
	op->ops.generic = (op_func)func;
}

Christoph Mallon's avatar
Christoph Mallon committed
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
static void make_binop_type(ir_type **const memoized, ir_type *const left, ir_type *const right, ir_type *const res)
{
	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);
	}
}

static void make_unop_type(ir_type **const memoized, ir_type *const op, ir_type *const res)
{
	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);
	}
}

752
753
754
755
756
/*
 * Initializes softfloat lowering.
 */
static void ir_prepare_softfloat_lowering(void)
{
Christoph Mallon's avatar
Christoph Mallon committed
757
	if (!lowered_type)
758
759
		lowered_type = pmap_create();

Christoph Mallon's avatar
Christoph Mallon committed
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
	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
777
778
	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
779
780
781
782
783
784
785
786
787
788
789
790
791
	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);
792
793
794
795
796
797
798
799
800
801
802
803
804
805
}

/**
 * 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
806
	ir_clear_opcodes_generic_func();
807
808
809
810
811
812
813
814
	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);

815
816
	bool *const changed_irgs = XMALLOCNZ(bool, get_irp_n_irgs());
	foreach_irp_irg(i, irg) {
817
818
		ir_nodeset_init(&created_mux_nodes);

819
		assure_irg_properties(irg, IR_GRAPH_PROPERTY_CONSISTENT_OUT_EDGES);
820

821
		irg_walk_graph(irg, NULL, lower_node, &changed_irgs[i]);
822
823
824
825
826
827
828

		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
829
	ir_clear_opcodes_generic_func();
830
831
832
833
834
	ir_register_softloat_lower_function(op_Call,  lower_Call);
	ir_register_softloat_lower_function(op_Const, lower_Const);
	ir_register_softloat_lower_function(op_Div,   lower_Div_mode);
	ir_register_softloat_lower_function(op_Load,  lower_Load);

835
	foreach_irp_irg(i, irg) {
Christoph Mallon's avatar
Christoph Mallon committed
836
837
838
		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);
839
840
841
		if (lowered_mtp != mtp)
			set_entity_type(ent, lowered_mtp);

842
		irg_walk_graph(irg, NULL, lower_mode, &changed_irgs[i]);
843
844

		/* fixup parameter entities */
Christoph Mallon's avatar
Christoph Mallon committed
845
846
847
848
849
		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);
850
			if (is_Primitive_type(type)) {
Christoph Mallon's avatar
Christoph Mallon committed
851
				ir_type *const lowered = lower_type(type);
852
853
854
				set_entity_type(member, lowered);
			}
		}
855
856
857

		confirm_irg_properties(irg, changed_irgs[i] ? IR_GRAPH_PROPERTIES_NONE
		                                            : IR_GRAPH_PROPERTIES_ALL);
858
	}
859
	free(changed_irgs);
860
}