mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Checked arithmetic by default.
This commit is contained in:
@@ -123,6 +123,9 @@ public:
|
||||
void setMostDerivedContract(ContractDefinition const& _contract) { m_mostDerivedContract = &_contract; }
|
||||
ContractDefinition const& mostDerivedContract() const;
|
||||
|
||||
void setArithmetic(Arithmetic _value) { m_arithmetic = _value; }
|
||||
Arithmetic arithmetic() const { return m_arithmetic; }
|
||||
|
||||
/// @returns the next function in the queue of functions that are still to be compiled
|
||||
/// (i.e. that were referenced during compilation but where we did not yet generate code for).
|
||||
/// Returns nullptr if the queue is empty. Does not remove the function from the queue,
|
||||
@@ -380,6 +383,8 @@ private:
|
||||
std::map<Declaration const*, std::vector<unsigned>> m_localVariables;
|
||||
/// The contract currently being compiled. Virtual function lookup starts from this contarct.
|
||||
ContractDefinition const* m_mostDerivedContract = nullptr;
|
||||
/// Whether to use checked arithmetic.
|
||||
Arithmetic m_arithmetic = Arithmetic::Checked;
|
||||
/// Stack of current visited AST nodes, used for location attachment
|
||||
std::stack<ASTNode const*> m_visitedNodes;
|
||||
/// The runtime context if in Creation mode, this is used for generating tags that would be stored into the storage and then used at runtime.
|
||||
|
||||
@@ -1247,19 +1247,31 @@ bool ContractCompiler::visit(PlaceholderStatement const& _placeholderStatement)
|
||||
{
|
||||
StackHeightChecker checker(m_context);
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _placeholderStatement);
|
||||
solAssert(m_context.arithmetic() == Arithmetic::Checked, "Placeholder cannot be used inside checked block.");
|
||||
appendModifierOrFunctionCode();
|
||||
solAssert(m_context.arithmetic() == Arithmetic::Checked, "Arithmetic not reset to 'checked'.");
|
||||
checker.check();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ContractCompiler::visit(Block const& _block)
|
||||
{
|
||||
if (_block.unchecked())
|
||||
{
|
||||
solAssert(m_context.arithmetic() == Arithmetic::Checked, "");
|
||||
m_context.setArithmetic(Arithmetic::Wrapping);
|
||||
}
|
||||
storeStackHeight(&_block);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ContractCompiler::endVisit(Block const& _block)
|
||||
{
|
||||
if (_block.unchecked())
|
||||
{
|
||||
solAssert(m_context.arithmetic() == Arithmetic::Wrapping, "");
|
||||
m_context.setArithmetic(Arithmetic::Checked);
|
||||
}
|
||||
// Frees local variables declared in the scope of this block.
|
||||
popScopedVariables(&_block);
|
||||
}
|
||||
@@ -1327,6 +1339,8 @@ void ContractCompiler::appendModifierOrFunctionCode()
|
||||
|
||||
if (codeBlock)
|
||||
{
|
||||
m_context.setArithmetic(Arithmetic::Checked);
|
||||
|
||||
std::set<ExperimentalFeature> experimentalFeaturesOutside = m_context.experimentalFeaturesActive();
|
||||
m_context.setExperimentalFeatures(codeBlock->sourceUnit().annotation().experimentalFeatures);
|
||||
|
||||
|
||||
@@ -275,7 +275,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
|
||||
solAssert(*_assignment.annotation().type == leftType, "");
|
||||
bool cleanupNeeded = false;
|
||||
if (op != Token::Assign)
|
||||
cleanupNeeded = cleanupNeededForOp(leftType.category(), binOp);
|
||||
cleanupNeeded = cleanupNeededForOp(leftType.category(), binOp, m_context.arithmetic());
|
||||
_assignment.rightHandSide().accept(*this);
|
||||
// Perform some conversion already. This will convert storage types to memory and literals
|
||||
// to their actual type, but will not convert e.g. memory to storage.
|
||||
@@ -381,9 +381,10 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple)
|
||||
bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
|
||||
{
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _unaryOperation);
|
||||
if (_unaryOperation.annotation().type->category() == Type::Category::RationalNumber)
|
||||
Type const& type = *_unaryOperation.annotation().type;
|
||||
if (type.category() == Type::Category::RationalNumber)
|
||||
{
|
||||
m_context << _unaryOperation.annotation().type->literalValue(nullptr);
|
||||
m_context << type.literalValue(nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -406,24 +407,39 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
|
||||
case Token::Dec: // -- (pre- or postfix)
|
||||
solAssert(!!m_currentLValue, "LValue not retrieved.");
|
||||
solUnimplementedAssert(
|
||||
_unaryOperation.annotation().type->category() != Type::Category::FixedPoint,
|
||||
type.category() != Type::Category::FixedPoint,
|
||||
"Not yet implemented - FixedPointType."
|
||||
);
|
||||
m_currentLValue->retrieveValue(_unaryOperation.location());
|
||||
if (!_unaryOperation.isPrefixOperation())
|
||||
{
|
||||
// store value for later
|
||||
solUnimplementedAssert(_unaryOperation.annotation().type->sizeOnStack() == 1, "Stack size != 1 not implemented.");
|
||||
solUnimplementedAssert(type.sizeOnStack() == 1, "Stack size != 1 not implemented.");
|
||||
m_context << Instruction::DUP1;
|
||||
if (m_currentLValue->sizeOnStack() > 0)
|
||||
for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i)
|
||||
m_context << swapInstruction(i);
|
||||
}
|
||||
m_context << u256(1);
|
||||
if (_unaryOperation.getOperator() == Token::Inc)
|
||||
m_context << Instruction::ADD;
|
||||
{
|
||||
if (m_context.arithmetic() == Arithmetic::Checked)
|
||||
m_context.callYulFunction(m_context.utilFunctions().incrementCheckedFunction(type), 1, 1);
|
||||
else
|
||||
{
|
||||
m_context << u256(1);
|
||||
m_context << Instruction::ADD;
|
||||
}
|
||||
}
|
||||
else
|
||||
m_context << Instruction::SWAP1 << Instruction::SUB;
|
||||
{
|
||||
if (m_context.arithmetic() == Arithmetic::Checked)
|
||||
m_context.callYulFunction(m_context.utilFunctions().decrementCheckedFunction(type), 1, 1);
|
||||
else
|
||||
{
|
||||
m_context << u256(1);
|
||||
m_context << Instruction::SWAP1 << Instruction::SUB;
|
||||
}
|
||||
}
|
||||
// Stack for prefix: [ref...] (*ref)+-1
|
||||
// Stack for postfix: *ref [ref...] (*ref)+-1
|
||||
for (unsigned i = m_currentLValue->sizeOnStack(); i > 0; --i)
|
||||
@@ -437,7 +453,10 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
|
||||
// unary add, so basically no-op
|
||||
break;
|
||||
case Token::Sub: // -
|
||||
m_context << u256(0) << Instruction::SUB;
|
||||
if (m_context.arithmetic() == Arithmetic::Checked)
|
||||
m_context.callYulFunction(m_context.utilFunctions().negateNumberCheckedFunction(type), 1, 1);
|
||||
else
|
||||
m_context << u256(0) << Instruction::SUB;
|
||||
break;
|
||||
default:
|
||||
solAssert(false, "Invalid unary operator: " + string(TokenTraits::toString(_unaryOperation.getOperator())));
|
||||
@@ -460,7 +479,7 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
|
||||
m_context << commonType->literalValue(nullptr);
|
||||
else
|
||||
{
|
||||
bool cleanupNeeded = cleanupNeededForOp(commonType->category(), c_op);
|
||||
bool cleanupNeeded = cleanupNeededForOp(commonType->category(), c_op, m_context.arithmetic());
|
||||
|
||||
TypePointer leftTargetType = commonType;
|
||||
TypePointer rightTargetType =
|
||||
@@ -2112,34 +2131,65 @@ void ExpressionCompiler::appendArithmeticOperatorCode(Token _operator, Type cons
|
||||
solUnimplemented("Not yet implemented - FixedPointType.");
|
||||
|
||||
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
|
||||
bool const c_isSigned = type.isSigned();
|
||||
|
||||
switch (_operator)
|
||||
if (m_context.arithmetic() == Arithmetic::Checked)
|
||||
{
|
||||
case Token::Add:
|
||||
m_context << Instruction::ADD;
|
||||
break;
|
||||
case Token::Sub:
|
||||
m_context << Instruction::SUB;
|
||||
break;
|
||||
case Token::Mul:
|
||||
m_context << Instruction::MUL;
|
||||
break;
|
||||
case Token::Div:
|
||||
case Token::Mod:
|
||||
{
|
||||
// Test for division by zero
|
||||
m_context << Instruction::DUP2 << Instruction::ISZERO;
|
||||
m_context.appendConditionalInvalid();
|
||||
|
||||
if (_operator == Token::Div)
|
||||
m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
|
||||
else
|
||||
m_context << (c_isSigned ? Instruction::SMOD : Instruction::MOD);
|
||||
break;
|
||||
string functionName;
|
||||
switch (_operator)
|
||||
{
|
||||
case Token::Add:
|
||||
functionName = m_context.utilFunctions().overflowCheckedIntAddFunction(type);
|
||||
break;
|
||||
case Token::Sub:
|
||||
functionName = m_context.utilFunctions().overflowCheckedIntSubFunction(type);
|
||||
break;
|
||||
case Token::Mul:
|
||||
functionName = m_context.utilFunctions().overflowCheckedIntMulFunction(type);
|
||||
break;
|
||||
case Token::Div:
|
||||
functionName = m_context.utilFunctions().overflowCheckedIntDivFunction(type);
|
||||
break;
|
||||
case Token::Mod:
|
||||
functionName = m_context.utilFunctions().intModFunction(type);
|
||||
break;
|
||||
case Token::Exp:
|
||||
// EXP is handled in a different function.
|
||||
default:
|
||||
solAssert(false, "Unknown arithmetic operator.");
|
||||
}
|
||||
// TODO Maybe we want to force-inline this?
|
||||
m_context.callYulFunction(functionName, 2, 1);
|
||||
}
|
||||
default:
|
||||
solAssert(false, "Unknown arithmetic operator.");
|
||||
else
|
||||
{
|
||||
bool const c_isSigned = type.isSigned();
|
||||
|
||||
switch (_operator)
|
||||
{
|
||||
case Token::Add:
|
||||
m_context << Instruction::ADD;
|
||||
break;
|
||||
case Token::Sub:
|
||||
m_context << Instruction::SUB;
|
||||
break;
|
||||
case Token::Mul:
|
||||
m_context << Instruction::MUL;
|
||||
break;
|
||||
case Token::Div:
|
||||
case Token::Mod:
|
||||
{
|
||||
// Test for division by zero
|
||||
m_context << Instruction::DUP2 << Instruction::ISZERO;
|
||||
m_context.appendConditionalInvalid();
|
||||
|
||||
if (_operator == Token::Div)
|
||||
m_context << (c_isSigned ? Instruction::SDIV : Instruction::DIV);
|
||||
else
|
||||
m_context << (c_isSigned ? Instruction::SMOD : Instruction::MOD);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
solAssert(false, "Unknown arithmetic operator.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2237,7 +2287,14 @@ void ExpressionCompiler::appendExpOperatorCode(Type const& _valueType, Type cons
|
||||
solAssert(_valueType.category() == Type::Category::Integer, "");
|
||||
solAssert(!dynamic_cast<IntegerType const&>(_exponentType).isSigned(), "");
|
||||
|
||||
m_context << Instruction::EXP;
|
||||
|
||||
if (m_context.arithmetic() == Arithmetic::Checked)
|
||||
m_context.callYulFunction(m_context.utilFunctions().overflowCheckedIntExpFunction(
|
||||
dynamic_cast<IntegerType const&>(_valueType),
|
||||
dynamic_cast<IntegerType const&>(_exponentType)
|
||||
), 2, 1);
|
||||
else
|
||||
m_context << Instruction::EXP;
|
||||
}
|
||||
|
||||
void ExpressionCompiler::appendExternalFunctionCall(
|
||||
@@ -2561,11 +2618,15 @@ void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression)
|
||||
setLValue<StorageItem>(_expression, *_expression.annotation().type);
|
||||
}
|
||||
|
||||
bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token _op)
|
||||
bool ExpressionCompiler::cleanupNeededForOp(Type::Category _type, Token _op, Arithmetic _arithmetic)
|
||||
{
|
||||
if (TokenTraits::isCompareOp(_op) || TokenTraits::isShiftOp(_op))
|
||||
return true;
|
||||
else if (_type == Type::Category::Integer && (_op == Token::Div || _op == Token::Mod || _op == Token::Exp))
|
||||
else if (
|
||||
_arithmetic == Arithmetic::Wrapping &&
|
||||
_type == Type::Category::Integer &&
|
||||
(_op == Token::Div || _op == Token::Mod || _op == Token::Exp)
|
||||
)
|
||||
// We need cleanup for EXP because 0**0 == 1, but 0**0x100 == 0
|
||||
// It would suffice to clean the exponent, though.
|
||||
return true;
|
||||
|
||||
@@ -132,7 +132,7 @@ private:
|
||||
|
||||
/// @returns true if the operator applied to the given type requires a cleanup prior to the
|
||||
/// operation.
|
||||
static bool cleanupNeededForOp(Type::Category _type, Token _op);
|
||||
static bool cleanupNeededForOp(Type::Category _type, Token _op, Arithmetic _arithmetic);
|
||||
|
||||
void acceptAndConvert(Expression const& _expression, Type const& _type, bool _cleanupNeeded = false);
|
||||
|
||||
|
||||
@@ -483,6 +483,22 @@ string YulUtilFunctions::overflowCheckedIntAddFunction(IntegerType const& _type)
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::wrappingIntAddFunction(IntegerType const& _type)
|
||||
{
|
||||
string functionName = "wrapping_add_" + _type.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(x, y) -> sum {
|
||||
sum := <cleanupFunction>(add(x, y))
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
|
||||
{
|
||||
string functionName = "checked_mul_" + _type.identifier();
|
||||
@@ -519,6 +535,22 @@ string YulUtilFunctions::overflowCheckedIntMulFunction(IntegerType const& _type)
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::wrappingIntMulFunction(IntegerType const& _type)
|
||||
{
|
||||
string functionName = "wrapping_mul_" + _type.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(x, y) -> product {
|
||||
product := <cleanupFunction>(mul(x, y))
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
|
||||
{
|
||||
string functionName = "checked_div_" + _type.identifier();
|
||||
@@ -548,9 +580,30 @@ string YulUtilFunctions::overflowCheckedIntDivFunction(IntegerType const& _type)
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::checkedIntModFunction(IntegerType const& _type)
|
||||
string YulUtilFunctions::wrappingIntDivFunction(IntegerType const& _type)
|
||||
{
|
||||
string functionName = "checked_mod_" + _type.identifier();
|
||||
string functionName = "wrapping_div_" + _type.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(x, y) -> r {
|
||||
x := <cleanupFunction>(x)
|
||||
y := <cleanupFunction>(y)
|
||||
if iszero(y) { <error>() }
|
||||
r := <?signed>s</signed>div(x, y)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
("signed", _type.isSigned())
|
||||
("error", panicFunction())
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::intModFunction(IntegerType const& _type)
|
||||
{
|
||||
string functionName = "mod_" + _type.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return
|
||||
Whiskers(R"(
|
||||
@@ -599,6 +652,22 @@ string YulUtilFunctions::overflowCheckedIntSubFunction(IntegerType const& _type)
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::wrappingIntSubFunction(IntegerType const& _type)
|
||||
{
|
||||
string functionName = "wrapping_sub_" + _type.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&] {
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(x, y) -> diff {
|
||||
diff := <cleanupFunction>(sub(x, y))
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::overflowCheckedIntExpFunction(
|
||||
IntegerType const& _type,
|
||||
IntegerType const& _exponentType
|
||||
@@ -894,6 +963,30 @@ string YulUtilFunctions::overflowCheckedExpLoopFunction()
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::wrappingIntExpFunction(
|
||||
IntegerType const& _type,
|
||||
IntegerType const& _exponentType
|
||||
)
|
||||
{
|
||||
solAssert(!_exponentType.isSigned(), "");
|
||||
|
||||
string functionName = "wrapping_exp_" + _type.identifier() + "_" + _exponentType.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(base, exponent) -> power {
|
||||
base := <baseCleanupFunction>(base)
|
||||
exponent := <exponentCleanupFunction>(exponent)
|
||||
power := <baseCleanupFunction>(exp(base, exponent))
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("baseCleanupFunction", cleanupFunction(_type))
|
||||
("exponentCleanupFunction", cleanupFunction(_exponentType))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::extractByteArrayLengthFunction()
|
||||
{
|
||||
string functionName = "extract_byte_array_length";
|
||||
@@ -2951,30 +3044,39 @@ std::string YulUtilFunctions::decrementCheckedFunction(Type 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 {
|
||||
value := <cleanupFunction>(value)
|
||||
if <lt>(value, <minval>) { <panic>() }
|
||||
if eq(value, <minval>) { <panic>() }
|
||||
ret := sub(value, 1)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("panic", panicFunction())
|
||||
("minval", toCompactHexWithPrefix(minintval))
|
||||
("lt", type.isSigned() ? "slt" : "lt")
|
||||
("minval", toCompactHexWithPrefix(type.min()))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
std::string YulUtilFunctions::decrementWrappingFunction(Type const& _type)
|
||||
{
|
||||
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
|
||||
|
||||
string const functionName = "decrement_wrapping_" + _type.identifier();
|
||||
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return Whiskers(R"(
|
||||
function <functionName>(value) -> ret {
|
||||
ret := <cleanupFunction>(sub(value, 1))
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("cleanupFunction", cleanupFunction(type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
std::string YulUtilFunctions::incrementCheckedFunction(Type const& _type)
|
||||
{
|
||||
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
|
||||
@@ -2982,55 +3084,79 @@ std::string YulUtilFunctions::incrementCheckedFunction(Type 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 {
|
||||
value := <cleanupFunction>(value)
|
||||
if <gt>(value, <maxval>) { <panic>() }
|
||||
if eq(value, <maxval>) { <panic>() }
|
||||
ret := add(value, 1)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("maxval", toCompactHexWithPrefix(maxintval))
|
||||
("gt", type.isSigned() ? "sgt" : "gt")
|
||||
("maxval", toCompactHexWithPrefix(type.max()))
|
||||
("panic", panicFunction())
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
std::string YulUtilFunctions::incrementWrappingFunction(Type const& _type)
|
||||
{
|
||||
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
|
||||
|
||||
string const functionName = "increment_wrapping_" + _type.identifier();
|
||||
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return Whiskers(R"(
|
||||
function <functionName>(value) -> ret {
|
||||
ret := <cleanupFunction>(add(value, 1))
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("cleanupFunction", cleanupFunction(type))
|
||||
.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 {
|
||||
value := <cleanupFunction>(value)
|
||||
if slt(value, <minval>) { <panic>() }
|
||||
if eq(value, <minval>) { <panic>() }
|
||||
ret := sub(0, value)
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("minval", toCompactHexWithPrefix(minintval))
|
||||
("minval", toCompactHexWithPrefix(type.min()))
|
||||
("cleanupFunction", cleanupFunction(_type))
|
||||
("panic", panicFunction())
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::negateNumberWrappingFunction(Type const& _type)
|
||||
{
|
||||
IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
|
||||
solAssert(type.isSigned(), "Expected signed type!");
|
||||
|
||||
string const functionName = "negate_" + _type.identifier();
|
||||
return m_functionCollector.createFunction(functionName, [&]() {
|
||||
return Whiskers(R"(
|
||||
function <functionName>(value) -> ret {
|
||||
value := <cleanupFunction>(sub(0, value)))
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("cleanupFunction", cleanupFunction(type))
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::zeroValueFunction(Type const& _type, bool _splitFunctionTypes)
|
||||
{
|
||||
solAssert(_type.category() != Type::Category::Mapping, "");
|
||||
|
||||
@@ -106,24 +106,35 @@ public:
|
||||
|
||||
/// signature: (x, y) -> sum
|
||||
std::string overflowCheckedIntAddFunction(IntegerType const& _type);
|
||||
/// signature: (x, y) -> sum
|
||||
std::string wrappingIntAddFunction(IntegerType const& _type);
|
||||
|
||||
/// signature: (x, y) -> product
|
||||
std::string overflowCheckedIntMulFunction(IntegerType const& _type);
|
||||
/// signature: (x, y) -> product
|
||||
std::string wrappingIntMulFunction(IntegerType const& _type);
|
||||
|
||||
/// @returns name of function to perform division on integers.
|
||||
/// Checks for division by zero and the special case of
|
||||
/// signed division of the smallest number by -1.
|
||||
std::string overflowCheckedIntDivFunction(IntegerType const& _type);
|
||||
/// @returns name of function to perform division on integers.
|
||||
/// Checks for division by zero.
|
||||
std::string wrappingIntDivFunction(IntegerType const& _type);
|
||||
|
||||
/// @returns name of function to perform modulo on integers.
|
||||
/// Reverts for modulo by zero.
|
||||
std::string checkedIntModFunction(IntegerType const& _type);
|
||||
std::string intModFunction(IntegerType const& _type);
|
||||
|
||||
/// @returns computes the difference between two values.
|
||||
/// Assumes the input to be in range for the type.
|
||||
/// signature: (x, y) -> diff
|
||||
std::string overflowCheckedIntSubFunction(IntegerType const& _type);
|
||||
|
||||
/// @returns computes the difference between two values.
|
||||
/// signature: (x, y) -> diff
|
||||
std::string wrappingIntSubFunction(IntegerType const& _type);
|
||||
|
||||
/// @returns the name of the exponentiation function.
|
||||
/// signature: (base, exponent) -> power
|
||||
std::string overflowCheckedIntExpFunction(IntegerType const& _type, IntegerType const& _exponentType);
|
||||
@@ -151,6 +162,10 @@ public:
|
||||
/// signature: (power, base, exponent, max) -> power
|
||||
std::string overflowCheckedExpLoopFunction();
|
||||
|
||||
/// @returns the name of the exponentiation function.
|
||||
/// signature: (base, exponent) -> power
|
||||
std::string wrappingIntExpFunction(IntegerType const& _type, IntegerType const& _exponentType);
|
||||
|
||||
/// @returns the name of a function that fetches the length of the given
|
||||
/// array
|
||||
/// signature: (array) -> length
|
||||
@@ -367,9 +382,12 @@ public:
|
||||
std::string forwardingRevertFunction();
|
||||
|
||||
std::string incrementCheckedFunction(Type const& _type);
|
||||
std::string incrementWrappingFunction(Type const& _type);
|
||||
std::string decrementCheckedFunction(Type const& _type);
|
||||
std::string decrementWrappingFunction(Type const& _type);
|
||||
|
||||
std::string negateNumberCheckedFunction(Type const& _type);
|
||||
std::string negateNumberWrappingFunction(Type const& _type);
|
||||
|
||||
/// @returns the name of a function that returns the zero value for the
|
||||
/// provided type.
|
||||
|
||||
@@ -132,6 +132,9 @@ public:
|
||||
|
||||
langutil::EVMVersion evmVersion() const { return m_evmVersion; };
|
||||
|
||||
void setArithmetic(Arithmetic _value) { m_arithmetic = _value; }
|
||||
Arithmetic arithmetic() const { return m_arithmetic; }
|
||||
|
||||
ABIFunctions abiFunctions();
|
||||
|
||||
/// @returns code that stores @param _message for revert reason
|
||||
@@ -161,6 +164,8 @@ private:
|
||||
std::map<VariableDeclaration const*, std::pair<u256, unsigned>> m_stateVariables;
|
||||
MultiUseYulFunctionCollector m_functions;
|
||||
size_t m_varCounter = 0;
|
||||
/// Whether to use checked or wrapping arithmetic.
|
||||
Arithmetic m_arithmetic = Arithmetic::Checked;
|
||||
|
||||
/// Flag indicating whether any inline assembly block was seen.
|
||||
bool m_inlineAssemblySeen = false;
|
||||
|
||||
@@ -474,6 +474,25 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IRGeneratorForStatements::visit(Block const& _block)
|
||||
{
|
||||
if (_block.unchecked())
|
||||
{
|
||||
solAssert(m_context.arithmetic() == Arithmetic::Checked, "");
|
||||
m_context.setArithmetic(Arithmetic::Wrapping);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void IRGeneratorForStatements::endVisit(Block const& _block)
|
||||
{
|
||||
if (_block.unchecked())
|
||||
{
|
||||
solAssert(m_context.arithmetic() == Arithmetic::Wrapping, "");
|
||||
m_context.setArithmetic(Arithmetic::Checked);
|
||||
}
|
||||
}
|
||||
|
||||
bool IRGeneratorForStatements::visit(IfStatement const& _ifStatement)
|
||||
{
|
||||
_ifStatement.condition().accept(*this);
|
||||
@@ -618,11 +637,11 @@ void IRGeneratorForStatements::endVisit(UnaryOperation const& _unaryOperation)
|
||||
else if (op == Token::Sub)
|
||||
{
|
||||
IntegerType const& intType = *dynamic_cast<IntegerType const*>(&resultType);
|
||||
define(_unaryOperation) <<
|
||||
m_utils.negateNumberCheckedFunction(intType) <<
|
||||
"(" <<
|
||||
IRVariable(_unaryOperation.subExpression()).name() <<
|
||||
")\n";
|
||||
define(_unaryOperation) << (
|
||||
m_context.arithmetic() == Arithmetic::Checked ?
|
||||
m_utils.negateNumberCheckedFunction(intType) :
|
||||
m_utils.negateNumberWrappingFunction(intType)
|
||||
) << "(" << IRVariable(_unaryOperation.subExpression()).name() << ")\n";
|
||||
}
|
||||
else
|
||||
solUnimplementedAssert(false, "Unary operator not yet implemented");
|
||||
@@ -2560,23 +2579,23 @@ string IRGeneratorForStatements::binaryOperation(
|
||||
if (IntegerType const* type = dynamic_cast<IntegerType const*>(&_type))
|
||||
{
|
||||
string fun;
|
||||
// TODO: Implement all operations for signed and unsigned types.
|
||||
bool checked = m_context.arithmetic() == Arithmetic::Checked;
|
||||
switch (_operator)
|
||||
{
|
||||
case Token::Add:
|
||||
fun = m_utils.overflowCheckedIntAddFunction(*type);
|
||||
fun = checked ? m_utils.overflowCheckedIntAddFunction(*type) : m_utils.wrappingIntAddFunction(*type);
|
||||
break;
|
||||
case Token::Sub:
|
||||
fun = m_utils.overflowCheckedIntSubFunction(*type);
|
||||
fun = checked ? m_utils.overflowCheckedIntSubFunction(*type) : m_utils.wrappingIntSubFunction(*type);
|
||||
break;
|
||||
case Token::Mul:
|
||||
fun = m_utils.overflowCheckedIntMulFunction(*type);
|
||||
fun = checked ? m_utils.overflowCheckedIntMulFunction(*type) : m_utils.wrappingIntMulFunction(*type);
|
||||
break;
|
||||
case Token::Div:
|
||||
fun = m_utils.overflowCheckedIntDivFunction(*type);
|
||||
fun = checked ? m_utils.overflowCheckedIntDivFunction(*type) : m_utils.wrappingIntDivFunction(*type);
|
||||
break;
|
||||
case Token::Mod:
|
||||
fun = m_utils.checkedIntModFunction(*type);
|
||||
fun = m_utils.intModFunction(*type);
|
||||
break;
|
||||
case Token::BitOr:
|
||||
fun = "or";
|
||||
|
||||
@@ -66,6 +66,8 @@ public:
|
||||
bool visit(Conditional const& _conditional) override;
|
||||
bool visit(Assignment const& _assignment) override;
|
||||
bool visit(TupleExpression const& _tuple) override;
|
||||
bool visit(Block const& _block) override;
|
||||
void endVisit(Block const& _block) override;
|
||||
bool visit(IfStatement const& _ifStatement) override;
|
||||
bool visit(ForStatement const& _forStatement) override;
|
||||
bool visit(WhileStatement const& _whileStatement) override;
|
||||
|
||||
Reference in New Issue
Block a user