arm_emitter.c 19.3 KB
Newer Older
Christian Würdig's avatar
Christian Würdig committed
1
2
/*
 * This file is part of libFirm.
3
 * Copyright (C) 2012 University of Karlsruhe.
Christian Würdig's avatar
Christian Würdig committed
4
5
 */

6
7
8
/**
 * @file
 * @brief   arm emitter
Matthias Braun's avatar
Matthias Braun committed
9
 * @author  Oliver Richter, Tobias Gneist, Michael Beck, Matthias Braun
10
 */
Matthias Braun's avatar
Matthias Braun committed
11
#include <inttypes.h>
12

13
14
15
16
#include "arm_cconv.h"
#include "arm_emitter.h"
#include "arm_new_nodes.h"
#include "be_t.h"
17
#include "bearch_arm_t.h"
18
#include "beblocksched.h"
19
#include "bediagnostic.h"
20
21
22
#include "begnuas.h"
#include "benode.h"
#include "besched.h"
23
#include "debug.h"
24
25
#include "gen_arm_emitter.h"
#include "gen_arm_regalloc_if.h"
26
#include "irgwalk.h"
Matthias Braun's avatar
Matthias Braun committed
27
#include "panic.h"
28
29
#include "pmap.h"
#include "util.h"
30

31
32
33
34
35
36
37
38
39
40
41
42
43
/** An entry in the ent_or_tv set. */
typedef struct ent_or_tv_t ent_or_tv_t;
struct ent_or_tv_t {
	union {
		ir_entity  *entity;  /**< An entity. */
		ir_tarval  *tv;      /**< A tarval. */
		const void *generic; /**< For generic compare. */
	} u;
	unsigned     label;      /**< the associated label. */
	bool         is_entity;  /**< true if an entity is stored. */
	ent_or_tv_t *next;       /**< next in list. */
};

44
45
DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)

46
47
48
static struct obstack obst;
static pmap          *ent_or_tv;
static ent_or_tv_t   *ent_or_tv_first;
49
static ent_or_tv_t  **ent_or_tv_anchor;
50

51
52
static void arm_emit_register(const arch_register_t *reg)
{
53
	be_emit_string(reg->name);
54
55
}

Matthias Braun's avatar
Matthias Braun committed
56
static void arm_emit_source_register(const ir_node *node, int pos)
57
{
58
	const arch_register_t *reg = arch_get_irn_register_in(node, pos);
59
	arm_emit_register(reg);
Michael Beck's avatar
Michael Beck committed
60
}
61

Matthias Braun's avatar
Matthias Braun committed
62
static void arm_emit_dest_register(const ir_node *node, int pos)
63
{
64
	const arch_register_t *reg = arch_get_irn_register_out(node, pos);
65
	arm_emit_register(reg);
66
67
}

Matthias Braun's avatar
Matthias Braun committed
68
static void arm_emit_offset(const ir_node *node)
69
70
71
{
	const arm_load_store_attr_t *attr = get_arm_load_store_attr_const(node);
	assert(attr->base.is_load_store);
Michael Beck's avatar
Michael Beck committed
72

73
	be_emit_irprintf("0x%X", attr->offset);
74
75
76
}

/**
Michael Beck's avatar
Michael Beck committed
77
 * Emit the arm fpa instruction suffix depending on the mode.
78
 */
79
80
static void arm_emit_fpa_postfix(const ir_mode *mode)
{
Michael Beck's avatar
Michael Beck committed
81
	int bits = get_mode_size_bits(mode);
Michael Beck's avatar
Michael Beck committed
82
83
	char c = 'e';

Michael Beck's avatar
Michael Beck committed
84
	if (bits == 32)
Michael Beck's avatar
Michael Beck committed
85
		c = 's';
Michael Beck's avatar
Michael Beck committed
86
	else if (bits == 64)
Michael Beck's avatar
Michael Beck committed
87
88
		c = 'd';
	be_emit_char(c);
89
90
}

Matthias Braun's avatar
Matthias Braun committed
91
static void arm_emit_float_load_store_mode(const ir_node *node)
92
{
93
94
95
	const arm_load_store_attr_t *attr = get_arm_load_store_attr_const(node);
	arm_emit_fpa_postfix(attr->load_store_mode);
}
Michael Beck's avatar
Michael Beck committed
96

