Skip to content

More stack overflow fixes#7678

Merged
KevinRansom merged 22 commits into
dotnet:masterfrom
TIHan:bug/linear-match-overflow-fix
Nov 6, 2019
Merged

More stack overflow fixes#7678
KevinRansom merged 22 commits into
dotnet:masterfrom
TIHan:bug/linear-match-overflow-fix

Conversation

@TIHan

@TIHan TIHan commented Oct 2, 2019

Copy link
Copy Markdown
Contributor

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

TIHan commented Oct 31, 2019

Copy link
Copy Markdown
Contributor Author

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.

Comment thread src/fsharp/IlxGen.fs Outdated
// 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
Copy Markdown
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

TIHan commented Oct 31, 2019

Copy link
Copy Markdown
Contributor Author

Ok, CI passed. Surprising, also great.

Comment thread src/fsharp/IlxGen.fs Outdated
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
Copy Markdown
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
Copy Markdown
Contributor

@dsyme could you take a look at this?

@dsyme dsyme left a comment

Copy link
Copy Markdown
Contributor

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.

Comment thread src/fsharp/IlxGen.fs Outdated
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
Copy Markdown
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
Copy Markdown
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.

Comment thread src/fsharp/IlxGen.fs Outdated
|> Seq.filter (fun (KeyValue(_, (_, isTargetPostponed))) -> isTargetPostponed)
|> List.ofSeq

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

Copy link
Copy Markdown
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...

Comment thread src/fsharp/IlxGen.fs Outdated
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
Copy Markdown
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
Copy Markdown
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.

Comment thread src/fsharp/IlxGen.fs

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

Copy link
Copy Markdown
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
Copy Markdown
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.

Comment thread src/fsharp/IlxGen.fs Outdated
sigToImplRemapInfo = [] (* "module remap info" *)
withinSEH = false
isInLoop = false }
inLoop = false }

Copy link
Copy Markdown
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
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

big :)

@dsyme

dsyme commented Oct 31, 2019

Copy link
Copy Markdown
Contributor

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

@TIHan

TIHan commented Nov 1, 2019

Copy link
Copy Markdown
Contributor Author

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

@dsyme dsyme left a comment

Copy link
Copy Markdown
Contributor

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