From 9e708a98fdf80f79dfcff1d6e8f88d37499364c3 Mon Sep 17 00:00:00 2001 From: chriseth Date: Thu, 10 Feb 2022 19:16:10 +0100 Subject: [PATCH] User-defined literal suffixes: Code generation --- libsolidity/codegen/ExpressionCompiler.cpp | 43 +++++++++++- .../codegen/ir/IRGeneratorForStatements.cpp | 66 +++++++++++++++++-- 2 files changed, 99 insertions(+), 10 deletions(-) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 1c02b281c..84d7665c4 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -697,11 +697,48 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) // Callee removes them and pushes return values evmasm::AssemblyItem returnLabel = m_context.pushNewTag(); - for (unsigned i = 0; i < arguments.size(); ++i) - acceptAndConvert(*arguments[i], *function.parameterTypes()[i]); + + if (!_functionCall.isSuffixCall()) + { + for (unsigned i = 0; i < arguments.size(); ++i) + acceptAndConvert(*arguments[i], *parameterTypes[i]); + } + else + { + solAssert(arguments.size() == 1); + solAssert(arguments[0]); + auto const* literal = dynamic_cast(arguments[0].get()); + Type const& literalType = *literal->annotation().type; + solAssert(literal); + solAssert(literal->annotation().type); + + if (parameterTypes.size() == 1) + { + 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. + m_context << literalType.literalValue(literal); + utils().convertType(literalType, *parameterTypes.at(0)); + } + else + { + solAssert(parameterTypes.size() == 2); + + auto const* rationalNumberType = dynamic_cast(&literalType); + solAssert(rationalNumberType); + + auto&& [mantissa, exponent] = rationalNumberType->fractionalDecomposition(); + solAssert(mantissa && exponent); + m_context << mantissa->literalValue(nullptr); + utils().convertType(*mantissa, *parameterTypes.at(0)); + m_context << exponent->literalValue(nullptr); + utils().convertType(*exponent, *parameterTypes.at(1)); + } + } + _functionCall.expression().accept(*this); - unsigned parameterSize = CompilerUtils::sizeOnStack(function.parameterTypes()); + unsigned parameterSize = CompilerUtils::sizeOnStack(parameterTypes); if (function.hasBoundFirstArgument()) { // stack: arg2, ..., argn, label, arg1 diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index a9eb0bf15..59a063b9d 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -997,16 +997,64 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) break; case FunctionType::Kind::Internal: { - FunctionDefinition const* functionDef = ASTNode::resolveFunctionCall(_functionCall, &m_context.mostDerivedContract()); - + solAssert(functionType); solAssert(!functionType->takesArbitraryParameters()); vector args; - if (functionType->hasBoundFirstArgument()) - args += IRVariable(_functionCall.expression()).part("self").stackSlots(); + FunctionDefinition const* functionDef = nullptr; - for (size_t i = 0; i < arguments.size(); ++i) - args += convert(*arguments[i], *parameterTypes[i]).stackSlots(); + if (!_functionCall.isSuffixCall()) + { + functionDef = ASTNode::resolveFunctionCall(_functionCall, &m_context.mostDerivedContract()); + + if (functionType->hasBoundFirstArgument()) + args += IRVariable(_functionCall.expression()).part("self").stackSlots(); + + for (size_t i = 0; i < arguments.size(); ++i) + args += convert(*arguments[i], *parameterTypes[i]).stackSlots(); + } + else + { + functionDef = dynamic_cast(&functionType->declaration()); + solAssert(functionDef); + solAssert(!functionDef->virtualSemantics()); + solAssert(!functionType->hasBoundFirstArgument()); + + solAssert(arguments.size() == 1); + solAssert(arguments[0]); + auto const* literal = dynamic_cast(arguments[0].get()); + Type const& literalType = *literal->annotation().type; + solAssert(literal); + solAssert(literal->annotation().type); + + if (parameterTypes.size() == 2) + { + auto const* literalRationalType = dynamic_cast(&literalType); + solAssert(literalRationalType); + + auto&& [mantissa, exponent] = literalRationalType->fractionalDecomposition(); + solAssert(mantissa && exponent); + + IRVariable mantissaVar(m_context.newYulVariable(), *mantissa); + define(mantissaVar) << toCompactHexWithPrefix(mantissa->literalValue(literal)) << "\n"; + args += convert(mantissaVar, *parameterTypes[0]).stackSlots(); + + IRVariable exponentVar(m_context.newYulVariable(), *exponent); + define(exponentVar) << toCompactHexWithPrefix(exponent->literalValue(literal)) << "\n"; + args += 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"; + args += convert(value, *parameterTypes[0]).stackSlots(); + } + } if (functionDef) { @@ -2500,10 +2548,14 @@ bool IRGeneratorForStatements::visit(Literal const& _literal) define(_literal) << toCompactHexWithPrefix(literalType.literalValue(&_literal)) << "\n"; break; case Type::Category::StringLiteral: - break; // will be done during conversion + // 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; }