mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
2869 lines
84 KiB
C++
2869 lines
84 KiB
C++
/* ###
|
|
* IP: GHIDRA
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include "slgh_compile.hh"
|
|
#include "filemanage.hh"
|
|
#include <csignal>
|
|
|
|
SleighCompile *slgh; // Global pointer to sleigh object for use with parser
|
|
#ifdef YYDEBUG
|
|
extern int yydebug; // Global debugging state for parser
|
|
#endif
|
|
extern FILE *yyin; // Global pointer to file for lexer
|
|
extern int yyparse(void);
|
|
extern int yylex_destroy(void);
|
|
|
|
static VarnodeTpl *find_size(const ConstTpl &offset,const ConstructTpl *ct)
|
|
|
|
{ // Find a defining instance of the local variable
|
|
// with given -offset-
|
|
const vector<OpTpl *> &ops(ct->getOpvec());
|
|
VarnodeTpl *vn;
|
|
OpTpl *op;
|
|
|
|
for(int4 i=0;i<ops.size();++i) {
|
|
op = ops[i];
|
|
vn = op->getOut();
|
|
if ((vn!=(VarnodeTpl *)0)&&(vn->isLocalTemp())) {
|
|
if (vn->getOffset() == offset)
|
|
return vn;
|
|
}
|
|
for(int4 j=0;j<op->numInput();++j) {
|
|
vn = op->getIn(j);
|
|
if (vn->isLocalTemp()&&(vn->getOffset()==offset))
|
|
return vn;
|
|
}
|
|
}
|
|
return (VarnodeTpl *)0;
|
|
}
|
|
|
|
static bool force_exportsize(ConstructTpl *ct)
|
|
|
|
{ // Look for zero size temps in export statement
|
|
HandleTpl *result = ct->getResult();
|
|
if (result == (HandleTpl *)0) return true;
|
|
|
|
VarnodeTpl *vt;
|
|
|
|
if (result->getPtrSpace().isUniqueSpace()&&result->getPtrSize().isZero()) {
|
|
vt = find_size(result->getPtrOffset(),ct);
|
|
if (vt == (VarnodeTpl *)0) return false;
|
|
result->setPtrSize(vt->getSize());
|
|
}
|
|
else if (result->getSpace().isUniqueSpace()&&result->getSize().isZero()) {
|
|
vt = find_size(result->getPtrOffset(),ct);
|
|
if (vt == (VarnodeTpl *)0) return false;
|
|
result->setSize(vt->getSize());
|
|
}
|
|
return true;
|
|
}
|
|
|
|
SectionVector::SectionVector(ConstructTpl *rtl,SymbolScope *scope)
|
|
|
|
{
|
|
nextindex = -1;
|
|
main.section = rtl;
|
|
main.scope = scope;
|
|
}
|
|
|
|
void SectionVector::append(ConstructTpl *rtl,SymbolScope *scope)
|
|
|
|
{
|
|
while(named.size() <= nextindex)
|
|
named.push_back(RtlPair());
|
|
named[ nextindex ] = RtlPair(rtl,scope);
|
|
}
|
|
|
|
SpaceQuality::SpaceQuality(const string &nm)
|
|
|
|
{ // Default space qualities
|
|
name = nm;
|
|
type = ramtype;
|
|
size = 0;
|
|
wordsize = 1;
|
|
isdefault = false;
|
|
}
|
|
|
|
FieldQuality::FieldQuality(string *nm,uintb *l,uintb *h)
|
|
|
|
{
|
|
name = *nm;
|
|
low = *l;
|
|
high = *h;
|
|
signext = false;
|
|
flow = true;
|
|
hex = true;
|
|
delete nm;
|
|
delete l;
|
|
delete h;
|
|
}
|
|
|
|
void WithBlock::set(SubtableSymbol *s, PatternEquation *pq, vector<ContextChange *> *cvec)
|
|
|
|
{
|
|
ss = s;
|
|
pateq = pq;
|
|
if (pateq != (PatternEquation *)0)
|
|
pateq->layClaim();
|
|
if (cvec != (vector<ContextChange *> *)0) {
|
|
for(int4 i=0;i<cvec->size();++i)
|
|
contvec.push_back((*cvec)[i]); // Lay claim to -cvec-s pointers, we don't clone
|
|
delete cvec;
|
|
}
|
|
}
|
|
|
|
WithBlock::~WithBlock(void)
|
|
|
|
{
|
|
if (pateq != (PatternEquation *)0)
|
|
PatternEquation::release(pateq);
|
|
for(int4 i=0;i<contvec.size();++i) {
|
|
delete contvec[i];
|
|
}
|
|
}
|
|
|
|
PatternEquation *WithBlock::collectAndPrependPattern(const list<WithBlock> &stack, PatternEquation *pateq)
|
|
|
|
{
|
|
list<WithBlock>::const_iterator iter;
|
|
for(iter=stack.begin();iter!=stack.end();++iter) {
|
|
PatternEquation *witheq = (*iter).pateq;
|
|
if (witheq != (PatternEquation *)0)
|
|
pateq = new EquationAnd(witheq, pateq);
|
|
}
|
|
return pateq;
|
|
}
|
|
|
|
vector<ContextChange *> *WithBlock::collectAndPrependContext(const list<WithBlock> &stack, vector<ContextChange *> *contvec)
|
|
|
|
{ // Make new list of ContextChanges, prepending everything from stack to -contvec-, delete old contvec
|
|
vector<ContextChange *> *res = (vector<ContextChange *> *)0;
|
|
list<WithBlock>::const_iterator iter;
|
|
for(iter=stack.begin();iter!=stack.end();++iter) {
|
|
const vector<ContextChange *> &changelist( (*iter).contvec );
|
|
if (changelist.size() == 0) continue;
|
|
if (res == (vector<ContextChange *> *)0)
|
|
res = new vector<ContextChange *>();
|
|
for(int4 i=0;i<changelist.size();++i) {
|
|
res->push_back(changelist[i]->clone());
|
|
}
|
|
}
|
|
if (contvec != (vector<ContextChange *> *)0) {
|
|
if (contvec->size() != 0) {
|
|
if (res == (vector<ContextChange *> *)0)
|
|
res = new vector<ContextChange *>();
|
|
for(int4 i=0;i<contvec->size();++i)
|
|
res->push_back((*contvec)[i]); // lay claim to contvecs pointer
|
|
}
|
|
delete contvec;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
SubtableSymbol *WithBlock::getCurrentSubtable(const list<WithBlock> &stack)
|
|
|
|
{
|
|
list<WithBlock>::const_iterator iter;
|
|
for(iter=stack.begin();iter!=stack.end();++iter) {
|
|
if ((*iter).ss != (SubtableSymbol *)0)
|
|
return (*iter).ss;
|
|
}
|
|
return (SubtableSymbol *)0;
|
|
}
|
|
|
|
ConsistencyChecker::ConsistencyChecker(SubtableSymbol *rt,bool un,bool warndead)
|
|
|
|
{
|
|
root_symbol = rt;
|
|
unnecessarypcode = 0;
|
|
readnowrite = 0;
|
|
writenoread = 0;
|
|
printextwarning = un;
|
|
printdeadwarning = warndead;
|
|
}
|
|
|
|
int4 ConsistencyChecker::recoverSize(const ConstTpl &sizeconst,Constructor *ct)
|
|
|
|
{
|
|
int4 size,handindex;
|
|
OperandSymbol *opsym;
|
|
SubtableSymbol *tabsym;
|
|
map<SubtableSymbol *,int4>::const_iterator iter;
|
|
|
|
switch(sizeconst.getType()) {
|
|
case ConstTpl::real:
|
|
size = (int4) sizeconst.getReal();
|
|
break;
|
|
case ConstTpl::handle:
|
|
handindex = sizeconst.getHandleIndex();
|
|
opsym = ct->getOperand(handindex);
|
|
size = opsym->getSize();
|
|
if (size == -1) {
|
|
tabsym = dynamic_cast<SubtableSymbol *>(opsym->getDefiningSymbol());
|
|
if (tabsym == (SubtableSymbol *)0)
|
|
throw SleighError("Could not recover varnode template size");
|
|
iter = sizemap.find(tabsym);
|
|
if (iter == sizemap.end())
|
|
throw SleighError("Subtable out of order");
|
|
size = (*iter).second;
|
|
}
|
|
break;
|
|
default:
|
|
throw SleighError("Bad constant type as varnode template size");
|
|
}
|
|
return size;
|
|
}
|
|
|
|
void ConsistencyChecker::dealWithUnnecessaryExt(OpTpl *op,Constructor *ct)
|
|
|
|
{ // Deal with detected extension (SEXT or ZEXT) where the
|
|
// input size is the same as the output size
|
|
if (printextwarning) {
|
|
cerr << "Unnecessary ";
|
|
printOpName(cerr,op);
|
|
cerr << " in constructor starting at line " << dec << ct->getLineno() << endl;
|
|
}
|
|
op->setOpcode(CPUI_COPY); // Equivalent to copy
|
|
unnecessarypcode += 1;
|
|
}
|
|
|
|
void ConsistencyChecker::dealWithUnnecessaryTrunc(OpTpl *op,Constructor *ct)
|
|
|
|
{
|
|
if (printextwarning) {
|
|
cerr << "Unnecessary ";
|
|
printOpName(cerr,op);
|
|
cerr << " in constructor starting at line " << dec << ct->getLineno() << endl;
|
|
}
|
|
op->setOpcode(CPUI_COPY); // Equivalent to copy
|
|
op->removeInput(1);
|
|
unnecessarypcode += 1;
|
|
}
|
|
|
|
bool ConsistencyChecker::checkOpMisuse(OpTpl *op,Constructor *ct)
|
|
|
|
{
|
|
switch(op->getOpcode()) {
|
|
case CPUI_INT_LESS:
|
|
{
|
|
VarnodeTpl *vn = op->getIn(1);
|
|
if (vn->getSpace().isConstSpace() && vn->getOffset().isZero()) {
|
|
cerr << "Unsigned comparison with zero is always false in constructor starting at line " << dec << ct->getLineno() << endl;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ConsistencyChecker::sizeRestriction(OpTpl *op,Constructor *ct)
|
|
|
|
{ // Make sure op template meets size restrictions
|
|
// Return false and any info about mismatched sizes
|
|
int4 vnout,vn0,vn1;
|
|
AddrSpace *spc;
|
|
|
|
switch(op->getOpcode()) {
|
|
case CPUI_COPY: // Instructions where all inputs and output are same size
|
|
case CPUI_INT_2COMP:
|
|
case CPUI_INT_NEGATE:
|
|
case CPUI_FLOAT_NEG:
|
|
case CPUI_FLOAT_ABS:
|
|
case CPUI_FLOAT_SQRT:
|
|
case CPUI_FLOAT_CEIL:
|
|
case CPUI_FLOAT_FLOOR:
|
|
case CPUI_FLOAT_ROUND:
|
|
vnout = recoverSize(op->getOut()->getSize(),ct);
|
|
if (vnout == -1) {
|
|
printOpError(op,ct,-1,-1,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
vn0 = recoverSize(op->getIn(0)->getSize(),ct);
|
|
if (vn0 == -1) {
|
|
printOpError(op,ct,0,0,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
if (vnout == vn0) return true;
|
|
if ((vnout==0)||(vn0==0)) return true;
|
|
printOpError(op,ct,-1,0,"Input and output sizes must match");
|
|
return false;
|
|
case CPUI_INT_ADD:
|
|
case CPUI_INT_SUB:
|
|
case CPUI_INT_XOR:
|
|
case CPUI_INT_AND:
|
|
case CPUI_INT_OR:
|
|
case CPUI_INT_MULT:
|
|
case CPUI_INT_DIV:
|
|
case CPUI_INT_SDIV:
|
|
case CPUI_INT_REM:
|
|
case CPUI_INT_SREM:
|
|
case CPUI_FLOAT_ADD:
|
|
case CPUI_FLOAT_DIV:
|
|
case CPUI_FLOAT_MULT:
|
|
case CPUI_FLOAT_SUB:
|
|
vnout = recoverSize(op->getOut()->getSize(),ct);
|
|
if (vnout == -1) {
|
|
printOpError(op,ct,-1,-1,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
vn0 = recoverSize(op->getIn(0)->getSize(),ct);
|
|
if (vn0 == -1) {
|
|
printOpError(op,ct,0,0,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
vn1 = recoverSize(op->getIn(1)->getSize(),ct);
|
|
if (vn1 == -1) {
|
|
printOpError(op,ct,1,1,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
if ((vnout!=0)&&(vn0!=0)&&(vnout!=vn0)) {
|
|
printOpError(op,ct,-1,0,"The output and all input sizes must match");
|
|
return false;
|
|
}
|
|
if ((vnout!=0)&&(vn1!=0)&&(vnout!=vn1)) {
|
|
printOpError(op,ct,-1,1,"The output and all input sizes must match");
|
|
return false;
|
|
}
|
|
if ((vn0!=0)&&(vn1!=0)&&(vn0!=vn1)) {
|
|
printOpError(op,ct,0,1,"The output and all input sizes must match");
|
|
return false;
|
|
}
|
|
return true;
|
|
case CPUI_FLOAT_NAN:
|
|
vnout = recoverSize(op->getOut()->getSize(),ct);
|
|
if (vnout == -1) {
|
|
printOpError(op,ct,-1,-1,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
if (vnout != 1) {
|
|
printOpError(op,ct,-1,-1,"Output must be a boolean (size 1)");
|
|
return false;
|
|
}
|
|
break;
|
|
case CPUI_INT_EQUAL: // Instructions with bool output, all inputs equal size
|
|
case CPUI_INT_NOTEQUAL:
|
|
case CPUI_INT_SLESS:
|
|
case CPUI_INT_SLESSEQUAL:
|
|
case CPUI_INT_LESS:
|
|
case CPUI_INT_LESSEQUAL:
|
|
case CPUI_INT_CARRY:
|
|
case CPUI_INT_SCARRY:
|
|
case CPUI_INT_SBORROW:
|
|
case CPUI_FLOAT_EQUAL:
|
|
case CPUI_FLOAT_NOTEQUAL:
|
|
case CPUI_FLOAT_LESS:
|
|
case CPUI_FLOAT_LESSEQUAL:
|
|
vnout = recoverSize(op->getOut()->getSize(),ct);
|
|
if (vnout == -1) {
|
|
printOpError(op,ct,-1,-1,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
if (vnout != 1) {
|
|
printOpError(op,ct,-1,-1,"Output must be a boolean (size 1)");
|
|
return false;
|
|
}
|
|
vn0 = recoverSize(op->getIn(0)->getSize(),ct);
|
|
if (vn0 == -1) {
|
|
printOpError(op,ct,0,0,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
vn1 = recoverSize(op->getIn(1)->getSize(),ct);
|
|
if (vn1 == -1) {
|
|
printOpError(op,ct,1,1,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
if ((vn0==0)||(vn1==0)) return true;
|
|
if (vn0 != vn1) {
|
|
printOpError(op,ct,0,1,"Inputs must be the same size");
|
|
return false;
|
|
}
|
|
return true;
|
|
case CPUI_BOOL_XOR:
|
|
case CPUI_BOOL_AND:
|
|
case CPUI_BOOL_OR:
|
|
vnout = recoverSize(op->getOut()->getSize(),ct);
|
|
if (vnout == -1) {
|
|
printOpError(op,ct,-1,-1,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
if (vnout != 1) {
|
|
printOpError(op,ct,-1,-1,"Output must be a boolean (size 1)");
|
|
return false;
|
|
}
|
|
vn0 = recoverSize(op->getIn(0)->getSize(),ct);
|
|
if (vn0 == -1) {
|
|
printOpError(op,ct,0,0,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
if (vn0 != 1) {
|
|
printOpError(op,ct,0,0,"Input must be a boolean (size 1)");
|
|
return false;
|
|
}
|
|
return true;
|
|
case CPUI_BOOL_NEGATE:
|
|
vnout = recoverSize(op->getOut()->getSize(),ct);
|
|
if (vnout == -1) {
|
|
printOpError(op,ct,-1,-1,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
if (vnout != 1) {
|
|
printOpError(op,ct,-1,-1,"Output must be a boolean (size 1)");
|
|
return false;
|
|
}
|
|
vn0 = recoverSize(op->getIn(0)->getSize(),ct);
|
|
if (vn0 == -1) {
|
|
printOpError(op,ct,0,0,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
if (vn0 != 1) {
|
|
printOpError(op,ct,0,0,"Input must be a boolean (size 1)");
|
|
return false;
|
|
}
|
|
return true;
|
|
// The shift amount does not necessarily have to be the same size
|
|
// But the output and first parameter must be same size
|
|
case CPUI_INT_LEFT:
|
|
case CPUI_INT_RIGHT:
|
|
case CPUI_INT_SRIGHT:
|
|
vnout = recoverSize(op->getOut()->getSize(),ct);
|
|
if (vnout == -1) {
|
|
printOpError(op,ct,-1,-1,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
vn0 = recoverSize(op->getIn(0)->getSize(),ct);
|
|
if (vn0 == -1) {
|
|
printOpError(op,ct,0,0,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
if ((vnout==0)||(vn0==0)) return true;
|
|
if (vnout != vn0) {
|
|
printOpError(op,ct,-1,0,"Output and first input must be the same size");
|
|
return false;
|
|
}
|
|
return true;
|
|
case CPUI_INT_ZEXT:
|
|
case CPUI_INT_SEXT:
|
|
vnout = recoverSize(op->getOut()->getSize(),ct);
|
|
if (vnout == -1) {
|
|
printOpError(op,ct,-1,-1,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
vn0 = recoverSize(op->getIn(0)->getSize(),ct);
|
|
if (vn0 == -1) {
|
|
printOpError(op,ct,0,0,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
if ((vnout==0)||(vn0==0)) return true;
|
|
if (vnout == vn0) {
|
|
dealWithUnnecessaryExt(op,ct);
|
|
return true;
|
|
}
|
|
else if (vnout < vn0) {
|
|
printOpError(op,ct,-1,0,"Output size must be strictly bigger than input size");
|
|
return false;
|
|
}
|
|
return true;
|
|
case CPUI_CBRANCH:
|
|
vn1 = recoverSize(op->getIn(1)->getSize(),ct);
|
|
if (vn1 == -1) {
|
|
printOpError(op,ct,1,1,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
if (vn1 != 1) {
|
|
printOpError(op,ct,1,1,"Input must be a boolean (size 1)");
|
|
return false;
|
|
}
|
|
return true;
|
|
case CPUI_LOAD:
|
|
case CPUI_STORE:
|
|
if (op->getIn(0)->getOffset().getType() != ConstTpl::spaceid)
|
|
return true;
|
|
spc = op->getIn(0)->getOffset().getSpace();
|
|
vn1 = recoverSize(op->getIn(1)->getSize(),ct);
|
|
if (vn1 == -1) {
|
|
printOpError(op,ct,1,1,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
if ((vn1!=0)&&(vn1 != spc->getAddrSize())) {
|
|
printOpError(op,ct,1,1,"Pointer size must match size of space");
|
|
return false;
|
|
}
|
|
return true;
|
|
case CPUI_SUBPIECE:
|
|
vnout = recoverSize(op->getOut()->getSize(),ct);
|
|
if (vnout == -1) {
|
|
printOpError(op,ct,-1,-1,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
vn0 = recoverSize(op->getIn(0)->getSize(),ct);
|
|
if (vn0 == -1) {
|
|
printOpError(op,ct,0,0,"Using subtable with exports in expression");
|
|
return false;
|
|
}
|
|
vn1 = op->getIn(1)->getOffset().getReal();
|
|
if ((vnout==0)||(vn0==0)) return true;
|
|
if ((vnout==vn0)&&(vn1==0)) { // No actual truncation is occuring
|
|
dealWithUnnecessaryTrunc(op,ct);
|
|
return true;
|
|
}
|
|
else if (vnout>=vn0) {
|
|
printOpError(op,ct,-1,0,"Output must be strictly smaller than input");
|
|
return false;
|
|
}
|
|
if (vnout>vn0-vn1) {
|
|
printOpError(op,ct,-1,0,"Too much truncation");
|
|
return false;
|
|
}
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ConsistencyChecker::printOpName(ostream &s,OpTpl *op)
|
|
|
|
{
|
|
switch(op->getOpcode()) {
|
|
case CPUI_COPY:
|
|
s << "Copy(=)";
|
|
break;
|
|
case CPUI_LOAD:
|
|
s << "Load(*)";
|
|
break;
|
|
case CPUI_STORE:
|
|
s << "Store(*)";
|
|
break;
|
|
case CPUI_BRANCH:
|
|
s << "Branch(goto)";
|
|
break;
|
|
case CPUI_CBRANCH:
|
|
s << "Conditional branch(if)";
|
|
break;
|
|
case CPUI_BRANCHIND:
|
|
s << "Indirect branch(goto[])";
|
|
break;
|
|
case CPUI_CALL:
|
|
s << "Call";
|
|
break;
|
|
case CPUI_CALLIND:
|
|
s << "Indirect Call";
|
|
break;
|
|
case CPUI_CALLOTHER:
|
|
s << "User defined";
|
|
break;
|
|
case CPUI_RETURN:
|
|
s << "Return";
|
|
break;
|
|
case CPUI_INT_EQUAL:
|
|
s << "Equality(==)";
|
|
break;
|
|
case CPUI_INT_NOTEQUAL:
|
|
s << "Notequal(!=)";
|
|
break;
|
|
case CPUI_INT_SLESS:
|
|
s << "Signed less than(s<)";
|
|
break;
|
|
case CPUI_INT_SLESSEQUAL:
|
|
s << "Signed less than or equal(s<=)";
|
|
break;
|
|
case CPUI_INT_LESS:
|
|
s << "Less than(<)";
|
|
break;
|
|
case CPUI_INT_LESSEQUAL:
|
|
s << "Less than or equal(<=)";
|
|
break;
|
|
case CPUI_INT_ZEXT:
|
|
s << "Zero extension(zext)";
|
|
break;
|
|
case CPUI_INT_SEXT:
|
|
s << "Signed extension(sext)";
|
|
break;
|
|
case CPUI_INT_ADD:
|
|
s << "Addition(+)";
|
|
break;
|
|
case CPUI_INT_SUB:
|
|
s << "Subtraction(-)";
|
|
break;
|
|
case CPUI_INT_CARRY:
|
|
s << "Carry";
|
|
break;
|
|
case CPUI_INT_SCARRY:
|
|
s << "Signed carry";
|
|
break;
|
|
case CPUI_INT_SBORROW:
|
|
s << "Signed borrow";
|
|
break;
|
|
case CPUI_INT_2COMP:
|
|
s << "Twos complement(-)";
|
|
break;
|
|
case CPUI_INT_NEGATE:
|
|
s << "Negate(~)";
|
|
break;
|
|
case CPUI_INT_XOR:
|
|
s << "Exclusive or(^)";
|
|
break;
|
|
case CPUI_INT_AND:
|
|
s << "And(&)";
|
|
break;
|
|
case CPUI_INT_OR:
|
|
s << "Or(|)";
|
|
break;
|
|
case CPUI_INT_LEFT:
|
|
s << "Left shift(<<)";
|
|
break;
|
|
case CPUI_INT_RIGHT:
|
|
s << "Right shift(>>)";
|
|
break;
|
|
case CPUI_INT_SRIGHT:
|
|
s << "Signed right shift(s>>)";
|
|
break;
|
|
case CPUI_INT_MULT:
|
|
s << "Multiplication(*)";
|
|
break;
|
|
case CPUI_INT_DIV:
|
|
s << "Division(/)";
|
|
break;
|
|
case CPUI_INT_SDIV:
|
|
s << "Signed division(s/)";
|
|
break;
|
|
case CPUI_INT_REM:
|
|
s << "Remainder(%)";
|
|
break;
|
|
case CPUI_INT_SREM:
|
|
s << "Signed remainder(s%)";
|
|
break;
|
|
case CPUI_BOOL_NEGATE:
|
|
s << "Boolean negate(!)";
|
|
break;
|
|
case CPUI_BOOL_XOR:
|
|
s << "Boolean xor(^^)";
|
|
break;
|
|
case CPUI_BOOL_AND:
|
|
s << "Boolean and(&&)";
|
|
break;
|
|
case CPUI_BOOL_OR:
|
|
s << "Boolean or(||)";
|
|
break;
|
|
case CPUI_FLOAT_EQUAL:
|
|
s << "Float equal(f==)";
|
|
break;
|
|
case CPUI_FLOAT_NOTEQUAL:
|
|
s << "Float notequal(f!=)";
|
|
break;
|
|
case CPUI_FLOAT_LESS:
|
|
s << "Float less than(f<)";
|
|
break;
|
|
case CPUI_FLOAT_LESSEQUAL:
|
|
s << "Float less than or equal(f<=)";
|
|
break;
|
|
case CPUI_FLOAT_NAN:
|
|
s << "Not a number(nan)";
|
|
break;
|
|
case CPUI_FLOAT_ADD:
|
|
s << "Float addition(f+)";
|
|
break;
|
|
case CPUI_FLOAT_DIV:
|
|
s << "Float division(f/)";
|
|
break;
|
|
case CPUI_FLOAT_MULT:
|
|
s << "Float multiplication(f*)";
|
|
break;
|
|
case CPUI_FLOAT_SUB:
|
|
s << "Float subtractions(f-)";
|
|
break;
|
|
case CPUI_FLOAT_NEG:
|
|
s << "Float minus(f-)";
|
|
break;
|
|
case CPUI_FLOAT_ABS:
|
|
s << "Absolute value(abs)";
|
|
break;
|
|
case CPUI_FLOAT_SQRT:
|
|
s << "Square root";
|
|
break;
|
|
case CPUI_FLOAT_INT2FLOAT:
|
|
s << "Integer to float conversion(int2float)";
|
|
break;
|
|
case CPUI_FLOAT_FLOAT2FLOAT:
|
|
s << "Float to float conversion(float2float)";
|
|
break;
|
|
case CPUI_FLOAT_TRUNC:
|
|
s << "Float truncation(trunc)";
|
|
break;
|
|
case CPUI_FLOAT_CEIL:
|
|
s << "Ceiling(ceil)";
|
|
break;
|
|
case CPUI_FLOAT_FLOOR:
|
|
s << "Floor";
|
|
break;
|
|
case CPUI_FLOAT_ROUND:
|
|
s << "Round";
|
|
break;
|
|
case CPUI_MULTIEQUAL:
|
|
s << "Build";
|
|
break;
|
|
case CPUI_INDIRECT:
|
|
s << "Delay";
|
|
break;
|
|
case CPUI_SUBPIECE:
|
|
s << "Truncation(:)";
|
|
break;
|
|
case CPUI_SEGMENTOP:
|
|
s << "Segment table(segment)";
|
|
break;
|
|
case CPUI_CPOOLREF:
|
|
s << "Constant Pool(cpool)";
|
|
break;
|
|
case CPUI_NEW:
|
|
s << "New object(newobject)";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
OperandSymbol *ConsistencyChecker::getOperandSymbol(int4 slot,OpTpl *op,Constructor *ct)
|
|
|
|
{
|
|
VarnodeTpl *vn;
|
|
OperandSymbol *opsym;
|
|
int4 handindex;
|
|
|
|
if (slot == -1)
|
|
vn = op->getOut();
|
|
else
|
|
vn = op->getIn(slot);
|
|
|
|
switch(vn->getSize().getType()) {
|
|
case ConstTpl::handle:
|
|
handindex = vn->getSize().getHandleIndex();
|
|
opsym = ct->getOperand(handindex);
|
|
break;
|
|
default:
|
|
opsym = (OperandSymbol *)0;
|
|
break;
|
|
}
|
|
return opsym;
|
|
}
|
|
|
|
void ConsistencyChecker::printOpError(OpTpl *op,Constructor *ct,int4 err1,int4 err2,const string &msg)
|
|
|
|
{
|
|
SubtableSymbol *sym = ct->getParent();
|
|
OperandSymbol *op1,*op2;
|
|
|
|
op1 = getOperandSymbol(err1,op,ct);
|
|
if (err2 != err1)
|
|
op2 = getOperandSymbol(err2,op,ct);
|
|
else
|
|
op2 = (OperandSymbol *)0;
|
|
cerr << "Size restriction error in table \"" << sym->getName() << "\"" << endl;
|
|
cerr << " in constructor starting at line " << dec << ct->getLineno() << endl;
|
|
if ((op1 != (OperandSymbol *)0)&&(op2 != (OperandSymbol *)0)) {
|
|
cerr << " Problem with \"" << op1->getName();
|
|
cerr << "\" and \"" << op2->getName() << "\"";
|
|
}
|
|
else if (op1 != (OperandSymbol *)0)
|
|
cerr << " Problem with \"" << op1->getName() << "\"";
|
|
else if (op2 != (OperandSymbol *)0)
|
|
cerr << " Problem with \"" << op2->getName() << "\"";
|
|
else
|
|
cerr << " Problem";
|
|
cerr << " in ";
|
|
printOpName(cerr,op);
|
|
cerr << " operator" << endl << " " << msg << endl;
|
|
}
|
|
|
|
bool ConsistencyChecker::checkConstructorSection(Constructor *ct,ConstructTpl *cttpl)
|
|
|
|
{ // Check all the OpTpl s within the given section for consistency, return true if all tests pass
|
|
if (cttpl == (ConstructTpl *)0)
|
|
return true; // Nothing to check
|
|
vector<OpTpl *>::const_iterator iter;
|
|
const vector<OpTpl *> &ops(cttpl->getOpvec());
|
|
bool testresult = true;
|
|
|
|
for(iter=ops.begin();iter!=ops.end();++iter) {
|
|
if (!sizeRestriction(*iter,ct))
|
|
testresult = false;
|
|
if (!checkOpMisuse(*iter,ct))
|
|
testresult = false;
|
|
}
|
|
return testresult;
|
|
}
|
|
|
|
bool ConsistencyChecker::checkVarnodeTruncation(Constructor *ct,int4 slot,
|
|
OpTpl *op,VarnodeTpl *vn,bool isbigendian)
|
|
{
|
|
const ConstTpl &off( vn->getOffset() );
|
|
if (off.getType() != ConstTpl::handle) return true;
|
|
if (off.getSelect() != ConstTpl::v_offset_plus) return true;
|
|
ConstTpl::const_type sztype = vn->getSize().getType();
|
|
if ((sztype != ConstTpl::real)&&(sztype != ConstTpl::handle)) {
|
|
printOpError(op,ct,slot,slot,"Bad truncation expression");
|
|
return false;
|
|
}
|
|
int4 sz = recoverSize(off,ct); // Recover the size of the original operand
|
|
if (sz <= 0) {
|
|
printOpError(op,ct,slot,slot,"Could not recover size");
|
|
return false;
|
|
}
|
|
bool res = vn->adjustTruncation(sz,isbigendian);
|
|
if (!res) {
|
|
printOpError(op,ct,slot,slot,"Truncation operator out of bounds");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool ConsistencyChecker::checkSectionTruncations(Constructor *ct,ConstructTpl *cttpl,bool isbigendian)
|
|
|
|
{ // Check all the varnodes that have an offset_plus template
|
|
// adjust the plus if we are bigendian
|
|
// make sure the truncation is valid
|
|
vector<OpTpl *>::const_iterator iter;
|
|
const vector<OpTpl *> &ops(cttpl->getOpvec());
|
|
bool testresult = true;
|
|
|
|
for(iter=ops.begin();iter!=ops.end();++iter) {
|
|
OpTpl *op = *iter;
|
|
VarnodeTpl *outvn = op->getOut();
|
|
if (outvn != (VarnodeTpl *)0) {
|
|
if (!checkVarnodeTruncation(ct,-1,op,outvn,isbigendian))
|
|
testresult = false;
|
|
}
|
|
for(int4 i=0;i<op->numInput();++i) {
|
|
if (!checkVarnodeTruncation(ct,i,op,op->getIn(i),isbigendian))
|
|
testresult = false;
|
|
}
|
|
}
|
|
return testresult;
|
|
}
|
|
|
|
bool ConsistencyChecker::checkSubtable(SubtableSymbol *sym)
|
|
|
|
{
|
|
int4 tablesize = 0;
|
|
int4 numconstruct = sym->getNumConstructors();
|
|
Constructor *ct;
|
|
bool testresult = true;
|
|
bool seenemptyexport = false;
|
|
bool seennonemptyexport = false;
|
|
|
|
for(int4 i=0;i<numconstruct;++i) {
|
|
ct = sym->getConstructor(i);
|
|
if (!checkConstructorSection(ct,ct->getTempl()))
|
|
testresult = false;
|
|
int4 numsection = ct->getNumSections();
|
|
for(int4 j=0;j<numsection;++j) {
|
|
if (!checkConstructorSection(ct,ct->getNamedTempl(j)))
|
|
testresult = false;
|
|
}
|
|
|
|
if (ct->getTempl() == (ConstructTpl *)0) continue; // Unimplemented
|
|
HandleTpl *exportres = ct->getTempl()->getResult();
|
|
if (exportres != (HandleTpl *)0) {
|
|
if (seenemptyexport && (!seennonemptyexport)) {
|
|
cerr << "Table " << sym->getName() << " exports inconsistently" << endl;
|
|
cerr << "Constructor starting at line " << dec << ct->getLineno() << " is first inconsistency" << endl;
|
|
testresult = false;
|
|
}
|
|
seennonemptyexport = true;
|
|
int4 exsize = recoverSize(exportres->getSize(),ct);
|
|
if (tablesize == 0)
|
|
tablesize = exsize;
|
|
if ((exsize!=0)&&(exsize != tablesize)) {
|
|
cerr << "Table " << sym->getName() << " has inconsistent export size." << endl;
|
|
cerr << "Constructor starting at line " << dec << ct->getLineno() << " is first conflict" << endl;
|
|
testresult = false;
|
|
}
|
|
}
|
|
else {
|
|
if (seennonemptyexport && (!seenemptyexport)) {
|
|
cerr << "Table " << sym->getName() << " exports inconsistently" << endl;
|
|
cerr << "Constructor starting at line " << dec << ct->getLineno() << " is first inconsistency" << endl;
|
|
testresult = false;
|
|
}
|
|
seenemptyexport = true;
|
|
}
|
|
}
|
|
if (seennonemptyexport) {
|
|
if (tablesize == 0)
|
|
cerr << "Warning: Table " << sym->getName() << " exports size 0" << endl;
|
|
sizemap[sym] = tablesize; // Remember recovered size
|
|
}
|
|
else
|
|
sizemap[sym] = -1;
|
|
|
|
return testresult;
|
|
}
|
|
|
|
void ConsistencyChecker::setPostOrder(SubtableSymbol *root)
|
|
|
|
{
|
|
postorder.clear();
|
|
sizemap.clear();
|
|
|
|
// Establish post-order of SubtableSymbols so that we can
|
|
// recursively fill in sizes of varnodes which are exported
|
|
// from constructors
|
|
|
|
vector<SubtableSymbol *> path;
|
|
vector<int4> state;
|
|
vector<int4> ctstate;
|
|
|
|
sizemap[root] = -1; // Mark root as traversed
|
|
path.push_back(root);
|
|
state.push_back(0);
|
|
ctstate.push_back(0);
|
|
|
|
while(!path.empty()) {
|
|
SubtableSymbol *cur = path.back();
|
|
int4 ctind = state.back();
|
|
if (ctind >= cur->getNumConstructors()) {
|
|
path.pop_back(); // Table is fully traversed
|
|
state.pop_back();
|
|
ctstate.pop_back();
|
|
postorder.push_back(cur); // Post the traversed table
|
|
}
|
|
else {
|
|
Constructor *ct = cur->getConstructor(ctind);
|
|
int4 oper = ctstate.back();
|
|
if (oper >= ct->getNumOperands()) {
|
|
state.back() = ctind + 1; // Constructor fully traversed
|
|
ctstate.back() = 0;
|
|
}
|
|
else {
|
|
ctstate.back() = oper + 1;
|
|
OperandSymbol *opsym = ct->getOperand(oper);
|
|
SubtableSymbol *subsym = dynamic_cast<SubtableSymbol *>(opsym->getDefiningSymbol());
|
|
if (subsym != (SubtableSymbol *)0) {
|
|
map<SubtableSymbol *,int4>::const_iterator iter;
|
|
iter = sizemap.find(subsym);
|
|
if (iter == sizemap.end()) { // Not traversed yet
|
|
sizemap[subsym] = -1; // Mark table as traversed
|
|
path.push_back(subsym); // Recurse
|
|
state.push_back(0);
|
|
ctstate.push_back(0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ConsistencyChecker::possibleIntersection(const VarnodeTpl *vn1,const VarnodeTpl *vn2)
|
|
|
|
{ // Conservatively test whether vn1 and vn2 can intersect
|
|
if (vn1->getSpace().isConstSpace()) return false;
|
|
if (vn2->getSpace().isConstSpace()) return false;
|
|
|
|
bool u1 = vn1->getSpace().isUniqueSpace();
|
|
bool u2 = vn2->getSpace().isUniqueSpace();
|
|
|
|
if (u1 != u2) return false;
|
|
|
|
if (vn1->getSpace().getType() != ConstTpl::spaceid) return true;
|
|
if (vn2->getSpace().getType() != ConstTpl::spaceid) return true;
|
|
AddrSpace *spc = vn1->getSpace().getSpace();
|
|
if (spc != vn2->getSpace().getSpace()) return false;
|
|
|
|
|
|
if (vn2->getOffset().getType() != ConstTpl::real) return true;
|
|
if (vn2->getSize().getType() != ConstTpl::real) return true;
|
|
|
|
if (vn1->getOffset().getType() != ConstTpl::real) return true;
|
|
if (vn1->getSize().getType() != ConstTpl::real) return true;
|
|
|
|
uintb offset = vn1->getOffset().getReal();
|
|
uintb size = vn1->getSize().getReal();
|
|
|
|
uintb off = vn2->getOffset().getReal();
|
|
if (off+vn2->getSize().getReal()-1 < offset) return false;
|
|
if (off > (offset+size-1)) return false;
|
|
return true;
|
|
}
|
|
|
|
bool ConsistencyChecker::readWriteInterference(const VarnodeTpl *vn,const OpTpl *op,bool checkread) const
|
|
|
|
{ // Does op potentially read vn
|
|
// This is extremely conservative. Basically any op where
|
|
// we can't see exactly what might be written is considered
|
|
// interference
|
|
switch(op->getOpcode()) {
|
|
case BUILD:
|
|
case CROSSBUILD:
|
|
case DELAY_SLOT:
|
|
case MACROBUILD:
|
|
case CPUI_LOAD:
|
|
case CPUI_STORE:
|
|
case CPUI_BRANCH:
|
|
case CPUI_CBRANCH:
|
|
case CPUI_BRANCHIND:
|
|
case CPUI_CALL:
|
|
case CPUI_CALLIND:
|
|
case CPUI_CALLOTHER:
|
|
case CPUI_RETURN:
|
|
case LABELBUILD: // Another value might jump in here
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (checkread) {
|
|
int4 numinputs = op->numInput();
|
|
for(int4 i=0;i<numinputs;++i)
|
|
if (possibleIntersection(vn,op->getIn(i)))
|
|
return true;
|
|
}
|
|
|
|
// We always check for writes to -vn-
|
|
const VarnodeTpl *vn2 = op->getOut();
|
|
if (vn2 != (const VarnodeTpl *)0) {
|
|
if (possibleIntersection(vn,vn2))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ConsistencyChecker::examineVn(map<uintb,OptimizeRecord> &recs,
|
|
const VarnodeTpl *vn,uint4 i,int4 inslot,int4 secnum)
|
|
{ // If varnode is a temporary, count whether it is read or written
|
|
if (vn == (const VarnodeTpl *)0) return;
|
|
if (!vn->getSpace().isUniqueSpace()) return;
|
|
if (vn->getOffset().getType() != ConstTpl::real) return;
|
|
|
|
map<uintb,OptimizeRecord>::iterator iter;
|
|
iter = recs.insert( pair<uint4,OptimizeRecord>(vn->getOffset().getReal(),OptimizeRecord())).first;
|
|
if (inslot>=0) {
|
|
(*iter).second.readop = i;
|
|
(*iter).second.readcount += 1;
|
|
(*iter).second.inslot = inslot;
|
|
(*iter).second.readsection = secnum;
|
|
}
|
|
else {
|
|
(*iter).second.writeop = i;
|
|
(*iter).second.writecount += 1;
|
|
(*iter).second.writesection = secnum;
|
|
}
|
|
}
|
|
|
|
void ConsistencyChecker::optimizeGather1(Constructor *ct,map<uintb,OptimizeRecord> &recs,int4 secnum) const
|
|
|
|
{ // Look for reads and writes to temporaries, count how many times each temporary is read or written
|
|
ConstructTpl *tpl;
|
|
if (secnum < 0)
|
|
tpl = ct->getTempl();
|
|
else
|
|
tpl = ct->getNamedTempl(secnum);
|
|
if (tpl == (ConstructTpl *)0)
|
|
return;
|
|
const vector<OpTpl *> &ops( tpl->getOpvec() );
|
|
for(uint4 i=0;i<ops.size();++i) {
|
|
const OpTpl *op = ops[i];
|
|
for(uint4 j=0;j<op->numInput();++j) {
|
|
const VarnodeTpl *vnin = op->getIn(j);
|
|
examineVn(recs,vnin,i,j,secnum);
|
|
}
|
|
const VarnodeTpl *vn = op->getOut();
|
|
examineVn(recs,vn,i,-1,secnum);
|
|
}
|
|
}
|
|
|
|
void ConsistencyChecker::optimizeGather2(Constructor *ct,map<uintb,OptimizeRecord> &recs,int4 secnum) const
|
|
|
|
{ // Make sure any temp used by the export is not optimized away
|
|
ConstructTpl *tpl;
|
|
if (secnum < 0)
|
|
tpl = ct->getTempl();
|
|
else
|
|
tpl = ct->getNamedTempl(secnum);
|
|
if (tpl == (ConstructTpl *)0)
|
|
return;
|
|
HandleTpl *hand = tpl->getResult();
|
|
if (hand == (HandleTpl *)0) return;
|
|
if (hand->getPtrSpace().isUniqueSpace()) {
|
|
if (hand->getPtrOffset().getType() == ConstTpl::real) {
|
|
pair<map<uintb,OptimizeRecord>::iterator,bool> res;
|
|
uintb offset = hand->getPtrOffset().getReal();
|
|
res = recs.insert( pair<uintb,OptimizeRecord>(offset,OptimizeRecord()));
|
|
(*res.first).second.writeop = 0;
|
|
(*res.first).second.readop = 0;
|
|
(*res.first).second.writecount = 2;
|
|
(*res.first).second.readcount = 2;
|
|
(*res.first).second.readsection = -2;
|
|
(*res.first).second.writesection = -2;
|
|
}
|
|
}
|
|
if (hand->getSpace().isUniqueSpace()) {
|
|
if ((hand->getPtrSpace().getType() == ConstTpl::real)&&
|
|
(hand->getPtrOffset().getType() == ConstTpl::real)) {
|
|
pair<map<uintb,OptimizeRecord>::iterator,bool> res;
|
|
uintb offset = hand->getPtrOffset().getReal();
|
|
res = recs.insert( pair<uintb,OptimizeRecord>(offset,OptimizeRecord()));
|
|
(*res.first).second.writeop = 0;
|
|
(*res.first).second.readop = 0;
|
|
(*res.first).second.writecount = 2;
|
|
(*res.first).second.readcount = 2;
|
|
(*res.first).second.readsection = -2;
|
|
(*res.first).second.writesection = -2;
|
|
}
|
|
}
|
|
}
|
|
|
|
ConsistencyChecker::OptimizeRecord *ConsistencyChecker::findValidRule(Constructor *ct,map<uintb,OptimizeRecord> &recs) const
|
|
|
|
{
|
|
map<uintb,OptimizeRecord>::iterator iter;
|
|
iter = recs.begin();
|
|
while(iter != recs.end()) {
|
|
OptimizeRecord &currec( (*iter).second );
|
|
++iter;
|
|
if ((currec.writecount==1)&&(currec.readcount==1)&&(currec.readsection==currec.writesection)) {
|
|
// Temporary must be read and written exactly once
|
|
ConstructTpl *tpl;
|
|
if (currec.readsection < 0)
|
|
tpl = ct->getTempl();
|
|
else
|
|
tpl = ct->getNamedTempl(currec.readsection);
|
|
const vector<OpTpl *> &ops( tpl->getOpvec() );
|
|
const OpTpl *op = ops[ currec.readop ];
|
|
if (currec.writeop >= currec.readop) // Read must come after write
|
|
throw SleighError("Read of temporary before write");
|
|
if (op->getOpcode() == CPUI_COPY) {
|
|
bool saverecord = true;
|
|
currec.opttype = 0; // Read op is a COPY
|
|
const VarnodeTpl *vn = op->getOut();
|
|
for(int4 i=currec.writeop+1;i<currec.readop;++i) { // Check for interference between write and read
|
|
if (readWriteInterference(vn,ops[i],true)) {
|
|
saverecord = false;
|
|
break;
|
|
}
|
|
}
|
|
if (saverecord)
|
|
return &currec;
|
|
}
|
|
op = ops[ currec.writeop ];
|
|
if (op->getOpcode() == CPUI_COPY) {
|
|
bool saverecord = true;
|
|
currec.opttype = 1; // Write op is a COPY
|
|
const VarnodeTpl *vn = op->getIn(0);
|
|
for(int4 i=currec.writeop+1;i<currec.readop;++i) { // Check for interference between write and read
|
|
if (readWriteInterference(vn,ops[i],false)) {
|
|
saverecord = false;
|
|
break;
|
|
}
|
|
}
|
|
if (saverecord)
|
|
return &currec;
|
|
}
|
|
}
|
|
}
|
|
return (OptimizeRecord *)0;
|
|
}
|
|
|
|
void ConsistencyChecker::applyOptimization(Constructor *ct,const OptimizeRecord &rec)
|
|
|
|
{
|
|
vector<int4> deleteops;
|
|
ConstructTpl *ctempl;
|
|
if (rec.readsection < 0)
|
|
ctempl = ct->getTempl();
|
|
else
|
|
ctempl = ct->getNamedTempl(rec.readsection);
|
|
|
|
if (rec.opttype == 0) { // If read op is COPY
|
|
int4 readop = rec.readop;
|
|
OpTpl *op = ctempl->getOpvec()[ readop ];
|
|
VarnodeTpl *vnout = new VarnodeTpl(*op->getOut()); // Make COPY output
|
|
ctempl->setOutput(vnout,rec.writeop); // become write output
|
|
deleteops.push_back(readop); // and then delete the read (COPY)
|
|
}
|
|
else if (rec.opttype == 1) { // If write op is COPY
|
|
int4 writeop = rec.writeop;
|
|
OpTpl *op = ctempl->getOpvec()[ writeop ];
|
|
VarnodeTpl *vnin = new VarnodeTpl(*op->getIn(0)); // Make COPY input
|
|
ctempl->setInput(vnin,rec.readop,rec.inslot); // become read input
|
|
deleteops.push_back(writeop); // and then delete the write (COPY)
|
|
}
|
|
ctempl->deleteOps(deleteops);
|
|
}
|
|
|
|
void ConsistencyChecker::checkUnusedTemps(Constructor *ct,const map<uintb,OptimizeRecord> &recs)
|
|
|
|
{
|
|
map<uintb,OptimizeRecord>::const_iterator iter;
|
|
iter = recs.begin();
|
|
while(iter != recs.end()) {
|
|
const OptimizeRecord &currec( (*iter).second );
|
|
if (currec.readcount == 0) {
|
|
if (printdeadwarning)
|
|
cerr << "Warning: temporary is written but not read in constructor starting at line " << dec << ct->getLineno() << endl;
|
|
writenoread += 1;
|
|
}
|
|
else if (currec.writecount == 0) {
|
|
cerr << "Error: temporary is read but not written in constructor starting at line " << dec << ct->getLineno() << endl;
|
|
readnowrite += 1;
|
|
}
|
|
++iter;
|
|
}
|
|
}
|
|
|
|
void ConsistencyChecker::optimize(Constructor *ct)
|
|
|
|
{
|
|
OptimizeRecord *currec;
|
|
map<uintb,OptimizeRecord> recs;
|
|
int4 numsections = ct->getNumSections();
|
|
do {
|
|
recs.clear();
|
|
for(int4 i=-1;i<numsections;++i) {
|
|
optimizeGather1(ct,recs,i);
|
|
optimizeGather2(ct,recs,i);
|
|
}
|
|
currec = findValidRule(ct,recs);
|
|
if (currec != (OptimizeRecord *)0)
|
|
applyOptimization(ct,*currec);
|
|
} while(currec != (OptimizeRecord *)0);
|
|
checkUnusedTemps(ct,recs);
|
|
}
|
|
|
|
bool ConsistencyChecker::test(void)
|
|
|
|
{ // Main entry point for size consistency check
|
|
setPostOrder(root_symbol);
|
|
bool testresult = true;
|
|
|
|
for(int4 i=0;i<postorder.size();++i) {
|
|
SubtableSymbol *sym = postorder[i];
|
|
if (!checkSubtable(sym))
|
|
testresult = false;
|
|
}
|
|
return testresult;
|
|
}
|
|
|
|
bool ConsistencyChecker::testTruncations(bool isbigendian)
|
|
|
|
{
|
|
// Now that the sizemap is calculated, we can check/adjust the offset_plus templates
|
|
bool testresult = true;
|
|
for(int4 i=0;i<postorder.size();++i) {
|
|
SubtableSymbol *sym = postorder[i];
|
|
int4 numconstruct = sym->getNumConstructors();
|
|
Constructor *ct;
|
|
for(int4 j=0;j<numconstruct;++j) {
|
|
ct = sym->getConstructor(j);
|
|
|
|
int4 numsections = ct->getNumSections();
|
|
for(int4 k=-1;k<numsections;++k) {
|
|
ConstructTpl *tpl;
|
|
if (k < 0)
|
|
tpl = ct->getTempl();
|
|
else
|
|
tpl = ct->getNamedTempl(k);
|
|
if (tpl == (ConstructTpl *)0)
|
|
continue;
|
|
if (!checkSectionTruncations(ct,tpl,isbigendian))
|
|
testresult = false;
|
|
}
|
|
}
|
|
}
|
|
return testresult;
|
|
}
|
|
|
|
void ConsistencyChecker::optimizeAll(void)
|
|
|
|
{
|
|
for(int4 i=0;i<postorder.size();++i) {
|
|
SubtableSymbol *sym = postorder[i];
|
|
int4 numconstruct = sym->getNumConstructors();
|
|
Constructor *ct;
|
|
for(int4 i=0;i<numconstruct;++i) {
|
|
ct = sym->getConstructor(i);
|
|
optimize(ct);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FieldContext::operator<(const FieldContext &op2) const
|
|
|
|
{
|
|
if (sym->getName() != op2.sym->getName())
|
|
return (sym->getName() < op2.sym->getName());
|
|
return (qual->low < op2.qual->low);
|
|
}
|
|
|
|
void MacroBuilder::free(void)
|
|
|
|
{
|
|
vector<HandleTpl *>::iterator iter;
|
|
|
|
for(iter=params.begin();iter!=params.end();++iter)
|
|
delete *iter;
|
|
|
|
params.clear();
|
|
}
|
|
|
|
void MacroBuilder::reportError(const string &val)
|
|
|
|
{
|
|
slgh->reportError(val,false);
|
|
haserror = true;
|
|
}
|
|
|
|
void MacroBuilder::setMacroOp(OpTpl *macroop)
|
|
|
|
{ // Set up parameters for a particular macro invocation
|
|
VarnodeTpl *vn;
|
|
HandleTpl *hand;
|
|
free();
|
|
for(int4 i=1;i<macroop->numInput();++i) {
|
|
vn = macroop->getIn(i);
|
|
hand = new HandleTpl(vn);
|
|
params.push_back(hand);
|
|
}
|
|
}
|
|
|
|
bool MacroBuilder::transferOp(OpTpl *op,vector<HandleTpl *> ¶ms)
|
|
|
|
{ // Fix handle details of a macro generated OpTpl relative to its specific invocation
|
|
// and transfer it into the output stream
|
|
VarnodeTpl *outvn = op->getOut();
|
|
int4 handleIndex = 0;
|
|
int4 plus;
|
|
bool hasrealsize = false;
|
|
uintb realsize = 0;
|
|
|
|
if (outvn != (VarnodeTpl *)0) {
|
|
plus = outvn->transfer(params);
|
|
if (plus >= 0) {
|
|
reportError("Cannot currently assign to bitrange of macro parameter that is a temporary");
|
|
return false;
|
|
}
|
|
}
|
|
for(int4 i=0;i<op->numInput();++i) {
|
|
VarnodeTpl *vn = op->getIn(i);
|
|
if (vn->getOffset().getType() == ConstTpl::handle) {
|
|
handleIndex = vn->getOffset().getHandleIndex();
|
|
hasrealsize = (vn->getSize().getType() == ConstTpl::real);
|
|
realsize = vn->getSize().getReal();
|
|
}
|
|
plus = vn->transfer(params);
|
|
if (plus >= 0) {
|
|
if (!hasrealsize) {
|
|
reportError("Problem with bit range operator in macro");
|
|
return false;
|
|
}
|
|
uintb newtemp = slgh->getUniqueAddr(); // Generate a new temporary location
|
|
|
|
// Generate a SUBPIECE op that implements the offset_plus
|
|
OpTpl *subpieceop = new OpTpl(CPUI_SUBPIECE);
|
|
VarnodeTpl *newvn = new VarnodeTpl(ConstTpl(slgh->getUniqueSpace()),ConstTpl(ConstTpl::real,newtemp),
|
|
ConstTpl(ConstTpl::real,realsize));
|
|
subpieceop->setOutput(newvn);
|
|
HandleTpl *hand = params[handleIndex];
|
|
VarnodeTpl *origvn = new VarnodeTpl( hand->getSpace(), hand->getPtrOffset(), hand->getSize() );
|
|
subpieceop->addInput(origvn);
|
|
VarnodeTpl *plusvn = new VarnodeTpl( ConstTpl(slgh->getConstantSpace()), ConstTpl(ConstTpl::real,plus),
|
|
ConstTpl(ConstTpl::real, 4) );
|
|
subpieceop->addInput(plusvn);
|
|
outvec.push_back(subpieceop);
|
|
|
|
delete vn; // Replace original varnode
|
|
op->setInput(new VarnodeTpl( *newvn ), i); // with output of subpiece
|
|
}
|
|
}
|
|
outvec.push_back(op);
|
|
return true;
|
|
}
|
|
|
|
void MacroBuilder::dump(OpTpl *op)
|
|
|
|
{
|
|
OpTpl *clone;
|
|
VarnodeTpl *v_clone,*vn;
|
|
|
|
clone = new OpTpl(op->getOpcode());
|
|
vn = op->getOut();
|
|
if (vn != (VarnodeTpl *)0) {
|
|
v_clone = new VarnodeTpl(*vn);
|
|
clone->setOutput(v_clone);
|
|
}
|
|
for(int4 i=0;i<op->numInput();++i) {
|
|
vn = op->getIn(i);
|
|
v_clone = new VarnodeTpl(*vn);
|
|
if (v_clone->isRelative()) {
|
|
// Adjust relative index, depending on the labelbase
|
|
uintb val = v_clone->getOffset().getReal() + getLabelBase();
|
|
v_clone->setRelative(val);
|
|
}
|
|
clone->addInput(v_clone);
|
|
}
|
|
if (!transferOp(clone,params))
|
|
delete clone;
|
|
}
|
|
|
|
void MacroBuilder::setLabel(OpTpl *op)
|
|
|
|
{ // A label within a macro is local to the macro, but when
|
|
// we expand the macro, we have to adjust the index of
|
|
// the label, which is local to the macro, so that it fits
|
|
// in with other labels local to the parent
|
|
OpTpl *clone;
|
|
VarnodeTpl *v_clone;
|
|
|
|
clone = new OpTpl(op->getOpcode());
|
|
v_clone = new VarnodeTpl( *op->getIn(0) ); // Clone the label index
|
|
// Make adjustment to macro local value so that it is parent local
|
|
uintb val = v_clone->getOffset().getReal() + getLabelBase();
|
|
v_clone->setOffset(val);
|
|
clone->addInput(v_clone);
|
|
outvec.push_back(clone);
|
|
}
|
|
|
|
uintb SleighPcode::allocateTemp(void)
|
|
|
|
{
|
|
return compiler->getUniqueAddr();
|
|
}
|
|
|
|
void SleighPcode::reportError(const string &msg)
|
|
|
|
{
|
|
return compiler->reportError(msg,true);
|
|
}
|
|
|
|
void SleighPcode::addSymbol(SleighSymbol *sym)
|
|
|
|
{
|
|
return compiler->addSymbol(sym);
|
|
}
|
|
|
|
SleighCompile::SleighCompile(void)
|
|
: SleighBase()
|
|
{
|
|
pcode.setCompiler(this);
|
|
contextlock = false; // Context layout is not locked
|
|
userop_count = 0;
|
|
errors = 0;
|
|
warnunnecessarypcode = false;
|
|
warndeadtemps = false;
|
|
lenientconflicterrors = true;
|
|
warnallnops = false;
|
|
root = (SubtableSymbol *)0;
|
|
}
|
|
|
|
void SleighCompile::predefinedSymbols(void)
|
|
|
|
{ // Define the "pre" defined spaces and symbols
|
|
// This must happen after endian has been defined
|
|
symtab.addScope(); // Create global scope
|
|
|
|
// Some predefined symbols
|
|
root = new SubtableSymbol("instruction"); // Base constructors
|
|
symtab.addSymbol(root);
|
|
insertSpace(new ConstantSpace(this,this,"const",0));
|
|
SpaceSymbol *spacesym = new SpaceSymbol(getConstantSpace()); // Constant space
|
|
symtab.addSymbol(spacesym);
|
|
insertSpace(new UniqueSpace(this,this,"unique",numSpaces(),0));
|
|
spacesym = new SpaceSymbol(getUniqueSpace()); // Temporary register space
|
|
symtab.addSymbol(spacesym);
|
|
StartSymbol *startsym = new StartSymbol("inst_start",getConstantSpace());
|
|
symtab.addSymbol(startsym);
|
|
EndSymbol *endsym = new EndSymbol("inst_next",getConstantSpace());
|
|
symtab.addSymbol(endsym);
|
|
EpsilonSymbol *epsilon = new EpsilonSymbol("epsilon",getConstantSpace());
|
|
symtab.addSymbol(epsilon);
|
|
pcode.setConstantSpace(getConstantSpace());
|
|
pcode.setUniqueSpace(getUniqueSpace());
|
|
}
|
|
|
|
int4 SleighCompile::calcContextVarLayout(int4 start,int4 sz,int4 numbits)
|
|
|
|
{
|
|
VarnodeSymbol *sym = contexttable[start].sym;
|
|
FieldQuality *qual;
|
|
int4 i,j;
|
|
int4 maxbits;
|
|
|
|
if ((sym->getSize()) % 4 != 0)
|
|
reportError("Invalid size of context register: "+sym->getName()+" : must be a multiple of 4 bytes",false);
|
|
maxbits = sym->getSize() * 8 -1;
|
|
i = 0;
|
|
while(i<sz) {
|
|
|
|
qual = contexttable[i].qual;
|
|
int4 min = qual->low;
|
|
int4 max = qual->high;
|
|
if ((max - min) > (8*sizeof(uintm)))
|
|
reportError("Size of bitfield " + qual->name + " larger than 32-bits",false);
|
|
if (max > maxbits)
|
|
reportError("Scope of bitfield " + qual->name + " extends beyond the size of context register",false);
|
|
j = i+1;
|
|
// Find union of fields overlapping with first field
|
|
while(j<sz) {
|
|
qual = contexttable[j].qual;
|
|
if (qual->low <= max) { // We have overlap of context variables
|
|
if (qual->high > max)
|
|
max = qual->high;
|
|
// reportWarning("Local context variables overlap in "+sym->getName(),false);
|
|
}
|
|
else
|
|
break;
|
|
j = j+1;
|
|
}
|
|
|
|
int4 alloc = max-min+1;
|
|
int4 startword = numbits / (8*sizeof(uintm));
|
|
int4 endword = (numbits+alloc-1) / (8*sizeof(uintm));
|
|
if (startword != endword)
|
|
numbits = endword * (8*sizeof(uintm)); // Bump up to next word
|
|
|
|
uint4 low = numbits;
|
|
numbits += alloc;
|
|
|
|
for(;i<j;++i) {
|
|
qual = contexttable[i].qual;
|
|
uint4 l = qual->low - min + low;
|
|
uint4 h = numbits-1-(max-qual->high);
|
|
ContextField *field = new ContextField(qual->signext,l,h);
|
|
addSymbol(new ContextSymbol(qual->name,field,sym,qual->low,qual->high,qual->flow));
|
|
}
|
|
|
|
}
|
|
sym->markAsContext();
|
|
return numbits;
|
|
}
|
|
|
|
void SleighCompile::buildDecisionTrees(void)
|
|
|
|
{
|
|
DecisionProperties props;
|
|
root->buildDecisionTree(props);
|
|
|
|
for(int4 i=0;i<tables.size();++i)
|
|
tables[i]->buildDecisionTree(props);
|
|
|
|
const vector<string> &ierrors( props.getIdentErrors() );
|
|
for(int4 i=0;i<ierrors.size();++i) {
|
|
errors += 1;
|
|
cerr << ierrors[i];
|
|
}
|
|
|
|
if (!lenientconflicterrors) {
|
|
const vector<string> &cerrors( props.getConflictErrors() );
|
|
for(int4 i=0;i<cerrors.size();++i) {
|
|
errors += 1;
|
|
cerr << cerrors[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
void SleighCompile::buildPatterns(void)
|
|
|
|
{
|
|
if (root == 0) {
|
|
reportError("No patterns to match.",false);
|
|
return;
|
|
}
|
|
root->buildPattern(cerr); // This should recursively hit everything
|
|
if (root->isError()) errors += 1;
|
|
for(int4 i=0;i<tables.size();++i) {
|
|
if (tables[i]->isError())
|
|
errors += 1;
|
|
if (tables[i]->getPattern() == (TokenPattern *)0)
|
|
reportWarning("Unreferenced table: "+tables[i]->getName(),false);
|
|
}
|
|
}
|
|
|
|
void SleighCompile::checkConsistency(void)
|
|
|
|
{
|
|
ConsistencyChecker checker(root,warnunnecessarypcode,warndeadtemps);
|
|
|
|
if (!checker.test()) {
|
|
errors += 1;
|
|
return;
|
|
}
|
|
if (!checker.testTruncations(isBigEndian())) {
|
|
errors += 1;
|
|
return;
|
|
}
|
|
if ((!warnunnecessarypcode)&&(checker.getNumUnnecessaryPcode() > 0)) {
|
|
cerr << dec << checker.getNumUnnecessaryPcode();
|
|
cerr << " unnecessary extensions/truncations were converted to copies" << endl;
|
|
cerr << "Use -u switch to list each individually" << endl;
|
|
}
|
|
checker.optimizeAll();
|
|
if (checker.getNumReadNoWrite() > 0) {
|
|
errors += 1;
|
|
return;
|
|
}
|
|
if ((!warndeadtemps)&&(checker.getNumWriteNoRead() > 0)) {
|
|
cerr << dec << checker.getNumWriteNoRead();
|
|
cerr << " operations wrote to temporaries that were not read" << endl;
|
|
cerr << "Use -t switch to list each individually" << endl;
|
|
}
|
|
}
|
|
|
|
void SleighCompile::checkNops(void)
|
|
|
|
{
|
|
if (noplist.size() > 0) {
|
|
if (warnallnops) {
|
|
for(int4 i=0;i<noplist.size();++i)
|
|
cerr << noplist[i] << endl;
|
|
}
|
|
cerr << dec << (int4)noplist.size() << " NOP constructors found" << endl;
|
|
if (!warnallnops)
|
|
cerr << "Use -n switch to list each individually" << endl;
|
|
}
|
|
}
|
|
|
|
string SleighCompile::checkSymbols(SymbolScope *scope)
|
|
|
|
{ // Make sure label symbols are used properly
|
|
ostringstream s;
|
|
SymbolTree::const_iterator iter;
|
|
for(iter=scope->begin();iter!=scope->end();++iter) {
|
|
LabelSymbol *sym = (LabelSymbol *)*iter;
|
|
if (sym->getType() != SleighSymbol::label_symbol) continue;
|
|
if (sym->getRefCount() == 0)
|
|
s << " Label <" << sym->getName() << "> was placed but not used\n";
|
|
else if (!sym->isPlaced())
|
|
s << " Label <" << sym->getName() << "> was referenced but never placed\n";
|
|
}
|
|
return s.str();
|
|
}
|
|
|
|
void SleighCompile::addSymbol(SleighSymbol *sym)
|
|
|
|
{ // Make sure symbol table errors are caught
|
|
try {
|
|
symtab.addSymbol(sym);
|
|
}
|
|
catch(SleighError &err) {
|
|
reportError(err.explain,true);
|
|
}
|
|
}
|
|
|
|
void SleighCompile::reportError(const string &msg,bool includeline)
|
|
|
|
{
|
|
cerr << "Error in " << filename.back() << ' ';
|
|
if (includeline)
|
|
cerr << "at line " << dec << lineno.back() << ": ";
|
|
cerr << msg << endl;
|
|
errors += 1;
|
|
if (errors >50) {
|
|
cerr << "Too many errors: Aborting" << endl;
|
|
exit(2);
|
|
}
|
|
}
|
|
|
|
void SleighCompile::reportWarning(const string &msg,bool includeline)
|
|
|
|
{
|
|
cerr << "Warning in " << filename.back() << ' ';
|
|
if (includeline)
|
|
cerr << "at line " << dec << lineno.back() << ": ";
|
|
cerr << msg << endl;
|
|
}
|
|
|
|
uintb SleighCompile::getUniqueAddr(void)
|
|
|
|
{
|
|
uintb base = getUniqueBase();
|
|
setUniqueBase(base + 16); // Should be maximum size of a unique
|
|
return base;
|
|
}
|
|
|
|
void SleighCompile::process(void)
|
|
|
|
{ // Do all post processing on the parsed data structures
|
|
checkNops();
|
|
if (getDefaultSpace() == (AddrSpace *)0)
|
|
reportError("No default space specified",false);
|
|
if (errors>0) return;
|
|
checkConsistency();
|
|
if (errors>0) return;
|
|
buildPatterns();
|
|
if (errors>0) return;
|
|
buildDecisionTrees();
|
|
if (errors>0) return;
|
|
try {
|
|
buildXrefs(); // Make sure we can build crossrefs properly
|
|
} catch(SleighError &err) {
|
|
cerr << err.explain << endl;
|
|
errors += 1;
|
|
return;
|
|
}
|
|
checkUniqueAllocation();
|
|
symtab.purge(); // Get rid of any symbols we don't plan to save
|
|
}
|
|
|
|
// Methods needed by the lexer
|
|
|
|
void SleighCompile::calcContextLayout(void)
|
|
|
|
{
|
|
if (contextlock) return; // Already locked
|
|
contextlock = true;
|
|
|
|
int4 context_offset = 0;
|
|
int4 begin,sz;
|
|
stable_sort(contexttable.begin(),contexttable.end());
|
|
begin = 0;
|
|
while(begin < contexttable.size()) { // Define the context variables
|
|
sz = 1;
|
|
while ((begin+sz < contexttable.size())&&(contexttable[begin+sz].sym==contexttable[begin].sym))
|
|
sz += 1;
|
|
context_offset = calcContextVarLayout(begin,sz,context_offset);
|
|
begin += sz;
|
|
}
|
|
|
|
// context_size = (context_offset+8*sizeof(uintm)-1)/(8*sizeof(uintm));
|
|
|
|
// Delete the quals
|
|
for(int4 i=0;i<contexttable.size();++i) {
|
|
FieldQuality *qual = contexttable[i].qual;
|
|
delete qual;
|
|
}
|
|
|
|
contexttable.clear();
|
|
}
|
|
|
|
string SleighCompile::grabCurrentFilePath(void) const
|
|
|
|
{ // Get the path of the current file being parse as either an absolute path, or relative to cwd
|
|
if (relpath.empty()) return "";
|
|
return (relpath.back() + filename.back());
|
|
}
|
|
|
|
void SleighCompile::parseFromNewFile(const string &fname)
|
|
|
|
{
|
|
string base,path;
|
|
FileManage::splitPath(fname,path,base);
|
|
filename.push_back(base);
|
|
if (relpath.empty() || FileManage::isAbsolutePath(path))
|
|
relpath.push_back(path);
|
|
else { // Relative paths from successive includes, combine
|
|
string totalpath = relpath.back();
|
|
totalpath += path;
|
|
relpath.push_back(totalpath);
|
|
}
|
|
lineno.push_back(1);
|
|
}
|
|
|
|
void SleighCompile::parsePreprocMacro(void)
|
|
|
|
{
|
|
filename.push_back(filename.back()+":macro");
|
|
relpath.push_back(relpath.back());
|
|
lineno.push_back(lineno.back());
|
|
}
|
|
|
|
void SleighCompile::parseFileFinished(void)
|
|
|
|
{
|
|
filename.pop_back();
|
|
relpath.pop_back();
|
|
lineno.pop_back();
|
|
}
|
|
|
|
bool SleighCompile::getPreprocValue(const string &nm,string &res) const
|
|
|
|
{
|
|
map<string,string>::const_iterator iter = preproc_defines.find(nm);
|
|
if (iter == preproc_defines.end()) return false;
|
|
res = (*iter).second;
|
|
return true;
|
|
}
|
|
|
|
void SleighCompile::setPreprocValue(const string &nm,const string &value)
|
|
|
|
{
|
|
preproc_defines[nm] = value;
|
|
}
|
|
|
|
bool SleighCompile::undefinePreprocValue(const string &nm)
|
|
|
|
{
|
|
map<string,string>::iterator iter = preproc_defines.find(nm);
|
|
if (iter==preproc_defines.end()) return false;
|
|
preproc_defines.erase(iter);
|
|
return true;
|
|
}
|
|
|
|
// Functions needed by the parser
|
|
|
|
TokenSymbol *SleighCompile::defineToken(string *name,uintb *sz)
|
|
|
|
{
|
|
uint4 size = *sz;
|
|
delete sz;
|
|
if ((size&7)!=0) {
|
|
reportError(*name+"token size must be multiple of 8",true);
|
|
size = (size/8)+1;
|
|
}
|
|
else
|
|
size = size/8;
|
|
Token *newtoken = new Token(*name,size,isBigEndian(),tokentable.size());
|
|
tokentable.push_back(newtoken);
|
|
delete name;
|
|
TokenSymbol *res = new TokenSymbol(newtoken);
|
|
addSymbol(res);
|
|
return res;
|
|
}
|
|
|
|
void SleighCompile::addTokenField(TokenSymbol *sym,FieldQuality *qual)
|
|
|
|
{
|
|
TokenField *field = new TokenField(sym->getToken(),qual->signext,qual->low,qual->high);
|
|
addSymbol(new ValueSymbol(qual->name,field));
|
|
delete qual;
|
|
}
|
|
|
|
bool SleighCompile::addContextField(VarnodeSymbol *sym,FieldQuality *qual)
|
|
|
|
{
|
|
if (contextlock)
|
|
return false; // Context layout has already been satisfied
|
|
|
|
contexttable.push_back(FieldContext(sym,qual));
|
|
return true;
|
|
}
|
|
|
|
void SleighCompile::newSpace(SpaceQuality *qual)
|
|
|
|
{
|
|
if (qual->size == 0) {
|
|
reportError("Space definition missing size attribute",true);
|
|
delete qual;
|
|
return;
|
|
}
|
|
|
|
int4 delay = (qual->type == SpaceQuality::registertype) ? 0 : 1;
|
|
AddrSpace *spc = new AddrSpace(this,this,IPTR_PROCESSOR,qual->name,qual->size,qual->wordsize,numSpaces(),AddrSpace::hasphysical,delay);
|
|
insertSpace(spc);
|
|
if (qual->isdefault) {
|
|
if (getDefaultSpace() != (AddrSpace *)0)
|
|
reportError("Multiple default spaces",true);
|
|
else {
|
|
setDefaultSpace(spc->getIndex()); // Make the flagged space the default
|
|
pcode.setDefaultSpace(spc);
|
|
}
|
|
}
|
|
delete qual;
|
|
addSymbol( new SpaceSymbol(spc) );
|
|
}
|
|
|
|
SectionSymbol *SleighCompile::newSectionSymbol(const string &nm)
|
|
|
|
{
|
|
SectionSymbol *sym = new SectionSymbol(nm,sections.size());
|
|
try {
|
|
symtab.addGlobalSymbol(sym);
|
|
} catch(SleighError &err) {
|
|
reportError(err.explain,true);
|
|
}
|
|
sections.push_back(sym);
|
|
numSections = sections.size();
|
|
return sym;
|
|
}
|
|
|
|
void SleighCompile::setEndian(int4 end)
|
|
|
|
{ // This MUST be called at the very beginning of the parse
|
|
// The parser should enforce this
|
|
setBigEndian( (end == 1) );
|
|
predefinedSymbols(); // Set up symbols now that we know endianess
|
|
}
|
|
|
|
void SleighCompile::defineVarnodes(SpaceSymbol *spacesym,uintb *off,uintb *size,vector<string> *names)
|
|
|
|
{
|
|
AddrSpace *spc = spacesym->getSpace();
|
|
uintb myoff = *off;
|
|
for(int4 i=0;i<names->size();++i) {
|
|
if ((*names)[i] != "_")
|
|
addSymbol( new VarnodeSymbol((*names)[i],spc,myoff,*size) );
|
|
myoff += *size;
|
|
}
|
|
delete names;
|
|
delete off;
|
|
delete size;
|
|
}
|
|
|
|
void SleighCompile::defineBitrange(string *name,VarnodeSymbol *sym,uint4 bitoffset,uint4 numb)
|
|
|
|
{ // Define a new symbol as a subrange of bits within another symbol
|
|
// If the ends of the range fall on byte boundaries, we
|
|
// simply define a normal VarnodeSymbol, otherwise we create
|
|
// a special symbol which is a place holder for the bitrange operator
|
|
string namecopy = *name;
|
|
delete name;
|
|
uint4 size = 8*sym->getSize(); // Number of bits
|
|
if (numb == 0) {
|
|
reportError("Size of bitrange is zero for: "+namecopy,true);
|
|
return;
|
|
}
|
|
if ((bitoffset >= size)||((bitoffset+numb)>size)) {
|
|
reportError("Bad bitrange for: "+namecopy,true);
|
|
return;
|
|
}
|
|
if ((bitoffset%8 == 0)&&(numb%8 == 0)) {
|
|
// This can be reduced to an ordinary varnode definition
|
|
AddrSpace *newspace = sym->getFixedVarnode().space;
|
|
uintb newoffset = sym->getFixedVarnode().offset;
|
|
int4 newsize = numb/8;
|
|
if (isBigEndian())
|
|
newoffset += (size-bitoffset-numb)/8;
|
|
else
|
|
newoffset += bitoffset/8;
|
|
addSymbol( new VarnodeSymbol(namecopy,newspace,newoffset,newsize) );
|
|
}
|
|
else // Otherwise define the special symbol
|
|
addSymbol( new BitrangeSymbol(namecopy,sym,bitoffset,numb) );
|
|
}
|
|
|
|
void SleighCompile::addUserOp(vector<string> *names)
|
|
|
|
{
|
|
for(int4 i=0;i<names->size();++i) {
|
|
UserOpSymbol *sym = new UserOpSymbol((*names)[i]);
|
|
sym->setIndex(userop_count++);
|
|
addSymbol( sym );
|
|
}
|
|
delete names;
|
|
}
|
|
|
|
SleighSymbol *SleighCompile::dedupSymbolList(vector<SleighSymbol *> *symlist)
|
|
|
|
{ // Find duplicates in -symlist-, null out all but first
|
|
SleighSymbol *res = (SleighSymbol *)0;
|
|
for(int4 i=0;i<symlist->size();++i) {
|
|
SleighSymbol *sym = (*symlist)[i];
|
|
if (sym == (SleighSymbol *)0) continue;
|
|
for(int4 j=i+1;j<symlist->size();++j) {
|
|
if ((*symlist)[j] == sym) { // Found a duplicate
|
|
res = sym; // Return example duplicate for error reporting
|
|
(*symlist)[j] = (SleighSymbol *)0; // Null out the duplicate
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void SleighCompile::attachValues(vector<SleighSymbol *> *symlist,vector<intb> *numlist)
|
|
|
|
{
|
|
SleighSymbol *dupsym = dedupSymbolList(symlist);
|
|
if (dupsym != (SleighSymbol *)0)
|
|
reportWarning("\"attach values\" list contains duplicate entries: "+dupsym->getName(),true);
|
|
for(int4 i=0;i<symlist->size();++i) {
|
|
ValueSymbol *sym = (ValueSymbol *)(*symlist)[i];
|
|
if (sym == (ValueSymbol *)0) continue;
|
|
PatternValue *patval = sym->getPatternValue();
|
|
if (patval->maxValue() + 1 != numlist->size()) {
|
|
reportError("Attach value " + sym->getName() + " is wrong size for list", true);
|
|
}
|
|
symtab.replaceSymbol(sym, new ValueMapSymbol(sym->getName(),patval,*numlist));
|
|
}
|
|
delete numlist;
|
|
delete symlist;
|
|
}
|
|
|
|
void SleighCompile::attachNames(vector<SleighSymbol *> *symlist,vector<string> *names)
|
|
|
|
{
|
|
SleighSymbol *dupsym = dedupSymbolList(symlist);
|
|
if (dupsym != (SleighSymbol *)0)
|
|
reportWarning("\"attach names\" list contains duplicate entries: "+dupsym->getName(),true);
|
|
for(int4 i=0;i<symlist->size();++i) {
|
|
ValueSymbol *sym = (ValueSymbol *)(*symlist)[i];
|
|
if (sym == (ValueSymbol *)0) continue;
|
|
PatternValue *patval = sym->getPatternValue();
|
|
if (patval->maxValue() + 1 != names->size()) {
|
|
reportError("Attach name " + sym->getName() + " is wrong size for list", true);
|
|
}
|
|
symtab.replaceSymbol(sym,new NameSymbol(sym->getName(),patval,*names));
|
|
}
|
|
delete names;
|
|
delete symlist;
|
|
}
|
|
|
|
void SleighCompile::attachVarnodes(vector<SleighSymbol *> *symlist,vector<SleighSymbol *> *varlist)
|
|
|
|
{
|
|
SleighSymbol *dupsym = dedupSymbolList(symlist);
|
|
if (dupsym != (SleighSymbol *)0)
|
|
reportWarning("\"attach variables\" list contains duplicate entries: "+dupsym->getName(),true);
|
|
for(int4 i=0;i<symlist->size();++i) {
|
|
ValueSymbol *sym = (ValueSymbol *)(*symlist)[i];
|
|
if (sym == (ValueSymbol *)0) continue;
|
|
PatternValue *patval = sym->getPatternValue();
|
|
if (patval->maxValue() + 1 != varlist->size()) {
|
|
reportError("Attach varnode " + sym->getName() + " is wrong size for list", true);
|
|
}
|
|
int4 sz = 0;
|
|
for(int4 j=0;j<varlist->size();++j) {
|
|
VarnodeSymbol *vsym = (VarnodeSymbol *)(*varlist)[j];
|
|
if (vsym != (VarnodeSymbol *)0) {
|
|
if (sz == 0)
|
|
sz = vsym->getFixedVarnode().size;
|
|
else if (sz != vsym->getFixedVarnode().size) {
|
|
reportError("Attach statement contains varnodes of different sizes",true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
symtab.replaceSymbol(sym,new VarnodeListSymbol(sym->getName(),patval,*varlist));
|
|
}
|
|
delete varlist;
|
|
delete symlist;
|
|
}
|
|
|
|
SubtableSymbol *SleighCompile::newTable(string *nm)
|
|
|
|
{
|
|
SubtableSymbol *sym = new SubtableSymbol(*nm);
|
|
addSymbol(sym);
|
|
tables.push_back(sym);
|
|
delete nm;
|
|
return sym;
|
|
}
|
|
|
|
void SleighCompile::newOperand(Constructor *ct,string *nm)
|
|
|
|
{
|
|
int4 index = ct->getNumOperands();
|
|
OperandSymbol *sym = new OperandSymbol(*nm,index,ct);
|
|
addSymbol(sym);
|
|
ct->addOperand(sym);
|
|
delete nm;
|
|
}
|
|
|
|
PatternEquation *SleighCompile::constrainOperand(OperandSymbol *sym,PatternExpression *patexp)
|
|
|
|
{ // Create constraint on operand
|
|
PatternEquation *res;
|
|
FamilySymbol *famsym = dynamic_cast<FamilySymbol *>(sym->getDefiningSymbol());
|
|
if (famsym != (FamilySymbol *)0) { // Operand already defined as family symbol
|
|
// This equation must be a constraint
|
|
res = new EqualEquation(famsym->getPatternValue(),patexp);
|
|
}
|
|
else { // Operand is currently undefined, so we can't constrain
|
|
PatternExpression::release(patexp);
|
|
res = (PatternEquation *)0;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void SleighCompile::defineOperand(OperandSymbol *sym,PatternExpression *patexp)
|
|
|
|
{ // Define operand in terms of PatternExpression
|
|
try {
|
|
sym->defineOperand(patexp);
|
|
sym->setOffsetIrrelevant(); // If not a self-definition, the operand has no
|
|
// pattern directly associated with it, so
|
|
// the operand's offset is irrelevant
|
|
}
|
|
catch(SleighError &err) {
|
|
reportError(err.explain,true);
|
|
PatternExpression::release(patexp);
|
|
}
|
|
}
|
|
|
|
PatternEquation *SleighCompile::defineInvisibleOperand(TripleSymbol *sym)
|
|
|
|
{
|
|
int4 index = curct->getNumOperands();
|
|
OperandSymbol *opsym = new OperandSymbol(sym->getName(),index,curct);
|
|
addSymbol(opsym);
|
|
curct->addInvisibleOperand(opsym);
|
|
PatternEquation *res = new OperandEquation(opsym->getIndex());
|
|
SleighSymbol::symbol_type tp = sym->getType();
|
|
try {
|
|
if ((tp==SleighSymbol::value_symbol)||(tp==SleighSymbol::context_symbol)) {
|
|
opsym->defineOperand(sym->getPatternExpression());
|
|
}
|
|
else {
|
|
opsym->defineOperand(sym);
|
|
// reportWarning("Defining invisible operand "+sym->getName(),true);
|
|
}
|
|
}
|
|
catch(SleighError &err) {
|
|
reportError(err.explain,true);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void SleighCompile::selfDefine(OperandSymbol *sym)
|
|
|
|
{ // Define operand as global symbol of same name
|
|
TripleSymbol *glob = dynamic_cast<TripleSymbol *>(symtab.findSymbol(sym->getName(),1));
|
|
if (glob == (TripleSymbol *)0) {
|
|
reportError(sym->getName()+": No matching global symbol",true);
|
|
return;
|
|
}
|
|
SleighSymbol::symbol_type tp = glob->getType();
|
|
try {
|
|
if ((tp==SleighSymbol::value_symbol)||(tp==SleighSymbol::context_symbol)) {
|
|
sym->defineOperand(glob->getPatternExpression());
|
|
}
|
|
else
|
|
sym->defineOperand(glob);
|
|
}
|
|
catch(SleighError &err) {
|
|
reportError(err.explain,true);
|
|
}
|
|
}
|
|
|
|
ConstructTpl *SleighCompile::setResultVarnode(ConstructTpl *ct,VarnodeTpl *vn)
|
|
|
|
{ // Set constructors handle to indicate given varnode
|
|
HandleTpl *res = new HandleTpl(vn);
|
|
delete vn;
|
|
ct->setResult(res);
|
|
return ct;
|
|
}
|
|
|
|
ConstructTpl *SleighCompile::setResultStarVarnode(ConstructTpl *ct,StarQuality *star,VarnodeTpl *vn)
|
|
|
|
{ // Set constructors handle to be the value pointed
|
|
// at by -vn-
|
|
HandleTpl *res = new HandleTpl(star->id,ConstTpl(ConstTpl::real,star->size),vn,
|
|
getUniqueSpace(),getUniqueAddr());
|
|
delete star;
|
|
delete vn;
|
|
ct->setResult(res);
|
|
return ct;
|
|
}
|
|
|
|
bool SleighCompile::contextMod(vector<ContextChange *> *vec,ContextSymbol *sym,PatternExpression *pe)
|
|
|
|
{ // A temporary change to a context variable (within the parsing of a single instruction)
|
|
// Because we are in the middle of parsing, the "inst_next" value has not been computed yet
|
|
// So we check to make sure the value expression doesn't use this symbol
|
|
vector<const PatternValue *> vallist;
|
|
pe->listValues(vallist);
|
|
for(uint4 i=0;i<vallist.size();++i)
|
|
if (dynamic_cast<const EndInstructionValue *>(vallist[i]) != (const EndInstructionValue *)0)
|
|
return false;
|
|
// Otherwise we generate a "temporary" change to context instruction (ContextOp)
|
|
ContextField *field = (ContextField *)sym->getPatternValue();
|
|
ContextOp *op = new ContextOp(field->getStartBit(),field->getEndBit(),pe);
|
|
vec->push_back(op);
|
|
return true;
|
|
}
|
|
|
|
void SleighCompile::contextSet(vector<ContextChange *> *vec,TripleSymbol *sym,
|
|
ContextSymbol *cvar)
|
|
|
|
{ // A permanent (global) change to context. During parsing of an instruction, this change
|
|
// is put off until the full instruction has been parsed. The existing value in the context
|
|
// field is set permanently to that value starting at the address given by the address expression
|
|
ContextField *field = (ContextField *)cvar->getPatternValue();
|
|
ContextCommit *op = new ContextCommit(sym,field->getStartBit(),field->getEndBit(),cvar->getFlow());
|
|
vec->push_back(op);
|
|
}
|
|
|
|
MacroSymbol *SleighCompile::createMacro(string *name,vector<string> *params)
|
|
|
|
{ // create a macro symbol (with parameter names)
|
|
curct = (Constructor *)0; // Not currently defining a Constructor
|
|
curmacro = new MacroSymbol(*name,macrotable.size());
|
|
delete name;
|
|
addSymbol(curmacro);
|
|
symtab.addScope(); // New scope for the body of the macro definition
|
|
pcode.resetLabelCount(); // Macros have their own labels
|
|
for(int4 i=0;i<params->size();++i) {
|
|
OperandSymbol *oper = new OperandSymbol((*params)[i],i,(Constructor *)0);
|
|
addSymbol(oper);
|
|
curmacro->addOperand(oper);
|
|
}
|
|
delete params;
|
|
return curmacro;
|
|
}
|
|
|
|
void SleighCompile::compareMacroParams(MacroSymbol *sym,const vector<ExprTree *> ¶m)
|
|
|
|
{ // Match up any qualities of the macro's OperandSymbols with
|
|
// any OperandSymbol passed into the macro
|
|
for(uint4 i=0;i<param.size();++i) {
|
|
VarnodeTpl *outvn = param[i]->getOut();
|
|
if (outvn == (VarnodeTpl *)0) continue;
|
|
// Check if an OperandSymbol was passed into this macro
|
|
if (outvn->getOffset().getType() != ConstTpl::handle) continue;
|
|
int4 hand = outvn->getOffset().getHandleIndex();
|
|
|
|
// The matching operands
|
|
OperandSymbol *macroop = sym->getOperand(i);
|
|
OperandSymbol *parentop;
|
|
if (curct == (Constructor *)0)
|
|
parentop = curmacro->getOperand(hand);
|
|
else
|
|
parentop = curct->getOperand(hand);
|
|
|
|
// This is the only property we check right now
|
|
if (macroop->isCodeAddress())
|
|
parentop->setCodeAddress();
|
|
}
|
|
}
|
|
|
|
vector<OpTpl *> *SleighCompile::createMacroUse(MacroSymbol *sym,vector<ExprTree *> *param)
|
|
|
|
{ // Create macro build directive, given symbol and parameters
|
|
if (sym->getNumOperands() != param->size()) {
|
|
string errmsg = "Invocation of macro \"" + sym->getName();
|
|
if (param->size() > sym->getNumOperands())
|
|
errmsg += "\" passes too many parameters";
|
|
else
|
|
errmsg += "\" passes too few parameters";
|
|
reportError(errmsg,true);
|
|
return new vector<OpTpl *>;
|
|
}
|
|
compareMacroParams(sym,*param);
|
|
OpTpl *op = new OpTpl(MACROBUILD);
|
|
VarnodeTpl *idvn = new VarnodeTpl(ConstTpl(getConstantSpace()),
|
|
ConstTpl(ConstTpl::real,sym->getIndex()),
|
|
ConstTpl(ConstTpl::real,4));
|
|
op->addInput(idvn);
|
|
return ExprTree::appendParams(op,param);
|
|
}
|
|
|
|
SectionVector *SleighCompile::standaloneSection(ConstructTpl *main)
|
|
|
|
{ // Create SectionVector for just the main rtl section with no named sections
|
|
SectionVector *res = new SectionVector(main,symtab.getCurrentScope());
|
|
return res;
|
|
}
|
|
|
|
SectionVector *SleighCompile::firstNamedSection(ConstructTpl *main,SectionSymbol *sym)
|
|
|
|
{ // Start the first named p-code section after the main p-code section
|
|
sym->incrementDefineCount();
|
|
SymbolScope *curscope = symtab.getCurrentScope(); // This should be a Constructor scope
|
|
SymbolScope *parscope = curscope->getParent();
|
|
if (parscope != symtab.getGlobalScope())
|
|
throw LowlevelError("firstNamedSection called when not in Constructor scope"); // Unrecoverable error
|
|
symtab.addScope(); // Add new scope under the Constructor scope
|
|
SectionVector *res = new SectionVector(main,curscope);
|
|
res->setNextIndex(sym->getTemplateId());
|
|
return res;
|
|
}
|
|
|
|
SectionVector *SleighCompile::nextNamedSection(SectionVector *vec,ConstructTpl *section,SectionSymbol *sym)
|
|
|
|
{ // Add additional named p-code sections
|
|
sym->incrementDefineCount();
|
|
symtab.popScope(); // Pop the scope of the last named section
|
|
SymbolScope *curscope = symtab.getCurrentScope(); // This should now be the Constructor scope
|
|
SymbolScope *parscope = curscope->getParent();
|
|
if (parscope != symtab.getGlobalScope())
|
|
throw LowlevelError("nextNamedSection called when not in section scope"); // Unrecoverable
|
|
symtab.addScope(); // Add new scope under the Constructor scope (not the last section scope)
|
|
vec->append(section,curscope); // Associate finished section
|
|
vec->setNextIndex(sym->getTemplateId()); // Set index for the NEXT section (not been fully parsed yet)
|
|
return vec;
|
|
}
|
|
|
|
SectionVector *SleighCompile::finalNamedSection(SectionVector *vec,ConstructTpl *section)
|
|
|
|
{ // Fill-in final named section to match the previous SectionSymbol
|
|
vec->append(section,symtab.getCurrentScope());
|
|
symtab.popScope(); // Pop the section scope
|
|
return vec;
|
|
}
|
|
|
|
vector<OpTpl *> *SleighCompile::createCrossBuild(VarnodeTpl *addr,SectionSymbol *sym)
|
|
|
|
{ // Create the crossbuild directive as a pcode template
|
|
unique_allocatemask = 1;
|
|
vector<OpTpl *> *res = new vector<OpTpl *>();
|
|
VarnodeTpl *sectionid = new VarnodeTpl(ConstTpl(getConstantSpace()),
|
|
ConstTpl(ConstTpl::real,sym->getTemplateId()),
|
|
ConstTpl(ConstTpl::real,4));
|
|
// This is simply a single pcodeop (template), where the opcode indicates the crossbuild directive
|
|
OpTpl *op = new OpTpl( CROSSBUILD );
|
|
op->addInput(addr); // The first input is the VarnodeTpl representing the address
|
|
op->addInput(sectionid); // The second input is the indexed representing the named pcode section to build
|
|
res->push_back(op);
|
|
sym->incrementRefCount(); // Keep track of the references to the section symbol
|
|
return res;
|
|
}
|
|
|
|
Constructor *SleighCompile::createConstructor(SubtableSymbol *sym)
|
|
|
|
{
|
|
if (sym == (SubtableSymbol *)0)
|
|
sym = WithBlock::getCurrentSubtable(withstack);
|
|
if (sym == (SubtableSymbol *)0)
|
|
sym = root;
|
|
curmacro = (MacroSymbol *)0; // Not currently defining a macro
|
|
curct = new Constructor(sym);
|
|
curct->setLineno(lineno.back());
|
|
sym->addConstructor(curct);
|
|
symtab.addScope(); // Make a new symbol scope for our constructor
|
|
pcode.resetLabelCount();
|
|
return curct;
|
|
}
|
|
|
|
void SleighCompile::resetConstructors(void)
|
|
|
|
{ // Reset set state after a an error in previous constructor
|
|
symtab.setCurrentScope(symtab.getGlobalScope()); // Purge any dangling local scopes
|
|
}
|
|
|
|
bool SleighCompile::expandMacros(ConstructTpl *ctpl,const vector<ConstructTpl *> ¯otable)
|
|
|
|
{
|
|
vector<OpTpl *> newvec;
|
|
vector<OpTpl *>::const_iterator iter;
|
|
OpTpl *op;
|
|
|
|
for(iter=ctpl->getOpvec().begin();iter!=ctpl->getOpvec().end();++iter) {
|
|
op = *iter;
|
|
if (op->getOpcode() == MACROBUILD) {
|
|
MacroBuilder builder(this,newvec,ctpl->numLabels());
|
|
int4 index = op->getIn(0)->getOffset().getReal();
|
|
if (index >= macrotable.size())
|
|
return false;
|
|
builder.setMacroOp(op);
|
|
ConstructTpl *macro_tpl = macrotable[index];
|
|
builder.build(macro_tpl,-1);
|
|
ctpl->setNumLabels( ctpl->numLabels() + macro_tpl->numLabels() );
|
|
delete op; // Throw away the place holder op
|
|
if (builder.hasError())
|
|
return false;
|
|
}
|
|
else
|
|
newvec.push_back(op);
|
|
}
|
|
ctpl->setOpvec(newvec);
|
|
return true;
|
|
}
|
|
|
|
bool SleighCompile::finalizeSections(Constructor *big,SectionVector *vec)
|
|
|
|
{ // Do all final checks, expansions, and linking for p-code sections
|
|
vector<string> errors;
|
|
|
|
RtlPair cur = vec->getMainPair();
|
|
int4 i=-1;
|
|
string sectionstring = " Main section: ";
|
|
int4 max = vec->getMaxId();
|
|
for(;;) {
|
|
string errstring;
|
|
|
|
errstring = checkSymbols(cur.scope); // Check labels in the section's scope
|
|
if (errstring.size()==0) {
|
|
if (!expandMacros(cur.section,macrotable))
|
|
errors.push_back(sectionstring + "Could not expand macros");
|
|
vector<int4> check;
|
|
big->markSubtableOperands(check);
|
|
int4 res = cur.section->fillinBuild(check,getConstantSpace());
|
|
if (res == 1)
|
|
errors.push_back(sectionstring + "Duplicate BUILD statements");
|
|
if (res == 2)
|
|
errors.push_back(sectionstring + "Unnecessary BUILD statements");
|
|
|
|
if (!PcodeCompile::propagateSize(cur.section))
|
|
errors.push_back(sectionstring + "Could not resolve at least 1 variable size");
|
|
}
|
|
if (i < 0) { // These potential errors only apply to main section
|
|
if (cur.section->getResult() != (HandleTpl *)0) { // If there is an export statement
|
|
if (big->getParent()==root)
|
|
errors.push_back(" Cannot have export statement in root constructor");
|
|
else if (!force_exportsize(cur.section))
|
|
errors.push_back(" Size of export is unknown");
|
|
}
|
|
}
|
|
if (cur.section->delaySlot() != 0) { // Delay slot is present in this constructor
|
|
if (root != big->getParent()) { // it is not in a root constructor
|
|
reportWarning("Delay slot used in",false);
|
|
cerr << " ";
|
|
big->printInfo(cerr);
|
|
cerr << endl;
|
|
}
|
|
if (cur.section->delaySlot() > maxdelayslotbytes) // Keep track of maximum delayslot parameter
|
|
maxdelayslotbytes = cur.section->delaySlot();
|
|
}
|
|
do {
|
|
i += 1;
|
|
if (i >= max) break;
|
|
cur = vec->getNamedPair(i);
|
|
} while(cur.section == (ConstructTpl *)0);
|
|
|
|
if (i >= max) break;
|
|
SectionSymbol *sym = sections[i];
|
|
sectionstring = " " + sym->getName() + " section: ";
|
|
}
|
|
if (!errors.empty()) {
|
|
ostringstream s;
|
|
s << "in ";
|
|
big->printInfo(s);
|
|
reportError(s.str(),false);
|
|
for(int4 j=0;j<errors.size();++j)
|
|
cerr << errors[j] << endl;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void SleighCompile::shiftUniqueVn(VarnodeTpl *vn,int4 sa)
|
|
|
|
{ // If the varnode is in the unique space, shift its offset up by -sa- bits
|
|
if (vn->getSpace().isUniqueSpace() && (vn->getOffset().getType() == ConstTpl::real)) {
|
|
uintb val = vn->getOffset().getReal();
|
|
val <<= sa;
|
|
vn->setOffset(val);
|
|
}
|
|
}
|
|
|
|
void SleighCompile::shiftUniqueOp(OpTpl *op,int4 sa)
|
|
|
|
{ // Shift the offset up by -sa- bits for any varnode used by this -op- in the unique space
|
|
VarnodeTpl *outvn = op->getOut();
|
|
if (outvn != (VarnodeTpl *)0)
|
|
shiftUniqueVn(outvn,sa);
|
|
for(int4 i=0;i<op->numInput();++i)
|
|
shiftUniqueVn(op->getIn(i),sa);
|
|
}
|
|
|
|
void SleighCompile::shiftUniqueHandle(HandleTpl *hand,int4 sa)
|
|
|
|
{ // Shift the offset up by -sa- bits, for either the dynamic or static varnode aspects that are in the unique space
|
|
if (hand->getSpace().isUniqueSpace() && (hand->getPtrSpace().getType() == ConstTpl::real)
|
|
&& (hand->getPtrOffset().getType() == ConstTpl::real)) {
|
|
uintb val = hand->getPtrOffset().getReal();
|
|
val <<= sa;
|
|
hand->setPtrOffset(val);
|
|
}
|
|
else if (hand->getPtrSpace().isUniqueSpace() && (hand->getPtrOffset().getType() == ConstTpl::real)) {
|
|
uintb val = hand->getPtrOffset().getReal();
|
|
val <<= sa;
|
|
hand->setPtrOffset(val);
|
|
}
|
|
|
|
if (hand->getTempSpace().isUniqueSpace() && (hand->getTempOffset().getType() == ConstTpl::real)) {
|
|
uintb val = hand->getTempOffset().getReal();
|
|
val <<= sa;
|
|
hand->setTempOffset(val);
|
|
}
|
|
}
|
|
|
|
void SleighCompile::shiftUniqueConstruct(ConstructTpl *tpl,int4 sa)
|
|
|
|
{ // Shift the offset up by -sa- bits, for any varnode in the unique space associated with this template
|
|
HandleTpl *result = tpl->getResult();
|
|
if (result != (HandleTpl *)0)
|
|
shiftUniqueHandle(result,sa);
|
|
const vector<OpTpl *> &vec( tpl->getOpvec() );
|
|
for(int4 i=0;i<vec.size();++i)
|
|
shiftUniqueOp(vec[i],sa);
|
|
}
|
|
|
|
void SleighCompile::checkUniqueAllocation(void)
|
|
|
|
{ // With crossbuilds, temporaries may need to survive across instructions in a packet, so here we
|
|
// provide space in the offset of the temporary (within the unique space) so that the run-time sleigh
|
|
// engine can alter the value to prevent collisions with other nearby instructions
|
|
if (unique_allocatemask == 0) return; // We don't have any crossbuild directives
|
|
|
|
unique_allocatemask = 0xff; // Provide 8 bits of free space
|
|
int4 sa = 8;
|
|
int4 secsize = sections.size(); // This is the upper bound for section numbers
|
|
SubtableSymbol *sym = root; // Start with the instruction table
|
|
int4 i = -1;
|
|
for(;;) {
|
|
int4 numconst = sym->getNumConstructors();
|
|
for(int4 j=0;j<numconst;++j) {
|
|
Constructor *ct = sym->getConstructor(j);
|
|
ConstructTpl *tpl = ct->getTempl();
|
|
if (tpl != (ConstructTpl *)0)
|
|
shiftUniqueConstruct(tpl,sa);
|
|
for(int4 k=0;k<secsize;++k) {
|
|
ConstructTpl *namedtpl = ct->getNamedTempl(k);
|
|
if (namedtpl != (ConstructTpl *)0)
|
|
shiftUniqueConstruct(namedtpl,sa);
|
|
}
|
|
}
|
|
i+=1;
|
|
if (i>=tables.size()) break;
|
|
sym = tables[i];
|
|
}
|
|
uintm ubase = getUniqueBase(); // We have to adjust the unique base
|
|
ubase <<= sa;
|
|
setUniqueBase(ubase);
|
|
}
|
|
|
|
void SleighCompile::pushWith(SubtableSymbol *ss,PatternEquation *pateq,vector<ContextChange *> *contvec)
|
|
|
|
{
|
|
withstack.push_back(WithBlock());
|
|
withstack.back().set(ss,pateq,contvec);
|
|
}
|
|
|
|
void SleighCompile::popWith(void)
|
|
|
|
{
|
|
withstack.pop_back();
|
|
}
|
|
|
|
void SleighCompile::buildConstructor(Constructor *big,PatternEquation *pateq,vector<ContextChange *> *contvec,SectionVector *vec)
|
|
|
|
{ // Take all the different parse pieces for a Constructor and build the Constructor object
|
|
bool noerrors = true;
|
|
if (vec != (SectionVector *)0) { // If the sections were implemented
|
|
noerrors = finalizeSections(big,vec);
|
|
if (noerrors) { // Attach the sections to the Constructor
|
|
big->setMainSection(vec->getMainSection());
|
|
int4 max = vec->getMaxId();
|
|
for(int4 i=0;i<max;++i) {
|
|
ConstructTpl *section = vec->getNamedSection(i);
|
|
if (section != (ConstructTpl *)0)
|
|
big->setNamedSection(section,i);
|
|
}
|
|
}
|
|
delete vec;
|
|
}
|
|
if (noerrors) {
|
|
pateq = WithBlock::collectAndPrependPattern(withstack, pateq);
|
|
contvec = WithBlock::collectAndPrependContext(withstack, contvec);
|
|
big->addEquation(pateq);
|
|
big->removeTrailingSpace();
|
|
if (contvec != (vector<ContextChange *> *)0) {
|
|
big->addContext(*contvec);
|
|
delete contvec;
|
|
}
|
|
}
|
|
symtab.popScope(); // In all cases pop scope
|
|
}
|
|
|
|
void SleighCompile::buildMacro(MacroSymbol *sym,ConstructTpl *rtl)
|
|
|
|
{
|
|
string errstring = checkSymbols(symtab.getCurrentScope());
|
|
if (errstring.size() != 0) {
|
|
reportError(" in definition of macro "+sym->getName(),false);
|
|
cerr << errstring;
|
|
return;
|
|
}
|
|
if (!expandMacros(rtl,macrotable)) {
|
|
reportError("Could not expand submacro in definition of macro "+sym->getName(),true);
|
|
return;
|
|
}
|
|
PcodeCompile::propagateSize(rtl); // Propagate size information (as much as possible)
|
|
sym->setConstruct(rtl);
|
|
symtab.popScope(); // Pop local variables used to define macro
|
|
macrotable.push_back(rtl);
|
|
}
|
|
|
|
void SleighCompile::recordNop(void)
|
|
|
|
{
|
|
ostringstream s;
|
|
s << "NOP detected at " << filename.back() << ':' << dec << lineno.back();
|
|
noplist.push_back(s.str());
|
|
}
|
|
|
|
static int4 run_compilation(const char *filein,const char *fileout,SleighCompile &compiler)
|
|
|
|
{
|
|
compiler.parseFromNewFile(filein);
|
|
slgh = &compiler; // Set global pointer up for parser
|
|
yyin = fopen(filein,"r"); // Open the file for the lexer
|
|
if (yyin == (FILE *)0) {
|
|
cerr << "Unable to open specfile: " << filein << endl;
|
|
return 2;
|
|
}
|
|
|
|
try {
|
|
int4 parseres = yyparse(); // Try to parse
|
|
fclose(yyin);
|
|
if (parseres==0)
|
|
compiler.process(); // Do all the post-processing
|
|
if ((parseres==0)&&(compiler.numErrors()==0)) { // If no errors
|
|
ofstream s(fileout);
|
|
if (!s) {
|
|
ostringstream errs;
|
|
errs << "Unable to open output file: " << fileout;
|
|
throw SleighError(errs.str());
|
|
}
|
|
compiler.saveXml(s); // Dump output xml
|
|
s.close();
|
|
}
|
|
else {
|
|
cerr << "No output produced" <<endl;
|
|
return 2;
|
|
}
|
|
yylex_destroy(); // Make sure lexer is reset so we can parse multiple files
|
|
} catch(LowlevelError &err) {
|
|
cerr << "Unrecoverable error: " << err.explain << endl;
|
|
return 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int4 run_xml(const char *filein,SleighCompile &compiler)
|
|
|
|
{
|
|
ifstream s(filein);
|
|
Document *doc;
|
|
string specfileout;
|
|
string specfilein;
|
|
|
|
try {
|
|
doc = xml_tree(s);
|
|
}
|
|
catch(XmlError &err) {
|
|
cerr << "Unable to parse single input file as XML spec: " << filein << endl;
|
|
exit(1);
|
|
}
|
|
s.close();
|
|
|
|
Element *el = doc->getRoot();
|
|
for(;;) {
|
|
const List &list(el->getChildren());
|
|
List::const_iterator iter;
|
|
for(iter=list.begin();iter!=list.end();++iter) {
|
|
el = *iter;
|
|
if (el->getName() == "processorfile") {
|
|
specfileout = el->getContent();
|
|
int4 num = el->getNumAttributes();
|
|
for(int4 i=0;i<num;++i) {
|
|
if (el->getAttributeName(i)=="slaspec")
|
|
specfilein = el->getAttributeValue(i);
|
|
else {
|
|
compiler.setPreprocValue(el->getAttributeName(i),el->getAttributeValue(i));
|
|
}
|
|
}
|
|
}
|
|
else if (el->getName() == "language_spec")
|
|
break;
|
|
else if (el->getName() == "language_description")
|
|
break;
|
|
}
|
|
if (iter==list.end()) break;
|
|
}
|
|
delete doc;
|
|
|
|
if (specfilein.size() == 0) {
|
|
cerr << "Input slaspec file was not specified in " << filein << endl;
|
|
exit(1);
|
|
}
|
|
if (specfileout.size() == 0) {
|
|
cerr << "Output sla file was not specified in " << filein << endl;
|
|
exit(1);
|
|
}
|
|
return run_compilation(specfilein.c_str(),specfileout.c_str(),compiler);
|
|
}
|
|
|
|
static void findSlaSpecs(vector<string> &res, const string &dir, const string &suffix)
|
|
|
|
{
|
|
FileManage::matchListDir(res, suffix, true, dir, false);
|
|
|
|
vector<string> dirs;
|
|
FileManage::directoryList(dirs, dir);
|
|
vector<string>::const_iterator iter;
|
|
for(iter = dirs.begin();iter!=dirs.end();++iter) {
|
|
const string &nextdir( *iter );
|
|
findSlaSpecs(res, nextdir,suffix);
|
|
}
|
|
}
|
|
|
|
static void initCompiler(SleighCompile &compiler, map<string,string> &defines, bool enableUnnecessaryPcodeWarning, bool disableLenientConflict,
|
|
bool enableAllNopWarning,bool enableDeadTempWarning,bool enforceLocalKeyWord)
|
|
|
|
{
|
|
map<string,string>::iterator iter = defines.begin();
|
|
for (iter = defines.begin(); iter != defines.end(); iter++) {
|
|
compiler.setPreprocValue((*iter).first, (*iter).second);
|
|
}
|
|
if (enableUnnecessaryPcodeWarning) {
|
|
compiler.setUnnecessaryPcodeWarning(true);
|
|
}
|
|
if (disableLenientConflict) {
|
|
compiler.setLenientConflict(false);
|
|
}
|
|
if (enableAllNopWarning) {
|
|
compiler.setAllNopWarning( true );
|
|
}
|
|
if (enableDeadTempWarning) {
|
|
compiler.setDeadTempWarning(true);
|
|
}
|
|
if (enforceLocalKeyWord) {
|
|
compiler.setEnforceLocalKeyWord(true);
|
|
}
|
|
}
|
|
|
|
static void segvHandler(int sig) {
|
|
exit(1); // Just die - prevents OS from popping-up a dialog
|
|
}
|
|
|
|
int main(int argc,char **argv)
|
|
|
|
{
|
|
int4 retval = 0;
|
|
|
|
signal(SIGSEGV, &segvHandler); // Exit on SEGV errors
|
|
|
|
#ifdef YYDEBUG
|
|
yydebug = 0;
|
|
#endif
|
|
|
|
if (argc < 2) {
|
|
cerr << "USAGE: sleigh [-x] [-dNAME=VALUE] inputfile [outputfile]" << endl;
|
|
cerr << " -a scan for all slaspec files recursively where inputfile is a directory" << endl;
|
|
cerr << " -x turns on parser debugging" << endl;
|
|
cerr << " -u print warnings for unnecessary pcode instructions" << endl;
|
|
cerr << " -l report pattern conflicts" << endl;
|
|
cerr << " -n print warnings for all NOP constructors" << endl;
|
|
cerr << " -t print warnings for dead temporaries" << endl;
|
|
cerr << " -e enforce use of 'local' keyword for temporaries" << endl;
|
|
cerr << " -DNAME=VALUE defines a preprocessor macro NAME with value VALUE" << endl;
|
|
exit(2);
|
|
}
|
|
|
|
const string SLAEXT(".sla"); // Default sla extension
|
|
const string SLASPECEXT(".slaspec");
|
|
map<string,string> defines;
|
|
bool enableUnnecessaryPcodeWarning = false;
|
|
bool disableLenientConflict = false;
|
|
bool enableAllNopWarning = false;
|
|
bool enableDeadTempWarning = false;
|
|
bool enforceLocalKeyWord = false;
|
|
|
|
bool compileAll = false;
|
|
|
|
int4 i;
|
|
for(i=1;i<argc;++i) {
|
|
if (argv[i][0] != '-') break;
|
|
if (argv[i][1] == 'a')
|
|
compileAll = true;
|
|
else if (argv[i][1] == 'D') {
|
|
string preproc(argv[i]+2);
|
|
string::size_type pos = preproc.find('=');
|
|
if (pos == string::npos) {
|
|
cerr << "Bad sleigh option: "<< argv[i] << endl;
|
|
exit(1);
|
|
}
|
|
string name = preproc.substr(0,pos);
|
|
string value = preproc.substr(pos+1);
|
|
defines[name] = value;
|
|
}
|
|
else if (argv[i][1] == 'u')
|
|
enableUnnecessaryPcodeWarning = true;
|
|
else if (argv[i][1] == 'l')
|
|
disableLenientConflict = true;
|
|
else if (argv[i][1] == 'n')
|
|
enableAllNopWarning = true;
|
|
else if (argv[1][1] == 't')
|
|
enableDeadTempWarning = true;
|
|
else if (argv[1][1] == 'e')
|
|
enforceLocalKeyWord = true;
|
|
#ifdef YYDEBUG
|
|
else if (argv[i][1] == 'x')
|
|
yydebug = 1; // Debug option
|
|
#endif
|
|
else {
|
|
cerr << "Unknown option: " << argv[i] << endl;
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if (compileAll) {
|
|
|
|
if (i< argc-1) {
|
|
cerr << "Too many parameters" << endl;
|
|
exit(1);
|
|
}
|
|
const string::size_type slaspecExtLen = SLASPECEXT.length();
|
|
|
|
vector<string> slaspecs;
|
|
string dirStr = ".";
|
|
if (i != argc)
|
|
dirStr = argv[i];
|
|
findSlaSpecs(slaspecs, dirStr,SLASPECEXT);
|
|
cout << "Compiling " << slaspecs.size() << " slaspec files in " << dirStr << "\n";
|
|
for(int4 j=0;j<slaspecs.size();++j) {
|
|
string slaspec = slaspecs[j];
|
|
cout << "Compiling (" << (j+1) << " of " << slaspecs.size() << ") " << slaspec << "\n";
|
|
string sla = slaspec;
|
|
sla.replace(slaspec.length() - slaspecExtLen, slaspecExtLen, SLAEXT);
|
|
SleighCompile compiler;
|
|
initCompiler(compiler, defines, enableUnnecessaryPcodeWarning,
|
|
disableLenientConflict, enableAllNopWarning, enableDeadTempWarning, enforceLocalKeyWord);
|
|
retval = run_compilation(slaspec.c_str(),sla.c_str(),compiler);
|
|
if (retval != 0) {
|
|
return retval; // stop on first error
|
|
}
|
|
}
|
|
|
|
} else { // compile single specification
|
|
|
|
if (i==argc) {
|
|
cerr << "Missing input file name" << endl;
|
|
exit(1);
|
|
}
|
|
|
|
string fileinExamine(argv[i]);
|
|
string::size_type extInPos = fileinExamine.find(SLASPECEXT);
|
|
bool autoExtInSet = false;
|
|
string fileinPreExt = "";
|
|
if (extInPos == string::npos) { //No Extension Given...
|
|
fileinPreExt = fileinExamine;
|
|
fileinExamine.append(SLASPECEXT);
|
|
autoExtInSet = true;
|
|
} else {
|
|
fileinPreExt = fileinExamine.substr(0,extInPos);
|
|
}
|
|
|
|
if (i< argc-2) {
|
|
cerr << "Too many parameters" << endl;
|
|
exit(1);
|
|
}
|
|
|
|
SleighCompile compiler;
|
|
initCompiler(compiler, defines, enableUnnecessaryPcodeWarning,
|
|
disableLenientConflict, enableAllNopWarning, enableDeadTempWarning, enforceLocalKeyWord);
|
|
|
|
if (i < argc - 1) {
|
|
string fileoutExamine(argv[i+1]);
|
|
string::size_type extOutPos = fileoutExamine.find(SLAEXT);
|
|
if (extOutPos == string::npos) { // No Extension Given...
|
|
fileoutExamine.append(SLAEXT);
|
|
}
|
|
retval = run_compilation(fileinExamine.c_str(),fileoutExamine.c_str(),compiler);
|
|
}else{
|
|
//First determine whether or not to use Run_XML...
|
|
if (autoExtInSet) { //Assumed format of at least "sleigh file" -> "sleigh file.slaspec file.sla"
|
|
string fileoutSTR = fileinPreExt;
|
|
fileoutSTR.append(SLAEXT);
|
|
retval = run_compilation(fileinExamine.c_str(),fileoutSTR.c_str(),compiler);
|
|
}else{
|
|
retval = run_xml(fileinExamine.c_str(),compiler);
|
|
}
|
|
|
|
}
|
|
}
|
|
return retval;
|
|
}
|