diff --git a/scripts/verify-baseline-static/allowlist-aarch64.txt b/scripts/verify-baseline-static/allowlist-aarch64.txt index 0230802e39a..bef8ff3e965 100644 --- a/scripts/verify-baseline-static/allowlist-aarch64.txt +++ b/scripts/verify-baseline-static/allowlist-aarch64.txt @@ -5,7 +5,7 @@ # ---------------------------------------------------------------------------- # Bun's Highway SVE/SVE2 targets. Gate: hwy::SupportedTargets via getauxval(AT_HWCAP). -# (108 symbols) +# (116 symbols) # ---------------------------------------------------------------------------- _ZN3bun10N_SVE2_12810MemMemImplEPKhmS2_m [SVE] _ZN3bun10N_SVE2_12814DecodeHex8ImplEPKhPhm [SVE] @@ -19,8 +19,10 @@ _ZN3bun10N_SVE2_12819CopyAsciiPrefixImplEPKhmPh [S _ZN3bun10N_SVE2_12819FirstNonAscii16ImplEPKtm [SVE] _ZN3bun10N_SVE2_12820FillWithSkipMaskImplEPKhmPhS2_mb [SVE] _ZN3bun10N_SVE2_12821VisibleUTF16WidthImplEPKtmPm [SVE] +_ZN3bun10N_SVE2_12822IndexOfEscapeChar8ImplEPKhm [SVE] _ZN3bun10N_SVE2_12822VisibleLatin1WidthImplEPKhm [SVE] _ZN3bun10N_SVE2_12823HtmlEscapeExtraLen8ImplEPKhm [SVE] +_ZN3bun10N_SVE2_12823IndexOfEscapeChar16ImplEPKtm [SVE] _ZN3bun10N_SVE2_12824HtmlEscapeExtraLen16ImplEPKtm [SVE] _ZN3bun10N_SVE2_12825CountPrintableAscii16ImplEPKtm [SVE] _ZN3bun10N_SVE2_12826IndexOfHTMLEscapeChar8ImplEPKhm [SVE] @@ -46,8 +48,10 @@ _ZN3bun5N_SVE19CopyAsciiPrefixImplEPKhmPh [S _ZN3bun5N_SVE19FirstNonAscii16ImplEPKtm [SVE] _ZN3bun5N_SVE20FillWithSkipMaskImplEPKhmPhS2_mb [SVE] _ZN3bun5N_SVE21VisibleUTF16WidthImplEPKtmPm [SVE] +_ZN3bun5N_SVE22IndexOfEscapeChar8ImplEPKhm [SVE] _ZN3bun5N_SVE22VisibleLatin1WidthImplEPKhm [SVE] _ZN3bun5N_SVE23HtmlEscapeExtraLen8ImplEPKhm [SVE] +_ZN3bun5N_SVE23IndexOfEscapeChar16ImplEPKtm [SVE] _ZN3bun5N_SVE24HtmlEscapeExtraLen16ImplEPKtm [SVE] _ZN3bun5N_SVE25CountPrintableAscii16ImplEPKtm [SVE] _ZN3bun5N_SVE26IndexOfHTMLEscapeChar8ImplEPKhm [SVE] @@ -73,8 +77,10 @@ _ZN3bun6N_SVE219CopyAsciiPrefixImplEPKhmPh [S _ZN3bun6N_SVE219FirstNonAscii16ImplEPKtm [SVE] _ZN3bun6N_SVE220FillWithSkipMaskImplEPKhmPhS2_mb [SVE] _ZN3bun6N_SVE221VisibleUTF16WidthImplEPKtmPm [SVE] +_ZN3bun6N_SVE222IndexOfEscapeChar8ImplEPKhm [SVE] _ZN3bun6N_SVE222VisibleLatin1WidthImplEPKhm [SVE] _ZN3bun6N_SVE223HtmlEscapeExtraLen8ImplEPKhm [SVE] +_ZN3bun6N_SVE223IndexOfEscapeChar16ImplEPKtm [SVE] _ZN3bun6N_SVE224HtmlEscapeExtraLen16ImplEPKtm [SVE] _ZN3bun6N_SVE225CountPrintableAscii16ImplEPKtm [SVE] _ZN3bun6N_SVE226IndexOfHTMLEscapeChar8ImplEPKhm [SVE] @@ -100,8 +106,10 @@ _ZN3bun9N_SVE_25619CopyAsciiPrefixImplEPKhmPh [S _ZN3bun9N_SVE_25619FirstNonAscii16ImplEPKtm [SVE] _ZN3bun9N_SVE_25620FillWithSkipMaskImplEPKhmPhS2_mb [SVE] _ZN3bun9N_SVE_25621VisibleUTF16WidthImplEPKtmPm [SVE] +_ZN3bun9N_SVE_25622IndexOfEscapeChar8ImplEPKhm [SVE] _ZN3bun9N_SVE_25622VisibleLatin1WidthImplEPKhm [SVE] _ZN3bun9N_SVE_25623HtmlEscapeExtraLen8ImplEPKhm [SVE] +_ZN3bun9N_SVE_25623IndexOfEscapeChar16ImplEPKtm [SVE] _ZN3bun9N_SVE_25624HtmlEscapeExtraLen16ImplEPKtm [SVE] _ZN3bun9N_SVE_25625CountPrintableAscii16ImplEPKtm [SVE] _ZN3bun9N_SVE_25626IndexOfHTMLEscapeChar8ImplEPKhm [SVE] diff --git a/scripts/verify-baseline-static/allowlist-x64-windows.txt b/scripts/verify-baseline-static/allowlist-x64-windows.txt index 08f049e7973..be388ad3505 100644 --- a/scripts/verify-baseline-static/allowlist-x64-windows.txt +++ b/scripts/verify-baseline-static/allowlist-x64-windows.txt @@ -437,7 +437,7 @@ ctiMasmProbeTrampolineAVX [AVX] # ---------------------------------------------------------------------------- # Highway. MSVC-mangled bun::N_AVX* names. -# (162 symbols) +# (174 symbols) # ---------------------------------------------------------------------------- bun::N_AVX10_2::ContainsNewlineOrNonASCIIOrQuoteImpl [AVX, AVX512BW, AVX512F] bun::N_AVX10_2::CopyAsciiPrefixImpl [AVX, AVX512BW, AVX512F, AVX512VL] @@ -453,6 +453,8 @@ bun::N_AVX10_2::HtmlEscapeExtraLen16Impl [AVX, AVX2, bun::N_AVX10_2::HtmlEscapeExtraLen8Impl [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] bun::N_AVX10_2::IndexOfAnyCharImpl [AVX, AVX512BW, AVX512F, AVX512VL, AVX512_FP16] bun::N_AVX10_2::IndexOfCharImpl [AVX, AVX512BW, BMI2] +bun::N_AVX10_2::IndexOfEscapeChar16Impl [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, AVX512_FP16, AVX512_VBMI, BMI1, BMI2, GFNI] +bun::N_AVX10_2::IndexOfEscapeChar8Impl [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, AVX512_FP16, AVX512_VBMI, BMI1, BMI2, GFNI] bun::N_AVX10_2::IndexOfHTMLEscapeChar16Impl [AVX, AVX512BW, AVX512F] bun::N_AVX10_2::IndexOfHTMLEscapeChar8Impl [AVX, AVX512BW, AVX512F] bun::N_AVX10_2::IndexOfInterestingCharacterInMultilineCommentImpl [AVX, AVX512BW, AVX512F] @@ -480,6 +482,8 @@ bun::N_AVX2::HtmlEscapeExtraLen16Impl [AVX, AVX2] bun::N_AVX2::HtmlEscapeExtraLen8Impl [AVX, AVX2] bun::N_AVX2::IndexOfAnyCharImpl [AVX, AVX2] bun::N_AVX2::IndexOfCharImpl [AVX, AVX2] +bun::N_AVX2::IndexOfEscapeChar16Impl [AVX, AVX2, BMI1, BMI2] +bun::N_AVX2::IndexOfEscapeChar8Impl [AVX, AVX2, BMI1, BMI2] bun::N_AVX2::IndexOfHTMLEscapeChar16Impl [AVX, AVX2, BMI2] bun::N_AVX2::IndexOfHTMLEscapeChar8Impl [AVX, AVX2] bun::N_AVX2::IndexOfInterestingCharacterInMultilineCommentImpl [AVX, AVX2] @@ -507,6 +511,8 @@ bun::N_AVX3::HtmlEscapeExtraLen16Impl [AVX, AVX2, bun::N_AVX3::HtmlEscapeExtraLen8Impl [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] bun::N_AVX3::IndexOfAnyCharImpl [AVX, AVX512BW, AVX512F, AVX512VL] bun::N_AVX3::IndexOfCharImpl [AVX, AVX512BW, BMI2] +bun::N_AVX3::IndexOfEscapeChar16Impl [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, BMI1, BMI2] +bun::N_AVX3::IndexOfEscapeChar8Impl [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, BMI1, BMI2] bun::N_AVX3::IndexOfHTMLEscapeChar16Impl [AVX, AVX512BW, AVX512F] bun::N_AVX3::IndexOfHTMLEscapeChar8Impl [AVX, AVX512BW, AVX512F] bun::N_AVX3::IndexOfInterestingCharacterInMultilineCommentImpl [AVX, AVX512BW, AVX512F] @@ -534,6 +540,8 @@ bun::N_AVX3_DL::HtmlEscapeExtraLen16Impl [AVX, AVX2, bun::N_AVX3_DL::HtmlEscapeExtraLen8Impl [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] bun::N_AVX3_DL::IndexOfAnyCharImpl [AVX, AVX512BW, AVX512F, AVX512VL] bun::N_AVX3_DL::IndexOfCharImpl [AVX, AVX512BW, BMI2] +bun::N_AVX3_DL::IndexOfEscapeChar16Impl [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, AVX512_VBMI, BMI1, BMI2, GFNI] +bun::N_AVX3_DL::IndexOfEscapeChar8Impl [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, AVX512_VBMI, BMI1, BMI2, GFNI] bun::N_AVX3_DL::IndexOfHTMLEscapeChar16Impl [AVX, AVX512BW, AVX512F] bun::N_AVX3_DL::IndexOfHTMLEscapeChar8Impl [AVX, AVX512BW, AVX512F] bun::N_AVX3_DL::IndexOfInterestingCharacterInMultilineCommentImpl [AVX, AVX512BW, AVX512F] @@ -561,6 +569,8 @@ bun::N_AVX3_SPR::HtmlEscapeExtraLen16Impl [AVX, AVX2, bun::N_AVX3_SPR::HtmlEscapeExtraLen8Impl [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] bun::N_AVX3_SPR::IndexOfAnyCharImpl [AVX, AVX512BW, AVX512F, AVX512VL, AVX512_FP16] bun::N_AVX3_SPR::IndexOfCharImpl [AVX, AVX512BW, BMI2] +bun::N_AVX3_SPR::IndexOfEscapeChar16Impl [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, AVX512_FP16, AVX512_VBMI, BMI1, BMI2, GFNI] +bun::N_AVX3_SPR::IndexOfEscapeChar8Impl [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, AVX512_FP16, AVX512_VBMI, BMI1, BMI2, GFNI] bun::N_AVX3_SPR::IndexOfHTMLEscapeChar16Impl [AVX, AVX512BW, AVX512F] bun::N_AVX3_SPR::IndexOfHTMLEscapeChar8Impl [AVX, AVX512BW, AVX512F] bun::N_AVX3_SPR::IndexOfInterestingCharacterInMultilineCommentImpl [AVX, AVX512BW, AVX512F] @@ -588,6 +598,8 @@ bun::N_AVX3_ZEN4::HtmlEscapeExtraLen16Impl [AVX, AVX2, bun::N_AVX3_ZEN4::HtmlEscapeExtraLen8Impl [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] bun::N_AVX3_ZEN4::IndexOfAnyCharImpl [AVX, AVX512BW, AVX512F, AVX512VL] bun::N_AVX3_ZEN4::IndexOfCharImpl [AVX, AVX512BW, BMI2] +bun::N_AVX3_ZEN4::IndexOfEscapeChar16Impl [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, AVX512_VBMI, BMI1, BMI2, GFNI] +bun::N_AVX3_ZEN4::IndexOfEscapeChar8Impl [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, AVX512_VBMI, BMI1, BMI2, GFNI] bun::N_AVX3_ZEN4::IndexOfHTMLEscapeChar16Impl [AVX, AVX512BW, AVX512F] bun::N_AVX3_ZEN4::IndexOfHTMLEscapeChar8Impl [AVX, AVX512BW, AVX512F] bun::N_AVX3_ZEN4::IndexOfInterestingCharacterInMultilineCommentImpl [AVX, AVX512BW, AVX512F] diff --git a/scripts/verify-baseline-static/allowlist-x64.txt b/scripts/verify-baseline-static/allowlist-x64.txt index 16080c045de..d6006647983 100644 --- a/scripts/verify-baseline-static/allowlist-x64.txt +++ b/scripts/verify-baseline-static/allowlist-x64.txt @@ -459,7 +459,7 @@ ctiMasmProbeTrampolineAVX [AVX] # ---------------------------------------------------------------------------- # Bun's Highway SIMD. Gate: HWY_DYNAMIC_DISPATCH via hwy::SupportedTargets. -# (162 symbols) +# (174 symbols) # ---------------------------------------------------------------------------- _ZN3bun10N_AVX3_SPR10MemMemImplEPKhmS2_m [AVX, AVX512BW, AVX512F, BMI1] _ZN3bun10N_AVX3_SPR14DecodeHex8ImplEPKhPhm [AVX, AVX512BW, AVX512F, AVX512VL, AVX512_VBMI, GFNI] @@ -473,8 +473,10 @@ _ZN3bun10N_AVX3_SPR19CopyAsciiPrefixImplEPKhmPh [ _ZN3bun10N_AVX3_SPR19FirstNonAscii16ImplEPKtm [AVX, AVX512BW, AVX512F] _ZN3bun10N_AVX3_SPR20FillWithSkipMaskImplEPKhmPhS2_mb [AVX, AVX512DQ, AVX512F] _ZN3bun10N_AVX3_SPR21VisibleUTF16WidthImplEPKtmPm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, BMI1, BMI2] +_ZN3bun10N_AVX3_SPR22IndexOfEscapeChar8ImplEPKhm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, AVX512_FP16, AVX512_VBMI, BMI1, BMI2, GFNI] _ZN3bun10N_AVX3_SPR22VisibleLatin1WidthImplEPKhm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] _ZN3bun10N_AVX3_SPR23HtmlEscapeExtraLen8ImplEPKhm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] +_ZN3bun10N_AVX3_SPR23IndexOfEscapeChar16ImplEPKtm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, AVX512_FP16, AVX512_VBMI, BMI1, BMI2, GFNI] _ZN3bun10N_AVX3_SPR24HtmlEscapeExtraLen16ImplEPKtm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] _ZN3bun10N_AVX3_SPR25CountPrintableAscii16ImplEPKtm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] _ZN3bun10N_AVX3_SPR26IndexOfHTMLEscapeChar8ImplEPKhm [AVX, AVX512BW, AVX512F] @@ -500,8 +502,10 @@ _ZN3bun11N_AVX3_ZEN419CopyAsciiPrefixImplEPKhmPh [ _ZN3bun11N_AVX3_ZEN419FirstNonAscii16ImplEPKtm [AVX, AVX512BW, AVX512F] _ZN3bun11N_AVX3_ZEN420FillWithSkipMaskImplEPKhmPhS2_mb [AVX, AVX512DQ, AVX512F] _ZN3bun11N_AVX3_ZEN421VisibleUTF16WidthImplEPKtmPm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, BMI1, BMI2] +_ZN3bun11N_AVX3_ZEN422IndexOfEscapeChar8ImplEPKhm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, AVX512_VBMI, BMI1, BMI2, GFNI] _ZN3bun11N_AVX3_ZEN422VisibleLatin1WidthImplEPKhm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] _ZN3bun11N_AVX3_ZEN423HtmlEscapeExtraLen8ImplEPKhm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] +_ZN3bun11N_AVX3_ZEN423IndexOfEscapeChar16ImplEPKtm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, AVX512_VBMI, BMI1, BMI2, GFNI] _ZN3bun11N_AVX3_ZEN424HtmlEscapeExtraLen16ImplEPKtm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] _ZN3bun11N_AVX3_ZEN425CountPrintableAscii16ImplEPKtm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] _ZN3bun11N_AVX3_ZEN426IndexOfHTMLEscapeChar8ImplEPKhm [AVX, AVX512BW, AVX512F] @@ -527,8 +531,10 @@ _ZN3bun6N_AVX219CopyAsciiPrefixImplEPKhmPh [ _ZN3bun6N_AVX219FirstNonAscii16ImplEPKtm [AVX, AVX2, BMI2] _ZN3bun6N_AVX220FillWithSkipMaskImplEPKhmPhS2_mb [AVX] _ZN3bun6N_AVX221VisibleUTF16WidthImplEPKtmPm [AVX, AVX2, BMI1, BMI2] +_ZN3bun6N_AVX222IndexOfEscapeChar8ImplEPKhm [AVX, AVX2, BMI1, BMI2] _ZN3bun6N_AVX222VisibleLatin1WidthImplEPKhm [AVX, AVX2] _ZN3bun6N_AVX223HtmlEscapeExtraLen8ImplEPKhm [AVX, AVX2] +_ZN3bun6N_AVX223IndexOfEscapeChar16ImplEPKtm [AVX, AVX2, BMI1, BMI2] _ZN3bun6N_AVX224HtmlEscapeExtraLen16ImplEPKtm [AVX, AVX2] _ZN3bun6N_AVX225CountPrintableAscii16ImplEPKtm [AVX, AVX2] _ZN3bun6N_AVX226IndexOfHTMLEscapeChar8ImplEPKhm [AVX, AVX2] @@ -554,8 +560,10 @@ _ZN3bun6N_AVX319CopyAsciiPrefixImplEPKhmPh [ _ZN3bun6N_AVX319FirstNonAscii16ImplEPKtm [AVX, AVX512BW, AVX512F] _ZN3bun6N_AVX320FillWithSkipMaskImplEPKhmPhS2_mb [AVX, AVX512DQ, AVX512F] _ZN3bun6N_AVX321VisibleUTF16WidthImplEPKtmPm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, BMI1, BMI2] +_ZN3bun6N_AVX322IndexOfEscapeChar8ImplEPKhm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, BMI1, BMI2] _ZN3bun6N_AVX322VisibleLatin1WidthImplEPKhm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] _ZN3bun6N_AVX323HtmlEscapeExtraLen8ImplEPKhm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] +_ZN3bun6N_AVX323IndexOfEscapeChar16ImplEPKtm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, BMI1, BMI2] _ZN3bun6N_AVX324HtmlEscapeExtraLen16ImplEPKtm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] _ZN3bun6N_AVX325CountPrintableAscii16ImplEPKtm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] _ZN3bun6N_AVX326IndexOfHTMLEscapeChar8ImplEPKhm [AVX, AVX512BW, AVX512F] @@ -581,8 +589,10 @@ _ZN3bun9N_AVX10_219CopyAsciiPrefixImplEPKhmPh [ _ZN3bun9N_AVX10_219FirstNonAscii16ImplEPKtm [AVX, AVX512BW, AVX512F] _ZN3bun9N_AVX10_220FillWithSkipMaskImplEPKhmPhS2_mb [AVX, AVX512DQ, AVX512F] _ZN3bun9N_AVX10_221VisibleUTF16WidthImplEPKtmPm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, BMI1, BMI2] +_ZN3bun9N_AVX10_222IndexOfEscapeChar8ImplEPKhm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, AVX512_FP16, AVX512_VBMI, BMI1, BMI2, GFNI] _ZN3bun9N_AVX10_222VisibleLatin1WidthImplEPKhm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] _ZN3bun9N_AVX10_223HtmlEscapeExtraLen8ImplEPKhm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] +_ZN3bun9N_AVX10_223IndexOfEscapeChar16ImplEPKtm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, AVX512_FP16, AVX512_VBMI, BMI1, BMI2, GFNI] _ZN3bun9N_AVX10_224HtmlEscapeExtraLen16ImplEPKtm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] _ZN3bun9N_AVX10_225CountPrintableAscii16ImplEPKtm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] _ZN3bun9N_AVX10_226IndexOfHTMLEscapeChar8ImplEPKhm [AVX, AVX512BW, AVX512F] @@ -608,8 +618,10 @@ _ZN3bun9N_AVX3_DL19CopyAsciiPrefixImplEPKhmPh [ _ZN3bun9N_AVX3_DL19FirstNonAscii16ImplEPKtm [AVX, AVX512BW, AVX512F] _ZN3bun9N_AVX3_DL20FillWithSkipMaskImplEPKhmPhS2_mb [AVX, AVX512DQ, AVX512F] _ZN3bun9N_AVX3_DL21VisibleUTF16WidthImplEPKtmPm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, BMI1, BMI2] +_ZN3bun9N_AVX3_DL22IndexOfEscapeChar8ImplEPKhm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, AVX512_VBMI, BMI1, BMI2, GFNI] _ZN3bun9N_AVX3_DL22VisibleLatin1WidthImplEPKhm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] _ZN3bun9N_AVX3_DL23HtmlEscapeExtraLen8ImplEPKhm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] +_ZN3bun9N_AVX3_DL23IndexOfEscapeChar16ImplEPKtm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL, AVX512_VBMI, BMI1, BMI2, GFNI] _ZN3bun9N_AVX3_DL24HtmlEscapeExtraLen16ImplEPKtm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] _ZN3bun9N_AVX3_DL25CountPrintableAscii16ImplEPKtm [AVX, AVX2, AVX512BW, AVX512DQ, AVX512F, AVX512VL] _ZN3bun9N_AVX3_DL26IndexOfHTMLEscapeChar8ImplEPKhm [AVX, AVX512BW, AVX512F] diff --git a/src/jsc/bindings/ANSIHelpers.h b/src/jsc/bindings/ANSIHelpers.h index 4e03b667fcc..64ff646c14e 100644 --- a/src/jsc/bindings/ANSIHelpers.h +++ b/src/jsc/bindings/ANSIHelpers.h @@ -5,6 +5,13 @@ #include #include +// Runtime-dispatched (HWY_DYNAMIC_DISPATCH) escape scan, defined in +// highway_strings.cpp. Picks AVX2/AVX-512/SVE at runtime, so the no-escape fast +// path keeps wide vectors even on the -march=nehalem baseline build where the +// WTF SIMD helpers below would otherwise be pinned to SSE width. +extern "C" size_t highway_index_of_escape_char8(const uint8_t* input, size_t len); +extern "C" size_t highway_index_of_escape_char16(const uint16_t* input, size_t len); + namespace Bun { namespace ANSI { @@ -40,7 +47,16 @@ static auto exactEscapeMatch(std::conditional_t(chunk); } -// Find the first escape character in a string using SIMD +// A long no-escape scan delegates to the runtime-dispatched Highway kernel so +// it uses the widest SIMD the CPU supports at runtime rather than the build's +// static -march (the baseline build would otherwise be pinned to SSE width — +// this is the 16 KB no-ANSI stripANSI regression). The inlined WTF SIMD scan +// below stays the path for everything else. +static constexpr size_t kEscapeDispatchThreshold = 1024; + +// Find the first escape character in a string. An "escape character" is 0x1B, +// 0x90, 0x98, 0x9B, 0x9C, 0x9D, 0x9E or 0x9F — matching isEscapeCharacter plus +// 0x9C (C1 ST). template static const Char* findEscapeCharacter(const Char* start, const Char* end) { @@ -57,6 +73,25 @@ static const Char* findEscapeCharacter(const Char* start, const Char* end) constexpr auto escVector = SIMD::splat(0b00010000); auto it = start; + const size_t len = static_cast(end - start); + + // Long scans delegate to the runtime-dispatched Highway kernel so the + // baseline build isn't pinned to SSE width — but only once the first chunk + // is confirmed clean. When an escape sits at/near the start (e.g. dense SGR + // input, where stripANSI re-scans the still-large remainder after each + // sequence) the inlined path finds it in this one cheap chunk and never + // pays the kernel's per-call setup. + if (len >= kEscapeDispatchThreshold) { + const auto chunk = SIMD::load(reinterpret_cast(it)); + if (!SIMD::findFirstNonZeroIndex(SIMD::equal(SIMD::bitAnd(chunk, escMask), escVector))) { + size_t idx; + if constexpr (sizeof(Char) == 1) + idx = highway_index_of_escape_char8(reinterpret_cast(start), len); + else + idx = highway_index_of_escape_char16(reinterpret_cast(start), len); + return idx < len ? start + idx : nullptr; + } + } // 4x-unrolled prologue: process 4 chunks at a time, accumulating broad-mask // hits in a vector OR. Only do the NEON->GPR transfer + branch every 64 bytes diff --git a/src/jsc/bindings/highway_strings.cpp b/src/jsc/bindings/highway_strings.cpp index 90c749c71f6..1e8f55121c7 100644 --- a/src/jsc/bindings/highway_strings.cpp +++ b/src/jsc/bindings/highway_strings.cpp @@ -1358,6 +1358,98 @@ size_t FirstNonAscii8Impl(const uint8_t* HWY_RESTRICT input, size_t len) return len; } +// An "escape character" for ANSI tokenizing: ESC, the ST-terminated C1 +// introducers, plus C1 ST (0x9C) itself so a standalone terminator stops the +// scan. Matches ANSIHelpers.h's scalar contract (isEscapeCharacter + 0x9C). +template +static HWY_INLINE bool IsEscapeCharScalar(T c) +{ + return c == 0x1b || c == 0x90 || c == 0x98 || c == 0x9b + || c == 0x9c || c == 0x9d || c == 0x9e || c == 0x9f; +} + +// Scalar scan over [from, len); returns the first escape index or len. +template +static HWY_INLINE size_t IndexOfEscapeCharScalar(const T* HWY_RESTRICT input, size_t from, size_t len) +{ + for (size_t i = from; i < len; ++i) { + if (IsEscapeCharScalar(input[i])) + return i; + } + return len; +} + +// Index of the first ANSI escape introducer, or len if none. Shared by +// Bun.stripANSI / stringWidth / wrapAnsi / sliceAnsi via ANSIHelpers.h's +// findEscapeCharacter. +// +// Two-stage like the original WTF-SIMD version: a broad range mask +// (c & 0x70) == 0x10 catches 0x10-0x1F and 0x90-0x9F in one compare — the hot +// no-escape path pays only this per chunk — then an exact 8-value match +// refines a broad hit down to the real introducers. `T` is u8 (Latin-1) or +// u16 (UTF-16); on u16 the broad mask 0xFF70 also rejects code units >= 0x100. +// +// Short inputs take the scalar path before any vector setup, so the kernel is +// cheap when called standalone. (The only current caller, findEscapeCharacter, +// gates dispatch at >= kEscapeDispatchThreshold, but this is extern "C".) +template +static HWY_INLINE size_t IndexOfEscapeCharImpl(const T* HWY_RESTRICT input, size_t len) +{ + const hn::ScalableTag d; + const size_t N = hn::Lanes(d); + + if (len < N) + return IndexOfEscapeCharScalar(input, 0, len); + + // Broad range: (c & ~0b10001111) == 0b00010000 → 0x10-0x1F and 0x90-0x9F. + const auto broad_mask = hn::Set(d, static_cast(~0b10001111U)); + const auto broad_vec = hn::Set(d, static_cast(0b00010000)); + + // Exact introducers (including C1 ST 0x9C), used to reject broad-mask + // false positives (0x10-0x1A, 0x1C-0x1F, 0x91-0x97, 0x99-0x9A). + const auto vec_1b = hn::Set(d, static_cast(0x1b)); + const auto vec_90 = hn::Set(d, static_cast(0x90)); + const auto vec_98 = hn::Set(d, static_cast(0x98)); + const auto vec_9b = hn::Set(d, static_cast(0x9b)); + const auto vec_9c = hn::Set(d, static_cast(0x9c)); + const auto vec_9d = hn::Set(d, static_cast(0x9d)); + const auto vec_9e = hn::Set(d, static_cast(0x9e)); + const auto vec_9f = hn::Set(d, static_cast(0x9f)); + + const auto exact_match = [&](auto chunk) HWY_ATTR { + return hn::Or( + hn::Or(hn::Or(hn::Eq(chunk, vec_1b), hn::Eq(chunk, vec_90)), + hn::Or(hn::Eq(chunk, vec_98), hn::Eq(chunk, vec_9b))), + hn::Or(hn::Or(hn::Eq(chunk, vec_9c), hn::Eq(chunk, vec_9d)), + hn::Or(hn::Eq(chunk, vec_9e), hn::Eq(chunk, vec_9f)))); + }; + + size_t i = 0; + const size_t simd_len = len - (len % N); + for (; i < simd_len; i += N) { + const auto chunk = hn::LoadU(d, input + i); + const auto broad = hn::Eq(hn::And(chunk, broad_mask), broad_vec); + if (hn::AllFalse(d, broad)) + continue; + const intptr_t pos = hn::FindFirstTrue(d, exact_match(chunk)); + if (pos >= 0) { + return i + pos; + } + } + + return IndexOfEscapeCharScalar(input, i, len); +} + +size_t IndexOfEscapeChar8Impl(const uint8_t* HWY_RESTRICT input, size_t len) +{ + return IndexOfEscapeCharImpl(input, len); +} + +size_t IndexOfEscapeChar16Impl(const uint16_t* HWY_RESTRICT input, size_t len) +{ + return IndexOfEscapeCharImpl(input, len); +} + size_t CopyAsciiPrefixImpl(const uint8_t* HWY_RESTRICT src, size_t len, uint8_t* HWY_RESTRICT dst) { D8 d; @@ -1653,6 +1745,8 @@ HWY_EXPORT(HtmlEscapeExtraLen16Impl); HWY_EXPORT(HtmlEscapeExtraLen8Impl); HWY_EXPORT(IndexOfAnyCharImpl); HWY_EXPORT(IndexOfCharImpl); +HWY_EXPORT(IndexOfEscapeChar16Impl); +HWY_EXPORT(IndexOfEscapeChar8Impl); HWY_EXPORT(IndexOfHTMLEscapeChar8Impl); HWY_EXPORT(IndexOfHTMLEscapeChar16Impl); HWY_EXPORT(IndexOfInterestingCharacterInMultilineCommentImpl); @@ -1732,6 +1826,16 @@ size_t highway_index_of_char(const uint8_t* HWY_RESTRICT haystack, size_t haysta return HWY_DYNAMIC_DISPATCH(IndexOfCharImpl)(haystack, haystack_len, needle); } +size_t highway_index_of_escape_char8(const uint8_t* HWY_RESTRICT input, size_t len) +{ + return HWY_DYNAMIC_DISPATCH(IndexOfEscapeChar8Impl)(input, len); +} + +size_t highway_index_of_escape_char16(const uint16_t* HWY_RESTRICT input, size_t len) +{ + return HWY_DYNAMIC_DISPATCH(IndexOfEscapeChar16Impl)(input, len); +} + size_t highway_index_of_html_escape_char8(const uint8_t* HWY_RESTRICT text, size_t text_len) { return HWY_DYNAMIC_DISPATCH(IndexOfHTMLEscapeChar8Impl)(text, text_len); diff --git a/test/js/bun/util/stripANSI.test.ts b/test/js/bun/util/stripANSI.test.ts index a7dbd8073cb..192a82a89ed 100644 --- a/test/js/bun/util/stripANSI.test.ts +++ b/test/js/bun/util/stripANSI.test.ts @@ -478,4 +478,81 @@ describe("Bun.stripANSI", () => { expect(Bun.stripANSI(null as any)).toBe("null"); expect(Bun.stripANSI(undefined as any)).toBe("undefined"); }); + + // Large inputs take the runtime-dispatched Highway escape-scan kernel + // (findEscapeCharacter delegates to it past a 1 KB length threshold); shorter + // inputs use the inlined scan. Both implement the identical broad-mask + + // exact-match contract, so the core invariant is: a long all-ASCII prefix (no + // escapes — skipped by the scan) followed by any sequence strips to that + // prefix plus the short-input result for the sequence. These guard the + // kernel's correctness across SIMD-chunk boundaries and the widest vectors. + const prefix = Buffer.alloc(4096, "a").toString(); + const suffix = Buffer.alloc(4096, "b").toString(); + + describe("large-input escape scan", () => { + // The regressing benchmark case: a 16 KB string with no escapes must scan + // clean and return the *same* object (zero copy). toBe compares strings by + // value, so the heap-count delta is what actually proves no new string was + // allocated. Warm up once so the measured call has a settled baseline. + test("large no-ANSI string returns the same object", () => { + const input = Buffer.alloc(16 * 1024, "a").toString(); + Bun.stripANSI(input); + heapStats(); + + const before = heapStats().objectTypeCounts.string; + const result = Bun.stripANSI(input); + const after = heapStats().objectTypeCounts.string; + expect(result).toBe(input); + expect(after).toBe(before); // no copy made + }); + + // Dispatched path matches the inlined path for a variety of sequences: the + // long no-escape prefix is skipped identically, so stripping the prefixed + // input equals the prefix plus stripping the sequence alone. + const sequences = [ + "\x1b[31mred\x1b[39m", // CSI (ESC [) + "\x9b31mtext\x9b39m", // C1 CSI (0x9B) + "\x1b]8;;https://example.com\x07link\x1b]8;;\x07", // OSC hyperlink + "\x1b]0;title\x1b\\rest", // OSC with ST (ESC \) terminator + "\x1bDtext", // bare ESC sequence + "\x9ctext", // standalone C1 ST (0x9C) — in the mask, not an introducer + "no escapes at all here", // nothing to strip past the prefix + ]; + describe.each(sequences)("dispatched scan matches inlined for %j", seq => { + test("strips identically with and without a large prefix", () => { + const short = Bun.stripANSI(seq); + expect(Bun.stripANSI(prefix + seq)).toBe(prefix + short); + expect(Bun.stripANSI(prefix + seq + suffix)).toBe(prefix + Bun.stripANSI(seq + suffix)); + }); + }); + + // An ESC at each of many byte offsets exercises the SIMD chunk scan + // (offsets landing mid-vector) and the scalar tail, for lengths crossing + // the dispatch threshold. + test("ESC at every offset across the dispatch threshold", () => { + for (const total of [1024, 1040, 2048]) { + const filler = Buffer.alloc(total, "a").toString(); + for (let pos = 0; pos < total; pos += 13) { + const input = filler.slice(0, pos) + "\x1b[31m" + filler.slice(pos); + expect(Bun.stripANSI(input)).toBe(stripAnsi(input)); + } + } + }); + + // A UTF-16 (two-byte) large string routes through the u16 kernel. The + // leading non-Latin1 char forces a 16-bit JSString; the trailing ASCII run + // is where the escape scan does its work. + test("large UTF-16 string with escapes", () => { + const input = "☃" + prefix + "\x1b[31mred\x1b[39m" + suffix; + expect(Bun.stripANSI(input)).toBe("☃" + prefix + "red" + suffix); + }); + + // Broad-mask false positives (0x10-0x1A, 0x1C-0x1F, 0x91-0x97, 0x99-0x9A) + // are in the broad range but not the exact introducer set — the large-input + // kernel's refinement rejects them, so the bytes survive verbatim. + test("broad-mask false positives are preserved in a large string", () => { + const input = prefix + "\x11\x1a\x1f\x99\x9a" + suffix; + expect(Bun.stripANSI(input)).toBe(input); + }); + }); });