add more ir opt code, clearified what to opt, so current opt code may need rewrite

This commit is contained in:
Youmu Liu
2026-06-24 18:09:05 +08:00
parent 2b05d626c7
commit 45f9b04b6d
4 changed files with 121 additions and 54 deletions
+4 -1
View File
@@ -62,10 +62,13 @@ namespace StgSharp.RegularAnalysis.Text
} }
internal sealed class RegexFindSeqIR( internal sealed class RegexFindSeqIR(
string pattern string pattern,
bool isFirst
) : RegexIR ) : RegexIR
{ {
public bool IsFirst { get; } = isFirst;
public string Pattern { get; } = pattern; public string Pattern { get; } = pattern;
internal override RegexIRCommand Command => RegexIRCommand.Find_Seq; internal override RegexIRCommand Command => RegexIRCommand.Find_Seq;
@@ -28,6 +28,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -36,19 +37,22 @@ namespace StgSharp.RegularAnalysis.Text
public partial class RegexAnalyzer public partial class RegexAnalyzer
{ {
internal static RegexInfo ScanAndOptimizeIR( // this array is not thread safe so opt process must be done in single thread.
List<RegexIR> baseIR, private RegexIR[] _opt_buffer = new RegexIR[16];
out List<RegexIR> optIR
internal RegexInfo ScanAndOptimizeIR(
List<RegexIR> baseIR,
out List<RegexIR> optIR
) )
{ {
int index = 0; int index = 0;
Span<RegexIR> origin_span = CollectionsMarshal.AsSpan(baseIR);
List<RegexIR> optimizedIR = new(baseIR.Count); List<RegexIR> optimizedIR = new(baseIR.Count);
List<RegexIR> segment = []; List<RegexIR> segment = [];
RegexInfo info = new(); RegexInfo info = new();
do do
{ {
// TODO scan and opt here OptimizeIR(origin_span, ref index, segment, info);
OptimizeIR(baseIR, ref index, segment, info);
optimizedIR.AddRange(segment); optimizedIR.AddRange(segment);
index++; index++;
} while (index < baseIR.Count); } while (index < baseIR.Count);
@@ -56,37 +60,106 @@ namespace StgSharp.RegularAnalysis.Text
return info; return info;
} }
private static void OptimizeDot( private int ExpandCount(
RegexCountSetIR root, string pattern,
List<RegexIR> ir, int min,
ref int index, int max,
List<RegexIR> optimizedIR, bool is_greedy,
RegexInfo info in Span<RegexIR> span
)
{
// expand count operation to less cycles.
const int fast_seq_length = 32;
int fast_count = fast_seq_length / pattern.Length;
fast_count = int.Min(fast_count, min);
if (fast_count > 0)
{
string fast_pattern = string.Concat(Enumerable.Repeat(pattern, fast_count));
int fast_min = min / fast_count;
int consumed_count = fast_min * fast_count;
int remain_min = min - consumed_count;
int remain_max = max - consumed_count;
remain_max = int.Max(-1, remain_max); //for negative stands for infinitive
if (remain_max == 0)
{
// a{2}
RegexCountSeqIR fast = new(fast_min, fast_min, is_greedy, fast_pattern);
span[0] = fast;
return 1;
} else
{
// a{2,5}
RegexCountSeqIR fast = new(fast_min, fast_min, is_greedy, fast_pattern);
RegexCountSeqIR tail = new(remain_min, remain_max, is_greedy, pattern);
span[0] = fast;
span[1] = tail;
return 2;
}
} else if (fast_count == 0)
{
// least count is 0
span[0] = new RegexCountSeqIR(0, max, is_greedy, pattern);
return 1;
} else
{
// least count is infinitive
span[0] = new RegexCountSeqIR(-1, -1, is_greedy, pattern);
return 1;
}
}
private void OptimizeDot(
RegexCountSetIR root,
Span<RegexIR> ir,
ref int index,
List<RegexIR> optimizedIR,
RegexInfo info
) )
{ {
int next = index + 1; int next = index + 1;
if (next >= ir.Count) optimizedIR.Clear();
if (next >= ir.Length)
{ {
optimizedIR.Clear();
optimizedIR.Add(root); optimizedIR.Add(root);
return; return;
} }
RegexIR nextIR = ir[next]; RegexIR nextIR = ir[next];
if (nextIR.Command == RegexIRCommand.Match_Seq) if (nextIR is RegexMatchSeqIR match_seq)
{ {
// opt .*abc style // opt .*abc style
RegexFindSeqIR find = new((nextIR as RegexMatchSeqIR)!.Pattern); RegexFindSeqIR find = new(match_seq.Pattern);
optimizedIR.Add(find); optimizedIR.Add(find);
info.MinLength += find.Pattern.Length; info.MinLength += find.Pattern.Length;
return; return;
} else { } } else if (nextIR is RegexCountSeqIR count_seq)
{
// opt .* (abc)* or .*(abc){2,5} style
string pattern = count_seq.Sequence;
int min = count_seq.Min;
int max = count_seq.Max;
switch ((min, max))
{
case (0, _):
break;
default:
RegexFindSeqIR find = new(pattern);
// RegexCountSeqIR
break;
}
}
} }
private static void OptimizeIR( private void OptimizeIR(
List<RegexIR> ir, Span<RegexIR> ir,
ref int index, ref int index,
List<RegexIR> optimizedIR, List<RegexIR> optimizedIR,
RegexInfo info RegexInfo info
) )
{ {
optimizedIR.Clear(); optimizedIR.Clear();
@@ -120,6 +193,8 @@ namespace StgSharp.RegularAnalysis.Text
public int TryDepth { get; set; } public int TryDepth { get; set; }
public int GroupCount { get; set; }
} }
} }
+1 -1
View File
@@ -72,7 +72,7 @@ namespace StgSharp
} }
} }
[GeneratedRegex(@"a*.[abc]*(abcd|a(bc)c)+(?<name>abc)", RegexOptions.ExplicitCapture)] [GeneratedRegex(@".*a*b", RegexOptions.ExplicitCapture)]
internal static partial Regex GetRegex(); internal static partial Regex GetRegex();
} }
+20 -31
View File
@@ -33,43 +33,32 @@
1. 尽可能降低源生成或者运行时执行中,对多指令上下文的依赖,尽可能保证每一个IR可以单独输出最佳的代码 1. 尽可能降低源生成或者运行时执行中,对多指令上下文的依赖,尽可能保证每一个IR可以单独输出最佳的代码
2. 减少多余的结构,使得一行指令尽可能处理多的数据 2. 减少多余的结构,使得一行指令尽可能处理多的数据
### 语义改写优化 ### AST优化器
1. 计数器展开 1. ALT合并
建议在lower做 将多个ALT CASE合并到一个链表中
对于形如a+b或者a{2,4}b的正则表达式,可以优化为a*(ab|b)和a{2,3}(ab|b)的写法
2. 确定性计数展开 2. SPAN合并
建议在lower做 将连续的CHAR CONCAT合并为一个char span
对于形如a{m,n}的正则表达式,将其展开为a{m}a{0,n-m}的形式
3. charset合并 3. alt合并
建议在ast-opt做 将多个alt分支中的公共部分提取出来
对于形如[ abc ]|[ def ]的表达式,将其合并为[ abcdef ]
4. charset展开 ### IR优化器
在lower做
对于形如abc[ de ]的表达式,如果字符集容量不超过4,可以站卡为(abcd|abce)形式
### 匹配策略改变 1. .*a特征,优化为
FIND_SEQ(a, last)
.*?a特征,优化为
FIND_SEQ(a, first)
1. 长度预测 2. a+特征,优化为
在source-gen阶段做 MATCH_SEQ(a)
对于一个正则表达式,可以计算其所需的最小长度,可以在开始的时候提前进行比较 COUNT_SEQ(a,0,+inf,greedy)
2. .*优化 3. a{1} -> a
在lower阶段做
对于COUNT_SET min=0 max=-1 "."指令,也就是.*操作,将后续指令从match转为find
3. [ set ]*优化 4. .\*a\*特征,优化为.*
在source-gen阶段做
对于其他的COUNT_SET min=0 max=-1 "???"指令,使用IndexOf,IndexOfAnyExcept等进行优化
4. 首字母检查 5. a{m,n}特征,展开为
**这个不好做,因为IR直接把ALT打碎成if-else串了** MATCH_SEQ(aa...m个a....aa)
对于(abc|defg|xyz)表达式,可以检查其首字母 COUNT_SEQ(a,0,n,greedy)
### C# span opt trick
1. Span.StartWith
2. [ 0-9 ]展开为IndexOf("0123456789")