未验证 提交 47cc8d57 编写于 作者: K kerams 提交者: GitHub

Optimize pattern compilation (#15849)

* Optimize pattern compilation
上级 81c71553
......@@ -23,6 +23,7 @@ open FSharp.Compiler.TypedTreeBasics
open FSharp.Compiler.TypedTreeOps
open FSharp.Compiler.TypedTreeOps.DebugPrint
open FSharp.Compiler.TypeRelations
open type System.MemoryExtensions
exception MatchIncomplete of bool * (string * bool) option * range
exception RuleNeverMatched of range
......@@ -905,43 +906,38 @@ let rec layoutPat pat =
let mkFrontiers investigations clauseNumber =
investigations |> List.map (fun (actives, valMap) -> Frontier(clauseNumber, actives, valMap))
let singleFalseInvestigationPoint = [| false |]
// Search for pattern decision points that are decided "one at a time" - i.e. where there is no
// multi-way switching. For example partial active patterns
let rec investigationPoints inpPat =
seq {
match inpPat with
| TPat_query ((_, _, _, _, _, apinfo), subPat, _) ->
yield not apinfo.IsTotal
yield! investigationPoints subPat
| TPat_isinst (_, _tgtTy, subPatOpt, _) ->
yield false
match subPatOpt with
| None -> ()
| Some subPat ->
yield! investigationPoints subPat
| TPat_as (subPat, _, _) ->
yield! investigationPoints subPat
| TPat_disjs (subPats, _)
| TPat_conjs(subPats, _)
| TPat_tuple (_, subPats, _, _)
| TPat_recd (_, _, subPats, _) ->
for subPat in subPats do
yield! investigationPoints subPat
| TPat_exnconstr(_, subPats, _) ->
for subPat in subPats do
yield! investigationPoints subPat
| TPat_array (subPats, _, _)
| TPat_unioncase (_, _, subPats, _) ->
yield false
for subPat in subPats do
yield! investigationPoints subPat
| TPat_range _
| TPat_null _
| TPat_const _ ->
yield false
| TPat_wild _
| TPat_error _ -> ()
}
match inpPat with
| TPat_query((_, _, _, _, _, apinfo), subPat, _) ->
Array.prepend (not apinfo.IsTotal) (investigationPoints subPat)
| TPat_isinst(_, _tgtTy, subPatOpt, _) ->
match subPatOpt with
| None -> singleFalseInvestigationPoint
| Some subPat -> Array.prepend false (investigationPoints subPat)
| TPat_as(subPat, _, _) -> investigationPoints subPat
| TPat_disjs(subPats, _)
| TPat_conjs(subPats, _)
| TPat_tuple(_, subPats, _, _)
| TPat_exnconstr(_, subPats, _)
| TPat_recd(_, _, subPats, _) ->
subPats
|> Seq.collect investigationPoints
|> Seq.toArray
| TPat_array (subPats, _, _)
| TPat_unioncase (_, _, subPats, _) ->
subPats
|> Seq.collect investigationPoints
|> Seq.toArray
|> Array.prepend false
| TPat_range _
| TPat_null _
| TPat_const _ -> singleFalseInvestigationPoint
| TPat_wild _
| TPat_error _ -> [||]
let rec erasePartialPatterns inpPat =
match inpPat with
......@@ -1740,16 +1736,13 @@ let CompilePatternBasic
// So disjunction alone isn't considered problematic, but in combination with 'when' patterns
let isProblematicClause (clause: MatchClause) =
let ips =
seq {
yield! investigationPoints clause.Pattern
if clause.GuardExpr.IsSome then
yield true
} |> Seq.toArray
let ips = if isPatternDisjunctive clause.Pattern then Array.append ips ips else ips
// Look for multiple decision points.
// We don't mind about the last logical decision point
ips.Length > 0 && Array.exists id ips[0..ips.Length-2]
if clause.GuardExpr.IsSome then
isPatternDisjunctive clause.Pattern || Array.exists id (investigationPoints clause.Pattern)
else
// Look for multiple decision points.
// We don't mind about the last logical decision point
let ips = investigationPoints clause.Pattern
ips.Length > 0 && Span.exists id (ips.AsSpan (0, ips.Length - 1))
let rec CompilePattern g denv amap tcVal infoReader mExpr mMatch warnOnUnused actionOnFailure (origInputVal, origInputValTypars, origInputExprOpt) (clausesL: MatchClause list) inputTy resultTy =
match clausesL with
......
......@@ -335,6 +335,12 @@ module Array =
let endsWith (suffix: _[]) (whole: _[]) =
isSubArray suffix whole (whole.Length - suffix.Length)
let prepend item (array: 'T[]) =
let res = Array.zeroCreate (array.Length + 1)
res[0] <- item
Array.blit array 0 res 1 array.Length
res
module Option =
let mapFold f s opt =
......@@ -622,6 +628,17 @@ module ResizeArray =
// in order to prevent long-term storage of those values
chunkBySize maxArrayItemCount f inp
module Span =
let inline exists ([<InlineIfLambda>] predicate: 'T -> bool) (span: Span<'T>) =
let mutable state = false
let mutable i = 0
while not state && i < span.Length do
state <- predicate span[i]
i <- i + 1
state
module ValueOptionInternal =
let inline ofOption x =
......
......@@ -126,6 +126,8 @@ module internal Array =
/// Returns true if one array has trailing elements equal to another's.
val endsWith: suffix: 'a[] -> whole: 'a[] -> bool when 'a: equality
val prepend: item: 'T -> array: 'T[] -> 'T[]
module internal Option =
val mapFold: f: ('a -> 'b -> 'c * 'a) -> s: 'a -> opt: 'b option -> 'c option * 'a
......@@ -225,6 +227,9 @@ module internal ResizeArray =
/// probability of smaller collections. Stop-the-world is still possible, just less likely.
val mapToSmallArrayChunks: f: ('t -> 'a) -> inp: ResizeArray<'t> -> 'a[][]
module internal Span =
val inline exists: predicate: ('T -> bool) -> span: Span<'T> -> bool
module internal ValueOptionInternal =
val inline ofOption: x: 'a option -> 'a voption
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册