-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathvm.ml
294 lines (234 loc) · 8.92 KB
/
vm.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
(*simple stack-based VM with shared memory *)
(* VM supports only integers, so Booleans need to be mapped to integer
where 0 = false and 1 = true
*)
type instructions =
Halt
(* Stack operations *)
| PushS of int
| PopS
| Add
| Sub
| Div
| Mult
| Lt
| Gt
| Eq
| Output
(* (Conditional) jumps *)
| NonZero of int
| Zero of int
| Jump of int
| Label of int
| JumpMemLoc of int
(* Memory operations *)
| Assign of int*int
| PushToStack of int
| AssignFromStack of int*int
| Lock of int
| Unlock of int
(* run-time (environment) stack *)
| PushE of int
| PopE
| PushToEnv of int
| AssignFromEnv of int*int
| UpdateToEnv of int*int
type lockInfo = { locked : bool;
threadID : int }
(* sp and ep refer to the next available position *)
type thread = { pc : int ref;
code : instructions list;
stack : int array;
env : int array;
sp : int ref;
ep : int ref}
type state = { mem : int array;
memLock : lockInfo array;
threads : (thread list) ref;
activeThread : int ref}
let nameSupply = ref 1
let fresh _ = nameSupply := !nameSupply + 1;
!nameSupply
(* global shared memory *)
let memSize = 20000
let mkMem _ = Array.make memSize 0
let mkMemLock _ = Array.make memSize {locked = false; threadID = 0}
(* computation stack *)
let stkSize = 20000
let mkStk _ = Array.make stkSize 0
(* run-time environment stack *)
let envSize = 20000
let mkEnv _ = Array.make envSize 0
let mkThread cs = { pc = ref 0;
code = cs;
stack = mkStk();
env = mkEnv();
sp = ref 0;
ep = ref 0 }
let initState cs = { mem = mkMem();
memLock = mkMemLock();
threads = ref [mkThread cs];
activeThread = ref 0}
let inc r = r := !r + 1
let dec r = r := !r - 1
let singleStep id mem memLock t = match (List.nth t.code !(t.pc)) with
| Halt -> true
| PushS i -> t.stack.(!(t.sp)) <- i;
inc t.pc;
inc t.sp;
false
| PopS -> inc t.pc;
dec t.sp;
false
(* recall that sp refers to the next available position,
so must subtract 1 to access top element *)
| Add -> let i = !(t.sp) - 1
in t.stack.(i-1) <- t.stack.(i-1) + t.stack.(i);
inc t.pc;
dec t.sp;
false
| Sub -> let i = !(t.sp) - 1
in t.stack.(i-1) <- t.stack.(i-1) - t.stack.(i);
inc t.pc;
dec t.sp;
false
| Div -> let i = !(t.sp) - 1
in t.stack.(i-1) <- t.stack.(i-1) / t.stack.(i);
inc t.pc;
dec t.sp;
false
| Mult -> let i = !(t.sp) - 1
in t.stack.(i-1) <- t.stack.(i-1) * t.stack.(i);
inc t.pc;
dec t.sp;
false
| Lt -> let i = !(t.sp) - 1
in (if t.stack.(i-1) < t.stack.(i)
then t.stack.(i-1) <- 1
else t.stack.(i-1) <- 0);
inc t.pc;
dec t.sp;
false
| Gt -> let i = !(t.sp) - 1
in (if t.stack.(i-1) > t.stack.(i)
then t.stack.(i-1) <- 1
else t.stack.(i-1) <- 0);
inc t.pc;
dec t.sp;
false
| Eq -> let i = !(t.sp) - 1
in (if t.stack.(i-1) == t.stack.(i)
then t.stack.(i-1) <- 1
else t.stack.(i-1) <- 0);
inc t.pc;
dec t.sp;
false
| Output -> Printf.printf "%d \n" t.stack.(!(t.sp) - 1);
inc t.pc;
false
| NonZero i -> let x = t.stack.(!(t.sp) - 1) in
inc t.sp;
(if x == 0
then inc t.pc
else t.pc := i);
false
| Zero i -> let x = t.stack.(!(t.sp) - 1) in
inc t.sp;
(if x == 0
then t.pc := i
else inc t.pc);
false
| Jump i -> t.pc := i;
false
| Label i -> inc t.pc;
false
| JumpMemLoc loc -> t.pc := mem.(loc);
false
| Assign (loc,i) -> inc t.pc;
mem.(loc) <- i;
false
| PushToStack loc -> inc t.pc;
t.stack.(!(t.sp)) <- mem.(loc);
inc t.sp;
false
(* deref of the relative position relPos and assign to mem loc,
to access top-most stack element set relPos=1,
recall that sp refers to the next available position *)
| AssignFromStack (relPos,loc) -> inc t.pc;
mem.(loc) <- t.stack.(!(t.sp) - relPos);
false
(* lock a memory cell, note that acess via assign doesn't check if cell is locked,
hence, we assume that in case of shared memory, every access is protected by lock *)
| Lock loc -> if memLock.(loc).locked
then true
else (memLock.(loc) <- {locked = true; threadID = id};
inc t.pc;
false)
(* only the owner can unlock a memory cell, multiple unlock yield failure (true) *)
| Unlock loc -> if (not (memLock.(loc).locked && memLock.(loc).threadID == id))
then true
else (memLock.(loc) <- {locked = false; threadID = id};
inc t.pc;
false)
| PushE i -> t.env.(!(t.ep)) <- i;
inc t.pc;
inc t.ep;
false
| PopE -> inc t.pc;
dec t.ep;
false
| PushToEnv loc -> inc t.pc;
t.env.(!(t.ep)) <- mem.(loc);
inc t.ep;
false
| AssignFromEnv (relPos,loc) -> inc t.pc;
mem.(loc) <- t.env.(!(t.ep) - relPos);
false
| UpdateToEnv (relPos,loc) -> inc t.pc;
t.env.(!(t.ep) - relPos) <- mem.(loc);
false
let debug txt = Printf.printf txt;
Printf.printf "\n"
let run cs = let st = initState cs in
let stop = ref false in
while not !stop do
stop := true;
for i = 0 to List.length !(st.threads) - 1 do
if not (singleStep i st.mem st.memLock (List.nth !(st.threads) i))
then stop := false
done;
done;
st
let testProg1 = [PushS 1; PushS 2; Add; Output; Halt]
let testProg2 = [PushS 1; PushS 2; Lt; Output; Halt]
let getThread st i = (List.nth !(st.threads) i)
let printVm vmInst = match vmInst with
| Halt -> "Halt"
| PushS num -> "PushS " ^ (string_of_int num)
| PopS -> "PopS"
| Add -> "Add"
| Sub -> "Sub"
| Mult -> "Mult"
| Div -> "Div"
| Lt -> "Lt"
| Gt -> "Gt"
| Eq -> "Eq"
| Output -> "Output"
| NonZero place -> "NonZero " ^ (string_of_int place)
| Zero place -> "Zero " ^ (string_of_int place)
| Jump place -> "Jump " ^ (string_of_int place)
| JumpMemLoc place -> "JumpMemLoc " ^ (string_of_int place)
| Label l -> "Label " ^ (string_of_int l)
| Assign (mem, num) -> "Assign " ^ (string_of_int mem) ^ " " ^ (string_of_int num)
| PushToStack mem -> "PushToStack " ^ (string_of_int mem)
| AssignFromStack (sp, mem) -> "AssignFromStack " ^ (string_of_int sp) ^ " " ^ (string_of_int mem)
| PushE num -> "PushE " ^ (string_of_int num)
| PopE -> "PopE"
| PushToEnv num -> "PushToEnv " ^ (string_of_int num)
| AssignFromEnv (sp, mem) -> "AssignFromEnv " ^ (string_of_int sp) ^ " " ^ (string_of_int mem)
| UpdateToEnv (sp, mem) -> "UpdateToEnv " ^ (string_of_int sp) ^ " " ^ (string_of_int mem)
| _ -> "Not Implemented"
let rec printVMList vmList counter = match vmList with
| x::xs -> Printf.printf "Line %d: %s\n" counter (printVm x);
printVMList xs (counter + 1)
| [] -> debug ""