ia32_intrinsics.c 27.9 KB
Newer Older
Christian Würdig's avatar
Christian Würdig committed
1
/*
Michael Beck's avatar
Michael Beck committed
2
 * Copyright (C) 1995-2008 University of Karlsruhe.  All right reserved.
Christian Würdig's avatar
Christian Würdig committed
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *
 * This file is part of libFirm.
 *
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 2 as published by the Free Software
 * Foundation and appearing in the file LICENSE.GPL included in the
 * packaging of this file.
 *
 * Licensees holding valid libFirm Professional Edition licenses may use
 * this file in accordance with the libFirm Commercial License.
 * Agreement provided with the Software.
 *
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE.
 */

20
/**
Christian Würdig's avatar
Christian Würdig committed
21
22
23
24
25
 * @file
 * @brief       This file implements the mapping of 64Bit intrinsic
 *              functions to code or library calls.
 * @author      Michael Beck
 * @version     $Id$
26
 */
27
28
#include "config.h"

29
#include "iredges.h"
30
#include "irgmod.h"
31
32
33
34
#include "irop.h"
#include "irnode_t.h"
#include "ircons.h"
#include "irprog_t.h"
35
#include "lowering.h"
36
#include "array.h"
Matthias Braun's avatar
Matthias Braun committed
37
#include "error.h"
38

39
#include "ia32_new_nodes.h"
40
41
#include "bearch_ia32_t.h"
#include "gen_ia32_regalloc_if.h"
42

43
44
45
/** The array of all intrinsics that must be mapped. */
static i_record *intrinsics;

46
/** An array to cache all entities. */
47
static ir_entity *i_ents[iro_Last + 1];
48

49
/*
50
51
52
53
54
 * Maps all intrinsic calls that the backend support
 * and map all instructions the backend did not support
 * to runtime calls.
 */
void ia32_handle_intrinsics(void) {
Michael Beck's avatar
Michael Beck committed
55
56
57
	if (intrinsics && ARR_LEN(intrinsics) > 0) {
		lower_intrinsics(intrinsics, ARR_LEN(intrinsics), /*part_block_used=*/1);
	}
58
59
60
61
62
63
64
}

#define BINOP_Left_Low   0
#define BINOP_Left_High  1
#define BINOP_Right_Low  2
#define BINOP_Right_High 3

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
/**
 * Reroute edges from the pn_Call_T_result proj of a call.
 *
 * @param proj   the pn_Call_T_result Proj
 * @param l_res  the lower 32 bit result
 * @param h_res  the upper 32 bit result or NULL
 * @param irg    the graph to replace on
 */
static void reroute_result(ir_node *proj, ir_node *l_res, ir_node *h_res, ir_graph *irg) {
	const ir_edge_t *edge, *next;

	foreach_out_edge_safe(proj, edge, next) {
		ir_node *proj = get_edge_src_irn(edge);
		long    pn    = get_Proj_proj(proj);

		if (pn == 0) {
			edges_reroute(proj, l_res, irg);
		} else if (pn == 1 && h_res != NULL) {
			edges_reroute(proj, h_res, irg);
		} else {
			panic("Unsupported Result-Proj from Call found");
		}
	}
}

90
91
/**
 * Replace a call be a tuple of l_res, h_res.
92
93
94
95
96
97
 *
 * @param call   the call node to replace
 * @param l_res  the lower 32 bit result
 * @param h_res  the upper 32 bit result or NULL
 * @param irg    the graph to replace on
 * @param block  the block to replace on (always the call block)
98
 */
99
static void resolve_call(ir_node *call, ir_node *l_res, ir_node *h_res, ir_graph *irg, ir_node *block) {
100
	ir_node *jmp, *res, *in[2];
Christoph Mallon's avatar
Christoph Mallon committed
101
102
	ir_node *bad   = get_irg_bad(irg);
	ir_node *nomem = get_irg_no_mem(irg);
103
	int     old_cse;
104

105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
	if (edges_activated(irg)) {
		/* use rerouting to prevent some warning in the backend */
		const ir_edge_t *edge, *next;

		foreach_out_edge_safe(call, edge, next) {
			ir_node *proj = get_edge_src_irn(edge);
			pn_Call pn    = get_Proj_proj(proj);

			switch (pn) {
			case pn_Call_X_regular:
				/* Beware:
				 * We do not check here if this call really has exception and regular Proj's.
				 * new_r_Jmp might than be CSEd with the real exit jmp and then bad things happen
				 * (in movgen.c from 186.crafty for example).
				 * So be sure the newly created Jmp cannot CSE.
				 */
				old_cse = get_opt_cse();
				set_opt_cse(0);
123
				jmp = new_r_Jmp(block);
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
				set_opt_cse(old_cse);
				edges_reroute(proj, jmp, irg);
				break;

			case pn_Call_X_except:
			case pn_Call_P_value_res_base:
				/* should not happen here */
				edges_reroute(proj, bad, irg);
				break;
			case pn_Call_M_except:
				/* should not happen here */
				edges_reroute(proj, nomem, irg);
				break;
			case pn_Call_T_result:
				reroute_result(proj, l_res, h_res, irg);
				break;
			default:
				panic("Wrong Proj from Call");
			}
			kill_node(proj);
		}
		kill_node(call);
	} else {
		/* no edges, build Tuple */
		if (h_res == NULL)
			res = l_res;
		else {
			in[0] = l_res;
			in[1] = h_res;
153
			res = new_r_Tuple(block, 2, in);
154
155
156
157
158
159
160
161
162
163
164
165
166
		}

		turn_into_tuple(call, pn_Call_max);
		set_Tuple_pred(call, pn_Call_M_regular,        nomem);
		/*
		 * Beware:
		 * We do not check here if this call really has exception and regular Proj's.
		 * new_r_Jmp might than be CSEd with the real exit jmp and then bad things happen
		 * (in movgen.c from 186.crafty for example).
		 * So be sure the newly created Jmp cannot CSE.
		 */
		old_cse = get_opt_cse();
		set_opt_cse(0);
167
		jmp = new_r_Jmp(block);
168
169
170
171
172
173
174
175
		set_opt_cse(old_cse);

		set_Tuple_pred(call, pn_Call_X_regular,        jmp);
		set_Tuple_pred(call, pn_Call_X_except,         bad);
		set_Tuple_pred(call, pn_Call_T_result,         res);
		set_Tuple_pred(call, pn_Call_M_except,         nomem);
		set_Tuple_pred(call, pn_Call_P_value_res_base, bad);
	}
176
177
178
179
180
181
}

