Skip to content

Commit

Permalink
fix: Resolve transitive references correctly in ClosureConversion
Browse files Browse the repository at this point in the history
See `let_fun_closure.fs` for the reason.
We did need the *closure* of the reference graph.

This solution maybe adhoc: mutually recursion is a root cause of bugs.
We should think twice about it to handle in more sophisticated way.
It seems closure conversion should use the result of name resolution.
  • Loading branch information
vain0x committed Oct 4, 2019
1 parent c9dfb98 commit 675c691
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 17 deletions.
75 changes: 61 additions & 14 deletions boot/MiloneLang/ClosureConversion.fs
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,14 @@ let ccCtxAddRef varSerial (ctx: CcCtx) =

/// Called on leave function declaration to store the current known context.
let ccCtxLeaveFunDecl funSerial (ctx: CcCtx) =
ctx
|> ccCtxAddKnown funSerial
|> ccCtxWithFuns (ctx |> ccCtxGetFuns |> mapAdd funSerial (ctx |> ccCtxGetCurrent))
let ctx = ctx |> ccCtxAddKnown funSerial

// Update only first traversal.
let funs = ctx |> ccCtxGetFuns
if funs |> mapContainsKey funSerial then
ctx
else
ctx |> ccCtxWithFuns (funs |> mapAdd funSerial (ctx |> ccCtxGetCurrent))

/// Gets a list of captured variable serials for a function.
let ccCtxGetFunCapturedSerials funSerial (ctx: CcCtx) =
Expand All @@ -157,6 +162,51 @@ let ccCtxGetFunCaps funSerial (ctx: CcCtx): Caps =
|> setToList
|> chooseVars

/// Extends the list of references to include transitive references.
/// E.g. a function `f` uses `g` and `g` uses `h` (and `h` uses etc.),
/// we think `f` also uses `h`.
let ccCtxClosureRefs (ctx: CcCtx): CcCtx =
let emptySet = setEmpty (intHash, intCmp)

let rec closureRefs refs ccCtx (modified, visited, acc) =
match refs with
| [] ->
modified, visited, acc

| varSerial :: refs
when visited |> setContains varSerial ->
(modified, visited, acc) |> closureRefs refs ccCtx

| varSerial :: refs ->
let visited = visited |> setAdd varSerial
let modified = modified || (acc |> setContains varSerial |> not)
let acc = acc |> setAdd varSerial
let otherRefs = ccCtx |> ccCtxGetFunCapturedSerials varSerial |> setToList
(modified, visited, acc)
|> closureRefs otherRefs ccCtx
|> closureRefs refs ccCtx

let closureKnownCtx (modified, ccCtx) varSerial knownCtx =
let refs = knownCtx |> knownCtxGetRefs
match (false, emptySet, refs) |> closureRefs (refs |> setToList) ccCtx with
| true, _, refs ->
let knownCtx = knownCtx |> knownCtxWithRefs refs
true, ccCtx |> ccCtxWithFuns (ccCtx |> ccCtxGetFuns |> mapAdd varSerial knownCtx)

| false, _, _ ->
modified, ccCtx

let rec closureFuns (modified, ccCtx) =
if not modified then
ccCtx
else
ccCtx
|> ccCtxGetFuns
|> mapFold closureKnownCtx (false, ccCtx)
|> closureFuns

closureFuns (true, ctx)

// -----------------------------------------------
// Caps
// -----------------------------------------------
Expand Down Expand Up @@ -198,16 +248,10 @@ let capsUpdateFunDef funTy arity (caps: Caps) =
/// If a function `f` captures `x` and `y`,
/// each reference of `f` are replaced with an application `f x y`.
let declosureFunRef refVarSerial refTy refLoc ctx =
let ctx =
ctx
|> ccCtxGetFunCapturedSerials refVarSerial
|> setFold (fun ctx capVarSerial -> ctx |> ccCtxAddRef capVarSerial) ctx

let refExpr =
ctx
|> ccCtxGetFunCaps refVarSerial
|> capsMakeApp refVarSerial refTy refLoc

refExpr, ctx

let declosureFunDecl callee isMainFun args body next ty loc ctx =
Expand Down Expand Up @@ -339,13 +383,16 @@ let declosureUpdateFuns (ctx: CcCtx) =
let declosure (expr, tyCtx: TyCtx) =
let ccCtx = ccCtxFromTyCtx tyCtx

// Traverse for dependency collection.
// Transformed expression can be incorrect
// because captured variable list can be missing
// when to transform a function call before definition.
// Traverse for reference collection.
// NOTE: Converted expression is possibly incorrect
// because the set of captured variables is incomplete
// when to process a function reference before definition.
let _, ccCtx = (expr, ccCtx) |> declosureExpr

// Traverse again to transform function references/applications.
// Resolve transitive references.
let ccCtx = ccCtx |> ccCtxClosureRefs

// Traverse again to transform function references.
let expr, ccCtx = (expr, ccCtx) |> declosureExpr

let tyCtx = ccCtx |> declosureUpdateFuns |> ccCtxFeedbackToTyCtx tyCtx
Expand Down
65 changes: 62 additions & 3 deletions boot/tests/features/let_fun_closure/let_fun_closure.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,22 @@ int go_(int step_2, int n_);

int recursiveCase_(int arg_2);

int mutuallyRecursiveCase_(int a_, int arg_3);

int f1_(int a_, int arg_4);

int f2_(int a_, int arg_5);

int f3_(int a_, int arg_6);

int f4_(int a_, int arg_7);

int f5_(int a_, int arg_8);

int f6_(int a_, int arg_9);

int f7_(int a_, int arg_10);

int main();

int sub_(int step_, int x_) {
Expand Down Expand Up @@ -77,9 +93,52 @@ int recursiveCase_(int arg_2) {
return 0;
}

int mutuallyRecursiveCase_(int a_, int arg_3) {
int call_10 = f1_(a_, 0);
milone_assert((call_10 == 1), 21, 2);
int call_11 = 0;
return 0;
}

int f1_(int a_, int arg_4) {
int call_12 = f2_(a_, 0);
return call_12;
}

int f2_(int a_, int arg_5) {
int call_13 = f3_(a_, 0);
return call_13;
}

int f3_(int a_, int arg_6) {
int call_14 = f4_(a_, 0);
return call_14;
}

int f4_(int a_, int arg_7) {
int call_15 = f5_(a_, 0);
return call_15;
}

int f5_(int a_, int arg_8) {
int call_16 = f6_(a_, 0);
return call_16;
}

int f6_(int a_, int arg_9) {
int call_17 = f7_(a_, 0);
return call_17;
}

int f7_(int a_, int arg_10) {
return a_;
}

int main() {
int call_10 = basicCase_(0);
int call_11 = transitiveCase_(0);
int call_12 = recursiveCase_(0);
int a_ = 1;
int call_18 = basicCase_(0);
int call_19 = transitiveCase_(0);
int call_20 = recursiveCase_(0);
int call_21 = mutuallyRecursiveCase_(a_, 0);
return 0;
}

0 comments on commit 675c691

Please sign in to comment.