amd64_emitter.c 22.7 KB
Newer Older
1
2
/*
 * This file is part of libFirm.
3
 * Copyright (C) 2012 University of Karlsruhe.
4
5
6
7
8
9
 */

/**
 * @file
 * @brief   emit assembler for a backend graph
 */
Matthias Braun's avatar
Matthias Braun committed
10
#include <inttypes.h>
11

12
13
14
#include "amd64_emitter.h"
#include "amd64_new_nodes.h"
#include "amd64_nodes_attr.h"
15
#include "be_t.h"
16
#include "beasm.h"
17
#include "beblocksched.h"
18
#include "bediagnostic.h"
19
20
#include "begnuas.h"
#include "beirg.h"
21
#include "benode.h"
22
#include "besched.h"
23
#include "gen_amd64_emitter.h"
24
#include "gen_amd64_regalloc_if.h"
25
26
#include "irgwalk.h"
#include "panic.h"
27

28
29
static be_stack_layout_t *layout;

Christoph Mallon's avatar
Christoph Mallon committed
30
31
32
33
/**
 * Returns the target block for a control flow node.
 */
static ir_node *get_cfop_target_block(const ir_node *irn)
34
{
Christoph Mallon's avatar
Christoph Mallon committed
35
	return (ir_node*)get_irn_link(irn);
36
37
}

38
static char get_gp_mode_suffix(const amd64_insn_mode_t mode)
39
40
{
	switch (mode) {
41
42
43
44
	case INSN_MODE_8:  return 'b';
	case INSN_MODE_16: return 'w';
	case INSN_MODE_32: return 'l';
	case INSN_MODE_64: return 'q';
45
	case INSN_MODE_128:
46
47
	case INSN_MODE_INVALID:
		break;
48
	}
49
50
51
52
53
54
	panic("invalid insn mode");
}

static void amd64_emit_insn_mode_suffix(const amd64_insn_mode_t mode)
{
	be_emit_char(get_gp_mode_suffix(mode));
55
56
}

57
static char get_xmm_mode_suffix(const amd64_insn_mode_t mode)
58
{
59
	switch (mode) {
60
61
62
63
64
65
66
	case INSN_MODE_32:  return 's';
	case INSN_MODE_64:  return 'd';
	case INSN_MODE_128: return 'q';
	case INSN_MODE_8:
	case INSN_MODE_16:
	case INSN_MODE_INVALID:
		break;
67
	}
68
69
70
71
72
73
	panic("invalid insn mode");
}

static void amd64_emit_xmm_mode_suffix(const amd64_insn_mode_t mode)
{
	be_emit_char(get_xmm_mode_suffix(mode));
74
75
}

76
static const char *get_register_name_8bit(const arch_register_t *reg)
77
{
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
	switch (reg->global_index) {
	case REG_RAX: return "al";
	case REG_RBX: return "bl";
	case REG_RCX: return "cl";
	case REG_RDX: return "dl";
	case REG_RSP: return "spl";
	case REG_RBP: return "bpl";
	case REG_RSI: return "sil";
	case REG_RDI: return "dil";
	case REG_R8:  return "r8b";
	case REG_R9:  return "r9b";
	case REG_R10: return "r10b";
	case REG_R11: return "r11b";
	case REG_R12: return "r12b";
	case REG_R13: return "r13b";
	case REG_R14: return "r14b";
	case REG_R15: return "r15b";
95
96
	}
	panic("unexpected register number");
97
98
}

99
100
101
102
103
104
105
106
107
108
109
static const char *get_register_name_8bit_high(const arch_register_t *reg)
{
	switch (reg->global_index) {
	case REG_RAX: return "ah";
	case REG_RBX: return "bh";
	case REG_RCX: return "ch";
	case REG_RDX: return "dh";
	}
	panic("unexpected register number");
}

110
static const char *get_register_name_16bit(const arch_register_t *reg)
111
{
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
	switch (reg->global_index) {
	case REG_RAX: return "ax";
	case REG_RBX: return "bx";
	case REG_RCX: return "cx";
	case REG_RDX: return "dx";
	case REG_RSP: return "sp";
	case REG_RBP: return "bp";
	case REG_RSI: return "si";
	case REG_RDI: return "di";
	case REG_R8:  return "r8w";
	case REG_R9:  return "r9w";
	case REG_R10: return "r10w";
	case REG_R11: return "r11w";
	case REG_R12: return "r12w";
	case REG_R13: return "r13w";
	case REG_R14: return "r14w";
	case REG_R15: return "r15w";
129
130
	}
	panic("unexpected register number");
131
132
}