/**
 * Map an Add (a_l, a_h, b_l, b_h)
 */
static int map_Add(ir_node *call, void *ctx) {
182
183
184
185
186
187
188
189
190
191
192
193
194
	dbg_info *dbg        = get_irn_dbg_info(call);
	ir_node  *block      = get_nodes_block(call);
	ir_node  **params    = get_Call_param_arr(call);
	ir_type  *method     = get_Call_type(call);
	ir_node  *a_l        = params[BINOP_Left_Low];
	ir_node  *a_h        = params[BINOP_Left_High];
	ir_node  *b_l        = params[BINOP_Right_Low];
	ir_node  *b_h        = params[BINOP_Right_High];
	ir_mode  *l_mode     = get_type_mode(get_method_res_type(method, 0));
	ir_mode  *h_mode     = get_type_mode(get_method_res_type(method, 1));
	ir_mode  *mode_flags = ia32_reg_classes[CLASS_ia32_flags].mode;
	ir_node  *add_low, *add_high, *flags;
	ir_node  *l_res, *h_res;
Matthias Braun's avatar
Matthias Braun committed
195
	(void) ctx;
196
197
198

	/* l_res = a_l + b_l */
	/* h_res = a_h + b_h + carry */
199

200
	add_low  = new_bd_ia32_l_Add(dbg, block, a_l, b_l, mode_T);
201
	flags    = new_r_Proj(block, add_low, mode_flags, pn_ia32_flags);
202
	add_high = new_bd_ia32_l_Adc(dbg, block, a_h, b_h, flags, h_mode);
203

204
	l_res = new_r_Proj(block, add_low, l_mode, pn_ia32_res);
205
	h_res = add_high;
206

207
	resolve_call(call, l_res, h_res, current_ir_graph, block);
208
209
210
211
212
213
	return 1;
}

/**
 * Map a Sub (a_l, a_h, b_l, b_h)
 */
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
static int map_Sub(ir_node *call, void *ctx)
{
	dbg_info *dbg        = get_irn_dbg_info(call);
	ir_node  *block      = get_nodes_block(call);
	ir_node  **params    = get_Call_param_arr(call);
	ir_type  *method     = get_Call_type(call);
	ir_node  *a_l        = params[BINOP_Left_Low];
	ir_node  *a_h        = params[BINOP_Left_High];
	ir_node  *b_l        = params[BINOP_Right_Low];
	ir_node  *b_h        = params[BINOP_Right_High];
	ir_mode  *l_mode     = get_type_mode(get_method_res_type(method, 0));
	ir_mode  *h_mode     = get_type_mode(get_method_res_type(method, 1));
	ir_mode  *mode_flags = ia32_reg_classes[CLASS_ia32_flags].mode;
	ir_node  *sub_low, *sub_high, *flags;
	ir_node  *l_res, *h_res;
Matthias Braun's avatar
Matthias Braun committed
229
	(void) ctx;
230
231

	/* l_res = a_l - b_l */
232
	/* h_res = a_h - b_h - carry */
233

234
	sub_low  = new_bd_ia32_l_Sub(dbg, block, a_l, b_l, mode_T);
235
	flags    = new_r_Proj(block, sub_low, mode_flags, pn_ia32_flags);
236
	sub_high = new_bd_ia32_l_Sbb(dbg, block, a_h, b_h, flags, h_mode);
237

238
	l_res = new_r_Proj( block, sub_low, l_mode, pn_ia32_res);
239
	h_res = sub_high;
240

241
	resolve_call(call, l_res, h_res, current_ir_graph, block);
242
243
244
	return 1;
}

245
246
247
248
/**
 * Map a Shl (a_l, a_h, count)
 */
