Merge pull request #6740 from ethereum/unary-ops

[Sol -> Yul] Implement Int/Bool Unary: ++, --, ~, !
This commit is contained in:
chriseth
2019-05-20 18:06:01 +02:00
committed by GitHub
5 changed files with 307 additions and 4 deletions
+78
View File
@@ -1060,6 +1060,84 @@ string YulUtilFunctions::suffixedVariableNameList(string const& _baseName, size_
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 functionName =
+6 -1
View File
@@ -177,8 +177,13 @@ public:
/// If @a _startSuffix == @a _endSuffix, the empty string is returned.
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
/// use exactly one variable to hold the value.
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)
{
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) <<
formatNumber(type(_unaryOperation).literalValue(nullptr)) <<
formatNumber(resultType.literalValue(nullptr)) <<
"\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
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, "");
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)
@@ -82,6 +82,7 @@ private:
std::ostream& defineExpressionPart(Expression const& _expression, size_t _part);
void appendAndOrOperatorCode(BinaryOperation const& _binOp);
void appendSimpleUnaryOperation(UnaryOperation const& _operation, Expression const& _expr);
void setLValue(Expression const& _expression, std::unique_ptr<IRLValue> _lvalue);
void generateLoop(