diff options
Diffstat (limited to 'libsolidity')
-rw-r--r-- | libsolidity/analysis/TypeChecker.cpp | 7 | ||||
-rw-r--r-- | libsolidity/ast/ASTAnnotations.h | 2 | ||||
-rw-r--r-- | libsolidity/codegen/ArrayUtils.cpp | 9 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerContext.cpp | 4 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.cpp | 87 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.h | 26 | ||||
-rw-r--r-- | libsolidity/interface/CompilerStack.cpp | 2 |
7 files changed, 105 insertions, 32 deletions
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index 7235b57a..e414e27c 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -1529,6 +1529,8 @@ bool TypeChecker::visit(Identifier const& _identifier) !!annotation.referencedDeclaration, "Referenced declaration is null after overload resolution." ); + auto variableDeclaration = dynamic_cast<VariableDeclaration const*>(annotation.referencedDeclaration); + annotation.isConstant = variableDeclaration != nullptr && variableDeclaration->isConstant(); annotation.isLValue = annotation.referencedDeclaration->isLValue(); annotation.type = annotation.referencedDeclaration->type(); if (!annotation.type) @@ -1612,7 +1614,10 @@ void TypeChecker::requireLValue(Expression const& _expression) { _expression.annotation().lValueRequested = true; _expression.accept(*this); - if (!_expression.annotation().isLValue) + + if (_expression.annotation().isConstant) + typeError(_expression.location(), "Cannot assign to a constant variable."); + else if (!_expression.annotation().isLValue) typeError(_expression.location(), "Expression has to be an lvalue."); } diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h index 768e56db..9c4c3ae8 100644 --- a/libsolidity/ast/ASTAnnotations.h +++ b/libsolidity/ast/ASTAnnotations.h @@ -154,6 +154,8 @@ struct ExpressionAnnotation: ASTAnnotation { /// Inferred type of the expression. TypePointer type; + /// Whether the expression is a constant variable + bool isConstant = false; /// Whether it is an LValue (i.e. something that can be assigned to). bool isLValue = false; /// Whether the expression is used in a context where the LValue is actually required. diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 2c982982..352c7177 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -335,9 +335,14 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord if (baseSize > 1) m_context << u256(baseSize) << Instruction::MUL; // stack: <target> <source> <size> - //@TODO do not use ::CALL if less than 32 bytes? m_context << Instruction::DUP1 << Instruction::DUP4 << Instruction::DUP4; - utils.memoryCopy(); + // We can resort to copying full 32 bytes only if + // - the length is known to be a multiple of 32 or + // - we will pad to full 32 bytes later anyway. + if (((baseSize % 32) == 0) || _padToWordBoundaries) + utils.memoryCopy32(); + else + utils.memoryCopy(); m_context << Instruction::SWAP1 << Instruction::POP; // stack: <target> <size> diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp index 2de5a3ec..c14ab845 100644 --- a/libsolidity/codegen/CompilerContext.cpp +++ b/libsolidity/codegen/CompilerContext.cpp @@ -202,6 +202,8 @@ void CompilerContext::appendInlineAssembly( return false; unsigned stackDepth = _localVariables.end() - it; int stackDiff = _assembly.deposit() - startStackHeight + stackDepth; + if (_context == assembly::CodeGenerator::IdentifierContext::LValue) + stackDiff -= 1; if (stackDiff < 1 || stackDiff > 16) BOOST_THROW_EXCEPTION( CompilerError() << @@ -217,7 +219,7 @@ void CompilerContext::appendInlineAssembly( return true; }; - solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, *m_asm, identifierAccess), ""); + solAssert(assembly::InlineAssemblyStack().parseAndAssemble(*assembly, *m_asm, identifierAccess), "Failed to assemble inline assembly block."); } FunctionDefinition const& CompilerContext::resolveVirtualFunction( diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index 7c159ff7..7d382aba 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -298,22 +298,73 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) m_context << Instruction::SWAP1 << Instruction::POP; } +void CompilerUtils::memoryCopyPrecompile() +{ + // Stack here: size target source + + m_context.appendInlineAssembly(R"( + { + let words := div(add(len, 31), 32) + let cost := add(15, mul(3, words)) + jumpi(invalidJumpLabel, iszero(call(cost, $identityContractAddress, 0, src, len, dst, len))) + } + )", + { "len", "dst", "src" }, + map<string, string> { + { "$identityContractAddress", toString(identityContractAddress) } + } + ); + m_context << Instruction::POP << Instruction::POP << Instruction::POP; +} + +void CompilerUtils::memoryCopy32() +{ + // Stack here: size target source + + m_context.appendInlineAssembly(R"( + { + jumpi(end, eq(len, 0)) + start: + mstore(dst, mload(src)) + jumpi(end, iszero(gt(len, 32))) + dst := add(dst, 32) + src := add(src, 32) + len := sub(len, 32) + jump(start) + end: + } + )", + { "len", "dst", "src" } + ); + m_context << Instruction::POP << Instruction::POP << Instruction::POP; +} + void CompilerUtils::memoryCopy() { // Stack here: size target source - // stack for call: outsize target size source value contract gas - //@TODO do not use ::CALL if less than 32 bytes? - m_context << Instruction::DUP3 << Instruction::SWAP1; - m_context << u256(0) << u256(identityContractAddress); - // compute gas costs - m_context << u256(32) << Instruction::DUP5 << u256(31) << Instruction::ADD; - static unsigned c_identityGas = 15; - static unsigned c_identityWordGas = 3; - m_context << Instruction::DIV << u256(c_identityWordGas) << Instruction::MUL; - m_context << u256(c_identityGas) << Instruction::ADD; - m_context << Instruction::CALL; - m_context << Instruction::ISZERO; - m_context.appendConditionalJumpTo(m_context.errorTag()); + + m_context.appendInlineAssembly(R"( + { + // copy 32 bytes at once + start32: + jumpi(end32, lt(len, 32)) + mstore(dst, mload(src)) + dst := add(dst, 32) + src := add(src, 32) + len := sub(len, 32) + jump(start32) + end32: + + // copy the remainder (0 < len < 32) + let mask := sub(exp(256, sub(32, len)), 1) + let srcpart := and(mload(src), not(mask)) + let dstpart := and(mload(dst), mask) + mstore(dst, or(srcpart, dstpart)) + } + )", + { "len", "dst", "src" } + ); + m_context << Instruction::POP << Instruction::POP << Instruction::POP; } void CompilerUtils::splitExternalFunctionType(bool _leftAligned) @@ -901,9 +952,9 @@ void CompilerUtils::storeStringData(bytesConstRef _data) } } -unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries) +unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWords) { - unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries); + unsigned numBytes = _type.calldataEncodedSize(_padToWords); bool isExternalFunctionType = false; if (auto const* funType = dynamic_cast<FunctionType const*>(&_type)) if (funType->location() == FunctionType::Location::External) @@ -942,9 +993,9 @@ void CompilerUtils::cleanHigherOrderBits(IntegerType const& _typeOnStack) m_context << ((u256(1) << _typeOnStack.numBits()) - 1) << Instruction::AND; } -unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) +unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWords) { - unsigned numBytes = _type.calldataEncodedSize(_padToWordBoundaries); + unsigned numBytes = _type.calldataEncodedSize(_padToWords); bool leftAligned = _type.category() == Type::Category::FixedBytes; if (numBytes == 0) m_context << Instruction::POP; @@ -952,7 +1003,7 @@ unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBou { solAssert(numBytes <= 32, "Memory store of more than 32 bytes requested."); convertType(_type, _type, true); - if (numBytes != 32 && !leftAligned && !_padToWordBoundaries) + if (numBytes != 32 && !leftAligned && !_padToWords) // shift the value accordingly before storing m_context << (u256(1) << ((32 - numBytes) * 8)) << Instruction::MUL; } diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 0a5d8e1c..b9ed6757 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -52,13 +52,13 @@ public: /// @param _offset offset in memory (or calldata) /// @param _type data type to load /// @param _fromCalldata if true, load from calldata, not from memory - /// @param _padToWordBoundaries if true, assume the data is padded to word (32 byte) boundaries + /// @param _padToWords if true, assume the data is padded to full words (32 bytes) /// @returns the number of bytes consumed in memory. unsigned loadFromMemory( unsigned _offset, Type const& _type = IntegerType(256), bool _fromCalldata = false, - bool _padToWordBoundaries = false + bool _padToWords = false ); /// Dynamic version of @see loadFromMemory, expects the memory offset on the stack. /// Stack pre: memory_offset @@ -66,7 +66,7 @@ public: void loadFromMemoryDynamic( Type const& _type, bool _fromCalldata = false, - bool _padToWordBoundaries = true, + bool _padToWords = true, bool _keepUpdatedMemoryOffset = true ); /// Stores a 256 bit integer from stack in memory. @@ -76,11 +76,11 @@ public: /// 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. - /// @param _padToWordBoundaries if true, adds zeros to pad to multiple of 32 bytes. Array elements + /// @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. /// Stack pre: memory_offset value... /// Stack post: (memory_offset+length) - void storeInMemoryDynamic(Type const& _type, bool _padToWordBoundaries = true); + void storeInMemoryDynamic(Type const& _type, bool _padToWords = true); /// Copies values (of types @a _givenTypes) given on the stack to a location in memory given /// at the stack top, encoding them according to the ABI as the given types @a _targetTypes. @@ -88,7 +88,7 @@ public: /// Stack pre: <v1> <v2> ... <vn> <memptr> /// Stack post: <memptr_updated> /// Does not touch the memory-free pointer. - /// @param _padToWordBoundaries if false, all values are concatenated without padding. + /// @param _padToWords if false, all values are concatenated without padding. /// @param _copyDynamicDataInPlace if true, dynamic types is stored (without length) /// together with fixed-length data. /// @param _encodeAsLibraryTypes if true, encodes for a library function, e.g. does not @@ -98,7 +98,7 @@ public: void encodeToMemory( TypePointers const& _givenTypes = {}, TypePointers const& _targetTypes = {}, - bool _padToWordBoundaries = true, + bool _padToWords = true, bool _copyDynamicDataInPlace = false, bool _encodeAsLibraryTypes = false ); @@ -112,6 +112,14 @@ public: /// Uses a CALL to the identity contract to perform a memory-to-memory copy. /// Stack pre: <size> <target> <source> /// Stack post: + void memoryCopyPrecompile(); + /// Copies full 32 byte words in memory (regions cannot overlap), i.e. may copy more than length. + /// Stack pre: <size> <target> <source> + /// Stack post: + void memoryCopy32(); + /// Copies data in memory (regions cannot overlap). + /// Stack pre: <size> <target> <source> + /// Stack post: void memoryCopy(); /// Converts the combined and left-aligned (right-aligned if @a _rightAligned is true) @@ -185,9 +193,9 @@ private: void cleanHigherOrderBits(IntegerType const& _typeOnStack); /// Prepares the given type for storing in memory by shifting it if necessary. - unsigned prepareMemoryStore(Type const& _type, bool _padToWordBoundaries); + unsigned prepareMemoryStore(Type const& _type, bool _padToWords); /// Loads type from memory assuming memory offset is on stack top. - unsigned loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWordBoundaries); + unsigned loadFromMemoryHelper(Type const& _type, bool _fromCalldata, bool _padToWords); CompilerContext& m_context; }; diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp index eb588fc2..0b8cecc7 100644 --- a/libsolidity/interface/CompilerStack.cpp +++ b/libsolidity/interface/CompilerStack.cpp @@ -146,7 +146,7 @@ bool CompilerStack::parse() } } if (!Error::containsOnlyWarnings(m_errors)) - // errors while parsing. sould stop before type checking + // errors while parsing. should stop before type checking return false; resolveImports(); |