static int map_Shl(ir_node *call, void *ctx) {
249
250
251
252
253
254
255
256
257
	ir_graph *irg     = current_ir_graph;
	dbg_info *dbg     = get_irn_dbg_info(call);
	ir_node  *block   = get_nodes_block(call);
	ir_node  **params = get_Call_param_arr(call);
	ir_type  *method  = get_Call_type(call);
	ir_node  *a_l     = params[BINOP_Left_Low];
	ir_node  *a_h     = params[BINOP_Left_High];
	ir_node  *cnt     = params[BINOP_Right_Low];
	ir_mode  *l_mode  = get_type_mode(get_method_res_type(method, 0));
258
	ir_mode  *h_mode  = get_type_mode(get_method_res_type(method, 1));
Michael Beck's avatar
Michael Beck committed
259
260
	ir_mode  *c_mode;
	ir_node  *l_res, *h_res, *irn, *cond, *upper, *n_block, *l1, *l2, *h1, *h2, *in[2];
Matthias Braun's avatar
Matthias Braun committed
261
	(void) ctx;
262

Michael Beck's avatar
Michael Beck committed
263
264
265
266
267
268
269
	if (is_Const(cnt)) {
		/* the shift count is a const, create better code */
		tarval *tv = get_Const_tarval(cnt);

		if (tarval_cmp(tv, new_tarval_from_long(32, l_mode)) & (pn_Cmp_Gt|pn_Cmp_Eq)) {
			/* simplest case: shift only the lower bits. Note that there is no
			   need to reduce the constant here, this is done by the hardware.  */
270
271
			ir_node *conv = new_rd_Conv(dbg, block, a_l, h_mode);
			h_res = new_rd_Shl(dbg, block, conv, cnt, h_mode);
272
			l_res = new_rd_Const(dbg, irg, get_mode_null(l_mode));
Michael Beck's avatar
Michael Beck committed
273

274
275
		} else {
			/* h_res = SHLD a_h, a_l, cnt */
276
			h_res = new_bd_ia32_l_ShlD(dbg, block, a_h, a_l, cnt, h_mode);
277
278

			/* l_res = SHL a_l, cnt */
279
			l_res = new_bd_ia32_l_ShlDep(dbg, block, a_l, cnt, h_res, l_mode);
Michael Beck's avatar
Michael Beck committed
280
		}
281
282
283

		resolve_call(call, l_res, h_res, irg, block);
		return 1;
Michael Beck's avatar
Michael Beck committed
284
285
286
287
288
	}

	part_block(call);
	upper = get_nodes_block(call);

289
	/* h_res = SHLD a_h, a_l, cnt */
290
	h1 = new_bd_ia32_l_ShlD(dbg, upper, a_h, a_l, cnt, h_mode);
291
292

	/* l_res = SHL a_l, cnt */
293
	l1 = new_bd_ia32_l_ShlDep(dbg, upper, a_l, cnt, h1, l_mode);
Michael Beck's avatar
Michael Beck committed
294
295

	c_mode = get_irn_mode(cnt);
296
	irn    = new_r_Const_long(irg, c_mode, 32);
297
298
299
300
	irn    = new_rd_And(dbg, upper, cnt, irn, c_mode);
	irn    = new_rd_Cmp(dbg, upper, irn, new_r_Const(irg, get_mode_null(c_mode)));
	irn    = new_r_Proj(upper, irn, mode_b, pn_Cmp_Eq);
	cond   = new_rd_Cond(dbg, upper, irn);
Michael Beck's avatar
Michael Beck committed
301

302
303
	in[0]  = new_r_Proj(upper, cond, mode_X, pn_Cond_true);
	in[1]  = new_r_Proj(upper, cond, mode_X, pn_Cond_false);
Michael Beck's avatar
Michael Beck committed
304
305
306

	/* the block for cnt >= 32 */
	n_block = new_rd_Block(dbg, irg, 1, &in[1]);
307
	h2      = new_rd_Conv(dbg, n_block, l1, h_mode);
308
	l2      = new_r_Const(irg, get_mode_null(l_mode));
309
	in[1]   = new_r_Jmp(n_block);
Michael Beck's avatar
Michael Beck committed
310
311
312
313
314

	set_irn_in(block, 2, in);

	in[0] = l1;
	in[1] = l2;
315
	l_res = new_r_Phi(block, 2, in, l_mode);
Michael Beck's avatar
Michael Beck committed
316
	set_Block_phis(block, l_res);
Michael Beck's avatar
Michael Beck committed
317
318
319

	in[0] = h1;
	in[1] = h2;
320
	h_res = new_r_Phi(block, 2, in, h_mode);
Michael Beck's avatar
Michael Beck committed
321
322
	set_Phi_next(l_res, h_res);
	set_Phi_next(h_res, NULL);
Michael Beck's avatar
Michael Beck committed
323
324
325
326
327

	/* move it down */
	set_nodes_block(call, block);
	for (irn = get_irn_link(call); irn != NULL; irn = get_irn_link(irn))
		set_nodes_block(irn, block);
328
329
330
331
332
333
334
335
336

	resolve_call(call, l_res, h_res, irg, block);
	return 1;
}

/**
 * Map a Shr (a_l, a_h, count)
 */
