Browse Source

vm -> rust

master
Yuki Izumi 4 years ago
parent
commit
22cb971dc5
No known key found for this signature in database GPG Key ID: 44A3D2C95E26BB14
5 changed files with 182 additions and 383 deletions
  1. +0
    -14
      vm/CMakeLists.txt
  2. +5
    -1
      vm/src/main.rs
  3. +177
    -0
      vm/src/vm.rs
  4. +0
    -326
      vm/vm.c
  5. +0
    -42
      vm/vm.h

+ 0
- 14
vm/CMakeLists.txt View File

@@ -1,14 +0,0 @@
cmake_minimum_required(VERSION 2.8)
set(PROGRAM "vm")
set(PROGRAM_SOURCES
main.c
vm.h
vm.c)

add_executable(${PROGRAM} ${PROGRAM_SOURCES})
install(TARGETS ${PROGRAM}
EXPORT ${PROGRAM}
RUNTIME DESTINATION bin
)

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wno-unused-parameter -std=gnu99")

+ 5
- 1
vm/src/main.rs View File

@@ -1,6 +1,10 @@
#![feature(slice_concat_ext)]

extern crate getopts;
extern crate byteorder;

mod vm;

use std::io::Read;
use std::fs::File;
use std::env;
@@ -34,7 +38,7 @@ fn main() {
let mut f = File::open(input).unwrap();
f.read_to_end(&mut s).unwrap();

vm::run(&s[..]);
}

fn print_usage(program: &str, opts: Options) {


+ 177
- 0
vm/src/vm.rs View File

@@ -0,0 +1,177 @@
use std::iter::Iterator;
use std::io::{Cursor, Seek, SeekFrom};
use std::slice::SliceConcatExt;
use std::str;
use byteorder::{BigEndian, ReadBytesExt};

#[derive(Clone, Debug)]
enum Value {
String(String),
U8(u8),
U32(u32),
}

#[derive(Debug)]
struct Process<'a> {
pid: u32,
stack: Vec<Value>,
local: Vec<Vec<Option<Value>>>,
code: &'a[u8],
ip: u32,
}

pub fn run(init_code: &[u8]) {
let p = Process {
pid: 0,
stack: Vec::new(),
local: Vec::new(),
code: init_code,
ip: 0,
};
let mut ps = Vec::new();
ps.push(p);

let mut ix = 0;
while ps.len() > 0 {
ix = ix % ps.len();

if !step(&mut ps[ix]) {
ps.remove(ix);
} else {
ix += 1;
}
}
}

fn step<'a>(p: &mut Process<'a>) -> bool {
println!("p{:08x} (@{:08x}) stack: {}", p.pid, p.ip, format_values(&p.stack));
if p.local.len() > 0 {
let l = p.local[0].clone();
let vs = format_opt_values(l.into_iter());
println!(" locals: ({}) {}", p.local[0].len(), vs);
}

match read8(p) {
0x00 => {
// NOP
}
0x01 => {
// LD u8
let v = read8(p);
p.stack.push(Value::U8(v))
}
0x02 => {
// LD u32
let v = read32(p);
p.stack.push(Value::U32(v))
}
0x03 => {
// ADD (n, n)
let b = p.stack.pop();
let a = p.stack.pop();
match (a, b) {
(Some(Value::U8(av)), Some(Value::U8(bv))) => {
p.stack.push(Value::U8(av + bv))
}
(Some(Value::U32(av)), Some(Value::U32(bv))) => {
p.stack.push(Value::U32(av + bv))
}
(Some(Value::String(av)), Some(Value::String(bv))) => {
p.stack.push(Value::String(av + &bv))
}
(a, b) => {
panic!("p{:08x} hit invalid add @{:08x} ({:?} + {:?})", p.pid, p.ip, a, b)
}
}
}
0x04 => {
// LOCAL u8
let n = read8(p);
let mut v: Vec<Option<Value>> = Vec::with_capacity(n as usize);
v.resize(n as usize, None);
p.local.push(v)
}
0x05 => {
// DROPLOCAL
let _ = p.local.pop();
}
0x06 => {
// STO u8
let i = read8(p);
p.local[0][i as usize] = Some(p.stack[0].clone());
}
0x07 => {
// RC u8
let i = read8(p);
p.stack.push(p.local[0][i as usize].clone().unwrap());
}
0x08 => {
// DROP
let _ = p.stack.pop();
}
0x09 => {
// LDstr
let n = read32(p);
let s = str::from_utf8(readn(p, n)).unwrap().to_owned();
p.stack.push(Value::String(s))
}
0xFF => {
return false
}
_ => unreachable!()
}

true
}

fn read8(p: &mut Process) -> u8 {
if p.ip >= p.code.len() as u32 {
panic!("p{:08x}: invalid read8 @{:08x}", p.pid, p.ip);
}
p.ip += 1;
p.code[(p.ip - 1) as usize]
}

