ia32_intrinsics.c 27.4 KB
Newer Older
Christian Würdig's avatar
Christian Würdig committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*
 * Copyright (C) 1995-2007 University of Karlsruhe.  All right reserved.
 *
 * 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
29
30
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

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

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

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

/** An array to cache all entities */
48
static ir_entity *i_ents[iro_MaxOpcode];
49

50
/*
51
52
53
54
55
 * 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
56
57
58
	if (intrinsics && ARR_LEN(intrinsics) > 0) {
		lower_intrinsics(intrinsics, ARR_LEN(intrinsics), /*part_block_used=*/1);
	}
59
60
61
62
63
64
65
}

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

66
67
68
/**
 * Replace a call be a tuple of l_res, h_res.
 */
69
70
static void resolve_call(ir_node *call, ir_node *l_res, ir_node *h_res, ir_graph *irg, ir_node *block) {
	ir_node *res, *in[2];
Christoph Mallon's avatar
Christoph Mallon committed
71
72
	ir_node *bad   = get_irg_bad(irg);
	ir_node *nomem = get_irg_no_mem(irg);
73
74
75

	in[0] = l_res;
	in[1] = h_res;
76
	res = new_r_Tuple(irg, block, h_res == NULL ? 1 : 2, in);
77
78

	turn_into_tuple(call, pn_Call_max);
Christoph Mallon's avatar
Christoph Mallon committed
79
	set_Tuple_pred(call, pn_Call_M_regular,        nomem);
Matthias Braun's avatar
Matthias Braun committed
80
81
82
83
	/* Matze: the new_r_Jmp here sometimes CSEs and then bad things happen
	 * (in movgen.c from 186.crafty for example) I don't know why it is here
	 * and if this fix is correct... */
	/*set_Tuple_pred(call, pn_Call_X_regular,        new_r_Jmp(irg, block));*/
Christoph Mallon's avatar
Christoph Mallon committed
84
85
	set_Tuple_pred(call, pn_Call_X_regular,        bad);
	set_Tuple_pred(call, pn_Call_X_except,         bad);
86
	set_Tuple_pred(call, pn_Call_T_result,         res);
Christoph Mallon's avatar
Christoph Mallon committed
87
88
	set_Tuple_pred(call, pn_Call_M_except,         nomem);
	set_Tuple_pred(call, pn_Call_P_value_res_base, bad);
89
90
91
92
93
94
}

/**
 * Map an Add (a_l, a_h, b_l, b_h)
 */
static int map_Add(ir_node *call, void *ctx) {
95
96
97
98
99
100
101
102
103
104
105
106
107
108
	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  *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
109
	(void) ctx;
110
111
112

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

114
115
116
117
118
119
	add_low  = new_rd_ia32_l_Add(dbg, irg, block, a_l, b_l, mode_T);
	flags    = new_r_Proj(irg, block, add_low, mode_flags, pn_ia32_flags);
	add_high = new_rd_ia32_l_Adc(dbg, irg, block, a_h, b_h, flags, h_mode);

	l_res = new_r_Proj(irg, block, add_low, l_mode, pn_ia32_res);
	h_res = add_high;
120

121
	resolve_call(call, l_res, h_res, irg, block);
122
123
124
125
126
127
	return 1;
}

/**
 * Map a Sub (a_l, a_h, b_l, b_h)
 */
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
static int map_Sub(ir_node *call, void *ctx)
{
	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  *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
144
	(void) ctx;
145
146

	/* l_res = a_l - b_l */
147
	/* h_res = a_h - b_h - carry */
148

149
150
151
152
153
154
	sub_low  = new_rd_ia32_l_Sub(dbg, irg, block, a_l, b_l, mode_T);
	flags    = new_r_Proj(irg, block, sub_low, mode_flags, pn_ia32_flags);
	sub_high = new_rd_ia32_l_Sbb(dbg, irg, block, a_h, b_h, flags, h_mode);

	l_res = new_r_Proj(irg, block, sub_low, l_mode, pn_ia32_res);
	h_res = sub_high;
155

156
	resolve_call(call, l_res, h_res, irg, block);
157
158
159
	return 1;
}

