Skip to content
Open
Show file tree
Hide file tree
Changes from 58 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
e6a40c3
Add ARMv8-A (AArch64) backend (functional milestone)
clebreto Jul 7, 2026
ec87917
armv8a: add 32-bit (W register) instruction forms
clebreto Jul 7, 2026
b3df1fd
armv8a: reserve XZR/x18, enforce 16-byte SP alignment, fix SP restore
clebreto Jul 8, 2026
cd1f311
armv8a: prove core operation builders (ARMv8AFopn_coreP)
clebreto Jul 8, 2026
d6759bf
armv8a: prove params-level operation wrappers (ARMv8AFopnP)
clebreto Jul 8, 2026
4f5e044
armv8a: ground the shift-amount immediate checkers in the Arm ARM
clebreto Jul 8, 2026
598fd86
armv8a: model A64 immediate legality in the argument checkers
clebreto Jul 8, 2026
1745b1d
armv8a: prove the MOVZ/MOVN/MOVK immediate materialization (li)
clebreto Jul 9, 2026
6594122
armv8a: move the SP 16-byte alignment invariant to the stack-alloc or…
clebreto Jul 9, 2026
bc2af9c
armv8a: prove the stack-alloc and (most) linearization hypotheses
clebreto Jul 9, 2026
f313433
armv8a: complete the linearization hypotheses (armv8a_hliparams)
clebreto Jul 9, 2026
2eee820
armv8a: prove the assembly-generation hypotheses (armv8a_hagparams)
clebreto Jul 9, 2026
681c105
armv8a: prove stack zeroization (armv8a_hszparams)
clebreto Jul 9, 2026
737b366
armv8a: prove lowering correct, complete h_architecture_params
clebreto Jul 9, 2026
983734e
armv8a: accept explicitly masked variable shift amounts
clebreto Jul 9, 2026
767d809
armv8a: test the 32-bit (W register) operations
clebreto Jul 10, 2026
3d6ac4b
armv8a: alphabetize the armv8a entries in _CoqProject
clebreto Jul 10, 2026
3be1aea
armv8a: list the armv8a check category next to the other base archite…
clebreto Jul 10, 2026
d627c2f
armv8a: error out instead of claiming safety for the unsupported checker
clebreto Jul 10, 2026
3635bea
armv8a: factor the stack-pointer minimal-alignment bump in stackAlloc
clebreto Jul 10, 2026
8863f9a
armv8a: make the minimal stack-pointer alignment a global option
clebreto Jul 10, 2026
8333ff6
arm, armv8a: share the NZCV flag declarations in arm_common
clebreto Jul 10, 2026
cbfb7ce
arm, armv8a: share the condition-code type in arm_common
clebreto Jul 10, 2026
a1d9a57
arm, armv8a: share the condition-code evaluation in arm_common
clebreto Jul 10, 2026
cd98121
armv8a: remove the variable-shift mnemonics ASRV/LSLV/LSRV/RORV
clebreto Jul 10, 2026
41d3331
armv8a: carry the true per-operand width in pp_aop_args
clebreto Jul 10, 2026
ae3e6c1
armv8a: print conditions in place and make pp_asm_arg total
clebreto Jul 10, 2026
9474a95
armv8a: define each instruction's semantics next to its description
clebreto Jul 10, 2026
133614b
armv8a: list the same modules as the other architectures in extraction
clebreto Jul 10, 2026
27e574f
armv8a: define SUBS as ADCS x (invw y) true in the EasyCrypt model
clebreto Jul 10, 2026
c63f6fe
armv8a: generate size-suffixed op names, model both sizes in EasyCrypt
clebreto Jul 10, 2026
11669f2
armv8a: make is_doit_asm_op exhaustive, one case per mnemonic
clebreto Jul 10, 2026
dd6dc4b
armv8a: fuse multiplications of any width at least the target into MA…
clebreto Jul 10, 2026
e69a242
arm, armv8a: extract the shared arm_common module
clebreto Jul 10, 2026
6164586
armv8a: accept armv8a in the cmdliner tools (jasmin2ec, jasmin-ct, ...)
clebreto Jul 10, 2026
642405a
Merge branch 'main' into feature/armv8a
clebreto Jul 11, 2026
15101d8
armv8a: fix ROR in the EasyCrypt model: rotate, not arithmetic shift
clebreto Jul 11, 2026
6d7fcdf
armv8a: rename armv8a_linux_call_conv to armv8a_call_conv
clebreto Jul 11, 2026
c4e0d27
armv8a: complete the port to upstream main
clebreto Jul 11, 2026
2ea47ac
armv8a: add the stack-zeroization test categories
clebreto Jul 11, 2026
05545d0
armv8a: test the instruction semantics against the hardware
clebreto Jul 11, 2026
47b94ef
eclib: share the common Arm 32-bit operations in JModel_arm
clebreto Jul 11, 2026
ef8ea02
arch: carry the DOIT classification in the instruction descriptors
clebreto Jul 11, 2026
2633f35
attach doit info to the declaration of the instruction
bgregoir Jul 18, 2026
4175961
armv8a: repair the MSUB case of lower_Papp2P
clebreto Jul 11, 2026
ef31bc5
armv8a: restrict the shifted variant of instructions to register oper…
clebreto Jul 16, 2026
5d64730
armv8a: forbid ROR on the shifted arithmetic instructions
clebreto Jul 16, 2026
7177f76
arch: cap the default zeroization step by the widest store, not reg_size
clebreto Jul 16, 2026
c762306
armv8a: state MVN's register-only argument kinds directly
clebreto Jul 17, 2026
f00e297
armv8a: trim the alias-of-V boilerplate from the shift doc blocks
clebreto Jul 17, 2026
8c46ec4
armv8a: distill the instruction model to the lowering-emitted set
clebreto Jul 17, 2026
4d2d6af
armv8a: reject halfword shifts 32/48 on the 32-bit wide moves
clebreto Jul 21, 2026
8d4c50f
armv8a: drop the stale shifted-ROR skip note from the hw-semantics test
clebreto Jul 21, 2026
5be556f
Merge branch 'feature/doit-instr-desc' into feature/armv8a-distilled
clebreto Jul 22, 2026
2a8035c
armv8a: port the immediate-argument conditions to the arch-local inte…
clebreto Jul 22, 2026
270442c
armv8a: declare the DIT classification in the instruction descriptors
clebreto Jul 22, 2026
0e94d6c
Merge branch 'main' of jasmin-lang/jasmin into feature/armv8a-distilled
clebreto Aug 26, 2026
bae04d7
armv8a: follow the linearization lload/lstore width generalization
clebreto Aug 26, 2026
1507adc
armv8a: remove a stray duplicate of the add test
clebreto Aug 26, 2026
04af3df
arm: floor the stack-pointer alignment at the architectural 4 bytes
clebreto Aug 26, 2026
c78e644
armv8a: use the DOIT/NOT_DOIT constructors directly in the descriptors
clebreto Aug 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion compiler/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ CHECKCATS ?= \
x86-64-Intel \
x86-64-nolea \
arm-m4 \
armv8a \
risc-v \
x86-64-stack-zero-loop \
x86-64-stack-zero-unrolled \
arm-m4-stack-zero-loop \
arm-m4-stack-zero-unrolled \
armv8a-stack-zero-loop \
armv8a-stack-zero-unrolled \
risc-v-stack-zero-loop \
risc-v-stack-zero-unrolled \
CCT CCT-DOIT SCT
Expand All @@ -33,7 +36,7 @@ DISTDIR ?= jasmin-compiler
OBELISK ?= obelisk

