KeyFrame內部研究專用

Solidity Optimizer Under the Hood

DeFi Security Summit - DSS·11月23日週日·22 min英文

三句話摘要

深入解析 Solidity 編譯器優化器的運作原理,涵蓋 Legacy 與 Via-IR 兩條編譯管線、各類 Yul 優化 Pass 的機制,以及歷史上出現過的優化器 Bug。 理解 Solidity 優化器的具體機制(Peephole 等價替換、Yul 的 Load Resolver / Dead Code Eliminator / Unused Store Eliminator / Function Specializer),才能寫出對優化器友善的程式碼,並辨識出那些反直覺的邊界條件,避免在安全審計中漏掉優化器層面的潛在風險。 1. Legacy vs Via-IR 管線的根本差異

重點整理

重點
  • 1

    1. Legacy vs Via-IR 管線的根本差異

  • 2

    Legacy 管線直接將 Solidity 編譯為 EVM assembly 再優化;Via-IR 先轉換為 Yul 中間語言,在 Yul 層做高階優化後才降轉為 EVM assembly,因為 Yul 具有明確的結構化控制流,更易於靜態分析,也讓跨函式的全域優化成為可能。

  • 3

    2. Peephole 優化器的等價替換邏輯

  • 4

    Peephole 優化器一次只看 2-3 條 opcode,尋找可用更少或更便宜指令表達相同語意的模式,例如乘以零直接推入結果、`ISZERO x` 替換為 `x`、`PUSH` 後立刻 `POP` 整組刪除,以及 `ADDRESS` + `BALANCE` 合併為單一 `SELFBALANCE`。

  • 5

    3. Yul 優化器的語意感知能力

  • 6

    Yul 優化器不只看指令序列,還追蹤 storage 和 memory 在特定時間點的具體值,因此能在編譯期完成像 Keccak256 這類昂貴運算,把 runtime 的雜湊計算替換成一個 `PUSH32` 常數。

  • 7

    4. 優化器的邊界:演算法複雜度無法被消除

  • 8

    優化器保證不改變合約語意,只移除或簡化等價邏輯,但它無法把 O(n²) 的迴圈變成常數時間操作——演算法本身的效率仍由開發者負責。

實用技巧與重點

乾貨
  • 部署成本:每 1 byte 部署合約消耗 200 gas;合約大小硬上限為 24 KB
  • optimizer runs 參數:數字越高 → 可能增加 bytecode 大小但降低執行 Gas;數字越低 → bytecode 更小但執行更貴
  • Via-IR 優點:自動將區域變數 spill 到 memory,大幅減少 `Stack too deep` 錯誤
  • Yul 優化 Pass 清單:Load Resolver、Dead Code Eliminator、Unused Store Eliminator、Function Specializer(可透過自定義優化序列關閉特定 pass)
  • Function Specializer 反制技巧:透過一個「混淆函式」`stubborn(x)` 包裝字面常數參數,讓優化器無法識別為字面值,從而阻止特化版本生成,縮減 bytecode
  • 歷史 Bug 1(Unused Store Eliminator):含 inline assembly `return`/`stop` 的函式中,第一次 storage 寫入被錯誤判定為「死寫入」並刪除,實際上 assembly `return` 直接結束整個 call,導致後續覆寫永遠不會執行
  • 歷史 Bug 2(Unused Store Eliminator):跨 assembly 區塊的記憶體讀寫被誤判斷開連結,第一個 assembly block 的 `mstore` 被刪除,導致第二個 block 讀到未初始化或被 scratch space 汙染的值
  • Keccak256 預計算:當 memory[0] 寫入 `0xFF` 後無任何覆寫,優化器直接在編譯期算出雜湊值,bytecode 中只剩 `PUSH32 <digest>`
  • 自定義優化序列:編譯器輸入可傳入 `--yul-optimizations` 序列字串覆寫預設 pass 順序

結論

結論