Matthias Braun's avatar
Matthias Braun committed
97
static void arm_emit_float_arithmetic_mode(const ir_node *node)
98
99
100
{
	const arm_farith_attr_t *attr = get_arm_farith_attr_const(node);
	arm_emit_fpa_postfix(attr->mode);
Michael Beck's avatar
Michael Beck committed
101
102
}

103
static void arm_emit_address(const ir_node *node)
104
{
105
106
	const arm_Address_attr_t *address = get_arm_Address_attr_const(node);
	ir_entity                *entity  = address->entity;
107
108
109
110
111
112

	be_gas_emit_entity(entity);

	/* TODO do something with offset */
}

Matthias Braun's avatar
Matthias Braun committed
113
static void arm_emit_load_mode(const ir_node *node)
114
115
116
117
118
119
120
121
122
123
124
125
126
127
{
	const arm_load_store_attr_t *attr = get_arm_load_store_attr_const(node);
	ir_mode *mode      = attr->load_store_mode;
	int      bits      = get_mode_size_bits(mode);
	bool     is_signed = mode_is_signed(mode);
	if (bits == 16) {
		be_emit_string(is_signed ? "sh" : "h");
	} else if (bits == 8) {
		be_emit_string(is_signed ? "sb" : "b");
	} else {
		assert(bits == 32);
	}
}

Matthias Braun's avatar
Matthias Braun committed
128
static void arm_emit_store_mode(const ir_node *node)
129
130
131
132
133
134
135
136
137
138
139
140
141
{
	const arm_load_store_attr_t *attr = get_arm_load_store_attr_const(node);
	ir_mode *mode      = attr->load_store_mode;
	int      bits      = get_mode_size_bits(mode);
	if (bits == 16) {
		be_emit_cstring("h");
	} else if (bits == 8) {
		be_emit_cstring("b");
	} else {
		assert(bits == 32);
	}
}

142
static void emit_shf_mod_name(arm_shift_modifier_t mod)
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
{
	switch (mod) {
	case ARM_SHF_ASR_REG:
	case ARM_SHF_ASR_IMM:
		be_emit_cstring("asr");
		return;
	case ARM_SHF_LSL_REG:
	case ARM_SHF_LSL_IMM:
		be_emit_cstring("lsl");
		return;
	case ARM_SHF_LSR_REG:
	case ARM_SHF_LSR_IMM:
		be_emit_cstring("lsr");
		return;
	case ARM_SHF_ROR_REG:
	case ARM_SHF_ROR_IMM:
		be_emit_cstring("ror");
		return;
	default:
		break;
	}
	panic("can't emit this shf_mod_name %d", (int) mod);
}

Matthias Braun's avatar
Matthias Braun committed
167
static void arm_emit_shifter_operand(const ir_node *node)
168
{
169
	const arm_shifter_operand_t *attr = get_arm_shifter_operand_attr_const(node);
170
171
172

	switch (attr->shift_modifier) {
	case ARM_SHF_REG:
173
		arm_emit_source_register(node, attr->shifter_op_input);
174
175
176
177
		return;
	case ARM_SHF_IMM: {
		unsigned val = attr->immediate_value;
		val = (val >> attr->shift_immediate)
Matthias Braun's avatar
Matthias Braun committed
178
			| (val << ((32-attr->shift_immediate) & 31));
179
180
181
182
183
184
185
186
		val &= 0xFFFFFFFF;
		be_emit_irprintf("#0x%X", val);
		return;
	}
	case ARM_SHF_ASR_IMM:
	case ARM_SHF_LSL_IMM:
	case ARM_SHF_LSR_IMM:
	case ARM_SHF_ROR_IMM:
187
		arm_emit_source_register(node, attr->shifter_op_input);
188
189
190
191
192
193
194
195
196
		be_emit_cstring(", ");
		emit_shf_mod_name(attr->shift_modifier);
		be_emit_irprintf(" #0x%X", attr->shift_immediate);
		return;

	case ARM_SHF_ASR_REG:
	case ARM_SHF_LSL_REG:
	case ARM_SHF_LSR_REG:
	case ARM_SHF_ROR_REG:
197
		arm_emit_source_register(node, attr->shifter_op_input);
198
199
200
		be_emit_cstring(", ");
		emit_shf_mod_name(attr->shift_modifier);
		be_emit_cstring(" ");
201
		arm_emit_source_register(node, attr->shifter_op_input+1);
202
		return;
Michael Beck's avatar
Michael Beck committed
203

204
	case ARM_SHF_RRX:
205
		arm_emit_source_register(node, attr->shifter_op_input);
206
		panic("RRX shifter emitter TODO");
207

208
209
	case ARM_SHF_INVALID:
		break;
210
	}
211
	panic("invalid shift_modifier while emitting %+F", node);
212
213
}

