Instruction-cache invalidation for AArch64 self-modifying code after DC CVAC
0 reputation · 11 May 2025, 22:23 UTC
0 reputation · 11 May 2025, 22:23 UTC
The goal is to determine whether an AArch64 JIT or runtime that writes executable memory can rely on DC CVAC to the Point of Coherency plus a Data Synchronization Barrier alone, or whether an explicit instruction-cache invalidation and Instruction Synchronization Barrier are required before fetching the new instructions.
The architecture describes clean-to-PoC, DSB, instruction-cache invalidate, then ISB, but marks whether a fetch can observe a stale line after only the data-cache clean as implementation-defined. Some cores snoop instruction fetches against recent data writes; others do not. Behavior can differ between Cortex-A implementations and custom AArch64 cores, so identical generated code may run correctly on one silicon revision and silently execute stale instructions on another.
Assumptions: AArch64 execution state, coherent system cache, and runtime-generated code rather than a static image. The unresolved boundary is whether invalidation is mandatory for a given core or optional because of implementation-specific snooping.
Which AArch64 cores require explicit instruction-cache invalidation after DC CVAC and DSB before executing newly written code? Is the ISB alone sufficient after a complete data-cache clean, or must software always invalidate by address or all? What mechanism should portable runtime code use to declare this dependency without targeting a specific microarchitecture?
35775 reputation · 12 May 2025, 00:06 UTC
No. On AArch64, DC CVAC followed by DSB SY and ISB does not, by itself, guarantee that a core will fetch newly written instructions. The architecture leaves it implementation-defined whether a fetch can observe a stale instruction-cache line after only a data-cache clean to the Point of Coherency. Portable runtime code must therefore invalidate the instruction cache explicitly before executing self-modified or JIT-generated code.
DC CVAC makes a data cache line coherent to the Point of Coherency; it does not touch the instruction cache.DSB SY completes prior memory operations, and ISB synchronizes instruction fetching. Neither invalidates cached instructions.Most AArch64 cores do not snoop instruction fetches against recent data writes, so a cleaned data line can remain resident in the instruction cache as stale. Custom silicon may document different behavior, but that is not portable.
DC CVAC by address.DSB SY to ensure the clean completes.IC IVAU by address, or IC ICIALLU to invalidate all.ISB so the core discards prefetched stale instructions and refetches.DC CVAC, xn
DSB SY
IC IVAU, xn
ISB
Do not branch on a core ID unless you have vendor documentation stating that instruction fetches are coherent to the Point of Coherency for that exact silicon. The portable dependency is the canonical sequence above, exposed through compiler builtins such as __builtin___clear_cache or OS cache-flush APIs where available. If neither is available, emit the sequence directly in the JIT's executable-memory commit path.
Does your target silicon document that instruction fetches are coherent to the Point of Coherency? If yes, IC IVAU may be omitted for that specific core only; if no, or if you are building a portable runtime, keep the invalidation unconditional.
Build a minimal harness that writes a known instruction sequence to executable memory, runs DC CVAC plus DSB SY plus ISB without invalidation, then calls it. If the old code executes, add IC IVAU before the ISB and re-run. Do not infer production behavior from emulators alone; test on the target silicon.
Use comments to ask for clarification. Post a solution as an answer.
35,775 reputation · 12 May 2025, 04:17 UTC
On AArch64, relying on DC CVAC and DSB is insufficient for portable self-modifying code. While DC CVAC ensures the data reaches the Point of Coherency (PoC), the architecture does not guarantee hardware coherency between the data cache and instruction cache.
To ensure the fetch unit observes the new instructions, software must follow this specific sequence:
DC CVAC to clean the modified data line to the PoC.DSB to ensure the clean operation is complete.ICIALALL or IC IVUVA by address).DSB to ensure the invalidation is complete.ISB to flush the pipeline and prevent the execution of stale instructions.While some high-performance cores may implement hardware snooping that makes this appear optional, the architecture defines this as implementation-defined. For a portable runtime, explicit invalidation is mandatory to avoid silent failures on different silicon revisions.