理解 Solidity 優化器的具體機制(Peephole 等價替換、Yul 的 Load Resolver / Dead Code Eliminator / Unused Store Eliminator / Function Specializer),才能寫出對優化器友善的程式碼,並辨識出那些反直覺的邊界條件,避免在安全審計中漏掉優化器層面的潛在風險。

完整解析

詳細

Solidity 的優化器長期以來被開發者當作黑盒子使用——打開 `--optimize` 旗標,設一個很大的 runs 數字,然後期待 Gas 費用下降。這場演講的目標是揭開黑盒蓋子,說明優化器在兩條編譯管線中分別做了什麼事。

Solidity 提供兩種編譯路徑。Legacy 管線的流程相對直接:原始碼 → EVM assembly → 優化器作用於 assembly → bytecode。Via-IR 管線則多了一個中間層:原始碼先被翻譯成 Yul(一種精簡的結構化語言,具備函式、if 和 for,但沒有繼承、modifier 等高階語法糖),優化器在 Yul 層完成高階轉換後,才降轉為 EVM assembly,再執行一次 EVM 層的優化。目前預設仍是 Legacy 管線,但 Solidity 團隊計畫未來將 Via-IR 設為預設。Yul 的結構化控制流和明確的跳躍目標讓靜態分析更容易,也使跨函式的全域優化成為可能,同時它也是 Via-IR 能自動解決 `Stack too deep` 的根本原因。

在 EVM assembly 層,Peephole 優化器以滑動視窗掃描指令序列,尋找可用等價但更廉價指令替換的模式。乘以零直接輸出常數零、`PUSH` 後立刻 `POP` 整組刪除、兩次對調同一對堆疊元素合而為一、`ADDRESS` + `BALANCE` 合併為 `SELFBALANCE`——這些都是典型案例。更值得注意的是,優化器還會追蹤 storage 和 memory 在特定時間點的精確內容:當它確定 memory[0] 存放著 `0xFF` 且在雜湊之前沒有任何覆寫時,整個 Keccak256 運算會在編譯期直接完成,runtime bytecode 只剩一條 `PUSH32`。

Yul 優化器由多個 pass 串接而成,每個 pass 保持語意不變地對程式碼做轉換,且 pass 的執行順序至關重要——前一個 pass 建立的條件往往是後一個 pass 的前提。Load Resolver 偵測 storage/memory 的值是否已在堆疊上,若是則以 `DUP`/`SWAP` 重用而非重新 `SLOAD`,在同一變數多次使用的函式中可省下顯著的 Gas。Dead Code Eliminator 移除永遠不可能執行到的程式碼;值得注意的是,即使 `return` 位於加法之前,若加法不在 `unchecked` 區塊內,溢位檢查仍會保留——因為移除它會改變合約行為。Unused Store Eliminator 刪除對同一位置的早期寫入(若後續被覆寫前從未被讀取),但一旦中間存在外部呼叫,優化器必須保守假設對方可能讀取該 storage,因此不會刪除。Function Specializer 對以字面常數作為引數的呼叫點生成特化函式版本,有時能進一步觸發其他優化,但也可能膨脹 bytecode;開發者可用一個「混淆包裝函式」阻止特化,或在編譯器輸入中移除該 pass。

歷史上優化器確實引入過真實的 Bug。第一個出現在 Unused Store Eliminator:若函式先寫入 storage,再呼叫一個含有 inline assembly `return`(直接結束整個 call 而非僅返回函式)的子函式,接著在正常執行路徑再次寫入同一 slot,優化器會誤判第一次寫入為死寫入並刪除;然而 assembly `return` 路徑根本不會執行到第二次寫入,導致 storage 狀態與預期不符。第二個 Bug 同樣源於 Unused Store Eliminator:跨越兩個 inline assembly 區塊的硬編碼記憶體偏移量讀寫被判定為無關,第一個區塊的 `mstore` 遭刪除,第二個區塊讀到的是未初始化或被暫存空間汙染的值。