133
static const char *get_register_name_32bit(const arch_register_t *reg)
134
{
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
	switch (reg->global_index) {
	case REG_RAX: return "eax";
	case REG_RBX: return "ebx";
	case REG_RCX: return "ecx";
	case REG_RDX: return "edx";
	case REG_RSP: return "esp";
	case REG_RBP: return "ebp";
	case REG_RSI: return "esi";
	case REG_RDI: return "edi";
	case REG_R8:  return "r8d";
	case REG_R9:  return "r9d";
	case REG_R10: return "r10d";
	case REG_R11: return "r11d";
	case REG_R12: return "r12d";
	case REG_R13: return "r13d";
	case REG_R14: return "r14d";
	case REG_R15: return "r15d";
152
153
	}
	panic("unexpected register number");
154
155
156
157
158
159
160
161
}

static void emit_register(const arch_register_t *reg)
{
	be_emit_char('%');
	be_emit_string(reg->name);
}

162
163
static const char *get_register_name_mode(const arch_register_t *reg,
                                          const amd64_insn_mode_t mode)
164
165
{
	switch (mode) {
166
167
168
	case INSN_MODE_8:  return get_register_name_8bit(reg);
	case INSN_MODE_16: return get_register_name_16bit(reg);
	case INSN_MODE_32: return get_register_name_32bit(reg);
169
	case INSN_MODE_64:
170
171
172
	case INSN_MODE_128: return reg->name;
	case INSN_MODE_INVALID:
		break;
173
	}
174
175
176
177
178
179
	panic("invalid mode");
}

static void emit_register_insn_mode(const arch_register_t *reg,
                                    const amd64_insn_mode_t mode)
{
180
	be_emit_char('%');
181
	be_emit_string(get_register_name_mode(reg, mode));
182
183
}

184
static void emit_register_mode(const arch_register_t *reg,
185
186
                               amd64_insn_mode_t insn_mode)
{
187
	if (reg->cls == &amd64_reg_classes[CLASS_amd64_xmm]) {
188
189
190
191
192
193
		emit_register(reg);
	} else {
		emit_register_insn_mode(reg, insn_mode);
	}
}

194
typedef enum amd64_emit_mod_t {
195
196
197
198
199
	EMIT_NONE          = 0,
	EMIT_IGNORE_MODE   = 1U << 1,
	EMIT_FORCE_32      = 1U << 2,
	EMIT_CONV_DEST     = 1U << 3,
	EMIT_INDIRECT_STAR = 1U << 4,
200
} amd64_emit_mod_t;
yb9976's avatar
yb9976 committed
201
ENUM_BITSET(amd64_emit_mod_t)
202

Matthias Braun's avatar
Matthias Braun committed
203
static void amd64_emit_immediate64(const amd64_imm64_t *const imm)
204
{
205
206
207
	ir_entity *entity = imm->entity;
	if (entity != NULL) {
		be_gas_emit_entity(entity);
208
		if (imm->offset != 0)
Matthias Braun's avatar
Matthias Braun committed
209
			be_emit_irprintf("%+" PRId64, imm->offset);
210
	} else {
Matthias Braun's avatar
Matthias Braun committed
211
		be_emit_irprintf("0x%" PRIX64, imm->offset);
212
213
214
	}
}

215
static void amd64_emit_immediate32(bool const prefix, amd64_imm32_t const *const imm)
Matthias Braun's avatar
Matthias Braun committed
216
{
217
218
	if (prefix)
		be_emit_char('$');
219
	if (imm->entity) {
Matthias Braun's avatar
Matthias Braun committed
220
		be_gas_emit_entity(imm->entity);
221
		if (imm->offset != 0)
Matthias Braun's avatar
Matthias Braun committed
222
			be_emit_irprintf("%+" PRId32, imm->offset);
223
224
	} else {
		be_emit_irprintf("%" PRId32, imm->offset);
Matthias Braun's avatar
Matthias Braun committed
225
226
227
	}
}

228
229
230
231
232
233
static bool is_fp_relative(const ir_entity *entity)
{
	ir_type *owner = get_entity_owner(entity);
	return is_frame_type(owner) || owner == layout->arg_type;
}

Matthias Braun's avatar
Matthias Braun committed
234
static void amd64_emit_addr(const ir_node *const node,
Matthias Braun's avatar
Matthias Braun committed
235
                            const amd64_addr_t *const addr)