160
161
162
163
/**
 * Map a Shl (a_l, a_h, count)
 */
static int map_Shl(ir_node *call, void *ctx) {
164
165
166
167
168
169
170
171
172
	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));
173
	ir_mode  *h_mode  = get_type_mode(get_method_res_type(method, 1));
Michael Beck's avatar
Michael Beck committed
174
175
	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
176
	(void) ctx;
177

Michael Beck's avatar
Michael Beck committed
178
179
180
181
182
183
184
	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.  */
185
186
			ir_node *conv = new_rd_Conv(dbg, irg, block, a_l, h_mode);
			h_res = new_rd_Shl(dbg, irg, block, conv, cnt, h_mode);
Michael Beck's avatar
Michael Beck committed
187
188
			l_res = new_rd_Const(dbg, irg, block, l_mode, get_mode_null(l_mode));

189
190
		} else {
			/* h_res = SHLD a_h, a_l, cnt */
191
			h_res = new_rd_ia32_l_ShlD(dbg, irg, block, a_h, a_l, cnt, h_mode);
192
193
194

			/* l_res = SHL a_l, cnt */
			l_res = new_rd_ia32_l_ShlDep(dbg, irg, block, a_l, cnt, h_res, l_mode);
Michael Beck's avatar
Michael Beck committed
195
		}
196
197
198

		resolve_call(call, l_res, h_res, irg, block);
		return 1;
Michael Beck's avatar
Michael Beck committed
199
200
201
202
203
	}

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

204
	/* h_res = SHLD a_h, a_l, cnt */
205
	h1 = new_rd_ia32_l_ShlD(dbg, irg, upper, a_h, a_l, cnt, h_mode);
206
207

	/* l_res = SHL a_l, cnt */
Michael Beck's avatar
Michael Beck committed
208
209
210
211
212
213
214
215
216
217
218
219
220
221
	l1 = new_rd_ia32_l_ShlDep(dbg, irg, upper, a_l, cnt, h1, l_mode);

	c_mode = get_irn_mode(cnt);
	irn    = new_r_Const_long(irg, upper, c_mode, 32);
	irn    = new_rd_And(dbg, irg, upper, cnt, irn, c_mode);
	irn    = new_rd_Cmp(dbg, irg, upper, irn, new_r_Const(irg, upper, c_mode, get_mode_null(c_mode)));
	irn    = new_r_Proj(irg, upper, irn, mode_b, pn_Cmp_Eq);
	cond   = new_rd_Cond(dbg, irg, upper, irn);

	in[0]  = new_r_Proj(irg, upper, cond, mode_X, pn_Cond_true);
	in[1]  = new_r_Proj(irg, upper, cond, mode_X, pn_Cond_false);

	/* the block for cnt >= 32 */
	n_block = new_rd_Block(dbg, irg, 1, &in[1]);
222
	h2      = new_rd_Conv(dbg, irg, n_block, l1, h_mode);
Michael Beck's avatar
Michael Beck committed
223
224
225
226
227
228
229
230
231
232
233
234
	l2      = new_r_Const(irg, n_block, l_mode, get_mode_null(l_mode));
	in[1]   = new_r_Jmp(irg, n_block);

	set_irn_in(block, 2, in);

	in[0] = l1;
	in[1] = l2;
	l_res = new_r_Phi(irg, block, 2, in, l_mode);
	set_irn_link(block, l_res);

	in[0] = h1;
	in[1] = h2;
235
	h_res = new_r_Phi(irg, block, 2, in, h_mode);
Michael Beck's avatar
Michael Beck committed
236
237
238
239
240
241
242
	set_irn_link(l_res, h_res);
	set_irn_link(h_res, NULL);

	/* 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);
243
244
245
246
247
248
249
250
251

	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) {
252
253
254
255
256
257
258
259
260
	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));
261
	ir_mode  *h_mode  = get_type_mode(get_method_res_type(method, 1));
Michael Beck's avatar
Michael Beck committed
262
263
	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
264
	(void) ctx;
265

Michael Beck's avatar
Michael Beck committed
266
267
268
269
270
271
272
	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.  */