Michael Beck's avatar
Michael Beck committed
214
215
/**
 * Returns a unique label. This number will not be used a second time.
216
 */
217
218
static unsigned get_unique_label(void)
{
Michael Beck's avatar
Michael Beck committed
219
220
	static unsigned id = 0;
	return ++id;
221
222
}

223
static void emit_constant_name(const ent_or_tv_t *entry)
224
225
226
227
{
	be_emit_irprintf("%sC%u", be_gas_get_private_prefix(), entry->label);
}

Matthias Braun's avatar
Matthias Braun committed
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/**
 * Returns the target block for a control flow node.
 */
static ir_node *get_cfop_target_block(const ir_node *irn)
{
	return (ir_node*)get_irn_link(irn);
}

/**
 * Emit the target label for a control flow node.
 */
static void arm_emit_cfop_target(const ir_node *irn)
{
	ir_node *block = get_cfop_target_block(irn);
	be_gas_emit_block_name(block);
}

void arm_emitf(const ir_node *node, const char *format, ...)
{
	va_list ap;
	va_start(ap, format);
	be_emit_char('\t');
	for (;;) {
		const char *start = format;
252
		while (*format != '%' && *format != '\n'  && *format != '\0')
Matthias Braun's avatar
Matthias Braun committed
253
			++format;
254
255
		be_emit_string_len(start, format - start);

Matthias Braun's avatar
Matthias Braun committed
256
257
		if (*format == '\0')
			break;
258
259
260
261
262
263
264
265
266

		if (*format == '\n') {
			++format;
			be_emit_char('\n');
			be_emit_write_line();
			be_emit_char('\t');
			continue;
		}

Matthias Braun's avatar
Matthias Braun committed
267
268
269
270
271
272
273
274
		++format;

		switch (*format++) {
		case '%':
			be_emit_char('%');
			break;

		case 'S': {
275
			if (!is_digit(*format))
Matthias Braun's avatar
Matthias Braun committed
276
277
278
279
280
281
282
				goto unknown;
			unsigned const pos = *format++ - '0';
			arm_emit_source_register(node, pos);
			break;
		}

		case 'D': {
283
			if (!is_digit(*format))
Matthias Braun's avatar
Matthias Braun committed
284
285
286
287
288
289
290
				goto unknown;
			unsigned const pos = *format++ - '0';
			arm_emit_dest_register(node, pos);
			break;
		}

		case 'I':
291
			arm_emit_address(node);
Matthias Braun's avatar
Matthias Braun committed
292
293
294
295
296
297
298
299
300
301
302
			break;

		case 'o':
			arm_emit_offset(node);
			break;

		case 'O':
			arm_emit_shifter_operand(node);
			break;

		case 'C': {
303
			const ent_or_tv_t *name = va_arg(ap, const ent_or_tv_t*);
Matthias Braun's avatar
Matthias Braun committed
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
			emit_constant_name(name);
			break;
		}

		case 'm': {
			ir_mode *mode = va_arg(ap, ir_mode*);
			arm_emit_fpa_postfix(mode);
			break;
		}

		case 'M':
			switch (*format++) {
			case 'L': arm_emit_load_mode(node);             break;
			case 'S': arm_emit_store_mode(node);            break;
			case 'A': arm_emit_float_arithmetic_mode(node); break;
			case 'F': arm_emit_float_load_store_mode(node); break;
			default:
				--format;
				goto unknown;
			}
			break;

		case 'X': {
			int num = va_arg(ap, int);
			be_emit_irprintf("%X", num);
			break;
		}

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

		case 'd': {
			int num = va_arg(ap, int);
			be_emit_irprintf("%d", num);
			break;
		}

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

		case 'r': {
			arch_register_t *reg = va_arg(ap, arch_register_t*);
			arm_emit_register(reg);
			break;
		}

		case 't': {
			const ir_node *n = va_arg(ap, const ir_node*);
			arm_emit_cfop_target(n);
			break;
		}

		default:
unknown:
364
			panic("unknown format conversion");
Matthias Braun's avatar
Matthias Braun committed
365
366
367
368
369
370
		}
	}
	va_end(ap);
	be_emit_finish_line_gas(node);
}

