Skip to content

Commit

Permalink
Implement checked exponentiation.
Browse files Browse the repository at this point in the history
  • Loading branch information
chriseth committed Jul 22, 2020
1 parent 4a478f0 commit c05864d
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 0 deletions.
47 changes: 47 additions & 0 deletions libsolidity/codegen/YulUtilFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -603,6 +603,53 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
});
}

string YulUtilFunctions::overflowCheckedIntExpFunction(
IntegerType const& _type,
IntegerType const& _exponentType
)
{
solUnimplementedAssert(!_type.isSigned(), "");
solAssert(!_exponentType.isSigned(), "");
string functionName = "checked_exp_" + _type.identifier();
return m_functionCollector.createFunction(functionName, [&]() {
return
Whiskers(R"(
function <functionName>(base, exponent) -> power {
base := <cleanupFunction>(base)
exponent := <exponentCleanupFunction>(exponent)
// 0**0 == 1
if iszero(exponent) { power := 1 leave }
if or(
lt(base, 2),
eq(exponent, 1)
) { power := base leave }
power := 1
let max := <maxValue>
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 := <shr_1>(exponent)
}
if gt(power, div(max, base)) { revert(0, 0) }
power := mul(power, base)
}
)")
("functionName", functionName)
("maxValue", toCompactHexWithPrefix(u256(_type.maxValue())))
("cleanupFunction", cleanupFunction(_type))
("exponentCleanupFunction", cleanupFunction(_exponentType))
("shr_1", shiftRightFunction(1))
.render();
});
}

string YulUtilFunctions::extractByteArrayLengthFunction()
{
string functionName = "extract_byte_array_length";
Expand Down
3 changes: 3 additions & 0 deletions libsolidity/codegen/YulUtilFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ class YulUtilFunctions
/// signature: (x, y) -> diff
std::string overflowCheckedIntSubFunction(IntegerType const& _type);

/// signature: (x, y) -> power
std::string overflowCheckedIntExpFunction(IntegerType const& _type, IntegerType const& _exponentType);

/// @returns the name of a function that fetches the length of the given
/// array
/// signature: (array) -> length
Expand Down
3 changes: 3 additions & 0 deletions libsolidity/codegen/ir/IRGeneratorForStatements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2394,6 +2394,9 @@ string IRGeneratorForStatements::binaryOperation(
case Token::Mod:
fun = m_utils.checkedIntModFunction(*type);
break;
case Token::Exp:
fun = m_utils.overflowCheckedIntExpFunction(*type, *type);
break;
case Token::BitOr:
fun = "or";
break;
Expand Down
19 changes: 19 additions & 0 deletions test/libsolidity/semanticTests/viaYul/exp.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
contract C {
function f(uint x, uint y) public returns (uint) {
return x**y;
}
}
// ====
// compileViaYul: also
// ----
// f(uint256,uint256): 0, 0 -> 1
// f(uint256,uint256): 0, 1 -> 0x00
// f(uint256,uint256): 0, 2 -> 0x00
// f(uint256,uint256): 1, 0 -> 1
// f(uint256,uint256): 1, 1 -> 1
// f(uint256,uint256): 1, 2 -> 1
// f(uint256,uint256): 2, 0 -> 1
// f(uint256,uint256): 2, 1 -> 2
// f(uint256,uint256): 2, 2 -> 4
// f(uint256,uint256): 7, 63 -> 174251498233690814305510551794710260107945042018748343
// f(uint256,uint256): 128, 2 -> 0x4000
22 changes: 22 additions & 0 deletions test/libsolidity/semanticTests/viaYul/exp_overflow.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
contract C {
function f(uint8 x, uint8 y) public returns (uint) {
return x**y;
}
function g(uint x, uint y) public returns (uint) {
return x**y;
}
}
// ====
// compileViaYul: true
// ----
// f(uint8,uint8): 2, 7 -> 0x80
// f(uint8,uint8): 2, 8 -> FAILURE
// f(uint8,uint8): 15, 2 -> 225
// f(uint8,uint8): 6, 3 -> 0xd8
// f(uint8,uint8): 7, 2 -> 0x31
// f(uint8,uint8): 7, 3 -> FAILURE
// f(uint8,uint8): 7, 4 -> FAILURE
// g(uint256,uint256): 0x200000000000000000000000000000000, 1 -> 0x0200000000000000000000000000000000
// g(uint256,uint256): 0x100000000000000000000000000000010, 2 -> FAILURE
// g(uint256,uint256): 0x200000000000000000000000000000000, 2 -> FAILURE
// g(uint256,uint256): 0x200000000000000000000000000000000, 3 -> FAILURE

0 comments on commit c05864d

Please sign in to comment.