273
274
275
			ir_node *conv = new_rd_Conv(dbg, irg, block, a_h, l_mode);
			h_res = new_rd_Const(dbg, irg, block, h_mode, get_mode_null(h_mode));
			l_res = new_rd_Shr(dbg, irg, block, conv, cnt, l_mode);
276
277
278
		} else {
			/* l_res = SHRD a_h:a_l, cnt */
			l_res = new_rd_ia32_l_ShrD(dbg, irg, block, a_l, a_h, cnt, l_mode);
Michael Beck's avatar
Michael Beck committed
279

280
			/* h_res = SHR a_h, cnt */
281
			h_res = new_rd_ia32_l_ShrDep(dbg, irg, block, a_h, cnt, l_res, h_mode);
Michael Beck's avatar
Michael Beck committed
282
		}
283
284
		resolve_call(call, l_res, h_res, irg, block);
		return 1;
Michael Beck's avatar
Michael Beck committed
285
286
287
288
289
	}

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

290
	/* l_res = SHRD a_h:a_l, cnt */
Michael Beck's avatar
Michael Beck committed
291
	l1 = new_rd_ia32_l_ShrD(dbg, irg, upper, a_l, a_h, cnt, l_mode);
292

293
	/* h_res = SHR a_h, cnt */
294
	h1 = new_rd_ia32_l_ShrDep(dbg, irg, upper, a_h, cnt, l1, h_mode);
Michael Beck's avatar
Michael Beck committed
295
296
297
298
299
300
301
302
303
304
305
306
307

	c_mode = get_irn_mode(cnt);
	irn    = new_r_Const_long(irg, upper, c_mode, 32);
	irn    = new_rd_And(dbg, irg, upper, cnt, irn, c_mode);
	irn    = new_rd_Cmp(dbg, irg, upper, irn, new_r_Const(irg, upper, c_mode, get_mode_null(c_mode)));
	irn    = new_r_Proj(irg, upper, irn, mode_b, pn_Cmp_Eq);
	cond   = new_rd_Cond(dbg, irg, upper, irn);

	in[0]  = new_r_Proj(irg, upper, cond, mode_X, pn_Cond_true);
	in[1]  = new_r_Proj(irg, upper, cond, mode_X, pn_Cond_false);

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

	set_irn_in(block, 2, in);

	in[0] = l1;
	in[1] = l2;
	l_res = new_r_Phi(irg, block, 2, in, l_mode);
	set_irn_link(block, l_res);

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

	/* 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);
329
330
331
332
333
334
335
336
337

	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
338
339
340
341
342
343
344
345
346
	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));
347
	ir_mode  *h_mode  = get_type_mode(get_method_res_type(method, 1));
Michael Beck's avatar
Michael Beck committed
348
349
	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
350
	(void) ctx;
351

Michael Beck's avatar
Michael Beck committed
352
353
354
355
356
357
358
	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.  */
359
360
			ir_node *conv    = new_rd_Conv(dbg, irg, block, a_h, l_mode);
			ir_mode *c_mode  = get_irn_mode(cnt);
Michael Beck's avatar
Michael Beck committed
361

362
363
			h_res = new_rd_Shrs(dbg, irg, block, a_h, new_r_Const_long(irg, block, c_mode, 31), h_mode);
			l_res = new_rd_Shrs(dbg, irg, block, conv, cnt, l_mode);
364
365
366
		} else {
			/* l_res = SHRD a_h:a_l, cnt */
			l_res = new_rd_ia32_l_ShrD(dbg, irg, block, a_l, a_h, cnt, l_mode);
Michael Beck's avatar
Michael Beck committed
367

368
			/* h_res = SAR a_h, cnt */
369
			h_res = new_rd_ia32_l_SarDep(dbg, irg, block, a_h, cnt, l_res, h_mode);
Michael Beck's avatar
Michael Beck committed
370
		}