371
372
373
374
375
376
377
378
static ent_or_tv_t *get_ent_or_tv_entry(const ent_or_tv_t *key)
{
	ent_or_tv_t *entry = pmap_get(ent_or_tv_t, ent_or_tv, key->u.generic);
	if (entry == NULL) {
		entry = OALLOC(&obst, ent_or_tv_t);
		*entry = *key;
		entry->label = get_unique_label();
		entry->next  = NULL;
379
380
		*ent_or_tv_anchor = entry;
		ent_or_tv_anchor  = &entry->next;
381
382
383
384
385
		pmap_insert(ent_or_tv, key->u.generic, entry);
	}
	return entry;
}

Michael Beck's avatar
Michael Beck committed
386
/**
387
 * Emit an Address.
Michael Beck's avatar
Michael Beck committed
388
 */
389
static void emit_arm_Address(const ir_node *irn)
390
{
391
	const arm_Address_attr_t *attr = get_arm_Address_attr_const(irn);
392
	ent_or_tv_t key;
393
394
395
	key.u.entity  = attr->entity;
	key.is_entity = true;
	key.label     = 0;
396
	ent_or_tv_t *entry = get_ent_or_tv_entry(&key);
Michael Beck's avatar
Michael Beck committed
397

Michael Beck's avatar
Michael Beck committed
398
	/* load the symbol indirect */
Matthias Braun's avatar
Matthias Braun committed
399
	arm_emitf(irn, "ldr %D0, %C", entry);
400
401
}

402
403
static void emit_arm_FrameAddr(const ir_node *irn)
{
404
	const arm_Address_attr_t *attr = get_arm_Address_attr_const(irn);
Matthias Braun's avatar
Matthias Braun committed
405
	arm_emitf(irn, "add %D0, %S0, #0x%X", attr->fp_offset);
406
407
}

Michael Beck's avatar
Michael Beck committed
408
409
410
/**
 * Emit a floating point fpa constant.
 */
411
static void emit_arm_fConst(const ir_node *irn)
412
{
413
	ent_or_tv_t key;
414
	key.u.tv      = get_fConst_value(irn);
415
416
	key.is_entity = false;
	key.label     = 0;
417
	ent_or_tv_t *entry = get_ent_or_tv_entry(&key);
Michael Beck's avatar
Michael Beck committed
418
419

	/* load the tarval indirect */
Matthias Braun's avatar
Matthias Braun committed
420
421
	ir_mode *mode = get_irn_mode(irn);
	arm_emitf(irn, "ldf%m %D0, %C", mode, entry);
Michael Beck's avatar
Michael Beck committed
422
423
}

Michael Beck's avatar
Michael Beck committed
424
425
426
/**
 * Returns the next block in a block schedule.
 */
427
428
static ir_node *sched_next_block(const ir_node *block)
{
429
    return (ir_node*)get_irn_link(block);
Michael Beck's avatar
Michael Beck committed
430
431
432
}

/**
Michael Beck's avatar
Michael Beck committed
433
 * Emit a Compare with conditional branch.
Michael Beck's avatar
Michael Beck committed
434
 */
