Skip to content

Commit

Permalink
Wrapping arithmetics for Yul IR.
Browse files Browse the repository at this point in the history
  • Loading branch information
chriseth committed Sep 23, 2020
1 parent f330aa8 commit 8450cc3
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 10 deletions.
2 changes: 1 addition & 1 deletion libsolidity/codegen/ExpressionCompiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2104,7 +2104,7 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token _operator, Type cons
functionName = m_context.utilFunctions().overflowCheckedIntDivFunction(type);
break;
case Token::Mod:
functionName = m_context.utilFunctions().checkedIntModFunction(type);
functionName = m_context.utilFunctions().intModFunction(type);
break;
case Token::Exp:
// EXP is handled in a different function.
Expand Down
99 changes: 97 additions & 2 deletions libsolidity/codegen/YulUtilFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,22 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type)
});
}

string YulUtilFunctions::wrappingIntAddFunction(IntegerType const& _type)
{
string functionName = "wrapping_add_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(x, y) -> sum {
sum := <cleanupFunction>(add(x, y))
}
)")
("functionName", functionName)
("cleanupFunction", cleanupFunction(_type))
.render();
});
}

string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
{
string functionName = "checked_mul_" + _type.identifier();
Expand Down Expand Up @@ -522,6 +538,23 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
});
}

string YulUtilFunctions::wrappingIntMulFunction(IntegerType const& _type)
{
string functionName = "wrapping_mul_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return
// Multiplication by zero could be treated separately and directly return zero.
Whiskers(R"(
function <functionName>(x, y) -> product {
product := <cleanupFunction>(mul(x, y))
}
)")
("functionName", functionName)
("cleanupFunction", cleanupFunction(_type))
.render();
});
}

string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
{
string functionName = "checked_div_" + _type.identifier();
Expand Down Expand Up @@ -551,9 +584,30 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
});
}

string YulUtilFunctions::checkedIntModFunction(IntegerType const& _type)
string YulUtilFunctions::wrappingIntDivFunction(IntegerType const& _type)
{
string functionName = "wrapping_div_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(x, y) -> r {
x := <cleanupFunction>(x)
y := <cleanupFunction>(y)
if iszero(y) { <error>() }
r := <?signed>s</signed>div(x, y)
}
)")
("functionName", functionName)
("signed", _type.isSigned())
("error", arithmeticErrorFunction())
.render();
});
}


string YulUtilFunctions::intModFunction(IntegerType const& _type)
{
string functionName = "checked_mod_" + _type.identifier();
string functionName = "mod_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return
Whiskers(R"(
Expand Down Expand Up @@ -602,6 +656,23 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
});
}

string YulUtilFunctions::wrappingIntSubFunction(IntegerType const& _type)
{
string functionName = "wrapping_sub_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&] {
return
Whiskers(R"(
function <functionName>(x, y) -> diff {
// TODO do we need cleanup prior to calling sub()?
diff := <cleanupFunction>(sub(x, y))
}
)")
("functionName", functionName)
("cleanupFunction", cleanupFunction(_type))
.render();
});
}

string YulUtilFunctions::overflowCheckedIntExpFunction(
IntegerType const& _type,
IntegerType const& _exponentType
Expand Down Expand Up @@ -635,6 +706,30 @@ string YulUtilFunctions::overflowCheckedIntExpFunction(
});
}

string YulUtilFunctions::wrappingIntExpFunction(
IntegerType const& _type,
IntegerType const& _exponentType
)
{
solAssert(!_exponentType.isSigned(), "");

string functionName = "wrapping_exp_" + _type.identifier() + "_" + _exponentType.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(base, exponent) -> power {
base := <baseCleanupFunction>(base)
exponent := <exponentCleanupFunction>(exponent)
power := <baseCleanupFunction>(exp(base, exponent))
}
)")
("functionName", functionName)
("baseCleanupFunction", cleanupFunction(_type))
("exponentCleanupFunction", cleanupFunction(_exponentType))
.render();
});
}

