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>
extern char ** glob_argv;
/* Stack frames.
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 {
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) */
};
int REG_size; /* number of local variables */
val_t REG[1]; /* local variables (flexible array) */
};
void prepare_signals(void);
extern classtable_t TAG2VFT[4];
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;};
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] = "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}]"
else
strs = once new HashMap[Int, String]
if not strs.has_key(i) then strs[i] = "fra.me.REG[{i}]"
else
assert closure
var ind = register_escape_label(seq)
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
add_instr("goto {lab(return_label.as(not null))};")
end
end
cparams.add("val_t p{i}")
end
if closure_decls != null then
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
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(", ")});")
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)
end
end
if after_params != null then cparams.add(after_params)
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.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
v.add_instr("fra.me.REG_size = {std_slots_nb};")
# Declare/initialize local variables
end
var iclosdecls = closure_decls
if iclosdecls != null then
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
end
var k = 0
for r in params do
v.closures[iclosdecl] = i
var cs = iclosdecl.closure.signature # Closure signature
var subparams = new Array[String] # Parameters of the closure
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)
for j in [0..cs.arity[ do
var p = "val_t"
subparams.add(p)
# Compile closure definitions
var old_el = v.escaped_labels
var closdefs = closure_defs
# 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
+ # 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]
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)
for cd in closdefs do
if cd != null then
var cn = cd.compile_closure(v)
args.add(cn)
else
args.add("NULL")
end
end
end
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
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?
var els = v.escaped_labels
v.escaped_labels = old_el
# Is there possible escapes?
r = null
v.add_instr(s + ";")
end
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
# 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;")
v.indent
# No escape occured, continue as usual
v.add_instr("case 0: break;")
var seq = iels.key
if lls.has(seq) then
# Local escape occured
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
else
# Forward escape occured: register the escape label
assert v.closure
- # Forward escape occured, just pass to the next one
+ # If forward escape occured, just pass to the next one
- 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("\}")
end
v.unindent
v.add_instr("\}")
do
v.add_location(location)
var ivar: String
do
v.add_location(location)
var ivar: String
+ var args: Array[String]
- 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]}]"
else
ivar = "CREG[{v.closures[closure_decl]}]"
+ args = ["closctx_param"]
args.append(v.registers(exprs))
args.append(v.registers(exprs))
- var s = "(({v.clostypes[closure_decl]})({ivar}->fun))({args.join(", ")})"
+ var s = "(({v.clostypes[closure_decl]})({ivar}))({args.join(", ")})"
- 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
v.indent
var bs = break_seq
if bs != null then
do
var ivar: String
if v.closure then
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
else
ivar = "CREG[{v.closures[closure_decl]}]"
end
+ # 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
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
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 lls_old = v.local_labels
v.local_labels = new HashSet[ISeq]
+ # We are now in a new C context
var ctx_old = cv.ctx
cv.ctx = new CContext
cv.out_contexts.add(cv.ctx)
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 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 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
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("}")
ctx_old2.append(cv.ctx)
cv.ctx = ctx_old2
v.unindent
v.add_instr("}")
+ # Restore things
+ cv.ctx = ctx_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
-
+ return "((fun_t){cname})"