mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Overflow-checked addition.
This commit is contained in:
parent
826f2d9084
commit
18ab8aeb85
@ -69,6 +69,8 @@ set(sources
|
||||
codegen/YulUtilFunctions.cpp
|
||||
codegen/ir/IRGenerator.cpp
|
||||
codegen/ir/IRGenerator.h
|
||||
codegen/ir/IRGeneratorForStatements.cpp
|
||||
codegen/ir/IRGeneratorForStatements.h
|
||||
codegen/ir/IRGenerationContext.cpp
|
||||
codegen/ir/IRGenerationContext.h
|
||||
formal/SMTChecker.cpp
|
||||
|
@ -252,6 +252,36 @@ string YulUtilFunctions::roundUpFunction()
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::overflowCheckedUIntAddFunction(size_t _bits)
|
||||
{
|
||||
solAssert(0 < _bits && _bits <= 256 && _bits % 8 == 0, "");
|
||||
string functionName = "checked_add_uint_" + to_string(_bits);
|
||||
return m_functionCollector->createFunction(functionName, [&]() {
|
||||
if (_bits < 256)
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(x, y) -> sum {
|
||||
let mask := <mask>
|
||||
sum := add(and(x, mask), and(y, mask))
|
||||
if and(sum, not(mask)) { revert(0, 0) }
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
("mask", toCompactHexWithPrefix((u256(1) << _bits) - 1))
|
||||
.render();
|
||||
else
|
||||
return
|
||||
Whiskers(R"(
|
||||
function <functionName>(x, y) -> sum {
|
||||
sum := add(x, y)
|
||||
if lt(sum, x) { revert(0, 0) }
|
||||
}
|
||||
)")
|
||||
("functionName", functionName)
|
||||
.render();
|
||||
});
|
||||
}
|
||||
|
||||
string YulUtilFunctions::arrayLengthFunction(ArrayType const& _type)
|
||||
{
|
||||
string functionName = "array_length_" + _type.identifier();
|
||||
|
@ -73,6 +73,8 @@ public:
|
||||
/// of 32 or the input if it is a multiple of 32.
|
||||
std::string roundUpFunction();
|
||||
|
||||
std::string overflowCheckedUIntAddFunction(size_t _bits);
|
||||
|
||||
std::string arrayLengthFunction(ArrayType const& _type);
|
||||
/// @returns the name of a function that computes the number of bytes required
|
||||
/// to store an array in memory given its length (internally encoded, not ABI encoded).
|
||||
|
@ -35,3 +35,24 @@ string IRGenerationContext::addLocalVariable(VariableDeclaration const& _varDecl
|
||||
|
||||
return m_localVariables[&_varDecl] = "vloc_" + _varDecl.name() + "_" + to_string(_varDecl.id());
|
||||
}
|
||||
|
||||
string IRGenerationContext::variableName(VariableDeclaration const& _varDecl)
|
||||
{
|
||||
solAssert(
|
||||
m_localVariables.count(&_varDecl),
|
||||
"Unknown variable: " + _varDecl.name()
|
||||
);
|
||||
return m_localVariables[&_varDecl];
|
||||
}
|
||||
|
||||
string IRGenerationContext::newYulVariable()
|
||||
{
|
||||
return "_" + to_string(++m_varCounter);
|
||||
}
|
||||
|
||||
string IRGenerationContext::variable(Expression const& _expression)
|
||||
{
|
||||
unsigned size = _expression.annotation().type->sizeOnStack();
|
||||
solUnimplementedAssert(size == 1, "");
|
||||
return "expr_" + to_string(_expression.id());
|
||||
}
|
||||
|
@ -35,6 +35,7 @@ namespace solidity
|
||||
{
|
||||
|
||||
class VariableDeclaration;
|
||||
class Expression;
|
||||
|
||||
/**
|
||||
* Class that contains contextual information during IR generation.
|
||||
@ -51,12 +52,18 @@ public:
|
||||
std::shared_ptr<MultiUseYulFunctionCollector> functionCollector() const { return m_functions; }
|
||||
|
||||
std::string addLocalVariable(VariableDeclaration const& _varDecl);
|
||||
std::string variableName(VariableDeclaration const& _varDecl);
|
||||
std::string newYulVariable();
|
||||
/// @returns the variable (or comma-separated list of variables) that contain
|
||||
/// the value of the given expression.
|
||||
std::string variable(Expression const& _expression);
|
||||
|
||||
private:
|
||||
langutil::EVMVersion m_evmVersion;
|
||||
OptimiserSettings m_optimiserSettings;
|
||||
std::map<VariableDeclaration const*, std::string> m_localVariables;
|
||||
std::shared_ptr<MultiUseYulFunctionCollector> m_functions;
|
||||
size_t m_varCounter = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -22,7 +22,10 @@
|
||||
|
||||
#include <libsolidity/codegen/ir/IRGenerator.h>
|
||||
|
||||
#include <libsolidity/codegen/ir/IRGeneratorForStatements.h>
|
||||
|
||||
#include <libsolidity/ast/AST.h>
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
#include <libsolidity/codegen/ABIFunctions.h>
|
||||
#include <libsolidity/codegen/CompilerUtils.h>
|
||||
|
||||
@ -43,7 +46,7 @@ using namespace dev::solidity;
|
||||
pair<string, string> IRGenerator::run(ContractDefinition const& _contract)
|
||||
{
|
||||
// TODO Would be nice to pretty-print this while retaining comments.
|
||||
string ir = generateIR(_contract);
|
||||
string ir = generate(_contract);
|
||||
|
||||
yul::AssemblyStack asmStack(m_evmVersion, yul::AssemblyStack::Language::StrictAssembly, m_optimiserSettings);
|
||||
if (!asmStack.parseAndAnalyze("", ir))
|
||||
@ -69,7 +72,7 @@ pair<string, string> IRGenerator::run(ContractDefinition const& _contract)
|
||||
return {warning + ir, warning + asmStack.print()};
|
||||
}
|
||||
|
||||
string IRGenerator::generateIR(ContractDefinition const& _contract)
|
||||
string IRGenerator::generate(ContractDefinition const& _contract)
|
||||
{
|
||||
Whiskers t(R"(
|
||||
object "<CreationObject>" {
|
||||
@ -103,11 +106,18 @@ string IRGenerator::generateIR(ContractDefinition const& _contract)
|
||||
return t.render();
|
||||
}
|
||||
|
||||
string IRGenerator::generateIRFunction(FunctionDefinition const& _function)
|
||||
string IRGenerator::generate(Block const& _block)
|
||||
{
|
||||
IRGeneratorForStatements generator(m_context, m_utils);
|
||||
_block.accept(generator);
|
||||
return generator.code();
|
||||
}
|
||||
|
||||
string IRGenerator::generateFunction(FunctionDefinition const& _function)
|
||||
{
|
||||
string functionName = "fun_" + to_string(_function.id()) + "_" + _function.name();
|
||||
return m_context.functionCollector()->createFunction(functionName, [&]() {
|
||||
Whiskers t("function <functionName>(<params>) <returns> {}");
|
||||
Whiskers t("\nfunction <functionName>(<params>) <returns> {\n<body>\n}\n");
|
||||
t("functionName", functionName);
|
||||
string params;
|
||||
for (auto const& varDecl: _function.parameters())
|
||||
@ -117,6 +127,7 @@ string IRGenerator::generateIRFunction(FunctionDefinition const& _function)
|
||||
for (auto const& varDecl: _function.returnParameters())
|
||||
retParams += (retParams.empty() ? "" : ", ") + m_context.addLocalVariable(*varDecl);
|
||||
t("returns", retParams.empty() ? "" : " -> " + retParams);
|
||||
t("body", generate(_function.body()));
|
||||
return t.render();
|
||||
});
|
||||
}
|
||||
@ -200,7 +211,7 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
||||
templ["abiDecode"] = abiFunctions.tupleDecoder(type->parameterTypes());
|
||||
templ["params"] = m_utils.suffixedVariableNameList("param_", 0, paramVars);
|
||||
templ["retParams"] = m_utils.suffixedVariableNameList("ret_", retVars, 0);
|
||||
templ["function"] = generateIRFunction(dynamic_cast<FunctionDefinition const&>(type->declaration()));
|
||||
templ["function"] = generateFunction(dynamic_cast<FunctionDefinition const&>(type->declaration()));
|
||||
templ["allocate"] = m_utils.allocationFunction();
|
||||
templ["abiEncode"] = abiFunctions.tupleEncoder(type->returnParameterTypes(), type->returnParameterTypes(), false);
|
||||
templ["comma"] = retVars == 0 ? "" : ", ";
|
||||
@ -211,7 +222,7 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
|
||||
string fallbackCode;
|
||||
if (!fallback->isPayable())
|
||||
fallbackCode += callValueCheck();
|
||||
fallbackCode += generateIRFunction(*fallback) + "() stop()";
|
||||
fallbackCode += generateFunction(*fallback) + "() stop()";
|
||||
|
||||
t("fallback", fallbackCode);
|
||||
}
|
||||
|
@ -51,8 +51,11 @@ public:
|
||||
std::pair<std::string, std::string> run(ContractDefinition const& _contract);
|
||||
|
||||
private:
|
||||
std::string generateIR(ContractDefinition const& _contract);
|
||||
std::string generateIRFunction(FunctionDefinition const& _function);
|
||||
std::string generate(ContractDefinition const& _contract);
|
||||
std::string generate(Block const& _block);
|
||||
|
||||
/// Generates code for and returns the name of the function.
|
||||
std::string generateFunction(FunctionDefinition const& _function);
|
||||
|
||||
std::string constructorCode(FunctionDefinition const& _constructor);
|
||||
std::string deployCode(ContractDefinition const& _contract);
|
||||
|
108
libsolidity/codegen/ir/IRGeneratorForStatements.cpp
Normal file
108
libsolidity/codegen/ir/IRGeneratorForStatements.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Component that translates Solidity code into Yul at statement level and below.
|
||||
*/
|
||||
|
||||
#include <libsolidity/codegen/ir/IRGeneratorForStatements.h>
|
||||
|
||||
#include <libsolidity/codegen/ir/IRGenerationContext.h>
|
||||
#include <libsolidity/codegen/YulUtilFunctions.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
|
||||
bool IRGeneratorForStatements::visit(VariableDeclarationStatement const& _varDeclStatement)
|
||||
{
|
||||
for (auto const& decl: _varDeclStatement.declarations())
|
||||
if (decl)
|
||||
m_context.addLocalVariable(*decl);
|
||||
|
||||
if (Expression const* expression = _varDeclStatement.initialValue())
|
||||
{
|
||||
solUnimplementedAssert(_varDeclStatement.declarations().size() == 1, "");
|
||||
|
||||
expression->accept(*this);
|
||||
|
||||
solUnimplementedAssert(
|
||||
*expression->annotation().type == *_varDeclStatement.declarations().front()->type(),
|
||||
"Type conversion not yet implemented"
|
||||
);
|
||||
m_code <<
|
||||
"let " <<
|
||||
m_context.variableName(*_varDeclStatement.declarations().front()) <<
|
||||
" := " <<
|
||||
m_context.variable(*expression) <<
|
||||
"\n";
|
||||
}
|
||||
else
|
||||
for (auto const& decl: _varDeclStatement.declarations())
|
||||
if (decl)
|
||||
m_code << "let " << m_context.variableName(*decl) << "\n";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IRGeneratorForStatements::visit(Assignment const& _assignment)
|
||||
{
|
||||
solUnimplementedAssert(_assignment.assignmentOperator() == Token::Assign, "");
|
||||
|
||||
_assignment.rightHandSide().accept(*this);
|
||||
|
||||
solUnimplementedAssert(
|
||||
*_assignment.rightHandSide().annotation().type == *_assignment.leftHandSide().annotation().type,
|
||||
"Type conversion not yet implemented"
|
||||
);
|
||||
// TODO proper lvalue handling
|
||||
auto const& identifier = dynamic_cast<Identifier const&>(_assignment.leftHandSide());
|
||||
string varName = m_context.variableName(dynamic_cast<VariableDeclaration const&>(*identifier.annotation().referencedDeclaration));
|
||||
m_code << varName << " := " << m_context.variable(_assignment.rightHandSide()) << "\n";
|
||||
m_code << "let " << m_context.variable(_assignment) << " := " << varName << "\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
void IRGeneratorForStatements::endVisit(BinaryOperation const& _binOp)
|
||||
{
|
||||
solUnimplementedAssert(_binOp.getOperator() == Token::Add, "");
|
||||
solUnimplementedAssert(*_binOp.leftExpression().annotation().type == *_binOp.rightExpression().annotation().type, "");
|
||||
if (IntegerType const* type = dynamic_cast<IntegerType const*>(_binOp.annotation().commonType.get()))
|
||||
{
|
||||
solUnimplementedAssert(!type->isSigned(), "");
|
||||
m_code <<
|
||||
"let " <<
|
||||
m_context.variable(_binOp) <<
|
||||
" := " <<
|
||||
m_utils.overflowCheckedUIntAddFunction(type->numBits()) <<
|
||||
"(" <<
|
||||
m_context.variable(_binOp.leftExpression()) <<
|
||||
", " <<
|
||||
m_context.variable(_binOp.rightExpression()) <<
|
||||
")\n";
|
||||
}
|
||||
else
|
||||
solUnimplementedAssert(false, "");
|
||||
}
|
||||
|
||||
bool IRGeneratorForStatements::visit(Identifier const& _identifier)
|
||||
{
|
||||
auto const& decl = dynamic_cast<VariableDeclaration const&>(
|
||||
*_identifier.annotation().referencedDeclaration
|
||||
);
|
||||
m_code << "let " << m_context.variable(_identifier) << " := " << m_context.variableName(decl) << "\n";
|
||||
return false;
|
||||
}
|
59
libsolidity/codegen/ir/IRGeneratorForStatements.h
Normal file
59
libsolidity/codegen/ir/IRGeneratorForStatements.h
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/**
|
||||
* Component that translates Solidity code into Yul at statement level and below.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/ast/ASTVisitor.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
class IRGenerationContext;
|
||||
class YulUtilFunctions;
|
||||
|
||||
/**
|
||||
* Component that translates Solidity's AST into Yul at statement level and below.
|
||||
* It is an AST visitor that appends to an internal string buffer.
|
||||
*/
|
||||
class IRGeneratorForStatements: public ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
IRGeneratorForStatements(IRGenerationContext& _context, YulUtilFunctions& _utils):
|
||||
m_context(_context),
|
||||
m_utils(_utils)
|
||||
{}
|
||||
|
||||
std::string code() const { return m_code.str(); }
|
||||
|
||||
bool visit(VariableDeclarationStatement const& _variableDeclaration) override;
|
||||
bool visit(Assignment const& _assignment) override;
|
||||
void endVisit(BinaryOperation const& _binOp) override;
|
||||
bool visit(Identifier const& _identifier) override;
|
||||
|
||||
private:
|
||||
std::ostringstream m_code;
|
||||
IRGenerationContext& m_context;
|
||||
YulUtilFunctions& m_utils;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
contract C {
|
||||
function f(uint a, uint b) public pure returns (uint x) {
|
||||
x = a + b;
|
||||
}
|
||||
function g(uint8 a, uint8 b) public pure returns (uint8 x) {
|
||||
x = a + b;
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: true
|
||||
// ----
|
||||
// f(uint256,uint256): 5, 6 -> 11
|
||||
// f(uint256,uint256): -2, 1 -> -1
|
||||
// f(uint256,uint256): -2, 2 -> FAILURE
|
||||
// f(uint256,uint256): 2, -2 -> FAILURE
|
||||
// g(uint8,uint8): 128, 64 -> 192
|
||||
// g(uint8,uint8): 128, 127 -> 255
|
||||
// g(uint8,uint8): 128, 128 -> FAILURE
|
@ -0,0 +1,10 @@
|
||||
contract C {
|
||||
function f(address a) public pure returns (address x) {
|
||||
address b = a;
|
||||
x = b;
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: true
|
||||
// ----
|
||||
// f(address): 0x1234 -> 0x1234
|
10
test/libsolidity/semanticTests/viaYul/local_assignment.sol
Normal file
10
test/libsolidity/semanticTests/viaYul/local_assignment.sol
Normal file
@ -0,0 +1,10 @@
|
||||
contract C {
|
||||
function f(uint a) public pure returns (uint x) {
|
||||
uint b = a;
|
||||
x = b;
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: true
|
||||
// ----
|
||||
// f(uint256): 6 -> 6
|
@ -0,0 +1,10 @@
|
||||
contract C {
|
||||
function f(bool a) public pure returns (bool x) {
|
||||
bool b = a;
|
||||
x = b;
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: true
|
||||
// ----
|
||||
// f(bool): true -> true
|
10
test/libsolidity/semanticTests/viaYul/simple_assignment.sol
Normal file
10
test/libsolidity/semanticTests/viaYul/simple_assignment.sol
Normal file
@ -0,0 +1,10 @@
|
||||
contract C {
|
||||
function f(uint a, uint b) public pure returns (uint x, uint y) {
|
||||
x = a;
|
||||
y = b;
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: true
|
||||
// ----
|
||||
// f(uint256,uint256): 5, 6 -> 5, 6
|
Loading…
Reference in New Issue
Block a user