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

20
21
22
23
24
25
/**
 * @file
 * @brief   implementation of mips assembly emitter
 * @author  Matthias Braun, Mehdi
 * @version $Id$
 */
26
#include "config.h"
27
28
29
30
31
32
33
34
35
36
37
38

#include <limits.h>

#include "xmalloc.h"
#include "iredges.h"
#include "debug.h"
#include "irgwalk.h"
#include "irprintf.h"
#include "irop_t.h"
#include "irargs_t.h"
#include "irprog_t.h"
#include "irouts.h"
39
#include "tv.h"
40
#include "error.h"
41
42
43
44

#include "../besched.h"
#include "../benode_t.h"
#include "../beutil.h"
45
#include "../begnuas.h"
46
47
48
49
50
51
52

#include "mips_emitter.h"
#include "gen_mips_emitter.h"
#include "mips_nodes_attr.h"
#include "mips_new_nodes.h"
#include "mips_map_regs.h"

53
54
DEBUG_ONLY(static firm_dbg_module_t *dbg = NULL;)

Michael Beck's avatar
Michael Beck committed
55
56
#define BLOCK_PREFIX ".L"

57
58
#define SNPRINTF_BUF_LEN 128

59
60
61
/**
 * Returns the register at in position pos.
 */
62
static const arch_register_t *get_in_reg(const ir_node *node, int pos)
63
{
64
65
	ir_node                *op;
	const arch_register_t  *reg = NULL;
66

67
	assert(get_irn_arity(node) > pos && "Invalid IN position");
68

69
70
71
	/* The out register of the operator at position pos is the
	   in register we need. */
	op = get_irn_n(node, pos);
72

73
	reg = arch_get_irn_register(op);
74

75
76
	assert(reg && "no in register found");
	return reg;
77
78
79
80
81
}

/**
 * Returns the register at out position pos.
 */
82
static const arch_register_t *get_out_reg(const ir_node *node, int pos)
83
{
84
85
86
87
88
89
90
91
92
	ir_node                *proj;
	const arch_register_t  *reg = NULL;

	/* 1st case: irn is not of mode_T, so it has only                 */
	/*           one OUT register -> good                             */
	/* 2nd case: irn is of mode_T -> collect all Projs and ask the    */
	/*           Proj with the corresponding projnum for the register */

	if (get_irn_mode(node) != mode_T) {
93
		reg = arch_get_irn_register(node);
94
	} else if (is_mips_irn(node)) {
95
		reg = arch_irn_get_register(node, pos);
96
97
98
99
100
101
102
	} else {
		const ir_edge_t *edge;

		foreach_out_edge(node, edge) {
			proj = get_edge_src_irn(edge);
			assert(is_Proj(proj) && "non-Proj from mode_T node");
			if (get_Proj_proj(proj) == pos) {
103
				reg = arch_get_irn_register(proj);
104
105
106
107
108
109
110
				break;
			}
		}
	}

	assert(reg && "no out register found");
	return reg;
111
}
112
113
114
115
116
117
118
119
120
121
122
123

/*************************************************************
 *             _       _    __   _          _
 *            (_)     | |  / _| | |        | |
 *  _ __  _ __ _ _ __ | |_| |_  | |__   ___| |_ __   ___ _ __
 * | '_ \| '__| | '_ \| __|  _| | '_ \ / _ \ | '_ \ / _ \ '__|
 * | |_) | |  | | | | | |_| |   | | | |  __/ | |_) |  __/ |
 * | .__/|_|  |_|_| |_|\__|_|   |_| |_|\___|_| .__/ \___|_|
 * | |                                       | |
 * |_|                                       |_|
 *************************************************************/

124
125
126
/**
 * Emit the name of the source register at given input position.
 */
127
void mips_emit_source_register(const ir_node *node, int pos)
128
{
129
130
131
	const arch_register_t *reg = get_in_reg(node, pos);
	be_emit_char('$');
	be_emit_string(arch_register_get_name(reg));
132
133
}

134
135
136
/**
 * Emit the name of the destination register at given output position.
 */