static int map_Shr(ir_node *call, void *ctx) {
337
338
339
340
341
342
343
344
345
	ir_graph *irg     = current_ir_graph;
	dbg_info *dbg     = get_irn_dbg_info(call);
	ir_node  *block   = get_nodes_block(call);
	ir_node  **params = get_Call_param_arr(call);
	ir_type  *method  = get_Call_type(call);
	ir_node  *a_l     = params[BINOP_Left_Low];
	ir_node  *a_h     = params[BINOP_Left_High];
	ir_node  *cnt     = params[BINOP_Right_Low];
	ir_mode  *l_mode  = get_type_mode(get_method_res_type(method, 0));
346
	ir_mode  *h_mode  = get_type_mode(get_method_res_type(method, 1));
Michael Beck's avatar
Michael Beck committed
347
348
	ir_mode  *c_mode;
	ir_node  *l_res, *h_res, *irn, *cond, *upper, *n_block, *l1, *l2, *h1, *h2, *in[2];
Matthias Braun's avatar
Matthias Braun committed
349
	(void) ctx;
350

Michael Beck's avatar
Michael Beck committed
351
352
353
354
355
356
357
	if (is_Const(cnt)) {
		/* the shift count is a const, create better code */
		tarval *tv = get_Const_tarval(cnt);

		if (tarval_cmp(tv, new_tarval_from_long(32, l_mode)) & (pn_Cmp_Gt|pn_Cmp_Eq)) {
			/* simplest case: shift only the higher bits. Note that there is no
			   need to reduce the constant here, this is done by the hardware.  */
358
			ir_node *conv = new_rd_Conv(dbg, block, a_h, l_mode);
359
			h_res = new_rd_Const(dbg, irg, get_mode_null(h_mode));
360
			l_res = new_rd_Shr(dbg, block, conv, cnt, l_mode);
361
362
		} else {
			/* l_res = SHRD a_h:a_l, cnt */
363
			l_res = new_bd_ia32_l_ShrD(dbg, block, a_l, a_h, cnt, l_mode);
Michael Beck's avatar
Michael Beck committed
364

365
			/* h_res = SHR a_h, cnt */
366
			h_res = new_bd_ia32_l_ShrDep(dbg, block, a_h, cnt, l_res, h_mode);
Michael Beck's avatar
Michael Beck committed
367
		}
368
369
		resolve_call(call, l_res, h_res, irg, block);
		return 1;
Michael Beck's avatar
Michael Beck committed
370
371
372
373
374
	}

	part_block(call);
	upper = get_nodes_block(call);

375
	/* l_res = SHRD a_h:a_l, cnt */
376
	l1 = new_bd_ia32_l_ShrD(dbg, upper, a_l, a_h, cnt, l_mode);
377

378
	/* h_res = SHR a_h, cnt */
379
	h1 = new_bd_ia32_l_ShrDep(dbg, upper, a_h, cnt, l1, h_mode);
Michael Beck's avatar
Michael Beck committed
380
381

	c_mode = get_irn_mode(cnt);
382
	irn    = new_r_Const_long(irg, c_mode, 32);
383
384
385
386
	irn    = new_rd_And(dbg, upper, cnt, irn, c_mode);
	irn    = new_rd_Cmp(dbg, upper, irn, new_r_Const(irg, get_mode_null(c_mode)));
	irn    = new_r_Proj(upper, irn, mode_b, pn_Cmp_Eq);
	cond   = new_rd_Cond(dbg, upper, irn);
Michael Beck's avatar
Michael Beck committed
387

388
389
	in[0]  = new_r_Proj(upper, cond, mode_X, pn_Cond_true);
	in[1]  = new_r_Proj(upper, cond, mode_X, pn_Cond_false);
Michael Beck's avatar
Michael Beck committed
390
391
392

	/* the block for cnt >= 32 */
	n_block = new_rd_Block(dbg, irg, 1, &in[1]);
393
	l2      = new_rd_Conv(dbg, n_block, h1, l_mode);
394
	h2      = new_r_Const(irg, get_mode_null(h_mode));
395
	in[1]   = new_r_Jmp(n_block);
Michael Beck's avatar
Michael Beck committed
396
397
398
399
400

	set_irn_in(block, 2, in);

	in[0] = l1;
	in[1] = l2;
401
	l_res = new_r_Phi(block, 2, in, l_mode);
Michael Beck's avatar
Michael Beck committed
402
	set_Block_phis(block, l_res);
Michael Beck's avatar
Michael Beck committed
403
404
405

	in[0] = h1;
	in[1] = h2;
406
	h_res = new_r_Phi(block, 2, in, h_mode);
Michael Beck's avatar
Michael Beck committed
407
408
	set_Phi_next(l_res, h_res);
	set_Phi_next(h_res, NULL);
Michael Beck's avatar
Michael Beck committed
409
410
411
412
413

	/* move it down */
	set_nodes_block(call, block);
	for (irn = get_irn_link(call); irn != NULL; irn = get_irn_link(irn))
		set_nodes_block(irn, block);
414
415
416
417
418
419
420
421
422

	resolve_call(call, l_res, h_res, irg, block);
	return 1;
}

/**
 * Map a Shrs (a_l, a_h, count)
 */