435
436
static void emit_arm_B(const ir_node *irn)
{
Matthias Braun's avatar
Matthias Braun committed
437
438
	const ir_node *proj_true  = NULL;
	const ir_node *proj_false = NULL;
Michael Beck's avatar
Michael Beck committed
439
440
	foreach_out_edge(irn, edge) {
		ir_node *proj = get_edge_src_irn(edge);
441
		unsigned nr   = get_Proj_num(proj);
Michael Beck's avatar
Michael Beck committed
442
		if (nr == pn_Cond_true) {
443
			proj_true = proj;
444
		} else {
445
			proj_false = proj;
446
		}
Michael Beck's avatar
Michael Beck committed
447
	}
448

449
	ir_node *const op1 = get_irn_n(irn, n_arm_B_flags);
450
	assert(is_arm_Cmn(op1) || is_arm_Cmp(op1) || is_arm_Tst(op1));
Christoph Mallon's avatar
Christoph Mallon committed
451
452
453
454
455

	arm_cmp_attr_t const *const cmp_attr = get_arm_cmp_attr_const(op1);

	ir_relation relation = get_arm_CondJmp_relation(irn);
	if (cmp_attr->ins_permuted)
456
		relation = get_inversed_relation(relation);
457
458

	/* for now, the code works for scheduled and non-schedules blocks */
Matthias Braun's avatar
Matthias Braun committed
459
460
	const ir_node *block      = get_nodes_block(irn);
	const ir_node *next_block = sched_next_block(block);
461

462
463
	assert(relation != ir_relation_false);
	assert(relation != ir_relation_true);
464
465
466
467
468
469
470

	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;
471
		relation   = get_negated_relation(relation);
472
	}
473

Christoph Mallon's avatar
Christoph Mallon committed
474
475
	char const *suffix;
	bool const  is_signed = !cmp_attr->is_unsigned;
476
477
478
479
480
481
482
483
484
	switch (relation & (ir_relation_less_equal_greater)) {
		case ir_relation_equal:         suffix = "eq"; break;
		case ir_relation_less:          suffix = is_signed ? "lt" : "lo"; break;
		case ir_relation_less_equal:    suffix = is_signed ? "le" : "ls"; break;
		case ir_relation_greater:       suffix = is_signed ? "gt" : "hi"; break;
		case ir_relation_greater_equal: suffix = is_signed ? "ge" : "hs"; break;
		case ir_relation_less_greater:  suffix = "ne"; break;
		case ir_relation_less_equal_greater: suffix = "al"; break;
		default: panic("Cmp has unsupported relation");
485
486
487
	}

	/* emit the true proj */
Matthias Braun's avatar
Matthias Braun committed
488
	arm_emitf(irn, "b%s %t", suffix, proj_true);
489
490

	if (get_cfop_target_block(proj_false) == next_block) {
491
		if (be_options.verbose_asm) {
Matthias Braun's avatar
Matthias Braun committed
492
			arm_emitf(irn, "/* fallthrough to %t */", proj_false);
493
		}
494
	} else {
Matthias Braun's avatar
Matthias Braun committed
495
		arm_emitf(irn, "b %t", proj_false);
496
497
498
	}
}

499
500
static void emit_arm_SwitchJmp(const ir_node *irn)
{
Matthias Braun's avatar
Matthias Braun committed
501
	const arm_SwitchJmp_attr_t *attr = get_arm_SwitchJmp_attr_const(irn);
Matthias Braun's avatar
Matthias Braun committed
502
	arm_emitf(irn, "ldrls pc, [pc, %S0, asl #2]");
503

Matthias Braun's avatar
Matthias Braun committed
504
	be_emit_jump_table(irn, attr->table, NULL, get_cfop_target_block);
505
506
}

Michael Beck's avatar
Michael Beck committed
507
/** Emit an IncSP node */
508
509
static void emit_be_IncSP(const ir_node *irn)
{
510
	int offs = -be_get_IncSP_offset(irn);
Matthias Braun's avatar
Matthias Braun committed
511
	if (offs == 0)
512
		return;
Matthias Braun's avatar
Matthias Braun committed
513
514
515
516
517

	const char *op = "add";
	if (offs < 0) {
		op   = "sub";
		offs = -offs;
518
	}
Matthias Braun's avatar
Matthias Braun committed
519
	arm_emitf(irn, "%s %D0, %S0, #0x%X", op, offs);
520
521
}