137
void mips_emit_dest_register(const ir_node *node, int pos)
138
{
139
140
141
	const arch_register_t *reg = get_out_reg(node, pos);
	be_emit_char('$');
	be_emit_string(arch_register_get_name(reg));
142
143
144
}

#if 0
145
146
147
148
149
150
151
152
153
154
155
156
static const char *get_symconst_str(ir_node *node)
{
	ident *id;

	switch(get_SymConst_kind(node)) {
	case symconst_addr_name:
		id = get_SymConst_name(node);
		return get_id_str(id);
	case symconst_addr_ent:
		id = get_entity_ident(get_SymConst_entity(node));
		return get_id_str(id);
	default:
157
		panic("Unsupported SymConst kind");
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
	}

	return NULL;
}

/**
 * Return a const or symconst as string.
 */
static const char *node_const_to_str(ir_node *n)
{
	static char buf[64];
	const mips_attr_t *attr = get_mips_attr(n);
	long val;

	if(is_mips_load_r(n) || is_mips_store_r(n)) {
		mips_attr_t *attr = get_mips_attr(n);
		ir_node *symconst;

		if(attr->tv != NULL) {
			val = get_tarval_long(attr->tv);
			snprintf(buf, sizeof(buf), "%ld", val);

			return buf;
		}
		if(attr->stack_entity != NULL) {
			snprintf(buf, sizeof(buf), "%d", attr->stack_entity_offset);
			return buf;
		}

		symconst = get_irn_n(n, 1);
		assert(get_irn_opcode(symconst) == iro_SymConst);

		return get_symconst_str(symconst);
	} else if(is_mips_la(n)) {
		snprintf(buf, sizeof(buf), "%s", get_id_str(attr->symconst_id));
		return buf;
	} else if(is_mips_lli(n)) {
		assert(attr->tv != NULL);
		if(get_mode_sign(get_tarval_mode(attr->tv))) {
			long val = get_tarval_long(attr->tv);
Christian Würdig's avatar
Christian Würdig committed
198
			snprintf(buf, sizeof(buf), "0x%04lX", val & 0xffff);
199
200
		} else {
			unsigned long val = get_tarval_long(attr->tv);
Christian Würdig's avatar
Christian Würdig committed
201
			snprintf(buf, sizeof(buf), "0x%04lX", val & 0xffff);
202
203
204
205
206
207
208
209
		}

		return buf;
	} else if(is_mips_lui(n)) {
		assert(attr->tv != NULL);
		if(get_mode_sign(get_tarval_mode(attr->tv))) {
			long val = get_tarval_long(attr->tv);
			val = (val & 0xffff0000) >> 16;
Christian Würdig's avatar
Christian Würdig committed
210
			snprintf(buf, sizeof(buf), "0x%04lX", val & 0xffff);
211
212
213
		} else {
			unsigned long val = get_tarval_long(attr->tv);
			val = (val & 0xffff0000) >> 16;
Christian Würdig's avatar
Christian Würdig committed
214
			snprintf(buf, sizeof(buf), "0x%04lX", val & 0xffff);
215
216
217
218
219
220
221
222
223
224
225
		}

		return buf;
	}

	assert(attr->tv != NULL);
	val = get_tarval_long(attr->tv);
	snprintf(buf, sizeof(buf), "%ld", val);

	return buf;
}
226
#endif
227

228
void mips_emit_load_store_address(const ir_node *node, int pos)
229
230
231
{
	const mips_load_store_attr_t *attr = get_mips_load_store_attr_const(node);

232
233
234
	be_emit_irprintf("%d(", attr->offset);
	mips_emit_source_register(node, pos);
	be_emit_char(')');
235
236
}

237
void mips_emit_immediate_suffix(const ir_node *node, int pos)
238
239
240
{
	ir_node *op = get_irn_n(node, pos);
	if(is_mips_Immediate(op))
241
		be_emit_char('i');
242
243
}