Matthias Braun's avatar
Matthias Braun committed
236
{
Matthias Braun's avatar
Matthias Braun committed
237
	ir_entity *entity = addr->immediate.entity;
Matthias Braun's avatar
Matthias Braun committed
238
	if (entity != NULL) {
239
		if (is_fp_relative(entity)) {
Matthias Braun's avatar
Matthias Braun committed
240
241
242
243
			entity = NULL; /* only emit offset for frame entities */
		} else {
			be_gas_emit_entity(entity);
		}
Matthias Braun's avatar
Matthias Braun committed
244
245
	}

Matthias Braun's avatar
Matthias Braun committed
246
247
248
	int32_t offset      = addr->immediate.offset;
	uint8_t base_input  = addr->base_input;
	uint8_t index_input = addr->index_input;
Matthias Braun's avatar
Matthias Braun committed
249
250
251
	if (offset != 0 || (entity == NULL && base_input == NO_INPUT
	                    && index_input == NO_INPUT)) {
		if (entity != NULL) {
Matthias Braun's avatar
Matthias Braun committed
252
			be_emit_irprintf("%+" PRId32, offset);
Matthias Braun's avatar
Matthias Braun committed
253
		} else {
Matthias Braun's avatar
Matthias Braun committed
254
			be_emit_irprintf("%" PRId32, offset);
Matthias Braun's avatar
Matthias Braun committed
255
256
257
258
259
260
261
262
263
264
265
266
		}
	}

	if (base_input != NO_INPUT || index_input != NO_INPUT) {
		be_emit_char('(');

		if (base_input == RIP_INPUT) {
			be_emit_cstring("%rip");
		} else if (base_input != NO_INPUT) {
			const arch_register_t *reg
				= arch_get_irn_register_in(node, base_input);
			emit_register(reg);
Matthias Braun's avatar
Matthias Braun committed
267
268
269
270
271
272
273
		}

		if (index_input != NO_INPUT) {
			be_emit_char(',');
			const arch_register_t *reg
				= arch_get_irn_register_in(node, index_input);
			emit_register(reg);
Matthias Braun's avatar
Matthias Braun committed
274

Matthias Braun's avatar
Matthias Braun committed
275
			unsigned scale = addr->log_scale;
Matthias Braun's avatar
Matthias Braun committed
276
277
278
279
280
281
282
			if (scale > 0)
				be_emit_irprintf(",%u", 1 << scale);
		}
		be_emit_char(')');
	}
}

283
static void amd64_emit_am(const ir_node *const node, bool indirect_star)
Matthias Braun's avatar
Matthias Braun committed
284
{
Matthias Braun's avatar
Matthias Braun committed
285
	const amd64_addr_attr_t *const attr = get_amd64_addr_attr_const(node);
286

Matthias Braun's avatar
Matthias Braun committed
287
288
289
290
	switch ((amd64_op_mode_t)attr->base.op_mode) {
	case AMD64_OP_REG_IMM: {
		const amd64_binop_addr_attr_t *const binop_attr
			= (const amd64_binop_addr_attr_t*)attr;
291
		amd64_emit_immediate32(true, &binop_attr->u.immediate);
Matthias Braun's avatar
Matthias Braun committed
292
293
		be_emit_cstring(", ");
		const arch_register_t *reg = arch_get_irn_register_in(node, 0);
294
		emit_register_mode(reg, binop_attr->base.insn_mode);
Matthias Braun's avatar
Matthias Braun committed
295
296
		return;
	}
Matthias Braun's avatar
Matthias Braun committed
297
298
299
	case AMD64_OP_REG_REG: {
		const amd64_addr_attr_t *const addr_attr
			= (const amd64_addr_attr_t*)attr;
Matthias Braun's avatar
Matthias Braun committed
300
301
		const arch_register_t *reg0 = arch_get_irn_register_in(node, 0);
		const arch_register_t *reg1 = arch_get_irn_register_in(node, 1);
302
		emit_register_mode(reg1, addr_attr->insn_mode);
Matthias Braun's avatar
Matthias Braun committed
303
		be_emit_cstring(", ");
304
		emit_register_mode(reg0, addr_attr->insn_mode);
Matthias Braun's avatar
Matthias Braun committed
305
306
		return;
	}
Matthias Braun's avatar
Matthias Braun committed
307
308
309
310
	case AMD64_OP_ADDR_REG: {
		const amd64_binop_addr_attr_t *const binop_attr
			= (const amd64_binop_addr_attr_t*)attr;
		amd64_emit_addr(node, &attr->addr);
Matthias Braun's avatar
Matthias Braun committed
311
312
		be_emit_cstring(", ");
		const arch_register_t *reg
Matthias Braun's avatar
Matthias Braun committed
313
			= arch_get_irn_register_in(node, binop_attr->u.reg_input);
314
		emit_register_mode(reg, binop_attr->base.insn_mode);
Matthias Braun's avatar
Matthias Braun committed
315
316
		return;
	}
Matthias Braun's avatar
Matthias Braun committed
317
318
319
320
	case AMD64_OP_ADDR_IMM:
		panic("ADDR_IMM TODO");
	case AMD64_OP_ADDR:
		amd64_emit_addr(node, &attr->addr);
Matthias Braun's avatar
Matthias Braun committed
321
		return;
322
	case AMD64_OP_UNOP_REG:
323
324
325
		if (indirect_star)
			be_emit_char('*');
		/* FALLTHROUGH */
Matthias Braun's avatar
Matthias Braun committed
326
327
	case AMD64_OP_REG: {
		const arch_register_t *reg = arch_get_irn_register_in(node, 0);
328
		emit_register_mode(reg, attr->insn_mode);
Matthias Braun's avatar
Matthias Braun committed
329
330
		return;
	}
331
	case AMD64_OP_UNOP_IMM32:
332
		amd64_emit_immediate32(false, &attr->addr.immediate);
Matthias Braun's avatar
Matthias Braun committed
333
		return;
334
	case AMD64_OP_UNOP_ADDR:
335
336
		if (indirect_star)
			be_emit_char('*');
Matthias Braun's avatar
Matthias Braun committed
337
338
		amd64_emit_addr(node, &attr->addr);
		return;
339
340
341

	case AMD64_OP_RAX_REG: {
		const arch_register_t *reg = arch_get_irn_register_in(node, 1);
342
		emit_register_mode(reg, attr->insn_mode);
343
344
345
346
347
348
		return;
	}

	case AMD64_OP_RAX_ADDR:
		amd64_emit_addr(node, &attr->addr);
		return;
Matthias Braun's avatar
Matthias Braun committed
349
350
351
352
353
354
	case AMD64_OP_IMM32:
	case AMD64_OP_IMM64:
	case AMD64_OP_NONE:
	case AMD64_OP_SHIFT_REG:
	case AMD64_OP_SHIFT_IMM:
		break;
Matthias Braun's avatar
Matthias Braun committed
355
356
357
358
	}
	panic("invalid op_mode");
}