522
523
static void emit_be_Copy(const ir_node *irn)
{
524
525
	arch_register_t const *const out = arch_get_irn_register_out(irn, 0);
	if (arch_get_irn_register_in(irn, 0) == out) {
526
		/* omitted Copy */
527
528
529
		return;
	}

530
531
	arch_register_class_t const *const cls = out->cls;
	if (cls == &arm_reg_classes[CLASS_arm_gp]) {
Matthias Braun's avatar
Matthias Braun committed
532
		arm_emitf(irn, "mov %D0, %S0");
533
534
	} else if (cls == &arm_reg_classes[CLASS_arm_fpa]) {
		arm_emitf(irn, "mvf %D0, %S0");
535
	} else {
536
		panic("move not supported for this register class");
537
538
539
	}
}

540
541
static void emit_be_Perm(const ir_node *irn)
{
Matthias Braun's avatar
Matthias Braun committed
542
	arm_emitf(irn,
543
544
545
		"eor %D0, %D0, %D1\n"
		"eor %D1, %D0, %D1\n"
		"eor %D0, %D0, %D1");
Michael Beck's avatar
Michael Beck committed
546
547
}

548
549
550
551
552
static void emit_be_MemPerm(const ir_node *node)
{
	/* TODO: this implementation is slower than necessary.
	   The longterm goal is however to avoid the memperm node completely */

Matthias Braun's avatar
Matthias Braun committed
553
	int memperm_arity = be_get_MemPerm_entity_arity(node);
554
555
556
	if (memperm_arity > 12)
		panic("memperm with more than 12 inputs not supported yet");

Matthias Braun's avatar
Matthias Braun committed
557
558
	int sp_change = 0;
	for (int i = 0; i < memperm_arity; ++i) {
559
		/* spill register */
Matthias Braun's avatar
Matthias Braun committed
560
		arm_emitf(node, "str r%d, [sp, #-4]!", i);
561
562
		sp_change += 4;
		/* load from entity */
Matthias Braun's avatar
Matthias Braun committed
563
564
565
		ir_entity *entity = be_get_MemPerm_in_entity(node, i);
		int        offset = get_entity_offset(entity) + sp_change;
		arm_emitf(node, "ldr r%d, [sp, #%d]", i, offset);
566
567
	}

Matthias Braun's avatar
Matthias Braun committed
568
	for (int i = memperm_arity; i-- > 0; ) {
569
		/* store to new entity */
Matthias Braun's avatar
Matthias Braun committed
570
571
572
		ir_entity *entity = be_get_MemPerm_out_entity(node, i);
		int        offset = get_entity_offset(entity) + sp_change;
		arm_emitf(node, "str r%d, [sp, #%d]", i, offset);
573
		/* restore register */
Matthias Braun's avatar
Matthias Braun committed
574
		arm_emitf(node, "ldr r%d, [sp], #4", i);
575
576
577
578
579
580
581
		sp_change -= 4;
	}
	assert(sp_change == 0);
}

static void emit_arm_Jmp(const ir_node *node)
{
582
	/* for now, the code works for scheduled and non-schedules blocks */
Matthias Braun's avatar
Matthias Braun committed
583
584
	const ir_node *block      = get_nodes_block(node);
	const ir_node *next_block = sched_next_block(block);
585
	if (get_cfop_target_block(node) != next_block) {
Matthias Braun's avatar
Matthias Braun committed
586
		arm_emitf(node, "b %t", node);
587
	} else {
588
		if (be_options.verbose_asm) {
Matthias Braun's avatar
Matthias Braun committed
589
			arm_emitf(node, "/* fallthrough to %t */", node);
590
		}
Michael Beck's avatar
Michael Beck committed
591
	}
592
593
594
595
596
597
}

/**
 * Enters the emitter functions for handled nodes into the generic
 * pointer of an opcode.
 */
