compile: one closure context by function, and it is the stack frame
authorJean Privat <jean@pryen.org>
Tue, 18 Aug 2009 18:45:52 +0000 (14:45 -0400)
committerJean Privat <jean@pryen.org>
Tue, 18 Aug 2009 23:09:38 +0000 (19:09 -0400)
Closure definition directly use the stack frame of the caller to access the
closure environment (local variables, closure parameters, escape marker).
Closure function pointers are passed as separate additional arguments.

Signed-off-by: Jean Privat <jean@pryen.org>

clib/nit_common.h
src/compiling/compiling_icode.nit

index 93a42c3..429e1a1 100644 (file)
@@ -123,12 +123,19 @@ extern int glob_argc;
 extern char ** glob_argv;
 
 /* Stack frames.
- * Are used to store local variables (REGS) of function and provide information for stack dump */
+ * Are used to:
+ * - store local variables (REGS) of functions
+ * - store context for closure
+ * - provide information for stack dump
+ */
 struct stack_frame_t {
        struct stack_frame_t *prev; /* previous stack frame */
        const char *file; /* source filename (.nit) */
        int line; /* line number (in the source) */
        const char *meth; /* human function name (usually the method name) */
+       struct stack_frame_t *closure_ctx; /* closure context (for functions that have closure parameters) */
+       fun_t *closure_funs; /* closure functions (for functions that have closure parameters) */
+       int has_broke; /* has an escape occured? 0 if false, label_idx (>0) if true */
        int REG_size; /* number of local variables */
        val_t REG[1]; /* local variables (flexible array) */
 };
@@ -146,8 +153,4 @@ void nit_exit(int);
 void prepare_signals(void);
 extern classtable_t TAG2VFT[4];
 
-/* This structure is used to store closure.
- * Specific closure use a specific fun parameter.
- */
-struct WBT_ {fun_t fun; val_t *has_broke; val_t broke_value; val_t *variable; struct WBT_ **closurevariable;};
 #endif
index 75ceb7c..7a374d3 100644 (file)
@@ -51,7 +51,7 @@ class I2CCompilerVisitor
                                        if not strs.has_key(i) then strs[i] = "REGB{i}"
                                else if closure and not e.is_local then
                                        strs = once new HashMap[Int, String]
-                                       if not strs.has_key(i) then strs[i] = "closctx->variable[{i}]"
+                                       if not strs.has_key(i) then strs[i] = "closctx->REG[{i}]"
                                else
                                        strs = once new HashMap[Int, String]
                                        if not strs.has_key(i) then strs[i] = "fra.me.REG[{i}]"
@@ -134,7 +134,7 @@ class I2CCompilerVisitor
                else
                        assert closure
                        var ind = register_escape_label(seq)
-                       add_instr("closctx->has_broke = (val_t*){ind};")
+                       add_instr("closctx->has_broke = {ind};")
                        add_instr("goto {lab(return_label.as(not null))};")
                end
        end
@@ -224,19 +224,20 @@ redef class IRoutine
                        cparams.add("val_t p{i}")
                end
                if closure_decls != null then
