Kaynağa Gözat

Fixed an issue with nested for loop (due to temp registers not protected). Unit test added.

Marco Bambini 6 yıl önce
ebeveyn
işleme
9f829d8f21

+ 44 - 42
src/compiler/gravity_codegen.c

@@ -361,7 +361,7 @@ static void visit_compound_stmt (gvisitor_t *self, gnode_compound_stmt_t *node)
         // in case of function context cleanup temporary registers
         gravity_function_t *f = (gravity_function_t*)context_object;
         ircode_t *code = (ircode_t *)f->bytecode;
-        ircode_register_clear_temps(code);
+        ircode_register_temps_clear(code);
     });
 
     if (node->nclose != UINT32_MAX) {
@@ -540,14 +540,12 @@ static void visit_loop_for_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
     //    }
     // }
 
-    uint32_t $expr = ircode_register_push_temp(code);            // ++TEMP => 1
-    uint32_t $value = ircode_register_push_temp(code);            // ++TEMP => 2
-
-    // $expr and $value are temporary registers that must not be cleared by ircode_register_clear_temps
-    // in visit_compound_statement, so mark them to skip clear
-    ircode_register_set_skip_clear(code, $expr);
-    ircode_register_set_skip_clear(code, $value);
+    // $expr and $value are temporary registers that must be protected, otherwise
+    // in visit_compound_statement, all temp registers are released within ircode_register_temps_clear
+    uint32_t $expr = ircode_register_push_temp_protected(code);     // ++TEMP => 1
+    uint32_t $value = ircode_register_push_temp_protected(code);    // ++TEMP => 2
 
+    // get cpool index for the required methods
     uint16_t iterate_idx = gravity_function_cpool_add(GET_VM(), context_function, VALUE_FROM_CSTRING(NULL, ITERATOR_INIT_FUNCTION));
     uint16_t next_idx = gravity_function_cpool_add(GET_VM(), context_function, VALUE_FROM_CSTRING(NULL, ITERATOR_NEXT_FUNCTION));
     uint32_t cond_idx = node2index(node->cond);
@@ -559,28 +557,26 @@ static void visit_loop_for_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
 	ircode_add(code, MOVE, $expr, once_expr, 0, LINE_NUMBER(node));
 
     // generate code for $value = $expr.iterate(null);
-    uint32_t iterate_fn = ircode_register_push_temp(code);        // ++TEMP => 3
+    uint32_t iterate_fn = ircode_register_push_temp_protected(code);    // ++TEMP => 3
 	ircode_add(code, LOADK, iterate_fn, iterate_idx, 0, LINE_NUMBER(node));
 	ircode_add(code, LOAD, iterate_fn, $expr, iterate_fn, LINE_NUMBER(node));
-    ircode_register_set_skip_clear(code, iterate_fn);
 
-    uint32_t next_fn = ircode_register_push_temp(code);            // ++TEMP => 4
+    uint32_t next_fn = ircode_register_push_temp_protected(code);       // ++TEMP => 4
 	ircode_add(code, LOADK, next_fn, next_idx, 0, LINE_NUMBER(node));
 	ircode_add(code, LOAD, next_fn, $expr, next_fn, LINE_NUMBER(node));
-    ircode_register_set_skip_clear(code, next_fn);
 
-    uint32_t temp1 = ircode_register_push_temp(code);            // ++TEMP => 5
+    uint32_t temp1 = ircode_register_push_temp(code);                   // ++TEMP => 5
 	ircode_add(code, MOVE, temp1, iterate_fn, 0, LINE_NUMBER(node));
-    uint32_t temp2 = ircode_register_push_temp(code);            // ++TEMP => 6
+    uint32_t temp2 = ircode_register_push_temp(code);                   // ++TEMP => 6
 	ircode_add(code, MOVE, temp2, $expr, 0, LINE_NUMBER(node));
-    temp2 = ircode_register_push_temp(code);                    // ++TEMP => 7
-	ircode_add(code, LOADK, temp2, CPOOL_VALUE_NULL, 0, LINE_NUMBER(node));
+    uint32_t temp3 = ircode_register_push_temp(code);                   // ++TEMP => 7
+	ircode_add(code, LOADK, temp3, CPOOL_VALUE_NULL, 0, LINE_NUMBER(node));
 	ircode_add(code, CALL, $value, temp1, 2, LINE_NUMBER(node));
-    uint32_t temp = ircode_register_pop(code);                    // --TEMP => 6
+    uint32_t temp = ircode_register_pop(code);                          // --TEMP => 6
     DEBUG_ASSERT(temp != REGISTER_ERROR, "Unexpected register error.");
-    temp = ircode_register_pop(code);                            // --TEMP => 5
+    temp = ircode_register_pop(code);                                   // --TEMP => 5
     DEBUG_ASSERT(temp != REGISTER_ERROR, "Unexpected register error.");
-    temp = ircode_register_pop(code);                            // --TEMP => 4
+    temp = ircode_register_pop(code);                                   // --TEMP => 4
     DEBUG_ASSERT(temp != REGISTER_ERROR, "Unexpected register error.");
 
     // while code
@@ -594,39 +590,44 @@ static void visit_loop_for_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
 
     // cond = $expr.next($value);
     // cond is a local variable
-    temp1 = ircode_register_push_temp(code);                    // ++TEMP => 5
+    temp1 = ircode_register_push_temp_protected(code);                  // ++TEMP => 5
 	ircode_add(code, MOVE, temp1, next_fn, 0, LINE_NUMBER(node));
-    temp2 = ircode_register_push_temp(code);                    // ++TEMP => 6
+    temp2 = ircode_register_push_temp_protected(code);                  // ++TEMP => 6
 	ircode_add(code, MOVE, temp2, $expr, 0, LINE_NUMBER(node));
-    temp2 = ircode_register_push_temp(code);                    // ++TEMP => 7
-	ircode_add(code, MOVE, temp2, $value, 0, LINE_NUMBER(node));
+    temp3 = ircode_register_push_temp_protected(code);                  // ++TEMP => 7
+	ircode_add(code, MOVE, temp3, $value, 0, LINE_NUMBER(node));
 	ircode_add(code, CALL, cond_idx, temp1, 2, LINE_NUMBER(node));
-
+    
     // process statement
     visit(node->stmt);
+    
+    // temp registers can now be released
+    ircode_register_temp_unprotect(code, temp3);
+    ircode_register_temp_unprotect(code, temp2);
+    ircode_register_temp_unprotect(code, temp1);
 
     // pop next_fn temp register AFTER user code because function ptr must be protected inside loop
-    temp = ircode_register_pop(code);                            // --TEMP => 6
+    temp = ircode_register_pop(code);                                   // --TEMP => 6
     DEBUG_ASSERT(temp != REGISTER_ERROR, "Unexpected register error.");
-    temp = ircode_register_pop(code);                            // --TEMP => 5
+    temp = ircode_register_pop(code);                                   // --TEMP => 5
     DEBUG_ASSERT(temp != REGISTER_ERROR, "Unexpected register error.");
-    temp = ircode_register_pop(code);                            // --TEMP => 4
+    temp = ircode_register_pop(code);                                   // --TEMP => 4
     DEBUG_ASSERT(temp != REGISTER_ERROR, "Unexpected register error.");
 
     // update $value for the next check
     // $value = $expr.iterate($value);
-    temp1 = ircode_register_push_temp(code);                    // ++TEMP => 5
+    temp1 = ircode_register_push_temp(code);                            // ++TEMP => 5
 	ircode_add(code, MOVE, temp1, iterate_fn, 0, LINE_NUMBER(node));
-    temp2 = ircode_register_push_temp(code);                    // ++TEMP => 6
+    temp2 = ircode_register_push_temp(code);                            // ++TEMP => 6
 	ircode_add(code, MOVE, temp2, $expr, 0, LINE_NUMBER(node));
-    temp2 = ircode_register_push_temp(code);                    // ++TEMP => 7
+    temp2 = ircode_register_push_temp(code);                            // ++TEMP => 7
 	ircode_add(code, MOVE, temp2, $value, 0, LINE_NUMBER(node));
 	ircode_add(code, CALL, $value, temp1, 2, LINE_NUMBER(node));
-    temp = ircode_register_pop(code);                            // --TEMP => 6
+    temp = ircode_register_pop(code);                                   // --TEMP => 6
     DEBUG_ASSERT(temp != REGISTER_ERROR, "Unexpected register error.");
-    temp = ircode_register_pop(code);                            // --TEMP => 5
+    temp = ircode_register_pop(code);                                   // --TEMP => 5
     DEBUG_ASSERT(temp != REGISTER_ERROR, "Unexpected register error.");
-    temp = ircode_register_pop(code);                            // --TEMP => 4
+    temp = ircode_register_pop(code);                                   // --TEMP => 4
     DEBUG_ASSERT(temp != REGISTER_ERROR, "Unexpected register error.");
 
 	ircode_add(code, JUMP, labelTrue, 0, 0, LINE_NUMBER(node));
@@ -636,19 +637,20 @@ static void visit_loop_for_stmt (gvisitor_t *self, gnode_loop_stmt_t *node) {
     ircode_unsetlabel_true(code);
     ircode_unsetlabel_false(code);
 
-    temp = ircode_register_pop(code);                            // --TEMP => 3
+    temp = ircode_register_pop(code);                                   // --TEMP => 3
     DEBUG_ASSERT(temp != REGISTER_ERROR, "Unexpected register error.");
-    temp = ircode_register_pop(code);                            // --TEMP => 2
+    temp = ircode_register_pop(code);                                   // --TEMP => 2
     DEBUG_ASSERT(temp != REGISTER_ERROR, "Unexpected register error.");
-    temp = ircode_register_pop(code);                            // --TEMP => 1
+    temp = ircode_register_pop(code);                                   // --TEMP => 1
     DEBUG_ASSERT(temp != REGISTER_ERROR, "Unexpected register error.");
-    temp = ircode_register_pop(code);                            // --TEMP => 0
+    temp = ircode_register_pop(code);                                   // --TEMP => 0
     DEBUG_ASSERT(temp != REGISTER_ERROR, "Unexpected register error.");
 
-    ircode_register_unset_skip_clear(code, $expr);
-    ircode_register_unset_skip_clear(code, $value);
-    ircode_register_unset_skip_clear(code, iterate_fn);
-    ircode_register_unset_skip_clear(code, next_fn);
+    // mark main for registers as reusable
+    ircode_register_temp_unprotect(code, $expr);
+    ircode_register_temp_unprotect(code, $value);
+    ircode_register_temp_unprotect(code, iterate_fn);
+    ircode_register_temp_unprotect(code, next_fn);
 
     if (node->nclose != UINT32_MAX) {
 		ircode_add(code, CLOSE, node->nclose, 0, 0, LINE_NUMBER(node));
@@ -964,7 +966,7 @@ static void visit_function_decl (gvisitor_t *self, gnode_function_decl_t *node)
             // process node
             visit(val);
             // reset temp registers after each node
-            ircode_register_clear_temps(code);
+            ircode_register_temps_clear(code);
         });
     }
 