244
void mips_emit_immediate(const ir_node *node)
245
{
246
247
248
249
	const mips_immediate_attr_t *attr = get_mips_immediate_attr_const(node);

	switch(attr->imm_type) {
	case MIPS_IMM_CONST:
250
		be_emit_irprintf("%d", attr->val);
251
252
		break;
	case MIPS_IMM_SYMCONST_LO:
253
254
		be_emit_cstring("%lo($");
		be_emit_ident(get_entity_ld_ident(attr->entity));
255
		if(attr->val != 0) {
256
			be_emit_irprintf("%+d", attr->val);
257
		}
258
		be_emit_char(')');
259
260
		break;
	case MIPS_IMM_SYMCONST_HI:
261
262
		be_emit_cstring("%hi($");
		be_emit_ident(get_entity_ld_ident(attr->entity));
263
		if(attr->val != 0) {
264
			be_emit_irprintf("%+d", attr->val);
265
		}
266
		be_emit_char(')');
267
268
269
270
271
		break;
	default:
		panic("invalid immediate type found");
	}
}
272

273
274
275
/**
 * Emit the name of the destination register at given output position.
 */
276
void mips_emit_source_register_or_immediate(const ir_node *node, int pos)
277
278
279
{
	const ir_node *op = get_irn_n(node, pos);
	if(is_mips_Immediate(op)) {
280
		mips_emit_immediate(op);
Matthias Braun's avatar
Matthias Braun committed
281
	} else {
282
		mips_emit_source_register(node, pos);
Matthias Braun's avatar
Matthias Braun committed
283
	}
284
285
}

286
#if 0
287
288
289
/*
 * Add a number to a prefix. This number will not be used a second time.
 */
290
static char *get_unique_label(char *buf, size_t buflen, const char *prefix)
291
292
293
294
295
{
	static unsigned long id = 0;
	snprintf(buf, buflen, "%s%lu", prefix, ++id);
	return buf;
}
296
#endif
297
298

/************************************************************************/
Christian Würdig's avatar
Christian Würdig committed
299
/* ABI Handling                                                         */
300
301
/************************************************************************/

302
static
303
void mips_emit_IncSP(const ir_node *node)
304
{
Christian Würdig's avatar
Christian Würdig committed
305
306
	int   offset = be_get_IncSP_offset(node);

307
	if(offset == 0) {
308
		return;
309
	}
310

311
	if(offset > 0xffff || offset < -0xffff) {
312
		panic("stackframe > 2^16 bytes not supported yet");
313
314
315
	}

	if(offset > 0) {
316
		be_emit_irprintf("\tsubu $sp, $sp, %d", offset);
317
	} else {
318
		be_emit_irprintf("\taddu $sp, $sp, %d", -offset);
319
	}
320
	be_emit_finish_line_gas(node);
321
322
}

323
static void mips_emit_Copy(const ir_node *node)
324
{
325
326
327
328
329
	be_emit_cstring("\tmove ");
	mips_emit_dest_register(node, 0);
	be_emit_cstring(", ");
	mips_emit_source_register(node, 0);
	be_emit_finish_line_gas(node);
330
331
}

332
static void mips_emit_Return(const ir_node* node)
333
{
334
335
	be_emit_cstring("\tj $ra");
	be_emit_finish_line_gas(node);
336
337
}

338
static __attribute__((unused))
339
void mips_emit_nops(int n)
340
341
342
343
{
	int i;

	for(i = 0; i < n; ++i) {
344
345
		be_emit_cstring("\tnop\n");
		be_emit_write_line();
346
347
348
	}
}

349
static void mips_emit_Perm(const ir_node *node)
350
{
Matthias Braun's avatar
Matthias Braun committed
351
	assert(get_irn_arity(node) == 2);
352

353
354
355
356
357
358
359
	be_emit_cstring("\txor ");
	mips_emit_source_register(node, 0);
	be_emit_cstring(", ");
	mips_emit_source_register(node, 0);
	be_emit_cstring(", ");
	mips_emit_source_register(node, 1);
	be_emit_finish_line_gas(node);
360

361
	/* mips_emit_nops(3); */
362

363
364
365
366
367
368
369
	be_emit_cstring("\txor ");
	mips_emit_source_register(node, 1);
	be_emit_cstring(", ");
	mips_emit_source_register(node, 1);
	be_emit_cstring(", ");
	mips_emit_source_register(node, 0);
	be_emit_finish_line_gas(node);
370

371
	/* mips_emit_nops(3); */
372

373
374
375
376
377
378
379
	be_emit_cstring("\txor ");
	mips_emit_source_register(node, 0);
	be_emit_cstring(", ");
	mips_emit_source_register(node, 0);
	be_emit_cstring(", ");
	mips_emit_source_register(node, 1);
	be_emit_finish_line_gas(node);
380

381
	/* mips_emit_nops(3); */
382
383
384
}

