mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
Merge remote-tracking branch 'origin/patch'
This commit is contained in:
commit
eda9127c26
36 changed files with 3492 additions and 1126 deletions
|
@ -7,9 +7,11 @@ decomp_dbg
|
|||
decomp_opt
|
||||
ghidra_dbg
|
||||
ghidra_opt
|
||||
ghidra_test_dbg
|
||||
sleigh_dbg
|
||||
com_dbg
|
||||
com_opt
|
||||
test_dbg
|
||||
ghi_dbg
|
||||
ghi_opt
|
||||
sla_dbg
|
||||
|
|
|
@ -91,11 +91,11 @@ GHIDRA= ghidra_arch inject_ghidra ghidra_translate loadimage_ghidra \
|
|||
# Additional files specific to the sleigh compiler
|
||||
SLACOMP=slgh_compile slghparse slghscan
|
||||
# Additional special files that should not be considered part of the library
|
||||
SPECIAL=consolemain sleighexample
|
||||
SPECIAL=consolemain sleighexample test
|
||||
# Any additional modules for the command line decompiler
|
||||
EXTRA= $(filter-out $(CORE) $(DECCORE) $(SLEIGH) $(GHIDRA) $(SLACOMP) $(SPECIAL),$(ALL_NAMES))
|
||||
|
||||
EXECS=decomp_dbg decomp_opt ghidra_dbg ghidra_opt sleigh_dbg sleigh_opt libdecomp_dbg.a libdecomp.a
|
||||
EXECS=decomp_dbg decomp_opt ghidra_test_dbg ghidra_dbg ghidra_opt sleigh_dbg sleigh_opt libdecomp_dbg.a libdecomp.a
|
||||
|
||||
# Possible conditional compilation flags
|
||||
# __TERMINAL__ # Turn on terminal support for console mode
|
||||
|
@ -114,6 +114,9 @@ COMMANDLINE_NAMES=$(CORE) $(DECCORE) $(EXTRA) $(SLEIGH) consolemain
|
|||
COMMANDLINE_DEBUG=-DCPUI_DEBUG -D__TERMINAL__
|
||||
COMMANDLINE_OPT=-D__TERMINAL__
|
||||
|
||||
TEST_NAMES=$(CORE) $(DECCORE) $(SLEIGH) test
|
||||
TEST_DEBUG=-D__TERMINAL__ -g -O0
|
||||
|
||||
GHIDRA_NAMES=$(CORE) $(DECCORE) $(GHIDRA)
|
||||
GHIDRA_NAMES_DBG=$(GHIDRA_NAMES) callgraph ifacedecomp ifaceterm interface
|
||||
GHIDRA_DEBUG=-DCPUI_DEBUG
|
||||
|
@ -133,6 +136,7 @@ LIBDECOMP_NAMES=$(CORE) $(DECCORE) $(EXTRA) $(SLEIGH)
|
|||
# object file macros
|
||||
COMMANDLINE_DBG_OBJS=$(COMMANDLINE_NAMES:%=com_dbg/%.o)
|
||||
COMMANDLINE_OPT_OBJS=$(COMMANDLINE_NAMES:%=com_opt/%.o)
|
||||
TEST_DEBUG_OBJS=$(TEST_NAMES:%=test_dbg/%.o)
|
||||
GHIDRA_DBG_OBJS=$(GHIDRA_NAMES_DBG:%=ghi_dbg/%.o)
|
||||
GHIDRA_OPT_OBJS=$(GHIDRA_NAMES:%=ghi_opt/%.o)
|
||||
SLEIGH_DBG_OBJS=$(SLEIGH_NAMES:%=sla_dbg/%.o)
|
||||
|
@ -183,6 +187,9 @@ endif
|
|||
ifeq ($(MAKECMDGOALS),decomp_opt)
|
||||
DEPNAMES=com_opt/depend
|
||||
endif
|
||||
ifneq (,$(filter $(MAKECMDGOALS),ghidra_test_dbg test))
|
||||
DEPNAMES=test_dbg/depend
|
||||
endif
|
||||
ifeq ($(MAKECMDGOALS),reallyclean)
|
||||
DEPNAMES=
|
||||
endif
|
||||
|
@ -206,6 +213,8 @@ com_dbg/%.o: %.cc
|
|||
$(CXX) $(ARCH_TYPE) -c $(DBG_CXXFLAGS) $(ADDITIONAL_FLAGS) $(COMMANDLINE_DEBUG) $< -o $@
|
||||
com_opt/%.o: %.cc
|
||||
$(CXX) $(ARCH_TYPE) -c $(OPT_CXXFLAGS) $(ADDITIONAL_FLAGS) $(COMMANDLINE_OPT) $< -o $@
|
||||
test_dbg/%.o: %.cc
|
||||
$(CXX) $(ARCH_TYPE) -c $(OPT_CXXFLAGS) $(ADDITIONAL_FLAGS) $(TEST_DEBUG) $< -o $@
|
||||
ghi_dbg/%.o: %.cc
|
||||
$(CXX) $(ARCH_TYPE) -c $(DBG_CXXFLAGS) $(ADDITIONAL_FLAGS) $(GHIDRA_DEBUG) $< -o $@
|
||||
ghi_opt/%.o: %.cc
|
||||
|
@ -238,6 +247,12 @@ decomp_dbg: $(COMMANDLINE_DBG_OBJS)
|
|||
decomp_opt: $(COMMANDLINE_OPT_OBJS)
|
||||
$(CXX) $(OPT_CXXFLAGS) $(ARCH_TYPE) -o decomp_opt $(COMMANDLINE_OPT_OBJS) $(BFDLIB) $(LNK)
|
||||
|
||||
ghidra_test_dbg: $(TEST_DEBUG_OBJS)
|
||||
$(CXX) $(OPT_CXXFLAGS) $(ARCH_TYPE) -o ghidra_test_dbg $(TEST_DEBUG_OBJS) $(BFDLIB) $(LNK)
|
||||
|
||||
test: ghidra_test_dbg
|
||||
./ghidra_test_dbg
|
||||
|
||||
ghidra_dbg: $(GHIDRA_DBG_OBJS)
|
||||
$(CXX) $(DBG_CXXFLAGS) $(ADDITIONAL_FLAGS) $(MAKE_STATIC) $(ARCH_TYPE) -o ghidra_dbg $(GHIDRA_DBG_OBJS) $(LNK)
|
||||
|
||||
|
@ -324,6 +339,13 @@ com_opt/depend: $(COMMANDLINE_NAMES:%=%.cc)
|
|||
sed 's,\(.*\)\.o[ :]*,com_opt/\1.o $@ : ,g' < $@.$$$$ > $@; \
|
||||
rm -f $@.$$$$
|
||||
|
||||
test_dbg/depend: $(TEST_NAMES:%=%.cc)
|
||||
mkdir -p test_dbg
|
||||
@set -e; rm -f $@; \
|
||||
$(CXX) -MM $(TEST_DEBUG) $^ > $@.$$$$; \
|
||||
sed 's,\(.*\)\.o[ :]*,test_dbg/\1.o $@ : ,g' < $@.$$$$ > $@; \
|
||||
rm -f $@.$$$$
|
||||
|
||||
ghi_dbg/depend: $(GHIDRA_NAMES_DBG:%=%.cc)
|
||||
mkdir -p ghi_dbg
|
||||
@set -e; rm -f $@; \
|
||||
|
@ -372,7 +394,7 @@ doc:
|
|||
doxygen Doxyfile
|
||||
|
||||
clean:
|
||||
rm -f com_dbg/*.o com_opt/*.o ghi_dbg/*.o ghi_opt/*.o sla_dbg/*.o sla_opt/*.o
|
||||
rm -f com_dbg/*.o com_opt/*.o test_dbg/*.o ghi_dbg/*.o ghi_opt/*.o sla_dbg/*.o sla_opt/*.o
|
||||
rm -f *.gcov com_dbg/*.gcno com_dbg/*.gcda
|
||||
|
||||
resetgcov:
|
||||
|
@ -380,6 +402,6 @@ resetgcov:
|
|||
|
||||
reallyclean: clean
|
||||
rm -rf coreext_*.cc coreext_*.hh ghidraext_*.cc ghidraext_*.hh consoleext_*.cc consoleext_*.hh
|
||||
rm -rf com_dbg com_opt ghi_dbg ghi_opt sla_dbg sla_opt
|
||||
rm -rf com_dbg com_opt test_dbg ghi_dbg ghi_opt sla_dbg sla_opt
|
||||
rm -f $(EXECS) TAGS *~
|
||||
|
||||
|
|
|
@ -223,7 +223,6 @@ double FloatFormat::getHostFloat(uintb encoding,floatclass *type) const
|
|||
if (exp == 0) {
|
||||
if ( frac == 0 ) { // Floating point zero
|
||||
*type = zero;
|
||||
// FIXME: add on sign-bit for +0 or -0 allowed by standard
|
||||
return sgn ? -0.0 : +0.0;
|
||||
}
|
||||
*type = denormalized;
|
||||
|
@ -233,7 +232,6 @@ double FloatFormat::getHostFloat(uintb encoding,floatclass *type) const
|
|||
else if (exp == maxexponent) {
|
||||
if ( frac == 0 ) { // Floating point infinity
|
||||
*type = infinity;
|
||||
// FIXME: add on sign-bit for +inf or -inf allowed by standard
|
||||
return sgn ? -INFINITY : +INFINITY;
|
||||
}
|
||||
*type = nan;
|
||||
|
@ -254,6 +252,27 @@ double FloatFormat::getHostFloat(uintb encoding,floatclass *type) const
|
|||
return createFloat(sgn,frac,exp);
|
||||
}
|
||||
|
||||
/// \brief Round a floating point value to the nearest even
|
||||
///
|
||||
/// \param signif the significant bits of a floating point value
|
||||
/// \param lowbitpos the position in signif of the floating point
|
||||
/// \return true if we rounded up
|
||||
|
||||
bool FloatFormat::roundToNearestEven(uintb &signif, int4 lowbitpos)
|
||||
|
||||
{
|
||||
uintb lowbitmask = (lowbitpos < 8 * sizeof(uintb)) ? (1UL << lowbitpos) : 0;
|
||||
uintb midbitmask = 1UL << (lowbitpos - 1);
|
||||
uintb epsmask = midbitmask - 1;
|
||||
bool odd = (signif & lowbitmask) != 0;
|
||||
if ((signif & midbitmask) != 0 && ((signif & epsmask) != 0 || odd)) {
|
||||
signif += midbitmask;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// \param host is the double value to convert
|
||||
/// \return the equivalent encoded value
|
||||
uintb FloatFormat::getEncoding(double host) const
|
||||
|
@ -264,7 +283,7 @@ uintb FloatFormat::getEncoding(double host) const
|
|||
uintb signif;
|
||||
int4 exp;
|
||||
|
||||
type = extractExpSig(host,&sgn,&signif,&exp);
|
||||
type = extractExpSig(host, &sgn, &signif, &exp);
|
||||
if (type == zero)
|
||||
return getZeroEncoding(sgn);
|
||||
else if (type == infinity)
|
||||
|
@ -274,53 +293,114 @@ uintb FloatFormat::getEncoding(double host) const
|
|||
|
||||
// convert exponent and fractional to their encodings
|
||||
exp += bias;
|
||||
if (exp < 0) // Exponent is too small to represent
|
||||
return getZeroEncoding(sgn);
|
||||
if (exp > maxexponent) // Exponent is too big to represent
|
||||
|
||||
if (exp < -frac_size) // Exponent is too small to represent
|
||||
return getZeroEncoding(sgn); // TODO handle round to non-zero
|
||||
|
||||
if (exp < 1) { // Must be denormalized
|
||||
if (roundToNearestEven(signif, 8 * sizeof(uintb) - frac_size - exp)) {
|
||||
// TODO handle round to normal case
|
||||
if ((signif >> (8 * sizeof(uintb) - 1)) == 0) {
|
||||
signif = 1UL << (8 * sizeof(uintb) - 1);
|
||||
exp += 1;
|
||||
}
|
||||
}
|
||||
uintb res = getZeroEncoding(sgn);
|
||||
return setFractionalCode(res, signif >> (-exp));
|
||||
}
|
||||
|
||||
if (roundToNearestEven(signif, 8 * sizeof(uintb) - frac_size - 1)) {
|
||||
// if high bit is clear, then the add overflowed. Increase exp and set
|
||||
// signif to 1.
|
||||
if ((signif >> (8 * sizeof(uintb) - 1)) == 0) {
|
||||
signif = 1UL << (8 * sizeof(uintb) - 1);
|
||||
exp += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (exp >= maxexponent) // Exponent is too big to represent
|
||||
return getInfinityEncoding(sgn);
|
||||
if (jbitimplied && (exp !=0))
|
||||
|
||||
if (jbitimplied && (exp != 0))
|
||||
signif <<= 1; // Cut off top bit (which should be 1)
|
||||
|
||||
uintb res = 0;
|
||||
res = setFractionalCode(res,signif);
|
||||
res = setExponentCode(res,(uintb)exp);
|
||||
return setSign(res,sgn);
|
||||
res = setFractionalCode(res, signif);
|
||||
res = setExponentCode(res, (uintb)exp);
|
||||
return setSign(res, sgn);
|
||||
}
|
||||
|
||||
|
||||
/// \param encoding is the value in the \e other FloatFormat
|
||||
/// \param formin is the \e other FloatFormat
|
||||
/// \return the equivalent value in \b this FloatFormat
|
||||
uintb FloatFormat::convertEncoding(uintb encoding,const FloatFormat *formin) const
|
||||
uintb FloatFormat::convertEncoding(uintb encoding,
|
||||
const FloatFormat *formin) const
|
||||
|
||||
{
|
||||
bool sgn = formin->extractSign(encoding);
|
||||
uintb frac = formin->extractFractionalCode(encoding);
|
||||
uintb signif = formin->extractFractionalCode(encoding);
|
||||
int4 exp = formin->extractExponentCode(encoding);
|
||||
|
||||
|
||||
if (exp == formin->maxexponent) { // NaN or INFINITY encoding
|
||||
exp = maxexponent;
|
||||
}
|
||||
else {
|
||||
exp -= formin->bias;
|
||||
exp += bias;
|
||||
if (exp < 0)
|
||||
return getZeroEncoding(sgn);
|
||||
if (exp > maxexponent)
|
||||
if (signif != 0)
|
||||
return getNaNEncoding(sgn);
|
||||
else
|
||||
return getInfinityEncoding(sgn);
|
||||
}
|
||||
if (jbitimplied && !formin->jbitimplied)
|
||||
frac <<= 1; // Cut off top bit (which should be 1)
|
||||
else if (formin->jbitimplied && !jbitimplied) {
|
||||
frac >>= 1; // Make room for 1 jbit
|
||||
uintb highbit = 1;
|
||||
highbit <<= 8*sizeof(uintb)-1;
|
||||
frac |= highbit; // Stick bit in at top
|
||||
|
||||
if (exp == 0) { // incoming is subnormal
|
||||
if (signif == 0)
|
||||
return getZeroEncoding(sgn);
|
||||
|
||||
// normalize
|
||||
int4 lz = count_leading_zeros(signif);
|
||||
signif <<= lz;
|
||||
exp = -formin->bias - lz;
|
||||
}
|
||||
else { // incoming is normal
|
||||
exp -= formin->bias;
|
||||
if (jbitimplied)
|
||||
signif = (1UL << (8 * sizeof(uintb) - 1)) | (signif >> 1);
|
||||
}
|
||||
|
||||
exp += bias;
|
||||
|
||||
if (exp < -frac_size) // Exponent is too small to represent
|
||||
return getZeroEncoding(sgn); // TODO handle round to non-zero
|
||||
|
||||
if (exp < 1) { // Must be denormalized
|
||||
if (roundToNearestEven(signif, 8 * sizeof(uintb) - frac_size - exp)) {
|
||||
// TODO handle carry to normal case
|
||||
if ((signif >> (8 * sizeof(uintb) - 1)) == 0) {
|
||||
signif = 1UL << (8 * sizeof(uintb) - 1);
|
||||
exp += 1;
|
||||
}
|
||||
}
|
||||
uintb res = getZeroEncoding(sgn);
|
||||
return setFractionalCode(res, signif >> (-exp));
|
||||
}
|
||||
|
||||
if (roundToNearestEven(signif, 8 * sizeof(uintb) - frac_size - 1)) {
|
||||
// if high bit is clear, then the add overflowed. Increase exp and set
|
||||
// signif to 1.
|
||||
if ((signif >> (8 * sizeof(uintb) - 1)) == 0) {
|
||||
signif = 1UL << (8 * sizeof(uintb) - 1);
|
||||
exp += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (exp >= maxexponent) // Exponent is too big to represent
|
||||
return getInfinityEncoding(sgn);
|
||||
|
||||
if (jbitimplied && (exp != 0))
|
||||
signif <<= 1; // Cut off top bit (which should be 1)
|
||||
|
||||
uintb res = 0;
|
||||
res = setFractionalCode(res,frac);
|
||||
res = setExponentCode(res,(uintb)exp);
|
||||
return setSign(res,sgn);
|
||||
res = setFractionalCode(res, signif);
|
||||
res = setExponentCode(res, (uintb)exp);
|
||||
return setSign(res, sgn);
|
||||
}
|
||||
|
||||
// Currently we emulate floating point operations on the target
|
||||
|
@ -487,9 +567,7 @@ uintb FloatFormat::opInt2Float(uintb a,int4 sizein) const
|
|||
uintb FloatFormat::opFloat2Float(uintb a,const FloatFormat &outformat) const
|
||||
|
||||
{
|
||||
floatclass type;
|
||||
double val = getHostFloat(a,&type);
|
||||
return outformat.getEncoding(val);
|
||||
return outformat.convertEncoding(a, this);
|
||||
}
|
||||
|
||||
/// \param a is an encoded floating-point value
|
||||
|
@ -533,7 +611,8 @@ uintb FloatFormat::opRound(uintb a) const
|
|||
{
|
||||
floatclass type;
|
||||
double val = getHostFloat(a,&type);
|
||||
return getEncoding(floor(val+0.5));
|
||||
// return getEncoding(floor(val+.5)); // round half up
|
||||
return getEncoding(round(val)); // round half away from zero
|
||||
}
|
||||
|
||||
/// Write the format out to a \<floatformat> XML tag.
|
||||
|
|
|
@ -50,6 +50,7 @@ private:
|
|||
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);
|
||||
static bool roundToNearestEven(uintb &signif, int4 lowbitpos);
|
||||
uintb setFractionalCode(uintb x,uintb code) const; ///< Set the fractional part of an encoded value
|
||||
uintb setSign(uintb x,bool sign) const; ///< Set the sign bit of an encoded value
|
||||
uintb setExponentCode(uintb x,uintb code) const; ///< Set the exponent of an encoded value
|
||||
|
|
482
Ghidra/Features/Decompiler/src/decompile/cpp/test.cc
Normal file
482
Ghidra/Features/Decompiler/src/decompile/cpp/test.cc
Normal file
|
@ -0,0 +1,482 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
/// \file test.cc
|
||||
/// \brief Unit tests for Ghidra C++ components.
|
||||
|
||||
#include "float.hh"
|
||||
#include "opbehavior.hh"
|
||||
#include "test.hh"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
// utility functions
|
||||
float floatFromRawBits(uintb e) {
|
||||
float f;
|
||||
memcpy(&f, &e, 4);
|
||||
return f;
|
||||
}
|
||||
|
||||
uintb floatToRawBits(float f) {
|
||||
uintb result = 0;
|
||||
memcpy(&result, &f, 4);
|
||||
return result;
|
||||
}
|
||||
|
||||
double doubleFromRawBits(uintb e) {
|
||||
double f;
|
||||
memcpy(&f, &e, 8);
|
||||
return f;
|
||||
}
|
||||
|
||||
uintb doubleToRawBits(double f) {
|
||||
uintb result = 0;
|
||||
memcpy(&result, &f, 8);
|
||||
return result;
|
||||
}
|
||||
|
||||
// macros to preserve call site
|
||||
#define ASSERT_FLOAT_ENCODING(f) \
|
||||
do { \
|
||||
FloatFormat format(4); \
|
||||
\
|
||||
uintb true_encoding = floatToRawBits(f); \
|
||||
uintb encoding = format.getEncoding(f); \
|
||||
\
|
||||
ASSERT_EQUALS(true_encoding, encoding); \
|
||||
} while (0);
|
||||
|
||||
#define ASSERT_DOUBLE_ENCODING(f) \
|
||||
do { \
|
||||
FloatFormat format(8); \
|
||||
\
|
||||
uintb true_encoding = doubleToRawBits(f); \
|
||||
uintb encoding = format.getEncoding(f); \
|
||||
\
|
||||
ASSERT_EQUALS(true_encoding, encoding); \
|
||||
} while (0);
|
||||
|
||||
//// FloatFormat tests
|
||||
|
||||
static std::vector<float> float_test_values{
|
||||
-0.0f,
|
||||
+0.0f,
|
||||
-1.0f,
|
||||
+1.0f,
|
||||
|
||||
-1.234f,
|
||||
+1.234f,
|
||||
|
||||
-std::numeric_limits<float>::denorm_min(),
|
||||
std::numeric_limits<float>::denorm_min(),
|
||||
|
||||
std::numeric_limits<float>::min() - std::numeric_limits<float>::denorm_min(),
|
||||
std::numeric_limits<float>::min(),
|
||||
std::numeric_limits<float>::min() + std::numeric_limits<float>::denorm_min(),
|
||||
|
||||
-std::numeric_limits<float>::min() + std::numeric_limits<float>::denorm_min(),
|
||||
-std::numeric_limits<float>::min(),
|
||||
-std::numeric_limits<float>::min() - std::numeric_limits<float>::denorm_min(),
|
||||
|
||||
std::numeric_limits<float>::max(),
|
||||
|
||||
std::numeric_limits<float>::quiet_NaN(),
|
||||
|
||||
-std::numeric_limits<float>::infinity(),
|
||||
std::numeric_limits<float>::infinity()
|
||||
};
|
||||
|
||||
static std::vector<int> int_test_values = {
|
||||
0, -1, 1, 1234, -1234, std::numeric_limits<int>::min(), std::numeric_limits<int>::max()
|
||||
};
|
||||
|
||||
TEST(float_encoding_normal) {
|
||||
ASSERT_FLOAT_ENCODING(1.234);
|
||||
ASSERT_FLOAT_ENCODING(-1.234);
|
||||
}
|
||||
|
||||
TEST(double_encoding_normal) {
|
||||
ASSERT_DOUBLE_ENCODING(1.234);
|
||||
ASSERT_DOUBLE_ENCODING(-1.234);
|
||||
}
|
||||
|
||||
TEST(float_encoding_nan) {
|
||||
ASSERT_FLOAT_ENCODING(std::numeric_limits<float>::quiet_NaN());
|
||||
ASSERT_FLOAT_ENCODING(-std::numeric_limits<float>::quiet_NaN());
|
||||
}
|
||||
|
||||
TEST(double_encoding_nan) {
|
||||
ASSERT_DOUBLE_ENCODING(std::numeric_limits<double>::quiet_NaN());
|
||||
ASSERT_DOUBLE_ENCODING(-std::numeric_limits<double>::quiet_NaN());
|
||||
}
|
||||
|
||||
TEST(float_encoding_subnormal) {
|
||||
ASSERT_FLOAT_ENCODING(std::numeric_limits<float>::denorm_min());
|
||||
ASSERT_FLOAT_ENCODING(-std::numeric_limits<float>::denorm_min());
|
||||
}
|
||||
|
||||
TEST(double_encoding_subnormal) {
|
||||
ASSERT_DOUBLE_ENCODING(std::numeric_limits<double>::denorm_min());
|
||||
ASSERT_DOUBLE_ENCODING(-std::numeric_limits<double>::denorm_min());
|
||||
}
|
||||
|
||||
TEST(float_encoding_min_normal) {
|
||||
ASSERT_FLOAT_ENCODING(std::numeric_limits<float>::min());
|
||||
ASSERT_FLOAT_ENCODING(-std::numeric_limits<float>::min());
|
||||
}
|
||||
|
||||
TEST(double_encoding_min_normal) {
|
||||
ASSERT_DOUBLE_ENCODING(std::numeric_limits<double>::min());
|
||||
ASSERT_DOUBLE_ENCODING(-std::numeric_limits<double>::min());
|
||||
}
|
||||
|
||||
TEST(float_encoding_infinity) {
|
||||
ASSERT_FLOAT_ENCODING(std::numeric_limits<float>::infinity());
|
||||
ASSERT_FLOAT_ENCODING(-std::numeric_limits<float>::infinity());
|
||||
}
|
||||
|
||||
TEST(double_encoding_infinity) {
|
||||
ASSERT_DOUBLE_ENCODING(std::numeric_limits<double>::infinity());
|
||||
ASSERT_DOUBLE_ENCODING(-std::numeric_limits<double>::infinity());
|
||||
}
|
||||
|
||||
TEST(float_midpoint_rounding) {
|
||||
FloatFormat ff(4);
|
||||
// IEEE754 recommends "round to nearest even" for binary formats, like single and double
|
||||
// precision floating point. It rounds to the nearest integer (significand) when unambiguous,
|
||||
// and to the nearest even on the midpoint.
|
||||
|
||||
// There are 52 bits of significand in a double and 23 in a float.
|
||||
// Below we construct a sequence of double precision values to demonstrate each case
|
||||
// in rounding,
|
||||
|
||||
// d0 - zeros in low 29 bits, round down
|
||||
// d1 - on the rounding midpoint with integer even integer part, round down
|
||||
// d2 - just above the midpoint, round up
|
||||
double d0 = doubleFromRawBits(0x4010000000000000L);
|
||||
double d1 = doubleFromRawBits(0x4010000010000000L);
|
||||
double d2 = doubleFromRawBits(0x4010000010000001L);
|
||||
|
||||
// d3 - zeros in low 29 bits, round down
|
||||
// d4 - on the rounding midpoint with integer part odd, round up
|
||||
// d5 - just above the midpoint, round up
|
||||
double d3 = doubleFromRawBits(0x4010000020000000L);
|
||||
double d4 = doubleFromRawBits(0x4010000030000000L);
|
||||
double d5 = doubleFromRawBits(0x4010000030000001L);
|
||||
|
||||
float f0 = (float)d0;
|
||||
float f1 = (float)d1;
|
||||
float f2 = (float)d2;
|
||||
float f3 = (float)d3;
|
||||
float f4 = (float)d4;
|
||||
float f5 = (float)d5;
|
||||
|
||||
uintb e0 = ff.getEncoding(d0);
|
||||
uintb e1 = ff.getEncoding(d1);
|
||||
uintb e2 = ff.getEncoding(d2);
|
||||
uintb e3 = ff.getEncoding(d3);
|
||||
uintb e4 = ff.getEncoding(d4);
|
||||
uintb e5 = ff.getEncoding(d5);
|
||||
|
||||
ASSERT_EQUALS(floatToRawBits(f0), e0);
|
||||
ASSERT_EQUALS(floatToRawBits(f1), e1);
|
||||
ASSERT_EQUALS(floatToRawBits(f2), e2);
|
||||
ASSERT_EQUALS(floatToRawBits(f3), e3);
|
||||
ASSERT_EQUALS(floatToRawBits(f4), e4);
|
||||
ASSERT_EQUALS(floatToRawBits(f5), e5);
|
||||
|
||||
ASSERT_EQUALS(e0, e1);
|
||||
ASSERT_NOT_EQUALS(e1, e2);
|
||||
|
||||
ASSERT_NOT_EQUALS(e3, e4);
|
||||
ASSERT_EQUALS(e4, e5);
|
||||
}
|
||||
|
||||
// op tests
|
||||
|
||||
// generated
|
||||
|
||||
TEST(float_opNan) {
|
||||
FloatFormat format(4);
|
||||
|
||||
for(float f:float_test_values) {
|
||||
uintb true_result = isnan(f);
|
||||
uintb encoding = format.getEncoding(f);
|
||||
uintb result = format.opNan(encoding);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(float_opNeg) {
|
||||
FloatFormat format(4);
|
||||
|
||||
for(float f:float_test_values) {
|
||||
uintb true_result = floatToRawBits(-f);
|
||||
uintb encoding = format.getEncoding(f);
|
||||
uintb result = format.opNeg(encoding);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(float_opAbs) {
|
||||
FloatFormat format(4);
|
||||
|
||||
for(float f:float_test_values) {
|
||||
uintb true_result = floatToRawBits(abs(f));
|
||||
uintb encoding = format.getEncoding(f);
|
||||
uintb result = format.opAbs(encoding);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(float_opSqrt) {
|
||||
FloatFormat format(4);
|
||||
|
||||
for(float f:float_test_values) {
|
||||
uintb true_result = floatToRawBits(sqrtf(f));
|
||||
uintb encoding = format.getEncoding(f);
|
||||
uintb result = format.opSqrt(encoding);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(float_opCeil) {
|
||||
FloatFormat format(4);
|
||||
|
||||
for(float f:float_test_values) {
|
||||
uintb true_result = floatToRawBits(ceilf(f));
|
||||
uintb encoding = format.getEncoding(f);
|
||||
uintb result = format.opCeil(encoding);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(float_opFloor) {
|
||||
FloatFormat format(4);
|
||||
|
||||
for(float f:float_test_values) {
|
||||
uintb true_result = floatToRawBits(floorf(f));
|
||||
uintb encoding = format.getEncoding(f);
|
||||
uintb result = format.opFloor(encoding);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(float_opRound) {
|
||||
FloatFormat format(4);
|
||||
|
||||
for(float f:float_test_values) {
|
||||
uintb true_result = floatToRawBits(roundf(f));
|
||||
uintb encoding = format.getEncoding(f);
|
||||
uintb result = format.opRound(encoding);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(float_opInt2Float_size4) {
|
||||
FloatFormat format(4);
|
||||
|
||||
for(int i:int_test_values) {
|
||||
uintb true_result = floatToRawBits((float)i);
|
||||
uintb result = format.opInt2Float(i, 4);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
// TODO other sized ints
|
||||
|
||||
TEST(float_to_double_opFloat2Float) {
|
||||
FloatFormat format(4);
|
||||
FloatFormat format8(8);
|
||||
|
||||
for(float f:float_test_values) {
|
||||
uintb true_result = doubleToRawBits((double)f);
|
||||
uintb encoding = format.getEncoding(f);
|
||||
uintb result = format.opFloat2Float(encoding, format8);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO float2float going the other direction, double_to_float_opFloat2Float
|
||||
|
||||
|
||||
TEST(float_opTrunc_to_int) {
|
||||
FloatFormat format(4);
|
||||
FloatFormat format8(8);
|
||||
|
||||
for(float f:float_test_values) {
|
||||
// avoid undefined behavior
|
||||
if((int64_t)f > std::numeric_limits<int>::max() || (int64_t)f < std::numeric_limits<int>::min())
|
||||
continue;
|
||||
uintb true_result = ((uintb)(int32_t)f) & 0xffffffff;
|
||||
uintb encoding = format.getEncoding(f);
|
||||
uintb result = format.opTrunc(encoding, 4);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO trunc to other sizes
|
||||
|
||||
|
||||
|
||||
TEST(float_opEqual) {
|
||||
FloatFormat format(4);
|
||||
|
||||
for(float f1:float_test_values) {
|
||||
uintb encoding1 = format.getEncoding(f1);
|
||||
for(float f2:float_test_values) {
|
||||
uintb true_result = (f1==f2);
|
||||
uintb encoding2 = format.getEncoding(f2);
|
||||
uintb result = format.opEqual(encoding1, encoding2);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(float_opNotEqual) {
|
||||
FloatFormat format(4);
|
||||
|
||||
for(float f1:float_test_values) {
|
||||
uintb encoding1 = format.getEncoding(f1);
|
||||
for(float f2:float_test_values) {
|
||||
uintb true_result = (f1!=f2);
|
||||
uintb encoding2 = format.getEncoding(f2);
|
||||
uintb result = format.opNotEqual(encoding1, encoding2);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(float_opLess) {
|
||||
FloatFormat format(4);
|
||||
|
||||
for(float f1:float_test_values) {
|
||||
uintb encoding1 = format.getEncoding(f1);
|
||||
for(float f2:float_test_values) {
|
||||
uintb true_result = (f1<f2);
|
||||
uintb encoding2 = format.getEncoding(f2);
|
||||
uintb result = format.opLess(encoding1, encoding2);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(float_opLessEqual) {
|
||||
FloatFormat format(4);
|
||||
|
||||
for(float f1:float_test_values) {
|
||||
uintb encoding1 = format.getEncoding(f1);
|
||||
for(float f2:float_test_values) {
|
||||
uintb true_result = (f1<=f2);
|
||||
uintb encoding2 = format.getEncoding(f2);
|
||||
uintb result = format.opLessEqual(encoding1, encoding2);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(float_opAdd) {
|
||||
FloatFormat format(4);
|
||||
|
||||
for(float f1:float_test_values) {
|
||||
uintb encoding1 = format.getEncoding(f1);
|
||||
for(float f2:float_test_values) {
|
||||
uintb true_result = floatToRawBits(f1+f2);
|
||||
uintb encoding2 = format.getEncoding(f2);
|
||||
uintb result = format.opAdd(encoding1, encoding2);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(float_opDiv) {
|
||||
FloatFormat format(4);
|
||||
|
||||
for(float f1:float_test_values) {
|
||||
uintb encoding1 = format.getEncoding(f1);
|
||||
for(float f2:float_test_values) {
|
||||
uintb true_result = floatToRawBits(f1/f2);
|
||||
uintb encoding2 = format.getEncoding(f2);
|
||||
uintb result = format.opDiv(encoding1, encoding2);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(float_opMult) {
|
||||
FloatFormat format(4);
|
||||
|
||||
for(float f1:float_test_values) {
|
||||
uintb encoding1 = format.getEncoding(f1);
|
||||
for(float f2:float_test_values) {
|
||||
uintb true_result = floatToRawBits(f1*f2);
|
||||
uintb encoding2 = format.getEncoding(f2);
|
||||
uintb result = format.opMult(encoding1, encoding2);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
TEST(float_opSub) {
|
||||
FloatFormat format(4);
|
||||
|
||||
for(float f1:float_test_values) {
|
||||
uintb encoding1 = format.getEncoding(f1);
|
||||
for(float f2:float_test_values) {
|
||||
uintb true_result = floatToRawBits(f1-f2);
|
||||
uintb encoding2 = format.getEncoding(f2);
|
||||
uintb result = format.opSub(encoding1, encoding2);
|
||||
|
||||
ASSERT_EQUALS(true_result, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// end generated
|
105
Ghidra/Features/Decompiler/src/decompile/cpp/test.hh
Normal file
105
Ghidra/Features/Decompiler/src/decompile/cpp/test.hh
Normal file
|
@ -0,0 +1,105 @@
|
|||
/* ###
|
||||
* 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.
|
||||
*/
|
||||
/// \file test.hh
|
||||
/// \brief Simple test framework
|
||||
///
|
||||
/// Include this file and any additional headers. Use TEST(testname) as
|
||||
/// prototype in test function definitions. E.g.
|
||||
/// test.cc:
|
||||
/// #include "float.hh"
|
||||
/// #include "test.hh"
|
||||
///
|
||||
/// TEST(zero_is_less_than_one) {
|
||||
/// ASSERT(0.0 < 1.0);
|
||||
/// }
|
||||
///
|
||||
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
|
||||
namespace {
|
||||
struct Test;
|
||||
typedef void (*testfunc_t)();
|
||||
|
||||
std::vector<Test *> tests;
|
||||
|
||||
struct Test {
|
||||
std::string name;
|
||||
testfunc_t func;
|
||||
|
||||
Test(const std::string &name, testfunc_t func) : name(name), func(func) {
|
||||
tests.push_back(this);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
#define TEST(testname) \
|
||||
void testname(); \
|
||||
Test testname##_obj{ #testname, testname }; \
|
||||
void testname()
|
||||
|
||||
#define ASSERT(test) \
|
||||
if (!(test)) { \
|
||||
std::cerr << " failed at " << __FILE__ << ":" << __LINE__ << " asserting \"" << #test << "\"." << std::endl; \
|
||||
throw 0; \
|
||||
}
|
||||
|
||||
#define ASSERT_EQUALS(a, b) \
|
||||
if ((a) != (b)) { \
|
||||
std::stringstream ssa, ssb; \
|
||||
ssa << (a); \
|
||||
ssb << (b); \
|
||||
std::cerr << " failed at " << __FILE__ << ":" << __LINE__ << " asserting \"" << ssa.str() \
|
||||
<< " == " << ssb.str() << "\"." << std::endl; \
|
||||
throw 0; \
|
||||
}
|
||||
|
||||
#define ASSERT_NOT_EQUALS(a, b) \
|
||||
if ((a) == (b)) { \
|
||||
std::stringstream ssa, ssb; \
|
||||
ssa << (a); \
|
||||
ssb << (b); \
|
||||
std::cerr << " failed at " << __FILE__ << ":" << __LINE__ << " asserting \"" << ssa.str() \
|
||||
<< " != " << ssb.str() << "\"." << std::endl; \
|
||||
throw 0; \
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
int total = 0;
|
||||
int passed = 0;
|
||||
|
||||
std::set<std::string> testnames(argv + 1, argv + argc);
|
||||
|
||||
for (auto &t : tests) {
|
||||
if(testnames.size()>0 && testnames.find(t->name)==testnames.end()) {
|
||||
continue;
|
||||
}
|
||||
std::cerr << "testing : " << t->name << " ..." << std::endl;
|
||||
++total;
|
||||
try {
|
||||
t->func();
|
||||
++passed;
|
||||
std::cerr << " passed." << std::endl;
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
std::cerr << "==============================" << std::endl;
|
||||
std::cerr << passed << "/" << total << " tests passed." << std::endl;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue