2 * 2005 Matthew
Fluet (mfluet@acm
.org
)
7 * ast
-to
-spec
.sml
- Conversion from CKIT
"ast" to a
"spec" (see spec
.sml
).
9 * (C
) 2001, Lucent Technologies
, Bell Labs
11 * author
: Matthias
Blume (blume@research
.bell
-labs
.com
)
13 structure AstToSpec
= struct
16 structure B
= Bindings
18 structure SS
= StringSet
19 structure SM
= StringMap
21 datatype context
= CONTEXT
of { gensym
: unit
-> string, anon
: bool }
25 exception Duplicate
of string
26 exception SkipFunction
of string
28 fun bug m
= raise Fail ("AstToSpec: bug: " ^ m
)
29 fun err m
= raise Fail ("AstToSpec: error: " ^ m
)
30 fun warn m
= TextIO.output (TextIO.stdErr
, "AstToSpec: warning: " ^ m
)
32 fun build
{ bundle
, sizes
: Sizes
.sizes
, collect_enums
,
33 cfiles
, match
, allSU
, eshift
, gensym_suffix
} =
38 fun warnLoc m
= warn (concat
[!curLoc
, ": ", m
])
40 val { ast
, tidtab
, errorCount
, warningCount
,
41 auxiliaryInfo
= { aidtab
, implicits
, env
} } = bundle
43 fun realFunctionDefComing sy
= let
44 fun isTheDef (A
.DECL (A
.FunctionDef (id
, _
, _
), _
, _
)) =
45 Symbol
.equal (#name id
, sy
)
48 List.exists isTheDef ast
51 val srcOf
= SourceMap
.locToString
53 fun isThisFile SourceMap
.UNKNOWN
= false
54 |
isThisFile (SourceMap
.LOC
{ srcFile
, ... }) =
55 List.exists (fn f
=> f
= srcFile
) cfiles
orelse
58 fun includedSU (tag
, loc
) = (allSU
orelse isThisFile loc
)
59 fun includedEnum (tag
, loc
) = isThisFile loc
61 fun includedTy (n
, loc
) = isThisFile loc
63 fun isFunction t
= TypeUtil
.isFunction tidtab t
64 fun getFunction t
= TypeUtil
.getFunction tidtab t
65 fun getCoreType t
= TypeUtil
.getCoreType tidtab t
68 if TypeUtil
.isConst tidtab t
then Spec
.RO
69 else case getCoreType t
of
70 A
.Array (_
, t
) => constness t
73 val sizerec
= { sizes
= sizes
, err
= err
, warn
= warn
, bug
= bug
}
75 fun sizeOf t
= #
bytes (Sizeof
.byteSizeOf sizerec tidtab t
)
77 val bytebits
= #
bits (#char sizes
)
78 val intbits
= #
bits (#
int sizes
)
79 val intalign
= #
align (#
int sizes
)
81 fun getField (m
, l
) = Sizeof
.getField
sizerec (m
, l
)
84 case Sizeof
.fieldOffsets sizerec tidtab t
of
85 NONE
=> bug
"no field offsets"
90 val gtys
= ref SM
.empty
91 val gvars
= ref SM
.empty
92 val gfuns
= ref SM
.empty
93 val named_enums
= ref SM
.empty
94 val anon_enums
= ref SM
.empty
96 val seen_structs
= ref SS
.empty
97 val seen_unions
= ref SS
.empty
100 val tags
= Tidtab
.uidtab () : (string * bool) Tidtab
.uidtab
102 fun mk_context_td tdname
=
112 if n
= 0 then "" else Int.toString n
,
118 fun mk_context_su (parent_tag
, anon
) =
126 concat
[parent_tag
, "'", Int.toString n
]
144 fun tagname (SOME t
, _
, _
) = (t
, false)
145 |
tagname (NONE
, CONTEXT
{ gensym
, anon
}, tid
) =
146 (case Tidtab
.find (tags
, tid
) of
151 Tidtab
.insert (tags
, tid
, (t
, anon
));
155 fun reported_tagname (t
, false) = t
156 |
reported_tagname (t
, true) = t ^ gensym_suffix
158 fun valty C A
.Void
= raise VoidType
159 | valty C A
.Ellipses
= raise Ellipsis
160 | valty
C (A
.Qual (q
, t
)) = valty C t
161 | valty
C (A
.Numeric (_
, _
, A
.SIGNED
, A
.CHAR
, _
)) = Spec
.BASIC Spec
.SCHAR
162 | valty
C (A
.Numeric (_
, _
, A
.UNSIGNED
, A
.CHAR
, _
)) = Spec
.BASIC Spec
.UCHAR
163 | valty
C (A
.Numeric (_
, _
, A
.SIGNED
, A
.SHORT
, _
)) = Spec
.BASIC Spec
.SSHORT
164 | valty
C (A
.Numeric (_
, _
, A
.UNSIGNED
, A
.SHORT
, _
)) = Spec
.BASIC Spec
.USHORT
165 | valty
C (A
.Numeric (_
, _
, A
.SIGNED
, A
.INT
, _
)) = Spec
.BASIC Spec
.SINT
166 | valty
C (A
.Numeric (_
, _
, A
.UNSIGNED
, A
.INT
, _
)) = Spec
.BASIC Spec
.UINT
167 | valty
C (A
.Numeric (_
, _
, A
.SIGNED
, A
.LONG
, _
)) = Spec
.BASIC Spec
.SLONG
168 | valty
C (A
.Numeric (_
, _
, A
.UNSIGNED
, A
.LONG
, _
)) = Spec
.BASIC Spec
.ULONG
169 | valty
C (A
.Numeric (_
, _
, A
.SIGNED
, A
.LONGLONG
, _
)) =
170 Spec
.BASIC Spec
.SLONGLONG
171 | valty
C (A
.Numeric (_
, _
, A
.UNSIGNED
, A
.LONGLONG
, _
)) =
172 Spec
.BASIC Spec
.ULONGLONG
173 | valty
C (A
.Numeric (_
, _
, _
, A
.FLOAT
, _
)) = Spec
.BASIC Spec
.FLOAT
174 | valty
C (A
.Numeric (_
, _
, _
, A
.DOUBLE
, _
)) = Spec
.BASIC Spec
.DOUBLE
175 | valty
C (A
.Numeric (_
, _
, _
, A
.LONGDOUBLE
, _
)) =
176 Spec
.UNIMPLEMENTED
"long double"
177 | valty
C (A
.Array (NONE
, t
)) = valty
C (A
.Pointer t
)
178 | valty
C (A
.Array (SOME (n
, _
), t
)) =
179 let val d
= Int.fromLarge n
181 if d
< 0 then err
"negative dimension"
182 else Spec
.ARR
{ t
= valty C t
, d
= d
, esz
= sizeOf t
}
184 | valty
C (A
.Pointer t
) =
185 (case getCoreType t
of
186 A
.Void
=> Spec
.VOIDPTR
187 | A
.Function f
=> fptrty C f
188 | _
=> Spec
.PTR (cobj C t
))
189 | valty
C (A
.Function f
) = fptrty C f
190 | valty
C (A
.StructRef tid
) = typeref (tid
, Spec
.STRUCT
, C
)
191 | valty
C (A
.UnionRef tid
) = typeref (tid
, Spec
.UNION
, C
)
192 | valty
C (A
.EnumRef tid
) = typeref (tid
, fn t
=> Spec
.ENUM (t
, false), C
)
193 | valty
C (A
.TypeRef tid
) =
194 typeref (tid
, fn _
=> bug
"missing typedef info", C
)
195 | valty C A
.Error
= err
"Error type"
197 and valty_nonvoid C t
= valty C t
198 handle VoidType
=> err
"void variable type"
200 and fun_valty_nonvoid C t
=
201 case valty_nonvoid C t
of
203 raise SkipFunction
"struct argument not supported"
205 raise SkipFunction
"union argument not supported"
208 and typeref (tid
, otherwise
, C
) =
209 case Tidtab
.find (tidtab
, tid
) of
210 NONE
=> bug
"tid not bound in tidtab"
211 | SOME
{ name
= SOME n
, ntype
= NONE
, ... } => otherwise n
212 | SOME
{ name
= NONE
, ntype
= NONE
, ... } =>
213 bug
"both name and ntype missing in tidtab binding"
214 | SOME
{ name
, ntype
= SOME nct
, location
, ... } =>
216 B
.Struct (tid
, members
) =>
217 structty (tid
, name
, C
, members
, location
)
218 | B
.Union (tid
, members
) =>
219 unionty (tid
, name
, C
, members
, location
)
220 | B
.Enum (tid
, edefs
) =>
221 enumty (tid
, name
, C
, edefs
, location
)
222 | B
.Typedef (_
, t
) => let
225 NONE
=> bug
"missing name in typedef"
227 val C
' = mk_context_td n
229 fun sameName
{ src
, name
, spec
} = name
= n
231 if includedTy (n
, location
) andalso
232 not (SM
.inDomain (!gtys
, n
)) then
233 gtys
:= SM
.insert (!gtys
, n
,
234 { src
= srcOf location
,
235 name
= n
, spec
= res
})
240 and enumty (tid
, name
, C
, edefs
, location
) = let
241 val (tag_stem
, anon
) = tagname (name
, C
, tid
)
242 val tag
= reported_tagname (tag_stem
, anon
)
243 fun one ({ name
, uid
, location
, ctype
, kind
}, i
) =
244 { name
= Symbol
.name name
, spec
= i
}
245 val enums
= if anon
then anon_enums
else named_enums
247 enums
:= SM
.insert (!enums
, tag
,
248 { src
= srcOf location
,
252 exclude
= not (includedEnum (tag
, location
)),
253 spec
= map one edefs
});
254 Spec
.ENUM (tag
, anon
)
257 and structty (tid
, name
, C
, members
, location
) = let
258 val (tag_stem
, anon
) = tagname (name
, C
, tid
)
259 val tag
= reported_tagname (tag_stem
, anon
)
260 val ty
= Spec
.STRUCT tag
261 val C
' = mk_context_su (tag_stem
, anon
)
263 if SS
.member (!seen_structs
, tag
) then ()
265 val _
= seen_structs
:= SS
.add (!seen_structs
, tag
)
267 val fol
= fieldOffsets (A
.StructRef tid
)
268 val ssize
= sizeOf (A
.StructRef tid
)
270 fun bfspec (offset
, bits
, shift
, (c
, t
)) = let
272 val bits
= Word.fromLargeInt bits
273 val shift
= eshift (shift
, intbits
, bits
)
274 val r
= { offset
= offset
,
280 Spec
.BASIC Spec
.UINT
=> Spec
.UBF r
281 | Spec
.BASIC Spec
.SINT
=> Spec
.SBF r
282 | _
=> err
"non-int bitfield"
285 fun synthetic (synth
, (_
, false), _
) = ([], synth
)
286 |
synthetic (synth
, (endp
, true), startp
) =
287 if endp
= startp
then ([], synth
)
288 else ([{ name
= Int.toString synth
,
292 Spec
.ARR
{ t
= Spec
.BASIC Spec
.UCHAR
,
295 synthetic
= true } }],
298 fun build ([], synth
, gap
) =
299 #
1 (synthetic (synth
, gap
, ssize
))
300 |
build ((t
, SOME m
, NONE
) :: rest
, synth
, gap
) =
301 let val bitoff
= #
bitOffset (getField (m
, fol
))
302 val bytoff
= bitoff
div bytebits
303 val (filler
, synth
) =
304 synthetic (synth
, gap
, bytoff
)
305 val endp
= bytoff
+ sizeOf t
307 if bitoff
mod bytebits
<> 0 then
308 bug
"non-bitfield not on byte boundary"
311 { name
= Symbol
.name (#name m
),
315 synthetic
= false } } ::
316 build (rest
, synth
, (endp
, false))
318 |
build ((t
, SOME m
, SOME b
) :: rest
, synth
, gap
) =
319 let val bitoff
= #
bitOffset (getField (m
, fol
))
321 (intalign
* (bitoff
div intalign
))
323 val gap
= (#
1 gap
, true)
325 { name
= Symbol
.name (#name m
),
326 spec
= bfspec (bytoff
, b
,
329 build (rest
, synth
, gap
)
331 |
build ((t
, NONE
, SOME _
) :: rest
, synth
, gap
) =
332 build (rest
, synth
, (#
1 gap
, true))
333 |
build ((_
, NONE
, NONE
) :: _
, _
, _
) =
334 bug
"unnamed struct member (not bitfield)"
336 val fields
= build (members
, 0, (0, false))
338 structs
:= { src
= srcOf location
,
341 size
= Word.fromInt ssize
,
342 exclude
= not (includedSU (tag
, location
)),
343 fields
= fields
} :: !structs
348 and unionty (tid
, name
, C
, members
, location
) = let
349 val (tag_stem
, anon
) = tagname (name
, C
, tid
)
350 val tag
= reported_tagname (tag_stem
, anon
)
351 val C
' = mk_context_su (tag_stem
, anon
)
352 val ty
= Spec
.UNION tag
354 fun mkField (t
, m
: A
.member
) = let
357 { name
= Symbol
.name (#name m
),
358 spec
= Spec
.OFIELD
{ offset
= 0,
360 synthetic
= false } }
363 if SS
.member (!seen_unions
, tag
) then ()
365 val _
= seen_unions
:= SS
.add (!seen_unions
, tag
)
366 val all
= map mkField members
368 unions
:= { src
= srcOf location
,
371 size
= Word.fromInt (sizeOf (A
.UnionRef tid
)),
372 exclude
= not (includedSU (tag
, location
)),
373 all
= all
} :: !unions
378 and cobj C t
= (constness t
, valty_nonvoid C t
)
380 and fptrty C f
= Spec
.FPTR (cft C f
)
382 and cft
C (res
, args
) =
383 { res
= case getCoreType res
of
385 | _
=> SOME (valty_nonvoid C res
),
387 [(arg
, _
)] => (case getCoreType arg
of
389 | _
=> [fun_valty_nonvoid C arg
])
390 | _
=> let fun build
[] = []
392 ([fun_valty_nonvoid C x
]
395 ("varargs not supported; \
396 \ignoring the ellipsis\n");
398 |
build ((x
, _
) :: xs
) =
399 fun_valty_nonvoid C x
:: build xs
404 fun ft_argnames (res
, args
) =
405 let val optids
= map (fn (_
, optid
) => optid
) args
407 if List.exists (not
o isSome
) optids
then NONE
408 else SOME (map valOf optids
)
411 fun functionName (f
: A
.id
, ailo
: A
.id list option
) = let
412 val n
= Symbol
.name (#name f
)
413 val anlo
= Option
.map (map (Symbol
.name
o #name
)) ailo
415 if n
= "_init" orelse n
= "_fini" orelse
416 SM
.inDomain (!gfuns
, n
) then ()
419 (case getFunction (#ctype f
) of
421 gfuns
:= SM
.insert (!gfuns
, n
,
424 spec
= cft tl_context fs
,
426 | NONE
=> bug
"function without function type")
427 handle SkipFunction reason
=>
428 warnLoc (reason ^
"; skipping function\n")
432 | A
.DEFAULT
=> doit ()
439 fun varDecl (v
: A
.id
) = let
441 (case getFunction (#ctype v
) of
442 SOME fs
=> if realFunctionDefComing (#name v
) then ()
443 else functionName (v
, ft_argnames fs
)
445 let val n
= Symbol
.name (#name v
)
447 if SM
.inDomain (!gvars
, n
) then ()
448 else gvars
:= SM
.insert
450 { src
= !curLoc
, name
= n
,
451 spec
= cobj tl_context
457 | A
.DEFAULT
=> doit ()
464 (* Spec
.SINT is an arbitrary choice
; the value gets
466 (ignore (typeref (tid
, fn _
=> Spec
.BASIC Spec
.SINT
, tl_context
))
467 handle VoidType
=> ()) (* ignore
type aliases for void
*)
469 fun declaration (A
.TypeDecl
{ tid
, ... }) = dotid tid
470 |
declaration (A
.VarDecl (v
, _
)) = varDecl v
472 fun coreExternalDecl (A
.ExternalDecl d
) = declaration d
473 |
coreExternalDecl (A
.FunctionDef (f
, argids
, _
)) =
474 functionName (f
, SOME argids
)
475 |
coreExternalDecl (A
.ExternalDeclExt _
) = ()
477 fun externalDecl (A
.DECL (d
, _
, l
)) =
478 if isThisFile l
then (curLoc
:= SourceMap
.locToString l
;
482 fun doast l
= app externalDecl l
484 fun gen_enums () = let
485 val ael
= SM
.listItems (!anon_enums
)
486 val nel
= SM
.listItems (!named_enums
)
489 | x $ y
= x
:: ", " :: y
490 fun onev (v
as { name
, spec
}, m
) =
491 if SM
.inDomain (m
, name
) then raise Duplicate name
492 else SM
.insert (m
, name
, v
)
493 fun onee ({ src
, tag
, anon
, spec
, descr
, exclude
}, (m
, sl
)) =
494 (foldl onev m spec
, src $ sl
)
496 if collect_enums
then
497 let val (m
, sl
) = foldl
onee (SM
.empty
, []) ael
499 if SM
.isEmpty m
then nel
500 else { src
= concat (rev sl
),
503 descr
= "collected from unnamed enumerations",
505 spec
= SM
.listItems m
}
507 end handle Duplicate name
=>
508 (warn (concat
["constant ", name
,
509 " defined more than once;\
510 \ disabling `-collect'\n"]);
516 app (dotid
o #
1) (Tidtab
.listItemsi tidtab
);
517 { structs
= !structs
,
519 gtys
= SM
.listItems (!gtys
),
520 gvars
= SM
.listItems (!gvars
),
521 gfuns
= SM
.listItems (!gfuns
),
522 enums
= gen_enums () } : Spec
.spec