# --------------------------------------------------------------------
.PHONY: all CIL check check-ci
.PHONY: all CIL check check-ci check-armv8a-semantics
.PHONY: clean install uninstall dist

all:
Expand All @@ -56,6 +59,12 @@ check-ec:
$(MAKE) -C examples/gimli/proofs
$(MAKE) -C examples/extraction-unit-tests

# Differential test of the ARMv8-A instruction semantics (the Coq model of
# armv8a_instr_decl.v, evaluated through its OCaml extraction) against real
# hardware execution. Requires an AArch64 (ARMv8-A) host.
check-armv8a-semantics:
$(MAKE) -C tests/armv8a-hw-semantics check

check-all: check
dune runtest -f

Expand Down
15 changes: 15 additions & 0 deletions compiler/config/tests.config
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ bin = ./scripts/check
args = -arch riscv
okdirs = examples/**/risc-v tests/success/**/risc-v tests/success/**/common

[test-armv8a]
bin = ./scripts/check
args = -arch armv8a
okdirs = tests/success/**/armv8a

[test-risc-v-extraction]
bin = ./scripts/extract-and-check
args = riscv
Expand All @@ -80,6 +85,16 @@ bin = ./scripts/check
args = -arch arm-m4 -stack-zero=unrolled
okdirs = tests/success/stack_zeroization/**/arm-m4