371
372
		resolve_call(call, l_res, h_res, irg, block);
		return 1;
Michael Beck's avatar
Michael Beck committed
373
374
375
376
377
	}

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

378
	/* l_res = SHRD a_h:a_l, cnt */
Michael Beck's avatar
Michael Beck committed
379
	l1 = new_rd_ia32_l_ShrD(dbg, irg, upper, a_l, a_h, cnt, l_mode);
380
381

	/* h_res = SAR a_h, cnt */
382
	h1 = new_rd_ia32_l_SarDep(dbg, irg, upper, a_h, cnt, l1, h_mode);
Michael Beck's avatar
Michael Beck committed
383
384
385
386
387
388
389
390
391
392
393
394
395

	c_mode = get_irn_mode(cnt);
	irn    = new_r_Const_long(irg, upper, c_mode, 32);
	irn    = new_rd_And(dbg, irg, upper, cnt, irn, c_mode);
	irn    = new_rd_Cmp(dbg, irg, upper, irn, new_r_Const(irg, upper, c_mode, get_mode_null(c_mode)));
	irn    = new_r_Proj(irg, upper, irn, mode_b, pn_Cmp_Eq);
	cond   = new_rd_Cond(dbg, irg, upper, irn);

	in[0]  = new_r_Proj(irg, upper, cond, mode_X, pn_Cond_true);
	in[1]  = new_r_Proj(irg, upper, cond, mode_X, pn_Cond_false);

	/* the block for cnt >= 32 */
	n_block = new_rd_Block(dbg, irg, 1, &in[1]);
396
397
	l2      = new_rd_Conv(dbg, irg, n_block, h1, l_mode);
	h2      = new_rd_Shrs(dbg, irg, n_block, a_h, new_r_Const_long(irg, block, c_mode, 31), h_mode);
Michael Beck's avatar
Michael Beck committed
398
399
400
401
402
403
404
405
406
407
408
	in[1]   = new_r_Jmp(irg, n_block);

	set_irn_in(block, 2, in);

	in[0] = l1;
	in[1] = l2;
	l_res = new_r_Phi(irg, block, 2, in, l_mode);
	set_irn_link(block, l_res);

	in[0] = h1;
	in[1] = h2;
409
	h_res = new_r_Phi(irg, block, 2, in, h_mode);
Michael Beck's avatar
Michael Beck committed
410
411
412
413
414
415
416
	set_irn_link(l_res, h_res);
	set_irn_link(h_res, NULL);

	/* 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);
417
418
419
420
421

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

422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
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;
}

455
456
457
458
/**
 * 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
459
460
461
462
463
464
465
466
467
468
	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  *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));
469
	ir_mode  *h_mode  = get_type_mode(get_method_res_type(method, 1));
470
	ir_node  *l_res, *h_res, *mul, *pEDX, *add;
Matthias Braun's avatar
Matthias Braun committed
471
	(void) ctx;
472
473
474
475
476
477
478
479
480
481

	/*
		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
482

483
	/* handle the often used case of 32x32=64 mul */
484
485
486
487
488
489
	if (is_sign_extend(a_l, a_h) && is_sign_extend(b_l, b_h)) {
		mul   = new_rd_ia32_l_IMul(dbg, irg, block, a_l, b_l);
		h_res = new_rd_Proj(dbg, irg, block, mul, h_mode, pn_ia32_l_Mul_EDX);
		l_res = new_rd_Proj(dbg, irg, block, mul, l_mode, pn_ia32_l_Mul_EAX);

		goto end;
490
491
	}

492
	mul   = new_rd_ia32_l_Mul(dbg, irg, block, a_l, b_l);
493
	pEDX  = new_rd_Proj(dbg, irg, block, mul, h_mode, pn_ia32_l_Mul_EDX);