fn read32(p: &mut Process) -> u32 {
if p.ip + 3 >= p.code.len() as u32 {
panic!("p{:08x}: invalid32 read @{:08x}", p.pid, p.ip);
}
p.ip += 4;
let mut cursor = Cursor::new(p.code);
cursor.seek(SeekFrom::Start((p.ip - 4) as u64)).unwrap();
cursor.read_u32::<BigEndian>().unwrap()
}

fn readn<'a>(p: &mut Process<'a>, n: u32) -> &'a[u8] {
if p.ip + n - 1 >= p.code.len() as u32 {
panic!("p{:08x}: invalid32 read @{:08x}", p.pid, p.ip);
}
p.ip += n;
&p.code[(p.ip - n) as usize..p.ip as usize]
}

fn format_values(vs: &[Value]) -> String {
let ss: Vec<String> = vs.into_iter().map(|v| format_value(v)).collect();
ss.join(", ")
}

fn format_value(v: &Value) -> String {
match *v {
Value::String(ref s) => format!(r#""{}""#, s),
Value::U8(n) => format!("{:02x}", n),
Value::U32(n) => format!("{:08x}", n),
}
}

fn format_opt_values<I: Iterator>(vs: I) -> String
where I: Iterator<Item=Option<Value>> {
let ss: Vec<String> = vs.map(|v| format_opt_value(&v)).collect();
ss.join(", ")
}

fn format_opt_value(v: &Option<Value>) -> String {
match *v {
None => "nil".to_owned(),
Some(ref v) => format_value(&v)
}
}

+ 0
- 326
vm/vm.c View File

@@ -1,326 +0,0 @@
#include <setjmp.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "vm.h"

static jmp_buf jb;

#define alloc(t, l) ({ \
value *_v = calloc(1, sizeof(*_v)); \
_v->type = (t); \
l; \
_v; \
})

static void free_value(value *v) {
switch (v->type) {
case T_STRING:
free(v->as.str);
break;
case T_U8:
case T_U32:
break;
default:
abort();
}
free(v);
}

static value *copy(value const *v) {
switch (v->type) {
case T_STRING:
return alloc(v->type, _v->as.str = strdup(v->as.str));
case T_U8:
case T_U32:
return alloc(v->type, _v->as.u32 = v->as.u32);
default:
abort();
}
}

static void fprintv(FILE *f, value const *v) {
if (!v) {
fprintf(f, "(nil)");
return;
}

switch (v->type) {
case T_STRING:
fprintf(f, "\"%s\"", v->as.str);
break;
case T_U8:
fprintf(f, "%02x", v->as.u8);
break;
case T_U32:
fprintf(f, "%08x", v->as.u32);
break;
default:
abort();
}
}

static stack *make_stack(stack *p, value *v) {
stack *s = calloc(1, sizeof(*s));
s->value = v;
s->prev = p;
return s;
}

static void fprints(FILE *f, stack const *s) {
int first = 1;
while (s) {
if (first)
first = 0;
else
fprintf(f, ", ");
fprintv(f, s->value);
s = s->prev;
}
}

static void free_stack(stack *s) {
while (s) {
free_value(s->value);
stack *p = s->prev;
free(s);
s = p;
}
}

static local *make_local(local *prev, uint8_t n) {
local *l = calloc(1, sizeof(*l));
l->n = n;
l->v = calloc(n, sizeof(*l->v));
l->prev = prev;
return l;
}

static void free_local(local *l) {
while (l) {
for (int i = 0; i < l->n; ++i)
free_value(l->v[i]);
free(l->v);
local *n = l->prev;
free(l);
l = n;
}
}

vm_process *make_process() {
static uint32_t pid_counter;
vm_process *p = calloc(1, sizeof(*p));
p->pid = ++pid_counter;
return p;
}

static void free_process(vm_process *p) {
free_stack(p->stack);
free_local(p->local);
free(p->code);
free(p);
}

static value *pop_stack(vm_process *p) {
if (!p->stack) {
printf("p%08x: invalid pop @%08x\n", p->pid, p->ip);
longjmp(jb, 0);
}

stack *s = p->stack;
value *v = s->value;
p->stack = s->prev;
free(s);
return v;
}

static void drop_local(vm_process *p) {
for (int i = 0; i < p->local->n; ++i)
free_value(p->local->v[i]);
free(p->local->v);
local *n = p->local->prev;
free(p->local);
p->local = n;
}

typedef struct vm_process_list {
vm_process *p;
struct vm_process_list *prev, *next;
} vm_process_list;

static uint8_t read8(vm_process *p) {
if (p->ip >= p->code_len) {
printf("p%08x: invalid read @%08x\n", p->pid, p->ip);
longjmp(jb, 0);
}
return p->code[p->ip++];
}

static uint32_t read32(vm_process *p) {
if (p->ip + 3 >= p->code_len) {
printf("p%08x: invalid read @%08x\n", p->pid, p->ip);
longjmp(jb, 0);
}
p->ip += 4;
return ntohl(*((uint32_t *) (p->code + p->ip) - 1));
}

