-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathContcomp.fs
More file actions
388 lines (329 loc) · 14 KB
/
Contcomp.fs
File metadata and controls
388 lines (329 loc) · 14 KB
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
(* File MicroC/Contcomp.fs
A continuation-based (backwards) compiler from micro-C, a fraction of
the C language, to an abstract machine.
sestoft@itu.dk * 2011-11-10
The abstract machine code is generated backwards, so that jumps to
jumps can be eliminated, so that tail-calls (calls immediately
followed by return) can be recognized, dead code can be eliminated,
etc.
抽象机器代码是向后生成的,这样就可以消除跳转,从而可以识别尾部调用(紧接着
返回的调用),消除死代码,等等。
The compilation of a block, which may contain a mixture of
declarations and statements, proceeds in two passes:
块的编译可能包含声明和语句的混合,分两步进行:
Pass 1: elaborate declarations to find the environment in which
each statement must be compiled; also translate
declarations into allocation instructions, of type
bstmtordec.
第1关:详细声明,找到每个语句必须编译的环境;还可以将声明转换为bstmtordec
类型的分配指令。
Pass 2: compile the statements in the given environments.
第2关:在给定的环境中编译语句。
*)
module Contcomp
open System.IO
open Absyn
open Machine
(* 上述第1和第2遍之间的中间表示: *)
type bstmtordec =
| BDec of instr list (* Declaration of local variable *)
| BStmt of stmt (* A statement *)
(* ------------------------------------------------------------------- *)
(* 执行局部优化的代码生成函数 *)
let rec addINCSP m1 C : instr list =
match C with
| INCSP m2 :: C1 -> addINCSP (m1+m2) C1
| RET m2 :: C1 -> RET (m2-m1) :: C1
| Label lab :: RET m2 :: _ -> RET (m2-m1) :: C
| _ -> if m1=0 then C else INCSP m1 :: C
let addLabel C : label * instr list = (* Conditional jump to C *)
match C with
| Label lab :: _ -> (lab, C)
| GOTO lab :: _ -> (lab, C)
| _ -> let lab = newLabel()
(lab, Label lab :: C)
let makeJump C : instr * instr list = (* Unconditional jump to C *)
match C with
| RET m :: _ -> (RET m, C)
| Label lab :: RET m :: _ -> (RET m, C)
| Label lab :: _ -> (GOTO lab, C)
| GOTO lab :: _ -> (GOTO lab, C)
| _ -> let lab = newLabel()
(GOTO lab, Label lab :: C)
let makeCall m lab C : instr list =
match C with
| RET n :: C1 -> TCALL(m, n, lab) :: C1
| Label _ :: RET n :: _ -> TCALL(m, n, lab) :: C
| _ -> CALL(m, lab) :: C
let rec deadcode C =
match C with
| [] -> []
| Label lab :: _ -> C
| _ :: C1 -> deadcode C1
let addNOT C =
match C with
| NOT :: C1 -> C1
| IFZERO lab :: C1 -> IFNZRO lab :: C1
| IFNZRO lab :: C1 -> IFZERO lab :: C1
| _ -> NOT :: C
let addJump jump C = (* jump is GOTO or RET *)
let C1 = deadcode C
match (jump, C1) with
| (GOTO lab1, Label lab2 :: _) -> if lab1=lab2 then C1
else GOTO lab1 :: C1
| _ -> jump :: C1
let addGOTO lab C =
addJump (GOTO lab) C
let rec addCST i C =
match (i, C) with
| (0, ADD :: C1) -> C1
| (0, SUB :: C1) -> C1
| (0, NOT :: C1) -> addCST 1 C1
| (_, NOT :: C1) -> addCST 0 C1
| (1, MUL :: C1) -> C1
| (1, DIV :: C1) -> C1
| (0, EQ :: C1) -> addNOT C1
| (_, INCSP m :: C1) -> if m < 0 then addINCSP (m+1) C1
else CSTI i :: C
| (0, IFZERO lab :: C1) -> addGOTO lab C1
| (_, IFZERO lab :: C1) -> C1
| (0, IFNZRO lab :: C1) -> C1
| (_, IFNZRO lab :: C1) -> addGOTO lab C1
| _ -> CSTI i :: C
(* ------------------------------------------------------------------- *)
(* 简单的环境操作 *)
type 'data Env = (string * 'data) list
let rec lookup env x =
match env with
| [] -> failwith (x + " not found")
| (y, v)::yr -> if x=y then v else lookup yr x
(* 全局变量有绝对地址,局部变量有偏移量: *)
type Var =
| Glovar of int (* 堆栈中的绝对地址 *)
| Locvar of int (* 相对于帧底部的地址 *)
(* 变量环境跟踪全局变量和局部变量,并跟踪局部变量的下一个可用偏移量 *)
type VarEnv = (Var * typ) Env * int
(* 函数环境将函数名映射到函数的标签、返回类型和参数声明 *)
type Paramdecs = (typ * string) list
type FunEnv = (label * typ option * Paramdecs) Env
(* 在varEnv中绑定声明的变量并生成代码来分配它: *)
let allocate (kind : int -> Var) (typ, x) (varEnv : VarEnv) : VarEnv * instr list =
let (env, fdepth) = varEnv
match typ with
| TypA (TypA _, _) -> failwith "allocate: arrays of arrays not permitted"
| TypA (t, Some i) ->
let newEnv = ((x, (kind (fdepth+i), typ)) :: env, fdepth+i+1)
let code = [INCSP i; GETSP; CSTI (i-1); SUB]
(newEnv, code)
| _ ->
let newEnv = ((x, (kind (fdepth), typ)) :: env, fdepth+1)
let code = [INCSP 1]
(newEnv, code)
(* 在env中绑定声明的参数: *)
let bindParam (env, fdepth) (typ, x) : VarEnv =
((x, (Locvar fdepth, typ)) :: env, fdepth+1);
let bindParams paras (env, fdepth) : VarEnv =
List.fold bindParam (env, fdepth) paras;
(* ------------------------------------------------------------------- *)
(* 为全局变量和全局函数构建环境*)
let makeGlobalEnvs(topdecs : topdec list) : VarEnv * FunEnv * instr list =
let rec addv decs varEnv funEnv =
match decs with
| [] -> (varEnv, funEnv, [])
| dec::decr ->
match dec with
| Vardec (typ, x) ->
let (varEnv1, code1) = allocate Glovar (typ, x) varEnv
let (varEnvr, funEnvr, coder) = addv decr varEnv1 funEnv
(varEnvr, funEnvr, code1 @ coder)
| Fundec (tyOpt, f, xs, body) ->
addv decr varEnv ((f, (newLabel(), tyOpt, xs)) :: funEnv)
addv topdecs ([], 0) []
(* ------------------------------------------------------------------- *)
(* Compiling micro-C statements:
* stmt is the statement to compile
* varenv is the local and global variable environment
* funEnv is the global function environment
* C is the code that follows the code for stmt
*)
(*
编译micro-C语句:
*stmt是要编译的语句
*varenv是局部和全局变量环境
*funEnv是全球功能环境
*C是stmt代码后面的代码
*)
let rec cStmt stmt (varEnv : VarEnv) (funEnv : FunEnv) (C : instr list) : instr list =
match stmt with
| If(e, stmt1, stmt2) ->
let (jumpend, C1) = makeJump C
let (labelse, C2) = addLabel (cStmt stmt2 varEnv funEnv C1)
cExpr e varEnv funEnv (IFZERO labelse
:: cStmt stmt1 varEnv funEnv (addJump jumpend C2))
| While(e, body) ->
let labbegin = newLabel()
let (jumptest, C1) =
makeJump (cExpr e varEnv funEnv (IFNZRO labbegin :: C))
addJump jumptest (Label labbegin :: cStmt body varEnv funEnv C1)
| Expr e ->
cExpr e varEnv funEnv (addINCSP -1 C)
| Block stmts ->
let rec pass1 stmts ((_, fdepth) as varEnv) =
match stmts with
| [] -> ([], fdepth)
| s1::sr ->
let (_, varEnv1) as res1 = bStmtordec s1 varEnv
let (resr, fdepthr) = pass1 sr varEnv1
(res1 :: resr, fdepthr)
let (stmtsback, fdepthend) = pass1 stmts varEnv
let rec pass2 pairs C =
match pairs with
| [] -> C
| (BDec code, varEnv) :: sr -> code @ pass2 sr C
| (BStmt stmt, varEnv) :: sr -> cStmt stmt varEnv funEnv (pass2 sr C)
pass2 stmtsback (addINCSP(snd varEnv - fdepthend) C)
| Return None ->
RET (snd varEnv - 1) :: deadcode C
| Return (Some e) ->
cExpr e varEnv funEnv (RET (snd varEnv) :: deadcode C)
and bStmtordec stmtOrDec varEnv : bstmtordec * VarEnv =
match stmtOrDec with
| Stmt stmt ->
(BStmt stmt, varEnv)
| Dec (typ, x) ->
let (varEnv1, code) = allocate Locvar (typ, x) varEnv
(BDec code, varEnv1)
(* Compiling micro-C expressions:
* e is the expression to compile
* varEnv is the compile-time variable environment
* funEnv is the compile-time environment
* C is the code following the code for this expression
Net effect principle: if the compilation (cExpr e varEnv funEnv C) of
expression e returns the instruction sequence instrs, then the
execution of instrs will have the same effect as an instruction
sequence that first computes the value of expression e on the stack
top and then executes C, but because of optimizations instrs may
actually achieve this in a different way.
*)
(*编译micro-C表达式:
*e是要编译的表达式
*varEnv是编译时变量环境
*funEnv是编译时环境
*C是这个表达式的代码后面的代码
净效应原理:如果表达式e的编译(cExpr e varEnv funEnv C)返回指令序列INSTR,
则INSTR的执行将与先在堆栈顶部计算表达式e的值,然后执行C的指令序列具有相同的
效果,但由于优化,INSTR实际上可能以不同的方式实现这一点。
*)
and cExpr (e : expr) (varEnv : VarEnv) (funEnv : FunEnv) (C : instr list) : instr list =
match e with
| Access acc -> cAccess acc varEnv funEnv (LDI :: C)
| Assign(acc, e) -> cAccess acc varEnv funEnv (cExpr e varEnv funEnv (STI :: C))
| CstI i -> addCST i C
| Addr acc -> cAccess acc varEnv funEnv C
| Prim1(ope, e1) ->
cExpr e1 varEnv funEnv
(match ope with
| "!" -> addNOT C
| "printi" -> PRINTI :: C
| "printc" -> PRINTC :: C
| _ -> failwith "unknown primitive 1")
| Prim2(ope, e1, e2) ->
cExpr e1 varEnv funEnv
(cExpr e2 varEnv funEnv
(match ope with
| "*" -> MUL :: C
| "+" -> ADD :: C
| "-" -> SUB :: C
| "/" -> DIV :: C
| "%" -> MOD :: C
| "==" -> EQ :: C
| "!=" -> EQ :: addNOT C
| "<" -> LT :: C
| ">=" -> LT :: addNOT C
| ">" -> SWAP :: LT :: C
| "<=" -> SWAP :: LT :: addNOT C
| _ -> failwith "unknown primitive 2"))
| Andalso(e1, e2) ->
match C with
| IFZERO lab :: _ ->
cExpr e1 varEnv funEnv (IFZERO lab :: cExpr e2 varEnv funEnv C)
| IFNZRO labthen :: C1 ->
let (labelse, C2) = addLabel C1
cExpr e1 varEnv funEnv
(IFZERO labelse
:: cExpr e2 varEnv funEnv (IFNZRO labthen :: C2))
| _ ->
let (jumpend, C1) = makeJump C
let (labfalse, C2) = addLabel (addCST 0 C1)
cExpr e1 varEnv funEnv
(IFZERO labfalse
:: cExpr e2 varEnv funEnv (addJump jumpend C2))
| Orelse(e1, e2) ->
match C with
| IFNZRO lab :: _ ->
cExpr e1 varEnv funEnv (IFNZRO lab :: cExpr e2 varEnv funEnv C)
| IFZERO labthen :: C1 ->
let(labelse, C2) = addLabel C1
cExpr e1 varEnv funEnv
(IFNZRO labelse :: cExpr e2 varEnv funEnv
(IFZERO labthen :: C2))
| _ ->
let (jumpend, C1) = makeJump C
let (labtrue, C2) = addLabel(addCST 1 C1)
cExpr e1 varEnv funEnv
(IFNZRO labtrue
:: cExpr e2 varEnv funEnv (addJump jumpend C2))
| Call(f, es) -> callfun f es varEnv funEnv C
(* 生成代码以访问变量、解引用指针或索引数组: *)
and cAccess access varEnv funEnv C =
match access with
| AccVar x ->
match lookup (fst varEnv) x with
| Glovar addr, _ -> addCST addr C
| Locvar addr, _ -> GETBP :: addCST addr (ADD :: C)
| AccDeref e ->
cExpr e varEnv funEnv C
| AccIndex(acc, idx) ->
cAccess acc varEnv funEnv (LDI :: cExpr idx varEnv funEnv (ADD :: C))
(* 生成用于计算表达式列表的代码: *)
and cExprs es varEnv funEnv C =
match es with
| [] -> C
| e1::er -> cExpr e1 varEnv funEnv (cExprs er varEnv funEnv C)
(* 生成代码来计算参数es,然后调用函数f: *)
and callfun f es varEnv funEnv C : instr list =
let (labf, tyOpt, paramdecs) = lookup funEnv f
let argc = List.length es
if argc = List.length paramdecs then
cExprs es varEnv funEnv (makeCall argc labf C)
else
failwith (f + ": parameter/argument mismatch")
(* 编译一个完整的micro-C程序:globals、对main的调用、函数 *)
let cProgram (Prog topdecs) : instr list =
let _ = resetLabels ()
let ((globalVarEnv, _), funEnv, globalInit) = makeGlobalEnvs topdecs
let compilefun (tyOpt, f, xs, body) =
let (labf, _, paras) = lookup funEnv f
let (envf, fdepthf) = bindParams paras (globalVarEnv, 0)
let C0 = [RET (List.length paras-1)]
let code = cStmt body (envf, fdepthf) funEnv C0
Label labf :: code
let functions =
List.choose (function
| Fundec (rTy, name, argTy, body)
-> Some (compilefun (rTy, name, argTy, body))
| Vardec _ -> None)
topdecs
let (mainlab, _, mainparams) = lookup funEnv "main"
let argc = List.length mainparams
globalInit
@ [LDARGS argc; CALL(argc, mainlab); STOP]
@ List.concat functions
(* 编译程序(抽象语法)并将其写入文件fname;另外,将程序作为指令列表返回。 *)
let intsToFile (inss : int list) (fname : string) =
File.WriteAllText(fname, String.concat " " (List.map string inss))
let contCompileToFile program fname =
let instrs = cProgram program
let bytecode = code2ints instrs
intsToFile bytecode fname; instrs
(* 示例程序可在文件ex1、ex2等文件中找到 *)