diff options
Diffstat (limited to 'gnu/usr.bin/cc/cc_int/bc-emit.c')
-rw-r--r-- | gnu/usr.bin/cc/cc_int/bc-emit.c | 991 |
1 files changed, 991 insertions, 0 deletions
diff --git a/gnu/usr.bin/cc/cc_int/bc-emit.c b/gnu/usr.bin/cc/cc_int/bc-emit.c new file mode 100644 index 0000000..b5d0c6c --- /dev/null +++ b/gnu/usr.bin/cc/cc_int/bc-emit.c @@ -0,0 +1,991 @@ +/* Output bytecodes for GNU C-compiler. + Copyright (C) 1993, 1994 Free Software Foundation, Inc. + +This file is part of GNU CC. + +GNU CC is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU CC is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU CC; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + + +#include "config.h" +#ifdef __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif +#include "machmode.h" +#include "rtl.h" +#include "real.h" +#include "obstack.h" +#include "bytecode.h" +#ifdef __GNUC__ +#include "bytetypes.h" +#endif +#include "bc-emit.h" +#include "bc-opcode.h" +#include "bc-typecd.h" +#include "bi-run.h" + +#include <stdio.h> + +extern char *xmalloc (), *xrealloc (); +extern void free (); + +extern struct obstack *rtl_obstack; + +/* Indexed by mode class, gives the narrowest mode for each class. */ + +extern enum machine_mode class_narrowest_mode[(int) MAX_MODE_CLASS]; + +/* Commonly used modes. */ +/* Mode whose width is BITS_PER_UNIT */ +extern enum machine_mode byte_mode; + +/* Mode whose width is BITS_PER_WORD */ +extern enum machine_mode word_mode; + +/* Vector indexed by opcode giving info about the args for each opcode. */ +static struct arityvec arityvec[] = { +#include "bc-arity.h" +}; + +/* How to print a symbol name for the assembler. */ +static void +prsym (file, s) + FILE *file; + char *s; +{ + if (*s == '*') + fprintf (file, "%s", s + 1); + else + +#ifdef NAMES_HAVE_UNDERSCORES + fprintf (file, "_%s", s); +#else + fprintf (file, "%s", s); +#endif + +} + +/* Maintain a bucket hash table for symbol names. */ + +#define HASH_BITS 32 +#define HASH_SIZE 509 + +static struct bc_sym *hashtab[HASH_SIZE]; + +static unsigned int +hash (name) + char *name; +{ + unsigned int hash = 0; + + while (*name) + { + hash = hash << 3 | hash >> HASH_BITS - 3; + hash += *name++; + } + + return hash % HASH_SIZE; +} + + +/* Look up the named symbol, creating it if it doesn't exist. */ +struct bc_sym * +sym_lookup (name) + char *name; +{ + int i; + struct bc_sym *s; + + i = hash (name); + for (s = hashtab[i]; s; s = s->next) + if (!strcmp (s->name, name)) + return s; + + s = (struct bc_sym *) xmalloc (sizeof (struct bc_sym)); + s->name = xmalloc (strlen (name) + 1); + strcpy (s->name, name); + s->defined = s->global = s->common = 0; + s->val = 0; + s->next = hashtab[i]; + hashtab[i] = s; + return s; +} + + +/* Write out .globl and common symbols to the named file. */ +static void +bc_sym_write (file) + FILE *file; +{ + int i; + struct bc_sym *s; + + for (i = 0; i < HASH_SIZE; ++i) + for (s = hashtab[i]; s; s = s->next) + { + if (s->global) + { + fprintf (file, "\n\t.globl "); + prsym (file, s->name); + putc ('\n', file); + if (s->common) + { + fprintf (file, "\n\t.comm "); + prsym (file, s->name); + fprintf (file, ", %d\n", s->val); + } + } + else if (s->common) + { + fprintf (file, "\n\t.lcomm "); + prsym (file, s->name); + fprintf (file, ", %d\n", s->val); + } + } +} + + + + +/* Create and initialize a new segment. */ +static struct bc_seg * +seg_create () +{ + struct bc_seg *result; + + result = (struct bc_seg *) xmalloc (sizeof (struct bc_seg)); + result->alloc = 256; + result->data = xmalloc (result->alloc); + result->size = 0; + result->syms = 0; + result->relocs = 0; + return result; +} + + +/* Advance the segment index to the next alignment boundary. */ +static void +seg_align (seg, log) + struct bc_seg *seg; + int log; +{ + unsigned int oldsize = seg->size; + + seg->size = seg->size + (1 << log) - 1 & ~((1 << log) - 1); + if (seg->size > seg->alloc) + { + while (seg->size > seg->alloc) + seg->alloc *= 2; + seg->data = xrealloc (seg->data, seg->alloc); + } + bzero (seg->data + oldsize, seg->size - oldsize); +} + + +/* Append the given data to the given segment. */ +static void +seg_data (seg, data, size) + struct bc_seg *seg; + char *data; + unsigned int size; +{ + if (seg->size + size > seg->alloc) + { + while (seg->size + size > seg->alloc) + seg->alloc *= 2; + seg->data = xrealloc (seg->data, seg->alloc); + } + + bcopy (data, seg->data + seg->size, size); + seg->size += size; +} + + +/* Append a zero-filled skip to the given segment. */ +static void +seg_skip (seg, size) + struct bc_seg *seg; + unsigned int size; +{ + if (seg->size + size > seg->alloc) + { + while (seg->size + size > seg->alloc) + seg->alloc *= 2; + seg->data = xrealloc (seg->data, seg->alloc); + } + + memset (seg->data + seg->size, 0, size); + seg->size += size; +} + + +/* Define the given name as the current offset in the given segment. It + is an error if the name is already defined. Return 0 or 1 indicating + failure or success respectively. */ +static int +seg_defsym (seg, name) + struct bc_seg *seg; + char *name; +{ + struct bc_sym *sym; + struct bc_segsym *segsym; + + sym = sym_lookup (name); + if (sym->defined) + return 0; + + sym->defined = 1; + sym->val = seg->size; + segsym = (struct bc_segsym *) xmalloc (sizeof (struct bc_segsym)); + segsym->sym = sym; + segsym->next = seg->syms; + seg->syms = segsym; + return 1; +} + + +/* Generate in seg's data a reference to the given sym, adjusted by + the given offset. */ +static void +seg_refsym (seg, name, offset) + struct bc_seg *seg; + char *name; + int offset; +{ + struct bc_sym *sym; + struct bc_segreloc *segreloc; + + sym = sym_lookup (name); + segreloc = (struct bc_segreloc *) xmalloc (sizeof (struct bc_segreloc)); + segreloc->offset = seg->size; + segreloc->sym = sym; + segreloc->next = seg->relocs; + seg->relocs = segreloc; + seg_data (seg, (char *) &offset, sizeof offset); +} + + +/* Concatenate the contents of given segments into the first argument. */ +static void +seg_concat (result, seg) + struct bc_seg *result, *seg; +{ + unsigned int fix; + struct bc_segsym *segsym; + struct bc_segreloc *segreloc; + + seg_align (result, MACHINE_SEG_ALIGN); + fix = result->size; + seg_data (result, seg->data, seg->size); + free (seg->data); + + /* Go through the symbols and relocs of SEG, adjusting their offsets + for their new location in RESULT. */ + if (seg->syms) + { + segsym = seg->syms; + do + segsym->sym->val += fix; + while (segsym->next && (segsym = segsym->next)); + segsym->next = result->syms; + result->syms = seg->syms; + } + if (seg->relocs) + { + segreloc = seg->relocs; + do + segreloc->offset += fix; + while (segreloc->next && (segreloc = segreloc->next)); + segreloc->next = result->relocs; + result->relocs = seg->relocs; + } + + free ((char *) seg); +} + +/* Write a segment to a file. */ +static void +bc_seg_write (seg, file) + struct bc_seg *seg; + FILE *file; +{ + struct bc_segsym *segsym, *nsegsym, *psegsym; + struct bc_segreloc *segreloc, *nsegreloc, *psegreloc; + int i, offset, flag; + + /* Reverse the list of symbols. */ + for (psegsym = 0, segsym = seg->syms; segsym; segsym = nsegsym) + { + nsegsym = segsym->next; + segsym->next = psegsym; + psegsym = segsym; + } + seg->syms = psegsym; + + /* Reverse the list of relocs. */ + for (psegreloc = 0, segreloc = seg->relocs; segreloc; segreloc = nsegreloc) + { + nsegreloc = segreloc->next; + segreloc->next = psegreloc; + psegreloc = segreloc; + } + seg->relocs = psegreloc; + + /* Output each byte of the segment. */ + for (i = 0, segsym = seg->syms, segreloc = seg->relocs; i < seg->size; ++i) + { + while (segsym && segsym->sym->val == i) + { + if (i % 8 != 0) + putc ('\n', file); + + BC_WRITE_SEGSYM (segsym, file); + segsym = segsym->next; + flag = 1; + } + if (segreloc && segreloc->offset == i) + { + if (i % 8 != 0) + putc ('\n', file); + + bcopy (seg->data + i, (char *) &offset, sizeof (int)); + i += sizeof (int) - 1; + + BC_WRITE_RELOC_ENTRY (segreloc, file, offset); + segreloc = segreloc->next; + flag = 1; + } + else + { + if (i % 8 == 0 || flag) + BC_START_BYTECODE_LINE (file); + + BC_WRITE_BYTECODE (i % 8 == 0 || flag ? ' ' : ',', + seg->data[i] & 0xFF, + file); + flag = 0; + if (i % 8 == 7) + putc ('\n', file); + } + } + + /* Paranoia check--we should have visited all syms and relocs during + the output pass. */ + + if (segsym || segreloc) + abort (); +} + + + +/* Text and data segments of the object file in making. */ +static struct bc_seg *bc_text_seg; +static struct bc_seg *bc_data_seg; + +/* Called before anything else in this module. */ +void +bc_initialize () +{ + int min_class_size[(int) MAX_MODE_CLASS]; + enum machine_mode mode; + int i; + + bc_init_mode_to_code_map (); + + bc_text_seg = seg_create (); + bc_data_seg = seg_create (); + + dconst0 = REAL_VALUE_ATOF ("0", DFmode); + dconst1 = REAL_VALUE_ATOF ("1", DFmode); + dconst2 = REAL_VALUE_ATOF ("2", DFmode); + dconstm1 = REAL_VALUE_ATOF ("-1", DFmode); + + /* Find the narrowest mode for each class and compute the word and byte + modes. */ + + for (i = 0; i < (int) MAX_MODE_CLASS; i++) + min_class_size[i] = 1000; + + for (mode = VOIDmode; (int) mode < (int) MAX_MACHINE_MODE; + mode = (enum machine_mode) ((int) mode + 1)) + { + if (GET_MODE_SIZE (mode) < min_class_size[(int) GET_MODE_CLASS (mode)]) + { + class_narrowest_mode[(int) GET_MODE_CLASS (mode)] = mode; + min_class_size[(int) GET_MODE_CLASS (mode)] = GET_MODE_SIZE (mode); + } + if (GET_MODE_CLASS (mode) == MODE_INT + && GET_MODE_BITSIZE (mode) == BITS_PER_UNIT) + byte_mode = mode; + + if (GET_MODE_CLASS (mode) == MODE_INT + && GET_MODE_BITSIZE (mode) == BITS_PER_WORD) + word_mode = mode; + } +} + + +/* External addresses referenced in a function. Rather than trying to + work relocatable address directly into bytecoded functions (which would + require us to provide hairy location info and possibly obey alignment + rules imposed by the architecture) we build an auxilary table of + pointer constants, and encode just offsets into this table into the + actual bytecode. */ +static struct bc_seg *ptrconsts; + +/* Trampoline code for the function entry. */ +struct bc_seg *trampoline; + +/* Actual byte code of the function. */ +struct bc_seg *bytecode; + +/* List of labels defined in the function. */ +struct bc_label *labels; + +/* List of label references in the function. */ +struct bc_labelref *labelrefs; + + +/* Add symbol to pointer table. Return offset into table where + pointer was stored. The offset usually goes into the bytecode + stream as a constP literal. */ +int +bc_define_pointer (p) + char *p; +{ + int offset = ptrconsts->size; + + seg_refsym (ptrconsts, p, 0); + return offset; +} + + +/* Begin a bytecoded function. */ +int +bc_begin_function (name) + char *name; +{ + ptrconsts = seg_create (); + trampoline = seg_create (); + bytecode = seg_create (); + return seg_defsym (trampoline, name); +} + + +/* Force alignment in inline bytecode. */ +void +bc_align_bytecode (align) + int align; +{ + seg_align (bytecode, align); +} + + +/* Emit data inline into bytecode. */ +void +bc_emit_bytecode_const (data, size) + char *data; + unsigned int size; +{ + if (bytecode) + seg_data (bytecode, data, size); +} + + +/* Create a new "bytecode label", to have its value defined later. + Bytecode labels have nothing to do with the object file symbol table, + and are purely local to a given bytecoded function. */ +struct bc_label * +bc_get_bytecode_label () +{ + struct bc_label *result; + + result = (struct bc_label *) xmalloc (sizeof (struct bc_label)); + result->defined = 0; + result->next = labels; + result->uid = 0; + labels = result; + return result; +} + + +/* Define the given label with the current location counter. */ +int +bc_emit_bytecode_labeldef (label) + struct bc_label *label; +{ + extern int bc_new_uid (); + + if (!label || label->defined) + return 0; + + label->offset = bytecode->size; + label->defined = 1; + label->uid = bc_new_uid (); + +#ifdef DEBUG_PRINT_CODE + fprintf (stderr, "$%lx:\n", label); +#endif + + return 1; +} + + +/* Generate a location-relative reference to the given bytecode label. + It need not be defined yet; label references will be backpatched later. */ +void +bc_emit_bytecode_labelref (label) + struct bc_label *label; +{ + struct bc_labelref *labelref; + static int zero; + + labelref = (struct bc_labelref *) xmalloc (sizeof (struct bc_labelref)); + labelref->label = label; + labelref->offset = bytecode->size; + labelref->next = labelrefs; + labelrefs = labelref; + +#ifdef DEBUG_PRINT_CODE + fprintf (stderr, " $%lx", label); +#endif + + seg_data (bytecode, (char *) &zero, sizeof zero); +} + + +/* Emit a reference to an external address; generate the reference in the + ptrconst area, and emit an offset in the bytecode. */ +void +bc_emit_code_labelref (name, offset) + char *name; + int offset; +{ + int ptroff; + + ptroff = ptrconsts->size / sizeof (char *); + seg_data (bytecode, (char *) &ptroff, sizeof ptroff); + seg_refsym (ptrconsts, name, offset); + +#ifdef DEBUG_PRINT_CODE + fprintf (stderr, " [external <%x> %s]", ptroff, name); +#endif +} + + +/* Backpatch label references in the byte code, and concatenate the bytecode + and pointer constant segments to the cumulative text for the object file. + Return a label name for the pointer constants region. */ +char * +bc_end_function () +{ + int addr; + struct bc_label *label, *next; + struct bc_labelref *ref, *nextref; + char ptrconsts_label[20]; + static int nlab; + + /* Backpatch bytecode label references. */ + for (ref = labelrefs; ref; ref = ref->next) + if (ref->label->defined) + { + addr = ref->label->offset; + bcopy ((char *) &addr, bytecode->data + ref->offset, sizeof addr); + } + + /* Free the chains of labelrefs and labeldefs. */ + for (ref = labelrefs; ref; ref = nextref) + { + nextref = ref->next; + free ((char *) ref); + } + + for (label = labels; label; label = next) + { + next = label->next; + free ((char *) label); + } + + seg_concat (trampoline, bytecode); + seg_align (trampoline, MACHINE_SEG_ALIGN); + sprintf (ptrconsts_label, "*LP%d", nlab++); + seg_defsym (trampoline, ptrconsts_label); + seg_concat (trampoline, ptrconsts); + seg_concat (bc_text_seg, trampoline); + + labels = 0; + labelrefs = 0; + trampoline = 0; + bytecode = 0; + ptrconsts = 0; + + return sym_lookup (ptrconsts_label)->name; +} + +/* Force alignment in const data. */ +void +bc_align_const (align) + int align; +{ + seg_align (bc_text_seg, align); +} + +/* Emit const data. */ +void +bc_emit_const (data, size) + char *data; + unsigned int size; +{ + seg_data (bc_text_seg, data, size); +} + +/* Emit a zero-filled constant skip. */ +void +bc_emit_const_skip (size) + unsigned int size; +{ + seg_skip (bc_text_seg, size); +} + +/* Emit a label definition in const data. */ +int +bc_emit_const_labeldef (name) + char *name; +{ + return seg_defsym (bc_text_seg, name); +} + +/* Emit a label reference in const data. */ +void +bc_emit_const_labelref (name, offset) + char *name; + int offset; +{ + seg_refsym (bc_text_seg, name, offset); +} + +/* Force alignment in data. */ +void +bc_align_data (align) + int align; +{ + seg_align (bc_data_seg, align); +} + +/* Emit data. */ +void +bc_emit_data (data, size) + char *data; + unsigned int size; +{ + seg_data (bc_data_seg, data, size); +} + +/* Emit a zero-filled data skip. */ +void +bc_emit_data_skip (size) + unsigned int size; +{ + seg_skip (bc_data_seg, size); +} + +/* Emit label definition in data. */ +int +bc_emit_data_labeldef (name) + char *name; +{ + return seg_defsym (bc_data_seg, name); +} + +/* Emit label reference in data. */ +void +bc_emit_data_labelref (name, offset) + char *name; + int offset; +{ + seg_refsym (bc_data_seg, name, offset); +} + +/* Emit a common block of the given name and size. Note that + when the .o file is actually written non-global "common" + blocks will have to be turned into space in the data section. */ +int +bc_emit_common (name, size) + char *name; + unsigned int size; +{ + struct bc_sym *sym; + + sym = sym_lookup (name); + if (sym->defined) + return 0; + + sym->defined = 1; + sym->common = 1; + sym->val = size; + return 1; +} + +/* Globalize the given label. */ +void +bc_globalize_label (name) + char *name; +{ + struct bc_sym *sym; + + sym = sym_lookup (name); + sym->global = 1; +} + +static enum { in_text, in_data } section = in_text; + +void +bc_text () +{ + section = in_text; +} + +void +bc_data () +{ + section = in_data; +} + +void +bc_align (align) + int align; +{ + if (section == in_text) + bc_align_const (align); + else + bc_align_data (align); +} + +void +bc_emit (data, size) + char *data; + unsigned int size; +{ + if (section == in_text) + bc_emit_const (data, size); + else + bc_emit_data (data, size); +} + +void +bc_emit_skip (size) + unsigned int size; +{ + if (section == in_text) + bc_emit_const_skip (size); + else + bc_emit_data_skip (size); +} + +int +bc_emit_labeldef (name) + char *name; +{ + if (section == in_text) + return bc_emit_const_labeldef (name); + else + return bc_emit_data_labeldef (name); +} + +void +bc_emit_labelref (name, offset) + char *name; + int offset; +{ + if (section == in_text) + bc_emit_const_labelref (name, offset); + else + bc_emit_data_labelref (name, offset); +} + +void +bc_write_file (file) + FILE *file; +{ + BC_WRITE_FILE (file); +} + + +/* Allocate a new bytecode rtx. + If you supply a null BC_LABEL, we generate one. */ + +rtx +bc_gen_rtx (label, offset, bc_label) + char *label; + int offset; + struct bc_label *bc_label; +{ + rtx r; + + if (bc_label == 0) + bc_label = (struct bc_label *) xmalloc (sizeof (struct bc_label)); + + r = gen_rtx (CODE_LABEL, VOIDmode, label, bc_label); + bc_label->offset = offset; + + return r; +} + + +/* Print bytecode rtx */ +void +bc_print_rtl (fp, r) + FILE *fp; + rtx r; +{ +#if 0 /* This needs to get fixed to really work again. */ + /* BC_WRITE_RTL has a definition + that doesn't even make sense for this use. */ + BC_WRITE_RTL (r, fp); +#endif +} + + +/* Emit a bytecode, keeping a running tally of the stack depth. */ +void +bc_emit_bytecode (bytecode) + enum bytecode_opcode bytecode; +{ + char byte; + static int prev_lineno = -1; + + byte = bytecode; + +#ifdef BCDEBUG_PRINT_CODE + if (lineno != prev_lineno) + { + fprintf (stderr, "<line %d>\n", lineno); + prev_lineno = lineno; + } + + fputs (opcode_name[(unsigned int) bytecode], stderr); +#endif + + /* Due to errors we are often requested to output bytecodes that + will cause an interpreter stack undeflow when executed. Instead of + dumping core on such occasions, we omit the bytecode. Erroneous code + should not be executed, regardless. This makes life much easier, since + we don't have to deceive ourselves about the known stack depth. */ + + bc_emit_bytecode_const (&byte, 1); + + if ((stack_depth -= arityvec[(int) bytecode].ninputs) >= 0) + { + if ((stack_depth += arityvec[(int) bytecode].noutputs) > max_stack_depth) + max_stack_depth = stack_depth; + } + +#ifdef VALIDATE_STACK_FOR_BC + VALIDATE_STACK_FOR_BC (); +#endif +} + + +#ifdef BCDEBUG_PRINT_CODE +#define PRLIT(TYPE, PTR) fprintf (stderr, " [%x]", *(TYPE *) PTR) +#else +#define PRLIT(X,Y) +#endif + +/* Emit a complete bytecode instruction, expecting the correct number + of literal values in the call. First argument is the instruction, the + remaining arguments are literals of size HOST_WIDE_INT or smaller. */ +void +bc_emit_instruction VPROTO((enum bytecode_opcode opcode, ...)) +{ +#ifndef __STDC__ + enum bytecode_opcode opcode; +#endif + va_list arguments; + int nliteral, instruction; + + VA_START (arguments, opcode); + +#ifndef __STDC__ + opcode = va_arg (arguments, enum bytecode_opcode); +#endif + + /* Emit instruction bytecode */ + bc_emit_bytecode (opcode); + instruction = (int) opcode; + + /* Loop literals and emit as bytecode constants */ + for (nliteral = 0; nliteral < arityvec[instruction].nliterals; nliteral++) + { + switch (arityvec[instruction].literals[nliteral]) + { +/* This conditional is a kludge, but it's necessary + because TYPE might be long long. */ +#ifdef __GNUC__ + /* Expand definitions into case statements */ +#define DEFTYPECODE(CODE, NAME, MODE, TYPE) \ + case CODE: \ + { \ + TYPE temp = va_arg (arguments, TYPE); \ + bc_emit_bytecode_const ((void *) &temp, sizeof temp); \ + PRLIT (TYPE, &temp); } \ + break; + +#include "bc-typecd.def" + +#undef DEFTYPECODE +#endif /* __GNUC__ */ + + default: + abort (); + } + } + +#ifdef BCDEBUG_PRINT_CODE + fputc ('\n', stderr); +#endif +} + +/* Emit the machine-code interface trampoline at the beginning of a byte + coded function. The argument is a label name of the interpreter + bytecode callinfo structure; the return value is a label name for + the beginning of the actual bytecode. */ +char * +bc_emit_trampoline (callinfo) + char *callinfo; +{ + char mylab[20]; + static int n; + + sprintf (mylab, "*LB%d", n++); + + BC_EMIT_TRAMPOLINE (trampoline, callinfo); + + seg_defsym (bytecode, mylab); + return sym_lookup (mylab)->name; +} + + +/* Simple strdup */ +char * +bc_xstrdup (str) + char *str; +{ + char *tmp = xmalloc (strlen (str) + 1); + + strcpy (tmp, str); + return tmp; +} |