annotate keiko/linker.c @ 1:b5139af1a420 tip basis

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