[test-armv8a-stack-zero-loop]
bin = ./scripts/check
args = -arch armv8a -stack-zero=loop
okdirs = tests/success/stack_zeroization/**/armv8a

[test-armv8a-stack-zero-unrolled]
bin = ./scripts/check
args = -arch armv8a -stack-zero=unrolled
okdirs = tests/success/stack_zeroization/**/armv8a

[test-risc-v-stack-zero-loop]
bin = ./scripts/check
args = -arch riscv -stack-zero=loop
Expand Down
1,490 changes: 1,490 additions & 0 deletions compiler/doc/armv8a_isa_docs.json

Large diffs are not rendered by default.

144 changes: 144 additions & 0 deletions compiler/doc/armv8a_register_model.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
# ARMv8-A (AArch64) register model and stack-pointer handling — rationale and sources

This note documents the modeling decisions in the Jasmin AArch64 backend that
concern the **architecture declaration** (`proofs/compiler/armv8a_decl.v`) and
the **stack-pointer handling** in the linearization parameters
(`proofs/compiler/armv8a_params.v`). Each decision departs from a naive "one
Coq constructor per hardware register" model for a documented architectural or
ABI reason; the citations below let a reviewer check every claim.

Primary sources:

- **Arm ARM** — *Arm Architecture Reference Manual, A-profile*, Arm DDI 0487M.a
(`DDI0487_M.a.a_a-profile_architecture_reference_manual.pdf`,
sha256 `c07b1302a3db0c6bc0490808c771d66684ddd5adfb2ef64fd23e6beeb7b22e48`; the
same edition and hash recorded in `armv8a_isa_docs.json`). Page ids below are
the manual's own `Sn-NNNN` labels.
- **AAPCS64** — *Procedure Call Standard for the Arm 64-bit Architecture
(AArch64)*, Arm IHI 0055F.
- **Apple** — *Writing ARM64 Code for Apple Platforms*, Apple Developer
documentation.

---

## 1. The general-purpose register set

AArch64 has 31 general-purpose registers `R0..R30`, each addressable as a 64-bit
`X` register or a 32-bit `W` register, plus a dedicated stack pointer `SP`.
`R30` is the procedure-call link register.

> Arm ARM, **B1.2 "Registers in AArch64 Execution state"**, p. B1-203:
> "31 general-purpose registers, R0 to R30 ... The X30 general-purpose register
> is used as the procedure call link register. SP — A 64-bit dedicated Stack
> Pointer register."

The model keeps `R0..R30` and `RSP`. Two registers that a hardware-faithful
model might include are deliberately omitted (§2, §3).

## 2. The zero register (XZR/WZR) is not modeled

In an instruction encoding the 5-bit register field value `31` (`0b11111`) does
**not** name a physical register; its meaning depends on which register accessor
the instruction uses:

> Arm ARM, **B1.2**, p. B1-206:
> "For the general-purpose register X[] accessor, register number 31 accesses
> the zero register, ZR, which reads as zero and ignores writes."

The stack pointer is reached through a **separate `SP[]` accessor**, so the same
field value `31` means `SP` only for the instructions that select it (ADD/SUB in
their SP forms, MOV-to/from-SP, and loads/stores that allow `SP` as base).

Consequences for the model:

- Modeling `ZR` as an allocatable register would be **unsound**: a value written
to it is discarded and any read returns 0. The register allocator, treating it
as ordinary storage, produced code such as `mov sp, xzr` (setting SP to 0).
- We therefore omit it entirely, exactly as the Jasmin RISC-V backend omits the
hardwired `x0`/`zero`. The handful of operations that genuinely need `ZR` as an
operand — `CMP` (`SUBS` to `ZR`), `NEG`, `TST`, etc. — are emitted as dedicated
assembler mnemonics by the printer, never as writes to a `ZR` register.
- `RSP` is the single encoding-31 register we keep, and it always denotes `SP`.

