Revision
1508
Author
vincent.isambart@gmail.com
Date
2009-05-01 20:49:04 -0700 (Fri, 01 May 2009)

Log Message

First version of code that save local variables in the heap if they can be used later.
It's far from being finished, but it's still a good start.

Modified Paths

Diff

Modified: MacRuby/branches/experimental/gc.c (1507 => 1508)


--- MacRuby/branches/experimental/gc.c	2009-04-30 23:06:00 UTC (rev 1507)
+++ MacRuby/branches/experimental/gc.c	2009-05-02 03:49:04 UTC (rev 1508)
@@ -239,6 +239,19 @@
 VALUE rb_mGC;
 
 void
+rb_gc_assign_weak_ref(const void *value, void *const*location)
+{
+    auto_assign_weak_reference(__auto_zone, value, location, NULL);
+}
+
+void*
+rb_gc_read_weak_ref(void **referrer)
+{
+    return auto_read_weak_reference(__auto_zone, referrer);
+}
+
+
+void
 rb_objc_wb(void *dst, void *newval)
 {
     if (!SPECIAL_CONST_P(newval)) {

Modified: MacRuby/branches/experimental/proc.c (1507 => 1508)


--- MacRuby/branches/experimental/proc.c	2009-04-30 23:06:00 UTC (rev 1507)
+++ MacRuby/branches/experimental/proc.c	2009-05-02 03:49:04 UTC (rev 1508)
@@ -34,12 +34,28 @@
     return obj;
 }
 
+void rb_gc_assign_weak_ref(const void *value, void *const*location);
+
 VALUE
 rb_proc_alloc_with_block(VALUE klass, rb_vm_block_t *proc)
 {
     VALUE obj;
     obj = Data_Wrap_Struct(klass, NULL, NULL, proc);
-    proc->flags |= VM_BLOCK_PROC;
+    if (!(proc->flags & VM_BLOCK_PROC)) {
+	proc->flags |= VM_BLOCK_PROC;
+	if (proc->parent_lvar_uses != NULL) {
+	    rb_vm_lvar_uses_t** parent_lvar_uses = proc->parent_lvar_uses;
+	    if ((*parent_lvar_uses == NULL) || ((*parent_lvar_uses)->uses_count == VM_LVAR_USES_SIZE)) {
+		rb_vm_lvar_uses_t* new_uses = malloc(sizeof(rb_vm_lvar_uses_t));
+		new_uses->next = *parent_lvar_uses;
+		new_uses->uses_count = 0;
+		*parent_lvar_uses = new_uses;
+	    }
+	    int current_index = (*parent_lvar_uses)->uses_count;
+	    rb_gc_assign_weak_ref(proc, &(*parent_lvar_uses)->uses[current_index]);
+	    ++(*parent_lvar_uses)->uses_count;
+	}
+    }
     return obj;
 }
 

Modified: MacRuby/branches/experimental/roxor.cpp (1507 => 1508)


--- MacRuby/branches/experimental/roxor.cpp	2009-04-30 23:06:00 UTC (rev 1507)
+++ MacRuby/branches/experimental/roxor.cpp	2009-05-02 03:49:04 UTC (rev 1508)
@@ -231,6 +231,7 @@
 	ID self_id;
 	Value *current_self;
 	bool current_block;
+	Value *current_lvar_uses;
 	BasicBlock *begin_bb;
 	BasicBlock *rescue_bb;
 	BasicBlock *ensure_bb;
@@ -279,6 +280,7 @@
 	Function *newRangeFunc;
 	Function *newRegexpFunc;
 	Function *strInternFunc;
+	Function *keepVarsFunc;
 	Function *masgnGetElemBeforeSplatFunc;
 	Function *masgnGetElemAfterSplatFunc;
 	Function *masgnGetSplatFunc;
@@ -324,6 +326,13 @@
 		: new IntToPtrInst(ptrint, PtrTy, "");
 	}
 
+	Instruction *compile_const_pointer_to_pointer(void *ptr, bool insert_to_bb=true) {
+	    Value *ptrint = ConstantInt::get(IntTy, (long)ptr);
+	    return insert_to_bb
+		? new IntToPtrInst(ptrint, PtrPtrTy, "", bb)
+		: new IntToPtrInst(ptrint, PtrPtrTy, "");
+	}
+
 	Value *compile_protected_call(Function *func, std::vector<Value *> &params);
 	void compile_dispatch_arguments(NODE *args, std::vector<Value *> &arguments, int *pargc);
 	Function::ArgumentListType::iterator
@@ -660,6 +669,7 @@
     current_instance_method = false;
     self_id = rb_intern("self");
     current_self = NULL;
+    current_lvar_uses = NULL;
     current_block = false;
     current_block_node = NULL;
     current_block_func = NULL;
@@ -706,6 +716,7 @@
     newRangeFunc = NULL;
     newRegexpFunc = NULL;
     strInternFunc = NULL;
+    keepVarsFunc = NULL;
     masgnGetElemBeforeSplatFunc = NULL;
     masgnGetElemAfterSplatFunc = NULL;
     masgnGetSplatFunc = NULL;
