aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/codegen
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/codegen')
-rw-r--r--libsolidity/codegen/ArrayUtils.cpp49
-rw-r--r--libsolidity/codegen/ArrayUtils.h6
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp30
-rw-r--r--libsolidity/codegen/CompilerUtils.h7
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp4
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp29
6 files changed, 102 insertions, 23 deletions
diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp
index 4703fc1f..0fe66d2d 100644
--- a/libsolidity/codegen/ArrayUtils.cpp
+++ b/libsolidity/codegen/ArrayUtils.cpp
@@ -774,6 +774,55 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const
);
}
+void ArrayUtils::incrementDynamicArraySize(ArrayType const& _type) const
+{
+ solAssert(_type.location() == DataLocation::Storage, "");
+ solAssert(_type.isDynamicallySized(), "");
+ if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32)
+ solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
+
+ if (_type.isByteArray())
+ {
+ // We almost always just add 2 (length of byte arrays is shifted left by one)
+ // except for the case where we transition from a short byte array
+ // to a long byte array, there we have to copy.
+ // This happens if the length is exactly 31, which means that the
+ // lowest-order byte (we actually use a mask with fewer bits) must
+ // be (31*2+0) = 62
+
+ m_context.appendInlineAssembly(R"({
+ let data := sload(ref)
+ let shifted_length := and(data, 63)
+ // We have to copy if length is exactly 31, because that marks
+ // the transition between in-place and out-of-place storage.
+ switch shifted_length
+ case 62
+ {
+ mstore(0, ref)
+ let data_area := keccak256(0, 0x20)
+ sstore(data_area, and(data, not(0xff)))
+ // New length is 32, encoded as (32 * 2 + 1)
+ sstore(ref, 65)
+ // Replace ref variable by new length
+ ref := 32
+ }
+ default
+ {
+ sstore(ref, add(data, 2))
+ // Replace ref variable by new length
+ if iszero(and(data, 1)) { data := shifted_length }
+ ref := add(div(data, 2), 1)
+ }
+ })", {"ref"});
+ }
+ else
+ m_context.appendInlineAssembly(R"({
+ let new_length := add(sload(ref), 1)
+ sstore(ref, new_length)
+ ref := new_length
+ })", {"ref"});
+}
+
void ArrayUtils::clearStorageLoop(TypePointer const& _type) const
{
m_context.callLowLevelFunction(
diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h
index f3ddc4ee..99786397 100644
--- a/libsolidity/codegen/ArrayUtils.h
+++ b/libsolidity/codegen/ArrayUtils.h
@@ -67,6 +67,12 @@ public:
/// Stack pre: reference (excludes byte offset) new_length
/// Stack post:
void resizeDynamicArray(ArrayType const& _type) const;
+ /// Increments the size of a dynamic array by one.
+ /// Does not touch the new data element. In case of a byte array, this might move the
+ /// data.
+ /// Stack pre: reference (excludes byte offset)
+ /// Stack post: new_length
+ void incrementDynamicArraySize(ArrayType const& _type) const;
/// Appends a loop that clears a sequence of storage slots of the given type (excluding end).
/// Stack pre: end_ref start_ref
/// Stack post: end_ref
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index deaef017..79aef7b0 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -21,6 +21,7 @@
*/
#include <libsolidity/codegen/CompilerUtils.h>
+
#include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/ArrayUtils.h>
#include <libsolidity/codegen/LValue.h>
@@ -39,11 +40,17 @@ namespace solidity
const unsigned CompilerUtils::dataStartOffset = 4;
const size_t CompilerUtils::freeMemoryPointer = 64;
+const size_t CompilerUtils::zeroPointer = CompilerUtils::freeMemoryPointer + 32;
+const size_t CompilerUtils::generalPurposeMemoryStart = CompilerUtils::zeroPointer + 32;
const unsigned CompilerUtils::identityContractAddress = 4;
+static_assert(CompilerUtils::freeMemoryPointer >= 64, "Free memory pointer must not overlap with scratch area.");
+static_assert(CompilerUtils::zeroPointer >= CompilerUtils::freeMemoryPointer + 32, "Zero pointer must not overlap with free memory pointer.");
+static_assert(CompilerUtils::generalPurposeMemoryStart >= CompilerUtils::zeroPointer + 32, "General purpose memory must not overlap with zero area.");
+
void CompilerUtils::initialiseFreeMemoryPointer()
{
- m_context << u256(freeMemoryPointer + 32);
+ m_context << u256(generalPurposeMemoryStart);
storeFreeMemoryPointer();
}
@@ -1051,6 +1058,13 @@ void CompilerUtils::pushZeroValue(Type const& _type)
return;
}
solAssert(referenceType->location() == DataLocation::Memory, "");
+ if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
+ if (arrayType->isDynamicallySized())
+ {
+ // Push a memory location that is (hopefully) always zero.
+ pushZeroPointer();
+ return;
+ }
TypePointer type = _type.shared_from_this();
m_context.callLowLevelFunction(
@@ -1071,13 +1085,8 @@ void CompilerUtils::pushZeroValue(Type const& _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)
+ solAssert(!arrayType->isDynamicallySized(), "");
+ if (arrayType->length() > 0)
{
_context << arrayType->length() << Instruction::SWAP1;
// stack: items_to_do memory_pos
@@ -1094,6 +1103,11 @@ void CompilerUtils::pushZeroValue(Type const& _type)
);
}
+void CompilerUtils::pushZeroPointer()
+{
+ m_context << u256(zeroPointer);
+}
+
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
{
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.baseStackOffsetOfVariable(_variable));
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index 389673ef..a32c5c6e 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -210,6 +210,9 @@ public:
/// Creates a zero-value for the given type and puts it onto the stack. This might allocate
/// memory for memory references.
void pushZeroValue(Type const& _type);
+ /// Pushes a pointer to the stack that points to a (potentially shared) location in memory
+ /// that always contains a zero. It is not allowed to write there.
+ void pushZeroPointer();
/// Moves the value that is at the top of the stack to a stack variable.
void moveToStackVariable(VariableDeclaration const& _variable);
@@ -255,6 +258,10 @@ public:
/// Position of the free-memory-pointer in memory;
static const size_t freeMemoryPointer;
+ /// Position of the memory slot that is always zero.
+ static const size_t zeroPointer;
+ /// Starting offset for memory available to the user (aka the contract).
+ static const size_t generalPurposeMemoryStart;
private:
/// Address of the precompiled identity contract.
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index ebd9139a..d3a7e4ea 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -157,8 +157,8 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c
);
solAssert(baseContract, "");
- if (!m_baseArguments.count(baseContract->constructor()) && !base->arguments().empty())
- m_baseArguments[baseContract->constructor()] = &base->arguments();
+ if (!m_baseArguments.count(baseContract->constructor()) && base->arguments() && !base->arguments()->empty())
+ m_baseArguments[baseContract->constructor()] = base->arguments();
}
}
// Initialization of state variables in base-to-derived order.
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 76aa6843..57d49ac6 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -821,24 +821,27 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
function.kind() == FunctionType::Kind::ArrayPush ?
make_shared<ArrayType>(DataLocation::Storage, paramType) :
make_shared<ArrayType>(DataLocation::Storage);
- // get the current length
- ArrayUtils(m_context).retrieveLength(*arrayType);
- m_context << Instruction::DUP1;
- // stack: ArrayReference currentLength currentLength
- m_context << u256(1) << Instruction::ADD;
- // stack: ArrayReference currentLength newLength
- m_context << Instruction::DUP3 << Instruction::DUP2;
- ArrayUtils(m_context).resizeDynamicArray(*arrayType);
- m_context << Instruction::SWAP2 << Instruction::SWAP1;
- // stack: newLength ArrayReference oldLength
- ArrayUtils(m_context).accessIndex(*arrayType, false);
- // stack: newLength storageSlot slotOffset
+ // stack: ArrayReference
arguments[0]->accept(*this);
+ TypePointer const& argType = arguments[0]->annotation().type;
+ // stack: ArrayReference argValue
+ utils().moveToStackTop(argType->sizeOnStack(), 1);
+ // stack: argValue ArrayReference
+ m_context << Instruction::DUP1;
+ ArrayUtils(m_context).incrementDynamicArraySize(*arrayType);
+ // stack: argValue ArrayReference newLength
+ m_context << Instruction::SWAP1;
+ // stack: argValue newLength ArrayReference
+ m_context << u256(1) << Instruction::DUP3 << Instruction::SUB;
+ // stack: argValue newLength ArrayReference (newLength-1)
+ ArrayUtils(m_context).accessIndex(*arrayType, false);
+ // stack: argValue newLength storageSlot slotOffset
+ utils().moveToStackTop(3, argType->sizeOnStack());
// stack: newLength storageSlot slotOffset argValue
TypePointer type = arguments[0]->annotation().type->closestTemporaryType(arrayType->baseType());
solAssert(type, "");
- utils().convertType(*arguments[0]->annotation().type, *type);
+ utils().convertType(*argType, *type);
utils().moveToStackTop(1 + type->sizeOnStack());
utils().moveToStackTop(1 + type->sizeOnStack());
// stack: newLength argValue storageSlot slotOffset