演講最後強調:優化器是一組定義明確的轉換規則,而非魔法。它能移除多餘的 opcode、合併等價指令、預計算常數結果,但它無法替代好的演算法設計——將 O(n²) 的迴圈降為常數時間,這件事始終是開發者的責任。

關鍵時刻

Pipeline v2

帶時間戳的重點,會在逐字稿層級分析上線後產生。目前請先透過原始影片觀看。

事實查核

Pipeline v2

說法查證是下一次管線升級的一部分。KeyFrame 只會顯示它真正能驗證的內容。

更多「GitHub 熱點」的內容

30 Self-Hosted Projects on GitHub: wacrm, halcyon-video, wrtag, Codeman, romm, wger, homelab, clay
12 min
GitHub 熱點英文8月18日

30 Self-Hosted Projects on GitHub: wacrm, halcyon-video, wrtag, Codeman, romm, wger, homelab, clay

GitHub Awesome

  • 數據主權與隱私核心:這些專案的共同特點是數據完全在使用者控制下,不依賴訂閱制或廠商,例如 WACRM 使用 Meta 官方 API 避免帳號停用,OpenArchiver 以 EML 標準格式本地儲存郵件,HQBase 在使用者自有 Cloudflare 帳戶內運行。
  • 現代技術棧達企業級質量:這些自架方案採用 Kubernetes、PostgreSQL、Cloudflare Workers 等企業級技術,證明開源不等於簡陋,Eden 家庭實驗室的完整配置就是最好例證。
  • 跨領域替代完整性:從業務通訊(WACRM、LibreDesk、HQBase)到多媒體(Halcyon、ROMM、Viofo Sync)、生產力(Super Productivity、Clay、Note Discovery)、財務(Tilevia、Taxhacker、Expenseive),開源生態已能覆蓋 SaaS 的主要應用場景。
The Next Game Engine Won't Have a Manual — Arturo Nunez, Nereu
19 min
GitHub 熱點中文8月18日

The Next Game Engine Won't Have a Manual — Arturo Nunez, Nereu

AI Engineer

  • 現有遊戲引擎要求開發者掌握程式設計、建模、渲染、動畫等多個領域,導致高學習曲線與冗長開發週期;Nereu 改用自然語言描述,讓使用者聚焦遊戲設計本身而非技術細節。
  • 系統採用實體-組件系統架構,每個遊戲物體只需加上描述用途的標籤(如「角色」「可動畫」「雙段跳」),引擎內的系統會自動查詢並執行相應邏輯,避免重複編寫樣板程式碼。
  • AI 助手 BB 透過場景上下文、使用者編輯位置和遊戲類型資訊,理解使用者意圖並自動新增或移除標籤;使用者隨時可提問如何實現特定功能,大幅降低認知負荷。
GitHub Trending Today #45: claudish-to-english, openanalytics, deepseek-harness, human-review, ha.mr
14 min
GitHub 熱點英文8月15日

GitHub Trending Today #45: claudish-to-english, openanalytics, deepseek-harness, human-review, ha.mr

Github Awesome

  • AI 代理與自動化趨勢:從 DeepSeq Harness 的模組化代理框架、到 Formin 的自動化軟體工廠(通過四個代理站點自動分類、規劃、實現、審查),再到 Agent Safe Pipeline 的安全隔離機制,展現 AI 代理工具生態正在成熟,重點從「能用」進化到「可控」與「可審計」。
  • 本地優先與隱私設計成為標準:Open Analytics 無 Cookie 追蹤、HA MR 瀏覽器側鏈接壓縮、BlueFairy 藍牙配對而非雲服務、TokenTab 本地成本追蹤、Mole 的預算硬約束機制,反映開發者對隱私邊界的明確劃分——不再默認上傳,而是明確選擇。
  • 開發者工具鏈的細節化:從 Human Review 的批量編輯反饋、Book to Skill 的文檔轉技能、Anti-slop 的類型安全檢查、到 PGBOT 的無寫入診斷,工具不再追求「大而全」,而是在特定工作流的某個環節解決明確問題。