Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More stack overflow fixes #7678

Merged
merged 22 commits into from
Nov 6, 2019

Conversation

TIHan
Copy link
Contributor

@TIHan TIHan commented Oct 2, 2019

To resolve this: #7673

What I'm curious about is the fact that the generated equality/comparison methods on the newly added tests are not broken apart into multiple methods. Effectively, these methods become very very large which probably isn't a good thing.

The initial fix addresses part of the issue with linear pattern matching combined with let bindings, but it still isn't enough. If these methods were broken apart, then the fix wouldn't need to be in IlxGen. But, this fix also breaks other stuff, so it isn't complete unfortunately.

Instead of trying to do this in IlxGen, we should break apart methods, which that is dictated by the optimizer I believe.

In regards to this specific issue, #7673, it can technically be resolved if we took out the stack guard. The stack guard will throw an exception earlier than an actual stack overflow, so the record type just happened to be in the sweet spot where the guard would throw but an overflow still wouldn't occur.

@TIHan TIHan force-pushed the bug/linear-match-overflow-fix branch from 83bc428 to 0a6764a Compare October 28, 2019 19:30
@TIHan TIHan changed the title [WIP] More stack overflow fixes More stack overflow fixes Oct 31, 2019
@TIHan
Copy link
Contributor Author

TIHan commented Oct 31, 2019

I've taken the approach of linearizing Expr.Match as a whole in IlxGen by using a queue and continuations. This is quite different from how Expr.Match is handled in other parts of the compiler, so we'll see how it works out. This could technically increase allocations when compiling to an assembly, but it may not be as bad as we think.

If this approach fails, the fallback is to mitigate the original user issue is: split non-user defined methods even when optimizations are turned off. Though, we will still have an issue with large structs as their generated methods do not get split due to byref free vars.

// Since code is branching and joining, the cgbuf stack is maintained manually.
let targetQueue = GenDecisionTreeAndTargets cenv cgbuf stackAtTargets eenv tree targets repeatSP sequelOnBranches

let rec popTargetQueue () =
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should add a comment describing why we are doing this.

@TIHan
Copy link
Contributor Author

TIHan commented Oct 31, 2019

Ok, CI passed. Surprising, also great.

CountClosure()
for cloTypeDef in cloTypeDefs do
cgbuf.mgbuf.AddTypeDef(ilCloTypeRef, cloTypeDef, false, false, None)
GenMethodForLambda cenv cgbuf.mgbuf eenv (entryPointInfo, cloinfo, eenvinner, body, isLocalTypeFunc, m)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a refactoring. Will be useful if we want to delay generation of methods from lambdas.

@cartermp cartermp requested a review from dsyme October 31, 2019 17:53
@cartermp
Copy link
Contributor

@dsyme could you take a look at this?

Copy link
Contributor

@dsyme dsyme left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll need to look at this really, really carefully in daylight, but I've left a few initial comments.

let targetInfos = GenDecisionTreeAndTargetsInner cenv cgbuf None stackAtTargets eenv tree targets repeatSP (IntMap.empty()) sequel
GenPostponedDecisionTreeTargets cenv cgbuf stackAtTargets targetInfos sequel
and GenDecisionTreeAndTargets cenv cgbuf stackAtTargets eenv tree targets repeatSP sequel : (Queue<unit -> (IlxGenEnv * EmitSequencePointState * Expr * sequel) option>) =
let rec genDecisions targetInfos decisions (queue: Queue<_>) =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't use any inner recursive functions in IlxGen.fs. They are always suspect w.r.t. readability, and IlxGen.fs is written without their use because there are enough side effects going on that it's plainer and simpler to do without them. Take it to the module scope and call it GenDecisionTreeAndQueuedTargets or something

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine, I can do that.

|> Seq.filter (fun (KeyValue(_, (_, isTargetPostponed))) -> isTargetPostponed)
|> List.ofSeq

let rec genRemaining remaining (queue: Queue<_>) =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise here - let rec inside let rec inside let is just too much...

GenDecisionTreeAndTargetsInner cenv cgbuf (Some caseLabel) stackAtTargets eenv caseTree targets repeatSP targetInfos sequel)
targetInfos
(decisions, caseLabels, cases)
|||> List.fold2 (fun decisions caseLabel (TCase(_, caseTree)) -> decisions @ [(Some caseLabel, eenv, caseTree)])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This tail @ looks suspect (it is quadratic if decisions is long)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I can see that. Actually, I should have just used List.map here, and would append the 1 default decision right before this. I think that would be fine.

@@ -5150,7 +5196,7 @@ and GenLetRecFixup cenv cgbuf eenv (ilxCloSpec: IlxClosureSpec, e, ilField: ILFi

/// Generate letrec bindings
and GenLetRecBindings cenv (cgbuf: CodeGenBuffer) eenv (allBinds: Bindings, m) =
let eenv = SetIsInLoop true eenv
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you undo the renaming here to minimize the diff please?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, that's fair. I'll do the same to the other changes.

@@ -7535,7 +7581,7 @@ let GetEmptyIlxGenEnv (ilg: ILGlobals) ccu =
innerVals = []
sigToImplRemapInfo = [] (* "module remap info" *)
withinSEH = false
isInLoop = false }
inLoop = false }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Likewise undo this renaming to minimize the diff

test997: string
test998: string
test999: string
test1000: string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

big :)

@dsyme
Copy link
Contributor

dsyme commented Oct 31, 2019

@cartermp @TIHan remind me if I neglect to look at this tomorrow....

@TIHan
Copy link
Contributor Author

TIHan commented Nov 1, 2019

Thank you @dsyme .

I made more adjustments.

I also realized that I didn't need to use a queue... I could just keep using CPS on the decision tree functions.

@TIHan TIHan force-pushed the bug/linear-match-overflow-fix branch from 31ba8b1 to 5b9126e Compare November 3, 2019 10:53
Copy link
Contributor

@dsyme dsyme left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work, much cleaner without the explicit stack!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants