From b138a40da6c385023567c0f9f61d46d45286f524 Mon Sep 17 00:00:00 2001 From: Yang Chen Date: Sat, 20 Sep 2014 18:26:37 -0700 Subject: [PATCH 1/7] Start a new branch for floating point work * It's based on the patch provided by Alastair Donaldson * Don't generate floating-point operands for bit-operators * Don't generate floating-point index for arrays * Don't generate floating-point operands for shifts Currently csmith is able to generate programs that can be compiled successfully, but full with UBs caused by convertion from floating-point to integers where the floating-point value is out of the range of integers. --- src/ArrayVariable.cpp | 2 + src/CGOptions.cpp | 2 + src/CGOptions.h | 4 + src/Constant.cpp | 186 ++++++++++++++++++++++----------- src/ExpressionVariable.cpp | 2 + src/Function.cpp | 4 + src/FunctionInvocation.cpp | 54 +++++++++- src/FunctionInvocation.h | 4 + src/Lhs.cpp | 3 + src/Probabilities.cpp | 14 +++ src/Probabilities.h | 5 + src/RandomProgramGenerator.cpp | 11 ++ src/StatementArrayOp.cpp | 4 + src/StatementAssign.cpp | 29 ++++- src/StatementAssign.h | 4 +- src/Type.cpp | 40 ++++--- src/Type.h | 7 +- src/VariableSelector.cpp | 2 + 18 files changed, 293 insertions(+), 84 deletions(-) diff --git a/src/ArrayVariable.cpp b/src/ArrayVariable.cpp index e811c6e4b..5f13c3e8f 100644 --- a/src/ArrayVariable.cpp +++ b/src/ArrayVariable.cpp @@ -302,6 +302,8 @@ ArrayVariable* ArrayVariable::itemize(const std::vector& indices, Block* blk) const { size_t i; + // Looks like this function is dead. + assert(0 && "Invoke a dead function?"); assert(collective == 0); ArrayVariable* av = new ArrayVariable(*this); VariableSelector::AllVars.push_back(av); diff --git a/src/CGOptions.cpp b/src/CGOptions.cpp index 8a89635fe..56720a0ca 100644 --- a/src/CGOptions.cpp +++ b/src/CGOptions.cpp @@ -135,6 +135,7 @@ DEFINE_GETTER_SETTER_BOOL(math_notmp) DEFINE_GETTER_SETTER_BOOL(longlong) DEFINE_GETTER_SETTER_BOOL(int8) DEFINE_GETTER_SETTER_BOOL(uint8) +DEFINE_GETTER_SETTER_BOOL(enable_float) DEFINE_GETTER_SETTER_BOOL(pointers) DEFINE_GETTER_SETTER_BOOL(arrays) DEFINE_GETTER_SETTER_BOOL(strict_const_arrays) @@ -251,6 +252,7 @@ CGOptions::set_default_settings(void) longlong(true); int8(true); uint8(true); + enable_float(false); pointers(true); arrays(true); strict_const_arrays(false); diff --git a/src/CGOptions.h b/src/CGOptions.h index b2471e0c6..0328ac2fd 100644 --- a/src/CGOptions.h +++ b/src/CGOptions.h @@ -253,6 +253,9 @@ class CGOptions { static bool uint8(void); static bool uint8(bool p); + static bool enable_float(void); + static bool enable_float(bool p); + static bool pointers(void); static bool pointers(bool p); @@ -532,6 +535,7 @@ class CGOptions { static bool longlong_; static bool int8_; static bool uint8_; + static bool enable_float_; static bool pointers_; static bool arrays_; static bool strict_const_arrays_; diff --git a/src/Constant.cpp b/src/Constant.cpp index 31364af99..c67f0f7e9 100644 --- a/src/Constant.cpp +++ b/src/Constant.cpp @@ -155,12 +155,56 @@ GenerateRandomLongLongConstant(void) static string GenerateRandomFloatConstant(void) { - // Generate a random floating point value with up to 10 digits of precision. (should look up precision of float/double.. 23 bits for IEEE-32?) string val = RandomDigits(5) + "." + RandomDigits(5); return val; } #endif // 0 +/* + * Generate hexadecimal floating point constants [0xF.FFFFFFp-99, 0xF.FFFFFFp+99] + */ +static string +GenerateRandomFloatHexConstant(void) +{ + int exp = pure_rnd_upto(100); + ostringstream oss; + oss << "0x" << RandomHexDigits(1) << "." << RandomHexDigits(6) << "p"; + + if (pure_rnd_flipcoin(50)) { + oss << "+"; + } + else { + oss << "-"; + } + oss << exp; + return oss.str(); +} + +/* + * Generate small hexadecimal floating point constants + */ +static string +GenerateSmallRandomFloatHexConstant(int num) +{ + ostringstream oss; + if (num >= 0) { + oss << "0x"; + } + else { + oss << "-0x"; + num = -num; + } + oss << num << "." << RandomHexDigits(1) << "p"; + + if (pure_rnd_flipcoin(50)) { + oss << "+1"; + } + else { + oss << "-1"; + } + return oss.str(); +} + static string GenerateRandomConstantInRange(const Type* type, int bound) { @@ -247,77 +291,95 @@ GenerateRandomUnionConstant(const Type* type) static string GenerateRandomConstant(const Type* type) { - string v; + string v; if (type == 0) { v = "0"; } - else if (type->eType == eStruct) { - v = GenerateRandomStructConstant(type); + else if (type->eType == eStruct) { + v = GenerateRandomStructConstant(type); ERROR_GUARD(""); - } + } else if (type->eType == eUnion) { - v = GenerateRandomUnionConstant(type); + v = GenerateRandomUnionConstant(type); ERROR_GUARD(""); - } - // the only possible constant for a pointer is "0" - else if (type->eType == ePointer) { - v = "0"; - } - else if (type->eType == eSimple) { - eSimpleType st = type->simple_type; - assert(st != eVoid); - //assert((eType >= 0) && (eType <= MAX_SIMPLE_TYPES)); - if (pure_rnd_flipcoin(50)) { - ERROR_GUARD(""); - int num = 0; - if (pure_rnd_flipcoin(50)) { - ERROR_GUARD(""); - num = pure_rnd_upto(3)-1; - } else { - ERROR_GUARD(""); - num = pure_rnd_upto(20)-10; - } + } + // the only possible constant for a pointer is "0" + else if (type->eType == ePointer) { + v = "0"; + } + else if (type->eType == eSimple) { + eSimpleType st = type->simple_type; + assert(st != eVoid); + //assert((eType >= 0) && (eType <= MAX_SIMPLE_TYPES)); + if (pure_rnd_flipcoin(50)) { + ERROR_GUARD(""); + int num = 0; + if (pure_rnd_flipcoin(50)) { + ERROR_GUARD(""); + num = pure_rnd_upto(3)-1; + } else { + ERROR_GUARD(""); + num = pure_rnd_upto(20)-10; + } // don't use negative number for unsigned type, as this causes //trouble for some static analyzers - ostringstream oss; - switch (type->simple_type) { - case eUChar: oss << (unsigned int)(unsigned char)num; break; - case eUShort: oss << (unsigned short)num; break; - case eUInt: oss << (unsigned int)num; break; - case eULong: - case eULongLong: - if (!CGOptions::longlong()) { - oss << (unsigned int)num; - } else { - oss << ((type->simple_type == eULong) ? (unsigned long)num : (unsigned INT64)num); - } - break; - default: oss << num; break; - } - if (CGOptions::ccomp()) - v = oss.str() + (type->is_signed() ? "" : "U"); - else - v = oss.str() + (type->is_signed() ? "L" : "UL"); - } else { + ostringstream oss; + switch (st) { + case eUChar: + oss << (unsigned int)(unsigned char)num; + break; + case eUShort: + oss << (unsigned short)num; + break; + case eUInt: + oss << (unsigned int)num; + break; + case eULong: + case eULongLong: + if (!CGOptions::longlong()) { + oss << (unsigned int)num; + } else { + oss << ((type->simple_type == eULong) ? (unsigned long)num : (unsigned INT64)num); + } + break; + case eFloat: + oss << GenerateSmallRandomFloatHexConstant(num); + break; + default: + oss << num; + break; + } + if (type->simple_type == eFloat) { + v = oss.str(); + } + else { + if (CGOptions::ccomp()) + v = oss.str() + (type->is_signed() ? "" : "U"); + else + v = oss.str() + (type->is_signed() ? "L" : "UL"); + } + } else { switch (st) { - case eVoid: v = "/* void */"; break; - case eChar: v = GenerateRandomCharConstant(); break; - case eInt: v = GenerateRandomIntConstant(); break; - case eShort: v = GenerateRandomShortConstant(); break; - case eLong: v = GenerateRandomLongConstant(); break; - case eLongLong: v = GenerateRandomLongLongConstant(); break; - case eUChar: v = GenerateRandomCharConstant(); break; - case eUInt: v = GenerateRandomIntConstant(); break; - case eUShort: v = GenerateRandomShortConstant(); break; - case eULong: v = GenerateRandomLongConstant(); break; - case eULongLong: v = GenerateRandomLongLongConstant(); break; - // case eFloat: v = GenerateRandomFloatConstant(); break; - // case eDouble: v = GenerateRandomFloatConstant(); break; + case eVoid: v = "/* void */"; break; + case eChar: v = GenerateRandomCharConstant(); break; + case eInt: v = GenerateRandomIntConstant(); break; + case eShort: v = GenerateRandomShortConstant(); break; + case eLong: v = GenerateRandomLongConstant(); break; + case eLongLong: v = GenerateRandomLongLongConstant(); break; + case eUChar: v = GenerateRandomCharConstant(); break; + case eUInt: v = GenerateRandomIntConstant(); break; + case eUShort: v = GenerateRandomShortConstant(); break; + case eULong: v = GenerateRandomLongConstant(); break; + case eULongLong: v = GenerateRandomLongLongConstant(); break; + case eFloat: v = GenerateRandomFloatHexConstant(); break; + // case eDouble: v = GenerateRandomFloatConstant(); break; + default: + assert(0 && "Unsupported type!"); } - } - } else { - assert(0); // no support for types other than integers and structs for now - } + } + } else { + assert(0); // no support for types other than integers and structs for now + } return (type->eType == eSimple && CGOptions::mark_mutable_const()) ? "(" + v + ")" : v; } diff --git a/src/ExpressionVariable.cpp b/src/ExpressionVariable.cpp index 2c5b4635b..df45fc1c2 100644 --- a/src/ExpressionVariable.cpp +++ b/src/ExpressionVariable.cpp @@ -73,6 +73,8 @@ ExpressionVariable::make_random(CGContext &cg_context, const Type* type, const C ERROR_GUARD(NULL); if (!var) continue; + if (!type->is_float() && var->type->is_float()) + continue; // forbid a parameter to take the address of an argument // this is to simplify the path shortcutting delta if (as_param && var->is_argument() && var->type->is_dereferenced_from(type)) { diff --git a/src/Function.cpp b/src/Function.cpp index 22fe7babc..89991ea42 100644 --- a/src/Function.cpp +++ b/src/Function.cpp @@ -278,6 +278,10 @@ Function::choose_func(vector funcs, // if type = 0, we don't care if (type && !type->is_convertable((*i)->return_type)) continue; + // Changing the behavior of is_convertable is quite dangerous. + // Making constraint here has less global effect. + if (type && (*i)->return_type->is_float() && !type->is_float()) + continue; if (qfer && (*i)->rv && !qfer->match((*i)->rv->qfer)) continue; // We cannot call a function that has an as-yet unknown effect. diff --git a/src/FunctionInvocation.cpp b/src/FunctionInvocation.cpp index 2b3de37eb..91f4649fc 100644 --- a/src/FunctionInvocation.cpp +++ b/src/FunctionInvocation.cpp @@ -75,9 +75,9 @@ using namespace std; */ FunctionInvocation * FunctionInvocation::make_random(bool is_std_func, - CGContext &cg_context, - const Type* type, - const CVQualifiers* qfer) + CGContext &cg_context, + const Type* type, + const CVQualifiers* qfer) { FunctionInvocation *fi = 0; // If we are looking for a program-defined function, try to find one. @@ -140,7 +140,10 @@ FunctionInvocation::make_random_unary(CGContext &cg_context, const Type* type) { DEPTH_GUARD_BY_TYPE_RETURN(dtFunctionInvocationRandomUnary, NULL); assert(type); - eUnaryOps op = (eUnaryOps)(rnd_upto(MAX_UNARY_OP, UNARY_OPS_PROB_FILTER)); + eUnaryOps op; + do { + op = (eUnaryOps)(rnd_upto(MAX_UNARY_OP, UNARY_OPS_PROB_FILTER)); + } while (type->is_float() && !UnaryOpWorksForFloat(op)); ERROR_GUARD(NULL); SafeOpFlags *flags = NULL; if (op == eMinus) { @@ -171,7 +174,10 @@ FunctionInvocation::make_random_binary(CGContext &cg_context, const Type* type) return make_random_binary_ptr_comparison(cg_context); } - eBinaryOps op = (eBinaryOps)(rnd_upto(MAX_BINARY_OP, BINARY_OPS_PROB_FILTER)); + eBinaryOps op; + do { + op = (eBinaryOps)(rnd_upto(MAX_BINARY_OP, BINARY_OPS_PROB_FILTER)); + } while (type->is_float() && !BinaryOpWorksForFloat(op)); ERROR_GUARD(NULL); assert(type); SafeOpFlags *flags = SafeOpFlags::make_random(sOpBinary, op); @@ -542,6 +548,7 @@ FunctionInvocation * FunctionInvocation::make_unary(CGContext &cg_context, eUnaryOps op, Expression *operand) { + assert(0 && "Dead function!"); DEPTH_GUARD_BY_TYPE_RETURN(dtFunctionInvocationUnary, NULL); SafeOpFlags *flags = SafeOpFlags::make_random(sOpUnary); ERROR_GUARD(NULL); @@ -626,6 +633,43 @@ FunctionInvocation::IsOrderedStandardFunc(eBinaryOps eFunc) return ((eFunc == eAnd) || (eFunc == eOr)); } +/* + * Return true if `op' is suitable as a floating point binary operator + */ +bool +FunctionInvocation::BinaryOpWorksForFloat(eBinaryOps op) +{ + switch (op) { + case eAdd: + case eSub: + case eMul: + case eDiv: + case eMod: + case eCmpGt: + case eCmpLt: + case eCmpGe: + case eCmpLe: + case eCmpEq: + case eCmpNe: // fall-through + return true; + default: + return false; + } +} + +bool +FunctionInvocation::UnaryOpWorksForFloat(eUnaryOps op) +{ + switch (op) { + case ePlus: + case eMinus: + case eNot: // fall-through + return true; + default: + return false; + } +} + /////////////////////////////////////////////////////////////////////////////// // Local Variables: diff --git a/src/FunctionInvocation.h b/src/FunctionInvocation.h index 12076af7b..cd272c59a 100644 --- a/src/FunctionInvocation.h +++ b/src/FunctionInvocation.h @@ -153,6 +153,10 @@ class FunctionInvocation static bool IsOrderedStandardFunc(eBinaryOps eFunc); + static bool BinaryOpWorksForFloat(eBinaryOps op); + + static bool UnaryOpWorksForFloat(eUnaryOps op); + virtual const Type &get_type(void) const = 0; virtual bool compatible(const Variable *) const { return false; } diff --git a/src/Lhs.cpp b/src/Lhs.cpp index 130a01303..191d83373 100644 --- a/src/Lhs.cpp +++ b/src/Lhs.cpp @@ -104,6 +104,9 @@ Lhs::make_random(CGContext &cg_context, const Type* t, const CVQualifiers* qfer, if (valid && CGOptions::ccomp() && var->isBitfield_ && t->is_long_long()) { valid = false; } + if (!t->is_float() && var->type->is_float()) { + valid = false; + } if (valid) { assert(var); Lhs tmp(*var, t, compound_assign); diff --git a/src/Probabilities.cpp b/src/Probabilities.cpp index 55175a273..f2fa2cfab 100644 --- a/src/Probabilities.cpp +++ b/src/Probabilities.cpp @@ -467,6 +467,9 @@ Probabilities::set_single_name_maps() // for choosing union as LType set_single_name("union_as_ltype_prob", pUnionAsLTypeProb); + // for choosing float as LType + set_single_name("float_as_ltype_prob", pFloatAsLTypeProb); + // for creating new array var set_single_name("new_array_var_prob", pNewArrayVariableProb); @@ -571,6 +574,10 @@ Probabilities::initialize_single_probs() m[pShiftByNonConstantProb] = 50; m[pStructAsLTypeProb] = 30; m[pUnionAsLTypeProb] = 25; + if (CGOptions::enable_float()) + m[pFloatAsLTypeProb] = 40; + else + m[pFloatAsLTypeProb] = 0; if (CGOptions::arrays()) m[pNewArrayVariableProb] = 20; else @@ -702,6 +709,13 @@ Probabilities::set_default_simple_types_prob() SET_SINGLE_NAME("ulong_long_prob", ULongLong, 0); } + if (CGOptions::enable_float()) { + SET_SINGLE_NAME("float_prob", Float, 1); + } + else { + SET_SINGLE_NAME("float_prob", Float, 0); + } + set_group_prob(true, pSimpleTypesProb, m); set_prob_filter(pSimpleTypesProb); } diff --git a/src/Probabilities.h b/src/Probabilities.h index 3df139afb..1970c6faa 100644 --- a/src/Probabilities.h +++ b/src/Probabilities.h @@ -60,6 +60,7 @@ enum ProbName { pPointerAsLTypeProb, pStructAsLTypeProb, pUnionAsLTypeProb, + pFloatAsLTypeProb, pNewArrayVariableProb, pAccessOnceVariableProb, pInlineFunctionProb, @@ -137,6 +138,7 @@ enum ProbName { pUShortProb, pULongProb, pULongLongProb, + pFloatProb, // for safe math ops pSafeOpsSizeProb, @@ -206,6 +208,9 @@ enum ProbName { #define UnionAsLTypeProb \ Probabilities::get_prob(pUnionAsLTypeProb) +#define FloatAsLTypeProb \ + Probabilities::get_prob(pFloatAsLTypeProb) + #define NewArrayVariableProb \ Probabilities::get_prob(pNewArrayVariableProb) diff --git a/src/RandomProgramGenerator.cpp b/src/RandomProgramGenerator.cpp index dd18e41ee..1701c30d3 100644 --- a/src/RandomProgramGenerator.cpp +++ b/src/RandomProgramGenerator.cpp @@ -166,6 +166,7 @@ static void print_help() cout << " --longlong| --no-longlong: enable | disable long long (enabled by default)." << endl << endl; cout << " --int8 | --no-int8: enable | disable int8_t (enabled by default)." << endl << endl; cout << " --uint8 | --no-uint8: enable | disable uint8_t (enabled by default)." << endl << endl; + cout << " --float | --no-float: enable | disable float (disabled by default)." << endl << endl; cout << " --main | --nomain: enable | disable to generate main function (enabled by default)." << endl << endl; cout << " --math64 | --no-math64: enable | disable 64-bit math ops (enabled by default)." << endl << endl; cout << " --inline-function | --no-inline-function: enable | disable inline attributes on generated functions." << endl << endl; @@ -776,6 +777,16 @@ main(int argc, char **argv) continue; } + if (strcmp (argv[i], "--float") == 0) { + CGOptions::enable_float(true); + continue; + } + + if (strcmp (argv[i], "--no-float") == 0) { + CGOptions::enable_float(false); + continue; + } + if (strcmp (argv[i], "--pointers") == 0) { CGOptions::pointers(true); continue; diff --git a/src/StatementArrayOp.cpp b/src/StatementArrayOp.cpp index 25e26370f..4978b0b97 100644 --- a/src/StatementArrayOp.cpp +++ b/src/StatementArrayOp.cpp @@ -115,6 +115,10 @@ StatementArrayOp::make_random_array_init(CGContext &cg_context) Variable *cv = NULL; do { cv = VariableSelector::SelectLoopCtrlVar(cg_context, invalid_vars); + if (cv->type->is_float()) { + invalid_vars.push_back(cv); + continue; + } if (cv->is_volatile()) vol_count++; if ((CGOptions::strict_volatile_rule() && (vol_count > 1) && cv->is_volatile()) diff --git a/src/StatementAssign.cpp b/src/StatementAssign.cpp index 304978be1..dc27aef25 100644 --- a/src/StatementAssign.cpp +++ b/src/StatementAssign.cpp @@ -87,7 +87,11 @@ StatementAssign::AssignOpsProbability(const Type* type) if (!CGOptions::compound_assignment()) { return eSimpleAssign; } - if (type && type->eType != eSimple) { + // First, floating point values do not apply to |=, &= and ^=. + // Second, similar to signed integers, we don't generate pre- or post- + // operators for floating point values. Instead, we will wrap all + // of these operations into safe_float_math later. + if (type && (type->eType != eSimple || type->get_base_type()->is_float())) { return eSimpleAssign; } @@ -113,7 +117,7 @@ StatementAssign::make_random(CGContext &cg_context, const Type* type, const CVQu // decide type if (type == NULL) { // stand_alone_assign = true; - type = Type::SelectLType(!cg_context.get_effect_context().is_side_effect_free(), op); + type = Type::SelectLType(!cg_context.get_effect_context().is_side_effect_free(), op); } assert(!type->is_const_struct_union()); @@ -191,6 +195,11 @@ StatementAssign::make_random(CGContext &cg_context, const Type* type, const CVQu if (CGOptions::ccomp() && lhs->get_var()->isBitfield_) { e->cast_type = type; } + // e can be of float type. So, we reset its + if ((lhs->get_var()->type->get_base_type()->is_float() || e->get_type().get_base_type()->is_float()) + && !StatementAssign::AssignOpWorksForFloat(op)) { + op = eSimpleAssign; + } if (CompatibleChecker::compatible_check(e, lhs)) { Error::set_error(COMPATIBLE_CHECK_ERROR); @@ -577,6 +586,22 @@ StatementAssign::OutputAsExpr(std::ostream &out) const } } +bool +StatementAssign::AssignOpWorksForFloat(eAssignOps op) +{ + switch (op) { + case eSimpleAssign: + case eMulAssign: + case eDivAssign: + case eRemAssign: + case eAddAssign: + case eSubAssign: + return true; + default: + return false; + } +} + /////////////////////////////////////////////////////////////////////////////// // Local Variables: diff --git a/src/StatementAssign.h b/src/StatementAssign.h index 7dc463ae3..fe775a282 100644 --- a/src/StatementAssign.h +++ b/src/StatementAssign.h @@ -107,6 +107,8 @@ class StatementAssign : public Statement static eBinaryOps compound_to_binary_ops(eAssignOps op); + static bool AssignOpWorksForFloat(eAssignOps op); + virtual ~StatementAssign(void); virtual void get_blocks(std::vector& /* blks */) const {}; @@ -124,7 +126,7 @@ class StatementAssign : public Statement virtual void OutputAsExpr(std::ostream &out) const; void OutputSimple(std::ostream &out) const; - + private: static eAssignOps AssignOpsProbability(const Type* type); diff --git a/src/Type.cpp b/src/Type.cpp index 1144e8f06..1617aa55e 100644 --- a/src/Type.cpp +++ b/src/Type.cpp @@ -402,6 +402,9 @@ Type::get_type_from_string(const string &type_string) else if (type_string == "ULonglong") { return &Type::get_simple_type(eULongLong); } + else if (type_string == "Float") { + return &Type::get_simple_type(eFloat); + } assert(0 && "Unsupported type string!"); return NULL; @@ -1397,6 +1400,7 @@ Type::is_promotable(const Type* t) const case eULong: return (t2 == eLong || t2 == eULong || t2 == eLongLong || t2 == eULongLong); case eLongLong: case eULongLong: return (t2 == eLongLong || t2 == eULongLong); + case eFloat: return (t2 != eVoid); default: break; } } @@ -1413,20 +1417,23 @@ Type::is_convertable(const Type* t) const { if (this == t) return true; - if (eType == eSimple && t->eType == eSimple) { + if (eType == eSimple && t->eType == eSimple) { if ((simple_type != eVoid && t->simple_type != eVoid) || simple_type == t->simple_type) return true; } else if (eType == ePointer && t->eType == ePointer) { - if (ptr_type == t->ptr_type) { - return true; - } - if (ptr_type->eType == eSimple && t->ptr_type->eType == eSimple) { - return ptr_type->SizeInBytes() == t->ptr_type->SizeInBytes(); - } + if (ptr_type == t->ptr_type) { + return true; + } + if (ptr_type->eType == eSimple && t->ptr_type->eType == eSimple) { + if(ptr_type->simple_type == eFloat && t->ptr_type->simple_type == eFloat) + return true; + else + return ptr_type->SizeInBytes() == t->ptr_type->SizeInBytes(); + } //return ptr_type->is_convertable(t->ptr_type); - //return t->ptr_type->is_promotable(ptr_type); + //return t->ptr_type->is_promotable(ptr_type); } return false; } @@ -1517,7 +1524,7 @@ Type::SizeInBytes(void) const case eUShort: return 2; case eULong: return 4; case eULongLong:return 8; -// case eFloat: return 4; + case eFloat: return 4; // case eDouble: return 8; } break; @@ -1583,6 +1590,13 @@ Type::SelectLType(bool no_volatile, eAssignOps op) } } + // choose float as LHS type + if (!type) { + if (StatementAssign::AssignOpWorksForFloat(op) && rnd_flipcoin(FloatAsLTypeProb)) { + type = &Type::get_simple_type(eFloat); + } + } + // default is any integer type if (!type) { type = get_int_type(); @@ -1636,15 +1650,17 @@ Type::Output(std::ostream &out) const case eSimple: if (this->simple_type == eVoid) { out << "void"; + } else if (this->simple_type == eFloat) { + out << "float"; } else { out << (is_signed() ? "int" : "uint"); out << (SizeInBytes() * 8); out << "_t"; } break; - case ePointer: ptr_type->Output( out ); out << "*"; break; - case eUnion: out << "union U" << sid; break; - case eStruct: out << "struct S" << sid; break; + case ePointer: ptr_type->Output( out ); out << "*"; break; + case eUnion: out << "union U" << sid; break; + case eStruct: out << "struct S" << sid; break; } } diff --git a/src/Type.h b/src/Type.h index 984b3602b..6e81c2f48 100644 --- a/src/Type.h +++ b/src/Type.h @@ -80,9 +80,9 @@ enum eSimpleType eUInt, eUShort, eULong, - eULongLong, -// eFloat, + eFloat, // eDouble, + eULongLong, }; #define MAX_SIMPLE_TYPES ((eSimpleType) (eULongLong+1)) @@ -233,6 +233,9 @@ class Type bool is_signed_char() const { return ((eType == eSimple) && (simple_type == eChar)); } + bool is_float() const { + return ((eType == eSimple) && (simple_type == eFloat)); + } bool is_promotable(const Type* t) const; bool is_convertable(const Type* t) const; bool is_derivable(const Type* t) const; diff --git a/src/VariableSelector.cpp b/src/VariableSelector.cpp index 29e9245b4..bfc775f2c 100644 --- a/src/VariableSelector.cpp +++ b/src/VariableSelector.cpp @@ -1396,6 +1396,8 @@ VariableSelector::itemize_array(CGContext& cg_context, const ArrayVariable* av) continue; if (CGOptions::ccomp() && iv->is_packed_aggregate_field_var()) continue; + if (iv->type->is_float()) + continue; // unfortunately different std::map implementations give us diff. order, we // have to sort them to generate consistant outputs across diff. platforms bool insert_middle = false; From d69927c63879ea925de558c62c73fc296a98967d Mon Sep 17 00:00:00 2001 From: Yang Chen Date: Sat, 20 Sep 2014 22:22:27 -0700 Subject: [PATCH 2/7] add safe_float functions * modified versions of safe_float functions suggested by Pascal * include float.h when --float is passed * should have mentioned in previous commit: floating point support is disabled by default, passing --float to enable it --- runtime/safe_math.m4 | 73 ++++++++++++++++++++++++++++++++++++++++++++ src/OutputMgr.cpp | 3 ++ 2 files changed, 76 insertions(+) diff --git a/runtime/safe_math.m4 b/runtime/safe_math.m4 index 4a286c303..314a1cdcc 100644 --- a/runtime/safe_math.m4 +++ b/runtime/safe_math.m4 @@ -287,4 +287,77 @@ safe_unsigned_math(uint32_t,UINT32_MAX) safe_unsigned_math(uint64_t,UINT64_MAX) #endif +dnl safe floating point computation, based on Pascal's suggestion + +define(`safe_float_math',` + +STATIC $1 +FUNC_NAME(add_func_$1_f_f)($1 sf1, $1 sf2 LOG_INDEX) +{ + LOG_EXEC + return +#ifndef UNSAFE_FLOAT + (fabs((0.5 * sf1) + (0.5 * sf2)) > (0.5 * DBL_MAX)) ? + UNDEFINED(sf1) : +#endif + (sf1 + sf2); +} + +STATIC $1 +FUNC_NAME(sub_func_$1_f_f)($1 sf1, $1 sf2 LOG_INDEX) +{ + LOG_EXEC + return +#ifndef UNSAFE_FLOAT + (fabs((0.5 * sf1) - (0.5 * sf2)) > (0.5 * DBL_MAX)) ? + UNDEFINED(sf1) : +#endif + (sf1 - sf2); +} + +STATIC $1 +FUNC_NAME(mul_func_$1_f_f)($1 sf1, $1 sf2 LOG_INDEX) +{ + LOG_EXEC + return +#ifndef UNSAFE_FLOAT + (fabs((0x1.0p-512 * sf1) * (0x1.0p-512 * sf2)) > (0x1.0p-1024 * DBL_MAX)) ? + UNDEFINED(sf1) : +#endif + (sf1 * sf2); +} + +STATIC $1 +FUNC_NAME(div_func_$1_f_f)($1 sf1, $1 sf2 LOG_INDEX) +{ + LOG_EXEC + return +#ifndef UNSAFE_FLOAT + (((sf2 == 0.0) || (fabs((0x1.0p-600 * sf1) / (0x1.0p600 * sf2))) > (0x1.0p-1000 * DBL_MAX))) ? + UNDEFINED(sf1) : +#endif + (sf1 / sf2); +} + +') + +safe_float_math(float) + +define(`safe_float_conversion',` +STATIC $2 +FUNC_NAME(convert_func_$1_to_$2)($1 sf1 LOG_INDEX) +{ + LOG_EXEC + return +#ifndef UNSAFE_FLOAT + ((sf1 <= $3) || (sf1 >= $4)) ? + UNDEFINED($4) : +#endif + (($2)(sf1)); +} +') + +safe_float_conversion(float, int32_t, INT32_MIN, INT32_MAX) + + #endif diff --git a/src/OutputMgr.cpp b/src/OutputMgr.cpp index a03c5e8c7..d5d300782 100644 --- a/src/OutputMgr.cpp +++ b/src/OutputMgr.cpp @@ -286,6 +286,9 @@ OutputMgr::OutputHeader(int argc, char *argv[], unsigned long seed) out << "#define NO_LONGLONG" << std::endl; out << endl; } + if (!CGOptions::enable_float()) { + out << "#include \n"; + } ExtensionMgr::OutputHeader(out); From 51197eca71b717fac19a9bbaeeb1f07d5c413545 Mon Sep 17 00:00:00 2001 From: Yang Chen Date: Sun, 21 Sep 2014 01:00:52 -0700 Subject: [PATCH 3/7] Silly bug fix include float.h and math.h when floating point support is enabled --- src/OutputMgr.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/OutputMgr.cpp b/src/OutputMgr.cpp index d5d300782..a68d81db4 100644 --- a/src/OutputMgr.cpp +++ b/src/OutputMgr.cpp @@ -286,8 +286,9 @@ OutputMgr::OutputHeader(int argc, char *argv[], unsigned long seed) out << "#define NO_LONGLONG" << std::endl; out << endl; } - if (!CGOptions::enable_float()) { + if (CGOptions::enable_float()) { out << "#include \n"; + out << "#include \n"; } ExtensionMgr::OutputHeader(out); From 5dc6c96582389521a7c37d3fa8f8320bf6341a35 Mon Sep 17 00:00:00 2001 From: Yang Chen Date: Sun, 21 Sep 2014 03:57:17 -0700 Subject: [PATCH 4/7] probably works now need more testing --- src/FunctionInvocation.cpp | 50 ++++++------- src/FunctionInvocationBinary.cpp | 10 +++ src/FunctionInvocationBinary.h | 2 + src/FunctionInvocationUnary.cpp | 31 ++++---- src/SafeOpFlags.cpp | 117 +++++++++++++++++++++++++++++-- src/SafeOpFlags.h | 18 ++++- src/StatementAssign.cpp | 9 ++- src/StatementAssign.h | 1 + src/StatementFor.cpp | 5 +- src/Type.cpp | 3 + 10 files changed, 189 insertions(+), 57 deletions(-) diff --git a/src/FunctionInvocation.cpp b/src/FunctionInvocation.cpp index 91f4649fc..639784dbe 100644 --- a/src/FunctionInvocation.cpp +++ b/src/FunctionInvocation.cpp @@ -141,17 +141,15 @@ FunctionInvocation::make_random_unary(CGContext &cg_context, const Type* type) DEPTH_GUARD_BY_TYPE_RETURN(dtFunctionInvocationRandomUnary, NULL); assert(type); eUnaryOps op; - do { + do { op = (eUnaryOps)(rnd_upto(MAX_UNARY_OP, UNARY_OPS_PROB_FILTER)); } while (type->is_float() && !UnaryOpWorksForFloat(op)); ERROR_GUARD(NULL); SafeOpFlags *flags = NULL; - if (op == eMinus) { - flags = SafeOpFlags::make_random(sOpUnary); - ERROR_GUARD(NULL); - type = flags->get_lhs_type(); - assert(type); - } + flags = SafeOpFlags::make_random_unary(type, NULL, op); + ERROR_GUARD(NULL); + type = flags->get_lhs_type(); + assert(type); FunctionInvocation *fi = FunctionInvocationUnary::CreateFunctionInvocationUnary(cg_context, op, flags); @@ -180,7 +178,7 @@ FunctionInvocation::make_random_binary(CGContext &cg_context, const Type* type) } while (type->is_float() && !BinaryOpWorksForFloat(op)); ERROR_GUARD(NULL); assert(type); - SafeOpFlags *flags = SafeOpFlags::make_random(sOpBinary, op); + SafeOpFlags *flags = SafeOpFlags::make_random_binary(type, NULL, NULL, sOpBinary, op); assert(flags); ERROR_GUARD(NULL); FunctionInvocationBinary *fi = FunctionInvocationBinary::CreateFunctionInvocationBinary(cg_context, op, flags); @@ -192,6 +190,11 @@ FunctionInvocation::make_random_binary(CGContext &cg_context, const Type* type) const Type* lhs_type = flags->get_lhs_type(); const Type* rhs_type = flags->get_rhs_type(); assert(lhs_type && rhs_type); + if (!BinaryOpWorksForFloat(op)) { + assert(!lhs_type->is_float() && "lhs_type is float!"); + assert(!rhs_type->is_float() && "rhs_type is float!"); + } + Expression *lhs = Expression::make_random(lhs_cg_context, lhs_type); ERROR_GUARD_AND_DEL1(NULL, fi); Expression *rhs = 0; @@ -233,7 +236,8 @@ FunctionInvocation::make_random_binary(CGContext &cg_context, const Type* type) else { rhs = Expression::make_random(rhs_cg_context, rhs_type); // avoid divide by zero or possible zero (reached by pointer comparison) - if ((op == eMod || op == eDiv) && (rhs->equals(0) || rhs->is_0_or_1())) { + if ((op == eMod || op == eDiv) && (rhs->equals(0) || rhs->is_0_or_1()) && + !lhs_type->is_float() && !rhs_type->is_float()) { VectorFilter f; f.add(eMod).add(eDiv).add(eLShift).add(eRShift); op = (eBinaryOps)(rnd_upto(MAX_BINARY_OP, &f)); @@ -244,6 +248,10 @@ FunctionInvocation::make_random_binary(CGContext &cg_context, const Type* type) } ERROR_GUARD_AND_DEL2(NULL, fi, lhs); + if (!BinaryOpWorksForFloat(op)) { + assert(!lhs->get_type().is_float() && "lhs is of float!"); + assert(!rhs->get_type().is_float() && "rhs is of float!"); + } if (CompatibleChecker::compatible_check(lhs, rhs)) { Error::set_error(COMPATIBLE_CHECK_ERROR); @@ -273,7 +281,7 @@ FunctionInvocation::make_random_binary_ptr_comparison(CGContext &cg_context) { eBinaryOps op = rnd_flipcoin(50) ? eCmpEq : eCmpNe; ERROR_GUARD(NULL); - SafeOpFlags *flags = SafeOpFlags::make_random(sOpBinary); + SafeOpFlags *flags = SafeOpFlags::make_random_binary(get_int_type(), NULL, NULL, sOpBinary, op); ERROR_GUARD(NULL); FunctionInvocation *fi = FunctionInvocationBinary::CreateFunctionInvocationBinary(cg_context, op, flags); @@ -541,32 +549,15 @@ FunctionInvocation::visit_facts(vector& inputs, CGContext& cg_conte return ok; } -/* - * Build an "invocation" of a unary operation. - */ -FunctionInvocation * -FunctionInvocation::make_unary(CGContext &cg_context, eUnaryOps op, - Expression *operand) -{ - assert(0 && "Dead function!"); - DEPTH_GUARD_BY_TYPE_RETURN(dtFunctionInvocationUnary, NULL); - SafeOpFlags *flags = SafeOpFlags::make_random(sOpUnary); - ERROR_GUARD(NULL); - FunctionInvocation *fi = FunctionInvocationUnary::CreateFunctionInvocationUnary(cg_context, op, flags); - fi->param_value.push_back(operand); - - return fi; -} - /* * Build an "invocation" of a binary operation. */ FunctionInvocation * FunctionInvocation::make_binary(CGContext &cg_context, eBinaryOps op, - Expression *lhs, Expression *rhs) + Expression *lhs, Expression *rhs) { DEPTH_GUARD_BY_TYPE_RETURN(dtFunctionInvocationBinary, NULL); - SafeOpFlags *flags = SafeOpFlags::make_random(sOpBinary); + SafeOpFlags *flags = SafeOpFlags::make_random_binary(NULL, &(lhs->get_type()), &(rhs->get_type()), sOpBinary, op); ERROR_GUARD(NULL); FunctionInvocation *fi = FunctionInvocationBinary::CreateFunctionInvocationBinary(cg_context, op, flags); fi->param_value.push_back(lhs); @@ -644,7 +635,6 @@ FunctionInvocation::BinaryOpWorksForFloat(eBinaryOps op) case eSub: case eMul: case eDiv: - case eMod: case eCmpGt: case eCmpLt: case eCmpGe: diff --git a/src/FunctionInvocationBinary.cpp b/src/FunctionInvocationBinary.cpp index b4c98a170..2c1617e50 100644 --- a/src/FunctionInvocationBinary.cpp +++ b/src/FunctionInvocationBinary.cpp @@ -53,6 +53,7 @@ FunctionInvocationBinary::CreateFunctionInvocationBinary(CGContext &cg_context, SafeOpFlags *flags) { FunctionInvocationBinary *fi = NULL; + assert(flags); if (flags && FunctionInvocationBinary::safe_ops(op)) { bool op1 = flags->get_op1_sign(); @@ -188,12 +189,21 @@ FunctionInvocationBinary::is_0_or_1(void) const return eFunc==eCmpGt || eFunc==eCmpLt || eFunc==eCmpGe || eFunc==eCmpLe || eFunc==eCmpEq || eFunc==eCmpNe; } +bool +FunctionInvocationBinary::is_return_type_float() const +{ + assert(op_flags); + return op_flags->get_op_size() == sFloat; +} + /* * XXX --- we should memoize the types of "standard functions." */ const Type & FunctionInvocationBinary::get_type(void) const { + if (is_return_type_float()) + return Type::get_simple_type(eFloat); switch (eFunc) { default: assert(!"invalid operator in FunctionInvocationBinary::get_type()"); diff --git a/src/FunctionInvocationBinary.h b/src/FunctionInvocationBinary.h index 90d9202f6..e4c60499f 100644 --- a/src/FunctionInvocationBinary.h +++ b/src/FunctionInvocationBinary.h @@ -92,6 +92,8 @@ class FunctionInvocationBinary : public FunctionInvocation private: + bool is_return_type_float() const; + static bool safe_ops(eBinaryOps op); // unimplemented diff --git a/src/FunctionInvocationUnary.cpp b/src/FunctionInvocationUnary.cpp index 4cf26b590..e306ddfaf 100644 --- a/src/FunctionInvocationUnary.cpp +++ b/src/FunctionInvocationUnary.cpp @@ -209,21 +209,28 @@ FunctionInvocationUnary::Output(std::ostream &out) const break; case eMinus: - if (CGOptions::avoid_signed_overflow()) { + if (CGOptions::avoid_signed_overflow()) { assert(op_flags); - string fname = op_flags->to_string(eFunc); - int id = SafeOpFlags::to_id(fname); - // don't use safe math wrapper if this function is specified in "--safe-math-wrapper" - if (CGOptions::safe_math_wrapper(id)) { - out << fname << "("; - if (CGOptions::math_notmp()) { - out << tmp_var << ", "; + if (op_flags->get_op_size() != sFloat) { + string fname = op_flags->to_string(eFunc); + int id = SafeOpFlags::to_id(fname); + // don't use safe math wrapper if this function is specified in "--safe-math-wrapper" + if (CGOptions::safe_math_wrapper(id)) { + out << fname << "("; + if (CGOptions::math_notmp()) { + out << tmp_var << ", "; + } + param_value[0]->Output(out); + if (CGOptions::identify_wrappers()) { + out << ", " << id; + } + out << ")"; + break; } + } + else { + OutputStandardFuncName(eFunc, out); param_value[0]->Output(out); - if (CGOptions::identify_wrappers()) { - out << ", " << id; - } - out << ")"; break; } } diff --git a/src/SafeOpFlags.cpp b/src/SafeOpFlags.cpp index 1adb2de9f..9dfe682fe 100644 --- a/src/SafeOpFlags.cpp +++ b/src/SafeOpFlags.cpp @@ -38,6 +38,7 @@ #include "Probabilities.h" #include "DepthSpec.h" #include "MspFilters.h" +#include "CGOptions.h" using namespace std; @@ -81,6 +82,7 @@ SafeOpFlags::flags_to_type(bool sign, enum SafeOpSize size) case sInt16: return eShort; case sInt32: return eInt; case sInt64: return eLongLong; + case sFloat: return eFloat; default: assert(0); break; } } @@ -113,18 +115,98 @@ SafeOpFlags::get_rhs_type(void) return &t; } +bool +SafeOpFlags::return_float_type(const Type *rv_type, const Type *op1_type, const Type *op2_type, + eBinaryOps bop) +{ + if (!CGOptions::enable_float()) + return false; + if (rv_type && rv_type->is_float()) + return true; + if ((op1_type && op1_type->is_float()) || (op2_type && op2_type->is_float())) + return true; + if (!FunctionInvocation::BinaryOpWorksForFloat(bop)) + return false; + return false; +} + +bool +SafeOpFlags::return_float_type(const Type *rv_type, const Type *op1_type, eUnaryOps uop) +{ + if (!CGOptions::enable_float()) + return false; + if (rv_type && rv_type->is_float()) + return true; + if (op1_type && op1_type->is_float()) + return true; + if (!FunctionInvocation::UnaryOpWorksForFloat(uop)) + return false; + return false; +} + + SafeOpFlags* -SafeOpFlags::make_random(SafeOpKind op_kind, eBinaryOps op) +SafeOpFlags::make_random_unary(const Type *rv_type, const Type *op1_type, eUnaryOps uop) +{ + SafeOpFlags *flags = new SafeOpFlags(); + assert("new SafeOpFlags fail!"); + bool rv_is_float = return_float_type(rv_type, op1_type, uop); + + // floating point is always signed + if (rv_is_float) { + assert(FunctionInvocation::UnaryOpWorksForFloat(uop) && "Invalid unary op"); + flags->op1_ = true; + } + else { + flags->op1_ = rnd_flipcoin(SafeOpsSignedProb); + } + flags->op2_ = flags->op1_; + + // ISSUE: in the old code, is_func is always true + // Probably need to be fixed later. + flags->is_func_ = true; + + MspSafeOpSizeFilter *filter = new MspSafeOpSizeFilter(MAX_BINARY_OP); + Probabilities::register_extra_filter(pSafeOpsSizeProb, filter); + if (rv_is_float) { + assert(CGOptions::enable_float()); + flags->op_size_ = sFloat; + } + else { + flags->op_size_ = (SafeOpSize)rnd_upto(MAX_SAFE_OP_SIZE-1, SAFE_OPS_SIZE_PROB_FILTER); + } + Probabilities::unregister_extra_filter(pSafeOpsSizeProb, filter); + + delete filter; + return flags; +} + +SafeOpFlags* +SafeOpFlags::make_random_binary(const Type *rv_type, const Type *op1_type, const Type *op2_type, + SafeOpKind op_kind, eBinaryOps bop) { DEPTH_GUARD_BY_TYPE_RETURN_WITH_FLAG(dtSafeOpFlags, op_kind, NULL); SafeOpFlags *flags = new SafeOpFlags(); assert("new SafeOpFlags fail!"); + bool rv_is_float = return_float_type(rv_type, op1_type, op2_type, bop); - flags->op1_ = rnd_flipcoin(SafeOpsSignedProb); + // floating point is always signed + if (rv_is_float) { + if (op_kind == sOpBinary) { + assert(FunctionInvocation::BinaryOpWorksForFloat(bop) && "Invalid binary op"); + } + flags->op1_ = true; + } + else { + flags->op1_ = rnd_flipcoin(SafeOpsSignedProb); + } ERROR_GUARD_AND_DEL1(NULL, flags); if (op_kind == sOpBinary) { - flags->op2_ = rnd_flipcoin(SafeOpsSignedProb); + if (rv_is_float) + flags->op2_ = true; + else + flags->op2_ = rnd_flipcoin(SafeOpsSignedProb); ERROR_GUARD_AND_DEL1(NULL, flags); } else { @@ -135,10 +217,16 @@ SafeOpFlags::make_random(SafeOpKind op_kind, eBinaryOps op) // Probably need to be fixed later. flags->is_func_ = true; - MspSafeOpSizeFilter *filter = new MspSafeOpSizeFilter(op); + MspSafeOpSizeFilter *filter = new MspSafeOpSizeFilter(bop); Probabilities::register_extra_filter(pSafeOpsSizeProb, filter); - flags->op_size_ = (SafeOpSize)rnd_upto(MAX_SAFE_OP_SIZE, SAFE_OPS_SIZE_PROB_FILTER); + if (rv_is_float) { + assert(CGOptions::enable_float()); + flags->op_size_ = sFloat; + } + else { + flags->op_size_ = (SafeOpSize)rnd_upto(MAX_SAFE_OP_SIZE-1, SAFE_OPS_SIZE_PROB_FILTER); + } Probabilities::unregister_extra_filter(pSafeOpsSizeProb, filter); ERROR_GUARD_AND_DEL2(NULL, flags, filter); @@ -209,10 +297,28 @@ SafeOpFlags::~SafeOpFlags() // Nothing to do } +std::string +SafeOpFlags::safe_float_func_string(enum eBinaryOps op) const +{ + string s; + switch (op) { + case eAdd: s = "safe_add_"; break; + case eSub: s = "safe_sub_"; break; + case eMul: s = "safe_mul_"; break; + case eDiv: s = "safe_div_"; break; + default: assert(0); break; + } + s += "func_float_f_f"; + return s; + +} + /* find the safe math function/macro name */ std::string SafeOpFlags::to_string(enum eBinaryOps op) const { + if (op_size_ == sFloat) + return safe_float_func_string(op); string s; switch (op) { case eAdd: s = "safe_add_"; break; @@ -237,6 +343,7 @@ SafeOpFlags::to_string(enum eBinaryOps op) const std::string SafeOpFlags::to_string(enum eUnaryOps op) const { + assert((op_size_ != sFloat) && "No safe unary function on floating point!"); string s; switch (op) { case eMinus: s = "safe_unary_minus_"; break; diff --git a/src/SafeOpFlags.h b/src/SafeOpFlags.h index 121e1025b..8cb0250a9 100644 --- a/src/SafeOpFlags.h +++ b/src/SafeOpFlags.h @@ -47,12 +47,16 @@ enum SafeOpSize { sInt16, sInt32, sInt64, + sFloat, }; -#define MAX_SAFE_OP_SIZE ((SafeOpSize) (sInt64+1)) +#define MAX_SAFE_OP_SIZE ((SafeOpSize) (sFloat+1)) class SafeOpFlags { public: - static SafeOpFlags *make_random(SafeOpKind op_kind, eBinaryOps op = MAX_BINARY_OP); + static SafeOpFlags *make_random_binary(const Type *rv_type, const Type *op1_type, const Type *op2_type, + SafeOpKind op_kind, eBinaryOps op); + + static SafeOpFlags *make_random_unary(const Type *rv_type, const Type *op1_type, eUnaryOps op); static SafeOpFlags *make_dummy_flags(); @@ -75,7 +79,7 @@ class SafeOpFlags { bool get_op2_sign() { return op2_; } - enum SafeOpSize get_op_size() { return op_size_; } + enum SafeOpSize get_op_size() const { return op_size_; } std::string to_string(enum eBinaryOps op) const; std::string to_string(enum eUnaryOps op) const; @@ -92,6 +96,14 @@ class SafeOpFlags { void OutputSign(std::ostream &out, bool sgnd) const; + bool static return_float_type(const Type *rv_type, const Type *op1_type, const Type *op2_type, + eBinaryOps op); + + bool static return_float_type(const Type *rv_type, const Type *op1_type, + eUnaryOps uop); + + std::string safe_float_func_string(enum eBinaryOps op) const; + SafeOpFlags(); SafeOpFlags(const SafeOpFlags &flags); diff --git a/src/StatementAssign.cpp b/src/StatementAssign.cpp index dc27aef25..a2761e71f 100644 --- a/src/StatementAssign.cpp +++ b/src/StatementAssign.cpp @@ -210,7 +210,7 @@ StatementAssign::make_random(CGContext &cg_context, const Type* type, const CVQu cg_context.merge_param_context(lhs_cg_context, true); ERROR_GUARD_AND_DEL2(NULL, e, lhs); - StatementAssign *stmt_assign = make_possible_compound_assign(cg_context, *lhs, op, *e); + StatementAssign *stmt_assign = make_possible_compound_assign(cg_context, type, *lhs, op, *e); ERROR_GUARD_AND_DEL2(NULL, e, lhs); return stmt_assign; } @@ -230,6 +230,7 @@ StatementAssign::safe_assign(eAssignOps op) StatementAssign * StatementAssign::make_possible_compound_assign(CGContext &cg_context, + const Type *type, const Lhs &l, eAssignOps op, const Expression &e) @@ -241,7 +242,6 @@ StatementAssign::make_possible_compound_assign(CGContext &cg_context, std::string tmp2; if (bop != MAX_BINARY_OP) { - //SafeOpFlags *local_fs = SafeOpFlags::make_random(sOpAssign, true); SafeOpFlags *local_fs = NULL; FunctionInvocation* fi = NULL; if (safe_assign(op)) { @@ -249,7 +249,7 @@ StatementAssign::make_possible_compound_assign(CGContext &cg_context, fi = new FunctionInvocationBinary(bop, local_fs); } else { - local_fs = SafeOpFlags::make_random(sOpAssign); + local_fs = SafeOpFlags::make_random_binary(type, &(l.get_type()), &(l.get_type()), sOpAssign, bop); ERROR_GUARD(NULL); fi = FunctionInvocationBinary::CreateFunctionInvocationBinary(cg_context, bop, local_fs); tmp1 = dynamic_cast(fi)->get_tmp_var1(); @@ -272,7 +272,7 @@ StatementAssign::make_possible_compound_assign(CGContext &cg_context, } #endif if (op != eSimpleAssign) { - fs = SafeOpFlags::make_random(sOpAssign); + fs = SafeOpFlags::make_random_binary(type, &(l.get_type()), &(rhs->get_type()), sOpAssign, bop); bool op1 = fs->get_op1_sign(); bool op2 = fs->get_op2_sign(); enum SafeOpSize size = fs->get_op_size(); @@ -593,7 +593,6 @@ StatementAssign::AssignOpWorksForFloat(eAssignOps op) case eSimpleAssign: case eMulAssign: case eDivAssign: - case eRemAssign: case eAddAssign: case eSubAssign: return true; diff --git a/src/StatementAssign.h b/src/StatementAssign.h index fe775a282..b0b94a384 100644 --- a/src/StatementAssign.h +++ b/src/StatementAssign.h @@ -82,6 +82,7 @@ class StatementAssign : public Statement static StatementAssign *make_random(CGContext &cg_context, const Type* type=0, const CVQualifiers* qfer=0); static StatementAssign *make_possible_compound_assign(CGContext &cg_context, + const Type *type, const Lhs &l, eAssignOps op, const Expression &e); diff --git a/src/StatementFor.cpp b/src/StatementFor.cpp index 5a207f17b..30d47812c 100644 --- a/src/StatementFor.cpp +++ b/src/StatementFor.cpp @@ -201,7 +201,8 @@ StatementFor::make_iteration(CGContext& cg_context, StatementAssign*& init, Expr assert(var); Lhs* lhs = new Lhs(*var); ERROR_GUARD_AND_DEL1(NULL, c_init); - SafeOpFlags *flags1 = SafeOpFlags::make_random(sOpAssign); + eBinaryOps bop = StatementAssign::compound_to_binary_ops(incr_op); + SafeOpFlags *flags1 = SafeOpFlags::make_random_binary(var->type, var->type, var->type, sOpAssign, bop); ERROR_GUARD_AND_DEL2(NULL, c_init, lhs); init = new StatementAssign(cg_context.get_current_block(), *lhs, *c_init, eSimpleAssign, flags1); @@ -235,7 +236,7 @@ StatementFor::make_iteration(CGContext& cg_context, StatementAssign*& init, Expr if (bound != INVALID_BOUND) { incr = new StatementAssign(cg_context.get_current_block(), *lhs1, *c_incr, incr_op); } else { - incr = StatementAssign::make_possible_compound_assign(cg_context, *lhs1, incr_op, *c_incr); + incr = StatementAssign::make_possible_compound_assign(cg_context, &(v->get_type()), *lhs1, incr_op, *c_incr); } return var; } diff --git a/src/Type.cpp b/src/Type.cpp index 1617aa55e..fd12c2ba1 100644 --- a/src/Type.cpp +++ b/src/Type.cpp @@ -1418,6 +1418,9 @@ Type::is_convertable(const Type* t) const if (this == t) return true; if (eType == eSimple && t->eType == eSimple) { + // forbiden conversion from float to int + if (t->is_float() && !is_float()) + return false; if ((simple_type != eVoid && t->simple_type != eVoid) || simple_type == t->simple_type) return true; From 6677b4e1aab60058567030e391e8fbffe76abde5 Mon Sep 17 00:00:00 2001 From: Yang Chen Date: Sun, 21 Sep 2014 15:41:36 -0700 Subject: [PATCH 5/7] removed an useless constraint on choose_fun since we changed is_convertable in Type.cpp, this constraint is no longer needed now --- src/Function.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Function.cpp b/src/Function.cpp index 89991ea42..958cfdee2 100644 --- a/src/Function.cpp +++ b/src/Function.cpp @@ -280,8 +280,8 @@ Function::choose_func(vector funcs, continue; // Changing the behavior of is_convertable is quite dangerous. // Making constraint here has less global effect. - if (type && (*i)->return_type->is_float() && !type->is_float()) - continue; + //if (type && (*i)->return_type->is_float() && !type->is_float()) + // continue; if (qfer && (*i)->rv && !qfer->match((*i)->rv->qfer)) continue; // We cannot call a function that has an as-yet unknown effect. From bc4444e785c8548da42919f00ff54b8594b372af Mon Sep 17 00:00:00 2001 From: Yang Chen Date: Sun, 21 Sep 2014 15:56:26 -0700 Subject: [PATCH 6/7] use FLT_MAX in safe_float functions --- runtime/safe_math.m4 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runtime/safe_math.m4 b/runtime/safe_math.m4 index 314a1cdcc..15ad04e6a 100644 --- a/runtime/safe_math.m4 +++ b/runtime/safe_math.m4 @@ -297,7 +297,7 @@ FUNC_NAME(add_func_$1_f_f)($1 sf1, $1 sf2 LOG_INDEX) LOG_EXEC return #ifndef UNSAFE_FLOAT - (fabs((0.5 * sf1) + (0.5 * sf2)) > (0.5 * DBL_MAX)) ? + (fabs((0.5 * sf1) + (0.5 * sf2)) > (0.5 * FLT_MAX)) ? UNDEFINED(sf1) : #endif (sf1 + sf2); @@ -309,7 +309,7 @@ FUNC_NAME(sub_func_$1_f_f)($1 sf1, $1 sf2 LOG_INDEX) LOG_EXEC return #ifndef UNSAFE_FLOAT - (fabs((0.5 * sf1) - (0.5 * sf2)) > (0.5 * DBL_MAX)) ? + (fabs((0.5 * sf1) - (0.5 * sf2)) > (0.5 * FLT_MAX)) ? UNDEFINED(sf1) : #endif (sf1 - sf2); @@ -321,7 +321,7 @@ FUNC_NAME(mul_func_$1_f_f)($1 sf1, $1 sf2 LOG_INDEX) LOG_EXEC return #ifndef UNSAFE_FLOAT - (fabs((0x1.0p-512 * sf1) * (0x1.0p-512 * sf2)) > (0x1.0p-1024 * DBL_MAX)) ? + (fabs((0x1.0p-512 * sf1) * (0x1.0p-512 * sf2)) > (0x1.0p-1024 * FLT_MAX)) ? UNDEFINED(sf1) : #endif (sf1 * sf2); @@ -333,7 +333,7 @@ FUNC_NAME(div_func_$1_f_f)($1 sf1, $1 sf2 LOG_INDEX) LOG_EXEC return #ifndef UNSAFE_FLOAT - (((sf2 == 0.0) || (fabs((0x1.0p-600 * sf1) / (0x1.0p600 * sf2))) > (0x1.0p-1000 * DBL_MAX))) ? + (((sf2 == 0.0) || (fabs((0x1.0p-600 * sf1) / (0x1.0p600 * sf2))) > (0x1.0p-1000 * FLT_MAX))) ? UNDEFINED(sf1) : #endif (sf1 / sf2); From 0aaafbf811d29989f3b6667aae73c520bb10ad55 Mon Sep 17 00:00:00 2001 From: Yang Chen Date: Mon, 22 Sep 2014 20:44:21 -0700 Subject: [PATCH 7/7] new safe_float_math function provided by Pascal --- runtime/safe_math.m4 | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/runtime/safe_math.m4 b/runtime/safe_math.m4 index 15ad04e6a..49eaf570a 100644 --- a/runtime/safe_math.m4 +++ b/runtime/safe_math.m4 @@ -297,7 +297,7 @@ FUNC_NAME(add_func_$1_f_f)($1 sf1, $1 sf2 LOG_INDEX) LOG_EXEC return #ifndef UNSAFE_FLOAT - (fabs((0.5 * sf1) + (0.5 * sf2)) > (0.5 * FLT_MAX)) ? + (fabs$2((0.5$2 * sf1) + (0.5$2 * sf2)) > (0.5$2 * $3)) ? UNDEFINED(sf1) : #endif (sf1 + sf2); @@ -309,7 +309,7 @@ FUNC_NAME(sub_func_$1_f_f)($1 sf1, $1 sf2 LOG_INDEX) LOG_EXEC return #ifndef UNSAFE_FLOAT - (fabs((0.5 * sf1) - (0.5 * sf2)) > (0.5 * FLT_MAX)) ? + (fabs$2((0.5$2 * sf1) - (0.5$2 * sf2)) > (0.5$2 * $3)) ? UNDEFINED(sf1) : #endif (sf1 - sf2); @@ -319,9 +319,9 @@ STATIC $1 FUNC_NAME(mul_func_$1_f_f)($1 sf1, $1 sf2 LOG_INDEX) { LOG_EXEC - return + return #ifndef UNSAFE_FLOAT - (fabs((0x1.0p-512 * sf1) * (0x1.0p-512 * sf2)) > (0x1.0p-1024 * FLT_MAX)) ? + (fabs((0x1.0p-100$2 * sf1) * ($4 * sf2)) > (0x1.0p-100$2 * ($4 * $3))) ? UNDEFINED(sf1) : #endif (sf1 * sf2); @@ -331,9 +331,9 @@ STATIC $1 FUNC_NAME(div_func_$1_f_f)($1 sf1, $1 sf2 LOG_INDEX) { LOG_EXEC - return + return #ifndef UNSAFE_FLOAT - (((sf2 == 0.0) || (fabs((0x1.0p-600 * sf1) / (0x1.0p600 * sf2))) > (0x1.0p-1000 * FLT_MAX))) ? + ((fabs(sf2) < 1.0$2) && (((sf2 == 0.0$2) || (fabs(($5 * sf1) / (0x1.0p100$2 * sf2))) > (0x1.0p-100$2 * ($5 * $3))))) ? UNDEFINED(sf1) : #endif (sf1 / sf2); @@ -341,7 +341,8 @@ FUNC_NAME(div_func_$1_f_f)($1 sf1, $1 sf2 LOG_INDEX) ') -safe_float_math(float) +safe_float_math(float,f,FLT_MAX,0x1.0p-28f,0x1.0p-49f) +safe_float_math(double,,DBL_MAX,0x1.0p-924,0x1.0p-974) define(`safe_float_conversion',` STATIC $2