compiler/ lib/ examples/ std/ arch/ rv64/ tests/ abi.sizes.rad 3.4 KiB aggregate.return.rad 4.0 KiB arith.assignment.rad 580 B arith.basic.rad 176 B arith.modulo.rad 96 B arith.subword.rad 3.8 KiB arith.sum.rad 177 B arith.w64.rad 4.1 KiB array.assign.rad 221 B array.bounds.check.rad 321 B array.index.assign.rad 367 B array.index.rad 249 B array.length.rad 262 B array.math.rad 1.2 KiB array.nested.assign.rad 319 B array.nested.rad 325 B array.record.elements.rad 1.7 KiB array.repeat.edge.rad 548 B array.repeat.rad 828 B array.return.rad 330 B array.slice.empty.rad 108 B array.slice.gen.end.rad 126 B array.slice.gen.index.rad 139 B array.slice.gen.open.rad 125 B array.slice.gen.start.end.rad 127 B array.slice.gen.start.rad 126 B array.slice.rad 759 B as.precedence.rad 212 B assert.basic.rad 387 B assert.fail.rad 138 B assert.false.rad 140 B assert.true.rad 100 B assign.mutable.rad 6.1 KiB assign.rad 129 B assign.shadow.mutable.rad 461 B binop.bitwise.rad 1.2 KiB binop.cmp.rad 426 B bool.comparison.array.rad 688 B bool.comparison.nested.gen.rad 1.0 KiB bool.comparison.opt.rad 905 B bool.comparison.record.gen.rad 1.0 KiB bool.comparison.record.rad 1.1 KiB bool.comparison.slice.gen.rad 157 B bool.comparison.slice.rad 4.1 KiB bool.comparison.slice.record.gen.rad 2.0 KiB bool.comparison.slice.union.gen.rad 2.5 KiB bool.comparison.union.ctor.rad 690 B bool.comparison.union.gen.rad 1.2 KiB bool.comparison.union.record.gen.rad 1.5 KiB bool.comparison.union.simple.gen.rad 281 B bool.operators.complex.rad 384 B bool.operators.rad 831 B bool.short.circuit.rad 2.3 KiB bool.simple.rad 194 B bool.values.rad 772 B builtin.size.align.rad 1.3 KiB builtin.sliceof.mut.rad 606 B builtin.sliceof.rad 505 B call.arg.clobber.rad 717 B call.basic.rad 241 B call.clobber.rad 462 B cast.same.size.rad 1.0 KiB casting.numbers.rad 1.5 KiB char.literal.rad 165 B compound.assign.field.rad 285 B compound.assign.rad 1.1 KiB cond.assign.rad 723 B cond.expr.aggregate.rad 1.1 KiB cond.expr.rad 1.8 KiB cond.for.else.break.rad 326 B cond.for.indexed.rad 238 B cond.for.rad 165 B cond.for.range.indexed.rad 526 B cond.for.range.rad 171 B cond.for.unsigned.range.rad 644 B cond.forever.break.continue.rad 182 B cond.forever.break.rad 232 B cond.fused.rad 926 B cond.if.case.rad 2.2 KiB cond.if.else.min.rad 143 B cond.if.else.rad 225 B cond.if.elseif.rad 420 B cond.if.noelse.rad 120 B cond.if.rad 865 B cond.match.fallthrough.rad 369 B cond.match.guard.rad 1.4 KiB cond.match.guard.regalloc.rad 1.3 KiB cond.while.else.break.rad 282 B cond.while.rad 119 B const.array.copy.mutate.rad 386 B const.array.rad 195 B const.basic.rad 325 B const.char.rad 159 B const.fn.array.rad 664 B const.record.array.rad 1.2 KiB const.record.array.simple.rad 523 B const.record.ctor.rad 170 B const.record.fn.rad 353 B const.record.rad 182 B const.slice.param.rad 333 B const.union.payload.ctor.rad 349 B const.union.record.literal.rad 359 B data.array.rad 767 B data.bool.rad 216 B data.i16.rad 261 B data.i32.rad 281 B data.i8.rad 248 B data.record.rad 561 B data.simple.rad 436 B data.u16.rad 220 B data.u32.rad 240 B data.u8.rad 208 B data.union.rad 886 B debug.tag.rad 557 B edge.cases.2.rad 337 B edge.cases.3.rad 594 B edge.cases.4.rad 1.2 KiB edge.cases.5.rad 1.0 KiB edge.cases.6.rad 2.6 KiB edge.cases.7.addr.bug.rad 224 B edge.cases.8.bug.rad 508 B edge.cases.rad 223 B error.basic.rad 159 B error.catch.rad 1.6 KiB error.division.zero.rad 164 B error.modulo.zero.rad 162 B error.multi.basic.rad 672 B error.multi.catch.rad 772 B error.multi.catch.typed.binding.rad 791 B error.multi.catch.typed.catchall.rad 1.0 KiB error.multi.catch.typed.rad 1.1 KiB error.multi.propagate.multi.rad 953 B error.multi.propagate.rad 825 B error.multi.try.optional.rad 507 B error.slice.bounds.rad 219 B error.try.bang.success.rad 370 B error.try.catch.binding.rad 2.0 KiB error.try.optional.rad 1.8 KiB error.try.rad 4.0 KiB fn.block.scope.rad 508 B fn.callback.nested.rad 1.2 KiB fn.default.rad 131 B fn.local.rad 140 B fn.recursion.2.rad 239 B fn.void.rad 150 B for.else.continue.rad 1.1 KiB frame.large.rad 567 B if-let-mut.rad 1.1 KiB iflet.shadow.leak.rad 317 B integer.bitwise.basic.rad 693 B integer.overflow.rad 1.8 KiB large.blit.store.rad 2.1 KiB let.guard.rad 1.9 KiB literal.w64.rad 1.7 KiB loc.addr.offset.bug.rad 410 B loc.addr.opt.to.opt.rad 433 B loc.addr.optional.assign.rad 408 B loc.addr.record.assign.rad 443 B loop.complex.flow.rad 1007 B loop.sealblock.rad 911 B match.array.rad 3.4 KiB match.char.rad 1.6 KiB match.multi.seal.rad 987 B match.multi.survive.rad 1.6 KiB match.mutref.push.rad 1.0 KiB match.mutref.union.rad 662 B match.nested.call.rad 1.7 KiB match.nested.deep.rad 2.2 KiB match.nested.deref.rad 3.7 KiB match.nested.guard.rad 1.6 KiB match.nested.iflet.guard.rad 1.6 KiB match.nested.iflet.rad 1.4 KiB match.nested.letelse.rad 813 B match.nested.letelse.union.rad 1.3 KiB match.nested.literal.rad 3.1 KiB match.nested.multi.rad 2.4 KiB match.nested.pattern.rad 5.2 KiB match.nested.record.rad 2.0 KiB match.nested.union.rad 2.3 KiB match.nested.whilelet.rad 2.4 KiB match.string.rad 1.8 KiB match.value.copy.rad 2.0 KiB match.void.then.or.rad 1.6 KiB memzero.result.bug.rad 806 B memzero.union.bug.rad 576 B mutref.loop.bug.rad 1.8 KiB opt.assignment.bug.rad 1.3 KiB opt.bug.test.rad 1.4 KiB opt.if.let.complex.rad 6.2 KiB opt.if.let.guard.rad 809 B opt.if.let.rad 956 B opt.nil.check.rad 1.5 KiB opt.record.eq.rad 842 B opt.record.rad 655 B opt.return.array.rad 289 B opt.return.nested.rad 797 B opt.return.record.rad 344 B opt.slice.npo.rad 2.8 KiB opt.type.rad 200 B opt.while.let.complex.rad 404 B panic.rad 111 B placeholder.basic.rad 133 B placeholder.comprehensive.rad 562 B pointer.copy.edge.case.rad 1.3 KiB pointer.slice.index.rad 269 B pointer.slice.store.rad 881 B prog.ackermann.rad 5.0 KiB prog.bignum.rad 9.4 KiB prog.binsearch.rad 2.4 KiB prog.bubblesort.rad 2.0 KiB prog.cordic.rad 6.9 KiB prog.crc32.rad 2.7 KiB prog.dijkstra.rad 7.7 KiB prog.eval.rad 6.2 KiB prog.hanoi.rad 3.8 KiB prog.huffman.rad 9.3 KiB prog.hybridsort.rad 3.0 KiB prog.linkedlist.rad 5.8 KiB prog.lzw.rad 6.7 KiB prog.matmul.rad 2.9 KiB prog.mersenne.rad 5.2 KiB prog.nqueens.rad 3.4 KiB prog.rbtree.rad 8.2 KiB prog.regex.rad 10.2 KiB prog.sha256.rad 7.0 KiB prog.sieve.rad 2.8 KiB prog.symtab.rad 10.1 KiB prog.tokenizer.rad 13.8 KiB prog.vm.rad 17.4 KiB ptr.assign.rad 137 B ptr.deref.rad 622 B ptr.eq.rad 966 B ptr.mutate.rad 244 B ptr.opaque.rad 1.4 KiB record.access.rad 285 B record.alignment.rad 179 B record.array.elements.rad 1.7 KiB record.copy.rad 2.0 KiB record.field.assign.rad 184 B record.nested.calls.2.rad 612 B record.nested.calls.3.rad 734 B record.param.lit.rad 353 B record.ptr.access.rad 227 B record.ptr.mutate.rad 243 B record.shorthand.rad 1.5 KiB record.unlabeled.rad 407 B ref.if.bug.rad 519 B ref.immut.loop.bug.rad 670 B ref.mut.ptr.rad 261 B regalloc.callee.save.rad 1.5 KiB regalloc.spill.reuse.rad 473 B reserve.loop.rad 392 B result.void.success.rad 716 B slice.alloc.loop.rad 788 B slice.append.rad 3.3 KiB slice.cap.rad 941 B slice.delete.rad 971 B slice.of.rad 460 B slice.subslice.rad 1.4 KiB spill.blockarg.clobber.rad 3.5 KiB spill.loop.rad 1.6 KiB stack.local.corrupt.rad 320 B static.array.mutate.rad 387 B static.basic.rad 327 B static.fn.array.rad 628 B static.record.array.rad 503 B static.slice.index.assign.rad 408 B static.slice.offset.rad 668 B string.basic.rad 149 B string.escape.rad 349 B string.index.rad 116 B switch.blockargs.clobber.rad 1.3 KiB trait.aggregate.ret.rad 1.5 KiB trait.array.optional.rad 1.7 KiB trait.basic.rad 565 B trait.control.flow.rad 1.1 KiB trait.fn.param.rad 1.6 KiB trait.multiple.methods.rad 1.2 KiB trait.multiple.traits.rad 1.2 KiB trait.multiple.types.rad 1.3 KiB trait.supertrait.rad 2.5 KiB trait.throws.rad 1.0 KiB trait.writer.rad 2.6 KiB type.unify.rad 4.5 KiB undefined.rad 417 B union-tag.rad 911 B union.bitfield.rad 1.2 KiB union.discriminant.cast.rad 389 B union.edge.case.2.rad 679 B union.edge.case.3.rad 608 B union.mixed.assign.rad 977 B union.payload.mutref.rad 1.4 KiB union.payload.rad 580 B union.record.forward.rad 1.3 KiB union.void.match.rad 403 B union.void.rad 824 B unsigned.compare.rad 1.9 KiB var.align.rad 1013 B var.infer.rad 549 B decode.rad 14.6 KiB emit.rad 24.4 KiB encode.rad 19.9 KiB isel.rad 41.1 KiB printer.rad 13.0 KiB tests.rad 15.7 KiB rv64.rad 13.0 KiB collections/ lang/ sys/ arch.rad 65 B collections.rad 36 B fmt.rad 3.8 KiB intrinsics.rad 206 B io.rad 1.2 KiB lang.rad 222 B mem.rad 2.2 KiB sys.rad 167 B testing.rad 2.4 KiB tests.rad 11.6 KiB vec.rad 3.1 KiB std.rad 231 B scripts/ seed/ test/ vim/ .gitignore 353 B .gitsigners 112 B LICENSE 1.1 KiB Makefile 3.1 KiB README 2.5 KiB std.lib 987 B std.lib.test 252 B
lib/std/arch/rv64/tests/prog.vm.rad 17.4 KiB raw
1
//! Stack-based virtual machine.
2
//! Implement a bytecode interpreter for a simple stack machine with
3
//! arithmetic, comparisons, jumps, local variables, and function calls.
4
//! Exercises: tagged unions, match, throw/try/catch, error handling,
5
//! records with slice fields, and complex interacting state.
6
7
const MAX_STACK: u32 = 64;
8
const MAX_CODE: u32 = 256;
9
const MAX_LOCALS: u32 = 16;
10
const MAX_FRAMES: u32 = 8;
11
12
/// Bytecode instructions.
13
union Op {
14
    /// Push an immediate value.
15
    Push(i32),
16
    /// Pop top two, push sum.
17
    Add,
18
    /// Pop top two, push difference (second - first).
19
    Sub,
20
    /// Pop top two, push product.
21
    Mul,
22
    /// Pop top two, push quotient.
23
    Div,
24
    /// Pop top two, push 1 if equal, 0 otherwise.
25
    Eq,
26
    /// Pop top two, push 1 if second < first, 0 otherwise.
27
    Lt,
28
    /// Pop top two, push 1 if second > first, 0 otherwise.
29
    Gt,
30
    /// Negate top of stack.
31
    Neg,
32
    /// Duplicate top of stack.
33
    Dup,
34
    /// Pop and discard top of stack.
35
    Pop,
36
    /// Store top of stack into local variable.
37
    Store(u32),
38
    /// Load local variable onto stack.
39
    Load(u32),
40
    /// Unconditional jump to instruction index.
41
    Jump(u32),
42
    /// Pop; if zero, jump to instruction index.
43
    JumpIfZero(u32),
44
    /// Call function at instruction index, saving return address.
45
    Call(u32),
46
    /// Return from function call.
47
    Ret,
48
    /// Halt execution, top of stack is result.
49
    Halt,
50
}
51
52
/// A call frame for function calls.
53
record Frame {
54
    returnAddr: u32,
55
    localBase: u32,
56
}
57
58
/// The VM state.
59
record VM {
60
    code: *[Op],
61
    codeLen: u32,
62
    stack: *mut [i32],
63
    sp: u32,
64
    locals: *mut [i32],
65
    frames: *mut [Frame],
66
    frameCount: u32,
67
    pc: u32,
68
}
69
70
/// VM error types.
71
union VmError {
72
    /// Stack underflow error.
73
    StackUnderflow,
74
    /// Stack overflow error.
75
    StackOverflow,
76
    /// Division by zero.
77
    DivByZero,
78
    /// Invalid instruction or PC.
79
    InvalidPC,
80
    /// Too many nested calls.
81
    CallOverflow,
82
}
83
84
/// Push a value onto the stack.
85
fn push(vm: *mut VM, value: i32) throws (VmError) {
86
    if vm.sp >= MAX_STACK {
87
        throw VmError::StackOverflow;
88
    }
89
    vm.stack[vm.sp] = value;
90
    vm.sp += 1;
91
}
92
93
/// Pop a value from the stack.
94
fn pop(vm: *mut VM) -> i32 throws (VmError) {
95
    if vm.sp == 0 {
96
        throw VmError::StackUnderflow;
97
    }
98
    vm.sp -= 1;
99
    return vm.stack[vm.sp];
100
}
101
102
/// Peek at the top of the stack without removing.
103
fn peek(vm: *VM) -> i32 throws (VmError) {
104
    if vm.sp == 0 {
105
        throw VmError::StackUnderflow;
106
    }
107
    return vm.stack[vm.sp - 1];
108
}
109
110
/// Execute the bytecode program.
111
fn execute(vm: *mut VM) -> i32 throws (VmError) {
112
    while vm.pc < vm.codeLen {
113
        let instr = vm.code[vm.pc];
114
        vm.pc += 1;
115
116
        match instr {
117
            case Op::Push(val) => {
118
                try push(vm, val);
119
            }
120
            case Op::Add => {
121
                let b = try pop(vm);
122
                let a = try pop(vm);
123
                try push(vm, a + b);
124
            }
125
            case Op::Sub => {
126
                let b = try pop(vm);
127
                let a = try pop(vm);
128
                try push(vm, a - b);
129
            }
130
            case Op::Mul => {
131
                let b = try pop(vm);
132
                let a = try pop(vm);
133
                try push(vm, a * b);
134
            }
135
            case Op::Div => {
136
                let b = try pop(vm);
137
                let a = try pop(vm);
138
                if b == 0 {
139
                    throw VmError::DivByZero;
140
                }
141
                try push(vm, a / b);
142
            }
143
            case Op::Eq => {
144
                let b = try pop(vm);
145
                let a = try pop(vm);
146
                let mut result: i32 = 0;
147
                if a == b {
148
                    result = 1;
149
                }
150
                try push(vm, result);
151
            }
152
            case Op::Lt => {
153
                let b = try pop(vm);
154
                let a = try pop(vm);
155
                let mut result: i32 = 0;
156
                if a < b {
157
                    result = 1;
158
                }
159
                try push(vm, result);
160
            }
161
            case Op::Gt => {
162
                let b = try pop(vm);
163
                let a = try pop(vm);
164
                let mut result: i32 = 0;
165
                if a > b {
166
                    result = 1;
167
                }
168
                try push(vm, result);
169
            }
170
            case Op::Neg => {
171
                let a = try pop(vm);
172
                try push(vm, 0 - a);
173
            }
174
            case Op::Dup => {
175
                let a = try peek(vm);
176
                try push(vm, a);
177
            }
178
            case Op::Pop => {
179
                try pop(vm);
180
            }
181
            case Op::Store(idx) => {
182
                let val = try pop(vm);
183
                let mut base: u32 = 0;
184
                if vm.frameCount > 0 {
185
                    base = vm.frames[vm.frameCount - 1].localBase;
186
                }
187
                vm.locals[base + idx] = val;
188
            }
189
            case Op::Load(idx) => {
190
                let mut base: u32 = 0;
191
                if vm.frameCount > 0 {
192
                    base = vm.frames[vm.frameCount - 1].localBase;
193
                }
194
                let val = vm.locals[base + idx];
195
                try push(vm, val);
196
            }
197
            case Op::Jump(target) => {
198
                vm.pc = target;
199
            }
200
            case Op::JumpIfZero(target) => {
201
                let cond = try pop(vm);
202
                if cond == 0 {
203
                    vm.pc = target;
204
                }
205
            }
206
            case Op::Call(target) => {
207
                if vm.frameCount >= MAX_FRAMES {
208
                    throw VmError::CallOverflow;
209
                }
210
                let mut base: u32 = 0;
211
                if vm.frameCount > 0 {
212
                    base = vm.frames[vm.frameCount - 1].localBase + MAX_LOCALS;
213
                }
214
                vm.frames[vm.frameCount] = Frame { returnAddr: vm.pc, localBase: base };
215
                vm.frameCount += 1;
216
                vm.pc = target;
217
            }
218
            case Op::Ret => {
219
                if vm.frameCount == 0 {
220
                    throw VmError::InvalidPC;
221
                }
222
                vm.frameCount -= 1;
223
                vm.pc = vm.frames[vm.frameCount].returnAddr;
224
            }
225
            case Op::Halt => {
226
                return try pop(vm);
227
            }
228
        }
229
230
        if vm.pc > vm.codeLen {
231
            throw VmError::InvalidPC;
232
        }
233
    }
234
    throw VmError::InvalidPC;
235
}
236
237
/// Helper to initialize VM and run a program.
238
fn runProgram(
239
    code: *[Op],
240
    codeLen: u32,
241
    stackBuf: *mut [i32],
242
    localsBuf: *mut [i32],
243
    framesBuf: *mut [Frame]
244
) -> i32 throws (VmError) {
245
    let mut vm = VM {
246
        code,
247
        codeLen,
248
        stack: stackBuf,
249
        sp: 0,
250
        locals: localsBuf,
251
        frames: framesBuf,
252
        frameCount: 0,
253
        pc: 0,
254
    };
255
    return try execute(&mut vm);
256
}
257
258
/// Test basic arithmetic: 3 + 4 * 2 = 11
259
fn testArith(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
260
    let mut code: [Op; 8] = [Op::Halt; 8];
261
    code[0] = Op::Push(3);
262
    code[1] = Op::Push(4);
263
    code[2] = Op::Push(2);
264
    code[3] = Op::Mul;
265
    code[4] = Op::Add;
266
    code[5] = Op::Halt;
267
268
    let result: i32 = try! runProgram(&code[..], 6, stackBuf, localsBuf, framesBuf);
269
    assert result == 11;
270
    return 0;
271
}
272
273
/// Test local variables: x = 5, y = 7, push x + y.
274
fn testLocals(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
275
    let mut code: [Op; 16] = [Op::Halt; 16];
276
    code[0] = Op::Push(5);
277
    code[1] = Op::Store(0);   // x = 5
278
    code[2] = Op::Push(7);
279
    code[3] = Op::Store(1);   // y = 7
280
    code[4] = Op::Load(0);    // push x
281
    code[5] = Op::Load(1);    // push y
282
    code[6] = Op::Add;        // x + y
283
    code[7] = Op::Halt;
284
285
    let result: i32 = try! runProgram(&code[..], 8, stackBuf, localsBuf, framesBuf);
286
    assert result == 12;
287
    return 0;
288
}
289
290
/// Test conditional jump: if 3 > 2 then push 42 else push 99.
291
fn testConditional(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
292
    let mut code: [Op; 16] = [Op::Halt; 16];
293
    code[0] = Op::Push(3);
294
    code[1] = Op::Push(2);
295
    code[2] = Op::Gt;            // 3 > 2 => 1
296
    code[3] = Op::JumpIfZero(6); // if false, jump to 6
297
    code[4] = Op::Push(42);      // true branch
298
    code[5] = Op::Jump(7);       // skip false branch
299
    code[6] = Op::Push(99);      // false branch
300
    code[7] = Op::Halt;
301
302
    let result: i32 = try! runProgram(&code[..], 8, stackBuf, localsBuf, framesBuf);
303
    assert result == 42;
304
    return 0;
305
}
306
307
/// Test loop: sum 1..5 using jumps.
308
/// local[0] = counter (starts at 1), local[1] = sum (starts at 0).
309
fn testLoop(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
310
    let mut code: [Op; 32] = [Op::Halt; 32];
311
    code[0] = Op::Push(1);
312
    code[1] = Op::Store(0);      // counter = 1
313
    code[2] = Op::Push(0);
314
    code[3] = Op::Store(1);      // sum = 0
315
    // Loop start (pc = 4):
316
    code[4] = Op::Load(0);       // push counter
317
    code[5] = Op::Push(6);
318
    code[6] = Op::Lt;            // counter < 6
319
    code[7] = Op::JumpIfZero(17); // if false, exit loop
320
    code[8] = Op::Load(1);       // push sum
321
    code[9] = Op::Load(0);       // push counter
322
    code[10] = Op::Add;          // sum + counter
323
    code[11] = Op::Store(1);     // sum = sum + counter
324
    code[12] = Op::Load(0);      // push counter
325
    code[13] = Op::Push(1);
326
    code[14] = Op::Add;          // counter + 1
327
    code[15] = Op::Store(0);     // counter = counter + 1
328
    code[16] = Op::Jump(4);      // back to loop start
329
    code[17] = Op::Load(1);      // push sum
330
    code[18] = Op::Halt;
331
332
    let result: i32 = try! runProgram(&code[..], 19, stackBuf, localsBuf, framesBuf);
333
    // sum = 1+2+3+4+5 = 15
334
    assert result == 15;
335
    return 0;
336
}
337
338
/// Test function call: call a function that computes n*2+1 for n=10.
339
fn testCall(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
340
    let mut code: [Op; 32] = [Op::Halt; 32];
341
342
    // Main: push argument on stack, call function, halt.
343
    code[0] = Op::Push(10);      // push argument
344
    code[1] = Op::Call(5);       // call function at 5
345
    // After return, result is on stack.
346
    code[2] = Op::Halt;
347
    code[3] = Op::Halt;          // padding
348
    code[4] = Op::Halt;          // padding
349
350
    // Function at pc 5:
351
    code[5] = Op::Store(0);      // pop arg into local[0]
352
    code[6] = Op::Load(0);       // push local[0]
353
    code[7] = Op::Push(2);
354
    code[8] = Op::Mul;           // n * 2
355
    code[9] = Op::Push(1);
356
    code[10] = Op::Add;          // n * 2 + 1
357
    code[11] = Op::Ret;          // return (result on stack)
358
359
    let result: i32 = try! runProgram(&code[..], 12, stackBuf, localsBuf, framesBuf);
360
    // 10 * 2 + 1 = 21
361
    assert result == 21;
362
    return 0;
363
}
364
365
/// Test division by zero detection using try...catch with error binding.
366
fn testDivByZero(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
367
    let mut code: [Op; 8] = [Op::Halt; 8];
368
    code[0] = Op::Push(42);
369
    code[1] = Op::Push(0);
370
    code[2] = Op::Div;
371
    code[3] = Op::Halt;
372
373
    let mut caught: i32 = 0;
374
    try runProgram(&code[..], 4, stackBuf, localsBuf, framesBuf) catch e {
375
        if e == VmError::DivByZero {
376
            caught = 1;
377
        } else {
378
            caught = 2;
379
        }
380
    };
381
    assert caught == 1;
382
    return 0;
383
}
384
385
/// Test negation and equality.
386
fn testNegAndEq(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
387
    let mut code: [Op; 16] = [Op::Halt; 16];
388
    code[0] = Op::Push(5);
389
    code[1] = Op::Neg;           // -5
390
    code[2] = Op::Push(-5);
391
    code[3] = Op::Eq;            // -5 == -5 => 1
392
    code[4] = Op::Halt;
393
394
    let result: i32 = try! runProgram(&code[..], 5, stackBuf, localsBuf, framesBuf);
395
    assert result == 1;
396
    return 0;
397
}
398
399
/// Test factorial using recursive calls: fact(6) = 720.
400
fn testFactorial(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
401
    let mut code: [Op; 32] = [Op::Halt; 32];
402
403
    // Main: push 6, call fact, halt.
404
    code[0] = Op::Push(6);
405
    code[1] = Op::Call(4);       // call fact at 4
406
    code[2] = Op::Halt;
407
    code[3] = Op::Halt;          // padding
408
409
    // fact(n) at pc 4:
410
    //   Store arg to local[0]
411
    //   If n <= 1, push 1, return
412
    //   Else push n, push fact(n-1), multiply, return
413
    code[4] = Op::Store(0);      // local[0] = n
414
    code[5] = Op::Load(0);       // push n
415
    code[6] = Op::Push(2);
416
    code[7] = Op::Lt;            // n < 2
417
    code[8] = Op::JumpIfZero(11); // if n >= 2, skip
418
    code[9] = Op::Push(1);       // base case: push 1
419
    code[10] = Op::Ret;
420
421
    // Recursive case:
422
    code[11] = Op::Load(0);      // push n
423
    code[12] = Op::Push(1);
424
    code[13] = Op::Sub;          // n - 1
425
    code[14] = Op::Call(4);      // fact(n-1)
426
    code[15] = Op::Load(0);      // push n
427
    code[16] = Op::Mul;          // fact(n-1) * n
428
    code[17] = Op::Ret;
429
430
    let result: i32 = try! runProgram(&code[..], 18, stackBuf, localsBuf, framesBuf);
431
    assert result == 720;
432
    return 0;
433
}
434
435
/// Test dup instruction.
436
fn testDup(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
437
    let mut code: [Op; 8] = [Op::Halt; 8];
438
    code[0] = Op::Push(7);
439
    code[1] = Op::Dup;
440
    code[2] = Op::Add;           // 7 + 7 = 14
441
    code[3] = Op::Halt;
442
443
    let result: i32 = try! runProgram(&code[..], 4, stackBuf, localsBuf, framesBuf);
444
    assert result == 14;
445
    return 0;
446
}
447
448
/// Test stack underflow detection using try...catch with error binding.
449
fn testStackUnderflow(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
450
    let mut code: [Op; 4] = [Op::Halt; 4];
451
    code[0] = Op::Add;  // nothing on stack
452
    code[1] = Op::Halt;
453
454
    let mut caught: i32 = 0;
455
    try runProgram(&code[..], 2, stackBuf, localsBuf, framesBuf) catch e {
456
        if e == VmError::StackUnderflow {
457
            caught = 1;
458
        } else {
459
            caught = 2;
460
        }
461
    };
462
    assert caught == 1;
463
    return 0;
464
}
465
466
/// Test that try...catch on success path does not execute catch block.
467
fn testSuccessNoCatch(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
468
    let mut code: [Op; 4] = [Op::Halt; 4];
469
    code[0] = Op::Push(99);
470
    code[1] = Op::Halt;
471
472
    let mut caught: i32 = 0;
473
    let result: i32 = try runProgram(&code[..], 2, stackBuf, localsBuf, framesBuf) catch e {
474
        caught = 1;
475
        return 1;
476
    };
477
    // Catch block should not have run.
478
    assert caught == 0;
479
    // Should have the success value.
480
    assert result == 99;
481
    return 0;
482
}
483
484
/// Test call overflow detection by exhausting frames.
485
fn testCallOverflow(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
486
    let mut code: [Op; 4] = [Op::Halt; 4];
487
    // Infinite recursion: function calls itself.
488
    code[0] = Op::Call(0);
489
    code[1] = Op::Halt;
490
491
    let mut caught: i32 = 0;
492
    try runProgram(&code[..], 2, stackBuf, localsBuf, framesBuf) catch e {
493
        if e == VmError::CallOverflow {
494
            caught = 1;
495
        } else {
496
            caught = 2;
497
        }
498
    };
499
    assert caught == 1;
500
    return 0;
501
}
502
503
/// Test that catch with no binding works (discard the error).
504
fn testCatchNoBinding(stackBuf: *mut [i32], localsBuf: *mut [i32], framesBuf: *mut [Frame]) -> i32 {
505
    let mut code: [Op; 4] = [Op::Halt; 4];
506
    code[0] = Op::Pop;  // underflow
507
    code[1] = Op::Halt;
508
509
    // Catch without binding - just swallow the error.
510
    try runProgram(&code[..], 2, stackBuf, localsBuf, framesBuf) catch {};
511
    return 0;
512
}
513
514
@default fn main() -> i32 {
515
    let mut stackBuf: [i32; 64] = [0; 64];
516
    let mut localsBuf: [i32; 128] = [0; 128];
517
    let mut framesBuf: [Frame; 8] = [Frame { returnAddr: 0, localBase: 0 }; 8];
518
519
    let r1 = testArith(&mut stackBuf[..], &mut localsBuf[..], &mut framesBuf[..]);
520
    if r1 != 0 {
521
        return 10 + r1;
522
    }
523
524
    let r2 = testLocals(&mut stackBuf[..], &mut localsBuf[..], &mut framesBuf[..]);
525
    if r2 != 0 {
526
        return 20 + r2;
527
    }
528
529
    let r3 = testConditional(&mut stackBuf[..], &mut localsBuf[..], &mut framesBuf[..]);
530
    if r3 != 0 {
531
        return 30 + r3;
532
    }
533
534
    let r4 = testLoop(&mut stackBuf[..], &mut localsBuf[..], &mut framesBuf[..]);
535
    if r4 != 0 {
536
        return 40 + r4;
537
    }
538
539
    let r5 = testCall(&mut stackBuf[..], &mut localsBuf[..], &mut framesBuf[..]);
540
    if r5 != 0 {
541
        return 50 + r5;
542
    }
543
544
    let r6 = testDivByZero(&mut stackBuf[..], &mut localsBuf[..], &mut framesBuf[..]);
545
    if r6 != 0 {
546
        return 60 + r6;
547
    }
548
549
    let r7 = testNegAndEq(&mut stackBuf[..], &mut localsBuf[..], &mut framesBuf[..]);
550
    if r7 != 0 {
551
        return 70 + r7;
552
    }
553
554
    let r8 = testFactorial(&mut stackBuf[..], &mut localsBuf[..], &mut framesBuf[..]);
555
    if r8 != 0 {
556
        return 80 + r8;
557
    }
558
559
    let r9 = testDup(&mut stackBuf[..], &mut localsBuf[..], &mut framesBuf[..]);
560
    if r9 != 0 {
561
        return 90 + r9;
562
    }
563
564
    let r10 = testStackUnderflow(&mut stackBuf[..], &mut localsBuf[..], &mut framesBuf[..]);
565
    if r10 != 0 {
566
        return 100 + r10;
567
    }
568
569
    let r11 = testSuccessNoCatch(&mut stackBuf[..], &mut localsBuf[..], &mut framesBuf[..]);
570
    if r11 != 0 {
571
        return 110 + r11;
572
    }
573
574
    let r12 = testCallOverflow(&mut stackBuf[..], &mut localsBuf[..], &mut framesBuf[..]);
575
    if r12 != 0 {
576
        return 120 + r12;
577
    }
578
579
    let r13 = testCatchNoBinding(&mut stackBuf[..], &mut localsBuf[..], &mut framesBuf[..]);
580
    if r13 != 0 {
581
        return 130 + r13;
582
    }
583
584
    return 0;
585
}