lower_softfloat.c 22.3 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
205
}

/**
 * @return A SymConst representing the function that replaces the given node.
 */
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
282
	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);
	symconst_symbol sym;
283
284
285
286
	sym.entity_p = ent;
	return new_r_SymConst(irg, mode_P_code, sym, symconst_addr_ent);
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

529
530
531
532
533
534
	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);
535

536
537
	set_irn_mode(n, lowered_mode);
	set_Const_tarval(n, int_tv);
538
539
540
541
542
}

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

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

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

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

/**
 * Transforms a Div into the appropriate soft float function.
 */
Christoph Mallon's avatar
Christoph Mallon committed
597
static void 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
602
		return;

Christoph Mallon's avatar
Christoph Mallon committed
603
604
605
606
607
	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);
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
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
670
671
		}
	}

}

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

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

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

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

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

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

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

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

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

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

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

Christoph Mallon's avatar
Christoph Mallon committed
751
	if (!lowered_type)
752
753
		lowered_type = pmap_create();

Christoph Mallon's avatar
Christoph Mallon committed
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
784
785
	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);
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
}

/**
 * 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
802
	ir_clear_opcodes_generic_func();
803
804
805
806
807
808
809
810
	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
811
812
813
	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);
814
815
816

		ir_nodeset_init(&created_mux_nodes);

817
		assure_edges(irg);
818
819
820
821
822
823
824
825
826

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

		irg_walk_graph(irg, NULL, lower_mode, NULL);
842
843

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