Matthias Braun's avatar
Matthias Braun committed
359
360
static amd64_insn_mode_t get_amd64_insn_mode(const ir_node *node)
{
361
	if (is_amd64_mov_imm(node)) {
Matthias Braun's avatar
Matthias Braun committed
362
363
364
365
366
367
368
369
370
371
		const amd64_movimm_attr_t *const attr
			= get_amd64_movimm_attr_const(node);
		return attr->insn_mode;
	} else {
		amd64_addr_attr_t const *const attr = get_amd64_addr_attr_const(node);
		return attr->insn_mode;
	}
}


Matthias Braun's avatar
Matthias Braun committed
372
373
static void emit_shiftop(const ir_node *const node)
{
Matthias Braun's avatar
Matthias Braun committed
374
	amd64_shift_attr_t const *const attr = get_amd64_shift_attr_const(node);
375

Matthias Braun's avatar
Matthias Braun committed
376
377
378
	switch (attr->base.op_mode) {
	case AMD64_OP_SHIFT_IMM: {
		be_emit_irprintf("$0x%X, ", attr->immediate);
Matthias Braun's avatar
Matthias Braun committed
379
		const arch_register_t *reg = arch_get_irn_register_in(node, 0);
380
		emit_register_mode(reg, attr->insn_mode);
Matthias Braun's avatar
Matthias Braun committed
381
382
		return;
	}
Matthias Braun's avatar
Matthias Braun committed
383
	case AMD64_OP_SHIFT_REG: {
Matthias Braun's avatar
Matthias Braun committed
384
385
		const arch_register_t *reg0 = arch_get_irn_register_in(node, 0);
		const arch_register_t *reg1 = arch_get_irn_register_in(node, 1);
386
		emit_register_mode(reg1, INSN_MODE_8);
Matthias Braun's avatar
Matthias Braun committed
387
		be_emit_cstring(", ");
388
		emit_register_mode(reg0, attr->insn_mode);
Matthias Braun's avatar
Matthias Braun committed
389
390
		return;
	}
Matthias Braun's avatar
Matthias Braun committed
391
392
	default:
		break;
Matthias Braun's avatar
Matthias Braun committed
393
394
395
396
	}
	panic("invalid op_mode for shiftop");
}