static int map_Shrs(ir_node *call, void *ctx) {
Michael Beck's avatar
Michael Beck committed
423
424
425
426
427
428
429
430
431
	ir_graph *irg     = current_ir_graph;
	dbg_info *dbg     = get_irn_dbg_info(call);
	ir_node  *block   = get_nodes_block(call);
	ir_node  **params = get_Call_param_arr(call);
	ir_type  *method  = get_Call_type(call);
	ir_node  *a_l     = params[BINOP_Left_Low];
	ir_node  *a_h     = params[BINOP_Left_High];
	ir_node  *cnt     = params[BINOP_Right_Low];
	ir_mode  *l_mode  = get_type_mode(get_method_res_type(method, 0));
432
	ir_mode  *h_mode  = get_type_mode(get_method_res_type(method, 1));
Michael Beck's avatar
Michael Beck committed
433
434
	ir_mode  *c_mode;
	ir_node  *l_res, *h_res, *irn, *cond, *upper, *n_block, *l1, *l2, *h1, *h2, *in[2];
Matthias Braun's avatar
Matthias Braun committed
435
	(void) ctx;
436

Michael Beck's avatar
Michael Beck committed
437
438
439
440
441
442
443
	if (is_Const(cnt)) {
		/* the shift count is a const, create better code */
		tarval *tv = get_Const_tarval(cnt);

		if (tarval_cmp(tv, new_tarval_from_long(32, l_mode)) & (pn_Cmp_Gt|pn_Cmp_Eq)) {
			/* simplest case: shift only the higher bits. Note that there is no
			   need to reduce the constant here, this is done by the hardware.  */
444
			ir_node *conv    = new_rd_Conv(dbg, block, a_h, l_mode);
445
			ir_mode *c_mode  = get_irn_mode(cnt);
Michael Beck's avatar
Michael Beck committed
446

447
448
			h_res = new_rd_Shrs(dbg, block, a_h, new_r_Const_long(irg, c_mode, 31), h_mode);
			l_res = new_rd_Shrs(dbg, block, conv, cnt, l_mode);
449
450
		} else {
			/* l_res = SHRD a_h:a_l, cnt */
451
			l_res = new_bd_ia32_l_ShrD(dbg, block, a_l, a_h, cnt, l_mode);
Michael Beck's avatar
Michael Beck committed
452

453
			/* h_res = SAR a_h, cnt */
454
			h_res = new_bd_ia32_l_SarDep(dbg, block, a_h, cnt, l_res, h_mode);
Michael Beck's avatar
Michael Beck committed
455
		}
456
457
		resolve_call(call, l_res, h_res, irg, block);
		return 1;
Michael Beck's avatar
Michael Beck committed
458
459
460
461
462
	}

	part_block(call);
	upper = get_nodes_block(call);

463
	/* l_res = SHRD a_h:a_l, cnt */
464
	l1 = new_bd_ia32_l_ShrD(dbg, upper, a_l, a_h, cnt, l_mode);
465
466

	/* h_res = SAR a_h, cnt */
467
	h1 = new_bd_ia32_l_SarDep(dbg, upper, a_h, cnt, l1, h_mode);
Michael Beck's avatar
Michael Beck committed
468
469

	c_mode = get_irn_mode(cnt);
470
	irn    = new_r_Const_long(irg, c_mode, 32);
471
472
473
474
	irn    = new_rd_And(dbg, upper, cnt, irn, c_mode);
	irn    = new_rd_Cmp(dbg, upper, irn, new_r_Const(irg, get_mode_null(c_mode)));
	irn    = new_r_Proj(upper, irn, mode_b, pn_Cmp_Eq);
	cond   = new_rd_Cond(dbg, upper, irn);
Michael Beck's avatar
Michael Beck committed
475

476
477
	in[0]  = new_r_Proj(upper, cond, mode_X, pn_Cond_true);
	in[1]  = new_r_Proj(upper, cond, mode_X, pn_Cond_false);
Michael Beck's avatar
Michael Beck committed
478
479
480

	/* the block for cnt >= 32 */
	n_block = new_rd_Block(dbg, irg, 1, &in[1]);
481
482
483
	l2      = new_rd_Conv(dbg, n_block, h1, l_mode);
	h2      = new_rd_Shrs(dbg, n_block, a_h, new_r_Const_long(irg, c_mode, 31), h_mode);
	in[1]   = new_r_Jmp(n_block);
Michael Beck's avatar
Michael Beck committed
484
485
486
487
488

	set_irn_in(block, 2, in);

	in[0] = l1;
	in[1] = l2;
489
	l_res = new_r_Phi(block, 2, in, l_mode);
Michael Beck's avatar
Michael Beck committed
490
	set_Block_phis(block, l_res);
Michael Beck's avatar
Michael Beck committed
491
492
493

	in[0] = h1;
	in[1] = h2;
494
	h_res = new_r_Phi(block, 2, in, h_mode);
Michael Beck's avatar
Michael Beck committed
495
496
	set_Phi_next(l_res, h_res);
	set_Phi_next(h_res, NULL);
Michael Beck's avatar
Michael Beck committed
497
498
499
500
501

	/* move it down */
	set_nodes_block(call, block);
	for (irn = get_irn_link(call); irn != NULL; irn = get_irn_link(irn))
		set_nodes_block(irn, block);
502
503
504
505
506

	resolve_call(call, l_res, h_res, irg, block);
	return 1;
}

507
508
509
/**
 * Checks where node high is a sign extension of low.
 */
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
static int is_sign_extend(ir_node *low, ir_node *high)
{
	if (is_Shrs(high)) {
		ir_node *high_l;
		ir_node *high_r;
		tarval  *shift_count;

		high_r = get_Shrs_right(high);
		if (!is_Const(high_r)) return 0;

		shift_count = get_Const_tarval(high_r);
		if (!tarval_is_long(shift_count))       return 0;
		if (get_tarval_long(shift_count) != 31) return 0;

		high_l = get_Shrs_left(high);

		if (is_Conv(low)    && get_Conv_op(low)    == high_l) return 1;
		if (is_Conv(high_l) && get_Conv_op(high_l) == low)    return 1;
	} else if (is_Const(low) && is_Const(high)) {
		tarval *tl = get_Const_tarval(low);
		tarval *th = get_Const_tarval(high);

		if (tarval_is_long(th) && tarval_is_long(tl)) {
			long l = get_tarval_long(tl);
			long h = get_tarval_long(th);

			return (h == 0  && l >= 0) || (h == -1 && l <  0);
		}
	}

	return 0;
}

543
544
545
546
/**
 * Map a Mul (a_l, a_h, b_l, b_h)
 */
