diff --git a/libsolidity/codegen/YulUtilFunctions.cpp b/libsolidity/codegen/YulUtilFunctions.cpp index f622ae812..4ac32a1fa 100644 --- a/libsolidity/codegen/YulUtilFunctions.cpp +++ b/libsolidity/codegen/YulUtilFunctions.cpp @@ -118,10 +118,10 @@ string YulUtilFunctions::requireOrAssertFunction(bool _assert, Type const* _mess if (!_messageType) return Whiskers(R"( function (condition) { - if iszero(condition) { } + if iszero(condition) { } } )") - ("invalidOrRevert", _assert ? "invalid()" : "revert(0, 0)") + ("error", _assert ? panicFunction() + "()" : "revert(0, 0)") ("functionName", functionName) .render(); @@ -457,7 +457,7 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type) string functionName = "checked_add_" + _type.identifier(); // TODO: Consider to add a special case for unsigned 256-bit integers // and use the following instead: - // sum := add(x, y) if lt(sum, x) { revert(0, 0) } + // sum := add(x, y) if lt(sum, x) { () } return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( @@ -466,12 +466,12 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type) y := (y) // overflow, if x >= 0 and y > (maxValue - x) - if and(iszero(slt(x, 0)), sgt(y, sub(, x))) { revert(0, 0) } + if and(iszero(slt(x, 0)), sgt(y, sub(, x))) { () } // underflow, if x < 0 and y < (minValue - x) - if and(slt(x, 0), slt(y, sub(, x))) { revert(0, 0) } + if and(slt(x, 0), slt(y, sub(, x))) { () } // overflow, if x > (maxValue - y) - if gt(x, sub(, y)) { revert(0, 0) } + if gt(x, sub(, y)) { () } sum := add(x, y) } @@ -481,6 +481,7 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type) ("maxValue", toCompactHexWithPrefix(u256(_type.maxValue()))) ("minValue", toCompactHexWithPrefix(u256(_type.minValue()))) ("cleanupFunction", cleanupFunction(_type)) + ("panic", panicFunction()) .render(); }); } @@ -497,16 +498,16 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type) y := (y) // overflow, if x > 0, y > 0 and x > (maxValue / y) - if and(and(sgt(x, 0), sgt(y, 0)), gt(x, div(, y))) { revert(0, 0) } + if and(and(sgt(x, 0), sgt(y, 0)), gt(x, div(, y))) { () } // underflow, if x > 0, y < 0 and y < (minValue / x) - if and(and(sgt(x, 0), slt(y, 0)), slt(y, sdiv(, x))) { revert(0, 0) } + if and(and(sgt(x, 0), slt(y, 0)), slt(y, sdiv(, x))) { () } // underflow, if x < 0, y > 0 and x < (minValue / y) - if and(and(slt(x, 0), sgt(y, 0)), slt(x, sdiv(, y))) { revert(0, 0) } + if and(and(slt(x, 0), sgt(y, 0)), slt(x, sdiv(, y))) { () } // overflow, if x < 0, y < 0 and x < (maxValue / y) - if and(and(slt(x, 0), slt(y, 0)), slt(x, sdiv(, y))) { revert(0, 0) } + if and(and(slt(x, 0), slt(y, 0)), slt(x, sdiv(, y))) { () } // overflow, if x != 0 and y > (maxValue / x) - if and(iszero(iszero(x)), gt(y, div(, x))) { revert(0, 0) } + if and(iszero(iszero(x)), gt(y, div(, x))) { () } product := mul(x, y) } @@ -516,6 +517,7 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type) ("maxValue", toCompactHexWithPrefix(u256(_type.maxValue()))) ("minValue", toCompactHexWithPrefix(u256(_type.minValue()))) ("cleanupFunction", cleanupFunction(_type)) + ("panic", panicFunction()) .render(); }); } @@ -529,13 +531,13 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type) function (x, y) -> r { x := (x) y := (y) - if iszero(y) { revert(0, 0) } + if iszero(y) { () } // overflow for minVal / -1 if and( eq(x, ), eq(y, sub(0, 1)) - ) { revert(0, 0) } + ) { () } r := sdiv(x, y) } @@ -544,6 +546,7 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type) ("signed", _type.isSigned()) ("minVal", toCompactHexWithPrefix(u256(_type.minValue()))) ("cleanupFunction", cleanupFunction(_type)) + ("panic", panicFunction()) .render(); }); } @@ -557,13 +560,14 @@ string YulUtilFunctions::checkedIntModFunction(IntegerType const& _type) function (x, y) -> r { x := (x) y := (y) - if iszero(y) { revert(0, 0) } + if iszero(y) { () } r := smod(x, y) } )") ("functionName", functionName) ("signed", _type.isSigned()) ("cleanupFunction", cleanupFunction(_type)) + ("panic", panicFunction()) .render(); }); } @@ -579,11 +583,11 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type) y := (y) // underflow, if y >= 0 and x < (minValue + y) - if and(iszero(slt(y, 0)), slt(x, add(, y))) { revert(0, 0) } + if and(iszero(slt(y, 0)), slt(x, add(, y))) { () } // overflow, if y < 0 and x > (maxValue + y) - if and(slt(y, 0), sgt(x, add(, y))) { revert(0, 0) } + if and(slt(y, 0), sgt(x, add(, y))) { () } - if lt(x, y) { revert(0, 0) } + if lt(x, y) { () } diff := sub(x, y) } @@ -593,6 +597,7 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type) ("maxValue", toCompactHexWithPrefix(u256(_type.maxValue()))) ("minValue", toCompactHexWithPrefix(u256(_type.minValue()))) ("cleanupFunction", cleanupFunction(_type)) + ("panic", panicFunction()) .render(); }); } @@ -660,9 +665,9 @@ string YulUtilFunctions::overflowCheckedUnsignedExpFunction() case 1 { power := 1 leave } case 2 { - if gt(exponent, 255) { revert(0, 0) } + if gt(exponent, 255) { () } power := exp(2, exponent) - if gt(power, max) { revert(0, 0) } + if gt(power, max) { () } leave } if or( @@ -671,17 +676,18 @@ string YulUtilFunctions::overflowCheckedUnsignedExpFunction() ) { power := exp(base, exponent) - if gt(power, max) { revert(0, 0) } + if gt(power, max) { () } leave } power, base := (1, base, exponent, max) - if gt(power, div(max, base)) { revert(0, 0) } + if gt(power, div(max, base)) { () } power := mul(power, base) } )") ("functionName", functionName) + ("panic", panicFunction()) ("expLoop", overflowCheckedExpLoopFunction()) ("shr_1", shiftRightFunction(1)) .render(); @@ -712,8 +718,8 @@ string YulUtilFunctions::overflowCheckedSignedExpFunction() // overflow check for base * base switch sgt(base, 0) - case 1 { if gt(base, div(max, base)) { revert(0, 0) } } - case 0 { if slt(base, sdiv(max, base)) { revert(0, 0) } } + case 1 { if gt(base, div(max, base)) { () } } + case 0 { if slt(base, sdiv(max, base)) { () } } if and(exponent, 1) { power := base @@ -725,12 +731,13 @@ string YulUtilFunctions::overflowCheckedSignedExpFunction() 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) } + if and(sgt(power, 0), gt(power, div(max, base))) { () } + if and(slt(power, 0), slt(power, sdiv(min, base))) { () } power := mul(power, base) } )") ("functionName", functionName) + ("panic", panicFunction()) ("expLoop", overflowCheckedExpLoopFunction()) ("shr_1", shiftRightFunction(1)) .render(); @@ -755,7 +762,7 @@ string YulUtilFunctions::overflowCheckedExpLoopFunction() for { } gt(exponent, 1) {} { // overflow check for base * base - if gt(base, div(max, base)) { revert(0, 0) } + if gt(base, div(max, base)) { () } if and(exponent, 1) { // No checks for power := mul(power, base) needed, because the check @@ -771,6 +778,7 @@ string YulUtilFunctions::overflowCheckedExpLoopFunction() } )") ("functionName", functionName) + ("panic", panicFunction()) ("shr_1", shiftRightFunction(1)) .render(); }); @@ -850,7 +858,7 @@ std::string YulUtilFunctions::resizeDynamicArrayFunction(ArrayType const& _type) return Whiskers(R"( function (array, newLen) { if gt(newLen, ) { - invalid() + () } let oldLen := (array) @@ -869,6 +877,7 @@ std::string YulUtilFunctions::resizeDynamicArrayFunction(ArrayType const& _type) } })") ("functionName", functionName) + ("panic", panicFunction()) ("fetchLength", arrayLengthFunction(_type)) ("convertToSize", arrayConvertLengthToSize(_type)) ("dataPosition", arrayDataAreaFunction(_type)) @@ -891,13 +900,14 @@ string YulUtilFunctions::storageArrayPopFunction(ArrayType const& _type) return Whiskers(R"( function (array) { let oldLen := (array) - if iszero(oldLen) { invalid() } + if iszero(oldLen) { () } let newLen := sub(oldLen, 1) let slot, offset := (array, newLen) (slot, offset) sstore(array, newLen) })") ("functionName", functionName) + ("panic", panicFunction()) ("fetchLength", arrayLengthFunction(_type)) ("indexAccess", storageArrayIndexAccessFunction(_type)) ("setToZero", storageSetToZeroFunction(*_type.baseType())) @@ -917,7 +927,7 @@ string YulUtilFunctions::storageByteArrayPopFunction(ArrayType const& _type) function (array) { let data := sload(array) let oldLen := (data) - if iszero(oldLen) { invalid() } + if iszero(oldLen) { () } switch eq(oldLen, 32) case 1 { @@ -946,6 +956,7 @@ string YulUtilFunctions::storageByteArrayPopFunction(ArrayType const& _type) sstore(array, data) })") ("functionName", functionName) + ("panic", panicFunction()) ("extractByteArrayLength", extractByteArrayLengthFunction()) ("dataAreaFunction", arrayDataAreaFunction(_type)) ("indexAccess", storageArrayIndexAccessFunction(_type)) @@ -968,7 +979,7 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type) let data := sload(array) let oldLen := (data) - if iszero(lt(oldLen, )) { invalid() } + if iszero(lt(oldLen, )) { () } switch gt(oldLen, 31) case 0 { @@ -999,13 +1010,14 @@ string YulUtilFunctions::storageArrayPushFunction(ArrayType const& _type) } let oldLen := sload(array) - if iszero(lt(oldLen, )) { invalid() } + if iszero(lt(oldLen, )) { () } sstore(array, add(oldLen, 1)) let slot, offset := (array, oldLen) (slot, offset, value) })") ("functionName", functionName) + ("panic", panicFunction()) ("extractByteArrayLength", _type.isByteArray() ? extractByteArrayLengthFunction() : "") ("dataAreaFunction", arrayDataAreaFunction(_type)) ("isByteArray", _type.isByteArray()) @@ -1032,12 +1044,13 @@ string YulUtilFunctions::storageArrayPushZeroFunction(ArrayType const& _type) return Whiskers(R"( function (array) -> slot, offset { let oldLen := (array) - if iszero(lt(oldLen, )) { invalid() } + if iszero(lt(oldLen, )) { () } sstore(array, add(oldLen, 1)) slot, offset := (array, oldLen) (slot, offset, ()) })") ("functionName", functionName) + ("panic", panicFunction()) ("fetchLength", arrayLengthFunction(_type)) ("indexAccess", storageArrayIndexAccessFunction(_type)) ("storeValue", updateStorageValueFunction(*_type.baseType(), *_type.baseType())) @@ -1172,7 +1185,7 @@ string YulUtilFunctions::arrayAllocationSizeFunction(ArrayType const& _type) Whiskers w(R"( function (length) -> size { // Make sure we can allocate memory without overflow - if gt(length, 0xffffffffffffffff) { revert(0, 0) } + if gt(length, 0xffffffffffffffff) { () } // round up size := and(add(length, 0x1f), not(0x1f)) @@ -1186,6 +1199,7 @@ string YulUtilFunctions::arrayAllocationSizeFunction(ArrayType const& _type) } )"); w("functionName", functionName); + w("panic", panicFunction()); w("byteArray", _type.isByteArray()); w("dynamic", _type.isDynamicallySized()); return w.render(); @@ -1230,7 +1244,7 @@ string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type) return Whiskers(R"( function (array, index) -> slot, offset { let arrayLength := (array) - if iszero(lt(index, arrayLength)) { invalid() } + if iszero(lt(index, arrayLength)) { () } @@ -1256,6 +1270,7 @@ string YulUtilFunctions::storageArrayIndexAccessFunction(ArrayType const& _type) } )") ("functionName", functionName) + ("panic", panicFunction()) ("arrayLen", arrayLengthFunction(_type)) ("dataAreaFunc", arrayDataAreaFunction(_type)) ("multipleItemsPerSlot", _type.baseType()->storageBytes() <= 16) @@ -1274,7 +1289,7 @@ string YulUtilFunctions::memoryArrayIndexAccessFunction(ArrayType const& _type) return Whiskers(R"( function (baseRef, index) -> addr { if iszero(lt(index, (baseRef))) { - invalid() + () } let offset := mul(index, ) @@ -1285,6 +1300,7 @@ string YulUtilFunctions::memoryArrayIndexAccessFunction(ArrayType const& _type) } )") ("functionName", functionName) + ("panic", panicFunction()) ("arrayLen", arrayLengthFunction(_type)) ("stride", to_string(_type.memoryStride())) ("dynamicallySized", _type.isDynamicallySized()) @@ -1299,7 +1315,7 @@ string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& _type return m_functionCollector.createFunction(functionName, [&]() { return Whiskers(R"( function (base_ref, length, index) -> addr, len { - if iszero(lt(index, length)) { invalid() } + if iszero(lt(index, length)) { () } addr := add(base_ref, mul(index, )) addr, len := (base_ref, addr) @@ -1307,6 +1323,7 @@ string YulUtilFunctions::calldataArrayIndexAccessFunction(ArrayType const& _type } )") ("functionName", functionName) + ("panic", panicFunction()) ("stride", to_string(_type.calldataStride())) ("dynamicallySized", _type.isDynamicallySized()) ("dynamicallyEncodedBase", _type.baseType()->isDynamicallyEncoded()) @@ -1852,12 +1869,13 @@ string YulUtilFunctions::allocationFunction() memPtr := mload() let newFreePtr := add(memPtr, size) // protect against overflow - if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) } + if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { () } mstore(, newFreePtr) } )") - ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer)) ("functionName", functionName) + ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer)) + ("panic", panicFunction()) .render(); }); } @@ -2427,7 +2445,7 @@ string YulUtilFunctions::validatorFunction(Type const& _type, bool _revertOnFail if (_revertOnFailure) templ("failure", "revert(0, 0)"); else - templ("failure", "invalid()"); + templ("failure", panicFunction() + "()"); switch (_type.category()) { @@ -2538,11 +2556,12 @@ std::string YulUtilFunctions::decrementCheckedFunction(Type const& _type) return Whiskers(R"( function (value) -> ret { value := (value) - if (value, ) { revert(0,0) } + if (value, ) { () } ret := sub(value, 1) } )") ("functionName", functionName) + ("panic", panicFunction()) ("minval", toCompactHexWithPrefix(minintval)) ("lt", type.isSigned() ? "slt" : "lt") ("cleanupFunction", cleanupFunction(_type)) @@ -2568,13 +2587,14 @@ std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type) return Whiskers(R"( function (value) -> ret { value := (value) - if (value, ) { revert(0,0) } + if (value, ) { () } ret := add(value, 1) } )") ("functionName", functionName) ("maxval", toCompactHexWithPrefix(maxintval)) ("gt", type.isSigned() ? "sgt" : "gt") + ("panic", panicFunction()) ("cleanupFunction", cleanupFunction(_type)) .render(); }); @@ -2593,13 +2613,14 @@ string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type) return Whiskers(R"( function (value) -> ret { value := (value) - if slt(value, ) { revert(0,0) } + if slt(value, ) { () } ret := sub(0, value) } )") ("functionName", functionName) ("minval", toCompactHexWithPrefix(minintval)) ("cleanupFunction", cleanupFunction(_type)) + ("panic", panicFunction()) .render(); }); } @@ -2935,6 +2956,20 @@ string YulUtilFunctions::revertReasonIfDebug(string const& _message) return revertReasonIfDebug(m_revertStrings, _message); } +string YulUtilFunctions::panicFunction() +{ + string functionName = "panic_error"; + return m_functionCollector.createFunction(functionName, [&]() { + return Whiskers(R"( + function () { + invalid() + } + )") + ("functionName", functionName) + .render(); + }); +} + string YulUtilFunctions::tryDecodeErrorMessageFunction() { string const functionName = "try_decode_error_message"; diff --git a/libsolidity/codegen/YulUtilFunctions.h b/libsolidity/codegen/YulUtilFunctions.h index 0e6fd5147..5437114f7 100644 --- a/libsolidity/codegen/YulUtilFunctions.h +++ b/libsolidity/codegen/YulUtilFunctions.h @@ -373,6 +373,10 @@ public: std::string revertReasonIfDebug(std::string const& _message = ""); + /// Executes the invalid opcode. + /// Might use revert with special error code in the future. + std::string panicFunction(); + /// Returns the name of a function that decodes an error message. /// signature: () -> arrayPtr /// diff --git a/libsolidity/codegen/ir/IRGenerator.cpp b/libsolidity/codegen/ir/IRGenerator.cpp index d820b4aa7..b1ab9dda4 100644 --- a/libsolidity/codegen/ir/IRGenerator.cpp +++ b/libsolidity/codegen/ir/IRGenerator.cpp @@ -204,10 +204,11 @@ InternalDispatchMap IRGenerator::generateInternalDispatchFunctions() := () } - default { invalid() } + default { () } } )"); templ("functionName", funName); + templ("panic", m_utils.panicFunction()); templ("in", suffixedVariableNameList("in_", 0, arity.in)); templ("out", suffixedVariableNameList("out_", 0, arity.out)); diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index 548351c60..338f0eb3e 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -1247,8 +1247,10 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) IRVariable modulus(m_context.newYulVariable(), *(parameterTypes[2])); define(modulus, *arguments[2]); - Whiskers templ("if iszero() { invalid() }\n"); - m_code << templ("modulus", modulus.name()).render(); + Whiskers templ("if iszero() { () }\n"); + templ("modulus", modulus.name()); + templ("panic", m_utils.panicFunction()); + m_code << templ.render(); string args; for (size_t i = 0; i < 2; ++i) @@ -1325,7 +1327,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) Whiskers t(R"( let := () let := add(, datasize("")) - if or(gt(, 0xffffffffffffffff), lt(, )) { revert(0, 0) } + if or(gt(, 0xffffffffffffffff), lt(, )) { () } datacopy(, dataoffset(""), datasize("")) := () @@ -1340,6 +1342,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) t("allocateTemporaryMemory", m_utils.allocationTemporaryMemoryFunction()); t("releaseTemporaryMemory", m_utils.releaseTemporaryMemoryFunction()); t("object", IRNames::creationObject(*contract)); + t("panic", m_utils.panicFunction()); t("abiEncode", m_context.abiFunctions().tupleEncoder(argumentTypes, functionType->parameterTypes(), false) ); @@ -1987,11 +1990,12 @@ void IRGeneratorForStatements::endVisit(IndexAccess const& _indexAccess) IRVariable index{m_context.newYulVariable(), *TypeProvider::uint256()}; define(index, *_indexAccess.indexExpression()); m_code << Whiskers(R"( - if iszero(lt(, )) { invalid() } + if iszero(lt(, )) { () } let := (byte(, )) )") ("index", index.name()) ("length", to_string(fixedBytesType.numBytes())) + ("panic", m_utils.panicFunction()) ("array", IRVariable(_indexAccess.baseExpression()).name()) ("shl248", m_utils.shiftLeftFunction(256 - 8)) ("result", IRVariable(_indexAccess).name())