Christoph Mallon's avatar
Christoph Mallon committed
397
void amd64_emitf(ir_node const *const node, char const *fmt, ...)
398
{
Christoph Mallon's avatar
Christoph Mallon committed
399
400
	va_list ap;
	va_start(ap, fmt);
401

Christoph Mallon's avatar
Christoph Mallon committed
402
403
404
	be_emit_char('\t');
	for (;;) {
		char const *start = fmt;
405

Christoph Mallon's avatar
Christoph Mallon committed
406
407
408
409
410
		while (*fmt != '%' && *fmt != '\n' && *fmt != '\0')
			++fmt;
		if (fmt != start) {
			be_emit_string_len(start, fmt - start);
		}
411

Christoph Mallon's avatar
Christoph Mallon committed
412
413
414
415
416
417
418
		if (*fmt == '\n') {
			be_emit_char('\n');
			be_emit_write_line();
			be_emit_char('\t');
			++fmt;
			continue;
		}
419

Christoph Mallon's avatar
Christoph Mallon committed
420
421
422
423
		if (*fmt == '\0')
			break;

		++fmt;
424
425
426
		amd64_emit_mod_t mod = EMIT_NONE;
		for (;;) {
			switch (*fmt) {
427
428
429
430
			case '^': mod |= EMIT_IGNORE_MODE;   break;
			case '3': mod |= EMIT_FORCE_32;      break;
			case '#': mod |= EMIT_CONV_DEST;     break;
			case '*': mod |= EMIT_INDIRECT_STAR; break;
431
432
433
434
435
436
			default:
				goto end_of_mods;
			}
			++fmt;
		}
end_of_mods:
Christoph Mallon's avatar
Christoph Mallon committed
437
438
439
440
441
442
443
444

		switch (*fmt++) {
			arch_register_t const *reg;

			case '%':
				be_emit_char('%');
				break;

445
446
			case 'A':
				switch (*fmt++) {
Matthias Braun's avatar
Matthias Braun committed
447
				case 'M':
448
					amd64_emit_am(node, mod & EMIT_INDIRECT_STAR);
449
					break;
Matthias Braun's avatar
Matthias Braun committed
450
				default: {
Matthias Braun's avatar
Matthias Braun committed
451
452
453
					amd64_addr_attr_t const *const attr
						= get_amd64_addr_attr_const(node);
					amd64_emit_addr(node, &attr->addr);
Matthias Braun's avatar
Matthias Braun committed
454
					--fmt;
455
456
457
458
				}
				}
				break;

Christoph Mallon's avatar
Christoph Mallon committed
459
			case 'C': {
460
461
				amd64_movimm_attr_t const *const attr
					= get_amd64_movimm_attr_const(node);
Matthias Braun's avatar
Matthias Braun committed
462
				amd64_emit_immediate64(&attr->immediate);
Christoph Mallon's avatar
Christoph Mallon committed
463
464
465
466
				break;
			}

			case 'D':
467
				if (!is_digit(*fmt))
Christoph Mallon's avatar
Christoph Mallon committed
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
					goto unknown;
				reg = arch_get_irn_register_out(node, *fmt++ - '0');
				goto emit_R;

			case 'E': {
				ir_entity const *const ent = va_arg(ap, ir_entity const*);
				be_gas_emit_entity(ent);
				break;
			}

			case 'L': {
				ir_node *const block = get_cfop_target_block(node);
				be_gas_emit_block_name(block);
				break;
			}

484
485
486
487
488
489
490
491
492
493
494
495
			case 'P': {
				x86_condition_code_t cc;
				if (*fmt == 'X') {
					++fmt;
					cc = (x86_condition_code_t)va_arg(ap, int);
				} else {
					panic("unknown modifier");
				}
				x86_emit_condition_code(cc);
				break;
			}

Christoph Mallon's avatar
Christoph Mallon committed
496
497
			case 'R':
				reg = va_arg(ap, arch_register_t const*);
Matthias Braun's avatar
Matthias Braun committed
498
				goto emit_R;
Christoph Mallon's avatar
Christoph Mallon committed
499
500

			case 'S': {
Matthias Braun's avatar
Matthias Braun committed
501
502
503
504
505
				if (*fmt == 'O') {
					++fmt;
					emit_shiftop(node);
					break;
				}
506
				if (!is_digit(*fmt))
Christoph Mallon's avatar
Christoph Mallon committed
507
					goto unknown;
508
				int const pos = *fmt++ - '0';
Christoph Mallon's avatar
Christoph Mallon committed
509
				reg = arch_get_irn_register_in(node, pos);
Matthias Braun's avatar
Matthias Braun committed
510
511
512
513
emit_R:
				if (mod & EMIT_IGNORE_MODE) {
					emit_register(reg);
				} else if (mod & EMIT_FORCE_32) {
514
					emit_register_mode(reg, INSN_MODE_32);
Matthias Braun's avatar
Matthias Braun committed
515
516
517
518
				} else if (mod & EMIT_CONV_DEST) {
					amd64_insn_mode_t src_mode  = get_amd64_insn_mode(node);
					amd64_insn_mode_t dest_mode = src_mode == INSN_MODE_64
					                            ? INSN_MODE_64 : INSN_MODE_32;
519
					emit_register_mode(reg, dest_mode);
Matthias Braun's avatar
Matthias Braun committed
520
				} else {
521
522
					amd64_insn_mode_t insn_mode = get_amd64_insn_mode(node);
					emit_register_mode(reg, insn_mode);
Matthias Braun's avatar
Matthias Braun committed
523
524
				}
				break;
Christoph Mallon's avatar
Christoph Mallon committed
525
526
			}

527
			case 'M': {
Matthias Braun's avatar
Matthias Braun committed
528
529
530
531
				if (*fmt == 'S') {
					++fmt;
					const amd64_shift_attr_t *attr
						= get_amd64_shift_attr_const(node);
532
					amd64_emit_insn_mode_suffix(attr->insn_mode);
Matthias Braun's avatar
Matthias Braun committed
533
534
535
536
				} else if (*fmt == 'M') {
					++fmt;
					const amd64_movimm_attr_t *attr
						= get_amd64_movimm_attr_const(node);
537
538
539
					amd64_emit_insn_mode_suffix(attr->insn_mode);
				} else if (*fmt == 'X') {
					++fmt;
540
541
542
					amd64_addr_attr_t const *const attr
						= get_amd64_addr_attr_const(node);
					amd64_emit_xmm_mode_suffix(attr->insn_mode);
Matthias Braun's avatar
Matthias Braun committed
543
544
545
				} else {
					amd64_addr_attr_t const *const attr
						= get_amd64_addr_attr_const(node);
546
					amd64_emit_insn_mode_suffix(attr->insn_mode);
Matthias Braun's avatar
Matthias Braun committed
547
				}
548
549
550
				break;
			}

Christoph Mallon's avatar
Christoph Mallon committed
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
			case 'd': {
				int const num = va_arg(ap, int);
				be_emit_irprintf("%d", num);
				break;
			}

			case 's': {
				char const *const str = va_arg(ap, char const*);
				be_emit_string(str);
				break;
			}

			case 'u': {
				unsigned const num = va_arg(ap, unsigned);
				be_emit_irprintf("%u", num);
				break;
			}

			default:
unknown:
				panic("unknown format conversion");
		}
	}
574

Christoph Mallon's avatar
Christoph Mallon committed
575
576
	be_emit_finish_line_gas(node);
	va_end(ap);
577
578
}

