mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge remote-tracking branch 'origin/develop' into breaking
This commit is contained in:
@@ -17,7 +17,7 @@
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
#include <libsolidity/analysis/ControlFlowBuilder.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
|
||||
using namespace solidity;
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include <libsolidity/interface/Version.h>
|
||||
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/AST.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/SemVerHandler.h>
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/AST.h>
|
||||
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
|
||||
@@ -2318,11 +2318,19 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
|
||||
functionType = dynamic_cast<FunctionType const*>(expressionType);
|
||||
funcCallAnno.kind = FunctionCallKind::FunctionCall;
|
||||
|
||||
if (auto memberAccess = dynamic_cast<MemberAccess const*>(&_functionCall.expression()))
|
||||
{
|
||||
if (dynamic_cast<FunctionDefinition const*>(memberAccess->annotation().referencedDeclaration))
|
||||
_functionCall.expression().annotation().calledDirectly = true;
|
||||
}
|
||||
else if (auto identifier = dynamic_cast<Identifier const*>(&_functionCall.expression()))
|
||||
if (dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration))
|
||||
_functionCall.expression().annotation().calledDirectly = true;
|
||||
|
||||
// Purity for function calls also depends upon the callee and its FunctionType
|
||||
funcCallAnno.isPure =
|
||||
argumentsArePure &&
|
||||
*_functionCall.expression().annotation().isPure &&
|
||||
functionType &&
|
||||
functionType->isPure();
|
||||
|
||||
if (
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
#include <libsolidity/analysis/ViewPureChecker.h>
|
||||
#include <libsolidity/ast/ExperimentalFeatures.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <libevmasm/SemanticInformation.h>
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
|
||||
namespace solidity::yul
|
||||
{
|
||||
// Forward-declaration to <yul/AsmData.h>
|
||||
// Forward-declaration to <yul/AST.h>
|
||||
struct Block;
|
||||
struct Dialect;
|
||||
}
|
||||
|
||||
@@ -262,6 +262,15 @@ struct ExpressionAnnotation: ASTAnnotation
|
||||
/// Types and - if given - names of arguments if the expr. is a function
|
||||
/// that is called, used for overload resolution
|
||||
std::optional<FuncCallArguments> arguments;
|
||||
|
||||
/// True if the expression consists solely of the name of the function and the function is called immediately
|
||||
/// instead of being stored or processed. The name may be qualified with the name of a contract, library
|
||||
/// module, etc., that clarifies the scope. For example: `m.L.f()`, where `m` is a module, `L` is a library
|
||||
/// and `f` is a function is a direct call. This means that the function to be called is known at compilation
|
||||
/// time and it's not necessary to rely on any runtime dispatch mechanism to resolve it.
|
||||
/// Note that even the simplest expressions, like `(f)()`, result in an indirect call even if they consist of
|
||||
/// values known at compilation time.
|
||||
bool calledDirectly = false;
|
||||
};
|
||||
|
||||
struct IdentifierAnnotation: ExpressionAnnotation
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
#include <libsolidity/ast/TypeProvider.h>
|
||||
|
||||
#include <libyul/AsmJsonConverter.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/AsmPrinter.h>
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
|
||||
#include <libsolutil/JSON.h>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <libyul/AsmJsonImporter.h>
|
||||
#include <libyul/AsmParser.h>
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/Dialect.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include <libyul/AsmPrinter.h>
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/backends/evm/AsmCodeGen.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
#include <libyul/backends/evm/EVMMetrics.h>
|
||||
|
||||
@@ -531,10 +531,15 @@ void CompilerUtils::encodeToMemory(
|
||||
if (_givenTypes[i]->category() == Type::Category::StringLiteral)
|
||||
{
|
||||
auto const& strType = dynamic_cast<StringLiteralType const&>(*_givenTypes[i]);
|
||||
m_context << u256(strType.value().size());
|
||||
auto const size = strType.value().size();
|
||||
m_context << u256(size);
|
||||
storeInMemoryDynamic(*TypeProvider::uint256(), true);
|
||||
// stack: ... <end_of_mem'>
|
||||
storeInMemoryDynamic(strType, _padToWordBoundaries);
|
||||
|
||||
// Do not output empty padding for zero-length strings.
|
||||
// TODO: handle this in storeInMemoryDynamic
|
||||
if (size != 0)
|
||||
storeInMemoryDynamic(strType, _padToWordBoundaries);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -104,12 +104,13 @@ public:
|
||||
/// Stores a 256 bit integer from stack in memory.
|
||||
/// @param _offset offset in memory
|
||||
void storeInMemory(unsigned _offset);
|
||||
|
||||
/// Dynamic version of @see storeInMemory, expects the memory offset below the value on the stack
|
||||
/// and also updates that. For reference types, only copies the data pointer. Fails for
|
||||
/// non-memory-references.
|
||||
/// non-memory-references. For string literals no value is available on the stack.
|
||||
/// @param _padToWords if true, adds zeros to pad to multiple of 32 bytes. Array elements
|
||||
/// are always padded (except for byte arrays), regardless of this parameter.
|
||||
/// @param _cleanup if true, adds code to cleanup the value before storing it.
|
||||
/// are always padded (except for byte arrays), regardless of this parameter.
|
||||
/// Stack pre: memory_offset value...
|
||||
/// Stack post: (memory_offset+length)
|
||||
void storeInMemoryDynamic(Type const& _type, bool _padToWords = true, bool _cleanup = true);
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
#include <libyul/AsmAnalysisInfo.h>
|
||||
#include <libyul/AsmAnalysis.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/backends/evm/AsmCodeGen.h>
|
||||
#include <libyul/backends/evm/EVMMetrics.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
|
||||
@@ -128,7 +128,7 @@ void IRGenerationContext::initializeInternalDispatch(InternalDispatchMap _intern
|
||||
{
|
||||
solAssert(internalDispatchClean(), "");
|
||||
|
||||
for (set<FunctionDefinition const*> const& functions: _internalDispatch | boost::adaptors::map_values)
|
||||
for (DispatchSet const& functions: _internalDispatch | boost::adaptors::map_values)
|
||||
for (auto function: functions)
|
||||
enqueueFunctionForCodeGeneration(*function);
|
||||
|
||||
@@ -137,38 +137,26 @@ void IRGenerationContext::initializeInternalDispatch(InternalDispatchMap _intern
|
||||
|
||||
InternalDispatchMap IRGenerationContext::consumeInternalDispatchMap()
|
||||
{
|
||||
m_directInternalFunctionCalls.clear();
|
||||
|
||||
InternalDispatchMap internalDispatch = move(m_internalDispatchMap);
|
||||
m_internalDispatchMap.clear();
|
||||
return internalDispatch;
|
||||
}
|
||||
|
||||
void IRGenerationContext::internalFunctionCalledDirectly(Expression const& _expression)
|
||||
void IRGenerationContext::addToInternalDispatch(FunctionDefinition const& _function)
|
||||
{
|
||||
solAssert(m_directInternalFunctionCalls.count(&_expression) == 0, "");
|
||||
FunctionType const* functionType = TypeProvider::function(_function, FunctionType::Kind::Internal);
|
||||
solAssert(functionType, "");
|
||||
|
||||
m_directInternalFunctionCalls.insert(&_expression);
|
||||
YulArity arity = YulArity::fromType(*functionType);
|
||||
|
||||
if (m_internalDispatchMap.count(arity) != 0 && m_internalDispatchMap[arity].count(&_function) != 0)
|
||||
// Note that m_internalDispatchMap[arity] is a set with a custom comparator, which looks at function IDs not definitions
|
||||
solAssert(*m_internalDispatchMap[arity].find(&_function) == &_function, "Different definitions with the same function ID");
|
||||
|
||||
m_internalDispatchMap[arity].insert(&_function);
|
||||
enqueueFunctionForCodeGeneration(_function);
|
||||
}
|
||||
|
||||
void IRGenerationContext::internalFunctionAccessed(Expression const& _expression, FunctionDefinition const& _function)
|
||||
{
|
||||
solAssert(
|
||||
IRHelpers::referencedFunctionDeclaration(_expression) &&
|
||||
_function.resolveVirtual(mostDerivedContract()) ==
|
||||
IRHelpers::referencedFunctionDeclaration(_expression)->resolveVirtual(mostDerivedContract()),
|
||||
"Function definition does not match the expression"
|
||||
);
|
||||
|
||||
if (m_directInternalFunctionCalls.count(&_expression) == 0)
|
||||
{
|
||||
FunctionType const* functionType = TypeProvider::function(_function, FunctionType::Kind::Internal);
|
||||
solAssert(functionType, "");
|
||||
|
||||
m_internalDispatchMap[YulArity::fromType(*functionType)].insert(&_function);
|
||||
enqueueFunctionForCodeGeneration(_function);
|
||||
}
|
||||
}
|
||||
|
||||
void IRGenerationContext::internalFunctionCalledThroughDispatch(YulArity const& _arity)
|
||||
{
|
||||
|
||||
@@ -44,7 +44,20 @@ namespace solidity::frontend
|
||||
class YulUtilFunctions;
|
||||
class ABIFunctions;
|
||||
|
||||
using InternalDispatchMap = std::map<YulArity, std::set<FunctionDefinition const*>>;
|
||||
struct AscendingFunctionIDCompare
|
||||
{
|
||||
bool operator()(FunctionDefinition const* _f1, FunctionDefinition const* _f2) const
|
||||
{
|
||||
// NULLs always first.
|
||||
if (_f1 != nullptr && _f2 != nullptr)
|
||||
return _f1->id() < _f2->id();
|
||||
else
|
||||
return _f1 == nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
using DispatchSet = std::set<FunctionDefinition const*, AscendingFunctionIDCompare>;
|
||||
using InternalDispatchMap = std::map<YulArity, DispatchSet>;
|
||||
|
||||
/**
|
||||
* Class that contains contextual information during IR generation.
|
||||
@@ -108,7 +121,7 @@ public:
|
||||
|
||||
void initializeInternalDispatch(InternalDispatchMap _internalDispatchMap);
|
||||
InternalDispatchMap consumeInternalDispatchMap();
|
||||
bool internalDispatchClean() const { return m_internalDispatchMap.empty() && m_directInternalFunctionCalls.empty(); }
|
||||
bool internalDispatchClean() const { return m_internalDispatchMap.empty(); }
|
||||
|
||||
/// Notifies the context that a function call that needs to go through internal dispatch was
|
||||
/// encountered while visiting the AST. This ensures that the corresponding dispatch function
|
||||
@@ -116,16 +129,8 @@ public:
|
||||
/// the code contains a call to an uninitialized function variable).
|
||||
void internalFunctionCalledThroughDispatch(YulArity const& _arity);
|
||||
|
||||
/// Notifies the context that a direct function call (i.e. not through internal dispatch) was
|
||||
/// encountered while visiting the AST. This lets the context know that the function should
|
||||
/// not be added to the dispatch (unless there are also indirect calls to it elsewhere else).
|
||||
void internalFunctionCalledDirectly(Expression const& _expression);
|
||||
|
||||
/// Notifies the context that a name representing an internal function has been found while
|
||||
/// visiting the AST. If the name has not been reported as a direct call using
|
||||
/// @a internalFunctionCalledDirectly(), it's assumed to represent function variable access
|
||||
/// and the function gets added to internal dispatch.
|
||||
void internalFunctionAccessed(Expression const& _expression, FunctionDefinition const& _function);
|
||||
/// Adds a function to the internal dispatch.
|
||||
void addToInternalDispatch(FunctionDefinition const& _function);
|
||||
|
||||
/// @returns a new copy of the utility function generator (but using the same function set).
|
||||
YulUtilFunctions utils();
|
||||
@@ -177,14 +182,13 @@ private:
|
||||
/// The order and duplicates are irrelevant here (hence std::set rather than std::queue) as
|
||||
/// long as the order of Yul functions in the generated code is deterministic and the same on
|
||||
/// all platforms - which is a property guaranteed by MultiUseYulFunctionCollector.
|
||||
std::set<FunctionDefinition const*> m_functionGenerationQueue;
|
||||
DispatchSet m_functionGenerationQueue;
|
||||
|
||||
/// Collection of functions that need to be callable via internal dispatch.
|
||||
/// Note that having a key with an empty set of functions is a valid situation. It means that
|
||||
/// the code contains a call via a pointer even though a specific function is never assigned to it.
|
||||
/// It will fail at runtime but the code must still compile.
|
||||
InternalDispatchMap m_internalDispatchMap;
|
||||
std::set<Expression const*> m_directInternalFunctionCalls;
|
||||
|
||||
std::set<ContractDefinition const*, ASTNode::CompareByID> m_subObjects;
|
||||
};
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
#include <libevmasm/GasMeter.h>
|
||||
|
||||
#include <libyul/AsmPrinter.h>
|
||||
#include <libyul/AsmData.h>
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/Dialect.h>
|
||||
#include <libyul/optimiser/ASTCopier.h>
|
||||
|
||||
@@ -829,20 +829,6 @@ bool IRGeneratorForStatements::visit(BinaryOperation const& _binOp)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IRGeneratorForStatements::visit(FunctionCall const& _functionCall)
|
||||
{
|
||||
setLocation(_functionCall);
|
||||
FunctionTypePointer functionType = dynamic_cast<FunctionType const*>(&type(_functionCall.expression()));
|
||||
if (
|
||||
functionType &&
|
||||
functionType->kind() == FunctionType::Kind::Internal &&
|
||||
IRHelpers::referencedFunctionDeclaration(_functionCall.expression())
|
||||
)
|
||||
m_context.internalFunctionCalledDirectly(_functionCall.expression());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
||||
{
|
||||
setLocation(_functionCall);
|
||||
@@ -1558,6 +1544,7 @@ void IRGeneratorForStatements::endVisit(FunctionCallOptions const& _options)
|
||||
void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
setLocation(_memberAccess);
|
||||
|
||||
ASTString const& member = _memberAccess.memberName();
|
||||
auto memberFunctionType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type);
|
||||
Type::Category objectCategory = _memberAccess.expression().annotation().type->category();
|
||||
@@ -1583,7 +1570,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
if (memberFunctionType->kind() == FunctionType::Kind::Internal)
|
||||
{
|
||||
define(IRVariable(_memberAccess).part("functionIdentifier")) << to_string(functionDefinition.id()) << "\n";
|
||||
m_context.internalFunctionAccessed(_memberAccess, functionDefinition);
|
||||
if (!_memberAccess.annotation().calledDirectly)
|
||||
m_context.addToInternalDispatch(functionDefinition);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1875,7 +1863,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
define(_memberAccess) << to_string(resolvedFunctionDef.id()) << "\n";
|
||||
solAssert(resolvedFunctionDef.functionType(true), "");
|
||||
solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal, "");
|
||||
m_context.internalFunctionAccessed(_memberAccess, resolvedFunctionDef);
|
||||
if (!_memberAccess.annotation().calledDirectly)
|
||||
m_context.addToInternalDispatch(resolvedFunctionDef);
|
||||
}
|
||||
else if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
|
||||
handleVariableReference(*variable, _memberAccess);
|
||||
@@ -1889,7 +1878,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration))
|
||||
{
|
||||
define(_memberAccess) << to_string(function->id()) << "\n";
|
||||
m_context.internalFunctionAccessed(_memberAccess, *function);
|
||||
if (!_memberAccess.annotation().calledDirectly)
|
||||
m_context.addToInternalDispatch(*function);
|
||||
}
|
||||
else
|
||||
solAssert(false, "Function not found in member access");
|
||||
@@ -1964,7 +1954,9 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
|
||||
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, "");
|
||||
|
||||
define(_memberAccess) << to_string(function->id()) << "\n";
|
||||
m_context.internalFunctionAccessed(_memberAccess, *function);
|
||||
|
||||
if (!_memberAccess.annotation().calledDirectly)
|
||||
m_context.addToInternalDispatch(*function);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2208,7 +2200,8 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
||||
|
||||
solAssert(resolvedFunctionDef.functionType(true), "");
|
||||
solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal, "");
|
||||
m_context.internalFunctionAccessed(_identifier, resolvedFunctionDef);
|
||||
if (!_identifier.annotation().calledDirectly)
|
||||
m_context.addToInternalDispatch(resolvedFunctionDef);
|
||||
}
|
||||
else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||
handleVariableReference(*varDecl, _identifier);
|
||||
|
||||
@@ -76,7 +76,6 @@ public:
|
||||
void endVisit(Return const& _return) override;
|
||||
void endVisit(UnaryOperation const& _unaryOperation) override;
|
||||
bool visit(BinaryOperation const& _binOp) override;
|
||||
bool visit(FunctionCall const& _funCall) override;
|
||||
void endVisit(FunctionCall const& _funCall) override;
|
||||
void endVisit(FunctionCallOptions const& _funCallOptions) override;
|
||||
void endVisit(MemberAccess const& _memberAccess) override;
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
#include <libyul/AsmJsonConverter.h>
|
||||
#include <libyul/AssemblyStack.h>
|
||||
#include <libyul/AsmParser.h>
|
||||
#include <libyul/AST.h>
|
||||
|
||||
#include <liblangutil/Scanner.h>
|
||||
#include <liblangutil/SemVerHandler.h>
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include <libsolidity/interface/Version.h>
|
||||
#include <libyul/AsmParser.h>
|
||||
#include <libyul/AST.h>
|
||||
#include <libyul/backends/evm/EVMDialect.h>
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <liblangutil/Scanner.h>
|
||||
|
||||
Reference in New Issue
Block a user