Michael Beck's avatar
Michael Beck committed
494
	l_res = new_rd_Proj(dbg, irg, block, mul, l_mode, pn_ia32_l_Mul_EAX);
495

496
497
498
499
500
501
	b_l   = new_rd_Conv(dbg, irg, block, b_l, h_mode);
	mul   = new_rd_Mul( dbg, irg, block, a_h, b_l, h_mode);
	add   = new_rd_Add( dbg, irg, block, mul, pEDX, h_mode);
	a_l   = new_rd_Conv(dbg, irg, block, a_l, h_mode);
	mul   = new_rd_Mul( dbg, irg, block, a_l, b_h, h_mode);
	h_res = new_rd_Add( dbg, irg, block, add, mul, h_mode);
502

503
end:
504
505
506
507
508
	resolve_call(call, l_res, h_res, irg, block);

	return 1;
}

509
510
511
512
/**
 * Map a Minus (a_l, a_h)
 */
static int map_Minus(ir_node *call, void *ctx) {
Michael Beck's avatar
Michael Beck committed
513
514
515
516
517
518
519
520
	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_mode  *l_mode  = get_type_mode(get_method_res_type(method, 0));
521
	ir_mode  *h_mode  = get_type_mode(get_method_res_type(method, 1));
522
	ir_node  *l_res, *h_res, *res;
Matthias Braun's avatar
Matthias Braun committed
523
	(void) ctx;
524

525
	res   = new_rd_ia32_Minus64Bit(dbg, irg, block, a_l, a_h);
Michael Beck's avatar
Michael Beck committed
526
	l_res = new_r_Proj(irg, block, res, l_mode, pn_ia32_Minus64Bit_low_res);
527
	h_res = new_r_Proj(irg, block, res, h_mode, pn_ia32_Minus64Bit_high_res);
528
529
530
531
532
533

	resolve_call(call, l_res, h_res, irg, block);

	return 1;
}

534
535
536
537
/**
 * Map a Abs (a_l, a_h)
 */
static int map_Abs(ir_node *call, void *ctx) {
538
539
540
541
542
543
544
545
546
547
548
	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_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;
549
	ir_node  *sign_l;
550
551
	ir_node  *l_sub;
	ir_node  *flags;
Matthias Braun's avatar
Matthias Braun committed
552
	(void) ctx;
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567

	/*
		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
568
	/* TODO: give a hint to the backend somehow to not create a cltd here... */
569
570
571
572
	sign   = new_rd_Shrs(dbg, irg, block, a_h, new_Const_long(l_mode, 31), h_mode);
	sign_l = new_rd_Conv(dbg, irg, block, sign, l_mode);
	sub_l  = new_rd_Eor(dbg, irg, block, a_l, sign_l, l_mode);
	sub_h  = new_rd_Eor(dbg, irg, block, a_h, sign,   h_mode);
573
574
575
576
577

	l_sub  = new_rd_ia32_l_Sub(dbg, irg, block, sub_l, sign_l, mode_T);
	l_res  = new_r_Proj(irg, block, l_sub, l_mode,     pn_ia32_res);
	flags  = new_r_Proj(irg, block, l_sub, mode_flags, pn_ia32_flags);
	h_res  = new_rd_ia32_l_Sbb(dbg, irg, block, sub_h, sign, flags, h_mode);
578
579
580
581
582
583

	resolve_call(call, l_res, h_res, irg, block);

	return 1;
}

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

586
/**
587
 * Maps a Div. Change into a library call
588
 */
589
static int map_Div(ir_node *call, void *ctx) {
590
	ia32_intrinsic_env_t *env = ctx;
Michael Beck's avatar
Michael Beck committed
591
	ir_type   *method    = get_Call_type(call);
592
	ir_mode   *h_mode    = get_type_mode(get_method_res_type(method, 1));
593
594
595
	ir_node   *ptr;
	ir_entity *ent;
	symconst_symbol sym;
596

597
	if (mode_is_signed(h_mode)) {
598
599
600
601
602
603
604
605
606
		/* 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 {
607
		/* 64bit unsigned Division */
608
609
610
611
612
613
614
		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"));
		}
615
	}