@@ -1226,11 +1237,13 @@
 
     if (prepareBlockFunc == NULL) {
 	// void *rb_vm_prepare_block(Function *func, NODE *node, VALUE self,
+	//			     rb_vm_lvar_uses_t **parent_lvar_uses,
 	//			     int dvars_size, ...);
 	std::vector<const Type *> types;
 	types.push_back(PtrTy);
 	types.push_back(PtrTy);
 	types.push_back(RubyObjTy);
+	types.push_back(PtrPtrTy);
 	types.push_back(Type::Int32Ty);
 	FunctionType *ft = FunctionType::get(PtrTy, types, true);
 	prepareBlockFunc = cast<Function>
@@ -1241,6 +1254,12 @@
     params.push_back(compile_const_pointer(current_block_func));
     params.push_back(compile_const_pointer(current_block_node));
     params.push_back(current_self);
+    if (current_lvar_uses == NULL) {
+	params.push_back(compile_const_pointer_to_pointer(NULL));
+    }
+    else {
+	params.push_back(current_lvar_uses);
+    }
 
     // Dvars.
     params.push_back(ConstantInt::get(Type::Int32Ty, (int)dvars.size()));
@@ -2984,7 +3003,15 @@
 		    dvars_arg->setName("dvars");
 		}
 
-		if (node->nd_tbl != NULL) {
+		Value *old_current_lvar_uses = current_lvar_uses;
+
+		if (node->nd_tbl == NULL) {
+		    current_lvar_uses = NULL;
+		}
+		else {
+		    current_lvar_uses = new AllocaInst(PtrTy, "", bb);
+		    new StoreInst(compile_const_pointer(NULL), current_lvar_uses, bb);
+
 		    int i, args_count = (int)node->nd_tbl[0];
 		    assert(args_count == nargs
 			    || args_count == nargs + 1 /* optional block */
@@ -3087,6 +3114,39 @@
 		if (val == NULL) {
 		    val = nilVal;
 		}
+
+		// current_lvar_uses has 2 uses or more if it is really used
+		// (there is always a StoreInst in which we assign it NULL)
+		if (current_lvar_uses != NULL && current_lvar_uses->hasNUsesOrMore(2)) {
+		    // TODO: only call the function is current_use is not NULL
+		    if (keepVarsFunc == NULL) {
+			// void rb_vm_keep_vars(rb_vm_lvar_uses_t *uses, int lvars_size, ...);
+			std::vector<const Type *> types;
+			types.push_back(PtrTy);
+			types.push_back(Type::Int32Ty);
+			FunctionType *ft = FunctionType::get(Type::VoidTy, types, true);
+			keepVarsFunc = cast<Function>
+			    (module->getOrInsertFunction("rb_vm_keep_vars", ft));
+		    }
+
+		    std::vector<Value *> params;
+
+		    params.push_back(new LoadInst(current_lvar_uses, "", bb));
+
+		    params.push_back(ConstantInt::get(Type::Int32Ty, (int)lvars.size()));
+		    for (std::map<ID, Value *>::iterator iter = lvars.begin();
+			 iter != lvars.end(); ++iter) {
+			ID name = iter->first;
+			Value *slot = iter->second;
+			if (std::find(dvars.begin(), dvars.end(), name) == dvars.end()) {
+			    params.push_back(ConstantInt::get(IntTy, (long)name));
+			    params.push_back(slot);
+			}
+		    }
+
+		    CallInst::Create(keepVarsFunc, params.begin(), params.end(), "", bb);
+		}
+
 		ReturnInst::Create(val, bb);
 
 		bb = old_bb;
@@ -3094,6 +3154,7 @@
 		lvars = old_lvars;
 		current_self = old_self;
 		rescue_bb = old_rescue_bb;
+		current_lvar_uses = old_current_lvar_uses;
 
 		return cast<Value>(f);
 	    }
@@ -7681,7 +7742,7 @@
 
 extern "C"
 rb_vm_block_t *
-rb_vm_prepare_block(void *llvm_function, NODE *node, VALUE self,
+rb_vm_prepare_block(void *llvm_function, NODE *node, VALUE self, rb_vm_lvar_uses_t **parent_lvar_uses,
 		    int dvars_size, ...)
 {
     NODE *cache_key;
@@ -7716,6 +7777,7 @@
 	}
 	b->flags = 0;
 	b->dvars_size = dvars_size;
+	b->parent_lvar_uses = NULL;
 
 	rb_objc_retain(b);
 	GET_VM()->blocks[cache_key] = b;
@@ -7728,6 +7790,7 @@
 
     b->self = self;
     b->node = node;
+    b->parent_lvar_uses = parent_lvar_uses;
 
     va_list ar;
     va_start(ar, dvars_size);
@@ -7757,7 +7820,72 @@
 }
 
 extern "C"
+void*
+rb_gc_read_weak_ref(void **referrer);
+
+struct rb_vm_kept_local {
+    ID name;
+    VALUE *stack_address;
+    VALUE *new_address;
+};
+
+extern "C"
 void
+rb_vm_keep_vars(rb_vm_lvar_uses_t *uses, int lvars_size, ...)
+{
+    rb_vm_lvar_uses_t *current = uses;
+    int use_index;
+
+    while (current != NULL) {
+	for (use_index = 0; use_index < current->uses_count; ++use_index) {
+	    if (rb_gc_read_weak_ref(&current->uses[use_index]) != NULL) {
+		goto use_found;
+	    }
+	}
+
+	void *old_current = current;
+	current = current->next;
+	free(old_current);
+    }
+    // no use still alive so nothing to do
+    return;
+
+use_found:
+    rb_vm_kept_local *locals = (rb_vm_kept_local *)alloca(sizeof(rb_vm_kept_local)*lvars_size);
+
+    va_list ar;
+    va_start(ar, lvars_size);
+    for (int i = 0; i < lvars_size; ++i) {
+	locals[i].name = va_arg(ar, ID);
+	locals[i].stack_address = va_arg(ar, VALUE *);
+	locals[i].new_address = (VALUE *)xmalloc(sizeof(VALUE));
+	GC_WB(locals[i].new_address, *locals[i].stack_address);
+    }
+    va_end(ar);
+
+    while (current != NULL) {
+	for (; use_index < current->uses_count; ++use_index) {
+	    rb_vm_block_t *block = (rb_vm_block_t *)rb_gc_read_weak_ref(&current->uses[use_index]);
+	    if (block != NULL) {
+		for (int dvar_index=0; dvar_index<block->dvars_size; ++dvar_index) {
+		    for (int lvar_index=0; lvar_index<lvars_size; ++lvar_index) {
+			if (block->dvars[dvar_index] == locals[lvar_index].stack_address) {
+			    GC_WB(&block->dvars[dvar_index], locals[lvar_index].new_address);
+			    break;
+			}
+		    }
+		}
+	    }
+	}
+	void *old_current = current;
+	current = current->next;
+	use_index = 0;
+	free(old_current);
+    }
+}
+
+extern "C"
+void
 rb_vm_push_binding(VALUE self, int lvars_size, ...)
 {
     rb_vm_binding_t *b = (rb_vm_binding_t *)xmalloc(sizeof(rb_vm_binding_t));

Modified: MacRuby/branches/experimental/roxor.h (1507 => 1508)


--- MacRuby/branches/experimental/roxor.h	2009-04-30 23:06:00 UTC (rev 1507)
+++ MacRuby/branches/experimental/roxor.h	2009-05-02 03:49:04 UTC (rev 1508)
@@ -19,6 +19,13 @@
 };
 typedef struct rb_vm_local rb_vm_local_t;
 
+#define VM_LVAR_USES_SIZE 128
+typedef struct rb_vm_lvar_uses {
+    int uses_count;
+    void *uses[VM_LVAR_USES_SIZE];
+    struct rb_vm_lvar_uses *next;
+} rb_vm_lvar_uses_t;
+
 #define VM_BLOCK_PROC	0x0001	// block is a Proc object
 #define VM_BLOCK_LAMBDA 0x0002	// block is a lambda
 #define VM_BLOCK_ACTIVE 0x0004	// block is active (being executed)
@@ -30,6 +37,7 @@
     IMP imp;
     int flags;
     rb_vm_local_t *locals;
+    rb_vm_lvar_uses_t **parent_lvar_uses;
     int dvars_size;
     VALUE *dvars[1];
 } rb_vm_block_t;
@@ -139,7 +147,8 @@
    return (rb_vm_block_t *)DATA_PTR(proc);
 }
 
