404 lines
12 KiB
C++
404 lines
12 KiB
C++
/*
|
|
* File: procs.c
|
|
* Purpose: Functions to support Call graphs and procedures
|
|
* Date: November 1993
|
|
* (C) Cristina Cifuentes
|
|
*/
|
|
|
|
#include <cstring>
|
|
#include <cassert>
|
|
#include "dcc.h"
|
|
#include "project.h"
|
|
|
|
extern Project g_proj;
|
|
/* Static indentation buffer */
|
|
static constexpr int indSize=81; /* size of indentation buffer; max 20 */
|
|
static char indentBuf[indSize] =
|
|
" ";
|
|
// not static, used in icode.cpp at emitGotoLabel
|
|
const char *indentStr(int indLevel) // Indentation according to the depth of the statement
|
|
{
|
|
return (&indentBuf[indSize-(indLevel*4)-1]);
|
|
}
|
|
|
|
|
|
/* Inserts an outEdge at the current callGraph pointer if the newProc does
|
|
* not exist. */
|
|
void CALL_GRAPH::insertArc (ilFunction newProc)
|
|
{
|
|
CALL_GRAPH *pcg;
|
|
|
|
|
|
/* Check if procedure already exists */
|
|
auto res=std::find_if(outEdges.begin(),outEdges.end(),[newProc](CALL_GRAPH *e) {return e->proc==newProc;});
|
|
if(res!=outEdges.end())
|
|
return;
|
|
/* Include new arc */
|
|
pcg = new CALL_GRAPH;
|
|
pcg->proc = newProc;
|
|
outEdges.push_back(pcg);
|
|
}
|
|
|
|
|
|
/* Inserts a (caller, callee) arc in the call graph tree. */
|
|
bool CALL_GRAPH::insertCallGraph(ilFunction caller, ilFunction callee)
|
|
{
|
|
if (proc == caller)
|
|
{
|
|
insertArc (callee);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
for (CALL_GRAPH *edg : outEdges)
|
|
if (edg->insertCallGraph (caller, callee))
|
|
return true;
|
|
return (false);
|
|
}
|
|
}
|
|
|
|
bool CALL_GRAPH::insertCallGraph(Function *caller, ilFunction callee)
|
|
{
|
|
return insertCallGraph(Project::get()->funcIter(caller),callee);
|
|
}
|
|
|
|
|
|
/* Displays the current node of the call graph, and invokes recursively on
|
|
* the nodes the procedure invokes. */
|
|
void CALL_GRAPH::writeNodeCallGraph(int indIdx)
|
|
{
|
|
printf ("%s%s\n", indentStr(indIdx), proc->name.c_str());
|
|
for (CALL_GRAPH *cg : outEdges)
|
|
cg->writeNodeCallGraph (indIdx + 1);
|
|
}
|
|
|
|
|
|
/* Writes the header and invokes recursive procedure */
|
|
void CALL_GRAPH::write()
|
|
{
|
|
printf ("\nCall Graph:\n");
|
|
writeNodeCallGraph (0);
|
|
}
|
|
|
|
|
|
/**************************************************************************
|
|
* Routines to support arguments
|
|
*************************************************************************/
|
|
|
|
/* Updates the argument table by including the register(s) (ie. lhs of
|
|
* picode) and the actual expression (ie. rhs of picode).
|
|
* Note: register(s) are only included once in the table. */
|
|
void LOCAL_ID::newRegArg(iICODE picode, iICODE ticode) const
|
|
{
|
|
AstIdent *lhs;
|
|
STKFRAME * call_args_stackframe, *target_stackframe;
|
|
const ID *id;
|
|
int tidx;
|
|
bool regExist=false;
|
|
condId type;
|
|
Function * tproc;
|
|
eReg regL = rUNDEF;
|
|
eReg regH; /* Registers involved in arguments */
|
|
|
|
/* Flag ticode as having register arguments */
|
|
tproc = ticode->hl()->call.proc;
|
|
tproc->flg |= REG_ARGS;
|
|
|
|
/* Get registers and index into target procedure's local list */
|
|
call_args_stackframe = ticode->hl()->call.args;
|
|
target_stackframe = &tproc->args;
|
|
lhs = dynamic_cast<AstIdent *>(picode->hl()->asgn.lhs());
|
|
RegisterNode *lhs_reg = dynamic_cast<RegisterNode *>(lhs);
|
|
assert(lhs);
|
|
type = lhs->ident.type();
|
|
if (lhs_reg)
|
|
{
|
|
regL = id_arr[lhs_reg->regiIdx].id.regi;
|
|
assert(regL!=rUNDEF);
|
|
if (regL < rAL)
|
|
tidx = tproc->localId.newByteWordReg(TYPE_WORD_SIGN, regL);
|
|
else
|
|
tidx = tproc->localId.newByteWordReg(TYPE_BYTE_SIGN, regL);
|
|
/* Check if register argument already on the formal argument list */
|
|
for(STKSYM &tgt_sym : *target_stackframe)
|
|
{
|
|
RegisterNode *tgt_sym_regs = dynamic_cast<RegisterNode *>(tgt_sym.regs);
|
|
if( tgt_sym_regs == NULL ) // both REGISTER and LONG_VAR require this precondition
|
|
continue;
|
|
if ( tgt_sym_regs->regiIdx == tidx )
|
|
{
|
|
regExist = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (type == LONG_VAR)
|
|
{
|
|
int longIdx = lhs->ident.idNode.longIdx;
|
|
LONGID_TYPE regHL = id_arr[longIdx].longId();
|
|
regH=regHL.h();
|
|
regL=regHL.l();
|
|
tidx = tproc->localId.newLongReg(TYPE_LONG_SIGN, regHL, tproc->Icode.begin() /*0*/);
|
|
/* Check if register argument already on the formal argument list */
|
|
for(STKSYM &tgt_sym : *target_stackframe)
|
|
{
|
|
if( tgt_sym.regs == NULL ) // both REGISTER and LONG_VAR require this precondition
|
|
continue;
|
|
if ( tgt_sym.regs->ident.idNode.longIdx == tidx )
|
|
{
|
|
regExist = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
;//regExist = false;
|
|
/* Do ts (formal arguments) */
|
|
if (regExist == false)
|
|
{
|
|
STKSYM newsym;
|
|
|
|
newsym.setArgName(target_stackframe->size());
|
|
|
|
if (type == REGISTER)
|
|
{
|
|
if (regL < rAL)
|
|
{
|
|
newsym.type = TYPE_WORD_SIGN;
|
|
newsym.regs = new RegisterNode(tidx, WORD_REG);
|
|
}
|
|
else
|
|
{
|
|
newsym.type = TYPE_BYTE_SIGN;
|
|
newsym.regs = new RegisterNode(tidx, BYTE_REG);
|
|
}
|
|
tproc->localId.id_arr[tidx].name = newsym.name;
|
|
}
|
|
else if (type == LONG_VAR)
|
|
{
|
|
newsym.regs = AstIdent::LongIdx (tidx);
|
|
newsym.type = TYPE_LONG_SIGN;
|
|
assert(regL!=rUNDEF);
|
|
tproc->localId.id_arr[tidx].name = newsym.name;
|
|
tproc->localId.propLongId (regL, regH, tproc->localId.id_arr[tidx].name.c_str());
|
|
}
|
|
target_stackframe->push_back(newsym);
|
|
target_stackframe->numArgs++;
|
|
}
|
|
|
|
/* Do ps (actual arguments) */
|
|
STKSYM newsym;
|
|
newsym.setArgName(call_args_stackframe->size());
|
|
newsym.actual = picode->hl()->asgn.rhs;
|
|
newsym.regs = lhs;
|
|
/* Mask off high and low register(s) in picode */
|
|
switch (type) {
|
|
case REGISTER:
|
|
id = &id_arr[lhs_reg->regiIdx];
|
|
picode->du.def.clrReg(id->id.regi);
|
|
if (id->id.regi < rAL)
|
|
newsym.type = TYPE_WORD_SIGN;
|
|
else
|
|
newsym.type = TYPE_BYTE_SIGN;
|
|
break;
|
|
case LONG_VAR:
|
|
id = &id_arr[lhs->ident.idNode.longIdx];
|
|
picode->du.def.clrReg(id->longId().h());
|
|
picode->du.def.clrReg(id->longId().l());
|
|
newsym.type = TYPE_LONG_SIGN;
|
|
break;
|
|
default:
|
|
fprintf(stderr,"LOCAL_ID::newRegArg unhandled type %d in masking low\n",type);
|
|
}
|
|
call_args_stackframe->push_back(newsym);
|
|
call_args_stackframe->numArgs++;
|
|
}
|
|
|
|
|
|
/** Inserts the new expression (ie. the actual parameter) on the argument
|
|
* list.
|
|
* @return true if it was a near call that made use of a segment register.
|
|
* false elsewhere
|
|
*/
|
|
bool CallType::newStkArg(Expr *exp, llIcode opcode, Function * pproc)
|
|
{
|
|
RegisterNode *expr = dynamic_cast<RegisterNode *>(exp);
|
|
|
|
uint8_t regi;
|
|
/* Check for far procedure call, in which case, references to segment
|
|
* registers are not be considered another parameter (i.e. they are
|
|
* long references to another segment) */
|
|
if (expr)
|
|
{
|
|
regi = pproc->localId.id_arr[expr->regiIdx].id.regi;
|
|
if ((regi >= rES) && (regi <= rDS))
|
|
{
|
|
if (opcode == iCALLF)
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/* Place register argument on the argument list */
|
|
STKSYM newsym;
|
|
newsym.actual = exp;
|
|
args->push_back(newsym);
|
|
args->numArgs++;
|
|
return false;
|
|
}
|
|
|
|
|
|
/* Places the actual argument exp in the position given by pos in the
|
|
* argument list of picode. */
|
|
void CallType::placeStkArg (Expr *exp, int pos)
|
|
{
|
|
(*args)[pos].actual = exp;
|
|
(*args)[pos].setArgName(pos);
|
|
}
|
|
|
|
Expr *CallType::toAst()
|
|
{
|
|
return new FuncNode( proc, args);
|
|
}
|
|
|
|
|
|
/* Checks to determine whether the expression (actual argument) has the
|
|
* same type as the given type (from the procedure's formal list). If not,
|
|
* the actual argument gets modified */
|
|
Expr *Function::adjustActArgType (Expr *_exp, hlType forType)
|
|
{
|
|
AstIdent *expr = dynamic_cast<AstIdent *>(_exp);
|
|
PROG &prog(Project::get()->prog);
|
|
hlType actType;
|
|
int offset, offL;
|
|
|
|
if (expr == NULL)
|
|
return _exp;
|
|
|
|
actType = expr-> expType (this);
|
|
if (actType == forType)
|
|
return _exp;
|
|
switch (forType)
|
|
{
|
|
case TYPE_UNKNOWN: case TYPE_BYTE_SIGN:
|
|
case TYPE_BYTE_UNSIGN: case TYPE_WORD_SIGN:
|
|
case TYPE_WORD_UNSIGN: case TYPE_LONG_SIGN:
|
|
case TYPE_LONG_UNSIGN: case TYPE_RECORD:
|
|
break;
|
|
|
|
case TYPE_PTR:
|
|
case TYPE_CONST:
|
|
break;
|
|
|
|
case TYPE_STR:
|
|
switch (actType) {
|
|
case TYPE_CONST:
|
|
/* It's an offset into image where a string is found. Point to the string. */
|
|
{
|
|
Constant *c=dynamic_cast<Constant *>(expr);
|
|
assert(c);
|
|
offL = c->kte.kte;
|
|
if (prog.fCOM)
|
|
offset = (state.r[rDS]<<4) + offL + 0x100;
|
|
else
|
|
offset = (state.r[rDS]<<4) + offL;
|
|
expr->ident.idNode.strIdx = offset;
|
|
expr->ident.type(STRING);
|
|
delete c;
|
|
return AstIdent::String(offset);
|
|
}
|
|
|
|
case TYPE_PTR:
|
|
/* It's a pointer to a char rather than a pointer to
|
|
* an integer */
|
|
/***HERE - modify the type ****/
|
|
break;
|
|
|
|
case TYPE_WORD_SIGN:
|
|
|
|
break;
|
|
default:
|
|
fprintf(stderr,"adjustForArgType unhandled actType_ %d \n",actType);
|
|
} /* eos */
|
|
break;
|
|
default:
|
|
fprintf(stderr,"adjustForArgType unhandled forType %d \n",forType);
|
|
}
|
|
return _exp;
|
|
}
|
|
|
|
|
|
/* Determines whether the formal argument has the same type as the given
|
|
* type (type of the actual argument). If not, the formal argument is
|
|
* changed its type */
|
|
void STKFRAME::adjustForArgType(size_t numArg_, hlType actType_)
|
|
{
|
|
hlType forType;
|
|
STKSYM * psym, * nsym;
|
|
int off;
|
|
/* If formal argument does not exist, do not create new ones, just
|
|
* ignore actual argument
|
|
*/
|
|
if(numArg_>size())
|
|
return;
|
|
|
|
/* Find stack offset for this argument */
|
|
off = m_minOff;
|
|
size_t i=0;
|
|
for(STKSYM &s : *this) // walk formal arguments upto numArg_
|
|
{
|
|
if(i>=numArg_)
|
|
break;
|
|
off+=s.size;
|
|
i++;
|
|
}
|
|
|
|
/* Find formal argument */
|
|
//psym = &at(numArg_);
|
|
//i = numArg_;
|
|
//auto iter=std::find_if(sym.begin(),sym.end(),[off](STKSYM &s)->bool {s.off==off;});
|
|
auto iter=std::find_if(begin()+numArg_,end(),[off](STKSYM &s)->bool {return s.label==off;});
|
|
if(iter==end()) // symbol not found
|
|
return;
|
|
psym = &(*iter);
|
|
|
|
forType = psym->type;
|
|
if (forType != actType_)
|
|
{
|
|
switch (actType_) {
|
|
case TYPE_UNKNOWN: case TYPE_BYTE_SIGN:
|
|
case TYPE_BYTE_UNSIGN: case TYPE_WORD_SIGN:
|
|
case TYPE_WORD_UNSIGN: case TYPE_RECORD:
|
|
break;
|
|
|
|
case TYPE_LONG_UNSIGN: case TYPE_LONG_SIGN:
|
|
if ((forType == TYPE_WORD_UNSIGN) ||
|
|
(forType == TYPE_WORD_SIGN) ||
|
|
(forType == TYPE_UNKNOWN))
|
|
{
|
|
/* Merge low and high */
|
|
psym->type = actType_;
|
|
psym->size = 4;
|
|
nsym = psym + 1;
|
|
nsym->macro = "HI";
|
|
psym->macro = "LO";
|
|
nsym->hasMacro = true;
|
|
psym->hasMacro = true;
|
|
nsym->name = psym->name;
|
|
nsym->invalid = true;
|
|
numArgs--;
|
|
}
|
|
break;
|
|
|
|
case TYPE_PTR:
|
|
case TYPE_CONST:
|
|
case TYPE_STR:
|
|
break;
|
|
default:
|
|
fprintf(stderr,"STKFRAME::adjustForArgType unhandled actType_ %d \n",actType_);
|
|
} /* eos */
|
|
}
|
|
}
|
|
|