mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
User-defined literal suffixes: Code generation
This commit is contained in:
parent
143722c705
commit
b84f1b586e
@ -701,7 +701,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
|||||||
appendInternalFunctionCall(
|
appendInternalFunctionCall(
|
||||||
function,
|
function,
|
||||||
_functionCall.expression(),
|
_functionCall.expression(),
|
||||||
arguments | ranges::views::indirect | ranges::views::addressof | ranges::to<vector>()
|
arguments
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2310,23 +2310,47 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExpressionCompiler::endVisit(Literal const& _literal)
|
bool ExpressionCompiler::visit(Literal const& _literal)
|
||||||
{
|
{
|
||||||
CompilerContext::LocationSetter locationSetter(m_context, _literal);
|
CompilerContext::LocationSetter locationSetter(m_context, _literal);
|
||||||
Type const* type = _literal.annotation().type;
|
|
||||||
|
|
||||||
switch (type->category())
|
if (_literal.suffixFunction())
|
||||||
{
|
{
|
||||||
case Type::Category::RationalNumber:
|
FunctionType const& functionType = *_literal.suffixFunction()->functionType(true);
|
||||||
case Type::Category::Bool:
|
|
||||||
case Type::Category::Address:
|
Expression const* callExpression = std::visit(GenericVisitor{
|
||||||
m_context << type->literalValue(&_literal);
|
[&](ASTPointer<Identifier> const& _identifier) -> Expression const* { return _identifier.get(); },
|
||||||
break;
|
[&](ASTPointer<MemberAccess> const& _memberAccess) -> Expression const* { return _memberAccess.get(); },
|
||||||
case Type::Category::StringLiteral:
|
[&](Literal::SubDenomination) -> Expression const* { return nullptr; },
|
||||||
break; // will be done during conversion
|
}, _literal.suffix());
|
||||||
default:
|
solAssert(callExpression);
|
||||||
solUnimplemented("Only integer, boolean and string literals implemented for now.");
|
|
||||||
|
appendInternalFunctionCall(functionType, *callExpression, _literal);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(_literal.hasSubDenomination() || !_literal.isSuffixed());
|
||||||
|
|
||||||
|
Type const* type = _literal.annotation().type;
|
||||||
|
|
||||||
|
switch (type->category())
|
||||||
|
{
|
||||||
|
case Type::Category::RationalNumber:
|
||||||
|
case Type::Category::Bool:
|
||||||
|
case Type::Category::Address:
|
||||||
|
m_context << type->literalValue(&_literal);
|
||||||
|
break;
|
||||||
|
case Type::Category::StringLiteral:
|
||||||
|
break; // will be done during conversion
|
||||||
|
default:
|
||||||
|
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)
|
void ExpressionCompiler::appendAndOrOperatorCode(BinaryOperation const& _binaryOperation)
|
||||||
@ -2597,7 +2621,10 @@ void ExpressionCompiler::appendExpOperatorCode(Type const& _valueType, Type cons
|
|||||||
void ExpressionCompiler::appendInternalFunctionCall(
|
void ExpressionCompiler::appendInternalFunctionCall(
|
||||||
FunctionType const& _functionType,
|
FunctionType const& _functionType,
|
||||||
Expression const& _callExpression,
|
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);
|
solAssert(_functionType.kind() == FunctionType::Kind::Internal);
|
||||||
@ -2605,12 +2632,49 @@ void ExpressionCompiler::appendInternalFunctionCall(
|
|||||||
// Calling convention: Caller pushes return address and arguments
|
// Calling convention: Caller pushes return address and arguments
|
||||||
// Callee removes them and pushes return values
|
// Callee removes them and pushes return values
|
||||||
|
|
||||||
|
TypePointers parameterTypes = _functionType.parameterTypes();
|
||||||
|
|
||||||
evmasm::AssemblyItem returnLabel = m_context.pushNewTag();
|
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);
|
||||||
|
|
||||||
_callExpression.accept(*this);
|
_callExpression.accept(*this);
|
||||||
|
|
||||||
unsigned parameterSize = CompilerUtils::sizeOnStack(_functionType.parameterTypes());
|
unsigned parameterSize = CompilerUtils::sizeOnStack(parameterTypes);
|
||||||
if (_functionType.hasBoundFirstArgument())
|
if (_functionType.hasBoundFirstArgument())
|
||||||
{
|
{
|
||||||
// stack: arg2, ..., argn, label, arg1
|
// stack: arg2, ..., argn, label, arg1
|
||||||
|
@ -90,7 +90,7 @@ private:
|
|||||||
bool visit(IndexAccess const& _indexAccess) override;
|
bool visit(IndexAccess const& _indexAccess) override;
|
||||||
bool visit(IndexRangeAccess const& _indexAccess) override;
|
bool visit(IndexRangeAccess const& _indexAccess) override;
|
||||||
void endVisit(Identifier const& _identifier) 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
|
///@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.
|
/// 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(
|
void appendInternalFunctionCall(
|
||||||
FunctionType const& _functionType,
|
FunctionType const& _functionType,
|
||||||
Expression const& _callExpression,
|
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.
|
/// 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,
|
/// @param _tryCall if true, this is the external call of a try statement. In that case,
|
||||||
|
@ -1000,11 +1000,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
|
|||||||
break;
|
break;
|
||||||
case FunctionType::Kind::Internal:
|
case FunctionType::Kind::Internal:
|
||||||
solAssert(functionType);
|
solAssert(functionType);
|
||||||
appendInternalFunctionCall(
|
appendInternalFunctionCall(_functionCall, *functionType, arguments);
|
||||||
_functionCall,
|
|
||||||
*functionType,
|
|
||||||
arguments | ranges::views::indirect | ranges::views::addressof | ranges::to<vector>
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
case FunctionType::Kind::External:
|
case FunctionType::Kind::External:
|
||||||
case FunctionType::Kind::DelegateCall:
|
case FunctionType::Kind::DelegateCall:
|
||||||
@ -2464,19 +2460,33 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
|
|||||||
bool IRGeneratorForStatements::visit(Literal const& _literal)
|
bool IRGeneratorForStatements::visit(Literal const& _literal)
|
||||||
{
|
{
|
||||||
setLocation(_literal);
|
setLocation(_literal);
|
||||||
Type const& literalType = type(_literal);
|
|
||||||
|
|
||||||
switch (literalType.category())
|
if (_literal.suffixFunction())
|
||||||
|
appendInternalFunctionCall(
|
||||||
|
_literal,
|
||||||
|
*_literal.suffixFunction()->functionType(true /* _internal */),
|
||||||
|
_literal
|
||||||
|
);
|
||||||
|
else
|
||||||
{
|
{
|
||||||
case Type::Category::RationalNumber:
|
solAssert(_literal.hasSubDenomination() || !_literal.isSuffixed());
|
||||||
case Type::Category::Bool:
|
|
||||||
case Type::Category::Address:
|
Type const& expressionType = type(_literal);
|
||||||
define(_literal) << toCompactHexWithPrefix(literalType.literalValue(&_literal)) << "\n";
|
switch (expressionType.category())
|
||||||
break;
|
{
|
||||||
case Type::Category::StringLiteral:
|
case Type::Category::RationalNumber:
|
||||||
break; // will be done during conversion
|
case Type::Category::Bool:
|
||||||
default:
|
case Type::Category::Address:
|
||||||
solUnimplemented("Only integer, boolean and string literals implemented for now.");
|
define(_literal) << toCompactHexWithPrefix(expressionType.literalValue(&_literal)) << "\n";
|
||||||
|
break;
|
||||||
|
case Type::Category::StringLiteral:
|
||||||
|
// A string literal cannot be simply assigned to a Yul variable so we don't create one here.
|
||||||
|
// Instead any expression that uses it has to generate custom conversion code that
|
||||||
|
// depends on where the string ultimately ends up (storage, memory, ABI encoded data, etc.).
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
solUnimplemented("Only integer, boolean and string literals implemented for now.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -2511,23 +2521,75 @@ void IRGeneratorForStatements::handleVariableReference(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void IRGeneratorForStatements::appendInternalFunctionCall(
|
void IRGeneratorForStatements::appendInternalFunctionCall(
|
||||||
FunctionCall const& _functionCall,
|
Expression const& _callExpression,
|
||||||
FunctionType const& _functionType,
|
FunctionType const& _functionType,
|
||||||
vector<Expression const*> const& _arguments
|
variant<
|
||||||
|
reference_wrapper<vector<ASTPointer<Expression const>> const>,
|
||||||
|
reference_wrapper<Literal const>
|
||||||
|
> _arguments
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
solAssert(!_functionType.takesArbitraryParameters());
|
solAssert(!_functionType.takesArbitraryParameters());
|
||||||
|
|
||||||
FunctionDefinition const *functionDefinition = ASTNode::resolveFunctionCall(_functionCall, &m_context.mostDerivedContract());
|
|
||||||
Expression const* identifierOrMemberAccess = &_functionCall.expression();
|
|
||||||
|
|
||||||
vector<string> convertedArguments;
|
vector<string> convertedArguments;
|
||||||
if (_functionType.hasBoundFirstArgument())
|
FunctionDefinition const* functionDefinition = nullptr;
|
||||||
convertedArguments += IRVariable(*identifierOrMemberAccess).part("self").stackSlots();
|
Expression const* identifierOrMemberAccess = nullptr;
|
||||||
|
std::visit(GenericVisitor{
|
||||||
|
[&](vector<ASTPointer<Expression const>> const& _argumentExpressions)
|
||||||
|
{
|
||||||
|
auto const* functionCall = dynamic_cast<FunctionCall const*>(&_callExpression);
|
||||||
|
solAssert(functionCall);
|
||||||
|
|
||||||
TypePointers parameterTypes = _functionType.parameterTypes();
|
functionDefinition = ASTNode::resolveFunctionCall(*functionCall, &m_context.mostDerivedContract());
|
||||||
for (size_t i = 0; i < _arguments.size(); ++i)
|
identifierOrMemberAccess = &functionCall->expression();
|
||||||
convertedArguments += convert(*_arguments[i], *parameterTypes[i]).stackSlots();
|
|
||||||
|
if (_functionType.hasBoundFirstArgument())
|
||||||
|
convertedArguments += IRVariable(*identifierOrMemberAccess).part("self").stackSlots();
|
||||||
|
|
||||||
|
TypePointers parameterTypes = _functionType.parameterTypes();
|
||||||
|
for (size_t i = 0; i < _argumentExpressions.size(); ++i)
|
||||||
|
convertedArguments += convert(*_argumentExpressions[i], *parameterTypes[i]).stackSlots();
|
||||||
|
},
|
||||||
|
[&](Literal const& _literal) {
|
||||||
|
auto const* literal = dynamic_cast<Literal const*>(&_callExpression);
|
||||||
|
solAssert(literal);
|
||||||
|
solAssert(literal->suffixFunction());
|
||||||
|
solAssert(!literal->suffixFunction()->virtualSemantics());
|
||||||
|
solAssert(!_functionType.hasBoundFirstArgument());
|
||||||
|
|
||||||
|
functionDefinition = literal->suffixFunction();
|
||||||
|
|
||||||
|
Type const& literalType = literal->typeOfValue();
|
||||||
|
auto const* literalRationalType = dynamic_cast<RationalNumberType const*>(&literalType);
|
||||||
|
vector<Type const*> parameterTypes = _functionType.parameterTypes();
|
||||||
|
if (parameterTypes.size() == 2)
|
||||||
|
{
|
||||||
|
solAssert(literalRationalType);
|
||||||
|
|
||||||
|
auto&& [mantissa, exponent] = literalRationalType->mantissaExponent();
|
||||||
|
solAssert(mantissa && exponent);
|
||||||
|
|
||||||
|
IRVariable mantissaVar(m_context.newYulVariable(), *mantissa);
|
||||||
|
define(mantissaVar) << toCompactHexWithPrefix(mantissa->literalValue(&_literal)) << "\n";
|
||||||
|
convertedArguments += convert(mantissaVar, *parameterTypes[0]).stackSlots();
|
||||||
|
|
||||||
|
IRVariable exponentVar(m_context.newYulVariable(), *exponent);
|
||||||
|
define(exponentVar) << toCompactHexWithPrefix(exponent->literalValue(&_literal)) << "\n";
|
||||||
|
convertedArguments += convert(exponentVar, *parameterTypes[1]).stackSlots();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
solAssert(parameterTypes.size() == 1);
|
||||||
|
|
||||||
|
IRVariable value(m_context.newYulVariable(), literalType);
|
||||||
|
if (literalType.category() != Type::Category::StringLiteral)
|
||||||
|
// NOTE: For string literals we do not need to define the variable. The variable
|
||||||
|
// value will be embedded inside the conversion function.
|
||||||
|
define(value) << toCompactHexWithPrefix(literalType.literalValue(&_literal)) << "\n";
|
||||||
|
convertedArguments += convert(value, *parameterTypes[0]).stackSlots();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, _arguments);
|
||||||
|
|
||||||
if (functionDefinition)
|
if (functionDefinition)
|
||||||
{
|
{
|
||||||
@ -2537,7 +2599,7 @@ void IRGeneratorForStatements::appendInternalFunctionCall(
|
|||||||
else
|
else
|
||||||
solAssert(_functionType == *functionDefinition->functionType(true /* _internal */));
|
solAssert(_functionType == *functionDefinition->functionType(true /* _internal */));
|
||||||
|
|
||||||
define(_functionCall) <<
|
define(_callExpression) <<
|
||||||
m_context.enqueueFunctionForCodeGeneration(*functionDefinition) <<
|
m_context.enqueueFunctionForCodeGeneration(*functionDefinition) <<
|
||||||
"(" <<
|
"(" <<
|
||||||
joinHumanReadable(convertedArguments) <<
|
joinHumanReadable(convertedArguments) <<
|
||||||
@ -2550,7 +2612,7 @@ void IRGeneratorForStatements::appendInternalFunctionCall(
|
|||||||
YulArity arity = YulArity::fromType(_functionType);
|
YulArity arity = YulArity::fromType(_functionType);
|
||||||
m_context.internalFunctionCalledThroughDispatch(arity);
|
m_context.internalFunctionCalledThroughDispatch(arity);
|
||||||
|
|
||||||
define(_functionCall) <<
|
define(_callExpression) <<
|
||||||
IRNames::internalDispatch(arity) <<
|
IRNames::internalDispatch(arity) <<
|
||||||
"(" <<
|
"(" <<
|
||||||
IRVariable(*identifierOrMemberAccess).part("functionIdentifier").name() <<
|
IRVariable(*identifierOrMemberAccess).part("functionIdentifier").name() <<
|
||||||
|
@ -155,9 +155,12 @@ private:
|
|||||||
/// Appends code to call an internal function with the given arguments.
|
/// Appends code to call an internal function with the given arguments.
|
||||||
/// All involved expressions have already been visited.
|
/// All involved expressions have already been visited.
|
||||||
void appendInternalFunctionCall(
|
void appendInternalFunctionCall(
|
||||||
FunctionCall const& _functionCall,
|
Expression const& _callExpression,
|
||||||
FunctionType const& _functionType,
|
FunctionType const& _functionType,
|
||||||
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 an external function with the given arguments.
|
/// Appends code to call an external function with the given arguments.
|
||||||
/// All involved expressions have already been visited.
|
/// All involved expressions have already been visited.
|
||||||
|
Loading…
Reference in New Issue
Block a user