lower_softfloat.c 22 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
17
18
19
20
21
22
23
 */

/**
 * @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"
#include "ircons.h"
#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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#include "irprog_t.h"
#include "lower_softfloat.h"
#include "lowering.h"
#include "pmap.h"
#include "type_t.h"
#include "tv_t.h"

typedef void (*lower_softfloat_func)(ir_node *node);

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
107
108
109
110
111
112
113
114
115
116
117
118
119
120

/**
 * @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;

	(void) env;

	if (lower_func != NULL)
		lower_func(n);
}

/**
 * @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.
 */
Christoph Mallon's avatar
Christoph Mallon committed
300
static void 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
305
		return;

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
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
}

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

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

	/* 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
359
360
361
362
		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));

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

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

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

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

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

Christoph Mallon's avatar
Christoph Mallon committed
397
	if (!need_lower)
398
399
400
401
402
403
404
405
406
		return;

	tp = lower_method_type(tp);
	set_Call_type(node, tp);
}

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

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

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

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

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

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

	/* We need two calls into the softfloat library */
Christoph Mallon's avatar
Christoph Mallon committed
494
495
496
497
498
499
500
501
502
	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))
503
504
505
506
507
508
509
510
511
512
513
			ir_nodeset_insert(&created_mux_nodes, mux);

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

	exchange(n, cmp);
}

/**
 * Adapts floating point constants.
 */
Christoph Mallon's avatar
Christoph Mallon committed
514
static void lower_Const(ir_node *const n)
515
{
516
517
	ir_mode *mode = get_irn_mode(n);
	if (!mode_is_float(mode))
518
519
		return;

520
521
522
	assert(get_mode_size_bits(mode) % 8 == 0);
	unsigned       size = get_mode_size_bits(mode) / 8;
	unsigned char *buf  = ALLOCAN(unsigned char, size);
523

524
525
526
527
528
529
	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);
530

531
532
	set_irn_mode(n, lowered_mode);
	set_Const_tarval(n, int_tv);
533
534
535
536
537
}

/**
 * Transforms a Conv into the appropriate soft float function.
 */
Christoph Mallon's avatar
Christoph Mallon committed
538
static void lower_Conv(ir_node *const n)
539
{
Christoph Mallon's avatar
Christoph Mallon committed
540
541
542
543
544
545
546
547
548
549
	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))
			return;
550
		if (mode_is_signed(mode))
Christoph Mallon's avatar
Christoph Mallon committed
551
			name = "fix";
552
		else
Christoph Mallon's avatar
Christoph Mallon committed
553
554
555
556
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);
			return;
		}
		if (get_mode_size_bits(op_mode) > get_mode_size_bits(mode))
			name = "trunc";
575
		else
Christoph Mallon's avatar
Christoph Mallon committed
576
			name = "extend";
577
578
	}

Christoph Mallon's avatar
Christoph Mallon committed
579
580
	ir_node *const in[]   = { op };
	ir_node       *result = make_softfloat_call(n, name, ARRAY_SIZE(in), in);
581
582

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

Christoph Mallon's avatar
Christoph Mallon committed
586
	exchange(n, result);
587
588
589
590
591
}

/**
 * Transforms a Div into the appropriate soft float function.
 */
Christoph Mallon's avatar
Christoph Mallon committed
592
static void lower_Div(ir_node *const n)
593
{
Christoph Mallon's avatar
Christoph Mallon committed
594
595
	ir_mode *const mode = get_Div_resmode(n);
	if (!mode_is_float(mode))
596
597
		return;

Christoph Mallon's avatar
Christoph Mallon committed
598
599
600
601
	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);
602
	ir_node *const call   = skip_Proj(skip_Proj(result));
603
604
605

	set_irn_pinned(call, get_irn_pinned(n));

606
	foreach_out_edge_safe(n, edge) {
607
		ir_node *proj = get_edge_src_irn(edge);
Christoph Mallon's avatar
Christoph Mallon committed
608
		if (!is_Proj(proj))
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
			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
625
			exchange(proj, result);
626
627
			break;
		default:
628
			panic("unexpected Proj number");
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
		}
	}

}

