lower_softfloat.c 22.2 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
62
63
64
#include "irprog_t.h"
#include "lower_softfloat.h"
#include "lowering.h"
#include "pmap.h"
#include "type_t.h"
#include "tv_t.h"

/** The debug handle */
DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)

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;

65
static ir_nodeset_t created_mux_nodes;
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
121
122
123

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

	case iro_Conv: {
		ir_mode *const mode = get_irn_mode(n);
145
146
147
148
149
150
151
152
153
154
155
		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
156
		} else if (operand_mode == mode_F) {
157
158
159
160
161
162
163
164
165
166
			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
167
		} else if (operand_mode == mode_Is || operand_mode == mode_Hs || operand_mode == mode_Bs) {
168
169
170
171
			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
172
		} else if (operand_mode == mode_Iu || operand_mode == mode_Hu || operand_mode == mode_Bu) {
173
174
175
176
			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
177
		} else if (operand_mode == mode_Ls) {
178
179
180
181
			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
182
		} else if (operand_mode == mode_Lu) {
183
184
185
186
187
188
			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
189
190
	}

191
192
193
194
195
196
197
198
199
	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;
	}

200
	panic("Could not determine a suitable type");
201
202
203
}

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

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

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

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

270
271
272
	assert(float_types <= 3);
	assert(double_types <= 3);

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

Christoph Mallon's avatar
Christoph Mallon committed
279
280
281
	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);
282
	return new_r_Address(irg, ent);
283
284
}

Christoph Mallon's avatar
Christoph Mallon committed
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
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;
}

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

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

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

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

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

366
	set_higher_type(res, mtp);
367
368
369
370
371
372
373
374
375
376
377

	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
378
	ir_type *tp         = get_Call_type(node);
379

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

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

Christoph Mallon's avatar
Christoph Mallon committed
400
	if (!need_lower)
401
402
403
404
405
406
407
408
409
		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
410
static void lower_Cmp(ir_node *const n)
411
{
Christoph Mallon's avatar
Christoph Mallon committed
412
413
414
	ir_node *const left    = get_Cmp_left(n);
	ir_mode *const op_mode = get_irn_mode(left);
	if (!mode_is_float(op_mode))
415
416
		return;

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

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

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

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

	/* We need two calls into the softfloat library */
Christoph Mallon's avatar
Christoph Mallon committed
497
498
499
500
501
502
503
504
505
	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))
506
507
508
509
510
511
512
513
514
515
516
			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
517
static void lower_Const(ir_node *const n)
518
{
519
520
	ir_mode *mode = get_irn_mode(n);
	if (!mode_is_float(mode))
521
522
		return;

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
537
538
539
540
}

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

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

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

Christoph Mallon's avatar
Christoph Mallon committed
589
	exchange(n, result);
590
591
592
593
594
}

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

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

	set_irn_pinned(call, get_irn_pinned(n));

609
	foreach_out_edge_safe(n, edge) {
610
		ir_node *proj = get_edge_src_irn(edge);
Christoph Mallon's avatar
Christoph Mallon committed
611
		if (!is_Proj(proj))
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
			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
628
			exchange(proj, result);
629
630
			break;
		default:
631
			panic("unexpected Proj number");
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
667
668
669
		}
	}

}

/**
 * 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
670
671
	ir_mode *const mode = get_irn_mode(n);
	if (!mode_is_float(mode))
672
673
		return;

Christoph Mallon's avatar
Christoph Mallon committed
674
675
676
	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);
677

Christoph Mallon's avatar
Christoph Mallon committed
678
	exchange(n, result);
679
680
681
682
683
684
685
}

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

Christoph Mallon's avatar
Christoph Mallon committed
690
691
692
693
	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);
694

Christoph Mallon's avatar
Christoph Mallon committed
695
	exchange(n, result);
696
697
698
699
700
701
702
}

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

Christoph Mallon's avatar
Christoph Mallon committed
707
708
709
710
	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);
711

Christoph Mallon's avatar
Christoph Mallon committed
712
	exchange(n, result);
713
714
715
716
717
718
719
720
721
722
}

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

742
743
744
745
746
747
748
/*
 * Initializes softfloat lowering.
 */
static void ir_prepare_softfloat_lowering(void)
{
	FIRM_DBG_REGISTER(dbg, "firm.lower.softfloat");

Christoph Mallon's avatar
Christoph Mallon committed
749
	if (!lowered_type)
750
751
		lowered_type = pmap_create();

Christoph Mallon's avatar
Christoph Mallon committed
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
	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);
	make_unop_type(&unop_tp_d_ls, type_D,  type_Is);
	make_unop_type(&unop_tp_d_lu, type_D,  type_Iu);
	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);
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
}

/**
 * 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)
{
	FIRM_DBG_REGISTER(dbg, "firm.lower.softfloat");

	ir_prepare_softfloat_lowering();

Matthias Braun's avatar
Matthias Braun committed
800
	ir_clear_opcodes_generic_func();
801
802
803
804
805
806
807
808
	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
809
810
811
	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);
812
813
814

		ir_nodeset_init(&created_mux_nodes);

815
		assure_edges(irg);
816
817
818
819
820
821
822
823
824

		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
825
	ir_clear_opcodes_generic_func();
826
827
828
829
830
	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
831
832
833
834
835
	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);
836
837
838
839
		if (lowered_mtp != mtp)
			set_entity_type(ent, lowered_mtp);

		irg_walk_graph(irg, NULL, lower_mode, NULL);
840
841

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