aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity/codegen/ExpressionCompiler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity/codegen/ExpressionCompiler.cpp')
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp54
1 files changed, 47 insertions, 7 deletions
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 4bcc1fa9..0470c3ec 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -866,6 +866,19 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
StorageByteArrayElement(m_context).storeValue(*type, _functionCall.location(), true);
break;
}
+ case FunctionType::Kind::ArrayPop:
+ {
+ _functionCall.expression().accept(*this);
+ solAssert(function.parameterTypes().empty(), "");
+
+ ArrayType const& arrayType = dynamic_cast<ArrayType const&>(
+ *dynamic_cast<MemberAccess const&>(_functionCall.expression()).expression().annotation().type
+ );
+ solAssert(arrayType.dataStoredIn(DataLocation::Storage), "");
+
+ ArrayUtils(m_context).popStorageArrayElement(arrayType);
+ break;
+ }
case FunctionType::Kind::ObjectCreation:
{
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_functionCall.annotation().type);
@@ -1345,11 +1358,13 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
break;
}
}
- else if (member == "push")
+ else if (member == "push" || member == "pop")
{
solAssert(
- type.isDynamicallySized() && type.location() == DataLocation::Storage,
- "Tried to use .push() on a non-dynamically sized array"
+ type.isDynamicallySized() &&
+ type.location() == DataLocation::Storage &&
+ type.category() == Type::Category::Array,
+ "Tried to use ." + member + "() on a non-dynamically sized array"
);
}
else
@@ -1722,11 +1737,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: