mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #6740 from ethereum/unary-ops
[Sol -> Yul] Implement Int/Bool Unary: ++, --, ~, !
This commit is contained in:
commit
e324deabd6
@ -1060,6 +1060,84 @@ string YulUtilFunctions::suffixedVariableNameList(string const& _baseName, size_
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string YulUtilFunctions::decrementCheckedFunction(Type const& _type)
|
||||||
|
{
|
||||||
|
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
|
||||||
|
|
||||||
|
string const functionName = "decrement_" + _type.identifier();
|
||||||
|
|
||||||
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
|
u256 minintval;
|
||||||
|
|
||||||
|
// Smallest admissible value to decrement
|
||||||
|
if (type.isSigned())
|
||||||
|
minintval = 0 - (u256(1) << (type.numBits() - 1)) + 1;
|
||||||
|
else
|
||||||
|
minintval = 1;
|
||||||
|
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(value) -> ret {
|
||||||
|
if <lt>(value, <minval>) { revert(0,0) }
|
||||||
|
ret := sub(value, 1)
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("minval", toCompactHexWithPrefix(minintval))
|
||||||
|
("lt", type.isSigned() ? "slt" : "lt")
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type)
|
||||||
|
{
|
||||||
|
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
|
||||||
|
|
||||||
|
string const functionName = "increment_" + _type.identifier();
|
||||||
|
|
||||||
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
|
u256 maxintval;
|
||||||
|
|
||||||
|
// Biggest admissible value to increment
|
||||||
|
if (type.isSigned())
|
||||||
|
maxintval = (u256(1) << (type.numBits() - 1)) - 2;
|
||||||
|
else
|
||||||
|
maxintval = (u256(1) << type.numBits()) - 2;
|
||||||
|
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(value) -> ret {
|
||||||
|
if <gt>(value, <maxval>) { revert(0,0) }
|
||||||
|
ret := add(value, 1)
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("maxval", toCompactHexWithPrefix(maxintval))
|
||||||
|
("gt", type.isSigned() ? "sgt" : "gt")
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
string YulUtilFunctions::negateNumberCheckedFunction(Type const& _type)
|
||||||
|
{
|
||||||
|
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
|
||||||
|
solAssert(type.isSigned(), "Expected signed type!");
|
||||||
|
|
||||||
|
string const functionName = "negate_" + _type.identifier();
|
||||||
|
|
||||||
|
u256 const minintval = 0 - (u256(1) << (type.numBits() - 1)) + 1;
|
||||||
|
|
||||||
|
return m_functionCollector->createFunction(functionName, [&]() {
|
||||||
|
return Whiskers(R"(
|
||||||
|
function <functionName>(_value) -> ret {
|
||||||
|
if slt(_value, <minval>) { revert(0,0) }
|
||||||
|
ret := sub(0, _value)
|
||||||
|
}
|
||||||
|
)")
|
||||||
|
("functionName", functionName)
|
||||||
|
("minval", toCompactHexWithPrefix(minintval))
|
||||||
|
.render();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const& _to)
|
string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const& _to)
|
||||||
{
|
{
|
||||||
string functionName =
|
string functionName =
|
||||||
|
@ -177,8 +177,13 @@ public:
|
|||||||
/// If @a _startSuffix == @a _endSuffix, the empty string is returned.
|
/// If @a _startSuffix == @a _endSuffix, the empty string is returned.
|
||||||
static std::string suffixedVariableNameList(std::string const& _baseName, size_t _startSuffix, size_t _endSuffix);
|
static std::string suffixedVariableNameList(std::string const& _baseName, size_t _startSuffix, size_t _endSuffix);
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
|
std::string incrementCheckedFunction(Type const& _type);
|
||||||
|
std::string decrementCheckedFunction(Type const& _type);
|
||||||
|
|
||||||
|
std::string negateNumberCheckedFunction(Type const& _type);
|
||||||
|
|
||||||
|
private:
|
||||||
/// Special case of conversionFunction - handles everything that does not
|
/// Special case of conversionFunction - handles everything that does not
|
||||||
/// use exactly one variable to hold the value.
|
/// use exactly one variable to hold the value.
|
||||||
std::string conversionFunctionSpecial(Type const& _from, Type const& _to);
|
std::string conversionFunctionSpecial(Type const& _from, Type const& _to);
|
||||||
|
@ -234,12 +234,95 @@ void IRGeneratorForStatements::endVisit(Return const& _return)
|
|||||||
|
|
||||||
void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation)
|
void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation)
|
||||||
{
|
{
|
||||||
if (type(_unaryOperation).category() == Type::Category::RationalNumber)
|
Type const& resultType = type(_unaryOperation);
|
||||||
|
Token const op = _unaryOperation.getOperator();
|
||||||
|
|
||||||
|
if (resultType.category() == Type::Category::RationalNumber)
|
||||||
|
{
|
||||||
defineExpression(_unaryOperation) <<
|
defineExpression(_unaryOperation) <<
|
||||||
formatNumber(type(_unaryOperation).literalValue(nullptr)) <<
|
formatNumber(resultType.literalValue(nullptr)) <<
|
||||||
"\n";
|
"\n";
|
||||||
|
}
|
||||||
|
else if (resultType.category() == Type::Category::Integer)
|
||||||
|
{
|
||||||
|
solAssert(resultType == type(_unaryOperation.subExpression()), "Result type doesn't match!");
|
||||||
|
|
||||||
|
if (op == Token::Inc || op == Token::Dec)
|
||||||
|
{
|
||||||
|
solAssert(!!m_currentLValue, "LValue not retrieved.");
|
||||||
|
string fetchValueExpr = m_currentLValue->retrieveValue();
|
||||||
|
string modifiedValue = m_context.newYulVariable();
|
||||||
|
string originalValue = m_context.newYulVariable();
|
||||||
|
|
||||||
|
m_code << "let " << originalValue << " := " << fetchValueExpr << "\n";
|
||||||
|
m_code <<
|
||||||
|
"let " <<
|
||||||
|
modifiedValue <<
|
||||||
|
" := " <<
|
||||||
|
(op == Token::Inc ?
|
||||||
|
m_utils.incrementCheckedFunction(resultType) :
|
||||||
|
m_utils.decrementCheckedFunction(resultType)
|
||||||
|
) <<
|
||||||
|
"(" <<
|
||||||
|
originalValue <<
|
||||||
|
")\n";
|
||||||
|
m_code << m_currentLValue->storeValue(modifiedValue, resultType);
|
||||||
|
m_currentLValue.reset();
|
||||||
|
|
||||||
|
defineExpression(_unaryOperation) <<
|
||||||
|
(_unaryOperation.isPrefixOperation() ? modifiedValue : originalValue) <<
|
||||||
|
"\n";
|
||||||
|
}
|
||||||
|
else if (op == Token::BitNot)
|
||||||
|
appendSimpleUnaryOperation(_unaryOperation, _unaryOperation.subExpression());
|
||||||
|
else if (op == Token::Add)
|
||||||
|
// According to SyntaxChecker...
|
||||||
|
solAssert(false, "Use of unary + is disallowed.");
|
||||||
|
else if (op == Token::Sub)
|
||||||
|
{
|
||||||
|
IntegerType const& intType = *dynamic_cast<IntegerType const*>(&resultType);
|
||||||
|
|
||||||
|
defineExpression(_unaryOperation) <<
|
||||||
|
m_utils.negateNumberCheckedFunction(intType) <<
|
||||||
|
"(" <<
|
||||||
|
m_context.variable(_unaryOperation.subExpression()) <<
|
||||||
|
")\n";
|
||||||
|
}
|
||||||
else
|
else
|
||||||
solUnimplementedAssert(false, "");
|
solUnimplementedAssert(false, "Unary operator not yet implemented");
|
||||||
|
}
|
||||||
|
else if (resultType.category() == Type::Category::Bool)
|
||||||
|
{
|
||||||
|
solAssert(
|
||||||
|
_unaryOperation.getOperator() != Token::BitNot,
|
||||||
|
"Bitwise Negation can't be done on bool!"
|
||||||
|
);
|
||||||
|
|
||||||
|
appendSimpleUnaryOperation(_unaryOperation, _unaryOperation.subExpression());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
solUnimplementedAssert(false, "Unary operator not yet implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
void IRGeneratorForStatements::appendSimpleUnaryOperation(UnaryOperation const& _operation, Expression const& _expr)
|
||||||
|
{
|
||||||
|
string func;
|
||||||
|
|
||||||
|
if (_operation.getOperator() == Token::Not)
|
||||||
|
func = "iszero";
|
||||||
|
else if (_operation.getOperator() == Token::BitNot)
|
||||||
|
func = "not";
|
||||||
|
else
|
||||||
|
solAssert(false, "Invalid Token!");
|
||||||
|
|
||||||
|
defineExpression(_operation) <<
|
||||||
|
m_utils.cleanupFunction(type(_expr)) <<
|
||||||
|
"(" <<
|
||||||
|
func <<
|
||||||
|
"(" <<
|
||||||
|
m_context.variable(_expr) <<
|
||||||
|
")" <<
|
||||||
|
")\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
||||||
|
@ -82,6 +82,7 @@ private:
|
|||||||
std::ostream& defineExpressionPart(Expression const& _expression, size_t _part);
|
std::ostream& defineExpressionPart(Expression const& _expression, size_t _part);
|
||||||
|
|
||||||
void appendAndOrOperatorCode(BinaryOperation const& _binOp);
|
void appendAndOrOperatorCode(BinaryOperation const& _binOp);
|
||||||
|
void appendSimpleUnaryOperation(UnaryOperation const& _operation, Expression const& _expr);
|
||||||
|
|
||||||
void setLValue(Expression const& _expression, std::unique_ptr<IRLValue> _lvalue);
|
void setLValue(Expression const& _expression, std::unique_ptr<IRLValue> _lvalue);
|
||||||
void generateLoop(
|
void generateLoop(
|
||||||
|
136
test/libsolidity/semanticTests/viaYul/unary_operations.sol
Normal file
136
test/libsolidity/semanticTests/viaYul/unary_operations.sol
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
contract C {
|
||||||
|
function preincr_u8(uint8 a) public pure returns (uint8) {
|
||||||
|
return ++a + a;
|
||||||
|
}
|
||||||
|
function postincr_u8(uint8 a) public pure returns (uint8) {
|
||||||
|
return a++ + a;
|
||||||
|
}
|
||||||
|
function predecr_u8(uint8 a) public pure returns (uint8) {
|
||||||
|
return --a + a;
|
||||||
|
}
|
||||||
|
function postdecr_u8(uint8 a) public pure returns (uint8) {
|
||||||
|
return a-- + a;
|
||||||
|
}
|
||||||
|
function preincr_s8(int8 a) public pure returns (int8 ret1, int8 ret2) {
|
||||||
|
ret1 = ++a;
|
||||||
|
ret2 = a;
|
||||||
|
}
|
||||||
|
function postincr_s8(int8 a) public pure returns (int8 ret1, int8 ret2) {
|
||||||
|
ret1 = a++;
|
||||||
|
ret2 = a;
|
||||||
|
}
|
||||||
|
function predecr_s8(int8 a) public pure returns (int8 ret1, int8 ret2) {
|
||||||
|
ret1 = --a;
|
||||||
|
ret2 = a;
|
||||||
|
}
|
||||||
|
function postdecr_s8(int8 a) public pure returns (int8 ret1, int8 ret2) {
|
||||||
|
ret1 = a--;
|
||||||
|
ret2 = a;
|
||||||
|
}
|
||||||
|
function preincr(uint a) public pure returns (uint) {
|
||||||
|
return ++a + a;
|
||||||
|
}
|
||||||
|
function postincr(uint a) public pure returns (uint) {
|
||||||
|
return a++ + a;
|
||||||
|
}
|
||||||
|
function predecr(uint a) public pure returns (uint) {
|
||||||
|
return --a + a;
|
||||||
|
}
|
||||||
|
function postdecr(uint a) public pure returns (uint) {
|
||||||
|
return a-- + a;
|
||||||
|
}
|
||||||
|
function not(bool a) public pure returns (bool)
|
||||||
|
{
|
||||||
|
return !a;
|
||||||
|
}
|
||||||
|
function bitnot(int256 a) public pure returns (int256)
|
||||||
|
{
|
||||||
|
return ~a;
|
||||||
|
}
|
||||||
|
function bitnot_u8(uint8 a) public pure returns (uint256 ret)
|
||||||
|
{
|
||||||
|
a = ~a;
|
||||||
|
assembly {
|
||||||
|
// Tests that the lower bit parts are cleaned up
|
||||||
|
ret := a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function bitnot_s8() public pure returns (int256 ret)
|
||||||
|
{
|
||||||
|
int8 a;
|
||||||
|
assembly {
|
||||||
|
a := 0x9C
|
||||||
|
}
|
||||||
|
|
||||||
|
a = ~a;
|
||||||
|
|
||||||
|
assembly {
|
||||||
|
// Tests that the lower bit parts are cleaned up
|
||||||
|
ret := a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function negate(int256 a) public pure returns (int256)
|
||||||
|
{
|
||||||
|
return -a;
|
||||||
|
}
|
||||||
|
function negate_s8(int8 a) public pure returns (int8)
|
||||||
|
{
|
||||||
|
return -a;
|
||||||
|
}
|
||||||
|
function negate_s16(int16 a) public pure returns (int16)
|
||||||
|
{
|
||||||
|
return -a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// compileViaYul: true
|
||||||
|
// ----
|
||||||
|
// preincr_s8(int8): 128 -> FAILURE
|
||||||
|
// postincr_s8(int8): 128 -> FAILURE
|
||||||
|
// preincr_s8(int8): 127 -> FAILURE
|
||||||
|
// postincr_s8(int8): 127 -> FAILURE
|
||||||
|
// preincr_s8(int8): 126 -> 127, 127
|
||||||
|
// postincr_s8(int8): 126 -> 126, 127
|
||||||
|
// predecr_s8(int8): -128 -> FAILURE
|
||||||
|
// postdecr_s8(int8): -128 -> FAILURE
|
||||||
|
// predecr_s8(int8): -127 -> -128, -128
|
||||||
|
// postdecr_s8(int8): -127 -> -127, -128
|
||||||
|
// preincr_s8(int8): -5 -> -4, -4
|
||||||
|
// postincr_s8(int8): -5 -> -5, -4
|
||||||
|
// predecr_s8(int8): -5 -> -6, -6
|
||||||
|
// postdecr_s8(int8): -5 -> -5, -6
|
||||||
|
// preincr_u8(uint8): 255 -> FAILURE
|
||||||
|
// postincr_u8(uint8): 255 -> FAILURE
|
||||||
|
// preincr_u8(uint8): 254 -> FAILURE
|
||||||
|
// postincr_u8(uint8): 254 -> FAILURE
|
||||||
|
// predecr_u8(uint8): 0 -> FAILURE
|
||||||
|
// postdecr_u8(uint8): 0 -> FAILURE
|
||||||
|
// predecr_u8(uint8): 1 -> 0
|
||||||
|
// postdecr_u8(uint8): 1 -> 1
|
||||||
|
// preincr_u8(uint8): 2 -> 6
|
||||||
|
// postincr_u8(uint8): 2 -> 5
|
||||||
|
// predecr_u8(uint8): 2 -> 2
|
||||||
|
// postdecr_u8(uint8): 2 -> 3
|
||||||
|
// preincr(uint256): 2 -> 6
|
||||||
|
// postincr(uint256): 2 -> 5
|
||||||
|
// predecr(uint256): 2 -> 2
|
||||||
|
// postdecr(uint256): 2 -> 3
|
||||||
|
// not(bool): true -> false
|
||||||
|
// not(bool): false -> true
|
||||||
|
// bitnot(int256): 5 -> -6
|
||||||
|
// bitnot(int256): 10 -> -11
|
||||||
|
// bitnot(int256): 0 -> -1
|
||||||
|
// bitnot(int256): -100 -> 99
|
||||||
|
// bitnot_u8(uint8): 100 -> 155
|
||||||
|
// bitnot_s8() -> 99
|
||||||
|
// negate(int256): -57896044618658097711785492504343953926634992332820282019728792003956564819968 -> FAILURE
|
||||||
|
// negate(int256): -57896044618658097711785492504343953926634992332820282019728792003956564819967 -> 57896044618658097711785492504343953926634992332820282019728792003956564819967
|
||||||
|
// negate(int256): 0 -> 0
|
||||||
|
// negate(int256): 1 -> -1
|
||||||
|
// negate(int256): -1 -> 1
|
||||||
|
// negate_s8(int8): -128 -> FAILURE
|
||||||
|
// negate_s8(int8): -138 -> FAILURE
|
||||||
|
// negate_s8(int8): -127 -> 127
|
||||||
|
// negate_s8(int8): 127 -> -127
|
||||||
|
// negate_s16(int16): -32768 -> FAILURE
|
||||||
|
// negate_s16(int16): -32767 -> 32767
|
Loading…
Reference in New Issue
Block a user