/**
 * Adapts the resmode of a Div.
 */
static void lower_Div_mode(ir_node *n)
{
	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);

	set_irn_mode(n, lowered_mode);
	set_Div_resmode(n, lowered_res_mode);
}

/**
 * Adapts the ls_mode of a Load.
 */
static void lower_Load(ir_node *n)
{
	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);

	set_irn_mode(n, lowered_mode);
	set_Load_mode(n, lowered_ls_mode);
}

/**
 * Transforms a Minus into the appropriate soft float function.
 */
static void lower_Minus(ir_node *n)
{
Christoph Mallon's avatar
Christoph Mallon committed
667
668
	ir_mode *const mode = get_irn_mode(n);
	if (!mode_is_float(mode))
669
670
		return;

Christoph Mallon's avatar
Christoph Mallon committed
671
672
673
	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);
674

Christoph Mallon's avatar
Christoph Mallon committed
675
	exchange(n, result);
676
677
678
679
680
681
682
}

/**
 * Transforms a Mul into the appropriate soft float function.
 */
static void lower_Mul(ir_node *n)
{
Christoph Mallon's avatar
Christoph Mallon committed
683
684
	ir_mode *const mode = get_irn_mode(n);
	if (!mode_is_float(mode))
685
686
		return;

Christoph Mallon's avatar
Christoph Mallon committed
687
688
689
690
	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);
691

Christoph Mallon's avatar
Christoph Mallon committed
692
	exchange(n, result);
693
694
695
696
697
698
699
}

/**
 * Transforms a Sub into the appropriate soft float function.
 */
static void lower_Sub(ir_node *n)
{
Christoph Mallon's avatar
Christoph Mallon committed
700
701
	ir_mode *const mode = get_irn_mode(n);
	if (!mode_is_float(mode))
702
703
		return;

Christoph Mallon's avatar
Christoph Mallon committed
704
705
706
707
	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);
708

Christoph Mallon's avatar
Christoph Mallon committed
709
	exchange(n, result);
710
711
712
713
714
715
716
717
718
719
}

/**
 * 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
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
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);
	}
}

739
740
741
742
743
/*
 * Initializes softfloat lowering.
 */
static void ir_prepare_softfloat_lowering(void)
{
Christoph Mallon's avatar
Christoph Mallon committed
744
	if (!lowered_type)
745
746
		lowered_type = pmap_create();

Christoph Mallon's avatar
Christoph Mallon committed
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
	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
764
765
	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
766
767
768
769
770
771
772
773
774
775
776
777
778
	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);
779
780
781
782
783
784
785
786
787
788
789
790
791
792
}

/**
 * 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
793
	ir_clear_opcodes_generic_func();
794
795
796
797
798
799
800
801
	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);

Christoph Mallon's avatar
Christoph Mallon committed
802
803
804
	size_t const n_irgs = get_irp_n_irgs();
	for (size_t i = 0; i < n_irgs; ++i) {
		ir_graph *const irg = get_irp_irg(i);
805
806
807

		ir_nodeset_init(&created_mux_nodes);

808
		assure_edges(irg);
809
810
811
812
813
814
815
816
817

		irg_walk_graph(irg, NULL, lower_node, NULL);

		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
818
	ir_clear_opcodes_generic_func();
819
820
821
822
823
	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);

Christoph Mallon's avatar
Christoph Mallon committed
824
825
826
827
828
	for (size_t i = 0; i < n_irgs; ++i) {
		ir_graph  *const irg         = get_irp_irg(i);
		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);
829
830
831
832
		if (lowered_mtp != mtp)
			set_entity_type(ent, lowered_mtp);

		irg_walk_graph(irg, NULL, lower_mode, NULL);
833
834

		/* fixup parameter entities */
Christoph Mallon's avatar
Christoph Mallon committed
835
836
837
838
839
		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);
840
			if (is_Primitive_type(type)) {
Christoph Mallon's avatar
Christoph Mallon committed
841
				ir_type *const lowered = lower_type(type);
842
843
844
				set_entity_type(member, lowered);
			}
		}
845
846
	}
}