+ 16 - 5
src/compiler/gravity_ircode.c

@@ -495,12 +495,18 @@ uint32_t ircode_register_first_temp_available (ircode_t *code) {
     return 0;
 }
 
+uint32_t ircode_register_push_temp_protected (ircode_t *code) {
+    uint32_t value = ircode_register_push_temp(code);
+    ircode_register_temp_protect(code, value);
+    return value;
+}
+
 uint32_t ircode_register_push_temp (ircode_t *code) {
     uint32_t value = ircode_register_new(code);
     marray_push(uint32_t, code->registers, value);
     if (value > code->maxtemp) {code->maxtemp = value; ++code->ntemps;}
 
-    DEBUG_REGISTER("PUSH REGISTER %d", value);
+    DEBUG_REGISTER("PUSH TEMP REGISTER %d", value);
     return value;
 }
 
@@ -544,17 +550,22 @@ uint32_t ircode_register_count (ircode_t *code) {
 
 // MARK: -
 
-void ircode_register_set_skip_clear (ircode_t *code, uint32_t nreg) {
+void ircode_register_temp_protect (ircode_t *code, uint32_t nreg) {
     code->skipclear[nreg] = true;
+    DEBUG_REGISTER("SET SKIP REGISTER %d", nreg);
 }
 
-void ircode_register_unset_skip_clear (ircode_t *code, uint32_t nreg) {
+void ircode_register_temp_unprotect (ircode_t *code, uint32_t nreg) {
     code->skipclear[nreg] = false;
+    DEBUG_REGISTER("UNSET SKIP REGISTER %d", nreg);
 }
 
-void ircode_register_clear_temps (ircode_t *code) {
+void ircode_register_temps_clear (ircode_t *code) {
     // clear all temporary registers (if not protected)
     for (uint32_t i=code->nlocals; i<=code->maxtemp; ++i) {
-        if (!code->skipclear[i]) code->state[i] = false;
+        if (!code->skipclear[i]) {
+            code->state[i] = false;
+            DEBUG_REGISTER("CLEAR TEMP REGISTER %d", i);
+        }
     }
 }

+ 4 - 3
src/compiler/gravity_ircode.h

@@ -101,6 +101,7 @@ void        ircode_set_index (uint32_t index, ircode_t *code, opcode_t op, uint3
 
 bool        ircode_register_istemp (ircode_t *code, uint32_t n);
 uint32_t    ircode_register_push_temp (ircode_t *code);
+uint32_t    ircode_register_push_temp_protected (ircode_t *code);
 uint32_t    ircode_register_push (ircode_t *code, uint32_t nreg);
 uint32_t    ircode_register_pop (ircode_t *code);
 uint32_t    ircode_register_first_temp_available (ircode_t *code);
@@ -113,8 +114,8 @@ void        ircode_register_clear (ircode_t *code, uint32_t nreg);
 void        ircode_register_set (ircode_t *code, uint32_t nreg);
 void        ircode_register_dump (ircode_t *code);
 
-void        ircode_register_set_skip_clear (ircode_t *code, uint32_t nreg);
-void        ircode_register_unset_skip_clear (ircode_t *code, uint32_t nreg);
-void        ircode_register_clear_temps (ircode_t *code);
+void        ircode_register_temp_protect (ircode_t *code, uint32_t nreg);
+void        ircode_register_temp_unprotect (ircode_t *code, uint32_t nreg);
+void        ircode_register_temps_clear (ircode_t *code);
 
 #endif

+ 1 - 1
src/runtime/gravity_core.c

@@ -1413,7 +1413,7 @@ static bool range_loop (gravity_vm *vm, gravity_value_t *args, uint16_t nargs, u
     if (!VALUE_ISA_CLOSURE(GET_VALUE(1))) RETURN_ERROR("Argument must be a Closure.");
 
     gravity_closure_t *closure = VALUE_AS_CLOSURE(GET_VALUE(1));    // closure to execute
-    gravity_value_t value = GET_VALUE(0);                            // self parameter
+    gravity_value_t value = GET_VALUE(0);                           // self parameter
     gravity_range_t *range = VALUE_AS_RANGE(value);
     bool is_forward = range->from < range->to;
 

+ 2 - 2
src/shared/gravity_value.h

@@ -66,8 +66,8 @@
 extern "C" {
 #endif
 
-#define GRAVITY_VERSION						"0.6.4"     // git tag 0.6.4
-#define GRAVITY_VERSION_NUMBER				0x000604    // git push --tags
+#define GRAVITY_VERSION						"0.6.5"     // git tag 0.6.5
+#define GRAVITY_VERSION_NUMBER				0x000605    // git push --tags
 #define GRAVITY_BUILD_DATE                  __DATE__
 
 #ifndef GRAVITY_ENABLE_DOUBLE

+ 21 - 0
test/unittest/nested_for.gravity

@@ -0,0 +1,21 @@
+#unittest {
+    name: "Nested for loops.";
+    result: 20131375;
+};
+
+func main() {
+    var sum = 0;
+    for (var a in 0...10) {
+        for (var b in 10...20) {
+            for (var c in 20...30) {
+                for (var d in 30...40) {
+                    for (var e in 40...50) {
+                        var n = a + b + c + d + e;
+                        sum += n;
+                    }
+                }
+            }
+        }
+    }
+    return sum;
+}