0
|
1 /*
|
|
2 * linker.c
|
|
3 *
|
|
4 * This file is part of the Oxford Oberon-2 compiler
|
|
5 * Copyright (c) 2006--2016 J. M. Spivey
|
|
6 * All rights reserved
|
|
7 *
|
|
8 * Redistribution and use in source and binary forms, with or without
|
|
9 * modification, are permitted provided that the following conditions are met:
|
|
10 *
|
|
11 * 1. Redistributions of source code must retain the above copyright notice,
|
|
12 * this list of conditions and the following disclaimer.
|
|
13 * 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
14 * this list of conditions and the following disclaimer in the documentation
|
|
15 * and/or other materials provided with the distribution.
|
|
16 * 3. The name of the author may not be used to endorse or promote products
|
|
17 * derived from this software without specific prior written permission.
|
|
18 *
|
|
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
24 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
25 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
27 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
28 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
29 */
|
|
30
|
|
31 #include <ctype.h>
|
|
32 #include "oblink.h"
|
|
33 #include "keiko.h"
|
|
34
|
|
35 static FILE *binfp; /* File for code output */
|
|
36
|
|
37 /* binwrite -- output code */
|
|
38 static void binwrite(void *buf, int size) {
|
|
39 int UNUSED nwritten = fwrite(buf, 1, size, binfp);
|
|
40 }
|
|
41
|
|
42 /* hexchar -- convert character from two-digit hex */
|
|
43 static char hexchar(char *s) {
|
|
44 char buf[3];
|
|
45
|
|
46 buf[0] = s[0]; buf[1] = s[1]; buf[2] = '\0';
|
|
47 return (char) strtoul(buf, NULL, 16);
|
|
48 }
|
|
49
|
|
50 static int iloc = 0, bloc = 0; /* Sizes of code, bss segments */
|
|
51 static int nmods = 0, nprocs = 0; /* Number of modules and procedures */
|
|
52 static symbol this_module; /* Current module */
|
|
53 static symbol this_proc; /* Current procedure */
|
|
54 static int proc_start; /* Start of procedure in dbuf */
|
|
55 static int code_size; /* Size of bytecode for procedure */
|
|
56
|
|
57 /* Instructions are stored as 'phrases' in abuf, a doubly-linked list.
|
|
58 Each phrase has a tentative assignment of a template, which describes
|
|
59 at least what arguments there should be; before the code is output, the
|
|
60 template may be replaced by one with bigger fields in order to make the
|
|
61 arguments fit. The code for a procedure is built up in abuf and output
|
|
62 at the end of the procedure. */
|
|
63
|
|
64 struct _phrase { /* An instruction in the assembler */
|
|
65 const char *q_name; /* Instruction name */
|
|
66 template q_templ; /* Best estimate of template */
|
|
67 int q_arg[MAXARGS]; /* Arguments */
|
|
68 int q_addr; /* Estimated address from start of proc */
|
|
69 symbol q_sym; /* Symbol for this address */
|
|
70 phrase q_target; /* Branch target */
|
|
71 phrase q_prev, q_next; /* Links in the list */
|
|
72 };
|
|
73
|
|
74 phrase abuf;
|
|
75
|
|
76 #define for_phrases(q) \
|
|
77 for (phrase q = abuf->q_next; q != abuf; q = q->q_next)
|
|
78
|
|
79 mempool pool;
|
|
80
|
|
81 static phrase alloc_phrase(void) {
|
|
82 return (phrase) pool_alloc(&pool, sizeof(struct _phrase));
|
|
83 }
|
|
84
|
|
85 static void init_abuf(void) {
|
|
86 pool_reset(&pool);
|
|
87 abuf = alloc_phrase();
|
|
88 abuf->q_name = (char *) "*dummy*";
|
|
89 abuf->q_templ = NULL;
|
|
90 abuf->q_addr = 0;
|
|
91 abuf->q_sym = NULL;
|
|
92 abuf->q_target = NULL;
|
|
93 abuf->q_prev = abuf->q_next = abuf;
|
|
94 }
|
|
95
|
|
96 static growdecl(dbuf);
|
|
97 #define dbuf growbuf(dbuf, uchar)
|
|
98 #define dloc growsize(dbuf)
|
|
99
|
|
100 static growdecl(rbuf);
|
|
101 #define rbuf growbuf(rbuf, unsigned)
|
|
102 #define rloc growsize(rbuf)
|
|
103
|
|
104 static growdecl(prims);
|
|
105 #define prims growbuf(prims, int)
|
|
106 #define nprims growsize(prims)
|
|
107
|
|
108 /* relocate -- record relocation bits */
|
|
109 void relocate(int loc, int bits) {
|
|
110 /* Each byte of relocation info covers CODES_PER_BYTE words */
|
|
111 int index = loc/(WORD_SIZE * CODES_PER_WORD);
|
|
112 int shift = loc/WORD_SIZE % CODES_PER_WORD * BITS_PER_CODE;
|
|
113
|
|
114 if (rloc < index+1) rloc = index+1;
|
|
115 buf_grow(rbuf);
|
|
116 rbuf[index] = (rbuf[index] & ~(CODE_MASK << shift)) | (bits << shift);
|
|
117 #ifdef DEBUG
|
|
118 if (dflag) printf("Reloc %d %d %#08x\n", loc, bits, rbuf[index]);
|
|
119 #endif
|
|
120 }
|
|
121
|
|
122 static void put_value(int addr, int value, int reloc) {
|
|
123 /* We carefully store all 4-byte values in dbuf in
|
|
124 machine-independent byte order: little-endian even if the host
|
|
125 is a big-endian machine. The value reloc determines how the
|
|
126 value should be relocated when the program is loaded by obx. */
|
|
127 put4(&dbuf[addr], value);
|
|
128 relocate(addr, reloc);
|
|
129 }
|
|
130
|
|
131 static int const_value(char *s) {
|
|
132 /* We must allow both signed and unsigned (especially hex)
|
|
133 constants, so negative numbers must be treated separately.
|
|
134 Note that strtol is specified to truncate to MAXINT on
|
|
135 overflow, not to operate mod 2^32. */
|
|
136
|
|
137 if (s == NULL)
|
|
138 return 0;
|
|
139 else if (s[0] == '-')
|
|
140 return strtol(s, NULL, 0);
|
|
141 else
|
|
142 return strtoul(s, NULL, 0);
|
|
143 }
|
|
144
|
|
145 static void data_value(int value, int reloc) {
|
|
146 buf_grow(dbuf);
|
|
147 put_value(dloc, value, reloc);
|
|
148 dloc += 4;
|
|
149 }
|
|
150
|
|
151 static void data_word(char *s) {
|
|
152 buf_grow(dbuf);
|
|
153 if (s == NULL || isdigit((int) s[0]) || s[0] == '-')
|
|
154 put_value(dloc, const_value(s), R_WORD);
|
|
155 else
|
|
156 use_global(find_symbol(s), dbuf, dloc);
|
|
157 dloc += 4;
|
|
158 }
|
|
159
|
|
160 static void put_string(char *str) {
|
|
161 char *s = str;
|
|
162 do {
|
|
163 buf_grow(dbuf);
|
|
164 dbuf[dloc++] = *s;
|
|
165 } while (*s++ != '\0');
|
|
166 }
|
|
167
|
|
168
|
|
169 /* Constant pool */
|
|
170
|
|
171 static growdecl(const_sym);
|
|
172 #define const_sym growbuf(const_sym, symbol)
|
|
173 #define nconsts growsize(const_sym)
|
|
174
|
|
175 #define get_const(n) get4(dbuf + proc_start + 4 * (CP_CONST+(n)))
|
|
176
|
|
177 static int find_const(int value, symbol sym) {
|
|
178 int i;
|
|
179
|
|
180 for (i = 0; i < nconsts; i++) {
|
|
181 if (sym == NULL
|
|
182 ? (const_sym[i] == NULL && get_const(i) == value)
|
|
183 : const_sym[i] == sym)
|
|
184 return i;
|
|
185 }
|
|
186
|
|
187 i = nconsts++;
|
|
188 buf_grow(const_sym);
|
|
189 const_sym[i] = sym;
|
|
190 buf_grow(dbuf);
|
|
191
|
|
192 if (sym == NULL)
|
|
193 put_value(dloc, value, R_WORD);
|
|
194 else
|
|
195 use_global(sym, dbuf, dloc);
|
|
196
|
|
197 dloc += 4;
|
|
198 return i;
|
|
199 }
|
|
200
|
|
201 static int find_dconst(int val0, int val1) {
|
|
202 int i;
|
|
203
|
|
204 for (i = 0; i < nconsts-1; i++) {
|
|
205 if (const_sym[i] == NULL && const_sym[i+1] == NULL
|
|
206 && get_const(i) == val0 && get_const(i+1) == val1)
|
|
207 return i;
|
|
208 }
|
|
209
|
|
210 i = nconsts; nconsts += 2;
|
|
211 buf_grow(const_sym);
|
|
212 const_sym[i] = const_sym[i+1] = NULL;
|
|
213 data_value(val0, R_WORD);
|
|
214 data_value(val1, R_WORD);
|
|
215
|
|
216 return i;
|
|
217 }
|
|
218
|
|
219 static int make_const(char *s) {
|
|
220 if (isdigit((int) s[0]) || s[0] == '-')
|
|
221 return find_const(const_value(s), NULL);
|
|
222 else
|
|
223 return find_const(0, find_symbol(s));
|
|
224 }
|
|
225
|
|
226
|
|
227 /* Instruction templates */
|
|
228
|
|
229 /* find_template -- find first template for instruction */
|
|
230 static template find_template(const char *name) {
|
|
231 const char *s = name;
|
|
232 int q = 0;
|
|
233 char ch;
|
|
234
|
|
235 /* The templates are organised in a trie */
|
|
236
|
|
237 do {
|
|
238 ch = *s++ & 0x7f;
|
|
239
|
|
240 if (templ_check[q+ch] != ch)
|
|
241 panic("*no template found for %s", name);
|
|
242
|
|
243 q = templ_trie[q+ch];
|
|
244 } while (ch != '\0');
|
|
245
|
|
246 return &templates[q];
|
|
247 }
|
|
248
|
|
249 /* fits -- test if an integer fits in a certain number of bits */
|
|
250 static mybool fits(int x, int n) {
|
|
251 int max = 1 << (n-1);
|
|
252 return (-max <= x && x < max);
|
|
253 }
|
|
254
|
|
255 /* fix_labels -- compute target for jump */
|
|
256 static void fix_labels(phrase q) {
|
|
257 const char *p = q->q_templ->t_pattern;
|
|
258
|
|
259 for (int j = 0; p[j] != '\0'; j++)
|
|
260 if (p[j] == 'R' || p[j] == 'S')
|
|
261 q->q_target = find_label(q->q_arg[j]);
|
|
262 }
|
|
263
|
|
264 /* displacement -- calculate branch displacement */
|
|
265 static int displacement(phrase q) {
|
|
266 /* Phrase |q| is a branch instruction. The signed displacement
|
|
267 is the distance from the opcode to the target. */
|
|
268 return (q->q_target->q_addr - q->q_addr);
|
|
269 }
|
|
270
|
|
271 /* match -- test whether a template matches its arguments */
|
|
272 static mybool match(phrase q, template t) {
|
|
273 /* Just check the last operand */
|
|
274 int n = strlen(t->t_pattern);
|
|
275 const char *p = t->t_pattern;
|
|
276 int *a = q->q_arg;
|
|
277
|
|
278 if (n == 0) return TRUE;
|
|
279
|
|
280 switch (p[n-1]) {
|
|
281 case 'N':
|
|
282 { int val = a[n-1];
|
|
283 return (val >= t->t_lo && val <= t->t_hi
|
|
284 && (val - t->t_lo) % t->t_step == 0); }
|
|
285 case '1':
|
|
286 case 'K':
|
|
287 return fits(a[n-1], 8);
|
|
288 case '2':
|
|
289 case 'L':
|
|
290 return fits(a[n-1], 16);
|
|
291 case 'R':
|
|
292 return fits(displacement(q), 16);
|
|
293 case 'S':
|
|
294 return fits(displacement(q), 8);
|
|
295 default:
|
|
296 return TRUE;
|
|
297 }
|
|
298 }
|
|
299
|
|
300 #ifdef DEBUG
|
|
301 static void print_args(phrase q) {
|
|
302 const char *patt = q->q_templ->t_pattern;
|
|
303
|
|
304 for (int j = 0; patt[j] != '\0'; j++) {
|
|
305 switch (patt[j]) {
|
|
306 case '1':
|
|
307 case '2':
|
|
308 case 'N':
|
|
309 case 'K':
|
|
310 case 'L':
|
|
311 printf(" %d", q->q_arg[j]); break;
|
|
312 case 'R':
|
|
313 case 'S':
|
|
314 printf(" %+d", displacement(q)); break;
|
|
315 default:
|
|
316 printf(" ???");
|
|
317 }
|
|
318 }
|
|
319 }
|
|
320 #endif
|
|
321
|
|
322 static int get_arg(char tmpl, char *rand, template t, int cxt[]) {
|
|
323 if (rand[0] == '$' && cxt != NULL)
|
|
324 return cxt[rand[1] - 'a'];
|
|
325
|
|
326 switch (tmpl) {
|
|
327 case '1':
|
|
328 case '2':
|
|
329 case 'N':
|
|
330 if (isdigit((int) rand[0]) || rand[0] == '-')
|
|
331 return const_value(rand);
|
|
332 else
|
|
333 return sym_value(find_symbol(rand));
|
|
334
|
|
335 case 'R':
|
|
336 case 'S':
|
|
337 return make_label(find_symbol(rand));
|
|
338
|
|
339 case 'K':
|
|
340 case 'L':
|
|
341 return make_const(rand);
|
|
342
|
|
343 default:
|
|
344 panic("*bad template %c for %s", tmpl, t->t_name);
|
|
345 return 0;
|
|
346 }
|
|
347 }
|
|
348
|
|
349 /* do_template -- enter an instruction */
|
|
350 static phrase do_template(template t, char *rands[], phrase rgt, int cxt[]) {
|
|
351 /* Template t determines the number and kinds of operands for the
|
|
352 instruction; depending on the values of the operands, it may or
|
|
353 may not end up actually matching the instruction. */
|
|
354
|
|
355 phrase q = alloc_phrase();
|
|
356 phrase lft = rgt->q_prev;
|
|
357 const char *patt = t->t_pattern;
|
|
358
|
|
359 q->q_name = t->t_name;
|
|
360 q->q_templ = t;
|
|
361 for (int i = 0; patt[i] != '\0'; i++)
|
|
362 q->q_arg[i] = get_arg(patt[i], rands[i], t, cxt);
|
|
363 q->q_addr = 0;
|
|
364 q->q_sym = NULL;
|
|
365 q->q_target = NULL;
|
|
366 q->q_prev = lft; q->q_next = rgt;
|
|
367 lft->q_next = rgt->q_prev = q;
|
|
368 return q;
|
|
369 }
|
|
370
|
|
371 /* expand -- replace macro by its expansion */
|
|
372 static phrase expand(phrase q) {
|
|
373 static char buf[128];
|
|
374 char *words[10];
|
|
375 template t = q->q_templ;
|
|
376 phrase r = q->q_prev, q1;
|
|
377
|
|
378 for (int i = 0; t->t_macro[i] != NULL; i++) {
|
|
379 strcpy(buf, t->t_macro[i]);
|
|
380 int n = split_line(buf, words);
|
|
381 template t1 = find_template(words[0]);
|
|
382 if (strlen(t1->t_pattern) != n-1 || t->t_size < 0)
|
|
383 panic("*macro expansion failed");
|
|
384
|
|
385 /* Insert expansion before original phrase */
|
|
386 q1 = do_template(t1, &words[1], q, q->q_arg);
|
|
387 fix_labels(q1);
|
|
388 }
|
|
389
|
|
390 /* Delete the original */
|
|
391 q->q_prev->q_next = q->q_next;
|
|
392 q->q_next->q_prev = q->q_prev;
|
|
393
|
|
394 return r->q_next;
|
|
395 }
|
|
396
|
|
397 /* check_matches -- revise choice of templates, return TRUE if ok already */
|
|
398 static mybool check_matches(void) {
|
|
399 mybool ok = TRUE;
|
|
400
|
|
401 for (phrase q = abuf->q_next; q != abuf; ) {
|
|
402 template t = q->q_templ;
|
|
403
|
|
404 if (t->t_macro[0] != NULL) {
|
|
405 /* A macro instruction: expand it */
|
|
406 q = expand(q);
|
|
407 ok = FALSE;
|
|
408 } else if (! match(q, t)) {
|
|
409 t++;
|
|
410
|
|
411 if (t >= &templates[NTEMPLATES] || t->t_name != NULL) {
|
|
412 panic("*no template fits %s", q->q_name);
|
|
413 }
|
|
414
|
|
415 q->q_templ = t;
|
|
416 ok = FALSE;
|
|
417 } else {
|
|
418 q = q->q_next;
|
|
419 }
|
|
420 }
|
|
421
|
|
422 return ok;
|
|
423 }
|
|
424
|
|
425 /* assemble -- assemble instructions */
|
|
426 static void assemble(void) {
|
|
427 mybool ok;
|
|
428 int trial = 0;
|
|
429
|
|
430 for_phrases (q) fix_labels(q);
|
|
431
|
|
432 /* A tentative assignment of templates has already been computed,
|
|
433 but the arguments may not fit in the field sizes assigned. So
|
|
434 now we repeatedly revise the assignment until all arguments fit.
|
|
435 Changing the assignment will increase the size of some instructions,
|
|
436 perhaps making branches longer so that they no longer fit either
|
|
437 -- that's why iteration is necessary.
|
|
438
|
|
439 The invariant is that there is no feasible choice of templates that
|
|
440 makes any instruction smaller than it is in the current assignment.
|
|
441 The variant is the total number of templates that remain to be tried.
|
|
442 Correctness of the algorithm follows from the fact that making one
|
|
443 instruction larger cannot allow another to be smaller. */
|
|
444
|
|
445 do {
|
|
446 int a = 0;
|
|
447 trial++;
|
|
448 #ifdef DEBUG
|
|
449 if (dflag > 0)
|
|
450 printf("Checking templates (pass %d)\n", trial);
|
|
451 #endif
|
|
452
|
|
453 /* Calculate address of each instruction */
|
|
454 for_phrases (q) {
|
|
455 q->q_addr = a;
|
|
456 a += q->q_templ->t_size;
|
|
457 }
|
|
458
|
|
459 code_size = a;
|
|
460 ok = check_matches(); /* Revise template choices */
|
|
461 } while (!ok);
|
|
462 }
|
|
463
|
|
464 /* make_binary -- output binary code */
|
|
465 static void make_binary(void) {
|
|
466 for_phrases (q) {
|
|
467 template t = q->q_templ;
|
|
468 const char *p = t->t_pattern;
|
|
469 int *a = q->q_arg;
|
|
470
|
|
471 #ifdef DEBUG
|
|
472 if (dflag > 0) {
|
|
473 printf("%d: %s(%s)", q->q_addr, q->q_name, p);
|
|
474 print_args(q);
|
|
475 printf("\n");
|
|
476 }
|
|
477 #endif
|
|
478
|
|
479 if (q->q_sym != NULL)
|
|
480 def_global(q->q_sym, CODE, iloc + q->q_addr, X_LINE);
|
|
481
|
|
482 if (p[0] == 'N')
|
|
483 write_int(1, t->t_op + (a[0] - t->t_lo)/t->t_step);
|
|
484 else if (t->t_oplen > 0)
|
|
485 binwrite(&t->t_op, t->t_oplen);
|
|
486
|
|
487 for (int j = 0; p[j] != '\0'; j++) {
|
|
488 switch (p[j]) {
|
|
489 case 'N':
|
|
490 break;
|
|
491 case '1':
|
|
492 case 'K':
|
|
493 write_int(1, a[j]); break;
|
|
494 case '2':
|
|
495 case 'L':
|
|
496 write_int(2, a[j]); break;
|
|
497 case 'R':
|
|
498 write_int(2, displacement(q)); break;
|
|
499 case 'S':
|
|
500 write_int(1, displacement(q)); break;
|
|
501 default:
|
|
502 panic("*bad template %c", p[j]);
|
|
503 }
|
|
504 }
|
|
505 }
|
|
506 }
|
|
507
|
|
508 /* MARK pseudo-instructions generate no code, and are used to place labels,
|
|
509 line numbers, etc. */
|
|
510 struct _template mark = {
|
|
511 "*MARK*", "", 0, 0, 0, 0, 0, 0, { NULL }
|
|
512 };
|
|
513
|
|
514 static phrase put_mark(symbol s) {
|
|
515 phrase q = do_template(&mark, NULL, abuf, NULL);
|
|
516 q->q_sym = s;
|
|
517 return q;
|
|
518 }
|
|
519
|
|
520 /* const_head -- start of constant pool */
|
|
521 static void const_head(int prim, int code, int reloc,
|
|
522 int frame, int stack, char *map) {
|
|
523 data_value(prim, R_SUBR); /* Primitive */
|
|
524 data_value(code, reloc); /* Entry point */
|
|
525 data_value(0, R_WORD); /* Code size */
|
|
526 data_value(frame, R_WORD); /* Frame size in words */
|
|
527 data_value(stack, R_WORD); /* Stack size in words */
|
|
528 data_word(map); /* Frame map */
|
|
529 data_value(0, R_WORD); /* Stack map table */
|
|
530 }
|
|
531
|
|
532 typedef struct {
|
|
533 phrase sm_addr; /* Pointer to the JPROC instruction */
|
|
534 char *sm_text; /* Symbol or numeric value */
|
|
535 } stackmap;
|
|
536
|
|
537 static growdecl(smbuf);
|
|
538 #define smbuf growbuf(smbuf, stackmap)
|
|
539 #define smp growsize(smbuf)
|
|
540
|
|
541 /* fix_stackmaps -- fix up the stack maps for the current procedure */
|
|
542 static void fix_stackmaps(void) {
|
|
543 if (smp == 0) return;
|
|
544
|
|
545 /* Fill in the address of the table in the constant pool */
|
|
546 put_value(proc_start + 4*CP_STKMAP, dloc, R_DATA);
|
|
547
|
|
548 /* Create the table itself */
|
|
549 for (int i = 0; i < smp; i++) {
|
|
550 stackmap *sm = &smbuf[i];
|
|
551
|
|
552 /* The return address for the call: '+1' to allow for the space
|
|
553 occupied by the JPROC instruction */
|
|
554 data_value(iloc + sm->sm_addr->q_addr + 1, R_CODE);
|
|
555
|
|
556 /* The stack map */
|
|
557 data_word(sm->sm_text);
|
|
558 }
|
|
559
|
|
560 data_value(0, R_WORD);
|
|
561 }
|
|
562
|
|
563 typedef struct {
|
|
564 int h_begin, h_end; /* Scope of handler */
|
|
565 symbol h_excep; /* Exception */
|
|
566 phrase h_body; /* Handler code */
|
|
567 } handler;
|
|
568
|
|
569 /* check_inproc -- panic if not in a procedure */
|
|
570 static void check_inproc(const char *opcode) {
|
|
571 if (this_proc == NULL)
|
|
572 panic("*%s occurs outside any procedure", opcode);
|
|
573 }
|
|
574
|
|
575 /* do_directive -- process a directive */
|
|
576 static void do_directive(const char *dir, int n, char *rands[], int nrands) {
|
|
577 union { int n; float f; } fcvt;
|
|
578 dblbuf dcvt;
|
|
579 int v;
|
|
580
|
|
581 switch (n) {
|
|
582 case D_LABEL:
|
|
583 check_inproc(dir);
|
|
584 /* Each label is defined as the |abuf| index of its target */
|
|
585 def_label(find_symbol(rands[0]), put_mark(NULL));
|
|
586 break;
|
|
587
|
|
588 case D_STRING:
|
|
589 for (int i = 0; rands[0][2*i] != '\0'; i++) {
|
|
590 buf_grow(dbuf);
|
|
591 dbuf[dloc++] = hexchar(&rands[0][2*i]);
|
|
592 }
|
|
593 dloc = align(dloc, 4);
|
|
594 break;
|
|
595
|
|
596 case D_CONST:
|
|
597 check_inproc(dir);
|
|
598 if ((isdigit((int) rands[0][0]) || rands[0][0] == '-')
|
|
599 && fits(v = const_value(rands[0]), 16))
|
|
600 gen_inst("PUSH %d", v);
|
|
601 else
|
|
602 gen_inst("LDKW %d", make_const(rands[0]));
|
|
603 break;
|
|
604
|
|
605 case D_GLOBAL:
|
|
606 check_inproc(dir);
|
|
607 gen_inst("LDKW %d", make_const(rands[0]));
|
|
608 break;
|
|
609
|
|
610 case D_FCONST:
|
|
611 check_inproc(dir);
|
|
612 fcvt.f = atof(rands[0]);
|
|
613 gen_inst("LDKF %d", find_const(fcvt.n, NULL));
|
|
614 break;
|
|
615
|
|
616 case D_DCONST:
|
|
617 check_inproc(dir);
|
|
618 dcvt.d = atof(rands[0]);
|
|
619 gen_inst("LDKD %d", find_dconst(dcvt.n.lo, dcvt.n.hi));
|
|
620 break;
|
|
621
|
|
622 case D_QCONST:
|
|
623 check_inproc(dir);
|
|
624 dcvt.q = strtoll(rands[0], NULL, 0);
|
|
625 gen_inst("LDKQ %d", find_dconst(dcvt.n.lo, dcvt.n.hi));
|
|
626 break;
|
|
627
|
|
628 case D_WORD:
|
|
629 data_word(rands[0]);
|
|
630 break;
|
|
631
|
|
632 case D_GLOVAR:
|
|
633 def_global(find_symbol(rands[0]), BSS, bloc, X_DATA);
|
|
634 bloc = align(bloc + strtoul(rands[1], NULL, 0), 4);
|
|
635 break;
|
|
636
|
|
637 case D_MODULE:
|
|
638 nmods++;
|
|
639 this_module = find_symbol(rands[0]);
|
|
640 def_global(this_module, DATA, dloc, X_MODULE);
|
|
641 module_data(this_module, strtoul(rands[1], NULL, 0),
|
|
642 strtol(rands[2], NULL, 0));
|
|
643 break;
|
|
644
|
|
645 case D_PRIMDEF:
|
|
646 nprocs++;
|
|
647 dloc = align(dloc, 8);
|
|
648 buf_grow(prims);
|
|
649 prims[nprims++] = dloc;
|
|
650 def_global(find_symbol(rands[0]), DATA, dloc, X_PROC);
|
|
651 const_head(DLTRAP, dloc + 4*CP_CONST + 4, R_DATA, 0, 0, NULL);
|
|
652 data_value(0, R_WORD); // Pointer to access block
|
|
653 put_string(rands[2]); // Type descriptor
|
|
654 put_string(rands[1]); // Symbol name
|
|
655 dloc = align(dloc, 4);
|
|
656 break;
|
|
657
|
|
658 case D_PROC:
|
|
659 nprocs++;
|
|
660 dloc = align(dloc, 8);
|
|
661 this_proc = find_symbol(rands[0]);
|
|
662 proc_start = dloc;
|
|
663 def_global(this_proc, DATA, proc_start, X_PROC);
|
|
664 const_head(INTERP, iloc, R_CODE, atoi(rands[1]),
|
|
665 atoi(rands[2]), rands[3]);
|
|
666
|
|
667 init_abuf();
|
|
668 init_labels();
|
|
669 nconsts = 0;
|
|
670 smp = 0;
|
|
671 break;
|
|
672
|
|
673 case D_STKMAP:
|
|
674 /* Stack map for a procedure call */
|
|
675 check_inproc(dir);
|
|
676 buf_grow(smbuf);
|
|
677 smbuf[smp].sm_addr = put_mark(NULL);
|
|
678 smbuf[smp].sm_text = must_strdup(rands[0]);
|
|
679 smp++;
|
|
680 break;
|
|
681
|
|
682 case D_END:
|
|
683 /* End of procedure body */
|
|
684 check_inproc(dir);
|
|
685 assemble(); /* Finally choose templates */
|
|
686 fix_stackmaps(); /* Compile the stack maps */
|
|
687 make_binary(); /* Output the code */
|
|
688 put_value(proc_start + 4*CP_SIZE, code_size, R_WORD);
|
|
689 iloc += code_size;
|
|
690 this_proc = NULL;
|
|
691 break;
|
|
692
|
|
693 case D_IMPORT:
|
|
694 case D_ENDHDR:
|
|
695 /* Ignore directives that appear in the file header */
|
|
696 break;
|
|
697
|
|
698 case D_DEFINE:
|
|
699 def_global(find_symbol(rands[0]), DATA, dloc, X_DATA);
|
|
700 break;
|
|
701
|
|
702 case D_LINE:
|
|
703 check_inproc(dir);
|
|
704
|
|
705 if (gflag) {
|
|
706 char buf[64];
|
|
707 sprintf(buf, "%s.%s", sym_name(this_module), rands[0]);
|
|
708 put_mark(make_symbol(buf));
|
|
709 }
|
|
710
|
|
711 if (linecount)
|
|
712 put_inst("LNUM", rands, nrands);
|
|
713
|
|
714 break;
|
|
715
|
|
716 #ifdef SPECIALS
|
|
717 case D_PCALL:
|
|
718 check_inproc(dir);
|
|
719 gen_inst("CALL %d", atoi(rands[0])+1);
|
|
720 break;
|
|
721
|
|
722 case D_PCALLW:
|
|
723 check_inproc(dir);
|
|
724 gen_inst("CALLW %d", atoi(rands[0])+1);
|
|
725 break;
|
|
726 #endif
|
|
727
|
|
728 default:
|
|
729 panic("*unknown directive %s (%d)", dir, n);
|
|
730 }
|
|
731 }
|
|
732
|
|
733 /* put_inst -- process one instruction or directive */
|
|
734 void put_inst(const char *name, char *rands[], unsigned nrands) {
|
|
735 template t = find_template(name);
|
|
736
|
|
737 if (nrands != strlen(t->t_pattern)) {
|
|
738 fprintf(stderr, "Instruction: %s", name);
|
|
739 for (int i = 0; i < nrands; i++)
|
|
740 fprintf(stderr, " %s", rands[i]);
|
|
741 fprintf(stderr, ", File: %s\n", err_file);
|
|
742 panic("*%s needs %d operands, got %d",
|
|
743 name, strlen(t->t_pattern), nrands);
|
|
744 }
|
|
745
|
|
746 if (t->t_size < 0)
|
|
747 do_directive(t->t_name, t->t_op, rands, nrands);
|
|
748 else {
|
|
749 check_inproc(name);
|
|
750 do_template(t, rands, abuf, NULL);
|
|
751 }
|
|
752 }
|
|
753
|
|
754 /* gen_inst -- generate an instruction from text */
|
|
755 void gen_inst(const char *fmt, ...) {
|
|
756 char line[80];
|
|
757 char *words[10];
|
|
758 int nwords;
|
|
759
|
|
760 va_list ap;
|
|
761
|
|
762 va_start(ap, fmt);
|
|
763 vsprintf(line, fmt, ap);
|
|
764 strcat(line, "\n");
|
|
765 va_end(ap);
|
|
766
|
|
767 nwords = split_line(line, words);
|
|
768 put_inst(words[0], &words[1], nwords-1);
|
|
769 }
|
|
770
|
|
771 /* save_string -- save a string in the data segment */
|
|
772 void save_string(const char *label, char *str) {
|
|
773 def_global(find_symbol(label), DATA, dloc, X_DATA);
|
|
774 put_string(str);
|
|
775 dloc = align(dloc, 4);
|
|
776 }
|
|
777
|
|
778
|
|
779 /* Object file output */
|
|
780
|
|
781 static int start; /* Starting offset of binary */
|
|
782
|
|
783 void init_linker(char *outname, char *interp) {
|
|
784 buf_init(dbuf, INIT_XMEM, 4, uchar, "data");
|
|
785 buf_init(rbuf, INIT_XMEM/(WORD_SIZE * CODES_PER_WORD),
|
|
786 1, unsigned, "relocation");
|
|
787 buf_init(smbuf, 16, 1, stackmap, "stack maps");
|
|
788 buf_init(const_sym, 256, 1, symbol, "constant pool");
|
|
789 buf_init(prims, 256, 1, int, "primitives");
|
|
790
|
|
791 binfp = fopen(outname, "wb");
|
|
792 if (binfp == NULL) {
|
|
793 perror(outname);
|
|
794 exit(2);
|
|
795 }
|
|
796
|
|
797 if (interp != NULL)
|
|
798 fprintf(binfp, "#!%s\n", interp);
|
|
799
|
|
800 start = ftell(binfp);
|
|
801 }
|
|
802
|
|
803 /* end_linking -- write later parts of object file */
|
|
804 void end_linking(void) {
|
|
805 trailer t;
|
|
806 int fsize, csize, symcount = 0, nwritten;
|
|
807 const char *magic = MAGIC;
|
|
808
|
|
809 csize = ftell(binfp) - start;
|
|
810 if (csize != iloc) {
|
|
811 fprintf(stderr, "csize = %d, iloc = %d\n", csize, iloc);
|
|
812 panic("*Wrong code size");
|
|
813 }
|
|
814
|
|
815 fix_data(dbuf, dloc);
|
|
816 rloc = (dloc/WORD_SIZE+CODES_PER_WORD-1)/CODES_PER_WORD;
|
|
817 buf_grow(rbuf);
|
|
818
|
|
819 binwrite(dbuf, dloc);
|
|
820 binwrite(rbuf, rloc * sizeof(unsigned));
|
|
821 if (!sflag) symcount = write_symtab();
|
|
822
|
|
823 fsize = ftell(binfp) + sizeof(trailer);
|
|
824
|
|
825 #define sym_val(x) (known(x) ? sym_value(find_symbol(x)) : 0)
|
|
826
|
|
827 /* Trailer */
|
|
828 strncpy((char *) t.magic, magic, 4);
|
|
829 put4(t.sig, SIG);
|
|
830 put4(t.primsig, 0);
|
|
831 put4(t.start, start - fsize);
|
|
832 put4(t.entry, sym_val("MAIN"));
|
|
833 put4(t.gcmap, sym_val("GCMAP"));
|
|
834 put4(t.libdir, sym_val("LIBDIR"));
|
|
835 put4(t.segment[S_CODE], iloc);
|
|
836 put4(t.segment[S_DATA], dloc);
|
|
837 put4(t.segment[S_BSS], bloc);
|
|
838 put4(t.segment[S_STACK], stack_size);
|
|
839 put4(t.nprocs, (sflag ? 0 : nprocs));
|
|
840 put4(t.nmods, (sflag ? 0 : nmods));
|
|
841 put4(t.nsyms, symcount);
|
|
842 nwritten = fwrite(&t, sizeof(trailer), 1, binfp);
|
|
843 if (nwritten < 1)
|
|
844 panic("Couldn't write trailer");
|
|
845
|
|
846 fclose(binfp);
|
|
847 }
|
|
848
|
|
849
|
|
850 /* Routines for writing values in machine-independent byte order */
|
|
851
|
|
852 void put_int(int n, uchar *buf, int x) {
|
|
853 for (int i = 0; i < n; i++)
|
|
854 buf[i] = (x >> (8*i)) & 0xff;
|
|
855 }
|
|
856
|
|
857 int get4(uchar *buf) {
|
|
858 return buf[0] + (buf[1] << 8) + (buf[2] << 16) + (buf[3] << 24);
|
|
859 }
|
|
860
|
|
861 void write_string(const char *s) {
|
|
862 binwrite((void *) s, strlen(s)+1);
|
|
863 }
|
|
864
|
|
865 void write_int(int n, int x) {
|
|
866 uchar buf[4];
|
|
867 put_int(n, buf, x);
|
|
868 binwrite(buf, n);
|
|
869 }
|
|
870
|
|
871
|
|
872 /* Primitive table */
|
|
873
|
|
874 void dump_prims(void) {
|
|
875 printf("/* Generated by oblink */\n\n");
|
|
876 printf("#include \"primtab.h\"\n\n");
|
|
877 printf("#define PRIMS(direct, indirect, wrapper)");
|
|
878
|
|
879 for (int i = 0; i < nprims; i++) {
|
|
880 char *tstring = (char *) &dbuf[prims[i]] + 4*CP_CONST + 4;
|
|
881 char *name = tstring + strlen(tstring) + 1;
|
|
882
|
|
883 printf(" \\\n");
|
|
884 if (tstring[0] == '*')
|
|
885 /* Declare a direct primitive */
|
|
886 printf(" direct(%s)", name);
|
|
887 else {
|
|
888 /* Build a wrapper */
|
|
889 char *mac = (isupper(name[0]) ? "indirect" : "wrapper");
|
|
890 printf(" %s(%s", mac, name);
|
|
891 for (int i = 0; tstring[i] != '\0'; i++)
|
|
892 printf(", %c", tstring[i]);
|
|
893 printf(")");
|
|
894 }
|
|
895 }
|
|
896
|
|
897 printf("\n\n");
|
|
898 printf("PRIMTAB(PRIMS)");
|
|
899 }
|
|
900
|