/************************************************************************/
Christian Würdig's avatar
Christian Würdig committed
385
/* Calls                                                                */
386
387
/************************************************************************/

388
static void mips_emit_Call(const ir_node *node)
389
{
Michael Beck's avatar
Michael Beck committed
390
391
	ir_entity *callee;

392
	be_emit_cstring("\tjal ");
393

Michael Beck's avatar
Michael Beck committed
394
395
	/* call of immediate value (label) */
	callee = be_Call_get_entity(node);
396
	if(callee != NULL) {
397
		be_emit_ident(get_entity_ld_ident(callee));
398
	} else {
399
		mips_emit_source_register(node, be_pos_Call_ptr);
400
	}
401
	be_emit_finish_line_gas(node);
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
}

/************************************************************************
 *      _
 *     | |_   _ _ __ ___  _ __  ___
 *  _  | | | | | '_ ` _ \| '_ \/ __|
 * | |_| | |_| | | | | | | |_) \__ \
 *  \___/ \__,_|_| |_| |_| .__/|___/
 *                       |_|
 ************************************************************************/

const char* mips_get_block_label(const ir_node* block)
{
	static char buf[64];
	snprintf(buf, sizeof(buf), "BLOCK_%ld", get_irn_node_nr(block));

	return buf;
}

421
422
423
/**
 * Emits a block label from the given block.
 */
424
static void mips_emit_block_label(const ir_node *block)
425
{
426
427
428
	if (has_Block_entity(block)) {
		ir_entity *entity = get_Block_entity(block);
		be_gas_emit_entity(entity);
Michael Beck's avatar
Michael Beck committed
429
	} else {
430
		be_emit_cstring(BLOCK_PREFIX);
431
		be_emit_irprintf("%ld", get_irn_node_nr(block));
Michael Beck's avatar
Michael Beck committed
432
433

	}
434
}
435

436
static void mips_emit_Jump(const ir_node *node)
437
438
{
	const ir_node *block = get_irn_link(node);
439
440
	assert(is_Block(block));

441
442
443
	be_emit_cstring("\tb ");
	mips_emit_block_label(block);
	be_emit_finish_line_gas(node);
444
445
}

Matthias Braun's avatar
Matthias Braun committed
446
ir_node *mips_get_jump_block(const ir_node* node, long projn)
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
{
	const ir_edge_t *oute;
	for(oute = get_irn_out_edge_first(node); oute != NULL;
	    oute = get_irn_out_edge_next(node, oute)) {
		ir_node *proj = get_edge_src_irn(oute);
		long n;
		assert(is_Proj(proj));

		n = get_Proj_proj(proj);
		if(n == projn)
			return get_irn_link(proj);
	}

	return NULL;
}

463
void mips_emit_jump_target_proj(const ir_node *node, long projn)
464
465
466
467
{
	ir_node *jumpblock = mips_get_jump_block(node, projn);
	assert(jumpblock != NULL);

468
	mips_emit_block_label(jumpblock);
469
470
}

471
void mips_emit_jump_target(const ir_node *node)
472
473
474
475
{
	ir_node *jumpblock = get_irn_link(node);
	assert(jumpblock != NULL);

476
	mips_emit_block_label(jumpblock);
477
478
}

479
void mips_emit_jump_or_fallthrough(const ir_node *node, long pn)
Matthias Braun's avatar
Matthias Braun committed
480
481
482
483
484
{
	ir_node *jumpblock = mips_get_jump_block(node, pn);
	assert(jumpblock != NULL);

	/* TODO: use fallthrough when possible */
485
486
	be_emit_cstring("b ");
	mips_emit_block_label(jumpblock);
Matthias Braun's avatar
Matthias Braun committed
487
488
}

