Merge remote-tracking branch 'origin/GP-2559_MaxFloatPrecision'

(Closes #4586, Closes #6708)
This commit is contained in:
Ryan Kurtz 2024-08-16 13:30:13 -04:00
commit 2c3a815163
15 changed files with 602 additions and 57 deletions

View File

@ -23,6 +23,7 @@ src/decompile/datatests/displayformat.xml||GHIDRA||||END|
src/decompile/datatests/divopt.xml||GHIDRA||||END|
src/decompile/datatests/dupptr.xml||GHIDRA||||END|
src/decompile/datatests/elseif.xml||GHIDRA||||END|
src/decompile/datatests/floatcast.xml||GHIDRA||||END|
src/decompile/datatests/floatprint.xml||GHIDRA||||END|
src/decompile/datatests/forloop1.xml||GHIDRA||||END|
src/decompile/datatests/forloop_loaditer.xml||GHIDRA||||END|

View File

@ -4,9 +4,9 @@
* 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.
@ -563,7 +563,10 @@ bool ActionLaneDivide::processVarnode(Funcdata &data,Varnode *vn,const LanedRegi
if (mode < 2)
collectLaneSizes(vn,lanedRegister,checkLanes);
else {
checkLanes.addLaneSize(4); // Default lane size
int4 defaultSize = data.getArch()->types->getSizeOfPointer(); // Default lane size
if (defaultSize != 4)
defaultSize = 8;
checkLanes.addLaneSize(defaultSize);
}
LanedRegister::const_iterator enditer = checkLanes.end();
for(LanedRegister::const_iterator iter=checkLanes.begin();iter!=enditer;++iter) {
@ -582,6 +585,7 @@ bool ActionLaneDivide::processVarnode(Funcdata &data,Varnode *vn,const LanedRegi
int4 ActionLaneDivide::apply(Funcdata &data)
{
data.setLanedRegGenerated();
map<VarnodeData,const LanedRegister *>::const_iterator iter;
for(int4 mode=0;mode<3;++mode) {
bool allStorageProcessed = true;
@ -594,6 +598,10 @@ int4 ActionLaneDivide::apply(Funcdata &data)
bool allVarnodesProcessed = true;
while(viter != venditer) {
Varnode *vn = *viter;
if (vn->hasNoDescend()) {
++viter;
continue;
}
if (processVarnode(data, vn, *lanedReg, mode)) {
viter = data.beginLoc(sz,addr);
venditer = data.endLoc(sz, addr); // Recalculate bounds
@ -610,7 +618,6 @@ int4 ActionLaneDivide::apply(Funcdata &data)
if (allStorageProcessed) break;
}
data.clearLanedAccessMap();
data.setLanedRegGenerated();
return 0;
}
@ -1337,7 +1344,6 @@ int4 ActionVarnodeProps::apply(Funcdata &data)
}
}
}
data.setLanedRegGenerated();
return 0;
}
@ -5515,6 +5521,7 @@ void ActionDatabase::universalAction(Architecture *conf)
actprop->addRule( new RuleOrMultiBool("analysis") );
actprop->addRule( new RuleXorSwap("analysis") );
actprop->addRule( new RuleLzcountShiftBool("analysis") );
actprop->addRule( new RuleFloatSign("analysis") );
actprop->addRule( new RuleSubvarAnd("subvar") );
actprop->addRule( new RuleSubvarSubpiece("subvar") );
actprop->addRule( new RuleSplitFlow("subvar") );
@ -5591,6 +5598,7 @@ void ActionDatabase::universalAction(Architecture *conf)
actcleanup->addRule( new RuleAddUnsigned("cleanup") );
actcleanup->addRule( new Rule2Comp2Sub("cleanup") );
actcleanup->addRule( new RuleSubRight("cleanup") );
actcleanup->addRule( new RuleFloatSignCleanup("cleanup") );
actcleanup->addRule( new RulePtrsubCharConstant("cleanup") );
actcleanup->addRule( new RuleExtensionPush("cleanup") );
actcleanup->addRule( new RulePieceStructure("cleanup") );

View File

@ -5,9 +5,9 @@
* 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.
@ -99,11 +99,11 @@ FloatFormat::floatclass FloatFormat::extractExpSig(double x,bool *sgn,uintb *sig
x = -x;
double norm = frexp(x,&e); // norm is between 1/2 and 1
norm = ldexp(norm,8*sizeof(uintb)-1); // norm between 2^62 and 2^63
*signif = (uintb)norm; // Convert to normalized integer
*signif <<= 1;
e -= 1; // Consider normalization between 1 and 2
e -= 1; // Consider normalization between 1 and 2
*exp = e;
return normalized;
}
@ -217,8 +217,9 @@ uintb FloatFormat::getNaNEncoding(bool sgn) const
void FloatFormat::calcPrecision(void)
{
float val = frac_size * 0.30103;
decimal_precision = (int4)floor(val + 0.5);
decimalMinPrecision = (int4)floor(frac_size * 0.30103);
// Precision needed to guarantee IEEE 754 binary -> decimal -> binary round trip conversion
decimalMaxPrecision = (int4)ceil((frac_size + 1) * 0.30103) + 1;
}
/// \param encoding is the encoding value
@ -417,6 +418,47 @@ uintb FloatFormat::convertEncoding(uintb encoding,
return setSign(res, sgn);
}
/// The string should be printed with the minimum number of digits to uniquely specify the underlying
/// binary value. This currently only works for the 32-bit and 64-bit IEEE 754 formats.
/// If the \b forcesci parameter is \b true, the string will always be printed using scientific notation.
/// \param host is the given value already converted to the host's \b double format.
/// \param forcesci is \b true if the value should be printed in scientific notation.
/// \return the decimal representation as a string
string FloatFormat::printDecimal(double host,bool forcesci) const
{
string res;
for(int4 prec=decimalMinPrecision;;++prec) {
ostringstream s;
if (forcesci) {
s.setf( ios::scientific ); // Set to scientific notation
s.precision(prec-1); // scientific doesn't include first digit in precision count
}
else {
s.unsetf( ios::floatfield ); // Use "default" notation to allow fewer digits to be printed if possible
s.precision(prec);
}
s << host;
if (prec == decimalMaxPrecision) {
return s.str();
}
res = s.str();
double roundtrip = 0.0;
istringstream t(res);
if (size <= 4) {
float tmp = 0.0;
t >> tmp;
roundtrip = tmp;
}
else {
t >> roundtrip;
}
if (roundtrip == host)
break;
}
return res;
}
// Currently we emulate floating point operations on the target
// By converting the encoding to the host's encoding and then
// performing the operation using the host's floating point unit

View File

@ -4,9 +4,9 @@
* 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.
@ -48,7 +48,8 @@ private:
int4 exp_size; ///< Number of bits in exponent
int4 bias; ///< What to add to real exponent to get encoding
int4 maxexponent; ///< Maximum possible exponent
int4 decimal_precision; ///< Number of decimal digits of precision
int4 decimalMinPrecision; ///< Minimum decimal digits of precision guaranteed by the format
int4 decimalMaxPrecision; ///< Maximum decimal digits of precision needed to uniquely represent value
bool jbitimplied; ///< Set to \b true if integer bit of 1 is assumed
static double createFloat(bool sign,uintb signif,int4 exp); ///< Create a double given sign, fractional, and exponent
static floatclass extractExpSig(double x,bool *sgn,uintb *signif,int4 *exp);
@ -65,13 +66,14 @@ public:
int4 getSize(void) const { return size; } ///< Get the size of the encoding in bytes
double getHostFloat(uintb encoding,floatclass *type) const; ///< Convert an encoding into host's double
uintb getEncoding(double host) const; ///< Convert host's double into \b this encoding
int4 getDecimalPrecision(void) const { return decimal_precision; } ///< Get number of digits of precision
uintb convertEncoding(uintb encoding,const FloatFormat *formin) const; ///< Convert between two different formats
uintb extractFractionalCode(uintb x) const; ///< Extract the fractional part of the encoding
bool extractSign(uintb x) const; ///< Extract the sign bit from the encoding
int4 extractExponentCode(uintb x) const; ///< Extract the exponent from the encoding
string printDecimal(double host,bool forcesci) const; ///< Print given value as a decimal string
// Operations on floating point values
uintb opEqual(uintb a,uintb b) const; ///< Equality comparison (==)

View File

@ -4,9 +4,9 @@
* 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.
@ -1388,19 +1388,11 @@ void PrintC::push_float(uintb val,int4 sz,tagtype tag,const Varnode *vn,const Pc
token = "NAN";
}
else {
ostringstream t;
if ((mods & force_scinote)!=0) {
t.setf( ios::scientific ); // Set to scientific notation
t.precision(format->getDecimalPrecision()-1);
t << floatval;
token = t.str();
token = format->printDecimal(floatval, true);
}
else {
// Try to print "minimal" accurate representation of the float
t.unsetf( ios::floatfield ); // Use "default" notation
t.precision(format->getDecimalPrecision());
t << floatval;
token = t.str();
token = format->printDecimal(floatval, false);
bool looksLikeFloat = false;
for(int4 i=0;i<token.size();++i) {
char c = token[i];

View File

@ -4,9 +4,9 @@
* 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.
@ -10453,4 +10453,93 @@ int4 RuleLzcountShiftBool::applyOp(PcodeOp *op,Funcdata &data)
return 0;
}
/// \class RuleFloatSign
/// \brief Convert floating-point \e sign bit manipulation into FLOAT_ABS or FLOAT_NEG
///
/// Transform floating-point specific operations
/// -- `x & 0x7fffffff => ABS(f)`
/// -- 'x ^ 0x80000000 => -f`
///
/// A Varnode is determined to be floating-point by participation in other floating-point operations,
/// not based on the data-type of the Varnode.
void RuleFloatSign::getOpList(vector<uint4> &oplist) const
{
uint4 list[] = { CPUI_FLOAT_EQUAL, CPUI_FLOAT_NOTEQUAL, CPUI_FLOAT_LESS, CPUI_FLOAT_LESSEQUAL, CPUI_FLOAT_NAN,
CPUI_FLOAT_ADD, CPUI_FLOAT_DIV, CPUI_FLOAT_MULT, CPUI_FLOAT_SUB, CPUI_FLOAT_NEG, CPUI_FLOAT_ABS,
CPUI_FLOAT_SQRT, CPUI_FLOAT_FLOAT2FLOAT, CPUI_FLOAT_CEIL, CPUI_FLOAT_FLOOR, CPUI_FLOAT_ROUND,
CPUI_FLOAT_INT2FLOAT, CPUI_FLOAT_TRUNC };
oplist.insert(oplist.end(),list,list+18);
}
int4 RuleFloatSign::applyOp(PcodeOp *op,Funcdata &data)
{
int4 res = 0;
OpCode opc = op->code();
if (opc != CPUI_FLOAT_INT2FLOAT) {
Varnode *vn = op->getIn(0);
if (vn->isWritten()) {
PcodeOp *signOp = vn->getDef();
OpCode resCode = TypeOp::floatSignManipulation(signOp);
if (resCode != CPUI_MAX) {
data.opRemoveInput(signOp, 1);
data.opSetOpcode(signOp, resCode);
res = 1;
}
}
if (op->numInput() == 2) {
vn = op->getIn(1);
if (vn->isWritten()) {
PcodeOp *signOp = vn->getDef();
OpCode resCode = TypeOp::floatSignManipulation(signOp);
if (resCode != CPUI_MAX) {
data.opRemoveInput(signOp, 1);
data.opSetOpcode(signOp, resCode);
res = 1;
}
}
}
}
if (op->isBoolOutput() || opc == CPUI_FLOAT_TRUNC)
return res;
list<PcodeOp *>::const_iterator iter;
Varnode *outvn = op->getOut();
for(iter=outvn->beginDescend();iter!=outvn->endDescend();++iter) {
PcodeOp *readOp = *iter;
OpCode resCode = TypeOp::floatSignManipulation(readOp);
if (resCode != CPUI_MAX) {
data.opRemoveInput(readOp, 1);
data.opSetOpcode(readOp, resCode);
res = 1;
}
}
return res;
}
/// \class RuleFloatSignCleanup
/// \brief Convert floating-point \e sign bit manipulation into FLOAT_ABS or FLOAT_NEG
///
/// A Varnode is determined to be floating-point by examining its data-type.
void RuleFloatSignCleanup::getOpList(vector<uint4> &oplist) const
{
oplist.push_back(CPUI_INT_AND);
oplist.push_back(CPUI_INT_XOR);
}
int4 RuleFloatSignCleanup::applyOp(PcodeOp *op,Funcdata &data)
{
if (op->getOut()->getType()->getMetatype() != TYPE_FLOAT) {
return 0;
}
OpCode opc = TypeOp::floatSignManipulation(op);
if (opc == CPUI_MAX)
return 0;
data.opRemoveInput(op, 1);
data.opSetOpcode(op, opc);
return 1;
}
} // End namespace ghidra

View File

@ -4,9 +4,9 @@
* 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.
@ -1644,5 +1644,27 @@ public:
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
};
class RuleFloatSign : public Rule {
public:
RuleFloatSign(const string &g) : Rule( g, 0, "floatsign") {} ///< Constructor
virtual Rule *clone(const ActionGroupList &grouplist) const {
if (!grouplist.contains(getGroup())) return (Rule *)0;
return new RuleFloatSign(getGroup());
}
virtual void getOpList(vector<uint4> &oplist) const;
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
};
class RuleFloatSignCleanup : public Rule {
public:
RuleFloatSignCleanup(const string &g) : Rule( g, 0, "floatsigncleanup") {} ///< Constructor
virtual Rule *clone(const ActionGroupList &grouplist) const {
if (!grouplist.contains(getGroup())) return (Rule *)0;
return new RuleFloatSignCleanup(getGroup());
}
virtual void getOpList(vector<uint4> &oplist) const;
virtual int4 applyOp(PcodeOp *op,Funcdata &data);
};
} // End namespace ghidra
#endif

View File

@ -4,9 +4,9 @@
* 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.
@ -34,10 +34,13 @@ AttributeId ATTRIB_PIECE = AttributeId("piece",94); // Open slots 94-102
void AddrSpace::calcScaleMask(void)
{
pointerLowerBound = (addressSize < 3) ? 0x100: 0x1000;
highest = calc_mask(addressSize); // Maximum address
highest = highest * wordsize + (wordsize-1); // Maximum byte address
pointerLowerBound = 0;
pointerUpperBound = highest;
uintb bufferSize = (addressSize < 3) ? 0x100 : 0x1000;
pointerLowerBound += bufferSize;
pointerUpperBound -= bufferSize;
}
/// Initialize an address space with its basic attributes

View File

@ -2567,7 +2567,133 @@ Datatype *SplitDatatype::getValueDatatype(PcodeOp *loadStore,int4 size,TypeFacto
}
else if (metain == TYPE_STRUCT || metain == TYPE_ARRAY)
return tlst->getExactPiece(resType, baseOffset, size);
return (Datatype *)0;}
return (Datatype *)0;
}
/// This method distinguishes between a floating-point variable with \e full precision, where all the
/// storage can vary (or is unknown), versus a value that is extended from a floating-point variable with
/// smaller storage. Within the data-flow above the given Varnode, we search for the maximum
/// precision coming through MULTIEQUAL, COPY, and unary floating-point operations. Binary operations
/// like FLOAT_ADD and FLOAT_MULT are not traversed and are assumed to produce a smaller precision.
/// If the method indicates \e full precision for the given Varnode, or if the data-flow does not involve
/// binary floating-point operations, it is accurate, otherwise it may under report the precision.
/// \param vn is the given Varnode
/// \return an approximation of the maximum precision
int4 SubfloatFlow::maxPrecision(Varnode *vn)
{
if (!vn->isWritten())
return vn->getSize();
PcodeOp *op = vn->getDef();
switch(op->code()) {
case CPUI_MULTIEQUAL:
case CPUI_FLOAT_NEG:
case CPUI_FLOAT_ABS:
case CPUI_FLOAT_SQRT:
case CPUI_FLOAT_CEIL:
case CPUI_FLOAT_FLOOR:
case CPUI_FLOAT_ROUND:
case CPUI_COPY:
break;
case CPUI_FLOAT_ADD:
case CPUI_FLOAT_SUB:
case CPUI_FLOAT_MULT:
case CPUI_FLOAT_DIV:
return 0; // Delay checking other binary ops
case CPUI_FLOAT_FLOAT2FLOAT:
case CPUI_FLOAT_INT2FLOAT: // Treat integer as having precision matching its size
if (op->getIn(0)->getSize() > vn->getSize())
return vn->getSize();
return op->getIn(0)->getSize();
default:
return vn->getSize();
}
map<PcodeOp *,int4>::const_iterator iter = maxPrecisionMap.find(op);
if (iter != maxPrecisionMap.end()) {
return (*iter).second;
}
vector<State> opStack;
opStack.emplace_back(op);
op->setMark();
int4 max = 0;
while(!opStack.empty()) {
State &state(opStack.back());
if (state.slot >= state.op->numInput()) {
max = state.maxPrecision;
state.op->clearMark();
maxPrecisionMap[state.op] = state.maxPrecision;
opStack.pop_back();
if (!opStack.empty()) {
opStack.back().incorporateInputSize(max);
}
continue;
}
Varnode *nextVn = state.op->getIn(state.slot);
state.slot += 1;
if (!nextVn->isWritten()) {
state.incorporateInputSize(nextVn->getSize());
continue;
}
PcodeOp *nextOp = nextVn->getDef();
if (nextOp->isMark()) {
continue; // Truncate the cycle edge
}
switch(nextOp->code()) {
case CPUI_MULTIEQUAL:
case CPUI_FLOAT_NEG:
case CPUI_FLOAT_ABS:
case CPUI_FLOAT_SQRT:
case CPUI_FLOAT_CEIL:
case CPUI_FLOAT_FLOOR:
case CPUI_FLOAT_ROUND:
case CPUI_COPY:
iter = maxPrecisionMap.find(nextOp);
if (iter != maxPrecisionMap.end()) {
// Seen the op before, incorporate its cached precision information
state.incorporateInputSize((*iter).second);
break;
}
nextOp->setMark();
opStack.emplace_back(nextOp); // Recursively push into the new op
break;
case CPUI_FLOAT_ADD:
case CPUI_FLOAT_SUB:
case CPUI_FLOAT_MULT:
case CPUI_FLOAT_DIV:
break;
case CPUI_FLOAT_FLOAT2FLOAT:
case CPUI_FLOAT_INT2FLOAT: // Treat integer as having precision matching its size
if (nextOp->getIn(0)->getSize() > nextVn->getSize())
state.incorporateInputSize(nextVn->getSize());
else
state.incorporateInputSize(nextOp->getIn(0)->getSize());
break;
default:
state.incorporateInputSize(nextVn->getSize());
break;
}
}
return max;
}
/// This is called only for binary floating-point ops: FLOAT_ADD, FLOAT_MULT, FLOAT_LESS, etc.
/// If the maximum precision reaching both input operands exceeds the \b precision established
/// for \b this Rule, \b true is returned, indicating the op cannot be truncated without losing precision.
/// We count on the fact that this test is applied to all binary operations encountered during Rule application.
/// This method will correctly return \b true for the earliest operations whose inputs both exceed the
/// \b precision, but, because of the way maxPrecision() is calculated, it may incorrectly return \b false
/// for later operations.
/// \param op is the given binary floating-point PcodeOp
/// \return \b true if both input operands exceed the established \b precision
bool SubfloatFlow::exceedsPrecision(PcodeOp *op)
{
int4 val1 = maxPrecision(op->getIn(0));
int4 val2 = maxPrecision(op->getIn(1));
int4 min = (val1 < val2) ? val1 : val2;
return (min > precision);
}
/// \brief Create and return a placeholder associated with the given Varnode
///
@ -2636,6 +2762,14 @@ bool SubfloatFlow::traceForward(TransformVar *rvn)
if ((outvn!=(Varnode *)0)&&(outvn->isMark()))
continue;
switch(op->code()) {
case CPUI_FLOAT_ADD:
case CPUI_FLOAT_SUB:
case CPUI_FLOAT_MULT:
case CPUI_FLOAT_DIV:
if (exceedsPrecision(op))
return false;
// fall through
case CPUI_MULTIEQUAL:
case CPUI_COPY:
case CPUI_FLOAT_CEIL:
case CPUI_FLOAT_FLOOR:
@ -2643,11 +2777,6 @@ bool SubfloatFlow::traceForward(TransformVar *rvn)
case CPUI_FLOAT_NEG:
case CPUI_FLOAT_ABS:
case CPUI_FLOAT_SQRT:
case CPUI_FLOAT_ADD:
case CPUI_FLOAT_SUB:
case CPUI_FLOAT_MULT:
case CPUI_FLOAT_DIV:
case CPUI_MULTIEQUAL:
{
TransformOp *rop = newOpReplace(op->numInput(), op->code(), op);
TransformVar *outrvn = setReplacement(outvn);
@ -2670,6 +2799,8 @@ bool SubfloatFlow::traceForward(TransformVar *rvn)
case CPUI_FLOAT_LESS:
case CPUI_FLOAT_LESSEQUAL:
{
if (exceedsPrecision(op))
return false;
int4 slot = op->getSlot(vn);
TransformVar *rvn2 = setReplacement(op->getIn(1-slot));
if (rvn2 == (TransformVar *)0) return false;
@ -2715,6 +2846,13 @@ bool SubfloatFlow::traceBackward(TransformVar *rvn)
if (op == (PcodeOp *)0) return true; // If vn is input
switch(op->code()) {
case CPUI_FLOAT_ADD:
case CPUI_FLOAT_SUB:
case CPUI_FLOAT_MULT:
case CPUI_FLOAT_DIV:
if (exceedsPrecision(op))
return false;
// fallthru
case CPUI_COPY:
case CPUI_FLOAT_CEIL:
case CPUI_FLOAT_FLOOR:
@ -2722,10 +2860,6 @@ bool SubfloatFlow::traceBackward(TransformVar *rvn)
case CPUI_FLOAT_NEG:
case CPUI_FLOAT_ABS:
case CPUI_FLOAT_SQRT:
case CPUI_FLOAT_ADD:
case CPUI_FLOAT_SUB:
case CPUI_FLOAT_MULT:
case CPUI_FLOAT_DIV:
case CPUI_MULTIEQUAL:
{
TransformOp *rop = rvn->getDef();
@ -3013,6 +3147,29 @@ bool LaneDivide::buildMultiequal(PcodeOp *op,TransformVar *outVars,int4 numLanes
return true;
}
/// \brief Split a given CPUI_INDIRECT operation into placeholders given the output lanes
///
/// Create the CPUI_INDIRECTs for each lane, sharing the same affecting \e iop.
/// \param op is the original CPUI_MULTIEQUAL PcodeOp
/// \param outVars is the placeholder variables making up the lanes of the output
/// \param numLanes is the number of lanes in the output
/// \param skipLanes is the index of the least significant output lane within the global description
/// \return \b true if the operation was fully modeled
bool LaneDivide::buildIndirect(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes)
{
TransformVar *inVn = setReplacement(op->getIn(0), numLanes, skipLanes);
if (inVn == (TransformVar *)0) return false;
for(int4 i=0;i<numLanes;++i) {
TransformOp *rop = newOpReplace(2, CPUI_INDIRECT, op);
opSetOutput(rop, outVars + i);
opSetInput(rop,inVn + i, 0);
opSetInput(rop,newIop(op->getIn(1)),1);
rop->inheritIndirect(op);
}
return true;
}
/// \brief Split a given CPUI_STORE operation into a sequence of STOREs of individual lanes
///
/// A new pointer is constructed for each individual lane into a temporary, then a
@ -3117,7 +3274,7 @@ bool LaneDivide::buildLoad(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4
/// \param op is the given CPUI_INT_RIGHT PcodeOp
/// \param outVars is the output placeholders for the RIGHT shift
/// \param numLanes is the number of lanes the shift is split into
/// \param skipLanes is the starting lane (within the global description) of the value being loaded
/// \param skipLanes is the starting lane (within the global description) of the output value
/// \return \b true if the CPUI_INT_RIGHT was successfully modeled on lanes
bool LaneDivide::buildRightShift(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes)
@ -3147,6 +3304,85 @@ bool LaneDivide::buildRightShift(PcodeOp *op,TransformVar *outVars,int4 numLanes
return true;
}
/// \brief Check that a CPUI_INT_LEFT respects the lanes then generate lane placeholders
///
/// For the given lane scheme, check that the LEFT shift is copying whole lanes to each other.
/// If so, generate the placeholder COPYs that model the shift.
/// \param op is the given CPUI_INT_LEFT PcodeOp
/// \param outVars is the output placeholders for the LEFT shift
/// \param numLanes is the number of lanes the shift is split into
/// \param skipLanes is the starting lane (within the global description) of the output value
/// \return \b true if the CPUI_INT_RIGHT was successfully modeled on lanes
bool LaneDivide::buildLeftShift(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes)
{
if (!op->getIn(1)->isConstant()) return false;
int4 shiftSize = (int4)op->getIn(1)->getOffset();
if ((shiftSize & 7) != 0) return false; // Not a multiple of 8
shiftSize /= 8;
int4 startPos = shiftSize + description.getPosition(skipLanes);
int4 startLane = description.getBoundary(startPos);
if (startLane < 0) return false; // Shift does not end on a lane boundary
int4 destLane = startLane;
int4 srcLane = skipLanes;
while(destLane - skipLanes < numLanes) {
if (description.getSize(srcLane) != description.getSize(destLane)) return false;
srcLane += 1;
destLane += 1;
}
TransformVar *inVars = setReplacement(op->getIn(0), numLanes, skipLanes);
if (inVars == (TransformVar *)0) return false;
for(int4 zeroLane=0;zeroLane < (startLane - skipLanes);++zeroLane) {
TransformOp *rop = newOpReplace(1, CPUI_COPY, op);
opSetOutput(rop,outVars + zeroLane);
opSetInput(rop,newConstant(description.getSize(zeroLane), 0, 0),0);
}
buildUnaryOp(CPUI_COPY, op, inVars, outVars + (startLane - skipLanes), numLanes - (startLane - skipLanes));
return true;
}
/// \brief Split a CPUI_INT_ZEXT into COPYs of lanes and COPYs of zero into lanes
///
/// If the input to the INT_ZEXT matches the lane boundaries. Placeholder COPYs are generated from
/// the input Varnode to the least significant lanes. Additional COPYs are generated which place a zero
/// in the remaining most significant lanes.
/// \param op is the given CPUI_INT_ZEXT PcodeOp
/// \param outVars is the output placeholders for the extension
/// \param numLanes is the number of lanes the extension is split into
/// \param skipLanes is the starting lane (within the global description) of the output of the extension
/// \return \b true if the CPUI_INT_ZEXT was successfully modeled on lanes
bool LaneDivide::buildZext(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes)
{
int4 inLanes,inSkip;
Varnode *invn = op->getIn(0);
if (!description.restriction(numLanes, skipLanes, 0, invn->getSize(), inLanes, inSkip)) {
return false;
}
// inSkip should always come back as equal to skipLanes
if (inLanes == 1) {
TransformOp *rop = newOpReplace(1, CPUI_COPY, op);
TransformVar *inVar = getPreexistingVarnode(invn);
opSetInput(rop,inVar,0);
opSetOutput(rop,outVars);
}
else {
TransformVar *inRvn = setReplacement(invn,inLanes,inSkip);
if (inRvn == (TransformVar *)0) return false;
for(int4 i=0;i<inLanes;++i) {
TransformOp *rop = newOpReplace(1, CPUI_COPY, op);
opSetInput(rop,inRvn+i,0);
opSetOutput(rop,outVars + i);
}
}
for(int4 i=0;i<numLanes-inLanes;++i) { // Write 0 constants to remaining lanes
TransformOp *rop = newOpReplace(1, CPUI_COPY, op);
opSetInput(rop,newConstant(description.getSize(skipLanes + inLanes + i), 0, 0),0);
opSetOutput(rop,outVars + inLanes + i);
}
return true;
}
/// \brief Push the logical lanes forward through any PcodeOp reading the given variable
///
/// Determine if the logical lanes can be pushed forward naturally, and create placeholder
@ -3216,6 +3452,7 @@ bool LaneDivide::traceForward(TransformVar *rvn,int4 numLanes,int4 skipLanes)
case CPUI_INT_OR:
case CPUI_INT_XOR:
case CPUI_MULTIEQUAL:
case CPUI_INDIRECT:
{
TransformVar *outRvn = setReplacement(outvn,numLanes,skipLanes);
if (outRvn == (TransformVar *)0) return false;
@ -3281,6 +3518,10 @@ bool LaneDivide::traceBackward(TransformVar *rvn,int4 numLanes,int4 skipLanes)
if (!buildMultiequal(op, rvn, numLanes, skipLanes))
return false;
break;
case CPUI_INDIRECT:
if (!buildIndirect(op, rvn, numLanes, skipLanes))
return false;
break;
case CPUI_SUBPIECE:
{
Varnode *inVn = op->getIn(0);
@ -3305,6 +3546,14 @@ bool LaneDivide::traceBackward(TransformVar *rvn,int4 numLanes,int4 skipLanes)
if (!buildRightShift(op, rvn, numLanes, skipLanes))
return false;
break;
case CPUI_INT_LEFT:
if (!buildLeftShift(op, rvn, numLanes, skipLanes))
return false;
break;
case CPUI_INT_ZEXT:
if (!buildZext(op, rvn, numLanes, skipLanes))
return false;
break;
default:
return false;
}

View File

@ -4,9 +4,9 @@
* 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.
@ -211,10 +211,24 @@ public:
/// and then rewrites the data-flow in terms of the lower precision, eliminating the
/// precision conversions.
class SubfloatFlow : public TransformManager {
/// \brief Internal state for walking floating-point data-flow and computing precision
class State {
public:
PcodeOp *op; ///< Operation being traversed
int4 slot; ///< Input edge being traversed
int4 maxPrecision; ///< Maximum precision traversed through inputs so far
State(PcodeOp *o) {
op = o; slot = 0; maxPrecision = 0; } ///< Constructor
/// \brief Accumulate precision coming in from an input Varnode to \b this node
void incorporateInputSize(int4 sz) { maxPrecision = (maxPrecision < sz) ? sz : maxPrecision; }
};
int4 precision; ///< Number of bytes of precision in the logical flow
int4 terminatorCount; ///< Number of terminating nodes reachable via the root
const FloatFormat *format; ///< The floating-point format of the logical value
vector<TransformVar *> worklist; ///< Current list of placeholders that still need to be traced
map<PcodeOp *,int4> maxPrecisionMap; ///< Maximum precision flowing into a particular floating-point op
int4 maxPrecision(Varnode *vn); ///< Calculate maximum floating-point precision reaching a given Varnode
bool exceedsPrecision(PcodeOp *op); ///< Determine if the given op exceeds our \b precision
TransformVar *setReplacement(Varnode *vn);
bool traceForward(TransformVar *rvn);
bool traceBackward(TransformVar *rvn);
@ -249,9 +263,12 @@ class LaneDivide : public TransformManager {
void buildBinaryOp(OpCode opc,PcodeOp *op,TransformVar *in0Vars,TransformVar *in1Vars,TransformVar *outVars,int4 numLanes);
bool buildPiece(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes);
bool buildMultiequal(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes);
bool buildIndirect(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes);
bool buildStore(PcodeOp *op,int4 numLanes,int4 skipLanes);
bool buildLoad(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes);
bool buildRightShift(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes);
bool buildLeftShift(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes);
bool buildZext(PcodeOp *op,TransformVar *outVars,int4 numLanes,int4 skipLanes);
bool traceForward(TransformVar *rvn,int4 numLanes,int4 skipLanes);
bool traceBackward(TransformVar *rvn,int4 numLanes,int4 skipLanes);
bool processNextWork(void); ///< Process the next Varnode on the work list

View File

@ -4,9 +4,9 @@
* 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.
@ -146,6 +146,35 @@ void TypeOp::selectJavaOperators(vector<TypeOp *> &inst,bool val)
}
}
/// Return CPUI_FLOAT_NEG if the \e sign bit is flipped, or CPUI_FLOAT_ABS if the \e sign bit is zeroed out.
/// Otherwise CPUI_MAX is returned.
/// \param op is the given PcodeOp to test
/// \return the floating-point operation the PcodeOp is equivalent to, or CPUI_MAX
OpCode TypeOp::floatSignManipulation(PcodeOp *op)
{
OpCode opc = op->code();
if (opc == CPUI_INT_AND) {
Varnode *cvn = op->getIn(1);
if (cvn->isConstant()) {
uintb val = calc_mask(cvn->getSize());
val >>= 1;
if (val == cvn->getOffset())
return CPUI_FLOAT_ABS;
}
}
else if (opc == CPUI_INT_XOR) {
Varnode *cvn = op->getIn(1);
if (cvn->isConstant()) {
uintb val = calc_mask(cvn->getSize());
val = val ^ (val >> 1);
if (val == cvn->getOffset())
return CPUI_FLOAT_NEG;
}
}
return CPUI_MAX;
}
/// \param t is the TypeFactory used to construct data-types
/// \param opc is the op-code value the new object will represent
/// \param n is the display name that will represent the op-code
@ -1333,7 +1362,12 @@ Datatype *TypeOpIntXor::getOutputToken(const PcodeOp *op,CastStrategy *castStrat
Datatype *TypeOpIntXor::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,Varnode *outvn,
int4 inslot,int4 outslot)
{
if (!alttype->isPowerOfTwo()) return (Datatype *)0; // Only propagate flag enums
if (!alttype->isPowerOfTwo()) {
if (alttype->getMetatype() != TYPE_FLOAT)
return (Datatype *)0;
if (floatSignManipulation(op) == CPUI_MAX)
return (Datatype *)0;
}
Datatype *newtype;
if (invn->isSpacebase()) {
AddrSpace *spc = tlst->getArch()->getDefaultDataSpace();
@ -1361,7 +1395,12 @@ Datatype *TypeOpIntAnd::getOutputToken(const PcodeOp *op,CastStrategy *castStrat
Datatype *TypeOpIntAnd::propagateType(Datatype *alttype,PcodeOp *op,Varnode *invn,Varnode *outvn,
int4 inslot,int4 outslot)
{
if (!alttype->isPowerOfTwo()) return (Datatype *)0; // Only propagate flag enums
if (!alttype->isPowerOfTwo()) {
if (alttype->getMetatype() != TYPE_FLOAT)
return (Datatype *)0;
if (floatSignManipulation(op) == CPUI_MAX)
return (Datatype *)0;
}
Datatype *newtype;
if (invn->isSpacebase()) {
AddrSpace *spc = tlst->getArch()->getDefaultDataSpace();

View File

@ -4,9 +4,9 @@
* 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.
@ -177,6 +177,9 @@ public:
/// \brief Toggle Java specific aspects of the op-code information
static void selectJavaOperators(vector<TypeOp *> &inst,bool val);
/// \brief Return the floating-point operation associated with the \e sign bit manipulation by the given PcodeOp
static OpCode floatSignManipulation(PcodeOp *op);
};
// Major classes of operations

View File

@ -0,0 +1,49 @@
<decompilertest>
<binaryimage arch="x86:LE:64:default:gcc">
<bytechunk space="ram" offset="0x100000" readonly="true">
f30f1efa0f28d0f20f10057100000083
ff0a7408660fefc0f30f5ac2f20f1015
6400000083fe0a7408660fefd2f30f5a
d1f20f5cc2f20f5ac0c3
</bytechunk>
<bytechunk space="ram" offset="0x10003a" readonly="true">
f30f1efaf30f
5ac983ff14741ff30f5ac0660f57054d
000000660f540d55000000660f2fc80f
97c00fb6c0c3f20f10052200000083fe
1475d8f20f100d1d000000ebce
</bytechunk>
<bytechunk space="ram" offset="0x100080" readonly="true">
17f25dd1adf9f13f891c09d1adf9f13f
</bytechunk>
<bytechunk space="ram" offset="0x100090" readonly="true">
dbccdd45cac0234033f68845cac02340
00000000000000800000000000000000
ffffffffffffff7f0000000000000000
</bytechunk>
<symbol space="ram" offset="0x100000" name="prec_conditions"/>
<symbol space="ram" offset="0x10003a" name="prec_comparison"/>
</binaryimage>
<script>
<com>option readonly on</com>
<com>parse line extern float4 prec_conditions(float4 a,float4 b,int4 cond1,int4 cond2);</com>
<com>parse line extern int4 prec_comparison(float4 c,float4 d,int4 cond1,int4 cond2);</com>
<com>lo fu prec_conditions</com>
<com>dec</com>
<com>print C</com>
<com>lo fu prec_comparison</com>
<com>dec</com>
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Floating-point cast #1" min="1" max="1">fVar1 = \(float8\)a;</stringmatch>
<stringmatch name="Floating-point cast #2" min="1" max="1">fVar2 = \(float8\)b;</stringmatch>
<stringmatch name="Floating-point cast #3" min="1" max="1">fVar1 = 1\.1234567812345;</stringmatch>
<stringmatch name="Floating-point cast #4" min="1" max="1">fVar2 = 1\.12345678;</stringmatch>
<stringmatch name="Floating-point cast #5" min="1" max="1">return \(float4\)\(fVar1 \- fVar2\);</stringmatch>
<stringmatch name="Floating-point cast #6" min="1" max="1">fVar1 = \(float8\)c;</stringmatch>
<stringmatch name="Floating-point cast #7" min="1" max="1">fVar2 = \(float8\)d;</stringmatch>
<stringmatch name="Floating-point cast #8" min="1" max="1">fVar1 = 9\.8765432198765;</stringmatch>
<stringmatch name="Floating-point cast #9" min="1" max="1">fVar2 = 9\.87654321;</stringmatch>
<stringmatch name="Floating-point cast #10" min="1" max="1">return.*\-fVar1 &lt; ABS\(fVar2\)</stringmatch>
</decompilertest>

View File

@ -53,7 +53,7 @@ bbbdd7d9df7cdb3d000000000000f03f
<com>print C</com>
<com>quit</com>
</script>
<stringmatch name="Float print #1" min="1" max="1">floatv1 = 0.3333333;</stringmatch>
<stringmatch name="Float print #1" min="1" max="1">floatv1 = 0.33333334;</stringmatch>
<stringmatch name="Float print #2" min="1" max="1">floatv2 = 2.0;</stringmatch>
<stringmatch name="Float print #3" min="1" max="1">floatv3 = -0.001;</stringmatch>
<stringmatch name="Float print #4" min="1" max="1">floatv4 = 1e-06;</stringmatch>
@ -66,5 +66,5 @@ bbbdd7d9df7cdb3d000000000000f03f
<stringmatch name="Float print #11" min="1" max="1">double4 = 1e-10;</stringmatch>
<stringmatch name="Float print #12" min="1" max="1">double5 = INFINITY;</stringmatch>
<stringmatch name="Float print #13" min="1" max="1">double6 = -NAN;</stringmatch>
<stringmatch name="Float print #14" min="1" max="1">double7 = 3.141592653589793e-06;</stringmatch>
<stringmatch name="Float print #14" min="1" max="1">double7 = 3.1415926535897933e-06;</stringmatch>
</decompilertest>

View File

@ -4,9 +4,9 @@
* 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.
@ -165,6 +165,35 @@ TEST(double_encoding_infinity) {
ASSERT_DOUBLE_ENCODING(-std::numeric_limits<double>::infinity());
}
TEST(float_decimal_precision) {
FloatFormat ff(4);
float f0 = floatFromRawBits(0x34000001);
ASSERT_EQUALS(ff.printDecimal(f0, false), "1.192093e-07")
float f1 = floatFromRawBits(0x34800000);
ASSERT_EQUALS(ff.printDecimal(f1, false), "2.3841858e-07")
float f2 = floatFromRawBits(0x3eaaaaab);
ASSERT_EQUALS(ff.printDecimal(f2, false), "0.33333334")
float f3 = floatFromRawBits(0x3e800000);
ASSERT_EQUALS(ff.printDecimal(f3, false), "0.25");
float f4 = floatFromRawBits(0x3de3ee46);
ASSERT_EQUALS(ff.printDecimal(f4, false), "0.111294314")
}
TEST(double_decimal_precision) {
FloatFormat ff(8);
double f0 = doubleFromRawBits(0x3fc5555555555555);
ASSERT_EQUALS(ff.printDecimal(f0, false), "0.16666666666666666");
double f1 = doubleFromRawBits(0x7fefffffffffffff);
ASSERT_EQUALS(ff.printDecimal(f1, false), "1.79769313486232e+308");
double f2 = doubleFromRawBits(0x3fd555555c7dda4b);
ASSERT_EQUALS(ff.printDecimal(f2, false), "0.33333334");
double f3 = doubleFromRawBits(0x3fd0000000000000);
ASSERT_EQUALS(ff.printDecimal(f3, false), "0.25");
double f4 = doubleFromRawBits(0x3fb999999999999a);
ASSERT_EQUALS(ff.printDecimal(f4, false), "0.1");
double f5 = doubleFromRawBits(0x3fbf7ced916872b0);
ASSERT_EQUALS(ff.printDecimal(f5, true), "1.23000000000000e-01");}
TEST(float_midpoint_rounding) {
FloatFormat ff(4);
// IEEE754 recommends "round to nearest even" for binary formats, like single and double