598
599
static void arm_register_emitters(void)
{
600
	be_init_emitters();
601
602
603
604

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

605
	/* custom emitter */
606
	be_set_emitter(op_arm_Address,   emit_arm_Address);
607
	be_set_emitter(op_arm_B,         emit_arm_B);
608
	be_set_emitter(op_arm_fConst,    emit_arm_fConst);
609
610
611
612
613
614
615
616
	be_set_emitter(op_arm_FrameAddr, emit_arm_FrameAddr);
	be_set_emitter(op_arm_Jmp,       emit_arm_Jmp);
	be_set_emitter(op_arm_SwitchJmp, emit_arm_SwitchJmp);
	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_MemPerm,    emit_be_MemPerm);
	be_set_emitter(op_be_Perm,       emit_be_Perm);
617
618
}

619
620
621
/**
 * emit the block label if needed.
 */
622
static void arm_emit_block_header(ir_node *block, ir_node *prev)
623
{
Matthias Braun's avatar
Matthias Braun committed
624
625
	int  n_cfgpreds = get_Block_n_cfgpreds(block);
	bool need_label;
626
	if (n_cfgpreds == 1) {
Matthias Braun's avatar
Matthias Braun committed
627
628
		const ir_node *pred       = get_Block_cfgpred(block, 0);
		const ir_node *pred_block = get_nodes_block(pred);
629
630
631

		/* we don't need labels for fallthrough blocks, however switch-jmps
		 * are no fallthroughs */
632
633
634
		need_label =
			pred_block != prev ||
			(is_Proj(pred) && is_arm_SwitchJmp(get_Proj_pred(pred)));
635
	} else {
636
		need_label = true;
637
638
	}

639
	be_gas_begin_block(block, need_label);
640
641
}

642
643
644
645
/**
 * Walks over the nodes in a block connected by scheduling edges
 * and emits code for each node.
 */
646
647
static void arm_gen_block(ir_node *block, ir_node *prev_block)
{
648
	arm_emit_block_header(block, prev_block);
649
	be_dwarf_location(get_irn_dbg_info(block));
650
	sched_foreach(block, irn) {
651
		be_emit_node(irn);
652
653
654
655
	}
}

/**
656
 * Block-walker:
657
658
 * Sets labels for control flow nodes (jump target)
 */
659
660
static void arm_gen_labels(ir_node *block, void *env)
{
661
	(void)env;
Matthias Braun's avatar
Matthias Braun committed
662
663
	for (int n = get_Block_n_cfgpreds(block); n-- > 0; ) {
		ir_node *pred = get_Block_cfgpred(block, n);
664
665
666
667
		set_irn_link(pred, block);
	}
}

668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
static parameter_dbg_info_t *construct_parameter_infos(ir_graph *irg)
{
	ir_entity            *entity   = get_irg_entity(irg);
	ir_type              *type     = get_entity_type(entity);
	calling_convention_t *cconv    = arm_decide_calling_convention(NULL, type);
	size_t                n_params = get_method_n_params(type);
	parameter_dbg_info_t *infos    = XMALLOCNZ(parameter_dbg_info_t, n_params);

	for (size_t i = 0; i < n_params; ++i) {
		const reg_or_stackslot_t *slot = &cconv->parameters[i];

		assert(infos[i].entity == NULL && infos[i].reg == NULL);
		if (slot->reg0 != NULL) {
			infos[i].reg = slot->reg0;
		} else {
			infos[i].entity = slot->entity;
		}
	}
	arm_free_calling_convention(cconv);

	return infos;
}

