diff options
Diffstat (limited to 'libsolidity/codegen')
-rw-r--r-- | libsolidity/codegen/ABIFunctions.cpp | 4 | ||||
-rw-r--r-- | libsolidity/codegen/ABIFunctions.h | 2 | ||||
-rw-r--r-- | libsolidity/codegen/ArrayUtils.cpp | 17 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.cpp | 84 | ||||
-rw-r--r-- | libsolidity/codegen/CompilerUtils.h | 2 | ||||
-rw-r--r-- | libsolidity/codegen/ContractCompiler.cpp | 8 | ||||
-rw-r--r-- | libsolidity/codegen/ExpressionCompiler.cpp | 103 |
7 files changed, 110 insertions, 110 deletions
diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp index 3e3aa0ae..4818e111 100644 --- a/libsolidity/codegen/ABIFunctions.cpp +++ b/libsolidity/codegen/ABIFunctions.cpp @@ -17,7 +17,7 @@ /** * @author Christian <chris@ethereum.org> * @date 2017 - * Routines that generate JULIA code related to ABI encoding, decoding and type conversions. + * Routines that generate Yul code related to ABI encoding, decoding and type conversions. */ #include <libsolidity/codegen/ABIFunctions.h> @@ -989,7 +989,7 @@ string ABIFunctions::abiEncodingFunctionStringLiteral( )"); templ("functionName", functionName); - // TODO this can make use of CODECOPY for large strings once we have that in JULIA + // TODO this can make use of CODECOPY for large strings once we have that in Yul size_t words = (value.size() + 31) / 32; templ("overallSize", to_string(32 + words * 32)); templ("length", to_string(value.size())); diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h index db4d40f5..6bfb3f15 100644 --- a/libsolidity/codegen/ABIFunctions.h +++ b/libsolidity/codegen/ABIFunctions.h @@ -17,7 +17,7 @@ /** * @author Christian <chris@ethereum.org> * @date 2017 - * Routines that generate JULIA code related to ABI encoding, decoding and type conversions. + * Routines that generate Yul code related to ABI encoding, decoding and type conversions. */ #pragma once diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp index 14c887c3..2b77db8f 100644 --- a/libsolidity/codegen/ArrayUtils.cpp +++ b/libsolidity/codegen/ArrayUtils.cpp @@ -303,12 +303,17 @@ void ArrayUtils::copyArrayToMemory(ArrayType const& _sourceType, bool _padToWord m_context << _sourceType.length(); if (baseSize > 1) m_context << u256(baseSize) << Instruction::MUL; - // stack: target source_offset source_len - m_context << Instruction::DUP1 << Instruction::DUP3 << Instruction::DUP5; - // stack: target source_offset source_len source_len source_offset target - m_context << Instruction::CALLDATACOPY; - m_context << Instruction::DUP3 << Instruction::ADD; - m_context << Instruction::SWAP2 << Instruction::POP << Instruction::POP; + + string routine = "calldatacopy(target, source, len)\n"; + if (_padToWordBoundaries) + routine += R"( + // Set padding suffix to zero + mstore(add(target, len), 0) + len := and(add(len, 0x1f), not(0x1f)) + )"; + routine += "target := add(target, len)\n"; + m_context.appendInlineAssembly("{" + routine + "}", {"target", "source", "len"}); + m_context << Instruction::POP << Instruction::POP; } else if (_sourceType.location() == DataLocation::Memory) { diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp index d9f17263..2f45765a 100644 --- a/libsolidity/codegen/CompilerUtils.cpp +++ b/libsolidity/codegen/CompilerUtils.cpp @@ -181,12 +181,12 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound } } -void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMemory, bool _revertOnOutOfBounds) +void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMemory) { /// Stack: <source_offset> <length> if (m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2)) { - // Use the new JULIA-based decoding function + // Use the new Yul-based decoding function auto stackHeightBefore = m_context.stackHeight(); abiDecodeV2(_typeParameters, _fromMemory); solAssert(m_context.stackHeight() - stackHeightBefore == sizeOnStack(_typeParameters) - 2, ""); @@ -194,14 +194,10 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem } //@todo this does not yet support nested dynamic arrays - - if (_revertOnOutOfBounds) - { - size_t encodedSize = 0; - for (auto const& t: _typeParameters) - encodedSize += t->decodingType()->calldataEncodedSize(true); - m_context.appendInlineAssembly("{ if lt(len, " + to_string(encodedSize) + ") { revert(0, 0) } }", {"len"}); - } + size_t encodedSize = 0; + for (auto const& t: _typeParameters) + encodedSize += t->decodingType()->calldataEncodedSize(true); + m_context.appendInlineAssembly("{ if lt(len, " + to_string(encodedSize) + ") { revert(0, 0) } }", {"len"}); m_context << Instruction::DUP2 << Instruction::ADD; m_context << Instruction::SWAP1; @@ -231,26 +227,21 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem { // compute data pointer m_context << Instruction::DUP1 << Instruction::MLOAD; - if (_revertOnOutOfBounds) - { - // Check that the data pointer is valid and that length times - // item size is still inside the range. - Whiskers templ(R"({ - if gt(ptr, 0x100000000) { revert(0, 0) } - ptr := add(ptr, base_offset) - let array_data_start := add(ptr, 0x20) - if gt(array_data_start, input_end) { revert(0, 0) } - let array_length := mload(ptr) - if or( - gt(array_length, 0x100000000), - gt(add(array_data_start, mul(array_length, <item_size>)), input_end) - ) { revert(0, 0) } - })"); - templ("item_size", to_string(arrayType.isByteArray() ? 1 : arrayType.baseType()->calldataEncodedSize(true))); - m_context.appendInlineAssembly(templ.render(), {"input_end", "base_offset", "offset", "ptr"}); - } - else - m_context << Instruction::DUP3 << Instruction::ADD; + // Check that the data pointer is valid and that length times + // item size is still inside the range. + Whiskers templ(R"({ + if gt(ptr, 0x100000000) { revert(0, 0) } + ptr := add(ptr, base_offset) + let array_data_start := add(ptr, 0x20) + if gt(array_data_start, input_end) { revert(0, 0) } + let array_length := mload(ptr) + if or( + gt(array_length, 0x100000000), + gt(add(array_data_start, mul(array_length, <item_size>)), input_end) + ) { revert(0, 0) } + })"); + templ("item_size", to_string(arrayType.isByteArray() ? 1 : arrayType.baseType()->calldataEncodedSize(true))); + m_context.appendInlineAssembly(templ.render(), {"input_end", "base_offset", "offset", "ptr"}); // stack: v1 v2 ... v(k-1) input_end base_offset current_offset v(k) moveIntoStack(3); m_context << u256(0x20) << Instruction::ADD; @@ -273,30 +264,25 @@ void CompilerUtils::abiDecode(TypePointers const& _typeParameters, bool _fromMem loadFromMemoryDynamic(IntegerType(256), !_fromMemory); m_context << Instruction::SWAP1; // stack: input_end base_offset next_pointer data_offset - if (_revertOnOutOfBounds) - m_context.appendInlineAssembly("{ if gt(data_offset, 0x100000000) { revert(0, 0) } }", {"data_offset"}); + m_context.appendInlineAssembly("{ if gt(data_offset, 0x100000000) { revert(0, 0) } }", {"data_offset"}); m_context << Instruction::DUP3 << Instruction::ADD; // stack: input_end base_offset next_pointer array_head_ptr - if (_revertOnOutOfBounds) - m_context.appendInlineAssembly( - "{ if gt(add(array_head_ptr, 0x20), input_end) { revert(0, 0) } }", - {"input_end", "base_offset", "next_ptr", "array_head_ptr"} - ); + m_context.appendInlineAssembly( + "{ if gt(add(array_head_ptr, 0x20), input_end) { revert(0, 0) } }", + {"input_end", "base_offset", "next_ptr", "array_head_ptr"} + ); // retrieve length loadFromMemoryDynamic(IntegerType(256), !_fromMemory, true); // stack: input_end base_offset next_pointer array_length data_pointer m_context << Instruction::SWAP2; // stack: input_end base_offset data_pointer array_length next_pointer - if (_revertOnOutOfBounds) - { - unsigned itemSize = arrayType.isByteArray() ? 1 : arrayType.baseType()->calldataEncodedSize(true); - m_context.appendInlineAssembly(R"({ - if or( - gt(array_length, 0x100000000), - gt(add(data_ptr, mul(array_length, )" + to_string(itemSize) + R"()), input_end) - ) { revert(0, 0) } - })", {"input_end", "base_offset", "data_ptr", "array_length", "next_ptr"}); - } + unsigned itemSize = arrayType.isByteArray() ? 1 : arrayType.baseType()->calldataEncodedSize(true); + m_context.appendInlineAssembly(R"({ + if or( + gt(array_length, 0x100000000), + gt(add(data_ptr, mul(array_length, )" + to_string(itemSize) + R"()), input_end) + ) { revert(0, 0) } + })", {"input_end", "base_offset", "data_ptr", "array_length", "next_ptr"}); } else { @@ -368,7 +354,7 @@ void CompilerUtils::encodeToMemory( m_context.experimentalFeatureActive(ExperimentalFeature::ABIEncoderV2) ) { - // Use the new JULIA-based encoding function + // Use the new Yul-based encoding function auto stackHeightBefore = m_context.stackHeight(); abiEncodeV2(_givenTypes, targetTypes, _encodeAsLibraryTypes); solAssert(stackHeightBefore - m_context.stackHeight() == sizeOnStack(_givenTypes), ""); @@ -524,7 +510,7 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type) codecopy(memptr, codesize(), size) memptr := add(memptr, size) })"); - templ("element_size", to_string(_type.baseType()->memoryHeadSize())); + templ("element_size", to_string(_type.isByteArray() ? 1 : _type.baseType()->memoryHeadSize())); m_context.appendInlineAssembly(templ.render(), {"length", "memptr"}); } else diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h index 8e3a8a5d..0ff3ad7c 100644 --- a/libsolidity/codegen/CompilerUtils.h +++ b/libsolidity/codegen/CompilerUtils.h @@ -102,7 +102,7 @@ public: /// area. Also has a hard cap of 0x100000000 for any given length/offset field. /// Stack pre: <source_offset> <length> /// Stack post: <value0> <value1> ... <valuen> - void abiDecode(TypePointers const& _typeParameters, bool _fromMemory = false, bool _revertOnOutOfBounds = false); + void abiDecode(TypePointers const& _typeParameters, bool _fromMemory = false); /// 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. diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp index f195b416..81aba21e 100644 --- a/libsolidity/codegen/ContractCompiler.cpp +++ b/libsolidity/codegen/ContractCompiler.cpp @@ -749,16 +749,16 @@ bool ContractCompiler::visit(ForStatement const& _forStatement) bool ContractCompiler::visit(Continue const& _continueStatement) { CompilerContext::LocationSetter locationSetter(m_context, _continueStatement); - if (!m_continueTags.empty()) - m_context.appendJumpTo(m_continueTags.back()); + solAssert(!m_continueTags.empty(), ""); + m_context.appendJumpTo(m_continueTags.back()); return false; } bool ContractCompiler::visit(Break const& _breakStatement) { CompilerContext::LocationSetter locationSetter(m_context, _breakStatement); - if (!m_breakTags.empty()) - m_context.appendJumpTo(m_breakTags.back()); + solAssert(!m_breakTags.empty(), ""); + m_context.appendJumpTo(m_breakTags.back()); return false; } diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 93d440c8..2e548e32 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -699,16 +699,24 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall) } case FunctionType::Kind::SHA3: { - TypePointers argumentTypes; - for (auto const& arg: arguments) + solAssert(arguments.size() == 1, ""); + solAssert(!function.padArguments(), ""); + TypePointer const& argType = arguments.front()->annotation().type; + solAssert(argType, ""); + arguments.front()->accept(*this); + // Optimization: If type is bytes or string, then do not encode, + // but directly compute keccak256 on memory. + if (*argType == ArrayType(DataLocation::Memory) || *argType == ArrayType(DataLocation::Memory, true)) { - arg->accept(*this); - argumentTypes.push_back(arg->annotation().type); + ArrayUtils(m_context).retrieveLength(ArrayType(DataLocation::Memory)); + m_context << Instruction::SWAP1 << u256(0x20) << Instruction::ADD; + } + else + { + utils().fetchFreeMemoryPointer(); + utils().packedEncode({argType}, TypePointers()); + utils().toSizeAfterFreeMemoryPointer(); } - utils().fetchFreeMemoryPointer(); - solAssert(!function.padArguments(), ""); - utils().packedEncode(argumentTypes, TypePointers()); - utils().toSizeAfterFreeMemoryPointer(); m_context << Instruction::KECCAK256; break; } @@ -1737,11 +1745,36 @@ void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator, Type co m_context << u256(2) << Instruction::EXP << Instruction::MUL; break; case Token::SAR: - // NOTE: SAR rounds differently than SDIV - if (m_context.evmVersion().hasBitwiseShifting() && !c_valueSigned) - m_context << Instruction::SHR; + if (m_context.evmVersion().hasBitwiseShifting()) + m_context << (c_valueSigned ? Instruction::SAR : Instruction::SHR); else - m_context << u256(2) << Instruction::EXP << Instruction::SWAP1 << (c_valueSigned ? Instruction::SDIV : Instruction::DIV); + { + if (c_valueSigned) + // In the following assembly snippet, xor_mask will be zero, if value_to_shift is positive. + // Therefor xor'ing with xor_mask is the identity and the computation reduces to + // div(value_to_shift, exp(2, shift_amount)), which is correct, since for positive values + // arithmetic right shift is dividing by a power of two (which, as a bitwise operation, results + // in discarding bits on the right and filling with zeros from the left). + // For negative values arithmetic right shift, viewed as a bitwise operation, discards bits to the + // right and fills in ones from the left. This is achieved as follows: + // If value_to_shift is negative, then xor_mask will have all bits set, so xor'ing with xor_mask + // will flip all bits. First all bits in value_to_shift are flipped. As for the positive case, + // dividing by a power of two using integer arithmetic results in discarding bits to the right + // and filling with zeros from the left. Flipping all bits in the result again, turns all zeros + // on the left to ones and restores the non-discarded, shifted bits to their original value (they + // have now been flipped twice). In summary we now have discarded bits to the right and filled with + // ones from the left, i.e. we have performed an arithmetic right shift. + m_context.appendInlineAssembly(R"({ + let xor_mask := sub(0, slt(value_to_shift, 0)) + value_to_shift := xor(div(xor(value_to_shift, xor_mask), exp(2, shift_amount)), xor_mask) + })", {"value_to_shift", "shift_amount"}); + else + m_context.appendInlineAssembly(R"({ + value_to_shift := div(value_to_shift, exp(2, shift_amount)) + })", {"value_to_shift", "shift_amount"}); + m_context << Instruction::POP; + + } break; case Token::SHR: default: @@ -1777,13 +1810,14 @@ void ExpressionCompiler::appendExternalFunctionCall( if (_functionType.bound()) utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack()); + bool const v050 = m_context.experimentalFeatureActive(ExperimentalFeature::V050); auto funKind = _functionType.kind(); bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall; bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode; bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall; bool useStaticCall = _functionType.stateMutability() <= StateMutability::View && - m_context.experimentalFeatureActive(ExperimentalFeature::V050) && + v050 && m_context.evmVersion().hasStaticCall(); bool haveReturndatacopy = m_context.evmVersion().supportsReturndata(); @@ -1811,38 +1845,12 @@ void ExpressionCompiler::appendExternalFunctionCall( // Evaluate arguments. TypePointers argumentTypes; TypePointers parameterTypes = _functionType.parameterTypes(); - bool manualFunctionId = false; - if ( - (funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall) && - !_arguments.empty() - ) - { - solAssert(_arguments.front()->annotation().type->mobileType(), ""); - manualFunctionId = - _arguments.front()->annotation().type->mobileType()->calldataEncodedSize(false) == - CompilerUtils::dataStartOffset; - } - if (manualFunctionId) - { - // If we have a Bare* and the first type has exactly 4 bytes, use it as - // function identifier. - _arguments.front()->accept(*this); - utils().convertType( - *_arguments.front()->annotation().type, - IntegerType(8 * CompilerUtils::dataStartOffset), - true - ); - for (unsigned i = 0; i < gasValueSize; ++i) - m_context << swapInstruction(gasValueSize - i); - gasStackPos++; - valueStackPos++; - } if (_functionType.bound()) { argumentTypes.push_back(_functionType.selfType()); parameterTypes.insert(parameterTypes.begin(), _functionType.selfType()); } - for (size_t i = manualFunctionId ? 1 : 0; i < _arguments.size(); ++i) + for (size_t i = 0; i < _arguments.size(); ++i) { _arguments[i]->accept(*this); argumentTypes.push_back(_arguments[i]->annotation().type); @@ -1878,19 +1886,20 @@ void ExpressionCompiler::appendExternalFunctionCall( // Copy function identifier to memory. utils().fetchFreeMemoryPointer(); - if (!_functionType.isBareCall() || manualFunctionId) + if (!_functionType.isBareCall()) { m_context << dupInstruction(2 + gasValueSize + CompilerUtils::sizeOnStack(argumentTypes)); utils().storeInMemoryDynamic(IntegerType(8 * CompilerUtils::dataStartOffset), false); } - // If the function takes arbitrary parameters, copy dynamic length data in place. + + // If the function takes arbitrary parameters or is a bare call, copy dynamic length data in place. // Move arguments to memory, will not update the free memory pointer (but will update the memory // pointer on the stack). utils().encodeToMemory( argumentTypes, parameterTypes, _functionType.padArguments(), - _functionType.takesArbitraryParameters(), + _functionType.takesArbitraryParameters() || _functionType.isBareCall(), isCallCode || isDelegateCall ); @@ -1971,9 +1980,9 @@ void ExpressionCompiler::appendExternalFunctionCall( unsigned remainsSize = 2 + // contract address, input_memory_end - _functionType.valueSet() + - _functionType.gasSet() + - (!_functionType.isBareCall() || manualFunctionId); + (_functionType.valueSet() ? 1 : 0) + + (_functionType.gasSet() ? 1 : 0) + + (!_functionType.isBareCall() ? 1 : 0); if (returnSuccessCondition) m_context << swapInstruction(remainsSize); @@ -2040,7 +2049,7 @@ void ExpressionCompiler::appendExternalFunctionCall( mstore(0x40, newMem) })", {"start", "size"}); - utils().abiDecode(returnTypes, true, true); + utils().abiDecode(returnTypes, true); } } |