From 8450cc34b1d04a0bb14bd8934388a3a352ebb6d5 Mon Sep 17 00:00:00 2001 From: chriseth Date: Wed, 16 Sep 2020 17:48:08 +0200 Subject: [PATCH] Wrapping arithmetics for Yul IR. --- libsolidity/codegen/ExpressionCompiler.cpp | 2 +- libsolidity/codegen/YulUtilFunctions.cpp | 99 ++++++++++++++++++- libsolidity/codegen/YulUtilFunctions.h | 17 +++- libsolidity/codegen/ir/IRGenerationContext.h | 6 ++ .../codegen/ir/IRGeneratorForStatements.cpp | 14 +-- 5 files changed, 128 insertions(+), 10 deletions(-) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index b4d98e51252c..d147c1d871d9 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -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. diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 6ffff447e0d9..39b0977ddecb 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -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 (x, y) -> sum { + sum := (add(x, y)) + } + )") + ("functionName", functionName) + ("cleanupFunction", cleanupFunction(_type)) + .render(); + }); +} + string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type) { string functionName = "checked_mul_" + _type.identifier(); @@ -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 (x, y) -> product { + product := (mul(x, y)) + } + )") + ("functionName", functionName) + ("cleanupFunction", cleanupFunction(_type)) + .render(); + }); +} + string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type) { string functionName = "checked_div_" + _type.identifier(); @@ -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 (x, y) -> r { + x := (x) + y := (y) + if iszero(y) { () } + r := sdiv(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"( @@ -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 (x, y) -> diff { + // TODO do we need cleanup prior to calling sub()? + diff := (sub(x, y)) + } + )") + ("functionName", functionName) + ("cleanupFunction", cleanupFunction(_type)) + .render(); + }); +} + string YulUtilFunctions::overflowCheckedIntExpFunction( IntegerType const& _type, IntegerType const& _exponentType @@ -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 (base, exponent) -> power { + base := (base) + exponent := (exponent) + power := (exp(base, exponent)) + } + )") + ("functionName", functionName) + ("baseCleanupFunction", cleanupFunction(_type)) + ("exponentCleanupFunction", cleanupFunction(_exponentType)) + .render(); + }); +} + string YulUtilFunctions::overflowCheckedUnsignedExpFunction() { string functionName = "checked_exp_unsigned"; diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index eb4df16396ca..d583442fdc2c 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -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 diff --git a/libsolidity/codegen/ir/IRGenerationContext.h b/libsolidity/codegen/ir/IRGenerationContext.h index a833fdfa5cc6..9021ecaf4729 100644 --- a/libsolidity/codegen/ir/IRGenerationContext.h +++ b/libsolidity/codegen/ir/IRGenerationContext.h @@ -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 @@ -158,6 +162,8 @@ class IRGenerationContext std::map> m_stateVariables; MultiUseYulFunctionCollector m_functions; size_t m_varCounter = 0; + /// Whether to use checked or wrapping arithmetic. + std::stack m_arithmetic; /// Function definitions queued for code generation. They're the Solidity functions whose calls /// were discovered by the IR generator during AST traversal. diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 176f876bd9e9..9828b92c5df7 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -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) { @@ -2508,23 +2510,23 @@ string IRGeneratorForStatements::binaryOperation( if (IntegerType const* type = dynamic_cast(&_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";