[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-deep-evm-10-huff-seutaeg-gwanli-dup-swap-gisul":3},{"article":4,"author":55},{"id":5,"category_id":6,"title":7,"slug":8,"excerpt":9,"content_md":10,"content_html":11,"locale":12,"author_id":13,"published":14,"published_at":15,"meta_title":16,"meta_description":17,"focus_keyword":18,"og_image":19,"canonical_url":19,"robots_meta":20,"created_at":15,"updated_at":15,"tags":21,"category_name":35,"related_articles":36},"d5000000-0000-0000-0000-000000000110","a0000000-0000-0000-0000-000000000052","Deep EVM #10: Huff 스택 관리 — takes(), returns(), dup\u002Fswap의 기술","deep-evm-10-huff-seutaeg-gwanli-dup-swap-gisul","EVM 스택 머신을 마스터하세요: 스택 상태를 시각화하고, dup1-16과 swap1-16을 효과적으로 사용하며, 값 유지, 회전, 정리를 위한 패턴을 구축합니다.","## 스택 머신 멘탈 모델\n\nEVM은 스택 머신입니다. 레지스터도, 명명된 변수도 없습니다 — 각 요소가 32바이트 워드인 1024 슬롯 깊이의 후입선출(LIFO) 스택만 있습니다. 모든 옵코드는 이 스택에 항목을 푸시하거나, 팝하거나, 재배열합니다. 현재 스택 상태를 머릿속에 유지할 수 없다면, 버그가 있는 바이트코드를 생성하게 됩니다. 이 기사는 그 멘탈 모델을 구축하는 것에 관한 것입니다.\n\n### 표기법 규칙\n\n이 기사 전반(그리고 Huff 주석에서)에서 스택 상태를 대괄호로 표현하며, 가장 왼쪽 항목이 스택의 상단입니다:\n\n```\n\u002F\u002F [top, second, third, ..., bottom]\n0x01  \u002F\u002F [1]\n0x02  \u002F\u002F [2, 1]\nadd   \u002F\u002F [3]\n```\n\n모든 Huff 매크로는 각 옵코드 뒤에 스택 주석이 있어야 합니다. 이것은 선택이 아닙니다 — 정확성을 감사하는 유일한 방법입니다.\n\n## DUP: 스택 항목 복제\n\nEVM은 `DUP1`부터 `DUP16`까지 제공합니다. `DUPn`은 상단에서 n번째 항목을 복사하여 스택에 푸시합니다. 스택이 1만큼 증가합니다.\n\n```huff\n\u002F\u002F 스택: [a, b, c, d]\ndup1   \u002F\u002F [a, a, b, c, d]       — 상단 복사\ndup3   \u002F\u002F [c, a, a, b, c, d]    — 상단에서 3번째 복사\n```\n\n가스 비용: 모든 DUPn에 대해 3 가스. EVM에서 가장 저렴한 연산 중 하나입니다.\n\n### 언제 DUP를 사용해야 하는가\n\nDUP는 **비파괴적 읽기**를 위한 도구입니다. 많은 옵코드가 인수를 소비하므로(ADD는 둘을 팝하고, 하나를 푸시), 나중에 값이 다시 필요하면 소비 옵코드에 넘기기 전에 DUP하세요.\n\n```huff\n#define macro SAFE_SUB() = takes(2) returns(1) {\n    \u002F\u002F takes: [a, b] — a - b 계산, b > a이면 되돌림\n    dup2 dup2       \u002F\u002F [a, b, a, b]\n    lt              \u002F\u002F [a \u003C b?, a, b]\n    revert_underflow jumpi  \u002F\u002F [a, b]\n    sub             \u002F\u002F [a - b]\n    done jump\n    revert_underflow:\n        0x00 0x00 revert\n    done:\n}\n```\n\n`dup2 dup2`에 주목하세요 — `lt`가 소비할 `a`와 `b` 모두를 복제합니다. `sub`에 원본이 여전히 필요하기 때문입니다.\n\n## SWAP: 스택 재배열\n\nEVM은 `SWAP1`부터 `SWAP16`까지 제공합니다. `SWAPn`은 상단 항목을 (n+1)번째 항목과 교환합니다. 스택 크기는 동일하게 유지됩니다.\n\n```huff\n\u002F\u002F 스택: [a, b, c, d]\nswap1  \u002F\u002F [b, a, c, d]          — 상단을 2번째와 교환\nswap3  \u002F\u002F [d, a, c, b]          — 상단을 4번째와 교환\n```\n\n가스 비용: 모든 SWAPn에 대해 3 가스.\n\n### 언제 SWAP를 사용해야 하는가\n\nSWAP는 특정 순서를 기대하는 옵코드에 대한 인수를 재정렬합니다. 예를 들어, `SUB`는 `stack[0] - stack[1]`을 계산합니다. 값이 잘못된 순서라면:\n\n```huff\n\u002F\u002F 스택: [b, a]  — 하지만 a - b를 원함\nswap1   \u002F\u002F [a, b]\nsub     \u002F\u002F [a - b]\n```\n\n## 깊이 16 제한\n\nDUP와 SWAP는 16 깊이까지만 도달합니다. 값이 17번째 위치 또는 더 깊으면 단일 옵코드로 접근할 수 없습니다. 이것은 EVM의 하드 제약입니다.\n\n**깊은 스택을 위한 전략:**\n\n1. **로직을 재구성**하여 필요한 값을 상단 근처에 유지. 최선의 접근법입니다.\n2. **메모리를 스크래치 공간으로 사용.** MSTORE로 값을 저장하고, 나중에 MLOAD로 검색. DUP의 3 가스 대비 3+3=6 가스이지만 깊이 장벽을 돌파합니다.\n3. **매크로를 더 작은 매크로로 분할**하여 각각 더 적은 스택 항목에서 작동하도록 합니다.\n\n```huff\n#define macro STASH_TO_MEMORY() = takes(1) returns(0) {\n    \u002F\u002F takes: [value]\n    0x80 mstore     \u002F\u002F []  — 0x80에 보관 (스크래치 공간)\n}\n\n#define macro RECALL_FROM_MEMORY() = takes(0) returns(1) {\n    0x80 mload      \u002F\u002F [value]\n}\n```\n\nMEV 컨트랙트에서 우리는 종종 `0x80..0xc0`을 스택이 16을 넘어가는 값을 위한 스크래치 영역으로 예약합니다.\n\n## 일반적인 패턴\n\n### 패턴 1: 소비 연산을 통해 값 유지하기\n\n`[x]`가 있고 `x`를 소비하는 옵코드를 호출해야 하지만 이후에도 `x`가 필요한 경우.\n\n```huff\n\u002F\u002F 원하는 것: x의 해시를 계산하되, x 유지\n\u002F\u002F 스택: [x]\ndup1        \u002F\u002F [x, x]\n0x00 mstore \u002F\u002F [x]  — memory[0] = x\n0x20 0x00   \u002F\u002F [0, 32, x]\nkeccak256   \u002F\u002F [hash, x]\n```\n\n### 패턴 2: 세 항목 회전\n\n`[a, b, c]`가 있고 `[c, a, b]`가 필요한 경우:\n\n```huff\nswap2       \u002F\u002F [c, b, a]\nswap1       \u002F\u002F [c, a, b]\n```\n\n2 옵코드, 6 가스. EVM에는 단일 옵코드 회전이 없습니다.\n\n`[a, b, c]`가 있고 `[b, c, a]`가 필요한 경우:\n\n```huff\nswap1       \u002F\u002F [b, a, c]\nswap2       \u002F\u002F [b, c, a]\n```\n\n### 패턴 3: 불필요한 스택 항목 정리\n\n계산 후 추가 항목이 있을 수 있습니다. `pop`(2 가스)으로 폐기:\n\n```huff\n\u002F\u002F 스택: [result, garbage1, garbage2]\nswap2       \u002F\u002F [garbage2, garbage1, result]\npop         \u002F\u002F [garbage1, result]\npop         \u002F\u002F [result]\n```\n\n또는 더 효율적으로:\n\n```huff\n\u002F\u002F 스택: [result, garbage1, garbage2]\nswap1 pop   \u002F\u002F [result, garbage2]\nswap1 pop   \u002F\u002F [result]\n```\n\n### 패턴 4: 쌍 복제\n\n상위 두 항목을 복사해야 합니다:\n\n```huff\n\u002F\u002F 스택: [a, b]\ndup2        \u002F\u002F [b, a, b]\ndup2        \u002F\u002F [a, b, a, b]\n```\n\n역순으로 DUP한다는 점에 주목하세요. `dup2`가 먼저 `b`(위치 2)를 복사하고, 그 다음 `dup2`가 `a`(스택이 증가했으므로 이제 위치 2)를 복사합니다. 이 패턴은 비교-전-산술 코드에서 끊임없이 나타납니다.\n\n## 스택 시각화 규율\n\nHuff를 작성할 때 이 규율을 채택하세요:\n\n1. **모든 줄에 주석** — 실행 후 스택 상태.\n2. **takes\u002Freturns 검증** — 진입과 종료 시 스택 항목 수를 세기.\n3. **모든 분기 추적** — 각 JUMPI에서, 취한 경로와 취하지 않은 경로 모두 스택을 유효한 상태로 남겨야 합니다.\n4. **스택 드리프트 주의** — 루프 본문이 푸시와 팝을 완벽하게 균형 잡지 못하면, 각 반복에서 스택이 증가하거나 축소됩니다.\n\n```huff\n#define macro TRANSFER() = takes(3) returns(0) {\n    \u002F\u002F takes: [amount, from, to]\n\n    \u002F\u002F 발신자 잔액 로드\n    dup2                    \u002F\u002F [from, amount, from, to]\n    sload                   \u002F\u002F [bal_from, amount, from, to]\n\n    \u002F\u002F 충분한 잔액 확인\n    dup1 dup3               \u002F\u002F [amount, bal_from, bal_from, amount, from, to]\n    gt                      \u002F\u002F [amount > bal_from?, bal_from, amount, from, to]\n    insufficient jumpi      \u002F\u002F [bal_from, amount, from, to]\n\n    \u002F\u002F 발신자에서 차감\n    dup2                    \u002F\u002F [amount, bal_from, amount, from, to]\n    swap1 sub               \u002F\u002F [bal_from - amount, amount, from, to]\n    dup3                    \u002F\u002F [from, new_bal, amount, from, to]\n    sstore                  \u002F\u002F [amount, from, to]\n\n    \u002F\u002F 수신자에 추가\n    dup3                    \u002F\u002F [to, amount, from, to]\n    sload                   \u002F\u002F [bal_to, amount, from, to]\n    add                     \u002F\u002F [new_bal_to, from, to]\n    swap2                   \u002F\u002F [to, from, new_bal_to]\n    sstore                  \u002F\u002F [from]\n    pop                     \u002F\u002F []\n    done jump\n\n    insufficient:\n        0x00 0x00 revert\n    done:\n}\n```\n\n모든 줄에 스택 주석이 있습니다. 모든 분기가 깔끔하게 종료됩니다. 이것이 올바른 Huff를 작성하는 유일한 방법입니다.\n\n## 스택 오류 디버깅\n\nHuff에서 가장 흔한 버그:\n\n1. **스택 언더플로** — 빈 스택에서 팝. EVM이 런타임에 되돌림. 원인: 잘못된 `takes` 수 또는 누락된 DUP.\n2. **JUMP 시 스택 불균형** — 두 개의 다른 경로에서 도달한 JUMPDEST가 서로 다른 스택 상태를 기대. 컴파일러가 이를 잡지 못합니다.\n3. **DUP\u002FSWAP 오프바이원** — 이전에 추가 푸시가 있었을 때 `dup3` 대 `dup4`. 이것이 스택 주석이 필수인 이유입니다.\n\n`huffc`에는 기본 스택 분석을 수행하는 `--stack-check` 플래그가 있습니다:\n\n```bash\nhuffc src\u002FContract.huff -r --stack-check\n```\n\n명확한 언더플로는 잡지만 모든 동적 점프 경로를 추적할 수는 없습니다. 복잡한 컨트랙트의 경우 `forge debug` 또는 `evm-trace`로 실행을 수동으로 추적하세요.\n\n## 고급: 레지스터 파일로서의 스택\n\n경험 많은 Huff 개발자는 상위 약 8개의 스택 위치를 레지스터 파일로 생각합니다:\n\n```\n위치 1 (상단):    작업 레지스터 — 현재 계산\n위치 2-3:        인수 레지스터 — 다음 연산 입력\n위치 4-6:        지역 변수 레지스터 — 곧 필요한 값\n위치 7-8:        컨텍스트 레지스터 — 루프 카운터, 베이스 포인터\n위치 9+:         스필 영역 — 거의 접근하지 않음, 메모리 고려\n```\n\n이 멘탈 모델은 값을 상단으로 SWAP할지 DUP할지, 그리고 메모리로 스필할 때를 결정하는 데 도움이 됩니다.\n\n## 요약\n\n스택 관리는 Huff 개발의 핵심 기술입니다. 비파괴적 읽기를 위한 DUP, 재정렬을 위한 SWAP, 그리고 깊이 16을 넘어가는 값을 위한 메모리. 모든 줄에 스택 상태를 주석으로 달고. 모든 분기를 검증하세요. 다음 기사에서는 이러한 기술을 사용하여 압축된 점프 테이블로 O(1) 함수 디스패처를 구축합니다 — 정밀한 스택 관리가 가스 절약으로 직접 변환되는 곳입니다.","\u003Ch2 id=\"\">스택 머신 멘탈 모델\u003C\u002Fh2>\n\u003Cp>EVM은 스택 머신입니다. 레지스터도, 명명된 변수도 없습니다 — 각 요소가 32바이트 워드인 1024 슬롯 깊이의 후입선출(LIFO) 스택만 있습니다. 모든 옵코드는 이 스택에 항목을 푸시하거나, 팝하거나, 재배열합니다. 현재 스택 상태를 머릿속에 유지할 수 없다면, 버그가 있는 바이트코드를 생성하게 됩니다. 이 기사는 그 멘탈 모델을 구축하는 것에 관한 것입니다.\u003C\u002Fp>\n\u003Ch3>표기법 규칙\u003C\u002Fh3>\n\u003Cp>이 기사 전반(그리고 Huff 주석에서)에서 스택 상태를 대괄호로 표현하며, 가장 왼쪽 항목이 스택의 상단입니다:\u003C\u002Fp>\n\u003Cpre>\u003Ccode>\u002F\u002F [top, second, third, ..., bottom]\n0x01  \u002F\u002F [1]\n0x02  \u002F\u002F [2, 1]\nadd   \u002F\u002F [3]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>모든 Huff 매크로는 각 옵코드 뒤에 스택 주석이 있어야 합니다. 이것은 선택이 아닙니다 — 정확성을 감사하는 유일한 방법입니다.\u003C\u002Fp>\n\u003Ch2 id=\"dup\">DUP: 스택 항목 복제\u003C\u002Fh2>\n\u003Cp>EVM은 \u003Ccode>DUP1\u003C\u002Fcode>부터 \u003Ccode>DUP16\u003C\u002Fcode>까지 제공합니다. \u003Ccode>DUPn\u003C\u002Fcode>은 상단에서 n번째 항목을 복사하여 스택에 푸시합니다. 스택이 1만큼 증가합니다.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">\u002F\u002F 스택: [a, b, c, d]\ndup1   \u002F\u002F [a, a, b, c, d]       — 상단 복사\ndup3   \u002F\u002F [c, a, a, b, c, d]    — 상단에서 3번째 복사\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>가스 비용: 모든 DUPn에 대해 3 가스. EVM에서 가장 저렴한 연산 중 하나입니다.\u003C\u002Fp>\n\u003Ch3>언제 DUP를 사용해야 하는가\u003C\u002Fh3>\n\u003Cp>DUP는 \u003Cstrong>비파괴적 읽기\u003C\u002Fstrong>를 위한 도구입니다. 많은 옵코드가 인수를 소비하므로(ADD는 둘을 팝하고, 하나를 푸시), 나중에 값이 다시 필요하면 소비 옵코드에 넘기기 전에 DUP하세요.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">#define macro SAFE_SUB() = takes(2) returns(1) {\n    \u002F\u002F takes: [a, b] — a - b 계산, b &gt; a이면 되돌림\n    dup2 dup2       \u002F\u002F [a, b, a, b]\n    lt              \u002F\u002F [a &lt; b?, a, b]\n    revert_underflow jumpi  \u002F\u002F [a, b]\n    sub             \u002F\u002F [a - b]\n    done jump\n    revert_underflow:\n        0x00 0x00 revert\n    done:\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Ccode>dup2 dup2\u003C\u002Fcode>에 주목하세요 — \u003Ccode>lt\u003C\u002Fcode>가 소비할 \u003Ccode>a\u003C\u002Fcode>와 \u003Ccode>b\u003C\u002Fcode> 모두를 복제합니다. \u003Ccode>sub\u003C\u002Fcode>에 원본이 여전히 필요하기 때문입니다.\u003C\u002Fp>\n\u003Ch2 id=\"swap\">SWAP: 스택 재배열\u003C\u002Fh2>\n\u003Cp>EVM은 \u003Ccode>SWAP1\u003C\u002Fcode>부터 \u003Ccode>SWAP16\u003C\u002Fcode>까지 제공합니다. \u003Ccode>SWAPn\u003C\u002Fcode>은 상단 항목을 (n+1)번째 항목과 교환합니다. 스택 크기는 동일하게 유지됩니다.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">\u002F\u002F 스택: [a, b, c, d]\nswap1  \u002F\u002F [b, a, c, d]          — 상단을 2번째와 교환\nswap3  \u002F\u002F [d, a, c, b]          — 상단을 4번째와 교환\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>가스 비용: 모든 SWAPn에 대해 3 가스.\u003C\u002Fp>\n\u003Ch3>언제 SWAP를 사용해야 하는가\u003C\u002Fh3>\n\u003Cp>SWAP는 특정 순서를 기대하는 옵코드에 대한 인수를 재정렬합니다. 예를 들어, \u003Ccode>SUB\u003C\u002Fcode>는 \u003Ccode>stack[0] - stack[1]\u003C\u002Fcode>을 계산합니다. 값이 잘못된 순서라면:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">\u002F\u002F 스택: [b, a]  — 하지만 a - b를 원함\nswap1   \u002F\u002F [a, b]\nsub     \u002F\u002F [a - b]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"16\">깊이 16 제한\u003C\u002Fh2>\n\u003Cp>DUP와 SWAP는 16 깊이까지만 도달합니다. 값이 17번째 위치 또는 더 깊으면 단일 옵코드로 접근할 수 없습니다. 이것은 EVM의 하드 제약입니다.\u003C\u002Fp>\n\u003Cp>\u003Cstrong>깊은 스택을 위한 전략:\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>로직을 재구성\u003C\u002Fstrong>하여 필요한 값을 상단 근처에 유지. 최선의 접근법입니다.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>메모리를 스크래치 공간으로 사용.\u003C\u002Fstrong> MSTORE로 값을 저장하고, 나중에 MLOAD로 검색. DUP의 3 가스 대비 3+3=6 가스이지만 깊이 장벽을 돌파합니다.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>매크로를 더 작은 매크로로 분할\u003C\u002Fstrong>하여 각각 더 적은 스택 항목에서 작동하도록 합니다.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cpre>\u003Ccode class=\"language-huff\">#define macro STASH_TO_MEMORY() = takes(1) returns(0) {\n    \u002F\u002F takes: [value]\n    0x80 mstore     \u002F\u002F []  — 0x80에 보관 (스크래치 공간)\n}\n\n#define macro RECALL_FROM_MEMORY() = takes(0) returns(1) {\n    0x80 mload      \u002F\u002F [value]\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>MEV 컨트랙트에서 우리는 종종 \u003Ccode>0x80..0xc0\u003C\u002Fcode>을 스택이 16을 넘어가는 값을 위한 스크래치 영역으로 예약합니다.\u003C\u002Fp>\n\u003Ch2 id=\"\">일반적인 패턴\u003C\u002Fh2>\n\u003Ch3>패턴 1: 소비 연산을 통해 값 유지하기\u003C\u002Fh3>\n\u003Cp>\u003Ccode>[x]\u003C\u002Fcode>가 있고 \u003Ccode>x\u003C\u002Fcode>를 소비하는 옵코드를 호출해야 하지만 이후에도 \u003Ccode>x\u003C\u002Fcode>가 필요한 경우.\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">\u002F\u002F 원하는 것: x의 해시를 계산하되, x 유지\n\u002F\u002F 스택: [x]\ndup1        \u002F\u002F [x, x]\n0x00 mstore \u002F\u002F [x]  — memory[0] = x\n0x20 0x00   \u002F\u002F [0, 32, x]\nkeccak256   \u002F\u002F [hash, x]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>패턴 2: 세 항목 회전\u003C\u002Fh3>\n\u003Cp>\u003Ccode>[a, b, c]\u003C\u002Fcode>가 있고 \u003Ccode>[c, a, b]\u003C\u002Fcode>가 필요한 경우:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">swap2       \u002F\u002F [c, b, a]\nswap1       \u002F\u002F [c, a, b]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>2 옵코드, 6 가스. EVM에는 단일 옵코드 회전이 없습니다.\u003C\u002Fp>\n\u003Cp>\u003Ccode>[a, b, c]\u003C\u002Fcode>가 있고 \u003Ccode>[b, c, a]\u003C\u002Fcode>가 필요한 경우:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">swap1       \u002F\u002F [b, a, c]\nswap2       \u002F\u002F [b, c, a]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>패턴 3: 불필요한 스택 항목 정리\u003C\u002Fh3>\n\u003Cp>계산 후 추가 항목이 있을 수 있습니다. \u003Ccode>pop\u003C\u002Fcode>(2 가스)으로 폐기:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">\u002F\u002F 스택: [result, garbage1, garbage2]\nswap2       \u002F\u002F [garbage2, garbage1, result]\npop         \u002F\u002F [garbage1, result]\npop         \u002F\u002F [result]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>또는 더 효율적으로:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">\u002F\u002F 스택: [result, garbage1, garbage2]\nswap1 pop   \u002F\u002F [result, garbage2]\nswap1 pop   \u002F\u002F [result]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3>패턴 4: 쌍 복제\u003C\u002Fh3>\n\u003Cp>상위 두 항목을 복사해야 합니다:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-huff\">\u002F\u002F 스택: [a, b]\ndup2        \u002F\u002F [b, a, b]\ndup2        \u002F\u002F [a, b, a, b]\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>역순으로 DUP한다는 점에 주목하세요. \u003Ccode>dup2\u003C\u002Fcode>가 먼저 \u003Ccode>b\u003C\u002Fcode>(위치 2)를 복사하고, 그 다음 \u003Ccode>dup2\u003C\u002Fcode>가 \u003Ccode>a\u003C\u002Fcode>(스택이 증가했으므로 이제 위치 2)를 복사합니다. 이 패턴은 비교-전-산술 코드에서 끊임없이 나타납니다.\u003C\u002Fp>\n\u003Ch2 id=\"\">스택 시각화 규율\u003C\u002Fh2>\n\u003Cp>Huff를 작성할 때 이 규율을 채택하세요:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>모든 줄에 주석\u003C\u002Fstrong> — 실행 후 스택 상태.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>takes\u002Freturns 검증\u003C\u002Fstrong> — 진입과 종료 시 스택 항목 수를 세기.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>모든 분기 추적\u003C\u002Fstrong> — 각 JUMPI에서, 취한 경로와 취하지 않은 경로 모두 스택을 유효한 상태로 남겨야 합니다.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>스택 드리프트 주의\u003C\u002Fstrong> — 루프 본문이 푸시와 팝을 완벽하게 균형 잡지 못하면, 각 반복에서 스택이 증가하거나 축소됩니다.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cpre>\u003Ccode class=\"language-huff\">#define macro TRANSFER() = takes(3) returns(0) {\n    \u002F\u002F takes: [amount, from, to]\n\n    \u002F\u002F 발신자 잔액 로드\n    dup2                    \u002F\u002F [from, amount, from, to]\n    sload                   \u002F\u002F [bal_from, amount, from, to]\n\n    \u002F\u002F 충분한 잔액 확인\n    dup1 dup3               \u002F\u002F [amount, bal_from, bal_from, amount, from, to]\n    gt                      \u002F\u002F [amount &gt; bal_from?, bal_from, amount, from, to]\n    insufficient jumpi      \u002F\u002F [bal_from, amount, from, to]\n\n    \u002F\u002F 발신자에서 차감\n    dup2                    \u002F\u002F [amount, bal_from, amount, from, to]\n    swap1 sub               \u002F\u002F [bal_from - amount, amount, from, to]\n    dup3                    \u002F\u002F [from, new_bal, amount, from, to]\n    sstore                  \u002F\u002F [amount, from, to]\n\n    \u002F\u002F 수신자에 추가\n    dup3                    \u002F\u002F [to, amount, from, to]\n    sload                   \u002F\u002F [bal_to, amount, from, to]\n    add                     \u002F\u002F [new_bal_to, from, to]\n    swap2                   \u002F\u002F [to, from, new_bal_to]\n    sstore                  \u002F\u002F [from]\n    pop                     \u002F\u002F []\n    done jump\n\n    insufficient:\n        0x00 0x00 revert\n    done:\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>모든 줄에 스택 주석이 있습니다. 모든 분기가 깔끔하게 종료됩니다. 이것이 올바른 Huff를 작성하는 유일한 방법입니다.\u003C\u002Fp>\n\u003Ch2 id=\"\">스택 오류 디버깅\u003C\u002Fh2>\n\u003Cp>Huff에서 가장 흔한 버그:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Cstrong>스택 언더플로\u003C\u002Fstrong> — 빈 스택에서 팝. EVM이 런타임에 되돌림. 원인: 잘못된 \u003Ccode>takes\u003C\u002Fcode> 수 또는 누락된 DUP.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>JUMP 시 스택 불균형\u003C\u002Fstrong> — 두 개의 다른 경로에서 도달한 JUMPDEST가 서로 다른 스택 상태를 기대. 컴파일러가 이를 잡지 못합니다.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>DUP\u002FSWAP 오프바이원\u003C\u002Fstrong> — 이전에 추가 푸시가 있었을 때 \u003Ccode>dup3\u003C\u002Fcode> 대 \u003Ccode>dup4\u003C\u002Fcode>. 이것이 스택 주석이 필수인 이유입니다.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>\u003Ccode>huffc\u003C\u002Fcode>에는 기본 스택 분석을 수행하는 \u003Ccode>--stack-check\u003C\u002Fcode> 플래그가 있습니다:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">huffc src\u002FContract.huff -r --stack-check\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>명확한 언더플로는 잡지만 모든 동적 점프 경로를 추적할 수는 없습니다. 복잡한 컨트랙트의 경우 \u003Ccode>forge debug\u003C\u002Fcode> 또는 \u003Ccode>evm-trace\u003C\u002Fcode>로 실행을 수동으로 추적하세요.\u003C\u002Fp>\n\u003Ch2 id=\"\">고급: 레지스터 파일로서의 스택\u003C\u002Fh2>\n\u003Cp>경험 많은 Huff 개발자는 상위 약 8개의 스택 위치를 레지스터 파일로 생각합니다:\u003C\u002Fp>\n\u003Cpre>\u003Ccode>위치 1 (상단):    작업 레지스터 — 현재 계산\n위치 2-3:        인수 레지스터 — 다음 연산 입력\n위치 4-6:        지역 변수 레지스터 — 곧 필요한 값\n위치 7-8:        컨텍스트 레지스터 — 루프 카운터, 베이스 포인터\n위치 9+:         스필 영역 — 거의 접근하지 않음, 메모리 고려\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>이 멘탈 모델은 값을 상단으로 SWAP할지 DUP할지, 그리고 메모리로 스필할 때를 결정하는 데 도움이 됩니다.\u003C\u002Fp>\n\u003Ch2 id=\"\">요약\u003C\u002Fh2>\n\u003Cp>스택 관리는 Huff 개발의 핵심 기술입니다. 비파괴적 읽기를 위한 DUP, 재정렬을 위한 SWAP, 그리고 깊이 16을 넘어가는 값을 위한 메모리. 모든 줄에 스택 상태를 주석으로 달고. 모든 분기를 검증하세요. 다음 기사에서는 이러한 기술을 사용하여 압축된 점프 테이블로 O(1) 함수 디스패처를 구축합니다 — 정밀한 스택 관리가 가스 절약으로 직접 변환되는 곳입니다.\u003C\u002Fp>\n","ko","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:28.019963Z","Deep EVM #10: Huff 스택 관리 — dup, swap, 스택 규율","Huff에서 EVM 스택 관리 마스터하기: dup1-16, swap1-16, 깊이 16 제한, 일반적인 패턴, 스택 오류 디버깅.","huff 스택 관리 evm",null,"index, follow",[22,27,31],{"id":23,"name":24,"slug":25,"created_at":26},"c0000000-0000-0000-0000-000000000016","EVM","evm","2026-03-28T10:44:21.513630Z",{"id":28,"name":29,"slug":30,"created_at":26},"c0000000-0000-0000-0000-000000000020","Gas Optimization","gas-optimization",{"id":32,"name":33,"slug":34,"created_at":26},"c0000000-0000-0000-0000-000000000017","Huff","huff","블록체인",[37,43,49],{"id":38,"title":39,"slug":40,"excerpt":41,"locale":12,"category_name":35,"published_at":42},"d0000000-0000-0000-0000-000000000605","Ethereum 상호운용성 레이어: 55개 이상의 L2가 하나의 체인이 되는 방법","ethereum-sangho-unyongseong-layer-55-l2-hana-chain","Ethereum에는 55개 이상의 Layer 2 롤업이 있어 유동성과 사용자 경험이 파편화되어 있습니다. Ethereum 상호운용성 레이어 — 크로스 롤업 메시징, 공유 시퀀서, 베이스드 롤업을 결합하여 — 하나의 조합 가능한 네트워크로 통합하는 것을 목표로 합니다.","2026-03-28T10:44:44.895917Z",{"id":44,"title":45,"slug":46,"excerpt":47,"locale":12,"category_name":35,"published_at":48},"d0000000-0000-0000-0000-000000000604","롤업을 넘어선 ZK 증명: Ethereum에서의 검증 가능한 AI 추론","rolleob-eul-neomeo-zk-jeungmyeong-ethereum-geomjeung-ai-churon","영지식 증명은 더 이상 단순한 스케일링 도구가 아닙니다. 2026년, zkML은 온체인에서 검증 가능한 AI 추론을 가능하게 하고, ZK 코프로세서는 무거운 연산을 오프체인으로 이동시키며 온체인에서 검증하고, SP1과 Jolt 같은 새로운 증명 시스템이 이를 실용적으로 만들고 있습니다.","2026-03-28T10:44:44.890168Z",{"id":50,"title":51,"slug":52,"excerpt":53,"locale":12,"category_name":35,"published_at":54},"d0000000-0000-0000-0000-000000000581","EIP-7702 실전 가이드: Pectra 이후 스마트 계정 플로우 구축","eip-7702-siljeon-gaideu-pectra-ihu-seumateu-gyejeong-peulro-guchuk","EIP-7702는 모든 Ethereum EOA가 단일 트랜잭션 내에서 스마트 컨트랙트로 임시 동작할 수 있게 합니다. 새로운 계정 추상화 프리미티브를 사용한 배치 트랜잭션, 가스 후원, 소셜 리커버리 구현 방법을 소개합니다.","2026-03-28T10:44:43.377765Z",{"id":13,"name":56,"slug":57,"bio":58,"photo_url":19,"linkedin":19,"role":59,"created_at":60,"updated_at":60},"Open Soft Team","open-soft-team","The engineering team at Open Soft, building premium software solutions from Bali, Indonesia.","Engineering Team","2026-03-28T08:31:22.226811Z"]