489
/************************************************************************
490
491
492
493
494
495
 *  ____          _ _       _         _                                 *
 * / ___|_      _(_) |_ ___| |__     | |_   _ _ __ ___  _ __            *
 * \___ \ \ /\ / / | __/ __| '_ \ _  | | | | | '_ ` _ \| '_ \           *
 *  ___) \ V  V /| | || (__| | | | |_| | |_| | | | | | | |_) |          *
 * |____/ \_/\_/ |_|\__\___|_| |_|\___/ \__,_|_| |_| |_| .__/           *
 *                                                     |_|              *
496
497
498
 *                                                                      *
 ************************************************************************/

499
#if 0
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
/* jump table entry (target and corresponding number) */
typedef struct _branch_t {
	ir_node *target;
	int      value;
} branch_t;

/* jump table for switch generation */
typedef struct _jmp_tbl_t {
	ir_node  *defBlock;        /**< default target */
	int       min_value;       /**< smallest switch case */
	int       max_value;       /**< largest switch case */
	int       num_branches;    /**< number of jumps */
	char     *label;           /**< label of the jump table */
	branch_t *branches;        /**< jump array */
} jmp_tbl_t;

/**
 * Compare two variables of type branch_t. Used to sort all switch cases
 */
519
520
static int mips_cmp_branch_t(const void *a, const void *b)
{
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
	branch_t *b1 = (branch_t *)a;
	branch_t *b2 = (branch_t *)b;

	if (b1->value <= b2->value)
		return -1;
	else
		return 1;
}

const char* mips_get_jumptbl_label(const ir_node* switchjmp)
{
	static char buf[64];
	snprintf(buf, sizeof(buf), "__JUMPTBL%ld", get_irn_node_nr(switchjmp));

	return buf;
}

/**
 * Emits code for a SwitchJmp (creates a jump table if
 * possible otherwise a cmp-jmp cascade). Stolen from ia32
 */
542
543
void emit_mips_jump_table(const ir_node *irn)
{
544
545
546
547
548
	int                lastval, i, i2, pn;
	jmp_tbl_t          tbl;
	ir_node           *proj;
	const ir_edge_t   *edge;
	const mips_attr_t *attr = get_mips_attr_const(irn);
549
550

	/* fill the table structure */
551
	tbl.label        = XMALLOCN(char, SNPRINTF_BUF_LEN);
552
553
554
	tbl.label        = get_unique_label(tbl.label, SNPRINTF_BUF_LEN, "JMPTBL_");
	tbl.defBlock     = NULL;
	tbl.num_branches = get_irn_n_edges(irn);
555
	tbl.branches     = XMALLOCNZ(branch_t, tbl.num_branches);
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
	tbl.min_value    = INT_MAX;
	tbl.max_value    = INT_MIN;

	i = 0;
	/* go over all proj's and collect them */
	foreach_out_edge(irn, edge) {
		proj = get_edge_src_irn(edge);
		assert(is_Proj(proj) && "Only proj allowed at SwitchJmp");

		pn = get_Proj_proj(proj);

		/* create branch entry */
		tbl.branches[i].target = get_irn_link(proj);
		tbl.branches[i].value  = pn;

		tbl.min_value = pn < tbl.min_value ? pn : tbl.min_value;
		tbl.max_value = pn > tbl.max_value ? pn : tbl.max_value;

		i++;
	}

	/* sort the branches by their number */
	qsort(tbl.branches, tbl.num_branches, sizeof(tbl.branches[0]), mips_cmp_branch_t);

580
581
582
	be_emit_string(mips_get_jumptbl_label(irn));
	be_emit_cstring(":\n");
	be_emit_write_line();
583
584
585
586
587
588
	lastval = tbl.min_value;
	for(i = 0; i < tbl.num_branches; ++i) {
		const branch_t *branch = &tbl.branches[i];
		int value = branch->value;

		for(i2 = lastval + 1; i2 < value; ++i2) {
589
590
591
592
			be_emit_cstring("\t.word ");
			be_emit_ident(get_entity_ld_ident(attr->symconst));
			be_emit_char('\n');
			be_emit_write_line();
593
594
		}

595
596
597
598
		be_emit_cstring("\t.word ");
		mips_emit_block_label(branch->target);
		be_emit_char('\n');
		be_emit_write_line();
599

600
601
602
603
604
605
606
607
608
		lastval = branch->value;
	}

	if (tbl.label)
		free(tbl.label);
	if (tbl.branches)
		free(tbl.branches);
}