string YulUtilFunctions::overflowCheckedUnsignedExpFunction()
{
string functionName = "checked_exp_unsigned";
Expand Down
17 changes: 16 additions & 1 deletion libsolidity/codegen/YulUtilFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,28 +107,43 @@ class YulUtilFunctions

/// signature: (x, y) -> sum
std::string overflowCheckedIntAddFunction(IntegerType const& _type);
/// signature: (x, y) -> sum
std::string wrappingIntAddFunction(IntegerType const& _type);

/// signature: (x, y) -> product
std::string overflowCheckedIntMulFunction(IntegerType const& _type);
/// signature: (x, y) -> product
std::string wrappingIntMulFunction(IntegerType const& _type);

/// @returns name of function to perform division on integers.
/// Checks for division by zero and the special case of
/// signed division of the smallest number by -1.
std::string overflowCheckedIntDivFunction(IntegerType const& _type);
/// @returns name of function to perform division on integers.
/// Checks for division by zero.
std::string wrappingIntDivFunction(IntegerType const& _type);

/// @returns name of function to perform modulo on integers.
/// Reverts for modulo by zero.
std::string checkedIntModFunction(IntegerType const& _type);
std::string intModFunction(IntegerType const& _type);

/// @returns computes the difference between two values.
/// Assumes the input to be in range for the type.
/// signature: (x, y) -> diff
std::string overflowCheckedIntSubFunction(IntegerType const& _type);

/// @returns computes the difference between two values.
/// signature: (x, y) -> diff
std::string wrappingIntSubFunction(IntegerType const& _type);

/// @returns the name of the exponentiation function.
/// signature: (base, exponent) -> power
std::string overflowCheckedIntExpFunction(IntegerType const& _type, IntegerType const& _exponentType);

/// @returns the name of the exponentiation function.
/// signature: (base, exponent) -> power
std::string wrappingIntExpFunction(IntegerType const& _type, IntegerType const& _exponentType);

/// Generic unsigned checked exponentiation function.
/// Reverts if the result is larger than max.
/// signature: (base, exponent, max) -> power
Expand Down
6 changes: 6 additions & 0 deletions libsolidity/codegen/ir/IRGenerationContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ class IRGenerationContext

langutil::EVMVersion evmVersion() const { return m_evmVersion; };

void pushArithmetic(Arithmetic _value) { m_arithmetic.push(_value); }
void popArithmetic() { m_arithmetic.pop(); }
Arithmetic arithmetic() const { return m_arithmetic.empty() ? Arithmetic::Checked : m_arithmetic.top(); }

ABIFunctions abiFunctions();

/// @returns code that stores @param _message for revert reason
Expand All @@ -158,6 +162,8 @@ class IRGenerationContext
std::map<VariableDeclaration const*, std::pair<u256, unsigned>> m_stateVariables;
MultiUseYulFunctionCollector m_functions;
size_t m_varCounter = 0;
/// Whether to use checked or wrapping arithmetic.
std::stack<Arithmetic> m_arithmetic;

/// Function definitions queued for code generation. They're the Solidity functions whose calls
/// were discovered by the IR generator during AST traversal.
Expand Down
14 changes: 8 additions & 6 deletions libsolidity/codegen/ir/IRGeneratorForStatements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,9 @@ void IRGeneratorForStatements::generate(Block const& _block)
{
try
{
m_context.pushArithmetic(_block.unchecked() ? Arithmetic::Wrapping : Arithmetic::Checked);
_block.accept(*this);
m_context.popArithmetic();
}
catch (langutil::UnimplementedFeatureError const& _error)
{
Expand Down Expand Up @@ -2508,23 +2510,23 @@ string IRGeneratorForStatements::binaryOperation(
if (IntegerType const* type = dynamic_cast<IntegerType const*>(&_type))
{
string fun;
// TODO: Implement all operations for signed and unsigned types.
bool checked = m_context.arithmetic() == Arithmetic::Checked;
switch (_operator)
{
case Token::Add:
fun = m_utils.overflowCheckedIntAddFunction(*type);
fun = checked ? m_utils.overflowCheckedIntAddFunction(*type) : m_utils.wrappingIntAddFunction(*type);
break;
case Token::Sub:
fun = m_utils.overflowCheckedIntSubFunction(*type);
fun = checked ? m_utils.overflowCheckedIntSubFunction(*type) : m_utils.wrappingIntSubFunction(*type);
break;
case Token::Mul:
fun = m_utils.overflowCheckedIntMulFunction(*type);
fun = checked ? m_utils.overflowCheckedIntMulFunction(*type) : m_utils.wrappingIntMulFunction(*type);
break;
case Token::Div:
fun = m_utils.overflowCheckedIntDivFunction(*type);
fun = checked ? m_utils.overflowCheckedIntDivFunction(*type) : m_utils.wrappingIntDivFunction(*type);
break;
case Token::Mod:
fun = m_utils.checkedIntModFunction(*type);
fun = m_utils.intModFunction(*type);
break;
case Token::BitOr:
fun = "or";
Expand Down

0 comments on commit 8450cc3

Please sign in to comment.