[TMP] Rewrite suffix handling in ExpressionCompiler

This commit is contained in:
Kamil Śliwak 2022-07-28 16:13:08 +02:00
parent f114acad71
commit 6fe8ff2acf
2 changed files with 70 additions and 43 deletions

View File

@ -634,7 +634,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
appendInternalFunctionCall(
function,
_functionCall.expression(),
arguments | ranges::views::indirect | ranges::views::addressof | ranges::to<vector>()
arguments
);
break;
}
@ -2181,8 +2181,8 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
}
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
{
// If the identifier is called right away, this code is executed in visit(FunctionCall...), because
// we want to avoid having a reference to the runtime function entry point in the
// If the identifier is called right away, this code is executed in visit(FunctionCall),
// or visit(Literal) because we want to avoid having a reference to the runtime function entry point in the
// constructor context, since this would force the compiler to include unreferenced
// internal functions in the runtime context.
solAssert(*_identifier.annotation().requiredLookup == VirtualLookup::Virtual, "");
@ -2225,45 +2225,22 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
}
}
void ExpressionCompiler::endVisit(Literal const& _literal)
bool ExpressionCompiler::visit(Literal const& _literal)
{
CompilerContext::LocationSetter locationSetter(m_context, _literal);
if (_literal.suffixDefinition())
if (_literal.suffixFunction())
{
FunctionType const& functionType = *_literal.suffixDefinition()->functionType(true);
FunctionType const& functionType = *_literal.suffixFunction()->functionType(true);
Expression const* callExpression = std::visit(GenericVisitor{
[&](ASTPointer<Identifier> const& _identifier) -> Expression const* { return _identifier.get(); },
[&](ASTPointer<MemberAccess> const& _memberAccess) -> Expression const* { return _memberAccess.get(); },
[&](Literal::SubDenomination) -> Expression const* { return nullptr; },
}, _literal.suffix());
solAssert(callExpression);
evmasm::AssemblyItem returnLabel = m_context.pushNewTag();
// TODO this is actually not always the right one.
auto type = TypeProvider::forLiteral(_literal);
if (dynamic_cast<RationalNumberType const*>(type) && dynamic_cast<RationalNumberType const*>(type)->isFractional())
{
auto&& [mantissa, exponent] = dynamic_cast<RationalNumberType const*>(type)->mantissaExponent();
m_context << mantissa->literalValue(nullptr);
utils().convertType(*mantissa, *functionType.parameterTypes().at(0));
m_context << exponent->literalValue(nullptr);
utils().convertType(*exponent, *functionType.parameterTypes().at(1));
}
else
{
// TODO reuse the code below
if (type->category() != Type::Category::StringLiteral)
m_context << type->literalValue(&_literal);
utils().convertType(*type, *functionType.parameterTypes().at(0));
}
m_context << m_context.functionEntryLabel(*_literal.suffixDefinition()).pushTag();
m_context.appendJump(evmasm::AssemblyItem::JumpType::IntoFunction);
m_context << returnLabel;
// callee adds return parameters, but removes arguments and return label
m_context.adjustStackOffset(static_cast<int>(
CompilerUtils::sizeOnStack(functionType.returnParameterTypes()) -
CompilerUtils::sizeOnStack(functionType.parameterTypes()) -
1
));
appendInternalFunctionCall(functionType, *callExpression, _literal);
}
else
{
@ -2271,7 +2248,6 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
Type const* type = _literal.annotation().type;
switch (type->category())
{
case Type::Category::RationalNumber:
@ -2285,6 +2261,11 @@ void ExpressionCompiler::endVisit(Literal const& _literal)
solUnimplemented("Only integer, boolean and string literals implemented for now.");
}
}
// Do not visit the suffix just like we do not visit the expression of a normal function call
// that is being called right away. Returning true would run endVisit(Identifier) and push the
// return label again.
return false;
}
void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation)
@ -2555,7 +2536,10 @@ void ExpressionCompiler::appendExpOperatorCode(Type const& _valueType, Type cons
void ExpressionCompiler::appendInternalFunctionCall(
FunctionType const& _functionType,
Expression const& _callExpression,
vector<Expression const*> const& _arguments
variant<
reference_wrapper<vector<ASTPointer<Expression const>> const>,
reference_wrapper<Literal const>
> _arguments
)
{
solAssert(_functionType.kind() == FunctionType::Kind::Internal);
@ -2563,9 +2547,45 @@ void ExpressionCompiler::appendInternalFunctionCall(
// Calling convention: Caller pushes return address and arguments
// Callee removes them and pushes return values
TypePointers parameterTypes = _functionType.parameterTypes();
evmasm::AssemblyItem returnLabel = m_context.pushNewTag();
for (unsigned i = 0; i < _arguments.size(); ++i)
acceptAndConvert(*_arguments[i], *_functionType.parameterTypes()[i]);
std::visit(GenericVisitor{
[&](vector<ASTPointer<Expression const>> const& _argumentExpressions)
{
for (unsigned i = 0; i < _argumentExpressions.size(); ++i)
acceptAndConvert(*_argumentExpressions[i], *parameterTypes[i]);
},
[&](Literal const& _literal) {
solAssert(_literal.suffixFunction());
// TODO this is actually not always the right one.
Type const* literalType = TypeProvider::forLiteral(_literal);
if (parameterTypes.size() == 1)
{
// TODO: We still need to do something for strings, don't we?
// The original TODO said 'reuse the code below', back when this part was in endVisit(Literal)
if (literalType->category() != Type::Category::StringLiteral)
m_context << literalType->literalValue(&_literal);
utils().convertType(*literalType, *parameterTypes.at(0));
}
else
{
solAssert(parameterTypes.size() == 2);
auto const* rationalNumberType = dynamic_cast<RationalNumberType const*>(literalType);
assert(rationalNumberType);
auto&& [mantissa, exponent] = rationalNumberType->mantissaExponent();
m_context << mantissa->literalValue(nullptr);
utils().convertType(*mantissa, *parameterTypes.at(0));
m_context << exponent->literalValue(nullptr);
utils().convertType(*exponent, *parameterTypes.at(1));
}
},
}, _arguments);
bool shortcutTaken = false;
if (auto const* identifier = dynamic_cast<Identifier const*>(&_callExpression))
@ -2590,7 +2610,7 @@ void ExpressionCompiler::appendInternalFunctionCall(
if (!shortcutTaken)
_callExpression.accept(*this);
unsigned parameterSize = CompilerUtils::sizeOnStack(_functionType.parameterTypes());
unsigned parameterSize = CompilerUtils::sizeOnStack(parameterTypes);
if (_functionType.bound())
{
// stack: arg2, ..., argn, label, arg1

View File

@ -90,7 +90,7 @@ private:
bool visit(IndexAccess const& _indexAccess) override;
bool visit(IndexRangeAccess const& _indexAccess) override;
void endVisit(Identifier const& _identifier) override;
void endVisit(Literal const& _literal) override;
bool visit(Literal const& _literal) override;
///@{
///@name Append code for various operator types
@ -105,10 +105,17 @@ private:
/// @}
/// Appends code to call an internal function of the given type with the given arguments.
/// The function can handle function calls using the standard call syntax and as a literal suffix.
/// Note that both cases allow for a single literal argument but would handle it differently.
/// The suffix case would split it into mantissa/exponent if the function has two parameters while
/// the standard syntax case would not.
void appendInternalFunctionCall(
FunctionType const& _functionType,
Expression const& _callExpression,
std::vector<Expression const*> const& _arguments
std::variant<
std::reference_wrapper<std::vector<ASTPointer<Expression const>> const>,
std::reference_wrapper<Literal const>
> _arguments
);
/// Appends code to call a function of the given type with the given arguments.
/// @param _tryCall if true, this is the external call of a try statement. In that case,