Mercurial > hg > compilers
comparison keiko/linker.c @ 0:bfdcc3820b32
Basis
author | Mike Spivey <mike@cs.ox.ac.uk> |
---|---|
date | Thu, 05 Oct 2017 08:04:15 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:bfdcc3820b32 |
---|---|
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 |