From: Stephen Toub Date: Mon, 15 Nov 2021 22:54:53 +0000 (-0500) Subject: Enable SymbolicRegexNode.IsNullableFor fast path to inline (#61605) X-Git-Tag: accepted/tizen/unified/riscv/20231226.055536~12248 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3b71d3b0aa3abd4696bf886e78f9b0993256be3b;p=platform%2Fupstream%2Fdotnet%2Fruntime.git Enable SymbolicRegexNode.IsNullableFor fast path to inline (#61605) --- diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Symbolic/SymbolicRegexNode.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Symbolic/SymbolicRegexNode.cs index 361f1a3..5ecadca 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Symbolic/SymbolicRegexNode.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/Symbolic/SymbolicRegexNode.cs @@ -173,101 +173,106 @@ namespace System.Text.RegularExpressions.Symbolic /// kind info for previous and next characters internal bool IsNullableFor(uint context) { - if (_nullabilityCache is null) + // if _nullabilityCache is null then IsNullable==CanBeNullable + // Observe that if IsNullable==true then CanBeNullable==true. + // but when the node does not start with an anchor + // and IsNullable==false then CanBeNullable==false. + + return _nullabilityCache is null ? + _info.IsNullable : + WithCache(context); + + // Separated out to enable the common case (no nullability cache) to be inlined + // and to avoid zero-init costs for generally unused state. + bool WithCache(uint context) { - // if _nullabilityCache is null then IsNullable==CanBeNullable - // Observe that if IsNullable==true then CanBeNullable==true. - // but when the node does not start with an anchor - // and IsNullable==false then CanBeNullable==false. - return _info.IsNullable; - } - - if (!StackHelper.TryEnsureSufficientExecutionStack()) - { - return StackHelper.CallOnEmptyStack(IsNullableFor, context); - } + if (!StackHelper.TryEnsureSufficientExecutionStack()) + { + return StackHelper.CallOnEmptyStack(IsNullableFor, context); + } - Debug.Assert(context < CharKind.ContextLimit); + Debug.Assert(context < CharKind.ContextLimit); - // If nullablity has been computed for the given context then return it - byte b = Volatile.Read(ref _nullabilityCache[context]); - if (b != UndefinedByte) - { - return b == TrueByte; - } + // If nullablity has been computed for the given context then return it + byte b = Volatile.Read(ref _nullabilityCache[context]); + if (b != UndefinedByte) + { + return b == TrueByte; + } - // Otherwise compute the nullability recursively for the given context - bool is_nullable; - switch (_kind) - { - case SymbolicRegexKind.Loop: - Debug.Assert(_left is not null); - is_nullable = _lower == 0 || _left.IsNullableFor(context); - break; + // Otherwise compute the nullability recursively for the given context + bool is_nullable; + switch (_kind) + { + case SymbolicRegexKind.Loop: + Debug.Assert(_left is not null); + is_nullable = _lower == 0 || _left.IsNullableFor(context); + break; - case SymbolicRegexKind.Concat: - Debug.Assert(_left is not null && _right is not null); - is_nullable = _left.IsNullableFor(context) && _right.IsNullableFor(context); - break; + case SymbolicRegexKind.Concat: + Debug.Assert(_left is not null && _right is not null); + is_nullable = _left.IsNullableFor(context) && _right.IsNullableFor(context); + break; - case SymbolicRegexKind.Or: - case SymbolicRegexKind.And: - Debug.Assert(_alts is not null); - is_nullable = _alts.IsNullableFor(context); - break; + case SymbolicRegexKind.Or: + case SymbolicRegexKind.And: + Debug.Assert(_alts is not null); + is_nullable = _alts.IsNullableFor(context); + break; - case SymbolicRegexKind.Not: - Debug.Assert(_left is not null); - is_nullable = !_left.IsNullableFor(context); - break; + case SymbolicRegexKind.Not: + Debug.Assert(_left is not null); + is_nullable = !_left.IsNullableFor(context); + break; - case SymbolicRegexKind.StartAnchor: - is_nullable = CharKind.Prev(context) == CharKind.StartStop; - break; + case SymbolicRegexKind.StartAnchor: + is_nullable = CharKind.Prev(context) == CharKind.StartStop; + break; - case SymbolicRegexKind.EndAnchor: - is_nullable = CharKind.Next(context) == CharKind.StartStop; - break; + case SymbolicRegexKind.EndAnchor: + is_nullable = CharKind.Next(context) == CharKind.StartStop; + break; - case SymbolicRegexKind.BOLAnchor: - // Beg-Of-Line anchor is nullable when the previous character is Newline or Start - // note: at least one of the bits must be 1, but both could also be 1 in case of very first newline - is_nullable = (CharKind.Prev(context) & CharKind.NewLineS) != 0; - break; + case SymbolicRegexKind.BOLAnchor: + // Beg-Of-Line anchor is nullable when the previous character is Newline or Start + // note: at least one of the bits must be 1, but both could also be 1 in case of very first newline + is_nullable = (CharKind.Prev(context) & CharKind.NewLineS) != 0; + break; - case SymbolicRegexKind.EOLAnchor: - // End-Of-Line anchor is nullable when the next character is Newline or Stop - // note: at least one of the bits must be 1, but both could also be 1 in case of \Z - is_nullable = (CharKind.Next(context) & CharKind.NewLineS) != 0; - break; + case SymbolicRegexKind.EOLAnchor: + // End-Of-Line anchor is nullable when the next character is Newline or Stop + // note: at least one of the bits must be 1, but both could also be 1 in case of \Z + is_nullable = (CharKind.Next(context) & CharKind.NewLineS) != 0; + break; - case SymbolicRegexKind.WBAnchor: - // test that prev char is word letter iff next is not not word letter - is_nullable = ((CharKind.Prev(context) & CharKind.WordLetter) ^ (CharKind.Next(context) & CharKind.WordLetter)) != 0; - break; + case SymbolicRegexKind.WBAnchor: + // test that prev char is word letter iff next is not not word letter + is_nullable = ((CharKind.Prev(context) & CharKind.WordLetter) ^ (CharKind.Next(context) & CharKind.WordLetter)) != 0; + break; - case SymbolicRegexKind.NWBAnchor: - // test that prev char is word letter iff next is word letter - is_nullable = ((CharKind.Prev(context) & CharKind.WordLetter) ^ (CharKind.Next(context) & CharKind.WordLetter)) == 0; - break; + case SymbolicRegexKind.NWBAnchor: + // test that prev char is word letter iff next is word letter + is_nullable = ((CharKind.Prev(context) & CharKind.WordLetter) ^ (CharKind.Next(context) & CharKind.WordLetter)) == 0; + break; - case SymbolicRegexKind.EndAnchorZ: - // \Z anchor is nullable when the next character is either the last Newline or Stop - // note: CharKind.NewLineS == CharKind.Newline|CharKind.StartStop - is_nullable = (CharKind.Next(context) & CharKind.StartStop) != 0; - break; + case SymbolicRegexKind.EndAnchorZ: + // \Z anchor is nullable when the next character is either the last Newline or Stop + // note: CharKind.NewLineS == CharKind.Newline|CharKind.StartStop + is_nullable = (CharKind.Next(context) & CharKind.StartStop) != 0; + break; - default: // SymbolicRegexKind.EndAnchorZRev: - // EndAnchorZRev (rev(\Z)) anchor is nullable when the prev character is either the first Newline or Start - // note: CharKind.NewLineS == CharKind.Newline|CharKind.StartStop - Debug.Assert(_kind == SymbolicRegexKind.EndAnchorZRev); - is_nullable = (CharKind.Prev(context) & CharKind.StartStop) != 0; - break; - } + default: // SymbolicRegexKind.EndAnchorZRev: + // EndAnchorZRev (rev(\Z)) anchor is nullable when the prev character is either the first Newline or Start + // note: CharKind.NewLineS == CharKind.Newline|CharKind.StartStop + Debug.Assert(_kind == SymbolicRegexKind.EndAnchorZRev); + is_nullable = (CharKind.Prev(context) & CharKind.StartStop) != 0; + break; + } - Volatile.Write(ref _nullabilityCache[context], is_nullable ? TrueByte : FalseByte); + Volatile.Write(ref _nullabilityCache[context], is_nullable ? TrueByte : FalseByte); - return is_nullable; + return is_nullable; + } } /// Returns true if this is equivalent to .* (the node must be eager also)