609
static void dump_jump_tables(ir_node* node, void *data)
610
{
611
	(void) data;
612
613
614

	// emit jump tables
	if(is_mips_SwitchJump(node)) {
615
616
		be_emit_cstring(".data\n");
		be_emit_write_line();
617

618
		emit_mips_jump_table(node);
619

620
621
		be_emit_cstring(".text\n");
		be_emit_write_line();
622
623
	}
}
624
#endif
625
626
627
628
629
630
631
632
633
634
635

/***********************************************************************************
 *                  _          __                                             _
 *                 (_)        / _|                                           | |
 *  _ __ ___   __ _ _ _ __   | |_ _ __ __ _ _ __ ___   _____      _____  _ __| | __
 * | '_ ` _ \ / _` | | '_ \  |  _| '__/ _` | '_ ` _ \ / _ \ \ /\ / / _ \| '__| |/ /
 * | | | | | | (_| | | | | | | | | | | (_| | | | | | |  __/\ V  V / (_) | |  |   <
 * |_| |_| |_|\__,_|_|_| |_| |_| |_|  \__,_|_| |_| |_|\___| \_/\_/ \___/|_|  |_|\_\
 *
 ***********************************************************************************/

636
static void mips_emit_nothing(const ir_node *node)
Matthias Braun's avatar
Matthias Braun committed
637
638
639
640
{
	(void) node;
}

641
static void mips_emit_this_shouldnt_happen(const ir_node *node)
642
{
Matthias Braun's avatar
Matthias Braun committed
643
	panic("Found non-lowered node %+F while emitting", node);
644
645
}

646
647
648
/**
 * The type of a emitter function.
 */
649
typedef void (*emit_func) (const ir_node *);
Matthias Braun's avatar
Matthias Braun committed
650

651
652
653
/**
 * Set a node emitter. Make it a bit more type safe.
 */
654
655
static void register_emitter(ir_op *op, emit_func func)
{
Matthias Braun's avatar
Matthias Braun committed
656
	op->ops.generic = (op_func) func;
657
658
659
660
661
}

/**
 * Register emitter functions for mips backend
 */
662
663
void mips_register_emitters(void)
{
664
665
666
667
668
669
	/* first clear the generic function pointer for all ops */
	clear_irp_opcodes_generic_func();

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

670
	/* benode emitter */
Matthias Braun's avatar
Matthias Braun committed
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
	register_emitter(op_be_IncSP, mips_emit_IncSP);
	register_emitter(op_be_AddSP, mips_emit_this_shouldnt_happen);
	register_emitter(op_be_Call, mips_emit_Call);
	register_emitter(op_be_Copy, mips_emit_Copy);
	register_emitter(op_be_Keep, mips_emit_nothing);
	register_emitter(op_be_Barrier, mips_emit_nothing);
	register_emitter(op_be_Return, mips_emit_Return);
	register_emitter(op_be_RegParams, mips_emit_nothing);
	register_emitter(op_be_Spill, mips_emit_this_shouldnt_happen);
	register_emitter(op_be_Reload, mips_emit_this_shouldnt_happen);
	register_emitter(op_be_Perm, mips_emit_Perm);

	register_emitter(op_Start, mips_emit_nothing);
	register_emitter(op_Proj, mips_emit_nothing);
	register_emitter(op_SymConst, mips_emit_this_shouldnt_happen);
	register_emitter(op_Const, mips_emit_this_shouldnt_happen);
	register_emitter(op_Jmp, mips_emit_Jump);
	register_emitter(op_Cmp, mips_emit_this_shouldnt_happen);
	register_emitter(op_Cond, mips_emit_this_shouldnt_happen);
Matthias Braun's avatar
Matthias Braun committed
690
	register_emitter(op_Phi, mips_emit_nothing);
691
692
693
694
695
}