## 3. The platform register (x18) is not modeled

`x18` is not an architectural special register; it is reserved by the software
ABI:

> AAPCS64 (IHI 0055F), *General-purpose registers*: r18 is
> "The Platform Register, if needed; otherwise a temporary register", and a
> platform ABI may reserve it.
>
> Apple, *Writing ARM64 Code for Apple Platforms*:
> "The platforms reserve register x18. Don't use this register."

On Apple platforms, under Windows, and under shadow-call-stack Linux, the OS or
runtime may overwrite `x18` **asynchronously** (i.e. between two instructions of
otherwise straight-line user code). Jasmin's register allocator chooses the
register that holds a function's saved stack pointer by proving the program does
not clobber it (`get_reg_oracle` in `compiler/src/regalloc.ml`); that analysis
cannot see writes performed outside the program, so it happily picked `x18` and
the saved SP was corrupted to 0 at run time.

Keeping `x18` out of the allocation pool is the only safe choice across these
platforms. On a platform where `x18` is a plain temporary this merely forgoes
one scratch register; correctness is unaffected.

## 4. The stack pointer must stay 16-byte aligned

Whenever `SP` is used as the base of a memory access it must be 16-byte aligned.
This is an **architectural** check (an exception), not just an ABI convention:

> Arm ARM, **D1.4.10.2 "SP alignment checking"**, p. D1-7135:
> - rule **RRDMXG**: "When the SP is used as the base address of a calculation,
> regardless of any offset applied by the instruction, if bits [3:0] of the SP
> are not 0b0000, there is a misaligned SP."
> - rule **RTFVSM**: "If SP alignment checking is enabled, then the execution of
> a load or store using the SP with a misaligned SP generates a synchronous SP
> Alignment exception on that load or store."
> - rule **RSTDYJ**: the check is enabled by `SCTLR_EL1.SA` (EL1),
> `SCTLR_EL1.SA0`/`SCTLR_EL2.SA0` (EL0), etc.

The check is enabled for EL0 on the platforms we target. The 16-byte alignment
of `SP` at public interfaces is also mandated by the AAPCS64 (IHI 0055F, *The
stack*: "SP mod 16 = 0"), which B1.2 (p. B1-203) cross-references.

Jasmin sizes each stack frame to the alignment of the objects it holds
(`sao_align`), which for pure 64-bit code is only 8 bytes, so a raw frame size
need not be a multiple of 16. The backend therefore:

- rounds every frame allocation/deallocation up to a multiple of 16
(`round_up_16`, used by `armv8a_allocate_stack_frame` /
`armv8a_free_stack_frame`), so nested calls preserve the invariant; and
- never aligns a re-aligned frame to less than 16 bytes
(`armv8a_set_up_sp_register`).

The extra padding lands at the top of the frame and is never accessed.

## 5. The stack pointer cannot be an LDR destination

Because a load writes its result through the general-purpose `X[]` accessor, and
register number 31 in that accessor is `ZR` (§2), an A64 `LDR` can never write
`SP`: its destination operand is `<Xt>`, not `<Xt|SP>`. (This differs from
ARMv7, where R13/SP is an ordinary register that `LDR` can target — which is why
the shared linearization default, `lloads_imm_dfl`, is fine for the 32-bit Arm
backend but not here.)

When the saved stack pointer has been spilled to the stack (the usual case for a
function that saves callee-saved registers), it must therefore be reloaded into
a scratch register and then copied to `SP` with a `MOV` (which reaches `SP` via
the `SP[]` accessor). This is what the backend's custom `armv8a_lloads` does: it
restores the ordinary saved registers first (relative to the still-live `SP`),
then loads the saved `SP` into `X17` and issues `MOV SP, X17`.

---

### Cross-reference to the code

