Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions liblll/CodeFragment.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -523,14 +523,30 @@ void CodeFragment::constructOperation(sp::utree const& _t, CompilerState& _s)
requireSize(1);
requireDeposit(0, 1);

m_asm.append(Instruction::MSIZE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a comment here explaining the rules:

  • returns msize prior to expansion
  • doesn't allocate in case of N = 0
  • rounds N to a multiple of 32
  • uses MLOAD to expand, which keeps memory untouched

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

m_asm.append(u256(0));
// (alloc N):
// - Evaluates to (msize) before the allocation - the start of the allocated memory
// - Does not allocate memory when N is zero
// - Size of memory allocated is N bytes rounded up to a multiple of 32
// - Uses MLOAD to expand MSIZE to avoid modifying memory.

auto end = m_asm.newTag();
m_asm.append(Instruction::MSIZE); // Result will be original top of memory
m_asm.append(code[0].m_asm, 1); // The alloc argument N
m_asm.append(Instruction::DUP1);
m_asm.append(Instruction::ISZERO);// (alloc 0) does not change MSIZE
m_asm.appendJumpI(end);
m_asm.append(u256(1));
m_asm.append(code[0].m_asm, 1);
m_asm.append(Instruction::MSIZE);
m_asm.append(Instruction::DUP2); // Copy N
m_asm.append(Instruction::SUB); // N-1
m_asm.append(u256(0x1f)); // Bit mask
m_asm.append(Instruction::NOT); // Invert
m_asm.append(Instruction::AND); // Align N-1 on 32 byte boundary
m_asm.append(Instruction::MSIZE); // MSIZE is cheap
m_asm.append(Instruction::ADD);
m_asm.append(Instruction::SUB);
m_asm.append(Instruction::MSTORE8);
m_asm.append(Instruction::MLOAD); // Updates MSIZE
m_asm.append(Instruction::POP); // Discard the result of the MLOAD
m_asm.append(end);
m_asm.append(Instruction::POP); // Discard duplicate N

_s.usedAlloc = true;
}
Expand Down
2 changes: 2 additions & 0 deletions liblll/CompilerState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ void CompilerState::populateStandard()
{
static const string s = "{"
"(def 'panic () (asm INVALID))"
// Alternative macro version of alloc, which is currently implemented in the parser
// "(def 'alloc (n) (raw (msize) (when n (pop (mload (+ (msize) (& (- n 1) (~ 0x1f))))))))"
"(def 'allgas (- (gas) 21))"
"(def 'send (to value) (call allgas to value 0 0 0 0))"
"(def 'send (gaslimit to value) (call gaslimit to value 0 0 0 0))"
Expand Down
55 changes: 55 additions & 0 deletions test/liblll/EndToEndTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,61 @@ BOOST_AUTO_TEST_CASE(send_three_args)
BOOST_CHECK(balanceAt(Address(0xdead)) == 42);
}

// Regression test for edge case that previously failed
BOOST_AUTO_TEST_CASE(alloc_zero)
{
char const* sourceCode = R"(
(returnlll
(seq
(mstore 0x00 (~ 0))
(alloc 0)
(return 0x00 0x20)))
)";
compileAndRun(sourceCode);
BOOST_CHECK(callFallback() == encodeArgs(u256(-1)));
}

BOOST_AUTO_TEST_CASE(alloc_size)
{
char const* sourceCode = R"(
(returnlll
(seq
(mstore 0x00 0) ; reserve space for the result of the alloc
(mstore 0x00 (alloc (calldataload 0x04)))
(return (- (msize) (mload 0x00)))))
)";
compileAndRun(sourceCode);
BOOST_CHECK(callContractFunction("test()", 0) == encodeArgs(u256(0)));
BOOST_CHECK(callContractFunction("test()", 1) == encodeArgs(u256(32)));
BOOST_CHECK(callContractFunction("test()", 32) == encodeArgs(u256(32)));
BOOST_CHECK(callContractFunction("test()", 33) == encodeArgs(u256(64)));
}

BOOST_AUTO_TEST_CASE(alloc_start)
{
char const* sourceCode = R"(
(returnlll
(seq
(mstore 0x40 0) ; Set initial MSIZE to 0x60
(return (alloc 1))))
)";
compileAndRun(sourceCode);
BOOST_CHECK(callFallback() == encodeArgs(96));
}

BOOST_AUTO_TEST_CASE(alloc_with_variable)
{
char const* sourceCode = R"(
(returnlll
(seq
(set 'x (alloc 1))
(mstore8 @x 42) ; ASCII '*'
(return @x 0x20)))
)";
compileAndRun(sourceCode);
BOOST_CHECK(callFallback() == encodeArgs("*"));
}

BOOST_AUTO_TEST_CASE(msg_six_args)
{
char const* sourceCode = R"(
Expand Down