[Sol -> Yul] Implement Int/Bool Unary: ++, --, ~, !

This commit is contained in:
Mathias Baumann 2019-05-13 15:26:55 +02:00
parent 4f3b7b232b
commit 7dbcb80523
5 changed files with 244 additions and 4 deletions

View File

@ -1032,6 +1032,62 @@ 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::conversionFunctionSpecial(Type const& _from, Type const& _to)
{
string functionName =

View File

@ -175,8 +175,11 @@ 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);
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);

View File

@ -234,12 +234,79 @@ 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_currentLValue->storeValue(modifiedValue, resultType);
defineExpression(_unaryOperation) <<
(_unaryOperation.isPrefixOperation() ? modifiedValue : originalValue) <<
"\n";
m_code << m_currentLValue->storeValue(modifiedValue, resultType);
m_currentLValue.reset();
}
else if (op == Token::BitNot)
appendSimpleUnaryOperation(_unaryOperation, _unaryOperation.subExpression());
else
solUnimplementedAssert(false, "Unary operator not yet implemented");
}
else if (resultType.category() == Type::Category::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)

View File

@ -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(

View File

@ -0,0 +1,113 @@
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
}
}
}
// ====
// 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