From 55e6a926926d197c49a102b4208cd3ba68fec507 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 15 Sep 2020 14:25:07 +0200 Subject: [PATCH 1/2] Add specialization for small numbers. --- libsolidity/codegen/YulUtilFunctions.cpp | 31 ++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 13ad3644c..0669b3f45 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -632,6 +632,16 @@ string YulUtilFunctions::overflowCheckedIntExpFunction( string YulUtilFunctions::overflowCheckedUnsignedExpFunction() { + // Checks for the "small number specialization" below. + using namespace boost::multiprecision; + solAssert(pow(bigint(10), 77) < pow(bigint(2), 256), ""); + solAssert(pow(bigint(11), 77) >= pow(bigint(2), 256), ""); + solAssert(pow(bigint(10), 78) >= pow(bigint(2), 256), ""); + + solAssert(pow(bigint(306), 31) < pow(bigint(2), 256), ""); + solAssert(pow(bigint(307), 31) >= pow(bigint(2), 256), ""); + solAssert(pow(bigint(306), 32) >= pow(bigint(2), 256), ""); + string functionName = "checked_exp_unsigned"; return m_functionCollector.createFunction(functionName, [&]() { return @@ -644,6 +654,27 @@ string YulUtilFunctions::overflowCheckedUnsignedExpFunction() if iszero(exponent) { power := 1 leave } if iszero(base) { power := 0 leave } + // Specializations for small bases + switch base + // 0 is handled above + case 1 { power := 1 leave } + case 2 + { + if gt(exponent, 255) { revert(0, 0) } + power := exp(2, exponent) + if gt(power, max) { revert(0, 0) } + leave + } + if or( + and(lt(base, 11), lt(exponent, 78)), + and(lt(base, 307), lt(exponent, 32)) + ) + { + power := exp(base, exponent) + if gt(power, max) { revert(0, 0) } + leave + } + power := 1 for { } gt(exponent, 1) {} From e696c4eafd6351507b85a077ab95d9b56f7a4b92 Mon Sep 17 00:00:00 2001 From: chriseth Date: Tue, 15 Sep 2020 14:33:36 +0200 Subject: [PATCH 2/2] Extract common loop. --- libsolidity/codegen/YulUtilFunctions.cpp | 51 +++++++++++++++--------- libsolidity/codegen/YulUtilFunctions.h | 4 ++ 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index 0669b3f45..f622ae812 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -675,25 +675,14 @@ string YulUtilFunctions::overflowCheckedUnsignedExpFunction() leave } - power := 1 + power, base := (1, base, exponent, max) - for { } gt(exponent, 1) {} - { - // overflow check for base * base - if gt(base, div(max, base)) { revert(0, 0) } - if and(exponent, 1) - { - // no check needed here because base >= power - power := mul(power, base) - } - base := mul(base, base) - exponent := (exponent) - } if gt(power, div(max, base)) { revert(0, 0) } power := mul(power, base) } )") ("functionName", functionName) + ("expLoop", overflowCheckedExpLoopFunction()) ("shr_1", shiftRightFunction(1)) .render(); }); @@ -734,6 +723,35 @@ string YulUtilFunctions::overflowCheckedSignedExpFunction() // Below this point, base is always positive. + power, base := (power, base, exponent, max) + + if and(sgt(power, 0), gt(power, div(max, base))) { revert(0, 0) } + if and(slt(power, 0), slt(power, sdiv(min, base))) { revert(0, 0) } + power := mul(power, base) + } + )") + ("functionName", functionName) + ("expLoop", overflowCheckedExpLoopFunction()) + ("shr_1", shiftRightFunction(1)) + .render(); + }); +} + +string YulUtilFunctions::overflowCheckedExpLoopFunction() +{ + // We use this loop for both signed and unsigned exponentiation + // because we pull out the first iteration in the signed case which + // results in the base always being positive. + + // This function does not include the final multiplication. + + string functionName = "checked_exp_helper"; + return m_functionCollector.createFunction(functionName, [&]() { + return + Whiskers(R"( + function (_power, _base, exponent, max) -> power, base { + power := _power + base := _base for { } gt(exponent, 1) {} { // overflow check for base * base @@ -743,16 +761,13 @@ string YulUtilFunctions::overflowCheckedSignedExpFunction() // No checks for power := mul(power, base) needed, because the check // for base * base above is sufficient, since: // |power| <= base (proof by induction) and thus: - // |power * base| <= base * base <= max <= |min| + // |power * base| <= base * base <= max <= |min| (for signed) + // (this is equally true for signed and unsigned exp) power := mul(power, base) } base := mul(base, base) exponent := (exponent) } - - if and(sgt(power, 0), gt(power, div(max, base))) { revert(0, 0) } - if and(slt(power, 0), slt(power, sdiv(min, base))) { revert(0, 0) } - power := mul(power, base) } )") ("functionName", functionName) diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 8394c33af..0e6fd5147 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -140,6 +140,10 @@ public: /// signature: (base, exponent, min, max) -> power std::string overflowCheckedSignedExpFunction(); + /// Helper function for the two checked exponentiation functions. + /// signature: (power, base, exponent, max) -> power + std::string overflowCheckedExpLoopFunction(); + /// @returns the name of a function that fetches the length of the given /// array /// signature: (array) -> length