GP-2793 Better support for metatype="ptr" pentry tags

This commit is contained in:
caheckman 2023-01-18 20:05:43 -05:00
parent ba0b42dc82
commit 52d97329b9
5 changed files with 762 additions and 58 deletions

View file

@ -553,7 +553,7 @@ ParamListStandard::ParamListStandard(const ParamListStandard &op2)
maxdelay = op2.maxdelay; maxdelay = op2.maxdelay;
pointermax = op2.pointermax; pointermax = op2.pointermax;
thisbeforeret = op2.thisbeforeret; thisbeforeret = op2.thisbeforeret;
resourceTwoStart = op2.resourceTwoStart; resourceStart = op2.resourceStart;
populateResolver(); populateResolver();
} }
@ -686,13 +686,14 @@ void ParamListStandard::assignMap(const vector<Datatype *> &proto,TypeFactory &t
res.back().type = pointertp; res.back().type = pointertp;
res.back().flags = ParameterPieces::indirectstorage; res.back().flags = ParameterPieces::indirectstorage;
} }
else else {
res.back().addr = assignAddress(proto[i],status); res.back().addr = assignAddress(proto[i],status);
if (res.back().addr.isInvalid())
throw ParamUnassignedError("Cannot assign parameter address for " + proto[i]->getName());
res.back().type = proto[i]; res.back().type = proto[i];
res.back().flags = 0; res.back().flags = 0;
} }
if (res.back().addr.isInvalid())
throw ParamUnassignedError("Cannot assign parameter address for " + proto[i]->getName());
}
} }
/// From among the ParamEntrys matching the given \e group, return the one that best matches /// From among the ParamEntrys matching the given \e group, return the one that best matches
@ -818,39 +819,33 @@ void ParamListStandard::buildTrialMap(ParamActive *active) const
active->sortTrials(); active->sortTrials();
} }
/// \brief Calculate the range of trials in each of the two resource sections /// \brief Calculate the range of trials in each resource sections
/// ///
/// The trials must already be mapped, which should put them in group order. The sections /// The trials must already be mapped, which should put them in group order. The sections
/// split at the group given by \b resourceTwoStart. We pass back the range of trial indices /// split at the groups given by \b resourceStart. We pass back the starting index for
/// for each section. If \b resourceTwoStart is 0, then there is really only one section, and /// each range of trials.
/// the empty range [0,0] is passed back for the second section.
/// \param active is the given set of parameter trials /// \param active is the given set of parameter trials
/// \param oneStart will pass back the index of the first trial in the first section /// \param trialStart will hold the starting index for each range of trials
/// \param oneStop will pass back the index (+1) of the last trial in the first section void ParamListStandard::separateSections(ParamActive *active,vector<int4> &trialStart) const
/// \param twoStart will pass back the index of the first trial in the second section
/// \param twoStop will pass back the index (+1) of the last trial in the second section
void ParamListStandard::separateSections(ParamActive *active,int4 &oneStart,int4 &oneStop,int4 &twoStart,int4 &twoStop) const
{ {
int4 numtrials = active->getNumTrials(); int4 numtrials = active->getNumTrials();
if (resourceTwoStart == 0) { int4 currentTrial = 0;
// Only one section int4 nextGroup = resourceStart[1];
oneStart = 0; int4 nextSection = 2;
oneStop = numtrials; trialStart.push_back(currentTrial);
twoStart = 0; for(;currentTrial<numtrials;++currentTrial) {
twoStop = 0; ParamTrial &curtrial(active->getTrial(currentTrial));
return;
}
int4 i=0;
for(;i<numtrials;++i) {
ParamTrial &curtrial(active->getTrial(i));
if (curtrial.getEntry()==(const ParamEntry *)0) continue; if (curtrial.getEntry()==(const ParamEntry *)0) continue;
if (curtrial.getEntry()->getGroup() >= resourceTwoStart) break; if (curtrial.getEntry()->getGroup() >= nextGroup) {
if (nextSection > resourceStart.size())
throw LowlevelError("Missing next resource start");
nextGroup = resourceStart[nextSection];
nextSection += 1;
trialStart.push_back(currentTrial);
} }
oneStart = 0; }
oneStop = i; trialStart.push_back(numtrials);
twoStart = i;
twoStop = numtrials;
} }
/// \brief Mark all the trials within the indicated groups as \e not \e used, except for one specified index /// \brief Mark all the trials within the indicated groups as \e not \e used, except for one specified index
@ -1116,15 +1111,19 @@ void ParamListStandard::populateResolver(void)
void ParamListStandard::parsePentry(Decoder &decoder,vector<EffectRecord> &effectlist, void ParamListStandard::parsePentry(Decoder &decoder,vector<EffectRecord> &effectlist,
int4 groupid,bool normalstack,bool autokill,bool splitFloat,bool grouped) int4 groupid,bool normalstack,bool autokill,bool splitFloat,bool grouped)
{ {
type_metatype lastMeta = TYPE_UNION;
if (!entry.empty()) {
lastMeta = entry.back().isGrouped() ? TYPE_UNKNOWN : entry.back().getType();
}
entry.emplace_back(groupid); entry.emplace_back(groupid);
entry.back().decode(decoder,normalstack,grouped,entry); entry.back().decode(decoder,normalstack,grouped,entry);
if (splitFloat) { if (splitFloat) {
if (!grouped && entry.back().getType() == TYPE_FLOAT) { type_metatype currentMeta = grouped ? TYPE_UNKNOWN : entry.back().getType();
if (resourceTwoStart >= 0) if (lastMeta != currentMeta) {
throw LowlevelError("parameter list floating-point entries must come first"); if (lastMeta > currentMeta)
throw LowlevelError("parameter list entries must be ordered by metatype");
resourceStart.push_back(groupid);
} }
else if (resourceTwoStart < 0)
resourceTwoStart = groupid; // First time we have seen an integer slot
} }
AddrSpace *spc = entry.back().getSpace(); AddrSpace *spc = entry.back().getSpace();
if (spc->getType() == IPTR_SPACEBASE) if (spc->getType() == IPTR_SPACEBASE)
@ -1173,16 +1172,23 @@ void ParamListStandard::fillinMap(ParamActive *active) const
{ {
if (active->getNumTrials() == 0) return; // No trials to check if (active->getNumTrials() == 0) return; // No trials to check
if (entry.empty())
throw LowlevelError("Cannot derive parameter storage for prototype model without parameter entries");
buildTrialMap(active); // Associate varnodes with sorted list of parameter locations buildTrialMap(active); // Associate varnodes with sorted list of parameter locations
forceExclusionGroup(active); forceExclusionGroup(active);
int4 oneStart,oneStop,twoStart,twoStop; vector<int4> trialStart;
separateSections(active,oneStart,oneStop,twoStart,twoStop); separateSections(active,trialStart);
forceNoUse(active,oneStart,oneStop); int4 numSection = trialStart.size() - 1;
forceNoUse(active,twoStart,twoStop); // Definitely not used -- overrides active for(int4 i=0;i<numSection;++i) {
forceInactiveChain(active,2,oneStart,oneStop,0); // Chains of inactivity override later actives // Definitely not used -- overrides active
forceInactiveChain(active,2,twoStart,twoStop,resourceTwoStart); forceNoUse(active,trialStart[i],trialStart[i+1]);
}
for(int4 i=0;i<numSection;++i) {
// Chains of inactivity override later actives
forceInactiveChain(active,2,trialStart[i],trialStart[i+1],resourceStart[i]);
}
// Mark every active trial as used // Mark every active trial as used
for(int4 i=0;i<active->getNumTrials();++i) { for(int4 i=0;i<active->getNumTrials();++i) {
@ -1354,7 +1360,6 @@ void ParamListStandard::decode(Decoder &decoder,vector<EffectRecord> &effectlist
splitFloat = decoder.readBool(); splitFloat = decoder.readBool();
} }
} }
resourceTwoStart = splitFloat ? -1 : 0;
for(;;) { for(;;) {
uint4 subId = decoder.peekElement(); uint4 subId = decoder.peekElement();
if (subId == 0) break; if (subId == 0) break;
@ -1366,6 +1371,7 @@ void ParamListStandard::decode(Decoder &decoder,vector<EffectRecord> &effectlist
} }
} }
decoder.closeElement(elemId); decoder.closeElement(elemId);
resourceStart.push_back(numgroup);
calcDelay(); calcDelay();
populateResolver(); populateResolver();
} }

