@@ 33,7 33,7 @@ struct UVMBlock
# holds live assignments maintained
context_scope = ILNullRef
# local values to capture for subscope
- scope : OrderedSet ILBlockRef
+ export_scope : OrderedSet ValueIndex
# holds global variables, heap and stack; routed through every block to
emulate mutation.
global_scope = ILNullRef
@@ 52,18 52,6 @@ struct UVMFunction
stack : Array UVMBlock
func : Value = null
- #value_cache : (Map ValueIndex Id)
- #init_block : UVMBlock
- #block : UVMBlock
- #active_label : Id = 0
- #active_break_label : Id = 0
-
- inline push (self vm ...)
- 'append self.stack
- UVMBlock
- builder = ILBuilder vm
- ...
-
inline top (self)
'last self.stack
@@ 77,6 65,7 @@ struct UVMFunction
level := 'top self
if (level.mem == ILNullRef)
using ('builder self)
+ #uvm.comment (text = "get mem")
level.mem = uvm.get (value = level.global_scope) (index = GSCOPE_MEM)
level.exported_mem = level.mem
level.mem
@@ 122,15 111,70 @@ struct UVMFunction
arg := copy ('resolve self v i-1)
# must be exported by the above level first
parent := self.stack @ i-1
- 'insert parent.scope arg
- idx := ('indexof parent.scope arg) as i32
+ 'insert parent.export_scope v
+ idx := ('indexof parent.export_scope v) as i32
level := self.stack @ i
arg := do
using level.builder
+ #uvm.comment
+ text = "import-scope arg"
uvm.get (value = level.context_scope) (index = idx)
'set level.values v arg
arg
+ fn build-scope (self)
+ level := 'top self
+ valcount := (countof level.export_scope) as i32
+ do
+ using ('builder self)
+ local context = uvm.alloc (size = 0) (count = valcount)
+ #uvm.comment
+ text = "export-scope {"
+ for i val in (enumerate level.export_scope)
+ val := try! ('get level.values val)
+ context = uvm.set
+ value = context
+ element = val
+ index = i
+ #uvm.comment
+ text = "} export-scope"
+ context
+
+ fn transport-scope (self)
+ # export all variables from the parent export scope, if any
+ count := countof self.stack
+ if (count <= 1)
+ return;
+ idx := count - 1
+ parent_idx := idx - 1
+ parent := self.stack @ parent_idx
+ using ('builder self)
+ #uvm.comment
+ text = "transport-scope {"
+ level := 'top self
+ # inherit 1:1 mapping of parent export scope so the routing to
+ later blocks remains as expected.
+ assert (empty? level.export_scope)
+ for i v in (enumerate parent.export_scope)
+ arg := do
+ uvm.get (value = level.context_scope) (index = i)
+ v := copy v
+ 'set level.values v arg
+ 'insert level.export_scope v
+ #uvm.comment
+ text = "} transport-scope"
+ ;
+
+ inline pop (self)
+ 'pop self.stack
+ ;
+
+ inline push (self vm ...)
+ 'append self.stack
+ UVMBlock
+ builder = ILBuilder vm
+ ...
+
################################################################################
struct UVMGenerator
@@ 185,8 229,11 @@ struct UVMGenerator
using ('builder ctx)
level := 'top ctx
scope := (uvm.scope)
+ #uvm.comment (text = "get branch gscope")
level.global_scope = uvm.get (value = scope) (index = 0)
+ #uvm.comment (text = "get branch lscope")
level.context_scope = uvm.get (value = scope) (index = 1)
+ 'transport-scope ctx
'translate-block self ctx block
'finalize-blocks self ctx startlevel
@@ 806,7 853,9 @@ struct UVMGenerator
mem := 'mem ctx
result := uvm.ccall (mem = mem) (target = func) (value = args)
if (returning? RT)
+ #uvm.comment (text = "get mem")
mem = uvm.get (value = result) (index = 0)
+ #uvm.comment (text = "get result")
uvm.get (value = result) (index = 1)
else
uvm.unreachable;
@@ 863,6 912,7 @@ struct UVMGenerator
level.mem = ILNullRef
for i argT in (enumerate (argument-type-elements rtype))
#valtype := 'translate-type self argT
+ #uvm.comment (text = "get return value")
'map@ ctx instr i
uvm.get (value = retval) (index = i + 1)
case ValueKind.CondBr
@@ 880,7 930,7 @@ struct UVMGenerator
using ('builder ctx)
# transfer heap and scope
level := 'top ctx
- context := 'build-scope self ctx
+ context := 'build-scope ctx
scope := uvm.alloc (size = 0) (count = 2)
scope := uvm.set (value = scope) (element = 'synced-gscope ctx) (index = 0)
scope := uvm.set (value = scope) (element = context) (index = 1)
@@ 914,7 964,7 @@ struct UVMGenerator
using ('builder ctx)
# transfer heap and scope
level := 'top ctx
- context := 'build-scope self ctx
+ context := 'build-scope ctx
scope := uvm.alloc (size = 0) (count = 2)
scope := uvm.set (value = scope) (element = 'synced-gscope ctx) (index = 0)
scope := uvm.set (value = scope) (element = context) (index = 1)
@@ 934,6 984,9 @@ struct UVMGenerator
when the block is finalized, the label contents need to
be filled in, with a final goto to our new block; all
this info can be retrieved while unpopping the stack.
+ #do
+ using ('builder ctx)
+ uvm.comment (text = "--- label body starts here")
rtype := 'qualified-typeof instr
level := 'top ctx
assert (not level.label)
@@ 945,8 998,11 @@ struct UVMGenerator
env := (uvm.scope)
#result := get (value = env) (index = 1)
level := 'top ctx
+ #uvm.comment (text = "get label gscope")
level.global_scope = uvm.get (value = env) (index = 0)
+ #uvm.comment (text = "get label lscope")
level.context_scope = uvm.get (value = env) (index = 1)
+ 'transport-scope ctx
if (returning? rtype)
#'set ctx.value_cache (ValueIndex instr -1) id
@@ 961,6 1017,7 @@ struct UVMGenerator
for i argT in (enumerate (argument-type-elements rtype))
#valname := 'dedup-valuename ctx (.. vn "_i")
#valtype := 'translate-type self argT
+ #uvm.comment (text = "get merge return value")
'map@ ctx instr i
uvm.get (value = env) (index = (i + 2))
#else
@@ 980,12 1037,15 @@ struct UVMGenerator
this-block := (uvm.block)
'map@ ctx instr -1 this-block
level := 'top ctx
+ #uvm.comment (text = "get loop label gscope")
level.global_scope = uvm.get
value = scope
index = 0
+ #uvm.comment (text = "get loop label lscope")
level.context_scope = uvm.get
value = scope
index = 1
+ 'transport-scope ctx
# repeat needs to route the context back into the loop, so
we bind it here verbatim.
'map@ ctx instr -2 level.context_scope
@@ 993,6 1053,7 @@ struct UVMGenerator
params := 'typed-attribute@ instr 0 0
paramtypes := 'qualified-typeof params
for i argT in (enumerate (argument-type-elements paramtypes))
+ #uvm.comment (text = "get loop label argument")
val := uvm.get
value = scope
index = i + 2
@@ 1005,7 1066,7 @@ struct UVMGenerator
do
level := 'top ctx
using ('builder ctx)
- context := 'build-scope self ctx
+ context := 'build-scope ctx
# build initial scope
argcount := 'typed-attribute-count instr 1
local initvals = uvm.alloc (size = 0) (count = argcount + 2)
@@ 1210,6 1271,8 @@ struct UVMGenerator
dest := try (copy ('get ctx.labels value))
else
error "cannot jump to label because it has not been translated yet"
+ #'transport-scope ctx
+ # finish label body
body := 'typed-body value
'translate-block self ctx body
func := 'toblock ('builder ctx)
@@ 1217,7 1280,7 @@ struct UVMGenerator
try ('unwrap level.alias)
then (value)
'set ctx.labels (copy value) func
- 'pop ctx.stack
+ 'pop ctx
func
inline finalize-all-but-current-block (self ctx startlevel)
@@ 1231,19 1294,6 @@ struct UVMGenerator
inline translate-value (self ctx value)
'translate self ctx (ValueIndex value)
- inline build-scope (self ctx)
- level := 'top ctx
- valcount := (countof level.scope) as i32
- do
- using ('builder ctx)
- local context = uvm.alloc (size = 0) (count = valcount)
- for i val in (enumerate level.scope)
- context = uvm.set
- value = context
- element = val
- index = i
- context
-
fn translate-ffi-type (self T)
returning uvm_cell_t
raising Error
@@ 1420,15 1470,18 @@ struct UVMGenerator
thisfunc := (uvm.block)
'map subctx value thisfunc
# has format {heap, empty}; second slot can be used for local state
+ #uvm.comment (text = "get function gscope")
global_scope := uvm.get (value = scope) (index = 0)
#heap := uvm.call (block = self.builtin_heap) (scope = 'cell self.vm)
#global_scope := uvm.alloc (size = 0) (count = 2)
#global_scope := uvm.set (value = global_scope) (element = heap) (index = 0)
# patched later
+ #uvm.comment (text = "get functable")
functable := uvm.get (value = global_scope) (index = GSCOPE_FUNCTABLE)
'map@ subctx value -1 functable
level.global_scope = global_scope
for i param in (enumerate ('typed-attributes value 0))
+ #uvm.comment (text = "get function argument")
instr := uvm.get
value = scope
index = i + 1
@@ 1649,8 1702,11 @@ struct UVMGenerator
'indexof self.functable value
then (idx)
# patch it in
+ assert (ctx.func != null)
functableid := try! ('resolve ctx (ValueIndex (copy ctx.func) -1) i)
using ('builder ctx)
+ #uvm.comment
+ text = "get functable func"
f := uvm.get (value = functableid) (index = idx)
'map ctx value f
return f
@@ 1709,7 1765,6 @@ struct UVMGenerator
level := 'top subctx
scope := (uvm.scope)
scope := fold (scope) for f in self.constructors
- print ('typeof f)
func := 'translate-constant self (copy f) 0
uvm.call (block = func) (scope = scope)
uvm.return (value = scope)
@@ 1728,6 1783,8 @@ struct UVMGenerator
if (self.flags & compile-flag-dump-disassembly)
uvm_dump_disas self.vm func 0
+ elseif (self.flags & compile-flag-dump-module)
+ uvm_dump_disas self.vm func uvmdf_recursive
entry := uvm_store self.vm func
ftable := do
@@ 77,6 77,10 @@ receives parameters, is evaluated and pr
namespace uvm {
+// todo: get this value from the OS
+static const size_t uvm_pagesize = sysconf(_SC_PAGE_SIZE);
+#define UVM_PAGESIZE ((size_t)uvm_pagesize)
+
#define UVM_VERSION_STRING_BASE "UVM-0.1"
#if UVM_REBUILD_CACHE_ON_COMPILE
#define UVM_VERSION_STRING UVM_VERSION_STRING_BASE "-" __DATE__ "-" __TIME__
@@ 499,7 503,6 @@ struct CHeap {
struct MemWriteTracker {
int uffd;
- long pagesize;
volatile int stop_pagefault_handler;
pthread_t uffd_thread;
@@ 596,7 599,7 @@ static void *handler(void *arg)
// write protection
struct uffdio_writeprotect uffdio_wp;
uffdio_wp.range.start = msg.arg.pagefault.address;
- uffdio_wp.range.len = p.pagesize;
+ uffdio_wp.range.len = UVM_PAGESIZE;
uffdio_wp.mode = 0;
if (ioctl(p.uffd, UFFDIO_WRITEPROTECT, &uffdio_wp) == -1) {
perror("thread/ioctl/uffdio_writeprotect");
@@ 610,7 613,7 @@ static void *handler(void *arg)
// zeropage
struct uffdio_zeropage zp;
zp.range.start = msg.arg.pagefault.address;
- zp.range.len = p.pagesize;
+ zp.range.len = UVM_PAGESIZE;
zp.mode = 0;
if (ioctl(p.uffd, UFFDIO_ZEROPAGE, &zp) == -1) {
perror("ioctl/zeropage");
@@ 691,7 694,6 @@ void uvm_setup_userfault_handler () {
mwt.stop_pagefault_handler = 0;
mwt.uffd = uffd;
- mwt.pagesize = sysconf(_SC_PAGE_SIZE);
pthread_create(&mwt.uffd_thread, NULL, handler, &mwt);
@@ 798,6 800,10 @@ UVM::UVM() {
err = mdb_env_create(&env);
assert(!err);
+ // todo: increase map size on demand
+ err = mdb_env_set_mapsize(env, UVM_PAGESIZE * 2560);
+ assert(!err);
+
err = mdb_env_open(env, dbpath, MDB_NOSUBDIR, 0666);
assert(!err);
@@ 2116,7 2122,7 @@ void uvm_dump_disas_instr(UVM &vm, Cell
#undef Fu32
default: w += fprintf(stderr, "?"); break;
}
- if (!(flags & uvm_dump_no_block_meta_info)) {
+ if (!(flags & uvmdf_no_block_meta_info)) {
for (; w < 40; ++w) {
fprintf(stderr, " ");
}
@@ 2131,42 2137,58 @@ void uvm_dump_disas_instr(UVM &vm, Cell
}
void uvm_dump_disas(UVM &vm, Cell &cell, uint32_t flags) {
- if (!(flags & uvm_dump_no_instructions)) {
- fprintf(stderr, " # block ");
- uvm_print_uref(stderr, uvm_cell_uref(vm, cell));
- auto err = uvm_check_valid_block(vm, cell);
- if (err != Validated_LooksFine) {
- fprintf(stderr, " !%s", uvm_block_validation_name(err));
- }
- fprintf(stderr, "\n");
- size_t count = cell.size() / sizeof(Instruction);
- for (size_t i = 0; i < count; ++i) {
- uvm_dump_disas_instr(vm, cell, i, flags);
- }
- } // if (!(flags & DumpFlags_NoInstructions))
- if (cell.count()) {
- fprintf(stderr, " # data\n");
- for (size_t i = 0; i < cell.count(); ++i) {
- fprintf(stderr, " @%zu = ", i);
- auto subcell = cell.cells()[i];
- if (!subcell) {
- fprintf(stderr, "undef ");
- }
- if (!uvm_try_unthunk(vm, *subcell)) {
- uvm_print_uref(stderr, uvm_cell_uref(vm, *subcell));
- fprintf(stderr, " ");
- fprintf(stderr, "<missing> ");
- } else if (uvm_is_valid_block(vm, *subcell)) {
- fprintf(stderr, "block ");
- uvm_print_uref(stderr, uvm_cell_uref(vm, *subcell));
- } else {
- uvm_print_uref(stderr, uvm_cell_uref(vm, *subcell));
- fprintf(stderr, " ");
- uvm_dump(vm, *subcell);
+ std::vector<Cell *> todo;
+ std::unordered_set<uvm_uref, uvm_uref_hash> seen;
+ todo.push_back(&cell);
+ seen.insert(uvm_cell_uref(vm, cell));
+ while (!todo.empty()) {
+ auto &cell = *todo.back();
+ todo.pop_back();
+ if (!(flags & uvmdf_no_instructions)) {
+ fprintf(stderr, " # block ");
+ uvm_print_uref(stderr, uvm_cell_uref(vm, cell));
+ auto err = uvm_check_valid_block(vm, cell);
+ if (err != Validated_LooksFine) {
+ fprintf(stderr, " !%s", uvm_block_validation_name(err));
}
fprintf(stderr, "\n");
+ size_t count = cell.size() / sizeof(Instruction);
+ for (size_t i = 0; i < count; ++i) {
+ uvm_dump_disas_instr(vm, cell, i, flags);
+ }
+ } // if (!(flags & DumpFlags_NoInstructions))
+ if (cell.count()) {
+ fprintf(stderr, " # data\n");
+ for (size_t i = 0; i < cell.count(); ++i) {
+ fprintf(stderr, " @%zu = ", i);
+ auto subcell = cell.cells()[i];
+ if (!subcell) {
+ fprintf(stderr, "undef ");
+ }
+ if (!uvm_try_unthunk(vm, *subcell)) {
+ uvm_print_uref(stderr, uvm_cell_uref(vm, *subcell));
+ fprintf(stderr, " ");
+ fprintf(stderr, "<missing> ");
+ } else if (uvm_is_valid_block(vm, *subcell)) {
+ fprintf(stderr, "block ");
+ auto subcell_uref = uvm_cell_uref(vm, *subcell);
+ uvm_print_uref(stderr, subcell_uref);
+ if (flags & uvmdf_recursive) {
+ if (!seen.count(subcell_uref)) {
+ todo.push_back(subcell);
+ seen.insert(subcell_uref);
+ }
+ }
+ } else {
+ uvm_print_uref(stderr, uvm_cell_uref(vm, *subcell));
+ fprintf(stderr, " ");
+ uvm_dump(vm, *subcell);
+ }
+ fprintf(stderr, "\n");
+ }
}
}
+ fprintf(stderr, "# %zu block(s) total\n", seen.size());
}
// we need to simulate accessing a contained pointer; for this, we
@@ 2225,7 2247,7 @@ void uvm_dump_stack_entry(UVM &vm, size_
if (true) {
if (block) {
fprintf(stderr, "\n");
- uvm_dump_disas(vm, *cell, uvm_dump_no_instructions);
+ uvm_dump_disas(vm, *cell, uvmdf_no_instructions);
} else {
uvm_dump(vm, *cell);
fprintf(stderr, "\n");
@@ 2263,13 2285,13 @@ void uvm_dump_stack(UVM &vm) {
uvm_dump_stack_entry(vm, start, start - 1, seen);
Cell *block = vm.stack[start - 2].cell;
for (size_t i = start; i < end; ++i) {
- uvm_dump_disas_instr(vm, *block, i - start, uvm_dump_no_block_meta_info);
+ uvm_dump_disas_instr(vm, *block, i - start, uvmdf_no_block_meta_info);
fprintf(stderr, "%%%zu -> ", i - start);
uvm_dump_stack_entry(vm, start, i, seen);
}
auto numinstr = block->size() / sizeof(Instruction);
if ((end - start) < numinstr)
- uvm_dump_disas_instr(vm, *block, end - start, uvm_dump_no_block_meta_info);
+ uvm_dump_disas_instr(vm, *block, end - start, uvmdf_no_block_meta_info);
}
fprintf(stderr, "\n");
}
@@ 2316,7 2338,7 @@ void uvm_dump_trace(UVM &vm) {
}
auto numinstr = block->size() / sizeof(Instruction);
if ((end - start) < numinstr)
- uvm_dump_disas_instr(vm, *block, end - start, uvm_dump_no_block_meta_info);
+ uvm_dump_disas_instr(vm, *block, end - start, 0/*uvm_dump_no_block_meta_info*/);
}
inline Instruction *uvm_stack_get_instruction(UVM &vm, uvm_ref ip) {
@@ 2477,6 2499,12 @@ inline void uvm_stack_cleanup_scope(UVM
inline void uvm_stack_goto(UVM &vm, Cell &block, Cell &scope, uvm_instruction_kind_t kind) {
uvm_unthunk(vm, block);
+ if (!uvm_is_valid_block(vm, block)) {
+ uvm_dump_disas(vm, block);
+ assert(false && "invalid block");
+ fprintf(stderr, "UVM: jumping to invalid block");
+ exit(1);
+ }
uvm_stack_cleanup_scope(vm);
uvm_stack_set(vm, vm.stack_start - 2, &block);
uvm_stack_set(vm, vm.stack_start - 1, &scope);
@@ 2612,7 2640,6 @@ FFITypeCacheEntry &uvm_translate_ffi_typ
// for the case that end < begin,
// partbegin..begin covers the same partially used range as end..partend
inline void usedpages(uintptr_t &partbegin, uintptr_t &begin, uintptr_t &end, uintptr_t &partend) {
-#define UVM_PAGESIZE 4096ull
// round down
partbegin = begin & ~(UVM_PAGESIZE-1);
// round up