Files
ldc/backend/out.c
Alexey Prokhin caad8cde58 Squashed 'dmd2/' content from commit 10017d5
git-subtree-dir: dmd2
git-subtree-split: 10017d50eaaff4ecdc37a0153b6c37ea0b004c81
2012-04-05 11:10:48 +04:00

1535 lines
41 KiB
C
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (C) 1984-1998 by Symantec
// Copyright (C) 2000-2011 by Digital Mars
// All Rights Reserved
// http://www.digitalmars.com
// Written by Walter Bright
/*
* This source file is made available for personal use
* only. The license is in /dmd/src/dmd/backendlicense.txt
* or /dm/src/dmd/backendlicense.txt
* For any other uses, please contact Digital Mars.
*/
#if !SPP
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "cc.h"
#include "oper.h"
#include "global.h"
#include "type.h"
#include "filespec.h"
#include "code.h"
#include "cgcv.h"
#include "go.h"
#include "dt.h"
#if SCPP
#include "parser.h"
#include "cpp.h"
#include "el.h"
#endif
static char __file__[] = __FILE__; /* for tassert.h */
#include "tassert.h"
static int addrparam; /* see if any parameters get their address taken */
/**********************************
* We put out an external definition.
*/
#if SCPP
void out_extdef(symbol *s)
{
pstate.STflags |= PFLextdef;
if (//config.flags2 & CFG2phgen ||
(config.flags2 & (CFG2phauto | CFG2phautoy) &&
!(pstate.STflags & (PFLhxwrote | PFLhxdone)))
)
synerr(EM_data_in_pch,prettyident(s)); // data or code in precompiled header
}
#endif
#if TX86
#if SCPP
/********************************
* Put out code segment name record.
*/
void outcsegname(char *csegname)
{
obj_codeseg(csegname,0);
}
#endif
#endif
/***********************************
* Output function thunk.
*/
#if SCPP
void outthunk(symbol *sthunk,symbol *sfunc,unsigned p,tym_t thisty,
targ_size_t d,int i,targ_size_t d2)
{
cod3_thunk(sthunk,sfunc,p,thisty,d,i,d2);
sthunk->Sfunc->Fflags &= ~Fpending;
sthunk->Sfunc->Fflags |= Foutput; /* mark it as having been output */
}
#endif
/***************************
* Write out statically allocated data.
* Input:
* s symbol to be initialized
*/
#if TX86
void outdata(symbol *s)
{
#if HTOD
return;
#endif
dt_t *dtstart,*dt;
targ_size_t datasize,a;
int seg;
targ_size_t offset;
int flags;
tym_t ty;
symbol_debug(s);
#ifdef DEBUG
debugy && dbg_printf("outdata('%s')\n",s->Sident);
#endif
//printf("outdata('%s', ty=x%x)\n",s->Sident,s->Stype->Tty);
//symbol_print(s);
// Data segment variables are always live on exit from a function
s->Sflags |= SFLlivexit;
dtstart = s->Sdt;
s->Sdt = NULL; // it will be free'd
#if OMFOBJ
int tls = 0;
#endif
#if SCPP && TARGET_WINDOS
if (eecontext.EEcompile)
{ s->Sfl = (s->ty() & mTYfar) ? FLfardata : FLextern;
s->Sseg = UNKNOWN;
goto Lret; // don't output any data
}
#endif
datasize = 0;
ty = s->ty();
if (ty & mTYexport && config.wflags & WFexpdef && s->Sclass != SCstatic)
obj_export(s,0); // export data definition
for (dt = dtstart; dt; dt = dt->DTnext)
{
//printf("dt = %p, dt = %d\n",dt,dt->dt);
switch (dt->dt)
{ case DT_abytes:
{ // Put out the data for the string, and
// reserve a spot for a pointer to that string
datasize += size(dt->Dty); // reserve spot for pointer to string
#if ELFOBJ || MACHOBJ
dt->DTabytes += elf_data_cdata(dt->DTpbytes,dt->DTnbytes,&dt->DTseg);
#else
int stringseg;
targ_size_t foffset;
targ_size_t *poffset;
#if TARGET_SEGMENTED
if (tybasic(dt->Dty) == TYcptr)
{ stringseg = cseg;
poffset = &Coffset;
}
else if (tybasic(dt->Dty) == TYfptr &&
dt->DTnbytes > config.threshold)
{
stringseg = obj_fardata(s->Sident,dt->DTnbytes,&foffset);
poffset = &foffset;
}
else
#endif
{ stringseg = DATA;
poffset = &Doffset;
}
dt->DTseg = stringseg;
dt->DTabytes += *poffset;
obj_bytes(stringseg,*poffset,dt->DTnbytes,dt->DTpbytes);
*poffset += dt->DTnbytes;
#endif
break;
}
case DT_ibytes:
datasize += dt->DTn;
break;
case DT_nbytes:
//printf("DT_nbytes %d\n", dt->DTnbytes);
datasize += dt->DTnbytes;
break;
case DT_symsize:
#if MARS
assert(0);
#else
dt->DTazeros = type_size(s->Stype);
#endif
goto case_azeros;
case DT_azeros:
/* A block of zeros
*/
//printf("DT_azeros %d\n", dt->DTazeros);
case_azeros:
datasize += dt->DTazeros;
if (dt == dtstart && !dt->DTnext && s->Sclass != SCcomdat)
{ /* first and only, so put in BSS segment
*/
switch (ty & mTYLINK)
{
#if TARGET_SEGMENTED
case mTYfar: // if far data
s->Sseg = obj_fardata(s->Sident,datasize,&s->Soffset);
s->Sfl = FLfardata;
break;
case mTYcs:
s->Sseg = cseg;
Coffset = align(datasize,Coffset);
s->Soffset = Coffset;
Coffset += datasize;
s->Sfl = FLcsdata;
break;
#endif
case mTYthread:
{ seg_data *pseg = obj_tlsseg_bss();
s->Sseg = pseg->SDseg;
#if ELFOBJ || MACHOBJ
elf_data_start(s, datasize, pseg->SDseg);
obj_lidata(pseg->SDseg, pseg->SDoffset, datasize);
#else
targ_size_t TDoffset = pseg->SDoffset;
TDoffset = align(datasize,TDoffset);
s->Soffset = TDoffset;
TDoffset += datasize;
pseg->SDoffset = TDoffset;
tls = 1;
#endif
s->Sfl = FLtlsdata;
break;
}
default:
#if OMFOBJ
s->Sseg = UDATA;
#endif
elf_data_start(s,datasize,UDATA);
obj_lidata(s->Sseg,s->Soffset,datasize);
s->Sfl = FLudata; // uninitialized data
break;
}
#if ELFOBJ || MACHOBJ
assert(s->Sseg != UNKNOWN);
if (s->Sclass == SCglobal || s->Sclass == SCstatic) // if a pubdef to be done
objpubdefsize(s->Sseg,s,s->Soffset,datasize); // do the definition
#else
if (s->Sclass == SCglobal) /* if a pubdef to be done */
objpubdef(s->Sseg,s,s->Soffset); /* do the definition */
#endif
searchfixlist(s);
if (config.fulltypes &&
!(s->Sclass == SCstatic && funcsym_p)) // not local static
cv_outsym(s);
#if SCPP
out_extdef(s);
#endif
goto Lret;
}
break;
case DT_common:
assert(!dt->DTnext);
outcommon(s,dt->DTazeros);
goto Lret;
case DT_xoff:
{ symbol *sb = dt->DTsym;
if (tyfunc(sb->ty()))
#if SCPP
nwc_mustwrite(sb);
#else
;
#endif
else if (sb->Sdt) // if initializer for symbol
outdata(sb); // write out data for symbol
}
case DT_coff:
datasize += size(dt->Dty);
break;
case DT_1byte:
datasize++;
break;
default:
#ifdef DEBUG
dbg_printf("dt = %p, dt = %d\n",dt,dt->dt);
#endif
assert(0);
}
}
if (s->Sclass == SCcomdat) // if initialized common block
{
#if ELFOBJ
seg = obj_comdatsize(s, datasize);
#else
seg = obj_comdat(s);
#endif
#if ELFOBJ || OMFOBJ
s->Soffset = 0;
#endif
switch (ty & mTYLINK)
{
#if TARGET_SEGMENTED
case mTYfar: // if far data
s->Sfl = FLfardata;
break;
case mTYcs:
s->Sfl = FLcsdata;
break;
#endif
case mTYnear:
case 0:
s->Sfl = FLdata; // initialized data
break;
case mTYthread:
s->Sfl = FLtlsdata;
#if OMFOBJ
tls = 1;
#endif
break;
default:
assert(0);
}
}
else
{
switch (ty & mTYLINK)
{
#if TARGET_SEGMENTED
case mTYfar: // if far data
seg = obj_fardata(s->Sident,datasize,&s->Soffset);
s->Sfl = FLfardata;
break;
case mTYcs:
assert(OMFOBJ);
seg = cseg;
Coffset = align(datasize,Coffset);
s->Soffset = Coffset;
s->Sfl = FLcsdata;
break;
#endif
case mTYthread:
{ seg_data *pseg = obj_tlsseg();
#if ELFOBJ || MACHOBJ
s->Sseg = pseg->SDseg;
elf_data_start(s, datasize, s->Sseg);
// s->Soffset = pseg->SDoffset;
#else
targ_size_t TDoffset = pseg->SDoffset;
TDoffset = align(datasize,TDoffset);
s->Soffset = TDoffset;
tls = 1;
#endif
seg = pseg->SDseg;
s->Sfl = FLtlsdata;
break;
}
case mTYnear:
case 0:
#if ELFOBJ || MACHOBJ
seg = elf_data_start(s,datasize,DATA);
#else
seg = DATA;
alignOffset(DATA, datasize);
s->Soffset = Doffset;
#endif
s->Sfl = FLdata; // initialized data
break;
default:
assert(0);
}
}
#if ELFOBJ || MACHOBJ
if (s->Sseg == UNKNOWN)
s->Sseg = seg;
else
seg = s->Sseg;
if (s->Sclass == SCglobal || s->Sclass == SCstatic)
{
objpubdefsize(s->Sseg,s,s->Soffset,datasize); // do the definition
}
#else
s->Sseg = seg;
if (s->Sclass == SCglobal) /* if a pubdef to be done */
objpubdef(seg,s,s->Soffset); /* do the definition */
#endif
assert(s->Sseg != UNKNOWN);
if (config.fulltypes &&
!(s->Sclass == SCstatic && funcsym_p)) // not local static
cv_outsym(s);
searchfixlist(s);
/* Go back through list, now that we know its size, and send out */
/* the data. */
offset = s->Soffset;
for (dt = dtstart; dt; dt = dt->DTnext)
{
switch (dt->dt)
{ case DT_abytes:
if (tyreg(dt->Dty))
flags = CFoff;
else
flags = CFoff | CFseg;
if (I64)
flags |= CFoffset64;
#if TARGET_SEGMENTED
if (tybasic(dt->Dty) == TYcptr)
reftocodseg(seg,offset,dt->DTabytes);
else
#endif
#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS
reftodatseg(seg,offset,dt->DTabytes,dt->DTseg,flags);
#else
/*else*/ if (dt->DTseg == DATA)
reftodatseg(seg,offset,dt->DTabytes,DATA,flags);
else
reftofarseg(seg,offset,dt->DTabytes,dt->DTseg,flags);
#endif
offset += size(dt->Dty);
break;
case DT_ibytes:
obj_bytes(seg,offset,dt->DTn,dt->DTdata);
offset += dt->DTn;
break;
case DT_nbytes:
obj_bytes(seg,offset,dt->DTnbytes,dt->DTpbytes);
offset += dt->DTnbytes;
break;
case DT_azeros:
//printf("obj_lidata(seg = %d, offset = %d, azeros = %d)\n", seg, offset, dt->DTazeros);
if (0 && seg == cseg)
{
obj_lidata(seg,offset,dt->DTazeros);
offset += dt->DTazeros;
}
else
{
SegData[seg]->SDoffset = offset;
obj_lidata(seg,offset,dt->DTazeros);
offset = SegData[seg]->SDoffset;
}
break;
case DT_xoff:
{
symbol *sb = dt->DTsym; // get external symbol pointer
a = dt->DToffset; // offset from it
if (tyreg(dt->Dty))
flags = CFoff;
else
flags = CFoff | CFseg;
if (I64)
flags |= CFoffset64;
offset += reftoident(seg,offset,sb,a,flags);
break;
}
case DT_coff:
reftocodseg(seg,offset,dt->DToffset);
offset += intsize;
break;
case DT_1byte:
obj_byte(seg,offset++,dt->DTonebyte);
break;
default:
#ifdef DEBUG
dbg_printf("dt = %p, dt = %d\n",dt,dt->dt);
#endif
assert(0);
}
}
Offset(seg) = offset;
#if SCPP
out_extdef(s);
#endif
Lret:
dt_free(dtstart);
}
/******************************
* Output n bytes of a common block, n > 0.
*/
void outcommon(symbol *s,targ_size_t n)
{
//printf("outcommon('%s',%d)\n",s->Sident,n);
if (n != 0)
{
assert(s->Sclass == SCglobal);
#if TARGET_SEGMENTED
if (s->ty() & mTYcs) // if store in code segment
{
/* COMDEFs not supported in code segment
* so put them out as initialized 0s
*/
dtnzeros(&s->Sdt,n);
outdata(s);
#if SCPP
out_extdef(s);
#endif
}
else
#endif
if (s->ty() & mTYthread) // if store in thread local segment
{
#if ELFOBJ
s->Sclass = SCcomdef;
obj_comdef(s, 0, n, 1);
#else
/* COMDEFs not supported in tls segment
* so put them out as COMDATs with initialized 0s
*/
s->Sclass = SCcomdat;
dtnzeros(&s->Sdt,n);
outdata(s);
#if SCPP && OMFOBJ
out_extdef(s);
#endif
#endif
}
else
{
#if ELFOBJ || MACHOBJ
s->Sclass = SCcomdef;
obj_comdef(s, 0, n, 1);
#else
s->Sclass = SCcomdef;
#if TARGET_SEGMENTED
s->Sxtrnnum = obj_comdef(s,(s->ty() & mTYfar) == 0,n,1);
#else
s->Sxtrnnum = obj_comdef(s,true,n,1);
#endif
s->Sseg = UNKNOWN;
#if TARGET_SEGMENTED
if (s->ty() & mTYfar)
s->Sfl = FLfardata;
else
#endif
s->Sfl = FLextern;
pstate.STflags |= PFLcomdef;
#if SCPP
ph_comdef(s); // notify PH that a COMDEF went out
#endif
#endif
}
if (config.fulltypes)
cv_outsym(s);
}
}
#endif // TX86
/******************************
* Walk expression tree, converting it from a PARSER tree to
* a code generator tree.
*/
STATIC void outelem(elem *e)
{
symbol *s;
tym_t tym;
elem *e1;
#if SCPP
type *t;
#endif
again:
assert(e);
elem_debug(e);
#ifdef DEBUG
if (EBIN(e))
assert(e->E1 && e->E2);
// else if (EUNA(e))
// assert(e->E1 && !e->E2);
#endif
#if SCPP
t = e->ET;
assert(t);
type_debug(t);
tym = t->Tty;
switch (tybasic(tym))
{ case TYstruct:
t->Tcount++;
break;
case TYarray:
t->Tcount++;
break;
case TYbool:
case TYwchar_t:
case TYchar16:
case TYmemptr:
case TYvtshape:
case TYnullptr:
tym = tym_conv(t);
e->ET = NULL;
break;
case TYenum:
tym = tym_conv(t->Tnext);
e->ET = NULL;
break;
default:
e->ET = NULL;
break;
}
e->Nflags = 0;
e->Ety = tym;
#endif
switch (e->Eoper)
{
default:
Lop:
#if DEBUG
//if (!EOP(e)) dbg_printf("e->Eoper = x%x\n",e->Eoper);
#endif
if (EBIN(e))
{ outelem(e->E1);
e = e->E2;
}
else if (EUNA(e))
{
e = e->E1;
}
else
break;
#if SCPP
type_free(t);
#endif
goto again; /* iterate instead of recurse */
case OPaddr:
e1 = e->E1;
if (e1->Eoper == OPvar)
{ // Fold into an OPrelconst
#if SCPP
el_copy(e,e1);
e->ET = t;
#else
tym = e->Ety;
el_copy(e,e1);
e->Ety = tym;
#endif
e->Eoper = OPrelconst;
el_free(e1);
goto again;
}
goto Lop;
case OPrelconst:
case OPvar:
L6:
s = e->EV.sp.Vsym;
assert(s);
symbol_debug(s);
switch (s->Sclass)
{
case SCregpar:
case SCparameter:
if (e->Eoper == OPrelconst)
addrparam = TRUE; // taking addr of param list
break;
case SCstatic:
case SClocstat:
case SCextern:
case SCglobal:
case SCcomdat:
case SCcomdef:
#if PSEUDO_REGS
case SCpseudo:
#endif
case SCinline:
case SCsinline:
case SCeinline:
s->Sflags |= SFLlivexit;
/* FALL-THROUGH */
case SCauto:
case SCregister:
case SCfastpar:
case SCbprel:
case SCtmp:
if (e->Eoper == OPrelconst)
{
s->Sflags &= ~(SFLunambig | GTregcand);
}
#if SCPP && TX86 && OMFOBJ
else if (s->ty() & mTYfar)
e->Ety |= mTYfar;
#endif
break;
#if SCPP
case SCmember:
err_noinstance(s->Sscope,s);
goto L5;
case SCstruct:
cpperr(EM_no_instance,s->Sident); // no instance of class
L5:
e->Eoper = OPconst;
e->Ety = TYint;
return;
case SCfuncalias:
e->EV.sp.Vsym = s->Sfunc->Falias;
goto L6;
case SCstack:
break;
case SCfunctempl:
cpperr(EM_no_template_instance, s->Sident);
break;
default:
#ifdef DEBUG
symbol_print(s);
WRclass((enum SC) s->Sclass);
#endif
assert(0);
#endif
}
#if SCPP
if (tyfunc(s->ty()))
{
#if SCPP
nwc_mustwrite(s); /* must write out function */
#else
;
#endif
}
else if (s->Sdt) /* if initializer for symbol */
outdata(s); // write out data for symbol
#if ELFOBJ || MACHOBJ
if (config.flags3 & CFG3pic)
{
elfobj_gotref(s);
}
#endif
#endif
break;
case OPstring:
case OPconst:
case OPstrthis:
break;
case OPsizeof:
#if SCPP
e->Eoper = OPconst;
e->EV.Vlong = type_size(e->EV.sp.Vsym->Stype);
#else
assert(0);
#endif
break;
#if SCPP
case OPstreq:
case OPstrpar:
case OPstrctor:
type_size(e->E1->ET);
goto Lop;
case OPasm:
break;
case OPctor:
nwc_mustwrite(e->EV.eop.Edtor);
case OPdtor:
// Don't put 'this' pointers in registers if we need
// them for EH stack cleanup.
e1 = e->E1;
elem_debug(e1);
if (e1->Eoper == OPadd)
e1 = e1->E1;
if (e1->Eoper == OPvar)
e1->EV.sp.Vsym->Sflags &= ~GTregcand;
goto Lop;
case OPmark:
break;
#endif
}
#if SCPP
type_free(t);
#endif
}
/*************************************
* Determine register candidates.
*/
STATIC void out_regcand_walk(elem *e);
void out_regcand(symtab_t *psymtab)
{
block *b;
SYMIDX si;
int ifunc;
//printf("out_regcand()\n");
ifunc = (tybasic(funcsym_p->ty()) == TYifunc);
for (si = 0; si < psymtab->top; si++)
{ symbol *s = psymtab->tab[si];
symbol_debug(s);
//assert(sytab[s->Sclass] & SCSS); // only stack variables
s->Ssymnum = si; // Ssymnum trashed by cpp_inlineexpand
if (!(s->ty() & mTYvolatile) &&
#if TX86
!(ifunc && (s->Sclass == SCparameter || s->Sclass == SCregpar)) &&
#endif
s->Sclass != SCstatic)
s->Sflags |= (GTregcand | SFLunambig); // assume register candidate
else
s->Sflags &= ~(GTregcand | SFLunambig);
}
addrparam = FALSE; // haven't taken addr of param yet
for (b = startblock; b; b = b->Bnext)
{
if (b->Belem)
out_regcand_walk(b->Belem);
// Any assembler blocks make everything ambiguous
if (b->BC == BCasm)
for (si = 0; si < psymtab->top; si++)
psymtab->tab[si]->Sflags &= ~(SFLunambig | GTregcand);
}
// If we took the address of one parameter, assume we took the
// address of all non-register parameters.
if (addrparam) // if took address of a parameter
{
for (si = 0; si < psymtab->top; si++)
if (psymtab->tab[si]->Sclass == SCparameter)
psymtab->tab[si]->Sflags &= ~(SFLunambig | GTregcand);
}
}
STATIC void out_regcand_walk(elem *e)
{ symbol *s;
while (1)
{ elem_debug(e);
if (EBIN(e))
{ if (e->Eoper == OPstreq)
{ if (e->E1->Eoper == OPvar)
{ s = e->E1->EV.sp.Vsym;
s->Sflags &= ~(SFLunambig | GTregcand);
}
if (e->E2->Eoper == OPvar)
{ s = e->E2->EV.sp.Vsym;
s->Sflags &= ~(SFLunambig | GTregcand);
}
}
out_regcand_walk(e->E1);
e = e->E2;
}
else if (EUNA(e))
{
// Don't put 'this' pointers in registers if we need
// them for EH stack cleanup.
if (e->Eoper == OPctor)
{ elem *e1 = e->E1;
if (e1->Eoper == OPadd)
e1 = e1->E1;
if (e1->Eoper == OPvar)
e1->EV.sp.Vsym->Sflags &= ~GTregcand;
}
e = e->E1;
}
else
{ if (e->Eoper == OPrelconst)
{
s = e->EV.sp.Vsym;
assert(s);
symbol_debug(s);
switch (s->Sclass)
{
case SCregpar:
case SCparameter:
addrparam = TRUE; // taking addr of param list
break;
case SCauto:
case SCregister:
case SCtmp:
case SCfastpar:
case SCbprel:
s->Sflags &= ~(SFLunambig | GTregcand);
break;
}
}
else if (e->Eoper == OPvar)
{
if (e->EV.sp.Voffset)
{ if (!(e->EV.sp.Voffset == 1 && tybyte(e->Ety)))
e->EV.sp.Vsym->Sflags &= ~GTregcand;
}
}
break;
}
}
}
/**************************
* Optimize function,
* generate code for it,
* and write it out.
*/
STATIC void writefunc2(symbol *sfunc);
void writefunc(symbol *sfunc)
{
#if HTOD
return;
#elif SCPP
writefunc2(sfunc);
#else
cstate.CSpsymtab = &globsym;
writefunc2(sfunc);
cstate.CSpsymtab = NULL;
#endif
}
STATIC void writefunc2(symbol *sfunc)
{
block *b;
unsigned nsymbols;
SYMIDX si;
int anyasm;
#if OMFOBJ
int csegsave;
targ_size_t coffsetsave;
#endif
func_t *f = sfunc->Sfunc;
tym_t tyf;
//printf("writefunc(%s)\n",sfunc->Sident);
debug(debugy && dbg_printf("writefunc(%s)\n",sfunc->Sident));
#if SCPP
if (CPP)
{
// If constructor or destructor, make sure it has been fixed.
if (f->Fflags & (Fctor | Fdtor))
assert(errcnt || f->Fflags & Ffixed);
// If this function is the 'trigger' to output the vtbl[], do so
if (f->Fflags3 & Fvtblgen && !eecontext.EEcompile)
{ Classsym *stag;
stag = (Classsym *) sfunc->Sscope;
{
enum SC scvtbl;
scvtbl = (enum SC) ((config.flags2 & CFG2comdat) ? SCcomdat : SCglobal);
n2_genvtbl(stag,scvtbl,1);
#if VBTABLES
n2_genvbtbl(stag,scvtbl,1);
#endif
#if TX86 && OMFOBJ
if (config.fulltypes == CV4)
cv4_struct(stag,2);
#endif
}
}
}
#endif
/* Signify that function has been output */
/* (before inline_do() to prevent infinite recursion!) */
f->Fflags &= ~Fpending;
f->Fflags |= Foutput;
if (
#if SCPP
errcnt ||
#endif
(eecontext.EEcompile && eecontext.EEfunc != sfunc))
return;
/* Copy local symbol table onto main one, making sure */
/* that the symbol numbers are adjusted accordingly */
//dbg_printf("f->Flocsym.top = %d\n",f->Flocsym.top);
nsymbols = f->Flocsym.top;
if (nsymbols > globsym.symmax)
{ /* Reallocate globsym.tab[] */
globsym.symmax = nsymbols;
globsym.tab = symtab_realloc(globsym.tab, globsym.symmax);
}
debug(debugy && dbg_printf("appending symbols to symtab...\n"));
assert(globsym.top == 0);
memcpy(&globsym.tab[0],&f->Flocsym.tab[0],nsymbols * sizeof(symbol *));
globsym.top = nsymbols;
assert(startblock == NULL);
if (f->Fflags & Finline) // if keep function around
{ // Generate copy of function
block *bf;
block **pb;
pb = &startblock;
for (bf = f->Fstartblock; bf; bf = bf->Bnext)
{
b = block_calloc();
*pb = b;
pb = &b->Bnext;
*b = *bf;
assert(!b->Bsucc);
assert(!b->Bpred);
b->Belem = el_copytree(b->Belem);
}
}
else
{ startblock = sfunc->Sfunc->Fstartblock;
sfunc->Sfunc->Fstartblock = NULL;
}
assert(startblock);
/* Do any in-line expansion of function calls inside sfunc */
#if SCPP
inline_do(sfunc);
#endif
#if SCPP
/* If function is _STIxxxx, add in the auto destructors */
#if NEWSTATICDTOR
if (cpp_stidtors && memcmp("__SI",sfunc->Sident,4) == 0)
#else
if (cpp_stidtors && memcmp("_STI",sfunc->Sident,4) == 0)
#endif
{ list_t el;
assert(startblock->Bnext == NULL);
el = cpp_stidtors;
do
{
startblock->Belem = el_combine(startblock->Belem,list_elem(el));
el = list_next(el);
} while (el);
list_free(&cpp_stidtors,FPNULL);
}
#endif
assert(funcsym_p == NULL);
funcsym_p = sfunc;
tyf = tybasic(sfunc->ty());
#if SCPP
out_extdef(sfunc);
#endif
// TX86 computes parameter offsets in stackoffsets()
//printf("globsym.top = %d\n", globsym.top);
for (si = 0; si < globsym.top; si++)
{ symbol *s = globsym.tab[si];
symbol_debug(s);
//printf("symbol %d '%s'\n",si,s->Sident);
type_size(s->Stype); // do any forward template instantiations
s->Ssymnum = si; // Ssymnum trashed by cpp_inlineexpand
s->Sflags &= ~(SFLunambig | GTregcand);
switch (s->Sclass)
{
#if SCPP
case SCfastpar:
Lfp:
s->Spreg = (tyf == TYmfunc) ? CX : AX;
case SCauto:
case SCregister:
s->Sfl = FLauto;
goto L3;
case SCtmp:
s->Sfl = FLtmp;
goto L3;
case SCbprel:
s->Sfl = FLbprel;
goto L3;
case SCregpar:
case SCparameter:
if (tyf == TYjfunc && si == 0 &&
type_jparam(s->Stype))
{ s->Sclass = SCfastpar; // put last parameter into register
goto Lfp;
}
#else
case SCfastpar:
case SCauto:
case SCregister:
s->Sfl = FLauto;
goto L3;
case SCtmp:
s->Sfl = FLtmp;
goto L3;
case SCbprel:
s->Sfl = FLbprel;
goto L3;
case SCregpar:
case SCparameter:
#endif
s->Sfl = FLpara;
if (tyf == TYifunc)
{ s->Sflags |= SFLlivexit;
break;
}
L3:
if (!(s->ty() & mTYvolatile))
s->Sflags |= GTregcand | SFLunambig; // assume register candidate */
break;
#if PSEUDO_REGS
case SCpseudo:
s->Sfl = FLpseudo;
break;
#endif
case SCstatic:
break; // already taken care of by datadef()
case SCstack:
s->Sfl = FLstack;
break;
default:
#ifdef DEBUG
symbol_print(s);
#endif
assert(0);
}
}
addrparam = FALSE; // haven't taken addr of param yet
anyasm = 0;
numblks = 0;
for (b = startblock; b; b = b->Bnext)
{
numblks++; // redo count
memset(&b->_BLU,0,sizeof(b->_BLU));
if (b->Belem)
{ outelem(b->Belem);
#if SCPP
if (el_noreturn(b->Belem) && !(config.flags3 & CFG3eh))
{ b->BC = BCexit;
list_free(&b->Bsucc,FPNULL);
}
#endif
#if MARS
if (b->Belem->Eoper == OPhalt)
{ b->BC = BCexit;
list_free(&b->Bsucc,FPNULL);
}
#endif
}
if (b->BC == BCasm)
anyasm = 1;
if (sfunc->Sflags & SFLexit && (b->BC == BCret || b->BC == BCretexp))
{ b->BC = BCexit;
list_free(&b->Bsucc,FPNULL);
}
assert(b != b->Bnext);
}
PARSER = 0;
if (eecontext.EEelem)
{ unsigned marksi = globsym.top;
eecontext.EEin++;
outelem(eecontext.EEelem);
eecontext.EEelem = doptelem(eecontext.EEelem,TRUE);
eecontext.EEin--;
eecontext_convs(marksi);
}
maxblks = 3 * numblks; // allow for increase in # of blocks
// If we took the address of one parameter, assume we took the
// address of all non-register parameters.
if (addrparam | anyasm) // if took address of a parameter
{
for (si = 0; si < globsym.top; si++)
if (anyasm || globsym.tab[si]->Sclass == SCparameter)
globsym.tab[si]->Sflags &= ~(SFLunambig | GTregcand);
}
block_pred(); // compute predecessors to blocks
block_compbcount(); // eliminate unreachable blocks
if (mfoptim)
{ OPTIMIZER = 1;
optfunc(); /* optimize function */
assert(dfo);
OPTIMIZER = 0;
}
else
{
//dbg_printf("blockopt()\n");
blockopt(0); /* optimize */
}
#if SCPP
if (CPP)
{
// Look for any blocks that return nothing.
// Do it after optimization to eliminate any spurious
// messages like the implicit return on { while(1) { ... } }
if (tybasic(funcsym_p->Stype->Tnext->Tty) != TYvoid &&
!(funcsym_p->Sfunc->Fflags & (Fctor | Fdtor | Finvariant))
#if DEBUG_XSYMGEN
/* the internal dataview function is allowed to lie about its return value */
&& compile_state != kDataView
#endif
)
{ char err;
err = 0;
for (b = startblock; b; b = b->Bnext)
{ if (b->BC == BCasm) // no errors if any asm blocks
err |= 2;
else if (b->BC == BCret)
err |= 1;
}
if (err == 1)
func_noreturnvalue();
}
}
#endif
assert(funcsym_p == sfunc);
if (eecontext.EEcompile != 1)
{
#if TX86
if (symbol_iscomdat(sfunc))
{
#if OMFOBJ
csegsave = cseg;
coffsetsave = Coffset;
#endif
obj_comdat(sfunc);
}
else
if (config.flags & CFGsegs) // if user set switch for this
{
#if SCPP || TARGET_WINDOS
obj_codeseg(cpp_mangle(funcsym_p),1);
#else
obj_codeseg(funcsym_p->Sident, 1);
#endif
// generate new code segment
}
cod3_align(); // align start of function
#if ELFOBJ || MACHOBJ
elf_func_start(sfunc);
#else
sfunc->Sseg = cseg; // current code seg
#endif
#endif
sfunc->Soffset = Coffset; // offset of start of function
searchfixlist(sfunc); // backpatch any refs to this function
}
//dbg_printf("codgen()\n");
#if SCPP
if (!errcnt)
#endif
codgen(); // generate code
//dbg_printf("after codgen for %s Coffset %x\n",sfunc->Sident,Coffset);
blocklist_free(&startblock);
#if SCPP
PARSER = 1;
#endif
#if ELFOBJ || MACHOBJ
elf_func_term(sfunc);
#endif
#if MARS
/* This is to make uplevel references to SCfastpar variables
* from nested functions work.
*/
for (si = 0; si < globsym.top; si++)
{
Symbol *s = globsym.tab[si];
switch (s->Sclass)
{ case SCfastpar:
s->Sclass = SCauto;
break;
}
}
#endif
if (eecontext.EEcompile == 1)
goto Ldone;
if (sfunc->Sclass == SCglobal)
{
#if OMFOBJ
if (!(config.flags4 & CFG4allcomdat))
objpubdef(cseg,sfunc,sfunc->Soffset); // make a public definition
#endif
#if SCPP && _WIN32
char *id;
// Determine which startup code to reference
if (!CPP || !isclassmember(sfunc)) // if not member function
{ static char *startup[] =
{ "__acrtused","__acrtused_winc","__acrtused_dll",
"__acrtused_con","__wacrtused","__wacrtused_con",
};
int i;
id = sfunc->Sident;
switch (id[0])
{
case 'D': if (strcmp(id,"DllMain"))
break;
if (config.exe == EX_NT)
{ i = 2;
goto L2;
}
break;
case 'm': if (strcmp(id,"main"))
break;
if (config.exe == EX_NT)
i = 3;
else if (config.wflags & WFwindows)
i = 1;
else
i = 0;
goto L2;
case 'w': if (strcmp(id,"wmain") == 0)
{
if (config.exe == EX_NT)
{ i = 5;
goto L2;
}
break;
}
case 'W': if (stricmp(id,"WinMain") == 0)
{
i = 0;
goto L2;
}
if (stricmp(id,"wWinMain") == 0)
{
if (config.exe == EX_NT)
{ i = 4;
goto L2;
}
}
break;
case 'L':
case 'l': if (stricmp(id,"LibMain"))
break;
if (config.exe != EX_NT && config.wflags & WFwindows)
{ i = 2;
goto L2;
}
break;
L2: objextdef(startup[i]); // pull in startup code
break;
}
}
#endif
}
if (config.wflags & WFexpdef &&
sfunc->Sclass != SCstatic &&
sfunc->Sclass != SCsinline &&
!(sfunc->Sclass == SCinline && !(config.flags2 & CFG2comdat)) &&
sfunc->ty() & mTYexport)
obj_export(sfunc,Poffset); // export function definition
if (config.fulltypes)
cv_func(sfunc); // debug info for function
#if MARS
/* After codgen() and writing debug info for the locals,
* readjust the offsets of all stack variables so they
* are relative to the frame pointer.
* Necessary for nested function access to lexically enclosing frames.
*/
cod3_adjSymOffsets();
#endif
#if OMFOBJ
if (symbol_iscomdat(sfunc)) // if generated a COMDAT
obj_setcodeseg(csegsave,coffsetsave); // reset to real code seg
#endif
/* Check if function is a constructor or destructor, by */
/* seeing if the function name starts with _STI or _STD */
{
#if _M_I86
short *p;
p = (short *) sfunc->Sident;
if (p[0] == 'S_' && (p[1] == 'IT' || p[1] == 'DT'))
#else
char *p;
p = sfunc->Sident;
if (p[0] == '_' && p[1] == 'S' && p[2] == 'T' &&
(p[3] == 'I' || p[3] == 'D'))
#endif
obj_funcptr(sfunc);
}
Ldone:
funcsym_p = NULL;
#if SCPP
// Free any added symbols
freesymtab(globsym.tab,nsymbols,globsym.top);
#endif
globsym.top = 0;
//dbg_printf("done with writefunc()\n");
#if TX86
util_free(dfo);
#else
MEM_PARF_FREE(dfo);
#endif
dfo = NULL;
}
/*************************
* Align segment offset.
* Input:
* seg segment to be aligned
* datasize size in bytes of object to be aligned
*/
void alignOffset(int seg,targ_size_t datasize)
{
targ_size_t alignbytes;
alignbytes = align(datasize,Offset(seg)) - Offset(seg);
//dbg_printf("seg %d datasize = x%x, Offset(seg) = x%x, alignbytes = x%x\n",
//seg,datasize,Offset(seg),alignbytes);
if (alignbytes)
obj_lidata(seg,Offset(seg),alignbytes);
#if OMFOBJ
//Offset(seg) += alignbytes; /* offset of start of data */
#endif
}
/***************************************
* Write data into read-only data segment.
* Return symbol for it.
*/
#define ROMAX 32
struct Readonly
{
symbol *sym;
size_t length;
unsigned char p[ROMAX];
};
#define RMAX 16
static Readonly readonly[RMAX];
static size_t readonly_length;
static size_t readonly_i;
void out_reset()
{
readonly_length = 0;
readonly_i = 0;
}
symbol *out_readonly_sym(tym_t ty, void *p, int len)
{
#if 0
printf("out_readonly_sym(ty = x%x)\n", ty);
for (int i = 0; i < len; i++)
printf(" [%d] = %02x\n", i, ((unsigned char*)p)[i]);
#endif
// Look for previous symbol we can reuse
for (int i = 0; i < readonly_length; i++)
{
Readonly *r = &readonly[i];
if (r->length == len && memcmp(p, r->p, len) == 0)
return r->sym;
}
symbol *s;
#if ELFOBJ
/* MACHOBJ can't go here, because the const data segment goes into
* the _TEXT segment, and one cannot have a fixup from _TEXT to _TEXT.
*/
s = elf_sym_cdata(ty, (char *)p, len);
#else
unsigned sz = tysize(ty);
alignOffset(DATA, sz);
s = symboldata(Doffset,ty | mTYconst);
obj_write_bytes(SegData[DATA], len, p);
//printf("s->Sseg = %d:x%x\n", s->Sseg, s->Soffset);
#endif
if (len <= ROMAX)
{ Readonly *r;
if (readonly_length < RMAX)
{
r = &readonly[readonly_length];
readonly_length++;
}
else
{ r = &readonly[readonly_i];
readonly_i++;
if (readonly_i >= RMAX)
readonly_i = 0;
}
r->length = len;
r->sym = s;
memcpy(r->p, p, len);
}
return s;
}
void Srcpos::print(const char *func)
{
printf("%s(", func);
#if MARS
printf("Sfilename = %s", Sfilename ? Sfilename : "null");
#else
Sfile *sf = Sfilptr ? *Sfilptr : NULL;
printf("Sfilptr = %p (filename = %s)", sf, sf ? sf->SFname : "null");
#endif
printf(", Slinnum = %u", Slinnum);
#if SOURCE_OFFSETS
printf(", Sfiloff = %d", Sfiloff);
#endif
printf(")\n");
}
#endif /* !SPP */