beemitter_binary.c 5.5 KB
Newer Older
1
2
/*
 * This file is part of libFirm.
3
 * Copyright (C) 2012 University of Karlsruhe.
4
5
6
7
8
9
10
11
12
13
14
15
16
 */

/**
 * @file
 * @brief       Interface for machine code output
 * @author      Matthias Braun
 * @date        12.03.2007
 */
#include <assert.h>
#include <limits.h>

#include "beemitter_binary.h"
#include "obst.h"
Matthias Braun's avatar
Matthias Braun committed
17
#include "panic.h"
18
19
20

static code_fragment_t *first_fragment;
static code_fragment_t *last_fragment;
yb9976's avatar
yb9976 committed
21
#ifndef NDEBUG
22
static const unsigned CODE_FRAGMENT_MAGIC = 0x4643414d;  /* "CFMA" */
yb9976's avatar
yb9976 committed
23
#endif
24
25
26
27
28
29
30

struct obstack code_fragment_obst;

/** returns current fragment (the address stays only valid until the next
    be_emit(8/16/32/entity) call!) */
code_fragment_t *be_get_current_fragment(void)
{
31
	code_fragment_t *fragment = (code_fragment_t*)obstack_base(&code_fragment_obst);
Michael Beck's avatar
Michael Beck committed
32
	assert(obstack_object_size(&code_fragment_obst) >= sizeof(code_fragment_t));
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
	assert(fragment->magic == CODE_FRAGMENT_MAGIC);

	return fragment;
}

/** allocates a new fragment on the obstack (warning: address is only valid
    till next be_emit */
static void alloc_fragment(void)
{
	code_fragment_t *fragment;

	/* shouldn't have any growing fragments */
	assert(obstack_object_size(&code_fragment_obst) == 0);

	obstack_blank(&code_fragment_obst, sizeof(*fragment));
48
	fragment = (code_fragment_t*)obstack_base(&code_fragment_obst);
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
	memset(fragment, 0, sizeof(*fragment));
#ifndef NDEBUG
	fragment->magic = CODE_FRAGMENT_MAGIC;
#endif
	fragment->len        = 0;
	fragment->alignment  = 1;
	fragment->offset     = 0;
	fragment->max_offset = UINT_MAX;
}

static code_fragment_t *finish_fragment(void)
{
	code_fragment_t *fragment = be_get_current_fragment();
	fragment->len
		= obstack_object_size(&code_fragment_obst) - sizeof(*fragment);

	fragment      = (code_fragment_t*) obstack_finish(&code_fragment_obst);
	last_fragment = fragment;

	if (first_fragment == NULL)
		first_fragment = fragment;

	return fragment;
}

void be_start_code_emitter(void)
{
	obstack_init(&code_fragment_obst);
	first_fragment = NULL;
	alloc_fragment();
}

void be_start_new_fragment(void)
{
	finish_fragment();
	alloc_fragment();
}

static void emit(FILE *file, const unsigned char *buffer, size_t len)
{
	size_t i;
	for (i = 0; i < len; ++i) {
		size_t i2;
		fputs("\t.byte ", file);
		for (i2 = i; i2 < i + 30 && i2 < len; ++i2) {
94
			fprintf(file, "0x%02X", (unsigned)buffer[i2]);
95
96
97
98
99
100
101
102
103
104
105
106
107
108
		}
		i = i2;
		fputs("\n", file);
	}
}

static unsigned align(unsigned offset, unsigned alignment)
{
	if (offset % alignment != 0) {
		offset += alignment - (offset % alignment);
	}
	return offset;
}

Matthias Braun's avatar
Matthias Braun committed
109
110
static bool determine_jumpsize_iteration(
		const binary_emiter_interface_t *interface)
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
{
	unsigned         offset     = 0;
	unsigned         max_offset = 0;
	bool             changed    = false;
	code_fragment_t *fragment;

	for (fragment = first_fragment; fragment != NULL;
	     fragment = fragment->next) {
	    unsigned alignment = fragment->alignment;

	    /* assure alignment */
	    offset     = align(offset, alignment);
	    max_offset = align(max_offset, alignment);

		if (offset != fragment->offset) {
Matthias Braun's avatar
Matthias Braun committed
126
			changed          = true;
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
			fragment->offset = offset;
		}
	    fragment->max_offset = max_offset;

		/* advance offset */
		offset     += fragment->len;
		max_offset += fragment->len;
		interface->determine_jumpsize(fragment);
		offset     += fragment->jumpsize_min;
		max_offset += fragment->jumpsize_max;
	}

	return changed;
}

static void determine_offsets(const binary_emiter_interface_t *interface)
{
	bool changed;

	assert(first_fragment->alignment == 1);
	first_fragment->offset     = 0;
	first_fragment->max_offset = 0;

Matthias Braun's avatar
Matthias Braun committed
150
151
152
153
154
155
	/* The algorithm calculates a lower and upper bound for the offset of each
	 * fragment. With this information we can calculate a lower and upper bound
	 * for the size of each jump instruction.
	 * A single iteration updates the offset bounds for all fragments and jump
	 * sizes for each fragment. We iterate until we had an iteration where
	 * none of the minimum offsets changed. */
156
	do {
Matthias Braun's avatar
Matthias Braun committed
157
		changed = determine_jumpsize_iteration(interface);
158
159
160
161
162
163
		/* TODO: we should have an abort mode for the case when the offsets
		   don't converge fast enough. We could simply use a pessimistic
		   solution after a few iterations... */
	} while (changed);
}

Matthias Braun's avatar
Matthias Braun committed
164
165
166
167
168
169
170
171
172
173
void be_emit_entity(ir_entity *entity, bool entity_sign, int offset,
                    bool is_relative)
{
	(void) entity;
	(void) entity_sign;
	(void) offset;
	(void) is_relative;
	panic("not implemented yet");
}

174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
void be_emit_code(FILE *output, const binary_emiter_interface_t *interface)
{
	unsigned offset;

	code_fragment_t *fragment;

	finish_fragment();

	/* determine near/far jumps */
	determine_offsets(interface);

	/* emit code */
	offset = 0;
	for (fragment = first_fragment; fragment != NULL;
	     fragment = fragment->next) {
	    unsigned char *jmpbuffer;
Michael Beck's avatar
Michael Beck committed
190
		unsigned nops;
191
192
193

	    /* assure alignment by emitting nops */
	    assert(fragment->offset >= offset);
Michael Beck's avatar
Michael Beck committed
194
	    nops = fragment->offset - offset;
195
	    if (nops > 0) {
196
			unsigned char *nopbuffer = (unsigned char*)obstack_alloc(&code_fragment_obst, nops);
197
198
			interface->create_nops(nopbuffer, nops);
			emit(output, nopbuffer, nops);
199
200
201
202
203
204
205
206
207
			offset = fragment->offset;
			obstack_free(&code_fragment_obst, nopbuffer);
		}

		/* emit the fragment */
		emit(output, fragment->data, fragment->len);
		offset += fragment->len;

		/* emit the jump */
208
		jmpbuffer = (unsigned char*)obstack_alloc(&code_fragment_obst, fragment->jumpsize_min);
209
210
211
212
213
214
		interface->emit_jump(fragment, jmpbuffer);
		emit(output, jmpbuffer, fragment->jumpsize_min);
		offset += fragment->jumpsize_min;
		obstack_free(&code_fragment_obst, jmpbuffer);
	}
}