a4cd5630b0
non-i386, non-unix, and generatable files have been trimmed, but can easily be added in later if needed. gcc-2.7.2.1 will follow shortly, it's a very small delta to this and it's handy to have both available for reference for such little cost. The freebsd-specific changes will then be committed, and once the dust has settled, the bmakefiles will be committed to use this code.
993 lines
21 KiB
C
993 lines
21 KiB
C
/* 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, 59 Temple Place - Suite 330,
|
||
Boston, MA 02111-1307, 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, ", %lu\n", s->val);
|
||
}
|
||
}
|
||
else if (s->common)
|
||
{
|
||
fprintf (file, "\n\t.lcomm ");
|
||
prsym (file, s->name);
|
||
fprintf (file, ", %lu\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 auxiliary 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 = (char) 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;
|
||
}
|