| Decision | File / definition | Source |
|---|---|---|
| Omit `XZR` | `armv8a_decl.v` — `register`, `registers`, `register_to_string` | Arm ARM B1.2, p. B1-206 |
| Omit `x18` | `armv8a_decl.v` — `register`, `registers`, `armv8a_internal_call_conv` | AAPCS64 IHI 0055F; Apple ABI |
| Round frames to 16 | `armv8a_params.v` — `round_up_16`, `armv8a_allocate_stack_frame`, `armv8a_free_stack_frame` | Arm ARM D1.4.10.2 (RRDMXG, RTFVSM) |
| Re-align frames ≥ 16 | `armv8a_params.v` — `armv8a_set_up_sp_register` | Arm ARM D1.4.10.2; AAPCS64 |
| Restore `SP` via scratch + `MOV` | `armv8a_params.v` — `armv8a_lloads` | Arm ARM B1.2, p. B1-206 |
3 changes: 2 additions & 1 deletion compiler/entry/commonCLI.ml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ open Utils
let arch =
let alts =
[
("x86-64", Utils.X86_64); ("arm-m4", Utils.ARM_M4); ("riscv", Utils.RISCV);
("x86-64", Utils.X86_64); ("arm-m4", Utils.ARM_M4);
("armv8a", Utils.ARMv8A); ("riscv", Utils.RISCV);
]
in
let doc =
Expand Down
8 changes: 4 additions & 4 deletions compiler/safetylib/arm_safety.ml
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ module Arm_safety
with type reg = Arm_decl.register
and type regx = Arch_utils.empty
and type xreg = Arch_utils.empty
and type rflag = Arm_decl.rflag
and type cond = Arm_decl.condt
and type rflag = Arm_common.rflag
and type cond = Arm_common.condt
and type asm_op = Arm_instr_decl.arm_op
and type extra_op = Arm_extra.arm_extra_op)
: SafetyArch
with type reg = Arm_decl.register
and type regx = Arch_utils.empty
and type xreg = Arch_utils.empty
and type rflag = Arm_decl.rflag
and type cond = Arm_decl.condt
and type rflag = Arm_common.rflag
and type cond = Arm_common.condt
and type asm_op = Arm_instr_decl.arm_op
and type extra_op = Arm_extra.arm_extra_op
= struct
Expand Down
9 changes: 9 additions & 0 deletions compiler/safetylib/safetyMain.ml
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,12 @@ let get_arch_with_analyze arch call_conv : (module ArchWithAnalyze) =

let analyze = Safety.analyze
end)
| ARMv8A ->
(module struct
module C = CoreArchFactory.Core_arch_ARMV8A
module A = Arch_full.Arch_from_Core_arch (C)

let analyze ?fmt:_ ~safety_param:_ _ _ =
hierror ~loc:Lnone ~kind:"safety analysis"
"the safety checker is not implemented for ARMv8-A"
end)
3 changes: 3 additions & 0 deletions compiler/scripts/check
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ elif [ $ARCH = "riscv" ]
then
# +m enables integer multiplication
ASARGS="--triple=riscv32 --mcpu=generic-rv32 -mattr=+m"
elif [ $ARCH = "armv8a" ]
then
ASARGS="--triple=aarch64"
else
ASARGS="--triple=x86_64"
fi
Expand Down
13 changes: 13 additions & 0 deletions compiler/src/arch_full.ml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,19 @@ module type Core_arch = sig

val callstyle : reg callstyle

(* Minimal alignment of every stack frame. Frame sizes are rounded up to
the frame's alignment (see [stack_frame_allocation_size]), so this
bounds the alignment the stack pointer keeps across calls. AArch64
requires 16 bytes (SP alignment checking, Arm ARM DDI0487M.a,
D1.4.10.2); other architectures have no requirement beyond the one of
the stack slots themselves. *)
val sp_min_align : Wsize.wsize

(* Widest store the architecture can perform in one instruction. Used to
cap the default stack-zeroization clear step when [sp_min_align]
raises the frame alignment beyond it. *)
val max_store_size : Wsize.wsize

val known_implicits : (Name.t * string) list

val is_ct_asm_op : asm_op -> bool
Expand Down
7 changes: 7 additions & 0 deletions compiler/src/arch_full.mli
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ module type Core_arch = sig

val callstyle : reg callstyle