+                       cparams.add("struct stack_frame_t *closctx_param")
                        for i in [0..closure_decls.length[ do
                                var closcn = "CLOS_{cname}_{i}"
                                var cs = closure_decls[i].closure.signature
                                var subparams = new Array[String] # Parameters of the closure
-                               subparams.add("struct WBT_ *")
+                               subparams.add("struct stack_frame_t *")
                                for j in [0..cs.arity[ do
                                        subparams.add("val_t")
                                end
                                var rr = "void"
                                if cs.return_type != null then rr = "val_t"
                                v.add_decl("typedef {rr} (*{closcn})({subparams.join(", ")});")
-                               cargs.add("wd{i}")
-                               cparams.add("struct WBT_ *wd{i}")
+                               cargs.add("clos_fun{i}")
+                               cparams.add("fun_t clos_fun{i}")
                        end
                end
                if after_params != null then cparams.add(after_params)
@@ -274,6 +275,7 @@ redef class IRoutine
                v.add_instr("fra.me.file = LOCATE_{v.visitor.module.name};")
                v.add_instr("fra.me.line = {ll};")
                v.add_instr("fra.me.meth = LOCATE_{v.basecname};")
+               v.add_instr("fra.me.has_broke = 0;")
                v.add_instr("fra.me.REG_size = {std_slots_nb};")
 
                # Declare/initialize local variables
@@ -285,9 +287,9 @@ redef class IRoutine
                end
                var iclosdecls = closure_decls
                if iclosdecls != null then
-                       v.add_decl("struct WBT_ *CREG[{iclosdecls.length}];")
-               else
-                       v.add_decl("struct WBT_ **CREG = NULL;")
+                       v.add_decl("fun_t CREG[{iclosdecls.length}];")
+                       v.add_instr("fra.me.closure_ctx = closctx_param;")
+                       v.add_instr("fra.me.closure_funs = CREG;")
                end
                var k = 0
                for r in params do
@@ -301,7 +303,7 @@ redef class IRoutine
                                v.closures[iclosdecl] = i
                                var cs = iclosdecl.closure.signature # Closure signature
                                var subparams = new Array[String] # Parameters of the closure
-                               subparams.add("struct WBT_ *")
+                               subparams.add("struct stack_frame_t *")
                                for j in [0..cs.arity[ do
                                        var p = "val_t"
                                        subparams.add(p)
@@ -441,26 +443,38 @@ redef class IAbsCall
                # Compile closure definitions
                var old_el = v.escaped_labels
                var closdefs = closure_defs
-               var closcns: nullable Array[String] = null
+               var closctx: nullable String = null # The closure context of closdefs
                if closdefs != null then
+                       # Get the closure context
+                       if v.closure then
+                               closctx = "closctx"
+                       else
+                               closctx = "(&(fra.me))"
+                       end
+
+                       # First aditionnal arguments is the closure context
+                       args.add(closctx)
+
+                       # We are in a new escape boundary
                        v.escaped_labels = new HashMap[ISeq, Int]
-                       closcns = new Array[String]
+
+                       # Compile each closures and add each sub-function as an other additionnal parameter
                        for cd in closdefs do
                                if cd != null then
                                        var cn = cd.compile_closure(v)
                                        args.add(cn)
-                                       closcns.add(cn)
                                else
                                        args.add("NULL")
                                end
                        end
                end
 
+               # Compile the real call
                var s = compile_call_to_c(v, args)
                var r: nullable String = s
 
                # Intercept escapes
-               if closcns != null then
+               if closctx != null then
                        var els = v.escaped_labels
                        v.escaped_labels = old_el
                        # Is there possible escapes?
@@ -473,15 +487,8 @@ redef class IAbsCall
                                        r = null
                                        v.add_instr(s + ";")
                                end
-                               # What is the escape index (if any?)
-                               # It's computed as the union of has_broke
-                               var switcha = new Array[String]
-                               for cn in closcns do
-                                       switcha.add("((int)({cn}->has_broke))")
-                               end
-                               var switch = switcha.join(" | ")
                                # What are the expected escape indexes
-                               v.add_instr("switch ({switch}) \{")
+                               v.add_instr("switch ({closctx}->has_broke) \{")
                                v.indent
                                # No escape occured, continue as usual
                                v.add_instr("case 0: break;")
@@ -492,7 +499,8 @@ redef class IAbsCall
                                        var seq = iels.key
                                        if lls.has(seq) then
                                                # Local escape occured
-                                               v.add_instr("case {iels.item}: goto {v.lab(seq)};")
+                                               # Clear the has_broke information and go to the target
+                                               v.add_instr("case {iels.item}: {closctx}->has_broke = 0; goto {v.lab(seq)};")
                                        else
                                                # Forward escape occured: register the escape label
                                                assert v.closure
@@ -501,9 +509,11 @@ redef class IAbsCall
                                        end
                                        iels.next
                                end
-                               # Forward escape occured, just pass to the next one
+                               # If forward escape occured, just pass to the next one
                                if forward_escape then
-                                       v.add_instr("default: closctx->has_broke = (val_t*)({switch}); goto {v.lab(v.return_label.as(not null))};")
+                                       # Do not need to copy 'has_broke' value since it is shared by the next one.
+                                       # So just exit the C function.
+                                       v.add_instr("default: goto {v.lab(v.return_label.as(not null))};")
                                end
                                v.unindent
                                v.add_instr("\}")
@@ -715,18 +725,21 @@ redef class IClosCall
        do
                v.add_location(location)
                var ivar: String
+               var args: Array[String]
                if v.closure then
-                       ivar = "closctx->closurevariable[{v.closures[closure_decl]}]"
+                       ivar = "closctx->closure_funs[{v.closures[closure_decl]}]"
+                       args = ["closctx->closure_ctx"]
                else
                        ivar = "CREG[{v.closures[closure_decl]}]"
+                       args = ["closctx_param"]
                end
-               var args = [ivar]
                args.append(v.registers(exprs))
 
-               var s = "(({v.clostypes[closure_decl]})({ivar}->fun))({args.join(", ")})"
+               var s = "(({v.clostypes[closure_decl]})({ivar}))({args.join(", ")})"
                store_result(v, s)
 
-               v.add_instr("if ({ivar}->has_broke) \{")
+               # Intercept escape
+               v.add_instr("if ({args.first}->has_broke) \{")
                v.indent
                var bs = break_seq
                if bs != null then
@@ -745,7 +758,7 @@ redef class IHasClos
        do
                var ivar: String
                if v.closure then
-                       ivar = "closctx->closurevariable[{v.closures[closure_decl]}]"
+                       ivar = "closctx->closure_funs[{v.closures[closure_decl]}]"
                else
                        ivar = "CREG[{v.closures[closure_decl]}]"
                end
@@ -753,54 +766,46 @@ redef class IHasClos
        end
 end
 
-
 redef class IClosureDef
+       # Compile the closure as a separate C function in the visitor out_contexts.
+       # Return a fun_t pointer to the function.
        fun compile_closure(v: I2CCompilerVisitor): String
        do
+               var cv = v.visitor
+
+               # We are now in a closure
                var cfc_old = v.closure
                v.closure = true
+
+               # We are now in a escape boundary
                var lls_old = v.local_labels
                v.local_labels = new HashSet[ISeq]
 
-               var cv = v.visitor
+               # We are now in a new C context
                var ctx_old = cv.ctx
                cv.ctx = new CContext
                cv.out_contexts.add(cv.ctx)
 
+               # Generate the C function
                var cname = "OC_{v.basecname}_{v.new_number}"
-               var args = compile_signature_to_c(v.visitor, cname, null, "struct WBT_ *closctx", null)
+               var args = compile_signature_to_c(v.visitor, cname, null, "struct stack_frame_t *closctx", null)
                var ctx_old2 = cv.ctx
                cv.ctx = new CContext
-
                var s = compile_inside_to_c(v, args)
                if s == null then
                        v.add_instr("return;")
                else
                        v.add_instr("return {s};")
                end
-
                ctx_old2.append(cv.ctx)
                cv.ctx = ctx_old2
                v.unindent
                v.add_instr("}")
-               cv.ctx = ctx_old
 
+               # Restore things
+               cv.ctx = ctx_old
                v.closure = cfc_old
-
-               # Build closure
-               var closcnv = "wbclos{v.new_number}"
-               v.add_decl("struct WBT_ {closcnv};")
-               v.add_instr("{closcnv}.fun = (fun_t){cname};")
-               v.add_instr("{closcnv}.has_broke = NULL;")
-               if cfc_old then
-                       v.add_instr("{closcnv}.variable = closctx->variable;")
-                       v.add_instr("{closcnv}.closurevariable = closctx->closurevariable;")
-               else
-                       v.add_instr("{closcnv}.variable = fra.me.REG;")
-                       v.add_instr("{closcnv}.closurevariable = CREG;")
-               end
-
                v.local_labels = lls_old
-               return "(&{closcnv})"
+               return "((fun_t){cname})"
        end
 end