579
580
581
582
583
/**
 * Returns the next block in a block schedule.
 */
static ir_node *sched_next_block(const ir_node *block)
{
584
    return (ir_node*)get_irn_link(block);
585
586
}

587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
static const char *get_register_name_ir_mode(const arch_register_t *reg,
                                             ir_mode *mode)
{
	if (get_mode_arithmetic(mode) != irma_twos_complement)
		return reg->name;
	switch (get_mode_size_bits(mode)) {
	case 8:  return get_register_name_8bit(reg);
	case 16: return get_register_name_16bit(reg);
	case 32: return get_register_name_32bit(reg);
	case 64: return reg->name;
	default:
		panic("unexpected mode size");
	}
}

static void emit_amd64_asm_register(const arch_register_t *reg, char modifier,
                                    ir_mode *mode)
{
	const char *name;
	switch (modifier) {
	case '\0':
		name = mode != NULL ? get_register_name_ir_mode(reg, mode) : reg->name;
		break;
	case  'b': name = get_register_name_8bit(reg); break;
	case  'h': name = get_register_name_8bit_high(reg); break;
	case  'w': name = get_register_name_16bit(reg); break;
	case  'k': name = get_register_name_32bit(reg); break;
	case  'q': name = reg->name; break;
	// gcc also knows 'x' V4SFmode, 't' V8SFmode, 'y' "st(0)" instead of "st",
	// 'd' duplicate operand for AVX instruction
	default:
		panic("invalid asm op modifier");
	}
	be_emit_char('%');
	be_emit_string(name);
}

624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
static void emit_amd64_asm_operand(ir_node const *const node, char const modifier, unsigned const pos)
{
	switch (modifier) {
	case '\0':
	case 'b':
	case 'h':
	case 'k':
	case 'q':
	case 'w':
		break;

	default:
		be_errorf(node, "asm contains unknown modifier '%c'", modifier);
		return;
	}

640
641
	be_asm_attr_t     const *const attr = get_be_asm_attr_const(node);
	x86_asm_operand_t const *const op   = &((x86_asm_operand_t const*)attr->operands)[pos];
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
672
	switch ((x86_asm_operand_kind_t)op->kind) {
	case ASM_OP_INVALID:
		panic("invalid asm operand");

	case ASM_OP_IN_REG: {
		arch_register_t const *const reg = arch_get_irn_register_in(node, op->inout_pos);
		emit_amd64_asm_register(reg, modifier, op->u.mode);
		return;
	}

	case ASM_OP_OUT_REG: {
		arch_register_t const *const reg = arch_get_irn_register_out(node, op->inout_pos);
		emit_amd64_asm_register(reg, modifier, op->u.mode);
		return;
	}

	case ASM_OP_MEMORY: {
		arch_register_t const *const reg = arch_get_irn_register_in(node, op->inout_pos);
		be_emit_irprintf("(%%%s)", reg->name);
		return;
	}

	case ASM_OP_IMMEDIATE: {
		amd64_imm32_t const imm = { op->u.imm32.entity, op->u.imm32.offset };
		amd64_emit_immediate32(true, &imm);
		return;
	}
	}
	panic("invalid asm operand kind");
}

