aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libdevcore/CommonIO.cpp39
-rw-r--r--libdevcore/CommonIO.h8
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp19
-rw-r--r--libyul/optimiser/CommonSubexpressionEliminator.cpp5
-rw-r--r--libyul/optimiser/DataFlowAnalyzer.cpp35
-rw-r--r--libyul/optimiser/DataFlowAnalyzer.h8
-rw-r--r--libyul/optimiser/FullInliner.cpp19
-rw-r--r--libyul/optimiser/FullInliner.h4
-rw-r--r--libyul/optimiser/Rematerialiser.cpp9
-rw-r--r--solc/CommandLineInterface.cpp10
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp15
-rw-r--r--test/libyul/YulOptimizerTest.cpp2
-rw-r--r--test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul22
-rw-r--r--test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul25
-rw-r--r--test/libyul/yulOptimizerTests/fullSimplify/operations.yul44
-rw-r--r--test/libyul/yulOptimizerTests/fullSimplify/signextend.yul12
16 files changed, 182 insertions, 94 deletions
diff --git a/libdevcore/CommonIO.cpp b/libdevcore/CommonIO.cpp
index 1aa3504c..cc730575 100644
--- a/libdevcore/CommonIO.cpp
+++ b/libdevcore/CommonIO.cpp
@@ -80,45 +80,6 @@ string dev::readStandardInput()
return ret;
}
-void dev::writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename)
-{
- namespace fs = boost::filesystem;
- if (_writeDeleteRename)
- {
- fs::path tempPath = fs::unique_path(_file + "-%%%%%%");
- writeFile(tempPath.string(), _data, false);
- // will delete _file if it exists
- fs::rename(tempPath, _file);
- }
- else
- {
- // create directory if not existent
- fs::path p(_file);
- if (!p.parent_path().empty() && !fs::exists(p.parent_path()))
- {
- fs::create_directories(p.parent_path());
- try
- {
- fs::permissions(p.parent_path(), fs::owner_all);
- }
- catch (...)
- {
- }
- }
-
- ofstream s(_file, ios::trunc | ios::binary);
- s.write(reinterpret_cast<char const*>(_data.data()), _data.size());
- assertThrow(s, FileError, "Could not write to file: " + _file);
- try
- {
- fs::permissions(_file, fs::owner_read|fs::owner_write);
- }
- catch (...)
- {
- }
- }
-}
-
#if defined(_WIN32)
class DisableConsoleBuffering
{
diff --git a/libdevcore/CommonIO.h b/libdevcore/CommonIO.h
index 928b6d15..b9f941ea 100644
--- a/libdevcore/CommonIO.h
+++ b/libdevcore/CommonIO.h
@@ -41,14 +41,6 @@ std::string readStandardInput();
/// Retrieve and returns a character from standard input (without waiting for EOL).
int readStandardInputChar();
-/// Write the given binary data into the given file, replacing the file if it pre-exists.
-/// Throws exception on error.
-/// @param _writeDeleteRename useful not to lose any data: If set, first writes to another file in
-/// the same directory and then moves that file.
-void writeFile(std::string const& _file, bytesConstRef _data, bool _writeDeleteRename = false);
-/// Write the given binary data into the given file, replacing the file if it pre-exists.
-inline void writeFile(std::string const& _file, bytes const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(&_data), _writeDeleteRename); }
-inline void writeFile(std::string const& _file, std::string const& _data, bool _writeDeleteRename = false) { writeFile(_file, bytesConstRef(_data), _writeDeleteRename); }
/// Converts arbitrary value to string representation using std::stringstream.
template <class _T>
std::string toString(_T const& _t)
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 27440289..749739ce 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -568,12 +568,13 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
case FunctionType::Kind::External:
case FunctionType::Kind::DelegateCall:
case FunctionType::Kind::BareCall:
- case FunctionType::Kind::BareCallCode:
case FunctionType::Kind::BareDelegateCall:
case FunctionType::Kind::BareStaticCall:
_functionCall.expression().accept(*this);
appendExternalFunctionCall(function, arguments);
break;
+ case FunctionType::Kind::BareCallCode:
+ solAssert(false, "Callcode has been removed.");
case FunctionType::Kind::Creation:
{
_functionCall.expression().accept(*this);
@@ -1328,8 +1329,6 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
m_context << Instruction::CALLVALUE;
else if (member == "origin")
m_context << Instruction::ORIGIN;
- else if (member == "gas")
- m_context << Instruction::GAS;
else if (member == "gasprice")
m_context << Instruction::GASPRICE;
else if (member == "data")
@@ -1337,9 +1336,10 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
else if (member == "sig")
m_context << u256(0) << Instruction::CALLDATALOAD
<< (u256(0xffffffff) << (256 - 32)) << Instruction::AND;
+ else if (member == "gas")
+ solAssert(false, "Gas has been removed.");
else if (member == "blockhash")
- {
- }
+ solAssert(false, "Blockhash has been removed.");
else
solAssert(false, "Unknown magic member.");
break;
@@ -1844,8 +1844,9 @@ void ExpressionCompiler::appendExternalFunctionCall(
solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), "");
- bool returnSuccessConditionAndReturndata = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::BareStaticCall;
- bool isCallCode = funKind == FunctionType::Kind::BareCallCode;
+ solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed.");
+
+ bool returnSuccessConditionAndReturndata = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::BareStaticCall;
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
bool useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (_functionType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall());
@@ -1930,7 +1931,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
parameterTypes,
_functionType.padArguments(),
_functionType.takesArbitraryParameters() || _functionType.isBareCall(),
- isCallCode || isDelegateCall
+ isDelegateCall
);
// Stack now:
@@ -2001,8 +2002,6 @@ void ExpressionCompiler::appendExternalFunctionCall(
// Order is important here, STATICCALL might overlap with DELEGATECALL.
if (isDelegateCall)
m_context << Instruction::DELEGATECALL;
- else if (isCallCode)
- m_context << Instruction::CALLCODE;
else if (useStaticCall)
m_context << Instruction::STATICCALL;
else
diff --git a/libyul/optimiser/CommonSubexpressionEliminator.cpp b/libyul/optimiser/CommonSubexpressionEliminator.cpp
index 23d15cad..51737097 100644
--- a/libyul/optimiser/CommonSubexpressionEliminator.cpp
+++ b/libyul/optimiser/CommonSubexpressionEliminator.cpp
@@ -50,8 +50,8 @@ void CommonSubexpressionEliminator::visit(Expression& _e)
if (m_value.at(name)->type() == typeid(Identifier))
{
string const& value = boost::get<Identifier>(*m_value.at(name)).name;
- if (inScope(value))
- _e = Identifier{locationOf(_e), value};
+ assertThrow(inScope(value), OptimizerException, "");
+ _e = Identifier{locationOf(_e), value};
}
}
}
@@ -61,6 +61,7 @@ void CommonSubexpressionEliminator::visit(Expression& _e)
for (auto const& var: m_value)
{
assertThrow(var.second, OptimizerException, "");
+ assertThrow(inScope(var.first), OptimizerException, "");
if (SyntacticalEqualityChecker::equal(_e, *var.second))
{
_e = Identifier{locationOf(_e), var.first};
diff --git a/libyul/optimiser/DataFlowAnalyzer.cpp b/libyul/optimiser/DataFlowAnalyzer.cpp
index ca1e5153..7ac42c30 100644
--- a/libyul/optimiser/DataFlowAnalyzer.cpp
+++ b/libyul/optimiser/DataFlowAnalyzer.cpp
@@ -84,19 +84,19 @@ void DataFlowAnalyzer::operator()(Switch& _switch)
void DataFlowAnalyzer::operator()(FunctionDefinition& _fun)
{
- m_variableScopes.emplace_back(true);
+ pushScope(true);
for (auto const& parameter: _fun.parameters)
m_variableScopes.back().variables.insert(parameter.name);
for (auto const& var: _fun.returnVariables)
m_variableScopes.back().variables.insert(var.name);
ASTModifier::operator()(_fun);
- m_variableScopes.pop_back();
+ popScope();
}
void DataFlowAnalyzer::operator()(ForLoop& _for)
{
// Special scope handling of the pre block.
- m_variableScopes.emplace_back(false);
+ pushScope(false);
for (auto& statement: _for.pre.statements)
visit(statement);
@@ -110,16 +110,15 @@ void DataFlowAnalyzer::operator()(ForLoop& _for)
(*this)(_for.post);
clearValues(assignments.names());
-
- m_variableScopes.pop_back();
+ popScope();
}
void DataFlowAnalyzer::operator()(Block& _block)
{
size_t numScopes = m_variableScopes.size();
- m_variableScopes.emplace_back(false);
+ pushScope(false);
ASTModifier::operator()(_block);
- m_variableScopes.pop_back();
+ popScope();
assertThrow(numScopes == m_variableScopes.size(), OptimizerException, "");
}
@@ -148,7 +147,18 @@ void DataFlowAnalyzer::handleAssignment(set<string> const& _variables, Expressio
}
}
-void DataFlowAnalyzer::clearValues(set<string> const& _variables)
+void DataFlowAnalyzer::pushScope(bool _functionScope)
+{
+ m_variableScopes.emplace_back(_functionScope);
+}
+
+void DataFlowAnalyzer::popScope()
+{
+ clearValues(std::move(m_variableScopes.back().variables));
+ m_variableScopes.pop_back();
+}
+
+void DataFlowAnalyzer::clearValues(set<string> _variables)
{
// All variables that reference variables to be cleared also have to be
// cleared, but not recursively, since only the value of the original
@@ -163,16 +173,15 @@ void DataFlowAnalyzer::clearValues(set<string> const& _variables)
// This cannot be easily tested since the substitutions will be done
// one by one on the fly, and the last line will just be add(1, 1)
- set<string> variables = _variables;
// Clear variables that reference variables to be cleared.
- for (auto const& name: variables)
+ for (auto const& name: _variables)
for (auto const& ref: m_referencedBy[name])
- variables.insert(ref);
+ _variables.insert(ref);
// Clear the value and update the reference relation.
- for (auto const& name: variables)
+ for (auto const& name: _variables)
m_value.erase(name);
- for (auto const& name: variables)
+ for (auto const& name: _variables)
{
for (auto const& ref: m_references[name])
m_referencedBy[ref].erase(name);
diff --git a/libyul/optimiser/DataFlowAnalyzer.h b/libyul/optimiser/DataFlowAnalyzer.h
index f998eadf..95671467 100644
--- a/libyul/optimiser/DataFlowAnalyzer.h
+++ b/libyul/optimiser/DataFlowAnalyzer.h
@@ -56,9 +56,15 @@ protected:
/// Registers the assignment.
void handleAssignment(std::set<std::string> const& _names, Expression* _value);
+ /// Creates a new inner scope.
+ void pushScope(bool _functionScope);
+
+ /// Removes the innermost scope and clears all variables in it.
+ void popScope();
+
/// Clears information about the values assigned to the given variables,
/// for example at points where control flow is merged.
- void clearValues(std::set<std::string> const& _names);
+ void clearValues(std::set<std::string> _names);
/// Returns true iff the variable is in scope.
bool inScope(std::string const& _variableName) const;
diff --git a/libyul/optimiser/FullInliner.cpp b/libyul/optimiser/FullInliner.cpp
index ce71eda5..280d625a 100644
--- a/libyul/optimiser/FullInliner.cpp
+++ b/libyul/optimiser/FullInliner.cpp
@@ -40,12 +40,9 @@ using namespace dev;
using namespace dev::yul;
using namespace dev::solidity;
-FullInliner::FullInliner(Block& _ast):
- m_ast(_ast), m_nameDispenser(_ast)
+FullInliner::FullInliner(Block& _ast, NameDispenser& _dispenser):
+ m_ast(_ast), m_nameDispenser(_dispenser)
{
- assertThrow(m_ast.statements.size() >= 1, OptimizerException, "");
- assertThrow(m_ast.statements.front().type() == typeid(Block), OptimizerException, "");
-
// Determine constants
SSAValueTracker tracker;
tracker(m_ast);
@@ -54,10 +51,11 @@ FullInliner::FullInliner(Block& _ast):
m_constants.insert(ssaValue.first);
map<string, size_t> references = ReferencesCounter::countReferences(m_ast);
- for (size_t i = 1; i < m_ast.statements.size(); ++i)
+ for (auto& statement: m_ast.statements)
{
- assertThrow(m_ast.statements.at(i).type() == typeid(FunctionDefinition), OptimizerException, "");
- FunctionDefinition& fun = boost::get<FunctionDefinition>(m_ast.statements.at(i));
+ if (statement.type() != typeid(FunctionDefinition))
+ continue;
+ FunctionDefinition& fun = boost::get<FunctionDefinition>(statement);
m_functions[fun.name] = &fun;
// Always inline functions that are only called once.
if (references[fun.name] == 1)
@@ -68,9 +66,10 @@ FullInliner::FullInliner(Block& _ast):
void FullInliner::run()
{
- assertThrow(m_ast.statements[0].type() == typeid(Block), OptimizerException, "");
+ for (auto& statement: m_ast.statements)
+ if (statement.type() == typeid(Block))
+ handleBlock("", boost::get<Block>(statement));
- handleBlock("", boost::get<Block>(m_ast.statements[0]));
// TODO it might be good to determine a visiting order:
// first handle functions that are called from many places.
for (auto const& fun: m_functions)
diff --git a/libyul/optimiser/FullInliner.h b/libyul/optimiser/FullInliner.h
index cd59ab46..5ecfb57a 100644
--- a/libyul/optimiser/FullInliner.h
+++ b/libyul/optimiser/FullInliner.h
@@ -71,7 +71,7 @@ class NameCollector;
class FullInliner: public ASTModifier
{
public:
- explicit FullInliner(Block& _ast);
+ explicit FullInliner(Block& _ast, NameDispenser& _dispenser);
void run();
@@ -94,7 +94,7 @@ private:
/// Variables that are constants (used for inlining heuristic)
std::set<std::string> m_constants;
std::map<std::string, size_t> m_functionSizes;
- NameDispenser m_nameDispenser;
+ NameDispenser& m_nameDispenser;
};
/**
diff --git a/libyul/optimiser/Rematerialiser.cpp b/libyul/optimiser/Rematerialiser.cpp
index dd6653ea..a99db0b6 100644
--- a/libyul/optimiser/Rematerialiser.cpp
+++ b/libyul/optimiser/Rematerialiser.cpp
@@ -38,16 +38,11 @@ void Rematerialiser::visit(Expression& _e)
if (m_value.count(identifier.name))
{
string name = identifier.name;
- bool expressionValid = true;
for (auto const& ref: m_references[name])
- if (!inScope(ref))
- {
- expressionValid = false;
- break;
- }
+ assertThrow(inScope(ref), OptimizerException, "");
assertThrow(m_value.at(name), OptimizerException, "");
auto const& value = *m_value.at(name);
- if (expressionValid && CodeSize::codeSize(value) <= 7)
+ if (CodeSize::codeSize(value) <= 7)
_e = (ASTCopier{}).translate(value);
}
}
diff --git a/solc/CommandLineInterface.cpp b/solc/CommandLineInterface.cpp
index e0c6a2b6..844cef90 100644
--- a/solc/CommandLineInterface.cpp
+++ b/solc/CommandLineInterface.cpp
@@ -1118,7 +1118,15 @@ void CommandLineInterface::writeLinkedFiles()
if (src.first == g_stdinFileName)
cout << src.second << endl;
else
- writeFile(src.first, src.second);
+ {
+ ofstream outFile(src.first);
+ outFile << src.second;
+ if (!outFile)
+ {
+ cerr << "Could not write to file " << src.first << ". Aborting." << endl;
+ return;
+ }
+ }
}
string CommandLineInterface::libraryPlaceholderHint(string const& _libraryName)
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index 7a496e64..133387c8 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -14010,6 +14010,21 @@ BOOST_AUTO_TEST_CASE(test_underscore_in_hex)
ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(u256(0x1234abcd1234)));
}
+BOOST_AUTO_TEST_CASE(flipping_sign_tests)
+{
+ char const* sourceCode = R"(
+ contract test {
+ function f() public returns (bool){
+ int x = -2**255;
+ assert(-x == x);
+ return true;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(true));
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libyul/YulOptimizerTest.cpp b/test/libyul/YulOptimizerTest.cpp
index f4ea6d67..6782f412 100644
--- a/test/libyul/YulOptimizerTest.cpp
+++ b/test/libyul/YulOptimizerTest.cpp
@@ -132,7 +132,7 @@ bool YulOptimizerTest::run(ostream& _stream, string const& _linePrefix, bool con
(FunctionGrouper{})(*m_ast);
NameDispenser nameDispenser(*m_ast);
ExpressionSplitter{nameDispenser}(*m_ast);
- FullInliner(*m_ast).run();
+ FullInliner(*m_ast, nameDispenser).run();
ExpressionJoiner::run(*m_ast);
}
else if (m_optimizerStep == "mainFunction")
diff --git a/test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul b/test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul
new file mode 100644
index 00000000..2df4f9d0
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/blockFlattener/switch_stmt.yul
@@ -0,0 +1,22 @@
+{
+ let a := 1
+ switch calldataload(0)
+ case 0 { { { mstore(0, 1) } } a := 8 }
+ default { a := 3 { a := 4 } }
+ a := 5
+}
+// ----
+// blockFlattener
+// {
+// let a := 1
+// switch calldataload(0)
+// case 0 {
+// mstore(0, 1)
+// a := 8
+// }
+// default {
+// a := 3
+// a := 4
+// }
+// a := 5
+// }
diff --git a/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul
new file mode 100644
index 00000000..49b4c916
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/commonSubexpressionEliminator/scopes.yul
@@ -0,0 +1,25 @@
+{
+ let a := 10
+ let x := 20
+ {
+ let b := calldataload(0)
+ let d := calldataload(1)
+ x := d
+ }
+ // We had a bug where "calldataload(0)" was incorrectly replaced by "b"
+ mstore(0, calldataload(0))
+ mstore(0, x)
+}
+// ----
+// commonSubexpressionEliminator
+// {
+// let a := 10
+// let x := 20
+// {
+// let b := calldataload(0)
+// let d := calldataload(1)
+// x := d
+// }
+// mstore(0, calldataload(0))
+// mstore(0, x)
+// }
diff --git a/test/libyul/yulOptimizerTests/fullSimplify/operations.yul b/test/libyul/yulOptimizerTests/fullSimplify/operations.yul
new file mode 100644
index 00000000..25467b62
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/fullSimplify/operations.yul
@@ -0,0 +1,44 @@
+{
+ let x := mload(0)
+ mstore(1, mul(x, 0))
+ mstore(2, div(x, 0))
+ mstore(3, div(0, x))
+ mstore(4, sdiv(x, 0))
+ mstore(5, sdiv(0, x))
+ mstore(6, and(0, x))
+ mstore(7, and(x, 0))
+ mstore(8, mod(0, x))
+ mstore(9, mod(x, 0))
+ mstore(10, lt(x, x))
+ mstore(11, gt(x, x))
+ mstore(12, slt(x, x))
+ mstore(13, sgt(x, x))
+ mstore(14, mod(x, x))
+ mstore(15, and(x, not(x)))
+ mstore(16, and(not(x), x))
+ mstore(17, or(x, not(x)))
+ mstore(18, or(not(x), x))
+}
+// ----
+// fullSimplify
+// {
+// pop(mload(0))
+// mstore(1, 0)
+// mstore(2, 0)
+// mstore(3, 0)
+// mstore(4, 0)
+// mstore(5, 0)
+// mstore(6, 0)
+// mstore(7, 0)
+// mstore(8, 0)
+// mstore(9, 0)
+// mstore(10, 0)
+// mstore(11, 0)
+// mstore(12, 0)
+// mstore(13, 0)
+// mstore(14, 0)
+// mstore(15, 0)
+// mstore(16, 0)
+// mstore(17, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
+// mstore(18, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
+// }
diff --git a/test/libyul/yulOptimizerTests/fullSimplify/signextend.yul b/test/libyul/yulOptimizerTests/fullSimplify/signextend.yul
new file mode 100644
index 00000000..714eb860
--- /dev/null
+++ b/test/libyul/yulOptimizerTests/fullSimplify/signextend.yul
@@ -0,0 +1,12 @@
+{
+ let x := 7
+ mstore(0, signextend(50, x))
+ let y := 255
+ mstore(1, signextend(0, y))
+}
+// ----
+// fullSimplify
+// {
+// mstore(0, 7)
+// mstore(1, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff)
+// }