View file

@ -284,7 +284,7 @@ class ParamActive {
bool needsfinalcheck; ///< Should a final pass be made on trials (to take into account control-flow changes) bool needsfinalcheck; ///< Should a final pass be made on trials (to take into account control-flow changes)
bool recoversubcall; ///< True if \b this is being used to recover prototypes of a sub-function call bool recoversubcall; ///< True if \b this is being used to recover prototypes of a sub-function call
public: public:
ParamActive(bool recoversub); ///< Constructor an empty container ParamActive(bool recoversub); ///< Construct an empty container
void clear(void); ///< Reset to an empty container void clear(void); ///< Reset to an empty container
void registerTrial(const Address &addr,int4 sz); ///< Add a new trial to the container void registerTrial(const Address &addr,int4 sz); ///< Add a new trial to the container
int4 getNumTrials(void) const { return trial.size(); } ///< Get the number of trials in \b this container int4 getNumTrials(void) const { return trial.size(); } ///< Get the number of trials in \b this container
@ -558,7 +558,7 @@ protected:
int4 maxdelay; ///< Maximum heritage delay across all parameters int4 maxdelay; ///< Maximum heritage delay across all parameters
int4 pointermax; ///< If non-zero, maximum size of a data-type before converting to a pointer int4 pointermax; ///< If non-zero, maximum size of a data-type before converting to a pointer
bool thisbeforeret; ///< Does a \b this parameter come before a hidden return parameter bool thisbeforeret; ///< Does a \b this parameter come before a hidden return parameter
int4 resourceTwoStart; ///< If there are two resource sections, the group of the first entry in the second section vector<int4> resourceStart; ///< The starting group for each resource section
list<ParamEntry> entry; ///< The ordered list of parameter entries list<ParamEntry> entry; ///< The ordered list of parameter entries
vector<ParamEntryResolver *> resolverMap; ///< Map from space id to resolver vector<ParamEntryResolver *> resolverMap; ///< Map from space id to resolver
AddrSpace *spacebase; ///< Address space containing relative offset parameters AddrSpace *spacebase; ///< Address space containing relative offset parameters
@ -566,7 +566,7 @@ protected:
Address assignAddress(const Datatype *tp,vector<int4> &status) const; ///< Assign storage for given parameter data-type Address assignAddress(const Datatype *tp,vector<int4> &status) const; ///< Assign storage for given parameter data-type
const ParamEntry *selectUnreferenceEntry(int4 grp,type_metatype prefType) const; ///< Select entry to fill an unreferenced param const ParamEntry *selectUnreferenceEntry(int4 grp,type_metatype prefType) const; ///< Select entry to fill an unreferenced param
void buildTrialMap(ParamActive *active) const; ///< Build map from parameter trials to model ParamEntrys void buildTrialMap(ParamActive *active) const; ///< Build map from parameter trials to model ParamEntrys
void separateSections(ParamActive *active,int4 &oneStart,int4 &oneStop,int4 &twoStart,int4 &twoStop) const; void separateSections(ParamActive *active,vector<int4> &trialStart) const;
static void markGroupNoUse(ParamActive *active,int4 groupUpper,int4 groupStart,int4 index); static void markGroupNoUse(ParamActive *active,int4 groupUpper,int4 groupStart,int4 index);
static void markBestInactive(ParamActive *active,int4 group,int4 groupStart,type_metatype prefType); static void markBestInactive(ParamActive *active,int4 group,int4 groupStart,type_metatype prefType);
static void forceExclusionGroup(ParamActive *active); static void forceExclusionGroup(ParamActive *active);

View file

@ -0,0 +1,686 @@
/* ###
* 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 "architecture.hh"
#include "grammar.hh"
#include "test.hh"
#include <iostream>
class FuncProtoTestEnvironment {
Architecture *g;
ProtoModel *mod1;
public:
FuncProtoTestEnvironment(void);
~FuncProtoTestEnvironment(void);
static void build(void);
static void registerModel1(ostream &s);
static void registerModel2(ostream &s);
static void registerModel3(ostream &s);
};
static FuncProtoTestEnvironment theEnviron;
static Architecture *glb;
FuncProtoTestEnvironment::FuncProtoTestEnvironment(void)
{
g = (Architecture *)0;
mod1 = (ProtoModel *)0;
}
void FuncProtoTestEnvironment::build(void)
{
if (theEnviron.g != (Architecture *)0) return;
ArchitectureCapability *xmlCapability = ArchitectureCapability::getCapability("xml");
istringstream s(
"<binaryimage arch=\"Toy:LE:32:default:default\"></binaryimage>"
);
DocumentStorage store;
Document *doc = store.parseDocument(s);
store.registerTag(doc->getRoot());
ostringstream s2;
s2 << "<specextensions> ";
registerModel1(s2);
registerModel2(s2);
registerModel3(s2);
s2 << "</specextensions>\n";
istringstream s3(s2.str());
doc = store.parseDocument(s3);
store.registerTag(doc->getRoot());
theEnviron.g = xmlCapability->buildArchitecture("", "", &cout);
theEnviron.g->init(store);
glb = theEnviron.g;
}
void FuncProtoTestEnvironment::registerModel1(ostream &s)
{
const char *text =
"<prototype name=\"__model1\" extrapop=\"unknown\" stackshift=\"4\">"
"<input pointermax=\"4\">"
"<pentry minsize=\"1\" maxsize=\"4\">"
"<register name=\"r12\"/>"
"</pentry>"
"<pentry minsize=\"1\" maxsize=\"4\">"
"<register name=\"r11\"/>"
"</pentry>"
"<pentry minsize=\"1\" maxsize=\"4\">"
"<register name=\"r10\"/>"
"</pentry>"
"<pentry minsize=\"1\" maxsize=\"4\">"
"<register name=\"r9\"/>"
"</pentry>"
"<pentry minsize=\"1\" maxsize=\"4\">"
"<register name=\"r8\"/>"
"</pentry>"
"<pentry minsize=\"1\" maxsize=\"500\" align=\"4\">"
"<addr offset=\"0\" space=\"stack\"/>"
"</pentry>"
"</input>"
"<output>"
"<pentry minsize=\"1\" maxsize=\"4\">"
"<register name=\"r12\"/>"
"</pentry>"
"</output>"
"</prototype>";
s << text << endl;
}
void FuncProtoTestEnvironment::registerModel2(ostream &s)
{
const char *text =
"<prototype name=\"__model2\" extrapop=\"unknown\" stackshift=\"4\">"
"<input>"
"<pentry minsize=\"1\" maxsize=\"4\" metatype=\"ptr\"><register name=\"r1\"/>"
"</pentry>"
"<pentry minsize=\"1\" maxsize=\"4\" metatype=\"ptr\"><register name=\"r2\"/>"
"</pentry>"
"<pentry minsize=\"1\" maxsize=\"4\" metatype=\"float\" extension=\"float\"><register name=\"r3\"/>"
"</pentry>"
"<pentry minsize=\"1\" maxsize=\"4\" metatype=\"float\" extension=\"float\"><register name=\"r4\"/>"
"</pentry>"
"<pentry minsize=\"1\" maxsize=\"4\" metatype=\"float\" extension=\"float\"><register name=\"r5\"/>"
"</pentry>"
"<pentry minsize=\"1\" maxsize=\"4\">"
"<register name=\"r10\"/>"
"</pentry>"
"<pentry minsize=\"1\" maxsize=\"4\">"
"<register name=\"r9\"/>"
"</pentry>"
"<pentry minsize=\"1\" maxsize=\"4\">"
"<register name=\"r8\"/>"
"</pentry>"
"<pentry minsize=\"5\" maxsize=\"8\">"
"<addr space=\"join\" piece1=\"r10\" piece2=\"r9\"/>"
"</pentry>"
"<pentry minsize=\"1\" maxsize=\"500\" align=\"4\">"
"<addr offset=\"0\" space=\"stack\"/>"
"</pentry>"
"</input>"
"<output>"
"<pentry minsize=\"1\" maxsize=\"4\">"
"<register name=\"r12\"/>"
"</pentry>"
"</output>"
"</prototype>";
s << text << endl;
}
void FuncProtoTestEnvironment::registerModel3(ostream &s)
{
const char *text =
"<prototype name=\"__model3\" extrapop=\"unknown\" stackshift=\"4\">"
"<input>"
"<group>"
"<pentry minsize=\"1\" maxsize=\"4\" metatype=\"float\" extension=\"float\"><register name=\"r3\"/>"
"</pentry>"
"<pentry minsize=\"1\" maxsize=\"4\"> "
"<register name=\"r10\"/> "
"</pentry> "
"</group> "
"<group>"
"<pentry minsize=\"1\" maxsize=\"4\" metatype=\"float\" extension=\"float\"><register name=\"r4\"/>"
"</pentry>"
"<pentry minsize=\"1\" maxsize=\"4\"> "
"<register name=\"r9\"/> "
"</pentry> "
"</group>"
"<group>"
"<pentry minsize=\"1\" maxsize=\"4\" metatype=\"float\" extension=\"float\"><register name=\"r5\"/>"
"</pentry>"
"<pentry minsize=\"1\" maxsize=\"4\"> "
"<register name=\"r8\"/> "
"</pentry> "
"</group> "
"<pentry minsize=\"1\" maxsize=\"500\" align=\"4\"> "
"<addr offset=\"0\" space=\"stack\"/> "
"</pentry> "
"</input>"
"<output>"
"<pentry minsize=\"1\" maxsize=\"4\">"
"<register name=\"r12\"/>"
"</pentry>"
"</output>"
"</prototype>";
s << text << endl;
}
ProtoModel *getModel(const string &nm)
{
FuncProtoTestEnvironment::build();
return glb->protoModels[nm];
}
bool register_equal(ParameterPieces &piece,const string &nm)
{
VarnodeData vData = glb->translate->getRegister(nm);
if (vData.space != piece.addr.getSpace())
return false;
if (vData.offset != piece.addr.getOffset())
return false;
return true;
}
FuncProtoTestEnvironment::~FuncProtoTestEnvironment(void)
{
if (g != (Architecture *)0)
delete g;
}
TEST(funcproto_register) {
ProtoModel *model = getModel("__model1");
istringstream s("void func(int4 a,int4 b);");
PrototypePieces pieces;
parse_protopieces(pieces,s,glb);
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
vector<ParameterPieces> res;
model->assignParameterStorage(pieces.intypes, res, false);
ASSERT_EQUALS(res.size(),3);
ASSERT(res[0].addr.isInvalid()); // Placeholder for void return value
ASSERT(register_equal(res[1],"r12"));
ASSERT(register_equal(res[2],"r11"));
}
TEST(funcproto_smallregister) {
ProtoModel *model = getModel("__model1");
istringstream s("int4 func(char a,int4 b,int2 c,int4 d);");
PrototypePieces pieces;
parse_protopieces(pieces,s,glb);
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
vector<ParameterPieces> res;
model->assignParameterStorage(pieces.intypes, res, false);
ASSERT_EQUALS(res.size(),5);
ASSERT(register_equal(res[0],"r12")); // output register
ASSERT_EQUALS(res[0].type->getName(),"int4");
ASSERT(register_equal(res[1],"r12"));
ASSERT_EQUALS(res[1].type->getName(),"char");
ASSERT(register_equal(res[2],"r11"));
ASSERT_EQUALS(res[2].type->getName(),"int4");
ASSERT(register_equal(res[3],"r10"));
ASSERT_EQUALS(res[3].type->getName(),"int2");
ASSERT(register_equal(res[4],"r9"));
ASSERT_EQUALS(res[4].type->getName(),"int4");
}
TEST(funcproto_stackalign) {
ProtoModel *model = getModel("__model1");
istringstream s("int4 func(int4 a,int4 b,int4 c,int4 d,int4 e,int2 f,int1 *g);");
PrototypePieces pieces;
parse_protopieces(pieces,s,glb);
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
vector<ParameterPieces> res;
model->assignParameterStorage(pieces.intypes, res, false);
ASSERT_EQUALS(res.size(),8);
ASSERT(register_equal(res[0],"r12")); // output register
ASSERT_EQUALS(res[0].type->getName(),"int4");
ASSERT(register_equal(res[1],"r12"));
ASSERT_EQUALS(res[1].type,res[0].type);
ASSERT(register_equal(res[2],"r11"));
ASSERT_EQUALS(res[2].type,res[0].type);
ASSERT(register_equal(res[3],"r10"));
ASSERT_EQUALS(res[3].type,res[0].type);
ASSERT(register_equal(res[4],"r9"));
ASSERT_EQUALS(res[4].type,res[0].type);
ASSERT(register_equal(res[5],"r8"));
ASSERT_EQUALS(res[5].type,res[0].type);
ASSERT_EQUALS(res[6].addr.getSpace(),glb->getStackSpace());
ASSERT_EQUALS(res[6].addr.getOffset(),0x0);
ASSERT_EQUALS(res[6].type->getName(),"int2");
ASSERT_EQUALS(res[7].addr.getSpace(),glb->getStackSpace());
ASSERT_EQUALS(res[7].addr.getOffset(),0x4);
ASSERT_EQUALS(res[7].type->getMetatype(),TYPE_PTR);
}
TEST(funcproto_pointeroverflow) {
ProtoModel *model = getModel("__model1");
istringstream s("int2 func(int4 a,int8 b,int4 c);");
PrototypePieces pieces;
parse_protopieces(pieces,s,glb);
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
vector<ParameterPieces> res;
model->assignParameterStorage(pieces.intypes, res, false);
ASSERT_EQUALS(res.size(),4);
ASSERT(register_equal(res[0],"r12")); // output register
ASSERT_EQUALS(res[0].type->getName(),"int2");
ASSERT(register_equal(res[1],"r12"));
ASSERT_EQUALS(res[1].type->getName(),"int4");
ASSERT(register_equal(res[2],"r11"));
ASSERT_EQUALS(res[2].type->getMetatype(),TYPE_PTR);
ASSERT_EQUALS(((TypePointer *)res[2].type)->getPtrTo()->getName(),"int8");
ASSERT_EQUALS(res[2].flags,ParameterPieces::indirectstorage);
ASSERT(register_equal(res[3],"r10"));
ASSERT_EQUALS(res[3].type->getName(),"int4");
}
TEST(funcproto_stackoverflow) {
ProtoModel *model = getModel("__model2");
istringstream s("char func(int4 a,int8 b,int4 c);");
PrototypePieces pieces;
parse_protopieces(pieces,s,glb);
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
vector<ParameterPieces> res;
model->assignParameterStorage(pieces.intypes, res, false);
ASSERT_EQUALS(res.size(),4);
ASSERT(register_equal(res[0],"r12")); // output register
ASSERT_EQUALS(res[0].type->getName(),"char");
ASSERT(register_equal(res[1],"r10"));
ASSERT_EQUALS(res[1].type->getName(),"int4");
ASSERT_EQUALS(res[2].addr.getSpace(),glb->getStackSpace());
ASSERT_EQUALS(res[2].addr.getOffset(),0x0); // Should overflow to stack
ASSERT_EQUALS(res[2].type->getName(),"int8");
ASSERT(register_equal(res[3],"r9")); // Should resume with next register
ASSERT_EQUALS(res[3].type->getName(),"int4");
}
TEST(funcproto_floatreg) {
ProtoModel *model = getModel("__model2");
istringstream s("void func(int4 a,float4 b,float4 c,int4 d,float4 d);");
PrototypePieces pieces;
parse_protopieces(pieces,s,glb);
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
vector<ParameterPieces> res;
model->assignParameterStorage(pieces.intypes, res, false);
ASSERT_EQUALS(res.size(),6);
ASSERT(res[0].addr.isInvalid());
ASSERT(register_equal(res[1],"r10"));
ASSERT(register_equal(res[2],"r3"));
ASSERT_EQUALS(res[2].type->getName(),"float4");
ASSERT(register_equal(res[3],"r4"));
ASSERT_EQUALS(res[3].type->getName(),"float4");
ASSERT(register_equal(res[4],"r9"));
ASSERT(register_equal(res[5],"r5"));
ASSERT_EQUALS(res[5].type->getName(),"float4");
}
TEST(funcproto_floattogeneric) {
ProtoModel *model = getModel("__model2");
istringstream s("float4 func(int4 a,float4 b,float4 c,float4 d,float4 e,float4 f);");
PrototypePieces pieces;
parse_protopieces(pieces,s,glb);
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
vector<ParameterPieces> res;
model->assignParameterStorage(pieces.intypes, res, false);
ASSERT_EQUALS(res.size(),7);
ASSERT(register_equal(res[0],"r12"));
ASSERT_EQUALS(res[0].type->getName(),"float4");
ASSERT(register_equal(res[1],"r10"));
ASSERT(register_equal(res[2],"r3"));
ASSERT_EQUALS(res[2].type->getName(),"float4");
ASSERT(register_equal(res[3],"r4"));
ASSERT_EQUALS(res[3].type->getName(),"float4");
ASSERT(register_equal(res[4],"r5"));
ASSERT_EQUALS(res[4].type->getName(),"float4");
ASSERT(register_equal(res[5],"r9")); // If float registers are exhausted, it should pick up with generic registers
ASSERT_EQUALS(res[5].type->getName(),"float4");
ASSERT(register_equal(res[6],"r8"));
ASSERT_EQUALS(res[6].type->getName(),"float4");
}
TEST(funcproto_grouped) {
ProtoModel *model = getModel("__model3");
istringstream s("float4 func(int4 a,float4 b,float4 c,int4 d,float4 e);");
PrototypePieces pieces;
parse_protopieces(pieces,s,glb);
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
vector<ParameterPieces> res;
model->assignParameterStorage(pieces.intypes, res, false);
ASSERT_EQUALS(res.size(),6);
ASSERT(register_equal(res[0],"r12"));
ASSERT_EQUALS(res[0].type->getName(),"float4");
ASSERT(register_equal(res[1],"r10"));
ASSERT_EQUALS(res[1].type->getName(),"int4");
ASSERT(register_equal(res[2],"r4"));
ASSERT_EQUALS(res[2].type->getName(),"float4");
ASSERT(register_equal(res[3],"r5"));
ASSERT_EQUALS(res[3].type->getName(),"float4");
ASSERT_EQUALS(res[4].addr.getSpace(),glb->getStackSpace());
ASSERT_EQUALS(res[4].addr.getOffset(),0x0);
ASSERT_EQUALS(res[4].type->getName(),"int4");
ASSERT_EQUALS(res[5].addr.getSpace(),glb->getStackSpace());
ASSERT_EQUALS(res[5].addr.getOffset(),0x4);
ASSERT_EQUALS(res[5].type->getName(),"float4");
}
TEST(funcproto_join) {
ProtoModel *model = getModel("__model2");
istringstream s("int2 func(int8 a,int4 b,int4 c);");
PrototypePieces pieces;
parse_protopieces(pieces,s,glb);
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
vector<ParameterPieces> res;
model->assignParameterStorage(pieces.intypes, res, false);
ASSERT_EQUALS(res.size(),4);
ASSERT(register_equal(res[0],"r12"));
ASSERT_EQUALS(res[0].type->getName(),"int2");
ASSERT_EQUALS(res[1].addr.getSpace(),glb->getJoinSpace());
ASSERT_EQUALS(res[1].type->getName(),"int8");
ASSERT(register_equal(res[2],"r8")); // r10 and r9 are consumed, should pick up with r8
ASSERT_EQUALS(res[2].type->getName(),"int4");
ASSERT_EQUALS(res[3].addr.getSpace(),glb->getStackSpace());
ASSERT_EQUALS(res[3].addr.getOffset(),0);
ASSERT_EQUALS(res[3].type->getName(),"int4");
}
TEST(funcproto_nojoin) {
ProtoModel *model = getModel("__model2");
istringstream s("int4 func(int4 a,int8 b,int4 c);");
PrototypePieces pieces;
parse_protopieces(pieces,s,glb);
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
vector<ParameterPieces> res;
model->assignParameterStorage(pieces.intypes, res, false);
ASSERT_EQUALS(res.size(),4);
ASSERT(register_equal(res[0],"r12"));
ASSERT_EQUALS(res[0].type->getName(),"int4");
ASSERT(register_equal(res[1],"r10")); // r10 consumed by first parameter
ASSERT_EQUALS(res[1].type->getName(),"int4");
ASSERT_EQUALS(res[2].addr.getSpace(),glb->getStackSpace()); // Big param must go to stack, can't use join
ASSERT_EQUALS(res[2].addr.getOffset(),0);
ASSERT_EQUALS(res[2].type->getName(),"int8");
ASSERT(register_equal(res[3],"r9")); // Next param can go back to register
ASSERT_EQUALS(res[3].type->getName(),"int4");
}
TEST(funcproto_hiddenreturn) {
ProtoModel *model = getModel("__model1");
istringstream s("int8 func(int4 a,int4 b);");
PrototypePieces pieces;
parse_protopieces(pieces,s,glb);
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
vector<ParameterPieces> res;
model->assignParameterStorage(pieces.intypes, res, false);
ASSERT_EQUALS(res.size(),4);
ASSERT(register_equal(res[0],"r12")); // Pointer to actual return value
ASSERT_EQUALS(res[0].type->getMetatype(),TYPE_PTR);
ASSERT_EQUALS(((TypePointer *)res[0].type)->getPtrTo()->getName(),"int8");
ASSERT(register_equal(res[1],"r12")); // Hidden return value pointer
ASSERT_EQUALS(res[1].flags,ParameterPieces::hiddenretparm);
ASSERT_EQUALS(res[1].type->getMetatype(),TYPE_PTR);
ASSERT_EQUALS(((TypePointer *)res[1].type)->getPtrTo()->getName(),"int8");
ASSERT(register_equal(res[2],"r11")); // First formal parameter pushed to second slot
ASSERT_EQUALS(res[2].type->getName(),"int4");
ASSERT(register_equal(res[3],"r10"));
ASSERT_EQUALS(res[3].type->getName(),"int4");
}
TEST(funcproto_mixedmeta) {
ProtoModel *model = getModel("__model2");
istringstream s("int4 func(char *a,int4 b,float4 c,int4 *d);");
PrototypePieces pieces;
parse_protopieces(pieces,s,glb);
pieces.intypes.insert(pieces.intypes.begin(),pieces.outtype);
vector<ParameterPieces> res;
model->assignParameterStorage(pieces.intypes, res, false);
ASSERT_EQUALS(res.size(),5);
ASSERT(register_equal(res[0],"r12"));
ASSERT(register_equal(res[1],"r1"));
ASSERT_EQUALS(res[1].type->getMetatype(),TYPE_PTR);
ASSERT(register_equal(res[2],"r10"));
ASSERT(register_equal(res[3],"r3"));
ASSERT(register_equal(res[4],"r2"));
ASSERT_EQUALS(res[4].type->getMetatype(),TYPE_PTR);
}
void registerActive(ParamActive &paramActive,const string &nm,int4 sz)
{
paramActive.registerTrial(glb->translate->getRegister(nm).getAddr(),sz);
int4 num = paramActive.getNumTrials();
paramActive.getTrial(num-1).markActive();
}
void stackActive(ParamActive &paramActive,uintb off,int4 sz)
{
paramActive.registerTrial(Address(glb->getStackSpace(),off),sz);
int4 num = paramActive.getNumTrials();
paramActive.getTrial(num-1).markActive();
}
bool register_used(ParamTrial &trial,const string &nm)
{
if (!trial.isUsed()) return false;
VarnodeData vData = glb->translate->getRegister(nm);
if (trial.getAddress() != vData.getAddr())
return false;
if (trial.getSize() != vData.size)
return false;
return true;
}
bool stack_used(ParamTrial &trial,uintb off,int4 sz)
{
if (!trial.isUsed()) return false;
Address addr(glb->getStackSpace(),off);
if (trial.getAddress() != addr)
return false;
if (trial.getSize() != sz)
return false;
return true;
}
TEST(funcproto_recoverbasic)
{
ProtoModel *model = getModel("__model1");
ParamActive paramActive(false);
registerActive(paramActive,"r11",4);
registerActive(paramActive,"r10",4);
registerActive(paramActive,"r12",4);
model->deriveInputMap(&paramActive);
ASSERT_EQUALS(paramActive.getNumTrials(),3);
ASSERT(register_used(paramActive.getTrial(0),"r12"));
ASSERT(register_used(paramActive.getTrial(1),"r11"));
ASSERT(register_used(paramActive.getTrial(2),"r10"));
}
TEST(funcproto_recoversmallreg)
{
ProtoModel *model = getModel("__model1");
ParamActive paramActive(false);
registerActive(paramActive,"r11",4);
registerActive(paramActive,"r12l",2);
registerActive(paramActive,"r10l",2);
model->deriveInputMap(&paramActive);
ASSERT_EQUALS(paramActive.getNumTrials(),3);
ASSERT(register_used(paramActive.getTrial(0),"r12l"));
ASSERT(register_used(paramActive.getTrial(1),"r11"));
ASSERT(register_used(paramActive.getTrial(2),"r10l"));
}
TEST(funcproto_recoverstack)
{
ProtoModel *model = getModel("__model2");
ParamActive paramActive(false);
registerActive(paramActive,"r10",4);
stackActive(paramActive, 0, 2);
registerActive(paramActive,"r8",4);
stackActive(paramActive, 4, 4);
registerActive(paramActive,"r9",4);
model->deriveInputMap(&paramActive);
ASSERT_EQUALS(paramActive.getNumTrials(),10);
ASSERT(paramActive.getTrial(0).isUnref());
ASSERT(paramActive.getTrial(1).isUnref());
ASSERT(paramActive.getTrial(2).isUnref());
ASSERT(paramActive.getTrial(3).isUnref());
ASSERT(paramActive.getTrial(4).isUnref());
ASSERT(!paramActive.getTrial(0).isUsed());
ASSERT(!paramActive.getTrial(1).isUsed());
ASSERT(!paramActive.getTrial(2).isUsed());
ASSERT(!paramActive.getTrial(3).isUsed());
ASSERT(!paramActive.getTrial(4).isUsed());
ASSERT(register_used(paramActive.getTrial(5),"r10"));
ASSERT(register_used(paramActive.getTrial(6),"r9"));
ASSERT(register_used(paramActive.getTrial(7),"r8"));
ASSERT(stack_used(paramActive.getTrial(8),0,2));
ASSERT(stack_used(paramActive.getTrial(9),4,4));
}
TEST(funcproto_recoverunrefregister)
{
ProtoModel *model = getModel("__model1");
ParamActive paramActive(false);
registerActive(paramActive,"r12",4);
registerActive(paramActive,"r10",4);
model->deriveInputMap(&paramActive);
ASSERT_EQUALS(paramActive.getNumTrials(),3);
ASSERT(register_used(paramActive.getTrial(0),"r12"));
ASSERT(register_used(paramActive.getTrial(1),"r11"));
ASSERT(paramActive.getTrial(1).isUnref());
ASSERT(register_used(paramActive.getTrial(2),"r10"));
}
TEST(funcproto_recoverunrefstack)
{
ProtoModel *model = getModel("__model2");
ParamActive paramActive(false);
stackActive(paramActive,4,4);
stackActive(paramActive,12,4);
registerActive(paramActive,"r8",4);
registerActive(paramActive,"r9",4);
registerActive(paramActive,"r10",4);
model->deriveInputMap(&paramActive);
ASSERT_EQUALS(paramActive.getNumTrials(),12);
ASSERT(paramActive.getTrial(0).isUnref());
ASSERT(paramActive.getTrial(1).isUnref());
ASSERT(paramActive.getTrial(2).isUnref());
ASSERT(paramActive.getTrial(3).isUnref());
ASSERT(paramActive.getTrial(4).isUnref());
ASSERT(!paramActive.getTrial(0).isUsed());
ASSERT(!paramActive.getTrial(1).isUsed());
ASSERT(!paramActive.getTrial(2).isUsed());
ASSERT(!paramActive.getTrial(3).isUsed());
ASSERT(!paramActive.getTrial(4).isUsed());
ASSERT(register_used(paramActive.getTrial(5),"r10"));
ASSERT(register_used(paramActive.getTrial(6),"r9"));
ASSERT(register_used(paramActive.getTrial(7),"r8"));
ASSERT(stack_used(paramActive.getTrial(8),0,4));
ASSERT(paramActive.getTrial(8).isUnref());
ASSERT(stack_used(paramActive.getTrial(9),4,4));
ASSERT(stack_used(paramActive.getTrial(10),8,4));
ASSERT(paramActive.getTrial(10).isUnref());
ASSERT(stack_used(paramActive.getTrial(11),12,4));
}
TEST(funcproto_recovergroups)
{
ProtoModel *model = getModel("__model3");
ParamActive paramActive(false);
registerActive(paramActive,"r3",4);
registerActive(paramActive,"r5",4);
registerActive(paramActive,"r9",4);
model->deriveInputMap(&paramActive);
ASSERT_EQUALS(paramActive.getNumTrials(),3);
ASSERT(register_used(paramActive.getTrial(0),"r3"));
ASSERT(register_used(paramActive.getTrial(1),"r9"));
ASSERT(register_used(paramActive.getTrial(2),"r5"));
}
TEST(funcproto_recoverholes)
{
ProtoModel *model = getModel("__model1");
ParamActive paramActive(false);
registerActive(paramActive,"r8",4);
registerActive(paramActive,"r12",4);
stackActive(paramActive,0,4);
model->deriveInputMap(&paramActive);
ASSERT_EQUALS(paramActive.getNumTrials(),6);
ASSERT(register_used(paramActive.getTrial(0),"r12"));
ASSERT(!paramActive.getTrial(1).isUsed());
ASSERT(paramActive.getTrial(1).isUnref());
ASSERT(!paramActive.getTrial(2).isUsed());
ASSERT(paramActive.getTrial(2).isUnref());
ASSERT(!paramActive.getTrial(3).isUsed());
ASSERT(paramActive.getTrial(3).isUnref());
ASSERT(!paramActive.getTrial(4).isUsed());
ASSERT(!paramActive.getTrial(5).isUsed());
}
TEST(funcproto_recoverfloat)
{
ProtoModel *model = getModel("__model2");
ParamActive paramActive(false);
registerActive(paramActive,"r10",4);
registerActive(paramActive,"r5",4);
registerActive(paramActive,"r3",4);
model->deriveInputMap(&paramActive);
ASSERT_EQUALS(paramActive.getNumTrials(),6);
ASSERT(paramActive.getTrial(0).isUnref());
ASSERT(paramActive.getTrial(1).isUnref());
ASSERT(!paramActive.getTrial(0).isUsed());
ASSERT(!paramActive.getTrial(1).isUsed());
ASSERT(register_used(paramActive.getTrial(2),"r3"));
ASSERT(register_used(paramActive.getTrial(3),"r4"));
ASSERT(register_used(paramActive.getTrial(4),"r5"));
ASSERT(register_used(paramActive.getTrial(5),"r10"));
}
TEST(funcproto_recovermixedmeta)
{
ProtoModel *model = getModel("__model2");
ParamActive paramActive(false);
registerActive(paramActive,"r10",4);
registerActive(paramActive,"r4",4);
registerActive(paramActive,"r1",4);
model->deriveInputMap(&paramActive);
ASSERT_EQUALS(paramActive.getNumTrials(),6);
ASSERT(register_used(paramActive.getTrial(0),"r1"));
ASSERT(paramActive.getTrial(1).isUnref());
ASSERT(!paramActive.getTrial(1).isUsed());
ASSERT(register_used(paramActive.getTrial(2),"r3"));
ASSERT(register_used(paramActive.getTrial(3),"r4"));
ASSERT(paramActive.getTrial(4).isUnref());
ASSERT(!paramActive.getTrial(4).isUsed());
ASSERT(register_used(paramActive.getTrial(5),"r10"))
}

View file

@ -38,7 +38,7 @@ public interface ParamList {
/** /**
* Given a list of datatypes, calculate the storage locations used for passing those datatypes * Given a list of datatypes, calculate the storage locations used for passing those datatypes
* @param prog is the active progra * @param prog is the active program
* @param proto is the list of datatypes * @param proto is the list of datatypes
* @param res is the vector for holding the VariableStorage corresponding to datatypes * @param res is the vector for holding the VariableStorage corresponding to datatypes
* @param addAutoParams if true add/process auto-parameters * @param addAutoParams if true add/process auto-parameters

View file

@ -43,7 +43,8 @@ public class ParamListStandard implements ParamList {
// protected int maxdelay; // protected int maxdelay;
protected int pointermax; // If non-zero, maximum size of a datatype before converting to a pointer protected int pointermax; // If non-zero, maximum size of a datatype before converting to a pointer
protected boolean thisbeforeret; // Do hidden return pointers usurp the storage of the this pointer protected boolean thisbeforeret; // Do hidden return pointers usurp the storage of the this pointer
protected int resourceTwoStart; // Group id starting the section resource section (or 0 if only one section) protected boolean splitMetatype; // Are metatyped entries in separate resource sections
// protected int[] resourceStart; // The starting group for each resource section
protected ParamEntry[] entry; protected ParamEntry[] entry;
protected AddressSpace spacebase; // Space containing relative offset parameters protected AddressSpace spacebase; // Space containing relative offset parameters
@ -212,7 +213,7 @@ public class ParamListStandard implements ParamList {
if (thisbeforeret) { if (thisbeforeret) {
encoder.writeBool(ATTRIB_THISBEFORERETPOINTER, true); encoder.writeBool(ATTRIB_THISBEFORERETPOINTER, true);
} }
if (isInput && resourceTwoStart == 0) { if (isInput && !splitMetatype) {
encoder.writeBool(ATTRIB_SEPARATEFLOAT, false); encoder.writeBool(ATTRIB_SEPARATEFLOAT, false);
} }
int curgroup = -1; int curgroup = -1;
@ -239,18 +240,25 @@ public class ParamListStandard implements ParamList {
private void parsePentry(XmlPullParser parser, CompilerSpec cspec, ArrayList<ParamEntry> pe, private void parsePentry(XmlPullParser parser, CompilerSpec cspec, ArrayList<ParamEntry> pe,
int groupid, boolean splitFloat, boolean grouped) throws XmlParseException { int groupid, boolean splitFloat, boolean grouped) throws XmlParseException {
int lastMeta = -1; // Smaller than any real metatype
if (!pe.isEmpty()) {
ParamEntry lastEntry = pe.get(pe.size() - 1);
lastMeta = lastEntry.isGrouped() ? ParamEntry.TYPE_UNKNOWN : lastEntry.getType();
}
ParamEntry pentry = new ParamEntry(groupid); ParamEntry pentry = new ParamEntry(groupid);
pe.add(pentry); pe.add(pentry);
pentry.restoreXml(parser, cspec, pe, grouped); pentry.restoreXml(parser, cspec, pe, grouped);
if (splitFloat) { if (splitFloat) {
if (!grouped && pentry.getType() == ParamEntry.TYPE_FLOAT) { int currentMeta = grouped ? ParamEntry.TYPE_UNKNOWN : pentry.getType();
if (resourceTwoStart >= 0) { if (lastMeta != currentMeta) {
if (lastMeta > currentMeta) {
throw new XmlParseException( throw new XmlParseException(
"parameter list floating-point entries must come first"); "parameter list entries must be ordered by metatype");
} }
} // int[] newResourceStart = new int[resourceStart.length + 1];
else if (resourceTwoStart < 0) { // System.arraycopy(resourceStart, 0, newResourceStart, 0, resourceStart.length);
resourceTwoStart = groupid; // First time we have seen an integer slot // newResourceStart[resourceStart.length] = groupid;
// resourceStart = newResourceStart;
} }
} }
if (pentry.getSpace().isStackSpace()) { if (pentry.getSpace().isStackSpace()) {
@ -293,7 +301,7 @@ public class ParamListStandard implements ParamList {
spacebase = null; spacebase = null;
pointermax = 0; pointermax = 0;
thisbeforeret = false; thisbeforeret = false;
boolean splitFloat = true; splitMetatype = true;
XmlElement mainel = parser.start(); XmlElement mainel = parser.start();
String attribute = mainel.getAttribute("pointermax"); String attribute = mainel.getAttribute("pointermax");
if (attribute != null) { if (attribute != null) {
@ -305,24 +313,28 @@ public class ParamListStandard implements ParamList {
} }
attribute = mainel.getAttribute("separatefloat"); attribute = mainel.getAttribute("separatefloat");
if (attribute != null) { if (attribute != null) {
splitFloat = SpecXmlUtils.decodeBoolean(attribute); splitMetatype = SpecXmlUtils.decodeBoolean(attribute);
} }
resourceTwoStart = splitFloat ? -1 : 0; // resourceStart = new int[0];
for (;;) { for (;;) {
XmlElement el = parser.peek(); XmlElement el = parser.peek();
if (!el.isStart()) { if (!el.isStart()) {
break; break;
} }
if (el.getName().equals("pentry")) { if (el.getName().equals("pentry")) {
parsePentry(parser, cspec, pe, numgroup, splitFloat, false); parsePentry(parser, cspec, pe, numgroup, splitMetatype, false);
} }
else if (el.getName().equals("group")) { else if (el.getName().equals("group")) {
parseGroup(parser, cspec, pe, numgroup, splitFloat); parseGroup(parser, cspec, pe, numgroup, splitMetatype);
} }
} }
parser.end(mainel); parser.end(mainel);
entry = new ParamEntry[pe.size()]; entry = new ParamEntry[pe.size()];
pe.toArray(entry); pe.toArray(entry);
// int[] newResourceStart = new int[resourceStart.length + 1];
// System.arraycopy(resourceStart, 0, newResourceStart, 0, resourceStart.length);
// newResourceStart[resourceStart.length] = numgroup;
// resourceStart = newResourceStart;
} }
@Override @Override