673
674
static void emit_amd64_asm(const ir_node *node)
{
675
	be_emit_asm(node, emit_amd64_asm_operand);
676
677
}

678
679
680
/**
 * Emit a Jmp.
 */
681
static void emit_amd64_jmp(const ir_node *node)
682
683
684
685
686
687
688
689
690
{
	ir_node *block, *next_block;

	/* for now, the code works for scheduled and non-schedules blocks */
	block = get_nodes_block(node);

	/* we have a block schedule */
	next_block = sched_next_block(block);
	if (get_cfop_target_block(node) != next_block) {
Christoph Mallon's avatar
Christoph Mallon committed
691
692
693
		amd64_emitf(node, "jmp %L");
	} else if (be_options.verbose_asm) {
		amd64_emitf(node, "/* fallthrough to %L */");
694
695
696
	}
}

697
static void emit_amd64_jmp_switch(const ir_node *node)
698
699
700
{
	const amd64_switch_jmp_attr_t *attr = get_amd64_switch_jmp_attr_const(node);

701
	amd64_emitf(node, "jmp *%E(,%^S0,8)", attr->table_entity);
702
703
	be_emit_jump_table(node, attr->table, attr->table_entity,
	                   get_cfop_target_block);
704
705
}

706
707
708
/**
 * Emit a Compare with conditional branch.
 */
709
static void emit_amd64_jcc(const ir_node *irn)
710
{
711
712
713
714
	const ir_node         *proj_true  = NULL;
	const ir_node         *proj_false = NULL;
	const ir_node         *block;
	const ir_node         *next_block;
715
716
	const amd64_cc_attr_t *attr = get_amd64_cc_attr_const(irn);
	x86_condition_code_t   cc   = attr->cc;
717
718
719

	foreach_out_edge(irn, edge) {
		ir_node *proj = get_edge_src_irn(edge);
720
		unsigned nr = get_Proj_num(proj);
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
		if (nr == pn_Cond_true) {
			proj_true = proj;
		} else {
			proj_false = proj;
		}
	}

	/* for now, the code works for scheduled and non-schedules blocks */
	block = get_nodes_block(irn);

	/* we have a block schedule */
	next_block = sched_next_block(block);

	if (get_cfop_target_block(proj_true) == next_block) {
		/* exchange both proj's so the second one can be omitted */
		const ir_node *t = proj_true;

		proj_true  = proj_false;
		proj_false = t;
740
		cc         = x86_negate_condition_code(cc);
741
742
	}

743
744
745
746
747
748
749
750
751
752
	if (cc & x86_cc_float_parity_cases) {
		/* Some floating point comparisons require a test of the parity flag,
		 * which indicates that the result is unordered */
		if (cc & x86_cc_negated) {
			amd64_emitf(proj_true, "jp %L");
		} else {
			amd64_emitf(proj_false, "jp %L");
		}
	}

753
	/* emit the true proj */
754
	amd64_emitf(proj_true, "j%PX %L", (int)cc);
Christoph Mallon's avatar
Christoph Mallon committed
755

756
757
758
759
	if (get_cfop_target_block(proj_false) == next_block) {
		if (be_options.verbose_asm)
			amd64_emitf(proj_false, "/* fallthrough to %L */");
	} else  {
Christoph Mallon's avatar
Christoph Mallon committed
760
		amd64_emitf(proj_false, "jmp %L");
761
762
763
	}
}

764
static void emit_amd64_mov_gp(const ir_node *node)
765
{
Matthias Braun's avatar
Matthias Braun committed
766
767
	const amd64_addr_attr_t *attr = get_amd64_addr_attr_const(node);
	switch (attr->insn_mode) {
768
769
770
771
772
773
774
	case INSN_MODE_8:  amd64_emitf(node, "movzbq %AM, %^D0"); return;
	case INSN_MODE_16: amd64_emitf(node, "movzwq %AM, %^D0"); return;
	case INSN_MODE_32: amd64_emitf(node, "movl %AM, %3D0");   return;
	case INSN_MODE_64: amd64_emitf(node, "movq %AM, %^D0");   return;
	case INSN_MODE_128:
	case INSN_MODE_INVALID:
		break;
775
	}
776
	panic("invalid insn mode");
777
778
}

779
780
781
782
783
/**
 * emit copy node
 */
static void emit_be_Copy(const ir_node *irn)
{
784
785
	arch_register_t const *const out = arch_get_irn_register_out(irn, 0);
	if (arch_get_irn_register_in(irn, 0) == out) {
786
787
788
789
		/* omitted Copy */
		return;
	}

790
791
	arch_register_class_t const *const cls = out->cls;
	if (cls == &amd64_reg_classes[CLASS_amd64_gp]) {
792
		amd64_emitf(irn, "mov %^S0, %^D0");
793
794
	} else if (cls == &amd64_reg_classes[CLASS_amd64_xmm]) {
		amd64_emitf(irn, "movapd %^S0, %^D0");
795
	} else {
796
		panic("move not supported for this register class");
797
798
	}
}
799

