diff --git a/lib/exprengine.cpp b/lib/exprengine.cpp index fc1dc847c..bccb3f239 100644 --- a/lib/exprengine.cpp +++ b/lib/exprengine.cpp @@ -188,10 +188,7 @@ namespace { return std::dynamic_pointer_cast(it->second); if (tok->varId() == 0) return std::shared_ptr(); - unsigned int size = 1; - for (const auto &dim : tok->variable()->dimensions()) - size *= dim.num; - auto val = std::make_shared(getNewSymbolName(), size, size); + auto val = std::make_shared(getNewSymbolName(), tok->variable()); memory[tok->varId()] = val; return val; } @@ -240,41 +237,148 @@ namespace { }; } +static ExprEngine::ValuePtr simplifyValue(ExprEngine::ValuePtr origValue) +{ + auto b = std::dynamic_pointer_cast(origValue); + if (!b) + return origValue; + if (!b->op1 || !b->op2) + return origValue; + auto intRange1 = std::dynamic_pointer_cast(b->op1); + auto intRange2 = std::dynamic_pointer_cast(b->op1); + if (intRange1 && intRange2 && intRange1->minValue == intRange1->maxValue && intRange2->minValue == intRange2->maxValue) { + const std::string &binop = b->binop; + int128_t v; + if (binop == "+") + v = intRange1->minValue + intRange2->minValue; + else if (binop == "-") + v = intRange1->minValue - intRange2->minValue; + else if (binop == "*") + v = intRange1->minValue * intRange2->minValue; + else if (binop == "/" && intRange2->minValue != 0) + v = intRange1->minValue / intRange2->minValue; + else if (binop == "%" && intRange2->minValue != 0) + v = intRange1->minValue % intRange2->minValue; + else + return origValue; + return std::make_shared(ExprEngine::str(v), v, v); + } + return origValue; +} + + +ExprEngine::ArrayValue::ArrayValue(const std::string &name, ExprEngine::ValuePtr size, ExprEngine::ValuePtr value) + : Value(name) + , size(size) +{ + assign(ExprEngine::ValuePtr(), value); +} + +ExprEngine::ArrayValue::ArrayValue(const std::string &name, const Variable *var) + : Value(name) +{ + if (var) { + int sz = 1; + for (const auto &dim : var->dimensions()) { + if (!dim.known) { + sz = -1; + break; + } + sz *= dim.num; + } + if (sz >= 1) + size = std::make_shared(std::to_string(sz), sz, sz); + } + assign(ExprEngine::ValuePtr(), std::make_shared()); +} + void ExprEngine::ArrayValue::assign(ExprEngine::ValuePtr index, ExprEngine::ValuePtr value) { - auto i1 = std::dynamic_pointer_cast(index); - if (i1) { - if (i1->minValue == i1->maxValue && i1->minValue >= 0 && i1->maxValue < data.size()) - data[i1->minValue] = value; + if (!index) + data.clear(); + if (value) { + ExprEngine::ArrayValue::IndexAndValue indexAndValue = {index, value}; + data.push_back(indexAndValue); } } void ExprEngine::ArrayValue::clear() { - auto zero = std::make_shared("0", 0, 0); - for (int i = 0; i < data.size(); ++i) - data[i] = zero; + data.clear(); + ExprEngine::ArrayValue::IndexAndValue indexAndValue = { + ExprEngine::ValuePtr(), std::make_shared("0", 0, 0) + }; + data.push_back(indexAndValue); } -ExprEngine::ValuePtr ExprEngine::ArrayValue::read(ExprEngine::ValuePtr index) +static bool isEqual(ExprEngine::ValuePtr v1, ExprEngine::ValuePtr v2) { - auto i1 = std::dynamic_pointer_cast(index); - if (i1) { - if (i1->minValue == i1->maxValue && i1->minValue >= 0 && i1->maxValue < data.size()) - return data[i1->minValue]; + if (!v1 || !v2) + return !v1 && !v2; + // TODO: Maybe we need better logic here: + return v1->getRange() == v2->getRange(); +} + +static bool isNonOverlapping(ExprEngine::ValuePtr v1, ExprEngine::ValuePtr v2) +{ + if (!v1 || !v2) + return false; // Don't know! + auto intRange1 = std::dynamic_pointer_cast(v1); + auto intRange2 = std::dynamic_pointer_cast(v2); + if (intRange1 && intRange2 && (intRange1->minValue > intRange2->maxValue || intRange1->maxValue < intRange2->maxValue)) + return true; + return false; +} + +std::vector ExprEngine::ArrayValue::read(ExprEngine::ValuePtr index) +{ + std::vector ret; + for (const auto indexAndValue : data) { + if (isEqual(index, indexAndValue.index)) + ret.clear(); + if (isNonOverlapping(index, indexAndValue.index)) + continue; + if (!indexAndValue.index && indexAndValue.value->type() == ExprEngine::ValueType::StringLiteralValue) { + auto stringLiteral = std::dynamic_pointer_cast(indexAndValue.value); + if (!stringLiteral) { + ret.push_back(std::make_shared("", -128, 128)); + continue; + } + if (auto i = std::dynamic_pointer_cast(index)) { + if (stringLiteral && i->minValue >= 0 && i->minValue == i->maxValue) { + int c = 0; + if (i->minValue < stringLiteral->size()) + c = stringLiteral->string[i->minValue]; + ret.push_back(std::make_shared(std::to_string(c), c, c)); + continue; + } + } + int cmin = 0, cmax = 0; + for (char c : stringLiteral->string) { + if (c < cmin) + cmin = c; + else if (c > cmax) + cmax = c; + } + ret.push_back(std::make_shared("", cmin, cmax)); + continue; + } + ret.push_back(indexAndValue.value); } - return ExprEngine::ValuePtr(); + return ret; } std::string ExprEngine::ArrayValue::getRange() const { - std::ostringstream r; - r << "["; - for (size_t i = 0; i < data.size(); ++i) { - r << (i==0?"":",") << data[i]->getRange(); + std::ostringstream ostr; + ostr << "size=" << (size ? size->name : std::string("(null)")); + for (const auto indexAndValue : data) { + ostr << ",[" + << (!indexAndValue.index ? std::string(":") : indexAndValue.index->name) + << "]=" + << indexAndValue.value->name; } - r << "]"; - return r.str(); + return ostr.str(); } std::string ExprEngine::PointerValue::getRange() const @@ -550,7 +654,7 @@ static ExprEngine::ValuePtr executeAssign(const Token *tok, Data &data) std::string binop(tok->str()); binop = binop.substr(0, binop.size() - 1); ExprEngine::ValuePtr lhsValue = executeExpression(tok->astOperand1(), data); - assignValue = std::make_shared(binop, lhsValue, rhsValue); + assignValue = simplifyValue(std::make_shared(binop, lhsValue, rhsValue)); } const Token *lhsToken = tok->astOperand1(); @@ -566,15 +670,8 @@ static ExprEngine::ValuePtr executeAssign(const Token *tok, Data &data) // Is it array initialization? const Token *arrayInit = lhsToken->astOperand1(); if (arrayInit && arrayInit->variable() && arrayInit->variable()->nameToken() == arrayInit) { - if (auto strval = std::dynamic_pointer_cast(assignValue)) { - for (size_t i = 0; i < strval->size(); ++i) { - uint8_t c = strval->string[i]; - arrayValue->data[i] = std::make_shared(std::to_string(int(c)),c,c); - } - auto v0 = std::make_shared("0",0,0); - for (size_t i = strval->size(); i < arrayValue->data.size(); ++i) - arrayValue->data[i] = v0; - } + if (assignValue->type() == ExprEngine::ValueType::StringLiteralValue) + arrayValue->assign(ExprEngine::ValuePtr(), assignValue); } else { auto indexValue = executeExpression(lhsToken->astOperand2(), data); arrayValue->assign(indexValue, assignValue); @@ -619,8 +716,7 @@ static ExprEngine::ValuePtr executeFunctionCall(const Token *tok, Data &data) ValueType vt(*argtok->valueType()); vt.pointer = 0; auto anyVal = getValueRangeFromValueType(data.getNewSymbolName(), &vt, *data.settings); - for (int i = 0; i < arrayValue->data.size(); ++i) - arrayValue->data[i] = anyVal; + arrayValue->assign(ExprEngine::ValuePtr(), anyVal); } else if (auto addressOf = std::dynamic_pointer_cast(val)) { ValueType vt(*argtok->valueType()); vt.pointer = 0; @@ -631,15 +727,10 @@ static ExprEngine::ValuePtr executeFunctionCall(const Token *tok, Data &data) // TODO Fix this hardcoding.. if (Token::simpleMatch(tok->astOperand1(), "calloc (") && argValues.size() == 2 && argValues[0] && argValues[1]) { - auto bufferSize = std::make_shared("*", argValues[0], argValues[1]); - ExprEngine::BinOpResult::IntOrFloatValue minValue, maxValue; - bufferSize->getRange(&minValue, &maxValue); - if (!minValue.isFloat()) { - auto buffer = std::make_shared(data.getNewSymbolName(), minValue.intValue, maxValue.intValue); - buffer->clear(); - call(data.callbacks, tok, buffer); - return buffer; - } + auto bufferSize = simplifyValue(std::make_shared("*", argValues[0], argValues[1])); + auto zero = std::make_shared("0", 0, 0); + auto buffer = std::make_shared(data.getNewSymbolName(), bufferSize, zero); + return buffer; } auto val = getValueRangeFromValueType(data.getNewSymbolName(), tok->valueType(), *data.settings); @@ -652,9 +743,10 @@ static ExprEngine::ValuePtr executeArrayIndex(const Token *tok, Data &data) auto arrayValue = data.getArrayValue(tok->astOperand1()); if (arrayValue) { auto indexValue = executeExpression(tok->astOperand2(), data); - auto value = arrayValue->read(indexValue); - call(data.callbacks, tok, value); - return value; + auto values = arrayValue->read(indexValue); + for (auto value: values) + call(data.callbacks, tok, value); + return values[0]; // FIXME we need to split the programstate here } return ExprEngine::ValuePtr(); } @@ -674,7 +766,7 @@ static ExprEngine::ValuePtr executeBinaryOp(const Token *tok, Data &data) ExprEngine::ValuePtr v1 = executeExpression(tok->astOperand1(), data); ExprEngine::ValuePtr v2 = executeExpression(tok->astOperand2(), data); if (v1 && v2) { - auto result = std::make_shared(tok->str(), v1, v2); + auto result = simplifyValue(std::make_shared(tok->str(), v1, v2)); call(data.callbacks, tok, result); return result; } @@ -785,10 +877,8 @@ static void execute(const Token *start, const Token *end, Data &data) for (const Token *tok = start; tok != end; tok = tok->next()) { if (Token::Match(tok, "[;{}]")) data.trackProgramState(tok); - if (tok->variable() && tok->variable()->nameToken() == tok) { - if (tok->variable()->isArray() && tok->variable()->dimensions().size() == 1 && tok->variable()->dimensions()[0].known) { - data.memory[tok->varId()] = std::make_shared(data.getNewSymbolName(), tok->variable()->dimension(0), tok->variable()->dimension(0)); - } + if (tok->variable() && tok->variable()->nameToken() == tok && tok->variable()->isArray()) { + data.memory[tok->varId()] = std::make_shared(data.getNewSymbolName(), tok->variable()); if (Token::Match(tok, "%name% [")) tok = tok->linkAt(1); } diff --git a/lib/exprengine.h b/lib/exprengine.h index e46aedc4a..6b24a33dd 100644 --- a/lib/exprengine.h +++ b/lib/exprengine.h @@ -36,6 +36,7 @@ class Tokenizer; class Scope; class Settings; class Token; +class Variable; #ifdef __GNUC__ typedef __int128_t int128_t; @@ -158,17 +159,8 @@ namespace ExprEngine { public: const int MAXSIZE = 0x100000; - ArrayValue(const std::string &name, int minSize, int maxSize) - : Value(name) - , minSize(minSize) - , maxSize(maxSize) { - if (minSize < 1) - minSize = 1; - // Known size.. - if (minSize == maxSize) - data.resize((minSize < MAXSIZE) ? minSize : MAXSIZE, - std::make_shared()); - } + ArrayValue(const std::string &name, ValuePtr size, ValuePtr value); + ArrayValue(const std::string &name, const Variable *var); ValueType type() const override { return ValueType::ArrayValue; @@ -177,11 +169,14 @@ namespace ExprEngine { void assign(ValuePtr index, ValuePtr value); void clear(); - ValuePtr read(ValuePtr index); + std::vector read(ValuePtr index); - std::vector data; - int minSize; - int maxSize; + struct IndexAndValue { + ValuePtr index; + ValuePtr value; + }; + std::vector data; + ValuePtr size; }; class StringLiteralValue: public Value { diff --git a/test/testexprengine.cpp b/test/testexprengine.cpp index 3a948d900..f144331df 100644 --- a/test/testexprengine.cpp +++ b/test/testexprengine.cpp @@ -113,7 +113,7 @@ private: } void dynamicAllocation1() { - ASSERT_EQUALS("[0]", getRange("char *f() { char *p = calloc(1,1); return p; }", "p")); + ASSERT_EQUALS("size=1,[:]=0", getRange("char *f() { char *p = calloc(1,1); return p; }", "p")); } void expr1() { @@ -180,7 +180,7 @@ private: } void if3() { - ASSERT_EQUALS("1,-2147483648:2147483647,-2147483648:2147483647", getRange("void f() { int x; if (a) { if (b) x=1; } a=x; }", "a=x")); + ASSERT_EQUALS("1,-2147483648:2147483647,-2147483648:2147483647", getRange("void f() { int x; if (a) { if (b) x=1; } x=x; }", "x=x")); } void if4() { @@ -237,7 +237,7 @@ private: } void pointerNull1() { - ASSERT_EQUALS("1", getRange("void f(void *p) { p = NULL; p += 1; }", "p+=1")); + ASSERT_EQUALS("0", getRange("void f(void *p) { p = NULL; p += 1; }", "p+=1")); } };