aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/codegen/CompilerUtils.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/codegen/CompilerUtils.cpp')
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp221
1 files changed, 153 insertions, 68 deletions
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index d5361ac6..7067ddd5 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -128,14 +128,14 @@ void CompilerUtils::storeInMemoryDynamic(Type const& _type, bool _padToWordBound
m_context << Instruction::DUP1;
storeStringData(bytesConstRef(str->value()));
if (_padToWordBoundaries)
- m_context << u256(((str->value().size() + 31) / 32) * 32);
+ m_context << u256(max<size_t>(32, ((str->value().size() + 31) / 32) * 32));
else
m_context << u256(str->value().size());
m_context << Instruction::ADD;
}
else if (
_type.category() == Type::Category::Function &&
- dynamic_cast<FunctionType const&>(_type).location() == FunctionType::Location::External
+ dynamic_cast<FunctionType const&>(_type).kind() == FunctionType::Kind::External
)
{
solUnimplementedAssert(_padToWordBoundaries, "Non-padded store for function not implemented.");
@@ -180,6 +180,9 @@ void CompilerUtils::encodeToMemory(
t = t->mobileType()->interfaceType(_encodeAsLibraryTypes)->encodingType();
}
+ if (_givenTypes.empty())
+ return;
+
// Stack during operation:
// <v1> <v2> ... <vn> <mem_start> <dyn_head_1> ... <dyn_head_r> <end_of_mem>
// The values dyn_head_i are added during the first loop and they point to the head part
@@ -200,6 +203,7 @@ void CompilerUtils::encodeToMemory(
// leave end_of_mem as dyn head pointer
m_context << Instruction::DUP1 << u256(32) << Instruction::ADD;
dynPointers++;
+ solAssert((argSize + dynPointers) < 16, "Stack too deep, try using less variables.");
}
else
{
@@ -298,21 +302,49 @@ void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type)
m_context << Instruction::SWAP1 << Instruction::POP;
}
+void CompilerUtils::memoryCopy32()
+{
+ // Stack here: size target source
+
+ m_context.appendInlineAssembly(R"(
+ {
+ for { let i := 0 } lt(i, len) { i := add(i, 32) } {
+ mstore(add(dst, i), mload(add(src, i)))
+ }
+ }
+ )",
+ { "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::POP; // ignore return value
+
+ m_context.appendInlineAssembly(R"(
+ {
+ // copy 32 bytes at once
+ for
+ {}
+ iszero(lt(len, 32))
+ {
+ dst := add(dst, 32)
+ src := add(src, 32)
+ len := sub(len, 32)
+ }
+ { mstore(dst, mload(src)) }
+
+ // 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)
@@ -321,13 +353,16 @@ void CompilerUtils::splitExternalFunctionType(bool _leftAligned)
// address (right aligned), function identifier (right aligned)
if (_leftAligned)
{
- m_context << Instruction::DUP1 << (u256(1) << (64 + 32)) << Instruction::SWAP1 << Instruction::DIV;
+ m_context << Instruction::DUP1;
+ rightShiftNumberOnStack(64 + 32, false);
// <input> <address>
- m_context << Instruction::SWAP1 << (u256(1) << 64) << Instruction::SWAP1 << Instruction::DIV;
+ m_context << Instruction::SWAP1;
+ rightShiftNumberOnStack(64, false);
}
else
{
- m_context << Instruction::DUP1 << (u256(1) << 32) << Instruction::SWAP1 << Instruction::DIV;
+ m_context << Instruction::DUP1;
+ rightShiftNumberOnStack(32, false);
m_context << ((u256(1) << 160) - 1) << Instruction::AND << Instruction::SWAP1;
}
m_context << u256(0xffffffffUL) << Instruction::AND;
@@ -339,10 +374,10 @@ void CompilerUtils::combineExternalFunctionType(bool _leftAligned)
m_context << u256(0xffffffffUL) << Instruction::AND << Instruction::SWAP1;
if (!_leftAligned)
m_context << ((u256(1) << 160) - 1) << Instruction::AND;
- m_context << (u256(1) << 32) << Instruction::MUL;
+ leftShiftNumberOnStack(32);
m_context << Instruction::OR;
if (_leftAligned)
- m_context << (u256(1) << 64) << Instruction::MUL;
+ leftShiftNumberOnStack(64);
}
void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function)
@@ -351,14 +386,21 @@ void CompilerUtils::pushCombinedFunctionEntryLabel(Declaration const& _function)
// If there is a runtime context, we have to merge both labels into the same
// stack slot in case we store it in storage.
if (CompilerContext* rtc = m_context.runtimeContext())
+ {
+ leftShiftNumberOnStack(32);
m_context <<
- (u256(1) << 32) <<
- Instruction::MUL <<
rtc->functionEntryLabel(_function).toSubAssemblyTag(m_context.runtimeSub()) <<
Instruction::OR;
+ }
}
-void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetType, bool _cleanupNeeded, bool _chopSignBits)
+void CompilerUtils::convertType(
+ Type const& _typeOnStack,
+ Type const& _targetType,
+ bool _cleanupNeeded,
+ bool _chopSignBits,
+ bool _asPartOfArgumentDecoding
+)
{
// For a type extension, we need to remove all higher-order bits that we might have ignored in
// previous operations.
@@ -387,7 +429,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
// conversion from bytes to integer. no need to clean the high bit
// only to shift right because of opposite alignment
IntegerType const& targetIntegerType = dynamic_cast<IntegerType const&>(_targetType);
- m_context << (u256(1) << (256 - typeOnStack.numBytes() * 8)) << Instruction::SWAP1 << Instruction::DIV;
+ rightShiftNumberOnStack(256 - typeOnStack.numBytes() * 8, false);
if (targetIntegerType.numBits() < typeOnStack.numBytes() * 8)
convertType(IntegerType(typeOnStack.numBytes() * 8), _targetType, _cleanupNeeded);
}
@@ -416,7 +458,10 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
EnumType const& enumType = dynamic_cast<decltype(enumType)>(_typeOnStack);
solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error.");
m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT;
- m_context.appendConditionalJumpTo(m_context.errorTag());
+ if (_asPartOfArgumentDecoding)
+ m_context.appendConditionalRevert();
+ else
+ m_context.appendConditionalInvalid();
enumOverflowCheckPending = false;
}
break;
@@ -435,7 +480,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
if (auto typeOnStack = dynamic_cast<IntegerType const*>(&_typeOnStack))
if (targetBytesType.numBytes() * 8 > typeOnStack->numBits())
cleanHigherOrderBits(*typeOnStack);
- m_context << (u256(1) << (256 - targetBytesType.numBytes() * 8)) << Instruction::MUL;
+ leftShiftNumberOnStack(256 - targetBytesType.numBytes() * 8);
}
else if (targetTypeCategory == Type::Category::Enum)
{
@@ -445,7 +490,7 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
EnumType const& enumType = dynamic_cast<decltype(enumType)>(_targetType);
solAssert(enumType.numberOfMembers() > 0, "empty enum should have caused a parser error.");
m_context << u256(enumType.numberOfMembers() - 1) << Instruction::DUP2 << Instruction::GT;
- m_context.appendConditionalJumpTo(m_context.errorTag());
+ m_context.appendConditionalInvalid();
enumOverflowCheckPending = false;
}
else if (targetTypeCategory == Type::Category::FixedPoint)
@@ -735,6 +780,20 @@ void CompilerUtils::convertType(Type const& _typeOnStack, Type const& _targetTyp
if (_cleanupNeeded)
m_context << Instruction::ISZERO << Instruction::ISZERO;
break;
+ case Type::Category::Function:
+ {
+ if (targetTypeCategory == Type::Category::Integer)
+ {
+ IntegerType const& targetType = dynamic_cast<IntegerType const&>(_targetType);
+ solAssert(targetType.isAddress(), "Function type can only be converted to address.");
+ FunctionType const& typeOnStack = dynamic_cast<FunctionType const&>(_typeOnStack);
+ solAssert(typeOnStack.kind() == FunctionType::Kind::External, "Only external function type can be converted.");
+
+ // stack: <address> <function_id>
+ m_context << Instruction::POP;
+ break;
+ }
+ }
default:
// All other types should not be convertible to non-equal types.
solAssert(_typeOnStack == _targetType, "Invalid type conversion requested.");
@@ -753,9 +812,11 @@ void CompilerUtils::pushZeroValue(Type const& _type)
{
if (auto const* funType = dynamic_cast<FunctionType const*>(&_type))
{
- if (funType->location() == FunctionType::Location::Internal)
+ if (funType->kind() == FunctionType::Kind::Internal)
{
- m_context << m_context.errorTag();
+ m_context << m_context.lowLevelFunctionTag("$invalidFunction", 0, 0, [](CompilerContext& _context) {
+ _context.appendInvalid();
+ });
return;
}
}
@@ -768,37 +829,46 @@ void CompilerUtils::pushZeroValue(Type const& _type)
}
solAssert(referenceType->location() == DataLocation::Memory, "");
- m_context << u256(max(32u, _type.calldataEncodedSize()));
- allocateMemory();
- m_context << Instruction::DUP1;
+ TypePointer type = _type.shared_from_this();
+ m_context.callLowLevelFunction(
+ "$pushZeroValue_" + referenceType->identifier(),
+ 0,
+ 1,
+ [type](CompilerContext& _context) {
+ CompilerUtils utils(_context);
+ _context << u256(max(32u, type->calldataEncodedSize()));
+ utils.allocateMemory();
+ _context << Instruction::DUP1;
- if (auto structType = dynamic_cast<StructType const*>(&_type))
- for (auto const& member: structType->members(nullptr))
- {
- pushZeroValue(*member.type);
- storeInMemoryDynamic(*member.type);
- }
- else if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
- {
- if (arrayType->isDynamicallySized())
- {
- // zero length
- m_context << u256(0);
- storeInMemoryDynamic(IntegerType(256));
- }
- else if (arrayType->length() > 0)
- {
- m_context << arrayType->length() << Instruction::SWAP1;
- // stack: items_to_do memory_pos
- zeroInitialiseMemoryArray(*arrayType);
- // stack: updated_memory_pos
- }
- }
- else
- solAssert(false, "Requested initialisation for unknown type: " + _type.toString());
+ if (auto structType = dynamic_cast<StructType const*>(type.get()))
+ for (auto const& member: structType->members(nullptr))
+ {
+ utils.pushZeroValue(*member.type);
+ utils.storeInMemoryDynamic(*member.type);
+ }
+ else if (auto arrayType = dynamic_cast<ArrayType const*>(type.get()))
+ {
+ if (arrayType->isDynamicallySized())
+ {
+ // zero length
+ _context << u256(0);
+ utils.storeInMemoryDynamic(IntegerType(256));
+ }
+ else if (arrayType->length() > 0)
+ {
+ _context << arrayType->length() << Instruction::SWAP1;
+ // stack: items_to_do memory_pos
+ utils.zeroInitialiseMemoryArray(*arrayType);
+ // stack: updated_memory_pos
+ }
+ }
+ else
+ solAssert(false, "Requested initialisation for unknown type: " + type->toString());
- // remove the updated memory pointer
- m_context << Instruction::POP;
+ // remove the updated memory pointer
+ _context << Instruction::POP;
+ }
+ );
}
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
@@ -875,7 +945,7 @@ unsigned CompilerUtils::sizeOnStack(vector<shared_ptr<Type const>> const& _varia
void CompilerUtils::computeHashStatic()
{
storeInMemory(0);
- m_context << u256(32) << u256(0) << Instruction::SHA3;
+ m_context << u256(32) << u256(0) << Instruction::KECCAK256;
}
void CompilerUtils::storeStringData(bytesConstRef _data)
@@ -900,12 +970,12 @@ 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)
+ if (funType->kind() == FunctionType::Kind::External)
isExternalFunctionType = true;
if (numBytes == 0)
{
@@ -920,11 +990,13 @@ unsigned CompilerUtils::loadFromMemoryHelper(Type const& _type, bool _fromCallda
{
bool leftAligned = _type.category() == Type::Category::FixedBytes;
// add leading or trailing zeros by dividing/multiplying depending on alignment
- u256 shiftFactor = u256(1) << ((32 - numBytes) * 8);
- m_context << shiftFactor << Instruction::SWAP1 << Instruction::DIV;
+ int shiftFactor = (32 - numBytes) * 8;
+ rightShiftNumberOnStack(shiftFactor, false);
if (leftAligned)
- m_context << shiftFactor << Instruction::MUL;
+ leftShiftNumberOnStack(shiftFactor);
}
+ if (_fromCalldata)
+ convertType(_type, _type, true, false, true);
return numBytes;
}
@@ -939,18 +1011,31 @@ void CompilerUtils::cleanHigherOrderBits(IntegerType const& _typeOnStack)
m_context << ((u256(1) << _typeOnStack.numBits()) - 1) << Instruction::AND;
}
-unsigned CompilerUtils::prepareMemoryStore(Type const& _type, bool _padToWordBoundaries) const
+void CompilerUtils::leftShiftNumberOnStack(unsigned _bits)
+{
+ solAssert(_bits < 256, "");
+ m_context << (u256(1) << _bits) << Instruction::MUL;
+}
+
+void CompilerUtils::rightShiftNumberOnStack(unsigned _bits, bool _isSigned)
+{
+ solAssert(_bits < 256, "");
+ m_context << (u256(1) << _bits) << Instruction::SWAP1 << (_isSigned ? Instruction::SDIV : Instruction::DIV);
+}
+
+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;
else
{
solAssert(numBytes <= 32, "Memory store of more than 32 bytes requested.");
- if (numBytes != 32 && !leftAligned && !_padToWordBoundaries)
+ convertType(_type, _type, true);
+ if (numBytes != 32 && !leftAligned && !_padToWords)
// shift the value accordingly before storing
- m_context << (u256(1) << ((32 - numBytes) * 8)) << Instruction::MUL;
+ leftShiftNumberOnStack((32 - numBytes) * 8);
}
return numBytes;
}