工程趣聞 2026 年 9 月 20 日

2026-09-20 — FEX-Emu拆解x86模擬ARM的記憶體與鎖硬傷

primary=https://fex-emu.com/Scourge-of-emulation/ primary=https://github.com/FEX-Emu/FEX/blob/e02953dc174531551219712df20355dcf4afc089/unittests/InstructionCountCI/FlagM/Atomics.json

FEX-Emu拆解x86模擬ARM的記憶體與鎖硬傷

FEX-Emu 官方部落格 · 2026-09-17

x86 的 LOCK 前綴指令(例如 LOCK CMPXCHGLOCK ADD)保證即使記憶體存取跨越 cacheline 邊界也絕不會撕裂資料,這種強一致性建立在 x86-TSO(Total Store Ordering)之上,但 ARM 原生只提供弱序(weak ordering)記憶體模型,要在 ARM 上忠實重現這種保證正是 FEX-Emu 團隊在其技術文章中攤開的核心難題。文章由 FEX-Emu 專案本身撰寫,說明他們在把 x86 遊戲二進位轉譯到 ARM 平台(含 Steam Deck 的高通晶片與 Apple M1 系統)時實際遇到的瓶頸。

背景

x86 程式設計者可以假設一旦某個 store 發生,其結果會立刻對所有其他處理器保持一致可見,這個假設是幾十年 x86 程式(尤其是無鎖資料結構與遊戲引擎)賴以正確運作的基礎。ARM 完全不做這種保證,一般 load 或 store 只有程式順序上的局部性,跨核心可見順序需要額外的屏障指令才能建立。FEX-Emu 必須在每一條被轉譯的 x86 記憶體指令周圍插入足夠的同步語意,才能讓移植過來的遊戲不會出現競態條件造成的隨機當機。

技術細節

早期做法是把每個帶序 x86 load/store 都翻成 ARM 的 acquire/release 指令,但這類指令開銷高,在 Cortex-X4、X925 這類核心上會造成約 50%~70% 的效能損失。ARMv8.3 開始加入的 FEAT_LRCPC(Load-Acquire RCpc)放寬了語意、降低了成本,後續 FEAT_LRCPC2 補上立即偏移定址、FEAT_LRCPC3 再擴充到向量與堆疊操作;FEAT_LSE2 則把對齊要求放寬到 16 位元組粒度。Apple M1 更直接在硬體層提供一個 TSO 開關,執行時切換成接近原生 x86 的循序保證。

針對 x86 的原子讀改寫指令,FEX-Emu 會對映到 ARMv8.1 引入的單一指令,而不是「載入、修改、條件寫回」的迴圈:

x86 指令ARM64 對應指令
LOCK ADDldaddal
LOCK CMPXCHGcasal
XCHGswpal
LOCK BTC/BTR/BTSldeoralbldclralbldsetalb

文章特別指出 LOCK NEG 是少數找不到一對一硬體對映的例外,只能靠組合指令模擬,並附上專案內對應的單元測試案例作為佐證。最棘手的是「split-lock」:當一個未對齊的原子操作跨越 cacheline 邊界時,x86 硬體仍保證整體原子性,ARM 卻會直接觸發對齊例外(alignment fault),因為 ARM 一般要求原子操作落在自然對齊邊界內。

目前業界的權宜作法各有取捨。Steam Deck 用的是高通客製核心,直接在核心層攔截並處理未對齊的原子操作;高通 Oryon-3 核心提供「coherent cachelines」,把 64 位元組對齊範圍內的原子操作視為硬體原生原子,但仍只是部分解法。FEX-Emu 提出的長期方向是使用 128 位元的 CASP 指令去涵蓋跨越粒度邊界的情況。這幾種路徑的共同代價是:一旦真的觸發對齊例外,就要經過核心訊號處理(signal handler)走一趟核心/使用者空間切換,單次開銷高達數千個週期,實測顯示未對齊原子操作的延遲變異可達千倍量級。

另一個容易被忽略的痛點是不可快取(uncached/write-combine)記憶體。PCIe 顯示卡的資料傳輸大量依賴 write-combine 語意的儲存,但現有 FEAT_LRCPC 系列擴充並未提供對等的 write-combine store,導致效能相較快取緩衝區可能慢達 816 倍。文章舉的實例是《Hollow Knight: Silksong》與《Subnautica 2》在受影響路徑上掉到不到 1 FPS。統一記憶體架構(UMA)的裝置(如 Tegra、Adreno 600 系列)可以改用快取緩衝區繞過問題,Mesa 專案的 Adreno Turnip 合併請求正是為此讓 FEX-Emu 避開非快取路徑。

影響範圍

這些問題直接衝擊所有在做二進位轉譯或跨架構相容層的團隊,不只是 FEX-Emu,Box64、任何自製的 x86-on-ARM 相容層乃至 Rosetta 一類系統都得面對同樣的記憶體模型落差。需要事先評估的取捨包括:

  • 是否有能力利用 FEAT_LRCPCFEAT_LSE2 等新擴充,還是得支援不具備這些擴充的舊款 ARM 核心。
  • split-lock 場景要靠核心補丁、硬體 coherent cacheline,還是等待 128 位元 CASP 之類方案落地。
  • GPU 相關的 write-combine 存取,是否能改走 UMA 快取緩衝區,或需要等圖形驅動(如 Turnip)補上對應路徑。
  • LOCK NEG 這種沒有硬體直接對映的指令,需要額外的組合指令與測試覆蓋。

對正在評估或維護 x86-on-ARM 相容層的工程師來說,這篇文章等於把「記憶體序」「split-lock」「非快取存取」三個容易被低估的效能與正確性坑,攤開成可以對照硬體世代(Cortex-X4/X925、Oryon-3、Apple M1)逐一檢查的清單。

原始來源:The scourge of x86 emulation(FEX-Emu 官方部落格)FEX-Emu Atomics.json 單元測試


End of article
0
Would love your thoughts, please comment.x
()
x