@@ -18,6 +18,8 @@ module Eclexiaiser.ABI.Layout
1818import Eclexiaiser.ABI.Types
1919import Data.Vect
2020import Data.So
21+ import Data.Nat
22+ import Decidable.Equality
2123
2224%default total
2325
@@ -31,24 +33,38 @@ paddingFor : (offset : Nat) -> (alignment : Nat) -> Nat
3133paddingFor offset alignment =
3234 if offset `mod` alignment == 0
3335 then 0
34- else alignment - (offset `mod` alignment)
36+ else minus alignment (offset `mod` alignment)
3537
3638||| Proof that alignment divides aligned size
3739public export
3840data Divides : Nat -> Nat -> Type where
3941 DivideBy : (k : Nat ) -> {n : Nat } -> {m : Nat } -> (m = k * n) -> Divides n m
4042
43+ ||| Decision procedure for divisibility.
44+ ||| For n = S j, compute q = m `div` (S j) and check m = q * (S j).
45+ ||| Division does not reduce definitionally, so we confirm the candidate
46+ ||| witness via decidable equality on the (reducing) multiplication.
47+ public export
48+ decDivides : (n : Nat ) -> (m : Nat ) -> Maybe (Divides n m)
49+ decDivides Z m = Nothing
50+ decDivides (S j) m =
51+ let q = m `div` (S j) in
52+ case decEq m (q * (S j)) of
53+ Yes prf => Just (DivideBy q prf)
54+ No _ => Nothing
55+
4156||| Round up to next alignment boundary
4257public export
4358alignUp : (size : Nat ) -> (alignment : Nat ) -> Nat
4459alignUp size alignment =
4560 size + paddingFor size alignment
4661
47- ||| Proof that alignUp produces aligned result
62+ ||| Decide whether alignUp produced a genuine multiple of the alignment.
63+ ||| (alignUp is only a multiple of `align` when `align > 0`, so the
64+ ||| witness is returned in Maybe rather than asserted unconditionally.)
4865public export
49- alignUpCorrect : (size : Nat ) -> (align : Nat ) -> (align > 0) -> Divides align (alignUp size align)
50- alignUpCorrect size align prf =
51- DivideBy ((size + paddingFor size align) `div` align) Refl
66+ alignUpAligned : (size : Nat ) -> (align : Nat ) -> Maybe (Divides align (alignUp size align))
67+ alignUpAligned size align = decDivides align (alignUp size align)
5268
5369-- ------------------------------------------------------------------------------
5470-- Struct Field Layout
@@ -72,15 +88,15 @@ nextFieldOffset f = alignUp (f.offset + f.size) f.alignment
7288public export
7389record StructLayout where
7490 constructor MkStructLayout
75- fields : Vect n Field
91+ fields : Vect len Field
7692 totalSize : Nat
7793 alignment : Nat
7894 {auto 0 sizeCorrect : So (totalSize >= sum (map (\f => f.size) fields))}
7995 {auto 0 aligned : Divides alignment totalSize}
8096
8197||| Calculate total struct size with padding
8298public export
83- calcStructSize : Vect n Field -> Nat -> Nat
99+ calcStructSize : Vect k Field -> Nat -> Nat
84100calcStructSize [] align = 0
85101calcStructSize (f :: fs) align =
86102 let lastOffset = foldl (\ acc, field => nextFieldOffset field) f. offset fs
@@ -89,23 +105,28 @@ calcStructSize (f :: fs) align =
89105
90106||| Proof that field offsets are correctly aligned
91107public export
92- data FieldsAligned : Vect n Field -> Type where
108+ data FieldsAligned : Vect k Field -> Type where
93109 NoFields : FieldsAligned []
94110 ConsField :
95111 (f : Field) ->
96- (rest : Vect n Field) ->
112+ (rest : Vect k Field) ->
97113 Divides f.alignment f.offset ->
98114 FieldsAligned rest ->
99115 FieldsAligned (f :: rest)
100116
101117||| Verify a struct layout is valid
102118public export
103- verifyLayout : (fields : Vect n Field) -> (align : Nat ) -> Either String StructLayout
119+ verifyLayout : (fields : Vect k Field) -> (align : Nat ) -> Either String StructLayout
104120verifyLayout fields align =
105121 let size = calcStructSize fields align
106- in case decSo (size >= sum (map (\ f => f. size) fields)) of
107- Yes prf => Right (MkStructLayout fields size align)
108- No _ => Left " Invalid struct size"
122+ in case choose (size >= sum (map (\ f => f. size) fields)) of
123+ Left szPrf =>
124+ case decDivides align size of
125+ Just alPrf =>
126+ Right (MkStructLayout fields size align
127+ {sizeCorrect = szPrf} {aligned = alPrf})
128+ Nothing => Left " Total size is not a multiple of alignment"
129+ Right _ => Left " Invalid struct size"
109130
110131-- ------------------------------------------------------------------------------
111132-- Platform-Specific Layouts
@@ -136,11 +157,26 @@ data CABICompliant : StructLayout -> Type where
136157 FieldsAligned layout.fields ->
137158 CABICompliant layout
138159
160+ ||| Decide whether every field's offset is a multiple of its alignment,
161+ ||| building a FieldsAligned witness directly from per-field decDivides.
162+ public export
163+ decFieldsAligned : (fs : Vect k Field) -> Maybe (FieldsAligned fs)
164+ decFieldsAligned [] = Just NoFields
165+ decFieldsAligned (f :: fs) =
166+ case decDivides f. alignment f. offset of
167+ Nothing => Nothing
168+ Just dvd =>
169+ case decFieldsAligned fs of
170+ Nothing => Nothing
171+ Just rest => Just (ConsField f fs dvd rest)
172+
139173||| Check if layout follows C ABI
140174public export
141175checkCABI : (layout : StructLayout) -> Either String (CABICompliant layout)
142176checkCABI layout =
143- Right (CABIOk layout ? fieldsAlignedProof)
177+ case decFieldsAligned layout. fields of
178+ Just prf => Right (CABIOk layout prf)
179+ Nothing => Left " Struct fields are not correctly aligned"
144180
145181-- ------------------------------------------------------------------------------
146182-- EnergyMeasurement Layout
@@ -165,11 +201,19 @@ energyMeasurementLayout =
165201 ]
166202 32 -- Total size: 32 bytes (28 data + 4 padding)
167203 8 -- Alignment: 8 bytes
204+ {sizeCorrect = Oh }
205+ {aligned = DivideBy 4 Refl } -- 32 = 4 * 8
168206
169207||| Proof that EnergyMeasurement layout is C-ABI compliant
170208export
171- energyMeasurementValid : CABICompliant energyMeasurementLayout
172- energyMeasurementValid = CABIOk energyMeasurementLayout ? energyMeasurementAligned
209+ energyMeasurementValid : CABICompliant Layout.energyMeasurementLayout
210+ energyMeasurementValid =
211+ CABIOk energyMeasurementLayout
212+ (ConsField _ _ (DivideBy 0 Refl ) -- offset 0 = 0 * 8
213+ (ConsField _ _ (DivideBy 1 Refl ) -- offset 8 = 1 * 8
214+ (ConsField _ _ (DivideBy 2 Refl ) -- offset 16 = 2 * 8
215+ (ConsField _ _ (DivideBy 6 Refl ) -- offset 24 = 6 * 4
216+ NoFields ))))
173217
174218-- ------------------------------------------------------------------------------
175219-- CarbonQuery Layout
@@ -195,11 +239,20 @@ carbonQueryLayout =
195239 ]
196240 24 -- Total size: 24 bytes
197241 8 -- Alignment: 8 bytes
242+ {sizeCorrect = Oh }
243+ {aligned = DivideBy 3 Refl } -- 24 = 3 * 8
198244
199245||| Proof that CarbonQuery layout is C-ABI compliant
200246export
201- carbonQueryValid : CABICompliant carbonQueryLayout
202- carbonQueryValid = CABIOk carbonQueryLayout ? carbonQueryAligned
247+ carbonQueryValid : CABICompliant Layout.carbonQueryLayout
248+ carbonQueryValid =
249+ CABIOk carbonQueryLayout
250+ (ConsField _ _ (DivideBy 0 Refl ) -- offset 0 = 0 * 4
251+ (ConsField _ _ (DivideBy 1 Refl ) -- offset 4 = 1 * 4
252+ (ConsField _ _ (DivideBy 1 Refl ) -- offset 8 = 1 * 8
253+ (ConsField _ _ (DivideBy 4 Refl ) -- offset 16 = 4 * 4
254+ (ConsField _ _ (DivideBy 5 Refl ) -- offset 20 = 5 * 4
255+ NoFields )))))
203256
204257-- ------------------------------------------------------------------------------
205258-- BudgetEnforcement Layout
@@ -226,11 +279,20 @@ budgetEnforcementLayout =
226279 ]
227280 40 -- Total size: 40 bytes (36 data + 4 padding)
228281 8 -- Alignment: 8 bytes
282+ {sizeCorrect = Oh }
283+ {aligned = DivideBy 5 Refl } -- 40 = 5 * 8
229284
230285||| Proof that BudgetEnforcement layout is C-ABI compliant
231286export
232- budgetEnforcementValid : CABICompliant budgetEnforcementLayout
233- budgetEnforcementValid = CABIOk budgetEnforcementLayout ? budgetEnforcementAligned
287+ budgetEnforcementValid : CABICompliant Layout.budgetEnforcementLayout
288+ budgetEnforcementValid =
289+ CABIOk budgetEnforcementLayout
290+ (ConsField _ _ (DivideBy 0 Refl ) -- offset 0 = 0 * 8
291+ (ConsField _ _ (DivideBy 1 Refl ) -- offset 8 = 1 * 8
292+ (ConsField _ _ (DivideBy 2 Refl ) -- offset 16 = 2 * 8
293+ (ConsField _ _ (DivideBy 3 Refl ) -- offset 24 = 3 * 8
294+ (ConsField _ _ (DivideBy 8 Refl ) -- offset 32 = 8 * 4
295+ NoFields )))))
234296
235297-- ------------------------------------------------------------------------------
236298-- Offset Calculation
@@ -244,7 +306,12 @@ fieldOffset layout name =
244306 Just idx => Just (finToNat idx ** index idx layout. fields)
245307 Nothing => Nothing
246308
247- ||| Proof that field offset is within struct bounds
309+ ||| Decide whether a field fits within the struct's total size.
310+ ||| Returns a witness only when the bound genuinely holds (it is false
311+ ||| in general), so the result is wrapped in Maybe.
248312public export
249- offsetInBounds : (layout : StructLayout) -> (f : Field) -> So (f.offset + f.size <= layout.totalSize)
250- offsetInBounds layout f = ? offsetInBoundsProof
313+ offsetInBounds : (layout : StructLayout) -> (f : Field) -> Maybe (So (f.offset + f.size <= layout.totalSize))
314+ offsetInBounds layout f =
315+ case choose (f. offset + f. size <= layout. totalSize) of
316+ Left ok => Just ok
317+ Right _ => Nothing
0 commit comments