static int map_Mul(ir_node *call, void *ctx) {
Michael Beck's avatar
Michael Beck committed
547
548
549
550
551
552
553
554
555
	dbg_info *dbg     = get_irn_dbg_info(call);
	ir_node  *block   = get_nodes_block(call);
	ir_node  **params = get_Call_param_arr(call);
	ir_type  *method  = get_Call_type(call);
	ir_node  *a_l     = params[BINOP_Left_Low];
	ir_node  *a_h     = params[BINOP_Left_High];
	ir_node  *b_l     = params[BINOP_Right_Low];
	ir_node  *b_h     = params[BINOP_Right_High];
	ir_mode  *l_mode  = get_type_mode(get_method_res_type(method, 0));
556
	ir_mode  *h_mode  = get_type_mode(get_method_res_type(method, 1));
557
	ir_node  *l_res, *h_res, *mul, *pEDX, *add;
Matthias Braun's avatar
Matthias Braun committed
558
	(void) ctx;
559
560
561
562
563
564
565
566
567
568

	/*
		EDX:EAX = a_l * b_l
		l_res   = EAX

		t1 = b_l * a_h
		t2 = t1 + EDX
		t3 = a_l * b_h
		h_res = t2 + t3
	*/
Michael Beck's avatar
Michael Beck committed
569

570
	/* handle the often used case of 32x32=64 mul */
571
	if (is_sign_extend(a_l, a_h) && is_sign_extend(b_l, b_h)) {
572
		mul   = new_bd_ia32_l_IMul(dbg, block, a_l, b_l);
573
574
		h_res = new_rd_Proj(dbg, block, mul, h_mode, pn_ia32_l_IMul_res_high);
		l_res = new_rd_Proj(dbg, block, mul, l_mode, pn_ia32_l_IMul_res_low);
575
576
577
	} else {
		/* note that zero extension is handled hare efficiently */
		mul   = new_bd_ia32_l_Mul(dbg, block, a_l, b_l);
578
579
		pEDX  = new_rd_Proj(dbg, block, mul, h_mode, pn_ia32_l_Mul_res_high);
		l_res = new_rd_Proj(dbg, block, mul, l_mode, pn_ia32_l_Mul_res_low);
580
581
582
583
584
585
586

		b_l   = new_rd_Conv(dbg, block, b_l, h_mode);
		mul   = new_rd_Mul( dbg, block, a_h, b_l, h_mode);
		add   = new_rd_Add( dbg, block, mul, pEDX, h_mode);
		a_l   = new_rd_Conv(dbg, block, a_l, h_mode);
		mul   = new_rd_Mul( dbg, block, a_l, b_h, h_mode);
		h_res = new_rd_Add( dbg, block, add, mul, h_mode);
587
	}
588
	resolve_call(call, l_res, h_res, current_ir_graph, block);
589
590
591
592

	return 1;
}

593
594
595
596
/**
 * Map a Minus (a_l, a_h)
 */
static int map_Minus(ir_node *call, void *ctx) {
Michael Beck's avatar
Michael Beck committed
597
598
599
600
601
602
603
	dbg_info *dbg     = get_irn_dbg_info(call);
	ir_node  *block   = get_nodes_block(call);
	ir_node  **params = get_Call_param_arr(call);
	ir_type  *method  = get_Call_type(call);
	ir_node  *a_l     = params[BINOP_Left_Low];
	ir_node  *a_h     = params[BINOP_Left_High];
	ir_mode  *l_mode  = get_type_mode(get_method_res_type(method, 0));
604
	ir_mode  *h_mode  = get_type_mode(get_method_res_type(method, 1));
605
	ir_node  *l_res, *h_res, *res;
Matthias Braun's avatar
Matthias Braun committed
606
	(void) ctx;
607

608
	res   = new_bd_ia32_Minus64Bit(dbg, block, a_l, a_h);
609
610
	l_res = new_r_Proj(block, res, l_mode, pn_ia32_Minus64Bit_low_res);
	h_res = new_r_Proj(block, res, h_mode, pn_ia32_Minus64Bit_high_res);
611

612
	resolve_call(call, l_res, h_res, current_ir_graph, block);
613
614
615
616

	return 1;
}

617
618
619
620
/**
 * Map a Abs (a_l, a_h)
 */
static int map_Abs(ir_node *call, void *ctx) {
621
622
623
624
625
626
627
628
629
630
	dbg_info *dbg        = get_irn_dbg_info(call);
	ir_node  *block      = get_nodes_block(call);
	ir_node  **params    = get_Call_param_arr(call);
	ir_type  *method     = get_Call_type(call);
	ir_node  *a_l        = params[BINOP_Left_Low];
	ir_node  *a_h        = params[BINOP_Left_High];
	ir_mode  *l_mode     = get_type_mode(get_method_res_type(method, 0));
	ir_mode  *h_mode     = get_type_mode(get_method_res_type(method, 1));
	ir_mode  *mode_flags = ia32_reg_classes[CLASS_ia32_flags].mode;
	ir_node  *l_res, *h_res, *sign, *sub_l, *sub_h;
631
	ir_node  *sign_l;
632
633
	ir_node  *l_sub;
	ir_node  *flags;
Matthias Braun's avatar
Matthias Braun committed
634
	(void) ctx;
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649

	/*
		Code inspired by gcc output :) (although gcc doubles the
		operation for t1 as t2 and uses t1 for operations with low part
		and t2 for operations with high part which is actually unnecessary
		because t1 and t2 represent the same value)

		t1    = SHRS a_h, 31
		t2    = a_l ^ t1
		t3    = a_h ^ t1
		l_res = t2 - t1
		h_res = t3 - t1 - carry

	*/

Matthias Braun's avatar
Matthias Braun committed
650
	/* TODO: give a hint to the backend somehow to not create a cltd here... */
651
652
653
654
	sign   = new_rd_Shrs(dbg, block, a_h, new_Const_long(l_mode, 31), h_mode);
	sign_l = new_rd_Conv(dbg, block, sign, l_mode);
	sub_l  = new_rd_Eor(dbg, block, a_l, sign_l, l_mode);
	sub_h  = new_rd_Eor(dbg, block, a_h, sign,   h_mode);
655

656
	l_sub  = new_bd_ia32_l_Sub(dbg, block, sub_l, sign_l, mode_T);
657
658
	l_res  = new_r_Proj(block, l_sub, l_mode,     pn_ia32_res);
	flags  = new_r_Proj(block, l_sub, mode_flags, pn_ia32_flags);
659
	h_res  = new_bd_ia32_l_Sbb(dbg, block, sub_h, sign, flags, h_mode);
660

661
	resolve_call(call, l_res, h_res, current_ir_graph, block);
662
663
664
665

	return 1;
}