-rb_vm_block_t *rb_vm_prepare_block(void *llvm_function, NODE *node, VALUE self, int dvars_size, ...);
+rb_vm_block_t *rb_vm_prepare_block(void *llvm_function, NODE *node, VALUE self,
+       rb_vm_lvar_uses_t **parent_lvar_uses, int dvars_size, ...);
 rb_vm_block_t *rb_vm_current_block(void);
 bool rb_vm_block_saved(void);
 void rb_vm_change_current_block(rb_vm_block_t *block);

Modified: MacRuby/branches/experimental/vm_eval.c (1507 => 1508)


--- MacRuby/branches/experimental/vm_eval.c	2009-04-30 23:06:00 UTC (rev 1507)
+++ MacRuby/branches/experimental/vm_eval.c	2009-05-02 03:49:04 UTC (rev 1508)
@@ -277,7 +277,7 @@
 		   VALUE (*bl_proc) (ANYARGS), VALUE data2)
 {
     NODE *node = NEW_IFUNC(bl_proc, data2);
-    rb_vm_block_t *b = rb_vm_prepare_block(NULL, node, obj, 0, 0);
+    rb_vm_block_t *b = rb_vm_prepare_block(NULL, node, obj, NULL, 0, 0);
     rb_vm_change_current_block(b);
     if (cache == NULL) {
 	cache = rb_vm_get_call_cache(sel);