static uint8_t *readn(vm_process *p, uint32_t n) {
if (p->ip + n - 1 >+ p->code_len) {
printf("p%08x: invalid read @%08x\n", p->pid, p->ip);
longjmp(jb, 0);
}
uint8_t *m = malloc(n);
memcpy(m, p->code + p->ip, n);
p->ip += n;
return m;
}

static int step(vm_process *p) {
fprintf(stdout, "p%08x (@%08x) stack: ", p->pid, p->ip);
fprints(stdout, p->stack);
fprintf(stdout, "\n");
if (p->local) {
fprintf(stdout, " locals: (%d) ", p->local->n);
for (int i = 0; i < p->local->n; ++i) {
if (i)
fprintf(stdout, ", ");
fprintv(stdout, p->local->v[i]);
}
fprintf(stdout, "\n");
}

uint8_t op;
switch (op = read8(p)) {
case 0x00:
// NOP
break;
case 0x01:
// LD u8
p->stack = make_stack(p->stack, alloc(T_U8, _v->as.u8 = read8(p)));
break;
case 0x02:
// LD u32
p->stack = make_stack(p->stack, alloc(T_U32, _v->as.u32 = read32(p)));
break;
case 0x03: {
// ADD (n, n)
value *b = pop_stack(p);
value *a = pop_stack(p);
if (a->type != b->type) {
printf("p%08x hit invalid add @%08x (%02x + %02x)\n",
p->pid, p->ip, a->type, b->type);
longjmp(jb, 0);
}
switch (a->type) {
case T_U8:
a->as.u8 += b->as.u8;
free_value(b);
p->stack = make_stack(p->stack, a);
break;
case T_U32:
a->as.u32 += b->as.u32;
free_value(b);
p->stack = make_stack(p->stack, a);
break;
case T_STRING: {
size_t alen = strlen(a->as.str),
blen = strlen(b->as.str);
a->as.str = realloc(a->as.str, alen + blen + 1);
memcpy(a->as.str + alen, b->as.str, blen + 1);
free_value(b);
p->stack = make_stack(p->stack, a);
break;
}
default:
printf("p%08x hit invalid add @%08x (%02x)\n", p->pid, p->ip, a->type);
longjmp(jb, 0);
}
break;
}
case 0x04: {
// LOCAL u8
p->local = make_local(p->local, read8(p));
break;
}
case 0x05: {
// ENDLOCAL
drop_local(p);
break;
}
case 0x06: {
// STO u8
p->local->v[read8(p)] = copy(p->stack->value);
break;
}
case 0x07: {
// RC u8
uint8_t n = read8(p);
p->stack = make_stack(p->stack, copy(p->local->v[n]));
break;
}
case 0x08: {
// DROP
free_value(pop_stack(p));
break;
}
case 0x09: {
// LDstr
uint32_t len = read32(p);
p->stack = make_stack(p->stack, alloc(T_STRING, _v->as.str = (char *) readn(p, len)));
break;
}
case 0xFF:
// RET
return 0;
default:
printf("p%08x hit unknown opcode %02x @%08x\n",
p->pid, op, p->ip);
return 0;
}

return 1;
}

void run(vm_process *init) {
vm_process_list *pl = calloc(1, sizeof(*pl));
pl->p = init;

vm_process_list *it = pl;

while (it) {
int failed = 0;
if (setjmp(jb)) {
failed = 1;
}

if (failed || !step(it->p)) {
if (failed) {
printf("[EXIT] p%08x: abnormal end\n", it->p->pid);
} else if (it->p->stack) {
printf("[EXIT] p%08x: normal (", it->p->pid);
fprintv(stdout, it->p->stack->value);
printf(")\n");
} else {
printf("[EXIT] p%08x: abnormal end (no stack)", it->p->pid);
}

free_process(it->p);
if (it->prev)
it->prev->next = it->next;
else
pl = it->next;
if (it->next)
it->next->prev = it->prev;
vm_process_list *n = it->next;
free(it);
it = n;
} else {
it = it->next;
}

if (!it)
it = pl;
}
}

+ 0
- 42
vm/vm.h View File

@@ -1,42 +0,0 @@
#ifndef VM_VM_H
#define VM_VM_H

typedef enum {
T_STRING,
T_U8,
T_U32,
} type;

typedef struct {
type type;
union {
char *str;
uint8_t u8;
uint32_t u32;
} as;
} value;

typedef struct stack {
value *value;
struct stack *prev;
} stack;

typedef struct local {
uint8_t n;
value **v;
struct local *prev;
} local;

typedef struct {
uint32_t pid;
stack *stack;
local *local;
uint8_t *code;
uint32_t code_len;
uint32_t ip;
} vm_process;

void run(vm_process *init);
vm_process *make_process();

#endif

Loading…
Cancel
Save