forked from vyperlang/vyper
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'feat/stack2mem' into feat/dft_upgrade
- Loading branch information
Showing
13 changed files
with
325 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import pytest | ||
|
||
from vyper.venom.mem_allocator import MemoryAllocator | ||
|
||
MEM_BLOCK_ADDRESS = 0x1000 | ||
|
||
|
||
@pytest.fixture | ||
def allocator(): | ||
return MemoryAllocator(1024, MEM_BLOCK_ADDRESS) | ||
|
||
|
||
def test_initial_state(allocator): | ||
assert allocator.get_free_memory() == 1024 | ||
assert allocator.get_allocated_memory() == 0 | ||
|
||
|
||
def test_single_allocation(allocator): | ||
addr = allocator.allocate(256) | ||
assert addr == MEM_BLOCK_ADDRESS | ||
assert allocator.get_free_memory() == 768 | ||
assert allocator.get_allocated_memory() == 256 | ||
|
||
|
||
def test_multiple_allocations(allocator): | ||
addr1 = allocator.allocate(256) | ||
addr2 = allocator.allocate(128) | ||
addr3 = allocator.allocate(64) | ||
|
||
assert addr1 == MEM_BLOCK_ADDRESS | ||
assert addr2 == MEM_BLOCK_ADDRESS + 256 | ||
assert addr3 == MEM_BLOCK_ADDRESS + 384 | ||
assert allocator.get_free_memory() == 576 | ||
assert allocator.get_allocated_memory() == 448 | ||
|
||
|
||
def test_deallocation(allocator): | ||
addr1 = allocator.allocate(256) | ||
addr2 = allocator.allocate(128) | ||
|
||
assert allocator.deallocate(addr1) is True | ||
assert allocator.get_free_memory() == 896 | ||
assert allocator.get_allocated_memory() == 128 | ||
|
||
assert allocator.deallocate(addr2) is True | ||
assert allocator.get_free_memory() == 1024 | ||
assert allocator.get_allocated_memory() == 0 | ||
|
||
|
||
def test_allocation_after_deallocation(allocator): | ||
addr1 = allocator.allocate(256) | ||
allocator.deallocate(addr1) | ||
addr2 = allocator.allocate(128) | ||
|
||
assert addr2 == MEM_BLOCK_ADDRESS | ||
assert allocator.get_free_memory() == 896 | ||
assert allocator.get_allocated_memory() == 128 | ||
|
||
|
||
def test_out_of_memory(allocator): | ||
allocator.allocate(1000) | ||
with pytest.raises(MemoryError): | ||
allocator.allocate(100) | ||
|
||
|
||
def test_invalid_deallocation(allocator): | ||
assert allocator.deallocate(0x2000) is False | ||
|
||
|
||
def test_fragmentation_and_merging(allocator): | ||
addr1 = allocator.allocate(256) | ||
addr2 = allocator.allocate(256) | ||
addr3 = allocator.allocate(256) | ||
|
||
assert allocator.get_free_memory() == 256 | ||
assert allocator.get_allocated_memory() == 768 | ||
|
||
allocator.deallocate(addr1) | ||
assert allocator.get_free_memory() == 512 | ||
assert allocator.get_allocated_memory() == 512 | ||
|
||
allocator.deallocate(addr3) | ||
assert allocator.get_free_memory() == 768 | ||
assert allocator.get_allocated_memory() == 256 | ||
|
||
addr4 = allocator.allocate(512) | ||
assert addr4 == MEM_BLOCK_ADDRESS + 512 | ||
assert allocator.get_free_memory() == 256 | ||
assert allocator.get_allocated_memory() == 768 | ||
|
||
allocator.deallocate(addr2) | ||
assert allocator.get_free_memory() == 512 | ||
assert allocator.get_allocated_memory() == 512 | ||
|
||
allocator.deallocate(addr4) | ||
assert allocator.get_free_memory() == 1024 # All blocks merged | ||
assert allocator.get_allocated_memory() == 0 | ||
|
||
# Test if we can now allocate the entire memory | ||
addr5 = allocator.allocate(1024) | ||
assert addr5 == MEM_BLOCK_ADDRESS | ||
assert allocator.get_free_memory() == 0 | ||
assert allocator.get_allocated_memory() == 1024 | ||
|
||
|
||
def test_exact_fit_allocation(allocator): | ||
addr1 = allocator.allocate(1024) | ||
assert addr1 == MEM_BLOCK_ADDRESS | ||
assert allocator.get_free_memory() == 0 | ||
assert allocator.get_allocated_memory() == 1024 | ||
|
||
allocator.deallocate(addr1) | ||
addr2 = allocator.allocate(1024) | ||
assert addr2 == MEM_BLOCK_ADDRESS | ||
assert allocator.get_free_memory() == 0 | ||
assert allocator.get_allocated_memory() == 1024 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
from typing import List | ||
|
||
|
||
class MemoryBlock: | ||
size: int | ||
address: int | ||
is_free: bool | ||
|
||
def __init__(self, size: int, address: int): | ||
self.size = size | ||
self.address = address | ||
self.is_free = True | ||
|
||
|
||
class MemoryAllocator: | ||
total_size: int | ||
start_address: int | ||
blocks: List[MemoryBlock] | ||
|
||
def __init__(self, total_size: int, start_address: int): | ||
self.total_size = total_size | ||
self.start_address = start_address | ||
self.blocks = [MemoryBlock(total_size, 0)] | ||
|
||
def allocate(self, size: int) -> int: | ||
# print(f"Allocating {size} bytes with free memory {self.get_free_memory()}") | ||
for block in self.blocks: | ||
if block.is_free and block.size >= size: | ||
if block.size > size: | ||
new_block = MemoryBlock(block.size - size, block.address + size) | ||
self.blocks.insert(self.blocks.index(block) + 1, new_block) | ||
block.size = size | ||
block.is_free = False | ||
return self.start_address + block.address | ||
raise MemoryError( | ||
f"Memory allocation failed for size {size} with free memory {self.get_free_memory()}" | ||
) | ||
|
||
def deallocate(self, address: int) -> bool: | ||
relative_address = address - self.start_address | ||
for block in self.blocks: | ||
if block.address == relative_address: | ||
block.is_free = True | ||
self._merge_adjacent_free_blocks() | ||
return True | ||
return False # invalid address | ||
|
||
def _merge_adjacent_free_blocks(self) -> None: | ||
i = 0 | ||
while i < len(self.blocks) - 1: | ||
if self.blocks[i].is_free and self.blocks[i + 1].is_free: | ||
self.blocks[i].size += self.blocks[i + 1].size | ||
self.blocks.pop(i + 1) | ||
else: | ||
i += 1 | ||
|
||
def get_free_memory(self) -> int: | ||
return sum(block.size for block in self.blocks if block.is_free) | ||
|
||
def get_allocated_memory(self) -> int: | ||
return sum(block.size for block in self.blocks if not block.is_free) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
from vyper.venom.basicblock import IRInstruction, IRLiteral | ||
from vyper.venom.passes.base_pass import IRPass | ||
|
||
|
||
class AllocaElimination(IRPass): | ||
""" | ||
This pass eliminates alloca instructions by allocating memory for them | ||
""" | ||
|
||
def run_pass(self): | ||
for bb in self.function.get_basic_blocks(): | ||
for inst in bb.instructions: | ||
if inst.opcode == "alloca": | ||
self._process_alloca(inst) | ||
|
||
def _process_alloca(self, inst: IRInstruction): | ||
offset, _size = inst.operands | ||
address = inst.parent.parent._mem_allocator.allocate(_size.value) | ||
inst.opcode = "store" | ||
inst.operands = [IRLiteral(address)] | ||
# print(f"Allocated address {address} for alloca {_size.value}") |
Oops, something went wrong.