/**
 * Emits assembly for a single node
 */
696
static void mips_emit_node(const ir_node *node)
697
{
698
	ir_op *op = get_irn_op(node);
699
700
701

	if (op->ops.generic) {
		emit_func emit = (emit_func) op->ops.generic;
702
		(*emit) (node);
703
	} else {
Matthias Braun's avatar
Matthias Braun committed
704
		panic("No emitter defined for node %+F", node);
705
706
707
708
709
710
711
	}
}

/**
 * Walks over the nodes in a block connected by scheduling edges
 * and emits code for each node.
 */
712
void mips_gen_block(const ir_node *block)
713
{
714
	ir_node *node;
715
716
717
718

	if (! is_Block(block))
		return;

719
720
721
	mips_emit_block_label(block);
	be_emit_cstring(":\n");
	be_emit_write_line();
722
723

	sched_foreach(block, node) {
724
		mips_emit_node(node);
725
	}
726

727
728
	be_emit_char('\n');
	be_emit_write_line();
729
730
731
732
733
}

/**
 * Emits code for function start.
 */
734
void mips_emit_func_prolog(ir_graph *irg)
735
{
736
	ident *irg_ident = get_entity_ld_ident(get_irg_entity(irg));
737
738

	// dump jump tables
739
	//irg_walk_graph(irg, NULL, dump_jump_tables, env);
740

741
742
	be_emit_write_line();
	be_gas_emit_switch_section(GAS_SECTION_TEXT);
743

744
	be_emit_cstring("\t.balign\t4\n");
745

746
747
748
	be_emit_cstring("\t.global\t");
	be_emit_ident(irg_ident);
	be_emit_char('\n');
749

750
	be_emit_cstring("\t.set\tnomips16\n");
751

752
753
754
	be_emit_cstring("\t.ent\t");
	be_emit_ident(irg_ident);
	be_emit_char('\n');
755

756
757
	be_emit_ident(irg_ident);
	be_emit_cstring(":\n");
758

759
760
761
	be_emit_cstring("\t.frame\t$fp, 24, $ra\n");
	be_emit_cstring("\t.mask\t0xc0000000, -4\n");
	be_emit_cstring("\t.fmask\t0x00000000, 0\n");
762

763
	be_emit_write_line();
764
765
766
767
768
}

/**
 * Emits code for function end
 */
769
void mips_emit_func_epilog(ir_graph *irg)
770
{
771
772
	ident *irg_ident = get_entity_ident(get_irg_entity(irg));

773
774
775
776
	be_emit_cstring("\t.end\t");
	be_emit_ident(irg_ident);
	be_emit_char('\n');
	be_emit_write_line();
777
778
779
780
781
782
783
784
785
}

/**
 * Sets labels for control flow nodes (jump target)
 */
void mips_gen_labels(ir_node *block, void *env)
{
	ir_node *pred;
	int n = get_Block_n_cfgpreds(block);
Matthias Braun's avatar
Matthias Braun committed
786
	(void) env;
787
788
789
790
791
792
793
794
795
796

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

/**
 * Main driver
 */
797
void mips_gen_routine(mips_code_gen_t *mips_cg, ir_graph *irg)
798
799
800
{
	int i, n;

801
	mips_register_emitters();
802

803
	irg_block_walk_graph(irg, mips_gen_labels, NULL, NULL);
804

805
	mips_emit_func_prolog(irg);
806

807
	n = ARR_LEN(mips_cg->block_schedule);
Matthias Braun's avatar
Matthias Braun committed
808
	for (i = 0; i < n; ++i) {
809
		ir_node *block = mips_cg->block_schedule[i];
810
		mips_gen_block(block);
811
812
	}

813
	mips_emit_func_epilog(irg);
814
815
816
817
818
}

void mips_init_emitter(void)
{
	FIRM_DBG_REGISTER(dbg, "firm.be.mips.emitter");
819
}