691
void arm_emit_function(ir_graph *irg)
692
{
693
694
	ent_or_tv = pmap_create();
	obstack_init(&obst);
695
696
	ent_or_tv_first  = NULL;
	ent_or_tv_anchor = &ent_or_tv_first;
697
698
699

	arm_register_emitters();

700
	/* create the block schedule */
Matthias Braun's avatar
Matthias Braun committed
701
	ir_node **blk_sched = be_create_block_schedule(irg);
Michael Beck's avatar
Michael Beck committed
702

Christoph Mallon's avatar
Christoph Mallon committed
703
704
	ir_entity            *const entity = get_irg_entity(irg);
	parameter_dbg_info_t *const infos  = construct_parameter_infos(irg);
705
	be_gas_emit_function_prolog(entity, 4, infos);
706

707
	ir_reserve_resources(irg, IR_RESOURCE_IRN_LINK);
708
	irg_block_walk_graph(irg, arm_gen_labels, NULL, NULL);
Michael Beck's avatar
Michael Beck committed
709

Christoph Mallon's avatar
Christoph Mallon committed
710
711
	ir_node *last_block = NULL;
	for (size_t i = 0, n = ARR_LEN(blk_sched); i < n;) {
Matthias Braun's avatar
Matthias Braun committed
712
713
		ir_node *block   = blk_sched[i++];
		ir_node *next_bl = i < n ? blk_sched[i] : NULL;
Michael Beck's avatar
Michael Beck committed
714
715
716

		/* set here the link. the emitter expects to find the next block here */
		set_irn_link(block, next_bl);
717
		arm_gen_block(block, last_block);
718
		last_block = block;
Michael Beck's avatar
Michael Beck committed
719
	}
720
	ir_free_resources(irg, IR_RESOURCE_IRN_LINK);
Michael Beck's avatar
Michael Beck committed
721

722
	/* emit entity and tarval values */
723
	if (ent_or_tv_first != NULL) {
724
		be_emit_cstring("\t.align 2\n");
Michael Beck's avatar
Michael Beck committed
725

Christoph Mallon's avatar
Christoph Mallon committed
726
		for (ent_or_tv_t *entry = ent_or_tv_first; entry; entry = entry->next) {
727
728
729
			emit_constant_name(entry);
			be_emit_cstring(":\n");
			be_emit_write_line();
Michael Beck's avatar
Michael Beck committed
730

731
			if (entry->is_entity) {
732
				be_emit_cstring("\t.word\t");
733
				be_gas_emit_entity(entry->u.entity);
734
735
				be_emit_char('\n');
				be_emit_write_line();
Michael Beck's avatar
Michael Beck committed
736
			} else {
Matthias Braun's avatar
Matthias Braun committed
737
738
				ir_tarval *tv   = entry->u.tv;
				unsigned   size = get_mode_size_bytes(get_tarval_mode(tv));
Michael Beck's avatar
Michael Beck committed
739

740
				/* beware: ARM fpa uses big endian format */
Christoph Mallon's avatar
Christoph Mallon committed
741
				for (unsigned vi = round_up2(size, 4); vi != 0;) {
Michael Beck's avatar
Michael Beck committed
742
					/* get 32 bits */
Christoph Mallon's avatar
Christoph Mallon committed
743
744
745
746
747
					uint32_t v;
					v  = get_tarval_sub_bits(tv, --vi) << 24;
					v |= get_tarval_sub_bits(tv, --vi) << 16;
					v |= get_tarval_sub_bits(tv, --vi) <<  8;
					v |= get_tarval_sub_bits(tv, --vi) <<  0;
Matthias Braun's avatar
Matthias Braun committed
748
					be_emit_irprintf("\t.word\t%" PRIu32 "\n", v);
749
					be_emit_write_line();
Michael Beck's avatar
Michael Beck committed
750
751
				}
			}
Michael Beck's avatar
Michael Beck committed
752
		}
753
754
		be_emit_char('\n');
		be_emit_write_line();
Michael Beck's avatar
Michael Beck committed
755
	}
756
757
	pmap_destroy(ent_or_tv);
	obstack_free(&obst, NULL);
758
759

	be_gas_emit_function_epilog(entity);
760
761
}

762
static const char *get_variant_string(arm_variant_t variant)
763
{
764
	switch (variant) {
765
766
767
768
769
	case ARM_VARIANT_4:   return "armv4";
	case ARM_VARIANT_5T:  return "armv5t";
	case ARM_VARIANT_6:   return "armv6";
	case ARM_VARIANT_6T2: return "armv6t2";
	case ARM_VARIANT_7:   return "armv7";
770
	}
771
772
773
774
775
776
	panic("invalid arm variant");
}

void arm_emit_file_prologue(void)
{
	be_emit_irprintf("\t.arch %s\n", get_variant_string(arm_cg_config.variant));
777
778
779
780
781
	be_emit_write_line();
	be_emit_cstring("\t.fpu softvfp\n");
	be_emit_write_line();
}

782
783
784
void arm_init_emitter(void)
{
	FIRM_DBG_REGISTER(dbg, "firm.be.arm.emit");
785
}