800
801
static void emit_be_Perm(const ir_node *node)
{
802
803
	arch_register_t const *const reg0 = arch_get_irn_register_out(node, 0);
	arch_register_t const *const reg1 = arch_get_irn_register_out(node, 1);
804

805
806
	arch_register_class_t const* const cls = reg0->cls;
	assert(cls == reg1->cls && "Register class mismatch at Perm");
807

808
809
810
811
812
813
814
	if (cls == &amd64_reg_classes[CLASS_amd64_gp]) {
		amd64_emitf(node, "xchg %^R, %^R", reg0, reg1);
	} else if (cls == &amd64_reg_classes[CLASS_amd64_xmm]) {
		amd64_emitf(node, "pxor %^R, %^R", reg0, reg1);
		amd64_emitf(node, "pxor %^R, %^R", reg1, reg0);
		amd64_emitf(node, "pxor %^R, %^R", reg0, reg1);
	} else {
815
816
817
818
		panic("unexpected register class in be_Perm (%+F)", node);
	}
}

819
820
821
822
823
824
825
826
827
828
829
/**
 * Emits code to increase stack pointer.
 */
static void emit_be_IncSP(const ir_node *node)
{
	int offs = be_get_IncSP_offset(node);

	if (offs == 0)
		return;

	if (offs > 0) {
Matthias Braun's avatar
Matthias Braun committed
830
		amd64_emitf(node, "subq $%d, %^D0", offs);
831
	} else {
Matthias Braun's avatar
Matthias Braun committed
832
		amd64_emitf(node, "addq $%d, %^D0", -offs);
833
834
835
	}
}

836
837
838
839
840
841
/**
 * Enters the emitter functions for handled nodes into the generic
 * pointer of an opcode.
 */
static void amd64_register_emitters(void)
{
842
	be_init_emitters();
843
844
845
846

	/* register all emitter functions defined in spec */
	amd64_register_spec_emitters();

847
848
849
	be_set_emitter(op_amd64_jcc,        emit_amd64_jcc);
	be_set_emitter(op_amd64_jmp,        emit_amd64_jmp);
	be_set_emitter(op_amd64_jmp_switch, emit_amd64_jmp_switch);
850
	be_set_emitter(op_amd64_mov_gp,     emit_amd64_mov_gp);
851
	be_set_emitter(op_be_Asm,           emit_amd64_asm);
852
853
854
855
	be_set_emitter(op_be_Copy,          emit_be_Copy);
	be_set_emitter(op_be_CopyKeep,      emit_be_Copy);
	be_set_emitter(op_be_IncSP,         emit_be_IncSP);
	be_set_emitter(op_be_Perm,          emit_be_Perm);
856
857
858
859
860
861
}

/**
 * Walks over the nodes in a block connected by scheduling edges
 * and emits code for each node.
 */
862
static void amd64_gen_block(ir_node *block)
863
{
864
	be_gas_begin_block(block, true);
865
866

	sched_foreach(block, node) {
867
		be_emit_node(node);
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
	}
}


/**
 * Sets labels for control flow nodes (jump target)
 * TODO: Jump optimization
 */
static void amd64_gen_labels(ir_node *block, void *env)
{
	ir_node *pred;
	int n = get_Block_n_cfgpreds(block);
	(void) env;

	for (n--; n >= 0; n--) {
		pred = get_Block_cfgpred(block, n);
		set_irn_link(pred, block);
	}
}

888
void amd64_emit_function(ir_graph *irg)
889
{
890
891
	ir_entity *entity = get_irg_entity(irg);
	ir_node  **blk_sched;
892
	size_t i, n;
893

894
895
	layout = be_get_irg_stack_layout(irg);

896
897
898
	/* register all emitter functions */
	amd64_register_emitters();

899
	blk_sched = be_create_block_schedule(irg);
900

901
	be_gas_emit_function_prolog(entity, 4, NULL);
902

903
	ir_reserve_resources(irg, IR_RESOURCE_IRN_LINK);
904
	irg_block_walk_graph(irg, amd64_gen_labels, NULL, NULL);
905
906

	n = ARR_LEN(blk_sched);
907
	for (i = 0; i < n; i++) {
908
		ir_node *block = blk_sched[i];
909
		ir_node *next  = (i + 1) < n ? blk_sched[i+1] : NULL;
910

911
		set_irn_link(block, next);
912
913
914
915
	}

	for (i = 0; i < n; ++i) {
		ir_node *block = blk_sched[i];
916
		amd64_gen_block(block);
917
	}
918
	ir_free_resources(irg, IR_RESOURCE_IRN_LINK);
919
920

	be_gas_emit_function_epilog(entity);
921
}