666
#define ID(x) new_id_from_chars(x, sizeof(x)-1)
Christian Würdig's avatar
Christian Würdig committed
667

668
/**
669
 * Maps a Div. Change into a library call.
670
 */
671
static int map_Div(ir_node *call, void *ctx) {
672
	ia32_intrinsic_env_t *env = ctx;
Michael Beck's avatar
Michael Beck committed
673
	ir_type   *method    = get_Call_type(call);
674
	ir_mode   *h_mode    = get_type_mode(get_method_res_type(method, 1));
675
676
677
	ir_node   *ptr;
	ir_entity *ent;
	symconst_symbol sym;
678

679
	if (mode_is_signed(h_mode)) {
680
681
682
683
684
685
686
687
688
		/* 64bit signed Division */
		ent = env->divdi3;
		if (ent == NULL) {
			/* create library entity */
			ent = env->divdi3 = new_entity(get_glob_type(), ID("__divdi3"), method);
			set_entity_visibility(ent, visibility_external_allocated);
			set_entity_ld_ident(ent, ID("__divdi3"));
		}
	} else {
689
		/* 64bit unsigned Division */
690
691
692
693
694
695
696
		ent = env->udivdi3;
		if (ent == NULL) {
			/* create library entity */
			ent = env->udivdi3 = new_entity(get_glob_type(), ID("__udivdi3"), method);
			set_entity_visibility(ent, visibility_external_allocated);
			set_entity_ld_ident(ent, ID("__udivdi3"));
		}
697
	}
698
699
700
	sym.entity_p = ent;
	ptr = get_Call_ptr(call);
	set_SymConst_symbol(ptr, sym);
701
702
703
	return 1;
}

704
705
706
/**
 * Maps a Mod. Change into a library call
 */
707
static int map_Mod(ir_node *call, void *ctx) {
708
709
	ia32_intrinsic_env_t *env = ctx;
	ir_type   *method    = get_Call_type(call);
710
	ir_mode   *h_mode    = get_type_mode(get_method_res_type(method, 1));
711
712
713
714
	ir_node   *ptr;
	ir_entity *ent;
	symconst_symbol sym;

715
	if (mode_is_signed(h_mode)) {
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
		/* 64bit signed Modulo */
		ent = env->moddi3;
		if (ent == NULL) {
			/* create library entity */
			ent = env->moddi3 = new_entity(get_glob_type(), ID("__moddi3"), method);
			set_entity_visibility(ent, visibility_external_allocated);
			set_entity_ld_ident(ent, ID("__moddi3"));
		}
	} else {
		/* 64bit signed Modulo */
		ent = env->umoddi3;
		if (ent == NULL) {
			/* create library entity */
			ent = env->umoddi3 = new_entity(get_glob_type(), ID("__umoddi3"), method);
			set_entity_visibility(ent, visibility_external_allocated);
			set_entity_ld_ident(ent, ID("__umoddi3"));
		}
	}
	sym.entity_p = ent;
	ptr = get_Call_ptr(call);
	set_SymConst_symbol(ptr, sym);
	return 1;
738
739
740
}

/**
741
 * Maps a Conv.
742
743
 */
