From e66383994d7f14b3903524bebe323b60efa448e4 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 5 Jun 2015 17:32:13 +0200 Subject: [PATCH] Dynamic memory. --- libsolidity/AST.cpp | 3 -- libsolidity/Compiler.cpp | 51 +++++++++++++++++++----------- libsolidity/CompilerUtils.cpp | 19 ++++++++++- libsolidity/CompilerUtils.h | 12 ++++++- libsolidity/ExpressionCompiler.cpp | 37 +++++++++++++++------- 5 files changed, 87 insertions(+), 35 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index 4c7168afa..7fd1425e5 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/AST.cpp @@ -462,9 +462,6 @@ void FunctionDefinition::checkTypeRequirements() { if (!var->getType()->canLiveOutsideStorage()) BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage.")); - // todo delete when will be implemented arrays as parameter type in internal functions - if (getVisibility() == Visibility::Public && var->getType()->getCategory() == Type::Category::Array) - BOOST_THROW_EXCEPTION(var->createTypeError("Arrays only implemented for external functions.")); if (getVisibility() >= Visibility::Public && !(var->getType()->externalType())) BOOST_THROW_EXCEPTION(var->createTypeError("Internal type is not allowed for public and external functions.")); } diff --git a/libsolidity/Compiler.cpp b/libsolidity/Compiler.cpp index 0a75e55a9..0d7fbbfed 100644 --- a/libsolidity/Compiler.cpp +++ b/libsolidity/Compiler.cpp @@ -52,6 +52,7 @@ void Compiler::compileContract(ContractDefinition const& _contract, map const& _contracts) { m_context = CompilerContext(); // clear it just in case + CompilerUtils(m_context).initialiseFreeMemoryPointer(); initializeContext(_contract, _contracts); appendFunctionSelector(_contract); set functions = m_context.getFunctionsWithoutCode(); @@ -67,6 +68,7 @@ void Compiler::compileContract(ContractDefinition const& _contract, // Swap the runtime context with the creation-time context swap(m_context, m_runtimeContext); + CompilerUtils(m_context).initialiseFreeMemoryPointer(); initializeContext(_contract, _contracts); packIntoContractCreator(_contract, m_runtimeContext); if (m_optimize) @@ -233,31 +235,42 @@ void Compiler::appendCalldataUnpacker(TypePointers const& _typeParameters, bool m_context << u256(CompilerUtils::dataStartOffset); for (TypePointer const& type: _typeParameters) { - switch (type->getCategory()) + if (type->getCategory() == Type::Category::Array) { - case Type::Category::Array: - if (type->isDynamicallySized()) + auto const& arrayType = dynamic_cast(*type); + if (arrayType.location() == ReferenceType::Location::CallData) { - // put on stack: data_pointer length - CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory); - // stack: data_offset next_pointer - //@todo once we support nested arrays, this offset needs to be dynamic. - m_context << eth::Instruction::SWAP1 << u256(CompilerUtils::dataStartOffset); - m_context << eth::Instruction::ADD; - // stack: next_pointer data_pointer - // retrieve length - CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); - // stack: next_pointer length data_pointer - m_context << eth::Instruction::SWAP2; + if (type->isDynamicallySized()) + { + // put on stack: data_pointer length + CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory); + // stack: data_offset next_pointer + //@todo once we support nested arrays, this offset needs to be dynamic. + m_context << eth::Instruction::SWAP1 << u256(CompilerUtils::dataStartOffset); + m_context << eth::Instruction::ADD; + // stack: next_pointer data_pointer + // retrieve length + CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); + // stack: next_pointer length data_pointer + m_context << eth::Instruction::SWAP2; + } + else + { + // leave the pointer on the stack + m_context << eth::Instruction::DUP1; + m_context << u256(type->getCalldataEncodedSize()) << eth::Instruction::ADD; + } } else { - // leave the pointer on the stack - m_context << eth::Instruction::DUP1; - m_context << u256(type->getCalldataEncodedSize()) << eth::Instruction::ADD; + solAssert(arrayType.location() == ReferenceType::Location::Memory, ""); + CompilerUtils(m_context).fetchFreeMemoryPointer(); + CompilerUtils(m_context).storeInMemoryDynamic(*type); + CompilerUtils(m_context).storeFreeMemoryPointer(); } - break; - default: + } + else + { solAssert(!type->isDynamicallySized(), "Unknown dynamically sized type: " + type->toString()); CompilerUtils(m_context).loadFromMemoryDynamic(*type, !_fromMemory, true); } diff --git a/libsolidity/CompilerUtils.cpp b/libsolidity/CompilerUtils.cpp index 3549ef98d..693bd4665 100644 --- a/libsolidity/CompilerUtils.cpp +++ b/libsolidity/CompilerUtils.cpp @@ -31,7 +31,24 @@ namespace dev namespace solidity { -const unsigned int CompilerUtils::dataStartOffset = 4; +const unsigned CompilerUtils::dataStartOffset = 4; +const size_t CompilerUtils::freeMemoryPointer = 64; + +void CompilerUtils::initialiseFreeMemoryPointer() +{ + m_context << u256(freeMemoryPointer + 32); + storeFreeMemoryPointer(); +} + +void CompilerUtils::fetchFreeMemoryPointer() +{ + m_context << u256(freeMemoryPointer) << eth::Instruction::MLOAD; +} + +void CompilerUtils::storeFreeMemoryPointer() +{ + m_context << u256(freeMemoryPointer) << eth::Instruction::MSTORE; +} unsigned CompilerUtils::loadFromMemory( unsigned _offset, diff --git a/libsolidity/CompilerUtils.h b/libsolidity/CompilerUtils.h index 45f53e12e..30ea5cc67 100644 --- a/libsolidity/CompilerUtils.h +++ b/libsolidity/CompilerUtils.h @@ -35,6 +35,13 @@ class CompilerUtils public: CompilerUtils(CompilerContext& _context): m_context(_context) {} + /// Stores the initial value of the free-memory-pointer at its position; + void initialiseFreeMemoryPointer(); + /// Copies the free memory pointer to the stack. + void fetchFreeMemoryPointer(); + /// Stores the free memory pointer from the stack. + void storeFreeMemoryPointer(); + /// Loads data from memory to the stack. /// @param _offset offset in memory (or calldata) /// @param _type data type to load @@ -95,7 +102,10 @@ public: /// Bytes we need to the start of call data. /// - The size in bytes of the function (hash) identifier. - static const unsigned int dataStartOffset; + static const unsigned dataStartOffset; + + /// Position of the free-memory-pointer in memory; + static const size_t freeMemoryPointer; private: /// Prepares the given type for storing in memory by shifting it if necessary. diff --git a/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index ba80a8ea2..31bb6dd10 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/ExpressionCompiler.cpp @@ -1061,22 +1061,32 @@ void ExpressionCompiler::appendExternalFunctionCall( bool returnSuccessCondition = _functionType.getLocation() == FunctionType::Location::Bare || _functionType.getLocation() == FunctionType::Location::BareCallCode; + + // Output data will be at FreeMemPtr, replacing input data. + //@todo only return the first return value for now Type const* firstType = _functionType.getReturnParameterTypes().empty() ? nullptr : _functionType.getReturnParameterTypes().front().get(); unsigned retSize = firstType ? firstType->getCalldataEncodedSize() : 0; if (returnSuccessCondition) retSize = 0; // return value actually is success condition - m_context << u256(retSize) << u256(0); + // put on stack: + m_context << u256(retSize); + CompilerUtils(m_context).fetchFreeMemoryPointer(); - if (_functionType.isBareCall()) - m_context << u256(0); - else + //@TODO CHECK ALL CALLS OF appendTypeMoveToMemory + + // copy arguments to memory and + // put on stack: + m_context << eth::Instruction::DUP1 << eth::Instruction::DUP1; + if (!_functionType.isBareCall()) { // copy function identifier m_context << eth::dupInstruction(gasValueSize + 3); - CompilerUtils(m_context).storeInMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8)); - m_context << u256(CompilerUtils::dataStartOffset); + CompilerUtils(m_context).storeInMemoryDynamic( + IntegerType(CompilerUtils::dataStartOffset * 8), + false + ); } // For bare call, activate "4 byte pad exception": If the first argument has exactly 4 bytes, @@ -1090,10 +1100,11 @@ void ExpressionCompiler::appendExternalFunctionCall( _functionType.getLocation() == FunctionType::Location::BareCallCode, _functionType.takesArbitraryParameters() ); + // now on stack: ... + m_context << eth::Instruction::SUB << eth::Instruction::DUP2; - // CALL arguments: outSize, outOff, inSize, (already present up to here) - // inOff, value, addr, gas (stack top) - m_context << u256(0); + // CALL arguments: outSize, outOff, inSize, inOff (already present up to here) + // value, addr, gas (stack top) if (_functionType.valueSet()) m_context << eth::dupInstruction(m_context.baseToCurrentStackOffset(valueStackPos)); else @@ -1141,11 +1152,15 @@ void ExpressionCompiler::appendExternalFunctionCall( else if (_functionType.getLocation() == FunctionType::Location::RIPEMD160) { // fix: built-in contract returns right-aligned data - CompilerUtils(m_context).loadFromMemory(0, IntegerType(160), false, true); + CompilerUtils(m_context).fetchFreeMemoryPointer(); + CompilerUtils(m_context).loadFromMemoryDynamic(IntegerType(160), false, true, false); appendTypeConversion(IntegerType(160), FixedBytesType(20)); } else if (firstType) - CompilerUtils(m_context).loadFromMemory(0, *firstType, false, true); + { + CompilerUtils(m_context).fetchFreeMemoryPointer(); + CompilerUtils(m_context).loadFromMemoryDynamic(*firstType, false, true, false); + } } void ExpressionCompiler::appendArgumentsCopyToMemory(