(* Minimal alignment of every stack frame; see arch_full.ml. *)
val sp_min_align : Wsize.wsize

(* Widest store the architecture can perform in one instruction; see
arch_full.ml. *)
val max_store_size : Wsize.wsize

val known_implicits : (Name.t * string) list

val is_ct_asm_op : asm_op -> bool
Expand Down
5 changes: 5 additions & 0 deletions compiler/src/arm_arch_full.ml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
open Arch_decl
open Arm_common
open Arm_decl


Expand Down Expand Up @@ -56,5 +57,9 @@ module Arm (Lowering_params : Arm_input) : Arch_full.Core_arch

let callstyle = Arch_full.ByReg { call = Some LR; return = false }

let sp_min_align = Wsize.U8
Comment thread
vbgl marked this conversation as resolved.
Outdated

let max_store_size = Wsize.U32

let internal_call_conv = Arm_decl.arm_internal_call_conv
end
63 changes: 63 additions & 0 deletions compiler/src/armv8a_arch_full.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
(* ARMv8A architecture full integration *)
open Arch_decl

module type Armv8a_input = sig
val call_conv : (Armv8a_decl.register, Arch_utils.empty, Arch_utils.empty, Arm_common.rflag, Arm_common.condt) calling_convention
end

(* Create arch_toIdent for ARMv8A *)
let atoI armv8a_decl =
let open Prog in
let mk_var k t s =
V.mk s (Reg(k,Direct)) (Conv.ty_of_cty (Type.atype_of_ltype t)) L._dummy [] in
match Arch_extra.MkAToIdent.mk armv8a_decl mk_var with
| Utils0.Error e ->
let e = Conv.error_of_cerror (Printer.pp_err ~debug:true) e in
raise (Utils.HiError e)
| Utils0.Ok atoI -> atoI

module Armv8a (Lowering_params : Armv8a_input) = struct
module AD = Armv8a_decl

type reg = AD.register
type regx = Arch_utils.empty
type xreg = Arch_utils.empty
type nonrec rflag = Arm_common.rflag
type cond = Arm_common.condt
type asm_op = Armv8a_instr_decl.armv8a_asm_op
type extra_op = Armv8a_extra.armv8a_extra_op

let atoI = atoI AD.armv8a_decl

let asm_e = Armv8a_extra.armv8a_extra atoI

let aparams = Armv8a_params.armv8a_params atoI

let known_implicits = ["NF", "_nf_"; "ZF", "_zf_"; "CF", "_cf_"; "VF", "_vf_"]

let alloc_stack_need_extra _ = false

let is_ct_asm_op (o : asm_op) =
match o with
| Armv8a_instr_decl.ARMv8A_op ((Armv8a_instr_decl.SDIV | Armv8a_instr_decl.UDIV), _) -> false
| _ -> true

let is_ct_asm_extra (_o : extra_op) = true

let not_saved_stack = []

let pp_asm = Pp_arm_v8a.print_prog

let callstyle = Arch_full.ByReg { call = Some Armv8a_decl.R30; return = true }

(* SP must stay 16-byte aligned: SP alignment checking, Arm ARM
DDI0487M.a, D1.4.10.2 (see also the AAPCS64 stack constraints). *)
let sp_min_align = Wsize.U128

(* One X-register store; SIMD (NEON) stores would raise this to u128. *)
let max_store_size = Wsize.U64

let internal_call_conv = Armv8a_decl.armv8a_internal_call_conv

include Lowering_params
end
9 changes: 8 additions & 1 deletion compiler/src/compile.ml
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,14 @@ let compile (type reg regx xreg rflag cond asm_op extra_op)
in

let szs_of_fn fn =
(get_annot fn).stack_zero_strategy
match (get_annot fn).stack_zero_strategy with
| Some (szs, None) when wsize_lt Arch.max_store_size Arch.sp_min_align ->
(* The default clear step is the frame alignment, which
[sp_min_align] may raise beyond the widest store the
architecture can perform in one instruction (u128 vs u64 on
armv8a): cap the default at that width. *)
Some (szs, Some Arch.max_store_size)
| szs -> szs
in

(* This implements an analysis returning the set of variables becoming dead
Expand Down
Loading