616
617
618
	sym.entity_p = ent;
	ptr = get_Call_ptr(call);
	set_SymConst_symbol(ptr, sym);
619
620
621
	return 1;
}

622
623
624
/**
 * Maps a Mod. Change into a library call
 */
625
static int map_Mod(ir_node *call, void *ctx) {
626
627
	ia32_intrinsic_env_t *env = ctx;
	ir_type   *method    = get_Call_type(call);
628
	ir_mode   *h_mode    = get_type_mode(get_method_res_type(method, 0));
629
630
631
632
	ir_node   *ptr;
	ir_entity *ent;
	symconst_symbol sym;

633
	if (mode_is_signed(h_mode)) {
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
		/* 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;
656
657
658
}

/**
659
 * Maps a Conv.
660
661
662
 */
static int map_Conv(ir_node *call, void *ctx) {
	ia32_intrinsic_env_t *env = ctx;
663
664
665
666
667
668
669
670
671
672
673
	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);
	int       gp_bytes    = get_mode_size_bytes(ia32_reg_classes[CLASS_ia32_gp].mode);
	ir_entity *ent;
	ir_node   *l_res, *h_res, *frame, *fres;
	ir_node   *store_l, *store_h;
	ir_node   *op_mem[2], *mem;
674
675
676
677
678
679
680
681
682
683

	if (n == 1) {
		/* 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");

		/* allocate memory on frame to store args */
684
		ent = env->irg == irg ? env->d_ll_conv : NULL;
685
		if (! ent) {
686
687
			ent      = env->d_ll_conv = frame_alloc_area(get_irg_frame_type(irg), 2 * gp_bytes, 16, 0);
			env->irg = irg;
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
		}

		/* Store arg */
		frame = get_irg_frame(irg);

		/*
			Now we create a node to move the value from a XMM register into
			x87 FPU because it is unknown here, which FPU is used.
			This node is killed in transformation phase when not needed.
			Otherwise it is split up into a movsd + fld
		*/
		a_f = new_rd_ia32_l_SSEtoX87(dbg, irg, block, frame, a_f, get_irg_no_mem(irg), mode_D);
		set_ia32_frame_ent(a_f, ent);
		set_ia32_use_frame(a_f);
		set_ia32_ls_mode(a_f, mode_D);

704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
		if (mode_is_signed(h_res_mode)) {
			/* a float to signed conv, the simple case */

			/* store from FPU as Int */
			a_f = new_rd_ia32_l_vfist(dbg, irg, block, frame, a_f, get_irg_no_mem(irg));
			set_ia32_frame_ent(a_f, ent);
			set_ia32_use_frame(a_f);
			set_ia32_ls_mode(a_f, mode_Ls);
			mem = a_f;

			/* load low part of the result */
			l_res = new_rd_ia32_l_Load(dbg, irg, block, frame, mem);
			set_ia32_frame_ent(l_res, ent);
			set_ia32_use_frame(l_res);
			set_ia32_ls_mode(l_res, l_res_mode);
			l_res = new_r_Proj(irg, block, l_res, l_res_mode, pn_ia32_l_Load_res);

			/* load hight part of the result */
			h_res = new_rd_ia32_l_Load(dbg, irg, block, frame, mem);
			set_ia32_frame_ent(h_res, ent);
			add_ia32_am_offs_int(h_res, gp_bytes);
			set_ia32_use_frame(h_res);
			set_ia32_ls_mode(h_res, h_res_mode);
			h_res = new_r_Proj(irg, block, h_res, h_res_mode, pn_ia32_l_Load_res);
		} else {
			/* a float to unsigned conv, more complicated */
			panic("Float->unsigned64 NYI\n");
		}
732
733
734
735
736
737
738
739
740
741

		/* lower the call */
		resolve_call(call, l_res, h_res, irg, block);
	}
	else if (n == 2) {
		/* 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
742
743
		assert(! mode_is_float(get_irn_mode(a_l))
				&& ! mode_is_float(get_irn_mode(a_h)));
744
745

		/* allocate memory on frame to store args */
746
		ent = env->irg == irg ? env->ll_d_conv : NULL;
747
748
		if (! ent) {
			ent = env->ll_d_conv = frame_alloc_area(get_irg_frame_type(irg), 2 * gp_bytes, 16, 0);
749
			env->irg = irg;
750
751
752
753
754
755
756
757
758
759
		}

		/* Store arg */
		frame = get_irg_frame(irg);

		/* store first arg (low part) */
		store_l   = new_rd_ia32_l_Store(dbg, irg, block, frame, a_l, get_irg_no_mem(irg));
		set_ia32_frame_ent(store_l, ent);
		set_ia32_use_frame(store_l);
		set_ia32_ls_mode(store_l, get_irn_mode(a_l));
760
		op_mem[0] = store_l;
761
762
763
764

		/* store second arg (high part) */
		store_h   = new_rd_ia32_l_Store(dbg, irg, block, frame, a_h, get_irg_no_mem(irg));
		set_ia32_frame_ent(store_h, ent);
765
		add_ia32_am_offs_int(store_h, gp_bytes);
766
767
		set_ia32_use_frame(store_h);
		set_ia32_ls_mode(store_h, get_irn_mode(a_h));
768
		op_mem[1] = store_h;
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800

		mem = new_r_Sync(irg, block, 2, op_mem);

		/* Load arg into x87 FPU (implicit convert) */
		fres = new_rd_ia32_l_vfild(dbg, irg, block, frame, mem);
		set_ia32_frame_ent(fres, ent);
		set_ia32_use_frame(fres);
		set_ia32_ls_mode(fres, mode_D);
		mem  = new_r_Proj(irg, block, fres, mode_M, pn_ia32_l_vfild_M);
		fres = new_r_Proj(irg, block, fres, fres_mode, pn_ia32_l_vfild_res);

		/*
			Now we create a node to move the loaded value into a XMM
			register because it is unknown here, which FPU is used.
			This node is killed in transformation phase when not needed.
			Otherwise it is split up into a fst + movsd
		*/
		fres = new_rd_ia32_l_X87toSSE(dbg, irg, block, frame, fres, mem, fres_mode);
		set_ia32_frame_ent(fres, ent);
		set_ia32_use_frame(fres);
		set_ia32_ls_mode(fres, fres_mode);

		/* lower the call */
		resolve_call(call, fres, NULL, irg, block);
	}
	else {
		assert(0 && "unexpected Conv call");
	}

	return 1;
}

801
/* Ia32 implementation of intrinsic mapping. */
802
803
804
ir_entity *ia32_create_intrinsic_fkt(ir_type *method, const ir_op *op,
                                     const ir_mode *imode, const ir_mode *omode,
                                     void *context)
805
806
{
	i_record      elt;
807
	ir_entity     **ent = NULL;
808
809
810
811
812
813
814
815
816
817
818
819
820
821
	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;
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
	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;
838
839
840
841
842
843
844
845
	case iro_Minus:
		ent    = &i_ents[iro_Minus];
		mapper = map_Minus;
		break;
	case iro_Abs:
		ent    = &i_ents[iro_Abs];
		mapper = map_Abs;
		break;
846
847
848
849
850
851
852
853
854
855
856
857
	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;
858
	default:
Christian Würdig's avatar
Christian Würdig committed
859
		fprintf(stderr, "FIXME: unhandled op for ia32 intrinsic function %s\n", get_id_str(op->name));
860
861
862
863
864
865
		return def_create_intrinsic_fkt(method, op, imode, omode, context);
	}

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

Sebastian Hack's avatar
Sebastian Hack committed
866
		ident *id = mangle(IDENT("L"), get_op_ident(op));
867
868
869
870
871
872
		*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;
873
	elt.i_call.ctx      = context;
874
875
876
	elt.i_call.link     = NULL;

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