본문으로 건너뛰기
블록체인Mar 28, 2026

Deep EVM #6: Yul 메모리 관리 — mstore, mload, 프리 메모리 포인터

OS
Open Soft Team

Engineering Team

가비지 컬렉터 없는 메모리 관리

Yul에서는 당신이 메모리 관리자입니다. 할당자도, 가비지 컬렉터도, 경계 검사도 없습니다. 메모리는 MLOAD와 MSTORE를 사용하여 읽고 쓰는 평면 바이트 배열입니다. 잘못된 오프셋에 쓰면 자체 데이터가 손상됩니다. 초기화되지 않은 오프셋에서 읽으면 쓰레기 값을 얻습니다.

이 수준의 제어가 바로 Yul을 가스 최적화에 강력하게 만드는 것이며 — 또한 위험하게 만드는 것입니다.

MSTORE, MLOAD, MSTORE8

세 옵코드가 메모리 접근을 처리합니다:

mstore(0x00, 0xdeadbeef)  // 오프셋 0에 32바이트 저장
let value := mload(0x00)  // 오프셋 0에서 32바이트 읽기
mstore8(0x00, 0xff)       // 오프셋 0에 1바이트 저장

프리 메모리 포인터 (0x40)

Solidity는 오프셋 0x40의 프리 메모리 포인터를 0x80으로 초기화합니다. 사용 가능한 메모리는 0x80부터 시작합니다.

Yul에서 메모리 할당

function allocate(size) -> ptr {
    ptr := mload(0x40)
    mstore(0x40, add(ptr, size))
}

Yul에서 ABI 인코딩된 콜데이터 구축

가장 흔한 Yul 작업 중 하나는 외부 호출을 위한 콜데이터 구축입니다:

function doTransfer(token, to, amount) {
    mstore(0x00, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
    mstore(0x04, to)
    mstore(0x24, amount)
    let success := call(gas(), token, 0, 0x00, 0x44, 0x00, 0x20)
    if iszero(and(success, or(
        iszero(returndatasize()),
        and(gt(returndatasize(), 31), mload(0x00))
    ))) { revert(0, 0) }
}

메모리 효율적 해싱

function getMappingSlot(key, baseSlot) -> slot {
    mstore(0x00, key)
    mstore(0x20, baseSlot)
    slot := keccak256(0x00, 0x40)
}

Returndata 포워딩 패턴

let success := call(gas(), target, value, inOffset, inSize, 0, 0)
let size := returndatasize()
returndatacopy(0x00, 0x00, size)
switch success
case 0 { revert(0x00, size) }
default { return(0x00, size) }

결론

Yul의 메모리 관리는 수동적이고 정밀하며 강력합니다. 이 글의 패턴들 — 스크래치 공간 사용, 콜데이터 구축, 해시 계산, returndata 포워딩 — 은 모든 가스 최적화 스마트 컨트랙트의 기본 구성 요소입니다.