-
Notifications
You must be signed in to change notification settings - Fork 108
/
Absyn-Expr.ML
509 lines (455 loc) · 17 KB
/
Absyn-Expr.ML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
(*
* Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
*
* SPDX-License-Identifier: BSD-2-Clause
*)
structure ExprDatatype =
struct
type numliteral_info =
{value: IntInf.int, suffix : string, base : StringCvt.radix}
(* use of IntInf makes no difference in Poly, but is useful in mlton *)
type 'a wrap = 'a RegionExtras.wrap
type 'a ctype = 'a CType.ctype
datatype literalconstant_node =
NUMCONST of numliteral_info
| STRING_LIT of string
type literalconstant = literalconstant_node wrap
datatype binoptype =
LogOr | LogAnd | Equals | NotEquals | BitwiseAnd | BitwiseOr
| BitwiseXOr
| Lt | Gt | Leq | Geq | Plus | Minus | Times | Divides | Modulus
| RShift | LShift
datatype unoptype = Negate | Not | Addr | BitNegate
type var_info = (int CType.ctype * more_info) option ref
datatype expr_node =
BinOp of binoptype * expr * expr
| UnOp of unoptype * expr
| CondExp of expr * expr * expr
| Constant of literalconstant
| Var of string * var_info
| StructDot of expr * string
| ArrayDeref of expr * expr
| Deref of expr
| TypeCast of expr ctype wrap * expr
| Sizeof of expr
| SizeofTy of expr ctype wrap
| EFnCall of expr * expr list
| CompLiteral of expr ctype * (designator list * initializer) list
| Arbitrary of expr ctype
| MKBOOL of expr
and expr = E of expr_node wrap
and initializer =
InitE of expr
| InitList of (designator list * initializer) list
and designator =
DesignE of expr
| DesignFld of string
datatype ecenv =
CE of {enumenv : (IntInf.int * string option) Symtab.table,
(* lookup is from econst name to value and the
name of the type it belongs to, if any
(they can be anonymous) *)
typing : expr -> int ctype,
structsize : string -> int}
end (* struct *)
signature EXPR =
sig
datatype binoptype = datatype ExprDatatype.binoptype
datatype unoptype = datatype ExprDatatype.unoptype
datatype expr_node = datatype ExprDatatype.expr_node
datatype initializer = datatype ExprDatatype.initializer
datatype designator = datatype ExprDatatype.designator
datatype literalconstant_node = datatype ExprDatatype.literalconstant_node
type expr
type numliteral_info = ExprDatatype.numliteral_info
type literalconstant = ExprDatatype.literalconstant
val unopname : unoptype -> string
val unop_compare : unoptype * unoptype -> order
val binopname : binoptype -> string
val binop_compare : binoptype * binoptype -> order
val expr_string : expr -> string
val numconst_type : numliteral_info -> 'a CType.ctype (* can raise Subscript *)
val eval_litconst : literalconstant -> IntInf.int * 'a CType.ctype
val enode : expr -> expr_node
val eleft : expr -> SourcePos.t
val eright : expr -> SourcePos.t
val ewrap : expr_node * SourcePos.t * SourcePos.t -> expr
val ebogwrap : expr_node -> expr
val eFail : expr * string -> exn
val expr_compare : expr * expr -> order
val is_number : expr -> bool
val fncall_free : expr -> bool
val eneeds_sc_protection : expr -> bool
val expr_int : IntInf.int -> expr
val eval_binop :
(SourcePos.t * SourcePos.t) ->
binoptype * (IntInf.int * 'a CType.ctype) * (IntInf.int * 'a CType.ctype) ->
IntInf.int * 'a CType.ctype
datatype ecenv = datatype ExprDatatype.ecenv (* "enumerated constant environment" *)
val eclookup : ecenv -> string -> (IntInf.int * string option) option
val constify_abtype : ecenv -> expr CType.ctype -> int CType.ctype
val consteval : ecenv -> expr -> IntInf.int
end
structure Expr : EXPR =
struct
open RegionExtras
open CType
open ExprDatatype
(* can raise Subscript *)
fun numconst_type {value,base,suffix} = let
val suffix = String.translate (str o Char.toLower) suffix
val suffixU = CharVector.exists (fn c => c = #"u") suffix
val suffixLL = String.isPrefix "ll" suffix orelse
String.isSuffix "ll" suffix
val suffixL = (String.isPrefix "l" suffix orelse
String.isSuffix "l" suffix)
val type_index = if suffixLL then 4 else if suffixL then 2 else 0
(* where to start looking in the typesequence *)
val typesequence = [Signed Int, Unsigned Int,
Signed Long, Unsigned Long,
Signed LongLong, Unsigned LongLong]
fun search i = let
val candidate = List.nth(typesequence, i)
in
if suffixU andalso is_signed candidate then search (i + 1)
else if base = StringCvt.DEC andalso not suffixU andalso
not (is_signed candidate)
then search (i + 1)
else if imax candidate >= value then candidate
else search (i + 1)
end
in
search type_index
end
fun eval_litconst lc = let
val fi = IntInf.fromInt
in
case node lc of
NUMCONST nc => (#value nc, numconst_type nc)
| STRING_LIT _ => (Feedback.errorStr'(left lc, right lc,
"Don't evaluate string literals!");
(fi 1, Signed Int))
end
fun unopname opn =
case opn of
Negate => "-"
| Not => "!"
| Addr => "&"
| BitNegate => "~"
val unop_compare = inv_img_cmp unopname String.compare
fun binopname opn =
case opn of
Lt => "<"
| Gt => ">"
| Leq => "<="
| Geq => ">="
| Equals => "=="
| NotEquals => "!="
| LogOr => "||"
| LogAnd => "&&"
| Plus => "+"
| Times => "*"
| Minus => "-"
| Divides => "/"
| BitwiseAnd => "&"
| BitwiseOr => "|"
| BitwiseXOr => "^"
| RShift => ">>"
| LShift => "<<"
| Modulus => "%"
val binop_compare = inv_img_cmp binopname String.compare
type var_info = (int ctype * more_info) option ref
fun eleft (E w) = valOf (Region.left (Region.Wrap.region w))
handle Option => bogus
fun eright (E w) = valOf (Region.right (Region.Wrap.region w))
handle Option => bogus
fun enode (E w) = node w
fun eregion (E w) = Region.Wrap.region w
fun ewrap (n, l, r) = E (wrap(n,l,r))
fun ebogwrap en = E (bogwrap en)
fun eFail (e, s) = Fail (Region.toString (eregion e) ^ ": " ^ s)
fun fncall_free e =
case enode e of
BinOp(_, e1, e2) => fncall_free e1 andalso fncall_free e2
| UnOp(_, e) => fncall_free e
| CondExp(e1,e2,e3) => fncall_free e1 andalso fncall_free e2 andalso
fncall_free e3
| StructDot(e, _) => fncall_free e
| ArrayDeref(e1, e2) => fncall_free e1 andalso fncall_free e2
| Deref e => fncall_free e
| TypeCast(_, e) => fncall_free e
| EFnCall _ => false
| CompLiteral (_, dilist) => List.all dinit_fncall_free dilist
| _ => true
and dinit_fncall_free (dis, init) =
List.all difncall_free dis andalso init_fncall_free init
and difncall_free (DesignE e) = fncall_free e
| difncall_free (DesignFld _) = true
and init_fncall_free (InitE e) = fncall_free e
| init_fncall_free (InitList dis) = List.all dinit_fncall_free dis
fun is_number e =
case enode e of
Constant litw => (case node litw of NUMCONST _ => true | _ => false)
| _ => false
fun sint i = {value = i, suffix = "", base = StringCvt.DEC}
fun expr_int i = ebogwrap (Constant (bogwrap (NUMCONST (sint i))))
fun expr_string e =
case enode e of
BinOp _ => "BinOp"
| UnOp _ => "UnOp"
| CondExp _ => "CondExp"
| Constant _ => "Constant"
| Var(nm, _) => "Var" ^ nm
| StructDot _ => "StructDot"
| ArrayDeref _ => "ArrayDeref"
| Deref _ => "Deref"
| TypeCast _ => "TypeCast"
| Sizeof _ => "Sizeof"
| SizeofTy _ => "SizeofTy"
| EFnCall _ => "EFnCall"
| CompLiteral _ => "CompLit"
| Arbitrary _ => "Arbitrary"
| MKBOOL _ => "MKBOOL"
| _ => "[whoa! Unknown expr type]"
fun radn r = let
open StringCvt
in
case r of
BIN => 2
| OCT => 8
| DEC => 10
| HEX => 16
end
fun nli_compare (nli1, nli2) = let
val {value = v1, suffix = s1, base = r1} = nli1
val {value = v2, suffix = s2, base = r2} = nli2
in
pair_compare (pair_compare (IntInf.compare, String.compare),
measure_cmp radn)
(((v1, s1), r1), ((v2, s2), r2))
end
(* ignores location information *)
fun lc_compare (lc1, lc2) =
case (node lc1, node lc2) of
(NUMCONST nli1, NUMCONST nli2) => nli_compare (nli1, nli2)
| (NUMCONST _, _) => LESS
| (_, NUMCONST _) => GREATER
| (STRING_LIT s1, STRING_LIT s2) => String.compare(s1, s2)
fun vi_compare((s1,vi1 : var_info), (s2,vi2)) =
case String.compare(s1, s2) of
EQUAL =>
if vi1 = vi2 then EQUAL
else option_compare (inv_img_cmp #1 (ctype_compare Int.compare))
(!vi1, !vi2)
| x => x
(* ignores location information *)
fun expr_compare (e1,e2) =
case String.compare (expr_string e1, expr_string e2) of
EQUAL =>
let
in
case (enode e1, enode e2) of
(BinOp(opn1, e11, e12), BinOp(opn2, e21, e22)) =>
(case binop_compare(opn1,opn2) of
EQUAL => list_compare expr_compare ([e11,e12], [e21,e22])
| x => x)
| (UnOp p1, UnOp p2) => pair_compare(unop_compare, expr_compare) (p1, p2)
| (CondExp(e11,e12,e13), CondExp(e21,e22,e23)) =>
list_compare expr_compare ([e11,e12,e13], [e21,e22,e23])
| (Constant lc1, Constant lc2) => lc_compare (lc1, lc2)
| (Var vi1, Var vi2) => vi_compare (vi1, vi2)
| (StructDot p1, StructDot p2) =>
pair_compare (expr_compare, String.compare) (p1,p2)
| (ArrayDeref p1, ArrayDeref p2) =>
pair_compare (expr_compare, expr_compare) (p1,p2)
| (Deref e1, Deref e2) => expr_compare (e1, e2)
| (TypeCast p1, TypeCast p2) =>
pair_compare (inv_img_cmp node (ctype_compare expr_compare),
expr_compare)
(p1, p2)
| (Sizeof e1, Sizeof e2) => expr_compare (e1, e2)
| (SizeofTy ty1, SizeofTy ty2) =>
inv_img_cmp node (ctype_compare expr_compare) (ty1, ty2)
| (EFnCall(e1, elist1), EFnCall(e2, elist2)) =>
list_compare expr_compare (e1::elist1, e2::elist2)
| (CompLiteral p1, CompLiteral p2) =>
pair_compare (ctype_compare expr_compare,
list_compare (pair_compare (list_compare d_cmp, i_cmp)))
(p1, p2)
| (Arbitrary ty1, Arbitrary ty2) => ctype_compare expr_compare (ty1, ty2)
| (MKBOOL e1, MKBOOL e2) => expr_compare (e1, e2)
| _ => raise Fail ("expr_compare: can't happen: " ^ expr_string e1)
end
| x => x
and d_cmp p =
case p of
(DesignE e1, DesignE e2) => expr_compare (e1, e2)
| (DesignE _, _) => LESS
| (_, DesignE _) => GREATER
| (DesignFld fld1, DesignFld fld2) => String.compare (fld1, fld2)
and i_cmp p =
case p of
(InitE e1, InitE e2) => expr_compare (e1,e2)
| (InitE _, _) => LESS
| (_, InitE _) => GREATER
| (InitList dil1, InitList dil2) =>
list_compare (pair_compare (list_compare d_cmp, i_cmp)) (dil1, dil2)
fun bool b = (if b then IntInf.fromInt 1 else IntInf.fromInt 0, Signed Int)
fun safeop (l,r) destty f x = let
val dmod = imax destty + 1
val dmin = imin destty
val result = f x
val overflow = result >= dmod orelse result < dmin
val result' = if overflow then (result mod dmod, destty)
else (result, destty)
in
if overflow andalso is_signed destty then
Feedback.errorStr'(l,r,"Signed overflow")
else ();
result'
end
fun eval_binop (l, r) (binop, (n1,ty1), (n2,ty2)) = let
open IntInf
val destty = case binop of
LShift => integer_promotions ty1
| RShift => integer_promotions ty2
| _ => arithmetic_conversion (ty1, ty2)
val safeop = fn f => safeop (l,r) destty f (n1,n2)
in
case binop of
Lt => bool (n1 < n2)
| Gt => bool (n1 > n2)
| Leq => bool (n1 <= n2)
| Geq => bool (n1 >= n2)
| Equals => bool (n1 = n2)
| NotEquals => bool (n1 <> n2)
| LogOr => bool (n1 <> 0 orelse n2 <> 0)
| LogAnd => bool (n1 <> 0 andalso n2 <> 0)
| Plus => safeop op+
| Times => safeop op*
| Minus => safeop op-
| Divides => safeop (op div)
| Modulus => safeop (op mod)
| BitwiseAnd => (andb(n1, n2), destty)
| BitwiseOr => (orb(n1, n2), destty)
| BitwiseXOr => (xorb(n1, n2), destty)
| LShift => (if n2 < 0 orelse n2 >= ty_width destty then
Feedback.errorStr'(l,r,"Invalid/overflowing shift")
else ();
safeop (fn (n1,n2) => <<(n1, Word.fromInt (toInt n2))))
| RShift => (if n2 < 0 orelse n2 >= ty_width destty orelse
(is_signed destty andalso n1 < 0)
then
Feedback.errorStr'(l,r,"Invalid/overflowing shift")
else ();
safeop (fn (n1,n2) => ~>>(n1, Word.fromInt (toInt n2))))
end
val fi = IntInf.fromInt
fun eval_unop (l, r, uop, (n, ty)) = let
open IntInf
val destty = integer_promotions ty
in
case uop of
Negate => safeop (l,r) destty ~ n
| Not => bool (n = 0)
| Addr => (Feedback.errorStr'(l,r, "Can't evaluate address-of in constant \
\expression");
(fi 0, Signed Int))
| BitNegate => (notb n, destty)
end
fun eclookup (CE {enumenv,...}) s = Symtab.lookup enumenv s
fun consteval0 (ecenv as CE {enumenv, typing, structsize}) e = let
val fi = IntInf.fromInt
val zero = (fi 0, Signed Int)
fun Fail s = (Feedback.errorStr'(eleft e, eright e, s); zero)
val consteval = consteval0 ecenv
in
case enode e of
BinOp (bop, e1, e2) => eval_binop (eleft e, eright e)
(bop, consteval e1, consteval e2)
| UnOp (uop, e) => eval_unop (eleft e, eright e, uop, consteval e)
| Constant lc => eval_litconst lc
| Var (s,_) => let
in
case Symtab.lookup enumenv s of
NONE => Fail ("Variable "^s^ " can't appear in a constant expression")
| SOME (v, _) => (v, Signed Int)
end
| StructDot _ =>
Fail "Can't evaluate fieldref in constant expression"
| ArrayDeref _ =>
Fail "Can't evaluate array deref in constant expression"
| Deref _ => Fail "Can't evaluate deref in constant expression"
| CondExp(g,t,e) => if #1 (consteval g) <> fi 0 then consteval t
else consteval e
| SizeofTy ty => (fi (sizeof structsize (constify_abtype ecenv (node ty))),
ImplementationTypes.size_t)
| Sizeof e0 => (fi (sizeof structsize (typing e0)),
ImplementationTypes.size_t)
| MKBOOL e => bool (#1 (consteval e) <> fi 0)
| TypeCast(destty, e0) => let
val (v,_) = consteval e0
in
safeop (eleft e, eright e) (node destty) (fn x => x) v
end
| _ => Fail ("Unexpected expression form (" ^ expr_string e ^
") in consteval")
end
and consteval ecenv e = #1 (consteval0 ecenv e)
and constify_abtype ecenv (ty : expr ctype) : int ctype = let
fun recurse ty =
case ty of
Array (ty0, SOME sz) => Array (recurse ty0,
SOME (IntInf.toInt (consteval ecenv sz)))
| Array (ty0, NONE) => Array(recurse ty0, NONE)
| Ptr ty => Ptr (recurse ty)
| Signed x => Signed x
| PlainChar => PlainChar
| Unsigned x => Unsigned x
| StructTy s => StructTy s
| EnumTy x => EnumTy x
| Bitfield(b,e) => Bitfield (b, IntInf.toInt (consteval ecenv e))
| Ident s => Ident s
| Function (retty, args) => Function (recurse retty, map recurse args)
| Void => Void
| Bool => Bool
| _ => raise Fail ("constify_abtype: unexpected type form: "^tyname0 (K "") ty)
in
recurse ty
end
(* predicates on expressions to determine if they can't be evaluated freely
when on the rhs of a short-circuiting operator. *)
fun bop_needs_scprot bop =
case bop of
Divides => true
| Modulus => true
| RShift => true
| LShift => true
| _ => false
fun uop_needs_scprot _ = false
fun eneeds_sc_protection e =
case enode e of
BinOp(bop, e1, e2) => bop_needs_scprot bop orelse
eneeds_sc_protection e1 orelse
eneeds_sc_protection e2
| UnOp(uop, e) => uop_needs_scprot uop orelse eneeds_sc_protection e
| CondExp(e1,e2,e3) => List.exists eneeds_sc_protection [e1,e2,e3]
| Constant _ => false
| Var _ => false
| StructDot (e,_) => eneeds_sc_protection e
| ArrayDeref _ => true (* could try to figure out if the array is
a pointer, but even if it isn't, there
should be bounds checking going on *)
| Deref _ => true
| TypeCast (_, e) => eneeds_sc_protection e
| Sizeof _ => false
| SizeofTy _ => false
| EFnCall _ => true
| CompLiteral (_, dis) => List.exists (i_needs_scprot o #2) dis
| Arbitrary _ => false
| _ => raise Fail ("eneeds_sc_protection: can't handle "^expr_string e)
and i_needs_scprot i =
case i of
InitE e => eneeds_sc_protection e
| InitList dis => List.exists (i_needs_scprot o #2) dis
end (* struct *)