static int map_Conv(ir_node *call, void *ctx) {
Matthias Braun's avatar
Matthias Braun committed
744
745
746
747
748
749
750
751
	ir_graph  *irg     = current_ir_graph;
	dbg_info  *dbg     = get_irn_dbg_info(call);
	ir_node   *block   = get_nodes_block(call);
	ir_node   **params = get_Call_param_arr(call);
	ir_type   *method  = get_Call_type(call);
	int       n        = get_Call_n_params(call);
	ir_node   *l_res, *h_res;
	(void) ctx;
752
753

	if (n == 1) {
754
755
		ir_node *float_to_ll;

756
757
758
759
760
761
762
		/* We have a Conv float -> long long here */
		ir_node *a_f        = params[0];
		ir_mode *l_res_mode = get_type_mode(get_method_res_type(method, 0));
		ir_mode *h_res_mode = get_type_mode(get_method_res_type(method, 1));

		assert(mode_is_float(get_irn_mode(a_f)) && "unexpected Conv call");

763
764
765
766
		if (mode_is_signed(h_res_mode)) {
			/* convert from float to signed 64bit */
			float_to_ll = new_bd_ia32_l_FloattoLL(dbg, block, a_f);

767
			l_res = new_r_Proj(block, float_to_ll, l_res_mode,
768
							   pn_ia32_l_FloattoLL_res_low);
769
			h_res = new_r_Proj(block, float_to_ll, h_res_mode,
770
771
772
773
774
							   pn_ia32_l_FloattoLL_res_high);
		} else {
			/* convert from float to signed 64bit */
			ir_mode *flt_mode = get_irn_mode(a_f);
			tarval  *flt_tv   = new_tarval_from_str("9223372036854775808", 19, flt_mode);
775
			ir_node *flt_corr = new_Const(flt_tv);
776
777
778
779
780
781
782
783
			ir_node *lower_blk = block;
			ir_node *upper_blk;
			ir_node *cmp, *proj, *cond, *blk, *int_phi, *flt_phi;
			ir_node *in[2];

			part_block(call);
			upper_blk = get_nodes_block(call);

784
785
786
787
788
			cmp   = new_rd_Cmp(dbg, upper_blk, a_f, flt_corr);
			proj  = new_r_Proj(upper_blk, cmp, mode_b, pn_Cmp_Lt);
			cond  = new_rd_Cond(dbg, upper_blk, proj);
			in[0] = new_r_Proj(upper_blk, cond, mode_X, pn_Cond_true);
			in[1] = new_r_Proj(upper_blk, cond, mode_X, pn_Cond_false);
789
			blk   = new_r_Block(irg, 1, &in[1]);
790
			in[1] = new_r_Jmp(blk);
791
792
793
794

			set_irn_in(lower_blk, 2, in);

			/* create to Phis */
795
			in[0] = new_Const(get_mode_null(h_res_mode));
796
797
			in[1] = new_Const_long(h_res_mode, 0x80000000);

798
			int_phi = new_r_Phi(lower_blk, 2, in, h_res_mode);
799
800

			in[0] = a_f;
801
			in[1] = new_rd_Sub(dbg, upper_blk, a_f, flt_corr, flt_mode);
802

803
			flt_phi = new_r_Phi(lower_blk, 2, in, flt_mode);
804

805
			/* fix Phi links for next part_block() */
806
807
808
			set_Block_phis(lower_blk, int_phi);
			set_Phi_next(int_phi, flt_phi);
			set_Phi_next(flt_phi, NULL);
809
810
811

			float_to_ll = new_bd_ia32_l_FloattoLL(dbg, lower_blk, flt_phi);

812
			l_res = new_r_Proj(lower_blk, float_to_ll, l_res_mode,
813
							   pn_ia32_l_FloattoLL_res_low);
814
			h_res = new_r_Proj(lower_blk, float_to_ll, h_res_mode,
815
816
							   pn_ia32_l_FloattoLL_res_high);

817
			h_res = new_rd_Add(dbg, lower_blk, h_res, int_phi, h_res_mode);
818
819
820
821
822
823
824
825

			/* move the call and its Proj's to the lower block */
			set_nodes_block(call, lower_blk);

			for (proj = get_irn_link(call); proj != NULL; proj = get_irn_link(proj))
				set_nodes_block(proj, lower_blk);
			block = lower_blk;
		}
826
827
		/* lower the call */
		resolve_call(call, l_res, h_res, irg, block);
828
829
830
	} else if (n == 2) {
		ir_node *ll_to_float;

831
832
833
834
835
		/* We have a Conv long long -> float here */
		ir_node *a_l       = params[BINOP_Left_Low];
		ir_node *a_h       = params[BINOP_Left_High];
		ir_mode *fres_mode = get_type_mode(get_method_res_type(method, 0));

Matthias Braun's avatar
Matthias Braun committed
836
837
		assert(! mode_is_float(get_irn_mode(a_l))
				&& ! mode_is_float(get_irn_mode(a_h)));
838

839
		ll_to_float = new_bd_ia32_l_LLtoFloat(dbg, block, a_h, a_l, fres_mode);
840
841

		/* lower the call */
842
843
844
		resolve_call(call, ll_to_float, NULL, irg, block);
	} else {
		panic("unexpected Conv call %+F", call);
845
846
847
848
849
	}

	return 1;
}

850
/* Ia32 implementation of intrinsic mapping. */
851
852
853
ir_entity *ia32_create_intrinsic_fkt(ir_type *method, const ir_op *op,
                                     const ir_mode *imode, const ir_mode *omode,
                                     void *context)
854
855
{
	i_record      elt;
856
	ir_entity     **ent = NULL;
857
858
859
860
861
862
863
864
865
866
867
868
869
870
	i_mapper_func mapper;

	if (! intrinsics)
		intrinsics = NEW_ARR_F(i_record, 0);

	switch (get_op_code(op)) {
	case iro_Add:
		ent    = &i_ents[iro_Add];
		mapper = map_Add;
		break;
	case iro_Sub:
		ent    = &i_ents[iro_Sub];
		mapper = map_Sub;
		break;
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
	case iro_Shl:
		ent    = &i_ents[iro_Shl];
		mapper = map_Shl;
		break;
	case iro_Shr:
		ent    = &i_ents[iro_Shr];
		mapper = map_Shr;
		break;
	case iro_Shrs:
		ent    = &i_ents[iro_Shrs];
		mapper = map_Shrs;
		break;
	case iro_Mul:
		ent    = &i_ents[iro_Mul];
		mapper = map_Mul;
		break;
887
888
889
890
891
892
893
894
	case iro_Minus:
		ent    = &i_ents[iro_Minus];
		mapper = map_Minus;
		break;
	case iro_Abs:
		ent    = &i_ents[iro_Abs];
		mapper = map_Abs;
		break;
895
896
897
898
899
900
901
902
903
904
905
906
	case iro_Div:
		ent    = &i_ents[iro_Div];
		mapper = map_Div;
		break;
	case iro_Mod:
		ent    = &i_ents[iro_Mod];
		mapper = map_Mod;
		break;
	case iro_Conv:
		ent    = &i_ents[iro_Conv];
		mapper = map_Conv;
		break;
907
	default:
Christian Würdig's avatar
Christian Würdig committed
908
		fprintf(stderr, "FIXME: unhandled op for ia32 intrinsic function %s\n", get_id_str(op->name));
909
910
911
912
913
914
		return def_create_intrinsic_fkt(method, op, imode, omode, context);
	}

	if (ent && ! *ent) {
#define IDENT(s)  new_id_from_chars(s, sizeof(s)-1)

915
		ident *id = id_mangle(IDENT("L"), get_op_ident(op));
916
917
918
919
920
921
		*ent = new_entity(get_glob_type(), id, method);
	}

	elt.i_call.kind     = INTRINSIC_CALL;
	elt.i_call.i_ent    = *ent;
	elt.i_call.i_mapper = mapper;
922
	elt.i_call.ctx      = context;
923
924
925
	elt.i_call.link     = NULL;

	ARR_APP1(i_record, intrinsics, elt);
Sebastian Hack's avatar
Sebastian Hack committed
926
	return *ent;
927
}