@@ -16,6 +16,8 @@ module Affinescriptiser.ABI.Layout
1616import Affinescriptiser.ABI.Types
1717import Data.Vect
1818import Data.So
19+ import Data.Nat
20+ import Decidable.Equality
1921
2022%default total
2123
@@ -29,42 +31,65 @@ paddingFor : (offset : Nat) -> (alignment : Nat) -> Nat
2931paddingFor offset alignment =
3032 if offset `mod` alignment == 0
3133 then 0
32- else alignment - (offset `mod` alignment)
34+ else minus alignment (offset `mod` alignment)
3335
3436||| Proof that alignment divides aligned size
3537public export
3638data Divides : Nat -> Nat -> Type where
3739 DivideBy : (k : Nat ) -> {n : Nat } -> {m : Nat } -> (m = k * n) -> Divides n m
3840
41+ ||| Sound decision procedure: does n divide m?
42+ ||| For n = S k, compute the candidate quotient q = m `div` (S k) and check
43+ ||| whether m really equals q * (S k). When it does, the equality witnesses
44+ ||| `Divides n m` directly; otherwise we have no evidence and return Nothing.
45+ ||| (Division by zero yields Nothing — zero divides nothing nonzero.)
46+ public export
47+ decDivides : (n : Nat ) -> (m : Nat ) -> Maybe (Divides n m)
48+ decDivides Z m = case decEq m Z of
49+ Yes prf => Just (DivideBy Z (rewrite prf in Refl ))
50+ No _ => Nothing
51+ decDivides (S k) m =
52+ let q = m `div` (S k) in
53+ case decEq m (q * (S k)) of
54+ Yes prf => Just (DivideBy q prf)
55+ No _ => Nothing
56+
3957||| Round up to next alignment boundary
4058public export
4159alignUp : (size : Nat ) -> (alignment : Nat ) -> Nat
4260alignUp size alignment =
4361 size + paddingFor size alignment
4462
45- ||| Proof that alignUp produces aligned result
63+ ||| Sound divisibility check for an aligned size. The general theorem
64+ ||| "alignUp size align is always divisible by align" needs div/mod lemmas and
65+ ||| is tracked as residual proof work; here we *decide* it via `decDivides`,
66+ ||| which returns a genuine witness when it holds. For the concrete ABI layouts
67+ ||| below, divisibility is proven outright with `DivideBy`.
4668public export
47- alignUpCorrect : (size : Nat ) -> (align : Nat ) -> (align > 0) -> Divides align (alignUp size align)
48- alignUpCorrect size align prf =
49- DivideBy (( size + paddingFor size align) `div` align) Refl
69+ alignUpDivides : (size : Nat ) -> (align : Nat ) ->
70+ Maybe (Divides align (alignUp size align))
71+ alignUpDivides size align = decDivides align (alignUp size align)
5072
5173-- ------------------------------------------------------------------------------
5274-- WASM Linear Memory Layout
5375-- ------------------------------------------------------------------------------
5476
55- ||| WASM page size is always 64KiB (65536 bytes)
77+ ||| WASM page size is always 64KiB (65536 bytes).
78+ ||| These memory-magnitude constants are `Integer` rather than `Nat`: the 4GiB
79+ ||| product below is far too large to normalise as a unary `Nat` at
80+ ||| type-checking time, and machine-memory sizes are naturally machine integers.
5681public export
57- wasmPageSize : Nat
82+ wasmPageSize : Integer
5883wasmPageSize = 65536
5984
6085||| Maximum WASM linear memory: 4GiB (65536 pages)
6186public export
62- wasmMaxPages : Nat
87+ wasmMaxPages : Integer
6388wasmMaxPages = 65536
6489
65- ||| Maximum WASM linear memory in bytes
90+ ||| Maximum WASM linear memory in bytes (4 294 967 296)
6691public export
67- wasmMaxMemory : Nat
92+ wasmMaxMemory : Integer
6893wasmMaxMemory = wasmPageSize * wasmMaxPages
6994
7095||| A region in WASM linear memory
@@ -151,7 +176,7 @@ record StructLayout where
151176
152177||| Calculate total struct size with padding
153178public export
154- calcStructSize : Vect n Field -> Nat -> Nat
179+ calcStructSize : Vect k Field -> Nat -> Nat
155180calcStructSize [] align = 0
156181calcStructSize (f :: fs) align =
157182 let lastOffset = foldl (\ acc, field => nextFieldOffset field) f. offset fs
@@ -160,23 +185,25 @@ calcStructSize (f :: fs) align =
160185
161186||| Proof that field offsets are correctly aligned
162187public export
163- data FieldsAligned : Vect n Field -> Type where
188+ data FieldsAligned : Vect k Field -> Type where
164189 NoFields : FieldsAligned []
165190 ConsField :
166191 (f : Field) ->
167- (rest : Vect n Field) ->
192+ (rest : Vect k Field) ->
168193 Divides f.alignment f.offset ->
169194 FieldsAligned rest ->
170195 FieldsAligned (f :: rest)
171196
172197||| Verify a struct layout is valid
173198public export
174- verifyLayout : (fields : Vect n Field) -> (align : Nat ) -> Either String StructLayout
199+ verifyLayout : (fields : Vect k Field) -> (align : Nat ) -> Either String StructLayout
175200verifyLayout fields align =
176201 let size = calcStructSize fields align
177- in case decSo (size >= sum (map (\ f => f. size) fields)) of
178- Yes prf => Right (MkStructLayout fields size align)
179- No _ => Left " Invalid struct size"
202+ in case choose (size >= sum (map (\ f => f. size) fields)) of
203+ Right _ => Left " Invalid struct size"
204+ Left szPrf => case decDivides align size of
205+ Nothing => Left " Total size is not a multiple of the alignment"
206+ Just dvd => Right (MkStructLayout fields size align {sizeCorrect = szPrf} {aligned = dvd})
180207
181208-- ------------------------------------------------------------------------------
182209-- WASM-Specific Layout Proofs
@@ -185,7 +212,7 @@ verifyLayout fields align =
185212||| Proof that a struct layout fits within a single WASM page
186213public export
187214data FitsInPage : StructLayout -> Type where
188- PageFit : (layout : StructLayout) -> {auto 0 ok : So (layout.totalSize <= wasmPageSize)} -> FitsInPage layout
215+ PageFit : (layout : StructLayout) -> {auto 0 ok : So (natToInteger layout.totalSize <= Layout. wasmPageSize)} -> FitsInPage layout
189216
190217||| Layout of the affine resource table in WASM linear memory
191218||| The resource table is a contiguous array of resource slots at a fixed
@@ -208,7 +235,7 @@ tableSize t = t.capacity * resourceLayoutSize t.platform
208235||| Proof that a resource table fits in WASM linear memory
209236public export
210237data TableInBounds : ResourceTable -> Type where
211- TableOk : (t : ResourceTable) -> {auto 0 ok : So (t.baseOffset + tableSize t <= wasmMaxMemory)} -> TableInBounds t
238+ TableOk : (t : ResourceTable) -> {auto 0 ok : So (natToInteger ( t.baseOffset + tableSize t) <= Layout. wasmMaxMemory)} -> TableInBounds t
212239
213240||| Get the WASM region for the nth resource in a table
214241public export
@@ -230,11 +257,25 @@ data CABICompliant : StructLayout -> Type where
230257 FieldsAligned layout.fields ->
231258 CABICompliant layout
232259
260+ ||| Decide whether every field of a vector is aligned (offset divisible by
261+ ||| the field's alignment), producing a FieldsAligned witness when so.
262+ public export
263+ decFieldsAligned : (fields : Vect k Field) -> Maybe (FieldsAligned fields)
264+ decFieldsAligned [] = Just NoFields
265+ decFieldsAligned (f :: fs) =
266+ case decDivides f. alignment f. offset of
267+ Nothing => Nothing
268+ Just dvd => case decFieldsAligned fs of
269+ Nothing => Nothing
270+ Just rest => Just (ConsField f fs dvd rest)
271+
233272||| Check if layout follows C ABI
234273public export
235274checkCABI : (layout : StructLayout) -> Either String (CABICompliant layout)
236275checkCABI layout =
237- Right (CABIOk layout ? fieldsAlignedProof)
276+ case decFieldsAligned layout. fields of
277+ Just prf => Right (CABIOk layout prf)
278+ Nothing => Left " Struct fields are not correctly aligned for C ABI"
238279
239280-- ------------------------------------------------------------------------------
240281-- Example: Tracked File Descriptor Layout
@@ -254,6 +295,8 @@ trackedFDLayout =
254295 ]
255296 20 -- Total size: 20 bytes
256297 4 -- Alignment: 4 bytes (WASM native)
298+ {sizeCorrect = Oh }
299+ {aligned = DivideBy 5 Refl } -- 20 = 5 * 4
257300
258301-- ------------------------------------------------------------------------------
259302-- Offset Calculation
@@ -269,5 +312,8 @@ fieldOffset layout name =
269312
270313||| Proof that field offset is within struct bounds
271314public export
272- offsetInBounds : (layout : StructLayout) -> (f : Field) -> So (f.offset + f.size <= layout.totalSize)
273- offsetInBounds layout f = ? offsetInBoundsProof
315+ offsetInBounds : (layout : StructLayout) -> (f : Field) -> Maybe (So (f.offset + f.size <= layout.totalSize))
316+ offsetInBounds layout f =
317+ case choose (f. offset + f. size <= layout. totalSize) of
318+ Left ok => Just ok
319+ Right _ => Nothing
0 commit comments