mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Move computation of constants out of types.cpp
This commit is contained in:
@@ -32,6 +32,201 @@ using namespace solidity;
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::langutil;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/// Check whether (_base ** _exp) fits into 4096 bits.
|
||||
bool fitsPrecisionExp(bigint const& _base, bigint const& _exp)
|
||||
{
|
||||
if (_base == 0)
|
||||
return true;
|
||||
|
||||
solAssert(_base > 0, "");
|
||||
|
||||
size_t const bitsMax = 4096;
|
||||
|
||||
unsigned mostSignificantBaseBit = boost::multiprecision::msb(_base);
|
||||
if (mostSignificantBaseBit == 0) // _base == 1
|
||||
return true;
|
||||
if (mostSignificantBaseBit > bitsMax) // _base >= 2 ^ 4096
|
||||
return false;
|
||||
|
||||
bigint bitsNeeded = _exp * (mostSignificantBaseBit + 1);
|
||||
|
||||
return bitsNeeded <= bitsMax;
|
||||
}
|
||||
|
||||
/// Checks whether _mantissa * (2 ** _expBase10) fits into 4096 bits.
|
||||
bool fitsPrecisionBase2(bigint const& _mantissa, uint32_t _expBase2)
|
||||
{
|
||||
return fitsPrecisionBaseX(_mantissa, 1.0, _expBase2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
optional<rational> ConstantEvaluator::evaluateBinaryOperator(Token _operator, rational const& _left, rational const& _right)
|
||||
{
|
||||
bool fractional = _left.denominator() != 1 || _right.denominator() != 1;
|
||||
switch (_operator)
|
||||
{
|
||||
//bit operations will only be enabled for integers and fixed types that resemble integers
|
||||
case Token::BitOr:
|
||||
if (fractional)
|
||||
return nullopt;
|
||||
else
|
||||
return _left.numerator() | _right.numerator();
|
||||
case Token::BitXor:
|
||||
if (fractional)
|
||||
return nullopt;
|
||||
else
|
||||
return _left.numerator() ^ _right.numerator();
|
||||
case Token::BitAnd:
|
||||
if (fractional)
|
||||
return nullopt;
|
||||
else
|
||||
return _left.numerator() & _right.numerator();
|
||||
case Token::Add: return _left + _right;
|
||||
case Token::Sub: return _left - _right;
|
||||
case Token::Mul: return _left * _right;
|
||||
case Token::Div:
|
||||
if (_right == rational(0))
|
||||
return nullopt;
|
||||
else
|
||||
return _left / _right;
|
||||
case Token::Mod:
|
||||
if (_right == rational(0))
|
||||
return nullopt;
|
||||
else if (fractional)
|
||||
{
|
||||
rational tempValue = _left / _right;
|
||||
return _left - (tempValue.numerator() / tempValue.denominator()) * _right;
|
||||
}
|
||||
else
|
||||
return _left.numerator() % _right.numerator();
|
||||
break;
|
||||
case Token::Exp:
|
||||
{
|
||||
if (_right.denominator() != 1)
|
||||
return nullopt;
|
||||
bigint const& exp = _right.numerator();
|
||||
|
||||
// x ** 0 = 1
|
||||
// for 0, 1 and -1 the size of the exponent doesn't have to be restricted
|
||||
if (exp == 0)
|
||||
return 1;
|
||||
else if (_left == 0 || _left == 1)
|
||||
return _left;
|
||||
else if (_left == -1)
|
||||
{
|
||||
bigint isOdd = abs(exp) & bigint(1);
|
||||
return 1 - 2 * isOdd.convert_to<int>();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (abs(exp) > numeric_limits<uint32_t>::max())
|
||||
return nullopt; // This will need too much memory to represent.
|
||||
|
||||
uint32_t absExp = bigint(abs(exp)).convert_to<uint32_t>();
|
||||
|
||||
if (!fitsPrecisionExp(abs(_left.numerator()), absExp) || !fitsPrecisionExp(abs(_left.denominator()), absExp))
|
||||
return nullopt;
|
||||
|
||||
static auto const optimizedPow = [](bigint const& _base, uint32_t _exponent) -> bigint {
|
||||
if (_base == 1)
|
||||
return 1;
|
||||
else if (_base == -1)
|
||||
return 1 - 2 * static_cast<int>(_exponent & 1);
|
||||
else
|
||||
return boost::multiprecision::pow(_base, _exponent);
|
||||
};
|
||||
|
||||
bigint numerator = optimizedPow(_left.numerator(), absExp);
|
||||
bigint denominator = optimizedPow(_left.denominator(), absExp);
|
||||
|
||||
if (exp >= 0)
|
||||
return makeRational(numerator, denominator);
|
||||
else
|
||||
// invert
|
||||
return makeRational(denominator, numerator);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Token::SHL:
|
||||
{
|
||||
if (fractional)
|
||||
return nullopt;
|
||||
else if (_right < 0)
|
||||
return nullopt;
|
||||
else if (_right > numeric_limits<uint32_t>::max())
|
||||
return nullopt;
|
||||
if (_left.numerator() == 0)
|
||||
return 0;
|
||||
else
|
||||
{
|
||||
uint32_t exponent = _right.numerator().convert_to<uint32_t>();
|
||||
if (!fitsPrecisionBase2(abs(_left.numerator()), exponent))
|
||||
return nullopt;
|
||||
return _left.numerator() * boost::multiprecision::pow(bigint(2), exponent);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// NOTE: we're using >> (SAR) to denote right shifting. The type of the LValue
|
||||
// determines the resulting type and the type of shift (SAR or SHR).
|
||||
case Token::SAR:
|
||||
{
|
||||
if (fractional)
|
||||
return nullopt;
|
||||
else if (_right < 0)
|
||||
return nullopt;
|
||||
else if (_right > numeric_limits<uint32_t>::max())
|
||||
return nullopt;
|
||||
if (_left.numerator() == 0)
|
||||
return 0;
|
||||
else
|
||||
{
|
||||
uint32_t exponent = _right.numerator().convert_to<uint32_t>();
|
||||
if (exponent > boost::multiprecision::msb(boost::multiprecision::abs(_left.numerator())))
|
||||
return _left.numerator() < 0 ? -1 : 0;
|
||||
else
|
||||
{
|
||||
if (_left.numerator() < 0)
|
||||
// Add 1 to the negative value before dividing to get a result that is strictly too large,
|
||||
// then subtract 1 afterwards to round towards negative infinity.
|
||||
// This is the same algorithm as used in ExpressionCompiler::appendShiftOperatorCode(...).
|
||||
// To see this note that for negative x, xor(x,all_ones) = (-x-1) and
|
||||
// therefore xor(div(xor(x,all_ones), exp(2, shift_amount)), all_ones) is
|
||||
// -(-x - 1) / 2^shift_amount - 1, which is the same as
|
||||
// (x + 1) / 2^shift_amount - 1.
|
||||
return rational((_left.numerator() + 1) / boost::multiprecision::pow(bigint(2), exponent) - bigint(1), 1);
|
||||
else
|
||||
return rational(_left.numerator() / boost::multiprecision::pow(bigint(2), exponent), 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
optional<rational> ConstantEvaluator::evaluateUnaryOperator(Token _operator, rational const& _input)
|
||||
{
|
||||
switch (_operator)
|
||||
{
|
||||
case Token::BitNot:
|
||||
if (_input.denominator() != 1)
|
||||
return nullopt;
|
||||
else
|
||||
return ~_input.numerator();
|
||||
case Token::Add:
|
||||
return +_input;
|
||||
case Token::Sub:
|
||||
return -_input;
|
||||
default:
|
||||
return nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
void ConstantEvaluator::endVisit(UnaryOperation const& _operation)
|
||||
{
|
||||
auto sub = type(_operation.subExpression());
|
||||
|
||||
@@ -56,6 +56,14 @@ public:
|
||||
|
||||
TypePointer evaluate(Expression const& _expr);
|
||||
|
||||
/// Performs arbitrary-precision evaluation of a binary operator. Returns nullopt on cases like
|
||||
/// division by zero or e.g. bit operators applied to fractional values.
|
||||
static std::optional<rational> evaluateBinaryOperator(Token _operator, rational const& _left, rational const& _right);
|
||||
|
||||
/// Performs arbitrary-precision evaluation of a unary operator. Returns nullopt on cases like
|
||||
/// bit operators applied to fractional values.
|
||||
static std::optional<rational> evaluateUnaryOperator(Token _operator, rational const& _input);
|
||||
|
||||
private:
|
||||
void endVisit(BinaryOperation const& _operation) override;
|
||||
void endVisit(UnaryOperation const& _operation) override;
|
||||
|
||||
Reference in New Issue
Block a user