Compiler overwrites wrong memory location in .NET 8 RC1 when using Unsafe.Add and Vector256<uint>[0] #92590

Description

@jwdj

Description

In a release build with .NET 8 RC1 the compiler overwrites memory it shouldn't touch.

Input is a Span<byte> of pixels with layout RGBA, where every fourth byte is an alpha byte set to 255 (index 3, 7, 11, etc).
The code only overwrites the RGB bytes (indexes 0, 1, 2, 4, 5, 6, 8, etc), but the compiler overwrites 3, 7, 11, etc too (with 0).
This overwrite happens when a ref byte (3 bytes earlier) is overwritten with the first element of a Vector256

Unsafe.Add(refdst_ptr,0)=(byte)blurredPixel0[0];// blurredPixel0 is a Vector256<uint> dst_ptr is ref byte// byte at dst_ptr + 3 is now overwritten with 0

Below is this line in more context. I have tried to shorten the example code but the bug is very specific and is Vector256 related.

Configuration

  • .NET 8.0 RC1
  • OS: Windows 10
  • Only tested with x64

Regression?

Yes, compared to .NET 7.0

Other information

The following configurations work fine:
dotnet run StackBlur --framework net7.0 --configuration Release
dotnet run StackBlur --framework net7.0 --configuration Debug
dotnet run StackBlur --framework net8.0 --configuration Debug

To reproduce the error (from PowerShell):
$env:DOTNET_JitDisasm = 'StackblurPass'
dotnet run StackBlur --framework net8.0 --configuration Release

Here the project code StackBlur.csproj:

<ProjectSdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>net7.0;net8.0</TargetFrameworks>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup>
</Project>

And the source code Program.cs:
The program is rather long but scroll down to where it is not indented to find the issue.

usingSystem;usingSystem.Numerics;usingSystem.Runtime.Intrinsics;usingSystem.Runtime.Intrinsics.X86;usingSystem.Runtime.CompilerServices;usingSystem.Runtime.InteropServices;usingSystem.Diagnostics;namespaceApplication{classProgram{staticvoidMain(string[]args){Console.WriteLine(Environment.Version);constintWidth=1920;constintHeight=1080;Span<byte>pixels=newbyte[Width*Height*sizeof(uint)];for(inti=3;i<pixels.Length;i+=4){pixels[i]=255;// fill alpha channel}pixels.Fill(255);vararea=Vector128.Create(0,0,384,384);StackBlur(pixels,Width,Height,Width*sizeof(uint),radius:100,area);for(inti=3;i<pixels.Length;i+=4){if(pixels[i]!=255){Console.WriteLine("FAILED");return;}}}staticushort[]stackblur_mul=newushort[255]{512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259};staticushort[]stackblur_shr=newushort[255]{9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24};//[MethodImpl(MethodImplOptions.NoOptimization)] // also 'fixes' the issuestaticvoidStackblurPass(Span<byte>src,///< input image dataintwidth,///< image widthintheight,///< image heightintrowStride,uintradius,///< blur intensity (should be in 2..254 range)boolvertical,Vector128<int>area){uintmul_sum=stackblur_mul[radius];intshr_sum=stackblur_shr[radius];varareaX=area[0];varareaY=area[1];varareaWidth=area[2];varareaHeight=area[3];intstrideX=4;intstrideY=rowStride;intareaMinX=areaX>=0?areaX:0;intareaMinY=areaY>=0?areaY:0;intmaxX=width-1;intmaxY=height-1;intareaMaxX=areaX+areaWidth-1;intareaMaxY=areaY+areaHeight-1;if(areaMaxX>maxX)areaMaxX=maxX;if(areaMaxY>maxY)areaMaxY=maxY;areaMaxX-=areaMinX;areaMaxY-=areaMinY;if(vertical){// transpose (swap X and Y)(maxX,maxY)=(maxY,maxX);(strideX,strideY)=(strideY,strideX);(areaMinX,areaMinY)=(areaMinY,areaMinX);(areaMaxX,areaMaxY)=(areaMaxY,areaMaxX);}uintinitialSum=(uint)SumNumbers((int)radius+1);refvarsourcePtr=refsrc[areaMinX*strideX+areaMinY*strideY];if(Avx2.IsSupported&&Vector256.IsHardwareAccelerated){varidx0=Vector256.Create(0,1,2,1*strideY,1*strideY+1,1*strideY+2,2*strideY,2*strideY+1);varidx1=Vector256.Create(2*strideY+2,3*strideY,3*strideY+1,3*strideY+2,4*strideY,4*strideY+1,4*strideY+2,5*strideY);varidx2=Vector256.Create(5*strideY+1,5*strideY+2,6*strideY,6*strideY+1,6*strideY+2,7*strideY,7*strideY+1,7*strideY+2);varlowerByte=Vector256.Create(0xFFU);[MethodImpl(MethodImplOptions.AggressiveInlining)]unsafevoidFillVectorsFromPixels(refbytesrc,refVector256<uint>data0,refVector256<uint>data1,refVector256<uint>data2){varsrc_ptr=(uint*)Unsafe.AsPointer(refsrc);data0=Avx2.GatherVector256(src_ptr,idx0,1);data1=Avx2.GatherVector256(src_ptr,idx1,1);data2=Avx2.GatherVector256(src_ptr,idx2,1);data0=Avx2.And(data0,lowerByte);data1=Avx2.And(data1,lowerByte);data2=Avx2.And(data2,lowerByte);}Span<Vector256<uint>>stack0=stackallocVector256<uint>[(int)radius*2+1];Span<Vector256<uint>>stack1=stackallocVector256<uint>[(int)radius*2+1];Span<Vector256<uint>>stack2=stackallocVector256<uint>[(int)radius*2+1];for(inty=0;y<=areaMaxY;y+=8){varsum_in0=Vector256<uint>.Zero;varsum_in1=Vector256<uint>.Zero;varsum_in2=Vector256<uint>.Zero;vardata0=Vector256<uint>.Zero;vardata1=Vector256<uint>.Zero;vardata2=Vector256<uint>.Zero;refvarsrc_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// start of line (0,y)FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);varsum0=Vector256.Multiply(data0,initialSum);varsum1=Vector256.Multiply(data1,initialSum);varsum2=Vector256.Multiply(data2,initialSum);varsum_out0=Vector256.Multiply(data0,radius+1);varsum_out1=Vector256.Multiply(data1,radius+1);varsum_out2=Vector256.Multiply(data2,radius+1);foreach(refvarstack_ptrinstack0.Slice(0,(int)radius+1)){stack_ptr=data0;}foreach(refvarstack_ptrinstack1.Slice(0,(int)radius+1)){stack_ptr=data1;}foreach(refvarstack_ptrinstack2.Slice(0,(int)radius+1)){stack_ptr=data2;}for(inti=1;i<=radius;i++){if(i<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);}refvarstack_ptr=refstack0[(int)(i+radius)];stack_ptr=data0;stack_ptr=refstack1[(int)(i+radius)];stack_ptr=data1;stack_ptr=refstack2[(int)(i+radius)];stack_ptr=data2;sum0+=data0*(uint)(radius+1-i);sum1+=data1*(uint)(radius+1-i);sum2+=data2*(uint)(radius+1-i);sum_in0+=data0;sum_in1+=data1;sum_in2+=data2;}intsp=(int)radius;intxp=(int)radius;if(xp>areaMaxX)xp=areaMaxX;src_ptr=refUnsafe.Add(refsourcePtr,xp*strideX+y*strideY);refvardst_ptr=refUnsafe.Add(refsourcePtr,y*strideY);for(intx=0;x<=areaMaxX;x++){varblurredPixel0=Vector256.ShiftRightLogical(Vector256.Multiply(sum0,mul_sum),shr_sum);varblurredPixel1=Vector256.ShiftRightLogical(Vector256.Multiply(sum1,mul_sum),shr_sum);varblurredPixel2=Vector256.ShiftRightLogical(Vector256.Multiply(sum2,mul_sum),shr_sum);
#if NET8_0_OR_GREATERConsole.WriteLine(Unsafe.Add(refdst_ptr,3).ToString("X"));// should print FF
#endif
Unsafe.Add(refdst_ptr,0)=(byte)blurredPixel0[0];// bug, should only write a single byte
#if NET8_0_OR_GREATERConsole.WriteLine(Unsafe.Add(refdst_ptr,3).ToString("X"));// should print FF, not 00
#endif
Unsafe.Add(refdst_ptr,1)=(byte)blurredPixel0[1];Unsafe.Add(refdst_ptr,2)=(byte)blurredPixel0[2];Unsafe.Add(refdst_ptr,0+strideY)=(byte)blurredPixel0[3];Unsafe.Add(refdst_ptr,1+strideY)=(byte)blurredPixel0[4];Unsafe.Add(refdst_ptr,2+strideY)=(byte)blurredPixel0[5];Unsafe.Add(refdst_ptr,0+strideY*2)=(byte)blurredPixel0[6];Unsafe.Add(refdst_ptr,1+strideY*2)=(byte)blurredPixel0[7];Unsafe.Add(refdst_ptr,2+strideY*2)=(byte)blurredPixel1[0];Unsafe.Add(refdst_ptr,0+strideY*3)=(byte)blurredPixel1[1];Unsafe.Add(refdst_ptr,1+strideY*3)=(byte)blurredPixel1[2];Unsafe.Add(refdst_ptr,2+strideY*3)=(byte)blurredPixel1[3];Unsafe.Add(refdst_ptr,0+strideY*4)=(byte)blurredPixel1[4];Unsafe.Add(refdst_ptr,1+strideY*4)=(byte)blurredPixel1[5];Unsafe.Add(refdst_ptr,2+strideY*4)=(byte)blurredPixel1[6];Unsafe.Add(refdst_ptr,0+strideY*5)=(byte)blurredPixel1[7];Unsafe.Add(refdst_ptr,1+strideY*5)=(byte)blurredPixel2[0];Unsafe.Add(refdst_ptr,2+strideY*5)=(byte)blurredPixel2[1];Unsafe.Add(refdst_ptr,0+strideY*6)=(byte)blurredPixel2[2];Unsafe.Add(refdst_ptr,1+strideY*6)=(byte)blurredPixel2[3];Unsafe.Add(refdst_ptr,2+strideY*6)=(byte)blurredPixel2[4];Unsafe.Add(refdst_ptr,0+strideY*7)=(byte)blurredPixel2[5];Unsafe.Add(refdst_ptr,1+strideY*7)=(byte)blurredPixel2[6];Unsafe.Add(refdst_ptr,2+strideY*7)=(byte)blurredPixel2[7];dst_ptr=refUnsafe.Add(refdst_ptr,strideX);sum0-=sum_out0;sum1-=sum_out1;sum2-=sum_out2;intstack_start=sp+(int)radius+1;if(stack_start>=stack0.Length)stack_start-=stack0.Length;refvarstack_ptr0=refstack0[stack_start];sum_out0-=stack_ptr0;refvarstack_ptr1=refstack1[stack_start];sum_out1-=stack_ptr1;refvarstack_ptr2=refstack2[stack_start];sum_out2-=stack_ptr2;if(xp<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);++xp;}stack_ptr0=data0;sum_in0+=data0;sum0+=sum_in0;stack_ptr1=data1;sum_in1+=data1;sum1+=sum_in1;stack_ptr2=data2;sum_in2+=data2;sum2+=sum_in2;++sp;if(sp>=stack0.Length)sp=0;stack_ptr0=refstack0[sp];sum_out0+=stack_ptr0;sum_in0-=stack_ptr0;stack_ptr1=refstack1[sp];sum_out1+=stack_ptr1;sum_in1-=stack_ptr1;stack_ptr2=refstack2[sp];sum_out2+=stack_ptr2;sum_in2-=stack_ptr2;}}}else{[MethodImpl(MethodImplOptions.AggressiveInlining)]staticvoidFillVectorFromPixel(refVector128<uint>data,refbytesrc_ptr){data=Vector128.Create((uint)src_ptr,(uint)Unsafe.Add(refsrc_ptr,1),(uint)Unsafe.Add(refsrc_ptr,2),(uint)Unsafe.Add(refsrc_ptr,3));}Span<Vector128<uint>>stack=stackallocVector128<uint>[(int)radius*2+1];for(inty=0;y<=areaMaxY;y++){varsum_in=Vector128<uint>.Zero;refbytesrc_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// start of line (0,y)Vector128<uint>data=Vector128<uint>.Zero;FillVectorFromPixel(refdata,refsrc_ptr);varsum=Vector128.Multiply(data,initialSum);varsum_out=Vector128.Multiply(data,radius+1);foreach(refvarstack_ptrinstack.Slice(0,(int)radius+1)){stack_ptr=data;}for(inti=1;i<=radius;i++){if(i<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorFromPixel(refdata,refsrc_ptr);}refvarstack_ptr=refstack[(int)(i+radius)];stack_ptr=data;sum+=data*(uint)(radius+1-i);sum_in+=data;}intsp=(int)radius;intxp=(int)radius;if(xp>areaMaxX)xp=areaMaxX;src_ptr=refUnsafe.Add(refsourcePtr,xp*strideX+y*strideY);// img.pix_ptr(xp, y);refbytedst_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// img.pix_ptr(0, y);for(intx=0;x<=areaMaxX;x++){varblurredPixel=Vector128.ShiftRightLogical(Vector128.Multiply(sum,mul_sum),shr_sum);dst_ptr=(byte)blurredPixel[0];Unsafe.Add(refdst_ptr,1)=(byte)blurredPixel[1];Unsafe.Add(refdst_ptr,2)=(byte)blurredPixel[2];Unsafe.Add(refdst_ptr,3)=(byte)blurredPixel[3];dst_ptr=refUnsafe.Add(refdst_ptr,strideX);sum-=sum_out;intstack_start=sp+(int)radius+1;if(stack_start>=stack.Length)stack_start-=stack.Length;refvarstack_ptr=refstack[stack_start];sum_out-=stack_ptr;if(xp<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorFromPixel(refdata,refsrc_ptr);++xp;}stack_ptr=data;sum_in+=data;sum+=sum_in;++sp;if(sp>=stack.Length)sp=0;stack_ptr=refstack[sp];sum_out+=stack_ptr;sum_in-=stack_ptr;}}}}staticintSumNumbers(intn){returnn*(n+1)/2;// fast way of calculating range (0..n).Sum()}/// Stackblur algorithm by Mario Klingemann/// Details here:/// http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html/// C++ implemenation base from:/// https://gist.github.com/benjamin9999/3809142/// http://www.antigrain.com/__code/include/agg_blur.h.html/// This version works only with RGBA colorstaticvoidStackBlur(Span<byte>src,///< input image dataintw,///< image widthinth,///< image heightintrowStride,uintradius///< blur intensity (should be in 2..254 range)){vararea=Vector128.Create(0,0,w,h);StackBlur(src,w,h,rowStride,radius,area);}staticvoidStackBlur(Span<byte>src,///< input image dataintw,///< image widthinth,///< image heightintrowStride,uintradius,///< blur intensity (should be in 2..254 range)Vector128<int>area){if(radius>254)return;if(radius<1)return;StackblurPass(src,w,h,rowStride,radius,vertical:false,area);StackblurPass(src,w,h,rowStride,radius,vertical:true,area);}}}

Metadata

Metadata

Assignees

Labels

area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

    , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
     blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
    }
    } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
    })();
    (function(){
    try {
    var __m = "github.com";
    var __re = new RegExp('^' + "github\\.com" + '
    
    Skip to content

    Compiler overwrites wrong memory location in .NET 8 RC1 when using Unsafe.Add and Vector256<uint>[0] #92590

    Description

    @jwdj

    Description

    In a release build with .NET 8 RC1 the compiler overwrites memory it shouldn't touch.

    Input is a Span<byte> of pixels with layout RGBA, where every fourth byte is an alpha byte set to 255 (index 3, 7, 11, etc).
    The code only overwrites the RGB bytes (indexes 0, 1, 2, 4, 5, 6, 8, etc), but the compiler overwrites 3, 7, 11, etc too (with 0).
    This overwrite happens when a ref byte (3 bytes earlier) is overwritten with the first element of a Vector256

    Unsafe.Add(refdst_ptr,0)=(byte)blurredPixel0[0];// blurredPixel0 is a Vector256<uint> dst_ptr is ref byte// byte at dst_ptr + 3 is now overwritten with 0

    Below is this line in more context. I have tried to shorten the example code but the bug is very specific and is Vector256 related.

    Configuration

    • .NET 8.0 RC1
    • OS: Windows 10
    • Only tested with x64

    Regression?

    Yes, compared to .NET 7.0

    Other information

    The following configurations work fine:
    dotnet run StackBlur --framework net7.0 --configuration Release
    dotnet run StackBlur --framework net7.0 --configuration Debug
    dotnet run StackBlur --framework net8.0 --configuration Debug

    To reproduce the error (from PowerShell):
    $env:DOTNET_JitDisasm = 'StackblurPass'
    dotnet run StackBlur --framework net8.0 --configuration Release

    Here the project code StackBlur.csproj:

    <ProjectSdk="Microsoft.NET.Sdk">
    <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFrameworks>net7.0;net8.0</TargetFrameworks>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup>
    </Project>

    And the source code Program.cs:
    The program is rather long but scroll down to where it is not indented to find the issue.

    usingSystem;usingSystem.Numerics;usingSystem.Runtime.Intrinsics;usingSystem.Runtime.Intrinsics.X86;usingSystem.Runtime.CompilerServices;usingSystem.Runtime.InteropServices;usingSystem.Diagnostics;namespaceApplication{classProgram{staticvoidMain(string[]args){Console.WriteLine(Environment.Version);constintWidth=1920;constintHeight=1080;Span<byte>pixels=newbyte[Width*Height*sizeof(uint)];for(inti=3;i<pixels.Length;i+=4){pixels[i]=255;// fill alpha channel}pixels.Fill(255);vararea=Vector128.Create(0,0,384,384);StackBlur(pixels,Width,Height,Width*sizeof(uint),radius:100,area);for(inti=3;i<pixels.Length;i+=4){if(pixels[i]!=255){Console.WriteLine("FAILED");return;}}}staticushort[]stackblur_mul=newushort[255]{512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259};staticushort[]stackblur_shr=newushort[255]{9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24};//[MethodImpl(MethodImplOptions.NoOptimization)] // also 'fixes' the issuestaticvoidStackblurPass(Span<byte>src,///< input image dataintwidth,///< image widthintheight,///< image heightintrowStride,uintradius,///< blur intensity (should be in 2..254 range)boolvertical,Vector128<int>area){uintmul_sum=stackblur_mul[radius];intshr_sum=stackblur_shr[radius];varareaX=area[0];varareaY=area[1];varareaWidth=area[2];varareaHeight=area[3];intstrideX=4;intstrideY=rowStride;intareaMinX=areaX>=0?areaX:0;intareaMinY=areaY>=0?areaY:0;intmaxX=width-1;intmaxY=height-1;intareaMaxX=areaX+areaWidth-1;intareaMaxY=areaY+areaHeight-1;if(areaMaxX>maxX)areaMaxX=maxX;if(areaMaxY>maxY)areaMaxY=maxY;areaMaxX-=areaMinX;areaMaxY-=areaMinY;if(vertical){// transpose (swap X and Y)(maxX,maxY)=(maxY,maxX);(strideX,strideY)=(strideY,strideX);(areaMinX,areaMinY)=(areaMinY,areaMinX);(areaMaxX,areaMaxY)=(areaMaxY,areaMaxX);}uintinitialSum=(uint)SumNumbers((int)radius+1);refvarsourcePtr=refsrc[areaMinX*strideX+areaMinY*strideY];if(Avx2.IsSupported&&Vector256.IsHardwareAccelerated){varidx0=Vector256.Create(0,1,2,1*strideY,1*strideY+1,1*strideY+2,2*strideY,2*strideY+1);varidx1=Vector256.Create(2*strideY+2,3*strideY,3*strideY+1,3*strideY+2,4*strideY,4*strideY+1,4*strideY+2,5*strideY);varidx2=Vector256.Create(5*strideY+1,5*strideY+2,6*strideY,6*strideY+1,6*strideY+2,7*strideY,7*strideY+1,7*strideY+2);varlowerByte=Vector256.Create(0xFFU);[MethodImpl(MethodImplOptions.AggressiveInlining)]unsafevoidFillVectorsFromPixels(refbytesrc,refVector256<uint>data0,refVector256<uint>data1,refVector256<uint>data2){varsrc_ptr=(uint*)Unsafe.AsPointer(refsrc);data0=Avx2.GatherVector256(src_ptr,idx0,1);data1=Avx2.GatherVector256(src_ptr,idx1,1);data2=Avx2.GatherVector256(src_ptr,idx2,1);data0=Avx2.And(data0,lowerByte);data1=Avx2.And(data1,lowerByte);data2=Avx2.And(data2,lowerByte);}Span<Vector256<uint>>stack0=stackallocVector256<uint>[(int)radius*2+1];Span<Vector256<uint>>stack1=stackallocVector256<uint>[(int)radius*2+1];Span<Vector256<uint>>stack2=stackallocVector256<uint>[(int)radius*2+1];for(inty=0;y<=areaMaxY;y+=8){varsum_in0=Vector256<uint>.Zero;varsum_in1=Vector256<uint>.Zero;varsum_in2=Vector256<uint>.Zero;vardata0=Vector256<uint>.Zero;vardata1=Vector256<uint>.Zero;vardata2=Vector256<uint>.Zero;refvarsrc_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// start of line (0,y)FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);varsum0=Vector256.Multiply(data0,initialSum);varsum1=Vector256.Multiply(data1,initialSum);varsum2=Vector256.Multiply(data2,initialSum);varsum_out0=Vector256.Multiply(data0,radius+1);varsum_out1=Vector256.Multiply(data1,radius+1);varsum_out2=Vector256.Multiply(data2,radius+1);foreach(refvarstack_ptrinstack0.Slice(0,(int)radius+1)){stack_ptr=data0;}foreach(refvarstack_ptrinstack1.Slice(0,(int)radius+1)){stack_ptr=data1;}foreach(refvarstack_ptrinstack2.Slice(0,(int)radius+1)){stack_ptr=data2;}for(inti=1;i<=radius;i++){if(i<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);}refvarstack_ptr=refstack0[(int)(i+radius)];stack_ptr=data0;stack_ptr=refstack1[(int)(i+radius)];stack_ptr=data1;stack_ptr=refstack2[(int)(i+radius)];stack_ptr=data2;sum0+=data0*(uint)(radius+1-i);sum1+=data1*(uint)(radius+1-i);sum2+=data2*(uint)(radius+1-i);sum_in0+=data0;sum_in1+=data1;sum_in2+=data2;}intsp=(int)radius;intxp=(int)radius;if(xp>areaMaxX)xp=areaMaxX;src_ptr=refUnsafe.Add(refsourcePtr,xp*strideX+y*strideY);refvardst_ptr=refUnsafe.Add(refsourcePtr,y*strideY);for(intx=0;x<=areaMaxX;x++){varblurredPixel0=Vector256.ShiftRightLogical(Vector256.Multiply(sum0,mul_sum),shr_sum);varblurredPixel1=Vector256.ShiftRightLogical(Vector256.Multiply(sum1,mul_sum),shr_sum);varblurredPixel2=Vector256.ShiftRightLogical(Vector256.Multiply(sum2,mul_sum),shr_sum);
    #if NET8_0_OR_GREATERConsole.WriteLine(Unsafe.Add(refdst_ptr,3).ToString("X"));// should print FF
    #endif
    Unsafe.Add(refdst_ptr,0)=(byte)blurredPixel0[0];// bug, should only write a single byte
    #if NET8_0_OR_GREATERConsole.WriteLine(Unsafe.Add(refdst_ptr,3).ToString("X"));// should print FF, not 00
    #endif
    Unsafe.Add(refdst_ptr,1)=(byte)blurredPixel0[1];Unsafe.Add(refdst_ptr,2)=(byte)blurredPixel0[2];Unsafe.Add(refdst_ptr,0+strideY)=(byte)blurredPixel0[3];Unsafe.Add(refdst_ptr,1+strideY)=(byte)blurredPixel0[4];Unsafe.Add(refdst_ptr,2+strideY)=(byte)blurredPixel0[5];Unsafe.Add(refdst_ptr,0+strideY*2)=(byte)blurredPixel0[6];Unsafe.Add(refdst_ptr,1+strideY*2)=(byte)blurredPixel0[7];Unsafe.Add(refdst_ptr,2+strideY*2)=(byte)blurredPixel1[0];Unsafe.Add(refdst_ptr,0+strideY*3)=(byte)blurredPixel1[1];Unsafe.Add(refdst_ptr,1+strideY*3)=(byte)blurredPixel1[2];Unsafe.Add(refdst_ptr,2+strideY*3)=(byte)blurredPixel1[3];Unsafe.Add(refdst_ptr,0+strideY*4)=(byte)blurredPixel1[4];Unsafe.Add(refdst_ptr,1+strideY*4)=(byte)blurredPixel1[5];Unsafe.Add(refdst_ptr,2+strideY*4)=(byte)blurredPixel1[6];Unsafe.Add(refdst_ptr,0+strideY*5)=(byte)blurredPixel1[7];Unsafe.Add(refdst_ptr,1+strideY*5)=(byte)blurredPixel2[0];Unsafe.Add(refdst_ptr,2+strideY*5)=(byte)blurredPixel2[1];Unsafe.Add(refdst_ptr,0+strideY*6)=(byte)blurredPixel2[2];Unsafe.Add(refdst_ptr,1+strideY*6)=(byte)blurredPixel2[3];Unsafe.Add(refdst_ptr,2+strideY*6)=(byte)blurredPixel2[4];Unsafe.Add(refdst_ptr,0+strideY*7)=(byte)blurredPixel2[5];Unsafe.Add(refdst_ptr,1+strideY*7)=(byte)blurredPixel2[6];Unsafe.Add(refdst_ptr,2+strideY*7)=(byte)blurredPixel2[7];dst_ptr=refUnsafe.Add(refdst_ptr,strideX);sum0-=sum_out0;sum1-=sum_out1;sum2-=sum_out2;intstack_start=sp+(int)radius+1;if(stack_start>=stack0.Length)stack_start-=stack0.Length;refvarstack_ptr0=refstack0[stack_start];sum_out0-=stack_ptr0;refvarstack_ptr1=refstack1[stack_start];sum_out1-=stack_ptr1;refvarstack_ptr2=refstack2[stack_start];sum_out2-=stack_ptr2;if(xp<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);++xp;}stack_ptr0=data0;sum_in0+=data0;sum0+=sum_in0;stack_ptr1=data1;sum_in1+=data1;sum1+=sum_in1;stack_ptr2=data2;sum_in2+=data2;sum2+=sum_in2;++sp;if(sp>=stack0.Length)sp=0;stack_ptr0=refstack0[sp];sum_out0+=stack_ptr0;sum_in0-=stack_ptr0;stack_ptr1=refstack1[sp];sum_out1+=stack_ptr1;sum_in1-=stack_ptr1;stack_ptr2=refstack2[sp];sum_out2+=stack_ptr2;sum_in2-=stack_ptr2;}}}else{[MethodImpl(MethodImplOptions.AggressiveInlining)]staticvoidFillVectorFromPixel(refVector128<uint>data,refbytesrc_ptr){data=Vector128.Create((uint)src_ptr,(uint)Unsafe.Add(refsrc_ptr,1),(uint)Unsafe.Add(refsrc_ptr,2),(uint)Unsafe.Add(refsrc_ptr,3));}Span<Vector128<uint>>stack=stackallocVector128<uint>[(int)radius*2+1];for(inty=0;y<=areaMaxY;y++){varsum_in=Vector128<uint>.Zero;refbytesrc_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// start of line (0,y)Vector128<uint>data=Vector128<uint>.Zero;FillVectorFromPixel(refdata,refsrc_ptr);varsum=Vector128.Multiply(data,initialSum);varsum_out=Vector128.Multiply(data,radius+1);foreach(refvarstack_ptrinstack.Slice(0,(int)radius+1)){stack_ptr=data;}for(inti=1;i<=radius;i++){if(i<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorFromPixel(refdata,refsrc_ptr);}refvarstack_ptr=refstack[(int)(i+radius)];stack_ptr=data;sum+=data*(uint)(radius+1-i);sum_in+=data;}intsp=(int)radius;intxp=(int)radius;if(xp>areaMaxX)xp=areaMaxX;src_ptr=refUnsafe.Add(refsourcePtr,xp*strideX+y*strideY);// img.pix_ptr(xp, y);refbytedst_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// img.pix_ptr(0, y);for(intx=0;x<=areaMaxX;x++){varblurredPixel=Vector128.ShiftRightLogical(Vector128.Multiply(sum,mul_sum),shr_sum);dst_ptr=(byte)blurredPixel[0];Unsafe.Add(refdst_ptr,1)=(byte)blurredPixel[1];Unsafe.Add(refdst_ptr,2)=(byte)blurredPixel[2];Unsafe.Add(refdst_ptr,3)=(byte)blurredPixel[3];dst_ptr=refUnsafe.Add(refdst_ptr,strideX);sum-=sum_out;intstack_start=sp+(int)radius+1;if(stack_start>=stack.Length)stack_start-=stack.Length;refvarstack_ptr=refstack[stack_start];sum_out-=stack_ptr;if(xp<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorFromPixel(refdata,refsrc_ptr);++xp;}stack_ptr=data;sum_in+=data;sum+=sum_in;++sp;if(sp>=stack.Length)sp=0;stack_ptr=refstack[sp];sum_out+=stack_ptr;sum_in-=stack_ptr;}}}}staticintSumNumbers(intn){returnn*(n+1)/2;// fast way of calculating range (0..n).Sum()}/// Stackblur algorithm by Mario Klingemann/// Details here:/// http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html/// C++ implemenation base from:/// https://gist.github.com/benjamin9999/3809142/// http://www.antigrain.com/__code/include/agg_blur.h.html/// This version works only with RGBA colorstaticvoidStackBlur(Span<byte>src,///< input image dataintw,///< image widthinth,///< image heightintrowStride,uintradius///< blur intensity (should be in 2..254 range)){vararea=Vector128.Create(0,0,w,h);StackBlur(src,w,h,rowStride,radius,area);}staticvoidStackBlur(Span<byte>src,///< input image dataintw,///< image widthinth,///< image heightintrowStride,uintradius,///< blur intensity (should be in 2..254 range)Vector128<int>area){if(radius>254)return;if(radius<1)return;StackblurPass(src,w,h,rowStride,radius,vertical:false,area);StackblurPass(src,w,h,rowStride,radius,vertical:true,area);}}}

    Metadata

    Metadata

    Assignees

    Labels

    area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
      Skip to content

      Compiler overwrites wrong memory location in .NET 8 RC1 when using Unsafe.Add and Vector256<uint>[0] #92590

      Description

      @jwdj

      Description

      In a release build with .NET 8 RC1 the compiler overwrites memory it shouldn't touch.

      Input is a Span<byte> of pixels with layout RGBA, where every fourth byte is an alpha byte set to 255 (index 3, 7, 11, etc).
      The code only overwrites the RGB bytes (indexes 0, 1, 2, 4, 5, 6, 8, etc), but the compiler overwrites 3, 7, 11, etc too (with 0).
      This overwrite happens when a ref byte (3 bytes earlier) is overwritten with the first element of a Vector256

      Unsafe.Add(refdst_ptr,0)=(byte)blurredPixel0[0];// blurredPixel0 is a Vector256<uint> dst_ptr is ref byte// byte at dst_ptr + 3 is now overwritten with 0

      Below is this line in more context. I have tried to shorten the example code but the bug is very specific and is Vector256 related.

      Configuration

      • .NET 8.0 RC1
      • OS: Windows 10
      • Only tested with x64

      Regression?

      Yes, compared to .NET 7.0

      Other information

      The following configurations work fine:
      dotnet run StackBlur --framework net7.0 --configuration Release
      dotnet run StackBlur --framework net7.0 --configuration Debug
      dotnet run StackBlur --framework net8.0 --configuration Debug

      To reproduce the error (from PowerShell):
      $env:DOTNET_JitDisasm = 'StackblurPass'
      dotnet run StackBlur --framework net8.0 --configuration Release

      Here the project code StackBlur.csproj:

      <ProjectSdk="Microsoft.NET.Sdk">
      <PropertyGroup>
      <OutputType>Exe</OutputType>
      <TargetFrameworks>net7.0;net8.0</TargetFrameworks>
      <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup>
      </Project>

      And the source code Program.cs:
      The program is rather long but scroll down to where it is not indented to find the issue.

      usingSystem;usingSystem.Numerics;usingSystem.Runtime.Intrinsics;usingSystem.Runtime.Intrinsics.X86;usingSystem.Runtime.CompilerServices;usingSystem.Runtime.InteropServices;usingSystem.Diagnostics;namespaceApplication{classProgram{staticvoidMain(string[]args){Console.WriteLine(Environment.Version);constintWidth=1920;constintHeight=1080;Span<byte>pixels=newbyte[Width*Height*sizeof(uint)];for(inti=3;i<pixels.Length;i+=4){pixels[i]=255;// fill alpha channel}pixels.Fill(255);vararea=Vector128.Create(0,0,384,384);StackBlur(pixels,Width,Height,Width*sizeof(uint),radius:100,area);for(inti=3;i<pixels.Length;i+=4){if(pixels[i]!=255){Console.WriteLine("FAILED");return;}}}staticushort[]stackblur_mul=newushort[255]{512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259};staticushort[]stackblur_shr=newushort[255]{9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24};//[MethodImpl(MethodImplOptions.NoOptimization)] // also 'fixes' the issuestaticvoidStackblurPass(Span<byte>src,///< input image dataintwidth,///< image widthintheight,///< image heightintrowStride,uintradius,///< blur intensity (should be in 2..254 range)boolvertical,Vector128<int>area){uintmul_sum=stackblur_mul[radius];intshr_sum=stackblur_shr[radius];varareaX=area[0];varareaY=area[1];varareaWidth=area[2];varareaHeight=area[3];intstrideX=4;intstrideY=rowStride;intareaMinX=areaX>=0?areaX:0;intareaMinY=areaY>=0?areaY:0;intmaxX=width-1;intmaxY=height-1;intareaMaxX=areaX+areaWidth-1;intareaMaxY=areaY+areaHeight-1;if(areaMaxX>maxX)areaMaxX=maxX;if(areaMaxY>maxY)areaMaxY=maxY;areaMaxX-=areaMinX;areaMaxY-=areaMinY;if(vertical){// transpose (swap X and Y)(maxX,maxY)=(maxY,maxX);(strideX,strideY)=(strideY,strideX);(areaMinX,areaMinY)=(areaMinY,areaMinX);(areaMaxX,areaMaxY)=(areaMaxY,areaMaxX);}uintinitialSum=(uint)SumNumbers((int)radius+1);refvarsourcePtr=refsrc[areaMinX*strideX+areaMinY*strideY];if(Avx2.IsSupported&&Vector256.IsHardwareAccelerated){varidx0=Vector256.Create(0,1,2,1*strideY,1*strideY+1,1*strideY+2,2*strideY,2*strideY+1);varidx1=Vector256.Create(2*strideY+2,3*strideY,3*strideY+1,3*strideY+2,4*strideY,4*strideY+1,4*strideY+2,5*strideY);varidx2=Vector256.Create(5*strideY+1,5*strideY+2,6*strideY,6*strideY+1,6*strideY+2,7*strideY,7*strideY+1,7*strideY+2);varlowerByte=Vector256.Create(0xFFU);[MethodImpl(MethodImplOptions.AggressiveInlining)]unsafevoidFillVectorsFromPixels(refbytesrc,refVector256<uint>data0,refVector256<uint>data1,refVector256<uint>data2){varsrc_ptr=(uint*)Unsafe.AsPointer(refsrc);data0=Avx2.GatherVector256(src_ptr,idx0,1);data1=Avx2.GatherVector256(src_ptr,idx1,1);data2=Avx2.GatherVector256(src_ptr,idx2,1);data0=Avx2.And(data0,lowerByte);data1=Avx2.And(data1,lowerByte);data2=Avx2.And(data2,lowerByte);}Span<Vector256<uint>>stack0=stackallocVector256<uint>[(int)radius*2+1];Span<Vector256<uint>>stack1=stackallocVector256<uint>[(int)radius*2+1];Span<Vector256<uint>>stack2=stackallocVector256<uint>[(int)radius*2+1];for(inty=0;y<=areaMaxY;y+=8){varsum_in0=Vector256<uint>.Zero;varsum_in1=Vector256<uint>.Zero;varsum_in2=Vector256<uint>.Zero;vardata0=Vector256<uint>.Zero;vardata1=Vector256<uint>.Zero;vardata2=Vector256<uint>.Zero;refvarsrc_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// start of line (0,y)FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);varsum0=Vector256.Multiply(data0,initialSum);varsum1=Vector256.Multiply(data1,initialSum);varsum2=Vector256.Multiply(data2,initialSum);varsum_out0=Vector256.Multiply(data0,radius+1);varsum_out1=Vector256.Multiply(data1,radius+1);varsum_out2=Vector256.Multiply(data2,radius+1);foreach(refvarstack_ptrinstack0.Slice(0,(int)radius+1)){stack_ptr=data0;}foreach(refvarstack_ptrinstack1.Slice(0,(int)radius+1)){stack_ptr=data1;}foreach(refvarstack_ptrinstack2.Slice(0,(int)radius+1)){stack_ptr=data2;}for(inti=1;i<=radius;i++){if(i<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);}refvarstack_ptr=refstack0[(int)(i+radius)];stack_ptr=data0;stack_ptr=refstack1[(int)(i+radius)];stack_ptr=data1;stack_ptr=refstack2[(int)(i+radius)];stack_ptr=data2;sum0+=data0*(uint)(radius+1-i);sum1+=data1*(uint)(radius+1-i);sum2+=data2*(uint)(radius+1-i);sum_in0+=data0;sum_in1+=data1;sum_in2+=data2;}intsp=(int)radius;intxp=(int)radius;if(xp>areaMaxX)xp=areaMaxX;src_ptr=refUnsafe.Add(refsourcePtr,xp*strideX+y*strideY);refvardst_ptr=refUnsafe.Add(refsourcePtr,y*strideY);for(intx=0;x<=areaMaxX;x++){varblurredPixel0=Vector256.ShiftRightLogical(Vector256.Multiply(sum0,mul_sum),shr_sum);varblurredPixel1=Vector256.ShiftRightLogical(Vector256.Multiply(sum1,mul_sum),shr_sum);varblurredPixel2=Vector256.ShiftRightLogical(Vector256.Multiply(sum2,mul_sum),shr_sum);
      #if NET8_0_OR_GREATERConsole.WriteLine(Unsafe.Add(refdst_ptr,3).ToString("X"));// should print FF
      #endif
      Unsafe.Add(refdst_ptr,0)=(byte)blurredPixel0[0];// bug, should only write a single byte
      #if NET8_0_OR_GREATERConsole.WriteLine(Unsafe.Add(refdst_ptr,3).ToString("X"));// should print FF, not 00
      #endif
      Unsafe.Add(refdst_ptr,1)=(byte)blurredPixel0[1];Unsafe.Add(refdst_ptr,2)=(byte)blurredPixel0[2];Unsafe.Add(refdst_ptr,0+strideY)=(byte)blurredPixel0[3];Unsafe.Add(refdst_ptr,1+strideY)=(byte)blurredPixel0[4];Unsafe.Add(refdst_ptr,2+strideY)=(byte)blurredPixel0[5];Unsafe.Add(refdst_ptr,0+strideY*2)=(byte)blurredPixel0[6];Unsafe.Add(refdst_ptr,1+strideY*2)=(byte)blurredPixel0[7];Unsafe.Add(refdst_ptr,2+strideY*2)=(byte)blurredPixel1[0];Unsafe.Add(refdst_ptr,0+strideY*3)=(byte)blurredPixel1[1];Unsafe.Add(refdst_ptr,1+strideY*3)=(byte)blurredPixel1[2];Unsafe.Add(refdst_ptr,2+strideY*3)=(byte)blurredPixel1[3];Unsafe.Add(refdst_ptr,0+strideY*4)=(byte)blurredPixel1[4];Unsafe.Add(refdst_ptr,1+strideY*4)=(byte)blurredPixel1[5];Unsafe.Add(refdst_ptr,2+strideY*4)=(byte)blurredPixel1[6];Unsafe.Add(refdst_ptr,0+strideY*5)=(byte)blurredPixel1[7];Unsafe.Add(refdst_ptr,1+strideY*5)=(byte)blurredPixel2[0];Unsafe.Add(refdst_ptr,2+strideY*5)=(byte)blurredPixel2[1];Unsafe.Add(refdst_ptr,0+strideY*6)=(byte)blurredPixel2[2];Unsafe.Add(refdst_ptr,1+strideY*6)=(byte)blurredPixel2[3];Unsafe.Add(refdst_ptr,2+strideY*6)=(byte)blurredPixel2[4];Unsafe.Add(refdst_ptr,0+strideY*7)=(byte)blurredPixel2[5];Unsafe.Add(refdst_ptr,1+strideY*7)=(byte)blurredPixel2[6];Unsafe.Add(refdst_ptr,2+strideY*7)=(byte)blurredPixel2[7];dst_ptr=refUnsafe.Add(refdst_ptr,strideX);sum0-=sum_out0;sum1-=sum_out1;sum2-=sum_out2;intstack_start=sp+(int)radius+1;if(stack_start>=stack0.Length)stack_start-=stack0.Length;refvarstack_ptr0=refstack0[stack_start];sum_out0-=stack_ptr0;refvarstack_ptr1=refstack1[stack_start];sum_out1-=stack_ptr1;refvarstack_ptr2=refstack2[stack_start];sum_out2-=stack_ptr2;if(xp<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);++xp;}stack_ptr0=data0;sum_in0+=data0;sum0+=sum_in0;stack_ptr1=data1;sum_in1+=data1;sum1+=sum_in1;stack_ptr2=data2;sum_in2+=data2;sum2+=sum_in2;++sp;if(sp>=stack0.Length)sp=0;stack_ptr0=refstack0[sp];sum_out0+=stack_ptr0;sum_in0-=stack_ptr0;stack_ptr1=refstack1[sp];sum_out1+=stack_ptr1;sum_in1-=stack_ptr1;stack_ptr2=refstack2[sp];sum_out2+=stack_ptr2;sum_in2-=stack_ptr2;}}}else{[MethodImpl(MethodImplOptions.AggressiveInlining)]staticvoidFillVectorFromPixel(refVector128<uint>data,refbytesrc_ptr){data=Vector128.Create((uint)src_ptr,(uint)Unsafe.Add(refsrc_ptr,1),(uint)Unsafe.Add(refsrc_ptr,2),(uint)Unsafe.Add(refsrc_ptr,3));}Span<Vector128<uint>>stack=stackallocVector128<uint>[(int)radius*2+1];for(inty=0;y<=areaMaxY;y++){varsum_in=Vector128<uint>.Zero;refbytesrc_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// start of line (0,y)Vector128<uint>data=Vector128<uint>.Zero;FillVectorFromPixel(refdata,refsrc_ptr);varsum=Vector128.Multiply(data,initialSum);varsum_out=Vector128.Multiply(data,radius+1);foreach(refvarstack_ptrinstack.Slice(0,(int)radius+1)){stack_ptr=data;}for(inti=1;i<=radius;i++){if(i<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorFromPixel(refdata,refsrc_ptr);}refvarstack_ptr=refstack[(int)(i+radius)];stack_ptr=data;sum+=data*(uint)(radius+1-i);sum_in+=data;}intsp=(int)radius;intxp=(int)radius;if(xp>areaMaxX)xp=areaMaxX;src_ptr=refUnsafe.Add(refsourcePtr,xp*strideX+y*strideY);// img.pix_ptr(xp, y);refbytedst_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// img.pix_ptr(0, y);for(intx=0;x<=areaMaxX;x++){varblurredPixel=Vector128.ShiftRightLogical(Vector128.Multiply(sum,mul_sum),shr_sum);dst_ptr=(byte)blurredPixel[0];Unsafe.Add(refdst_ptr,1)=(byte)blurredPixel[1];Unsafe.Add(refdst_ptr,2)=(byte)blurredPixel[2];Unsafe.Add(refdst_ptr,3)=(byte)blurredPixel[3];dst_ptr=refUnsafe.Add(refdst_ptr,strideX);sum-=sum_out;intstack_start=sp+(int)radius+1;if(stack_start>=stack.Length)stack_start-=stack.Length;refvarstack_ptr=refstack[stack_start];sum_out-=stack_ptr;if(xp<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorFromPixel(refdata,refsrc_ptr);++xp;}stack_ptr=data;sum_in+=data;sum+=sum_in;++sp;if(sp>=stack.Length)sp=0;stack_ptr=refstack[sp];sum_out+=stack_ptr;sum_in-=stack_ptr;}}}}staticintSumNumbers(intn){returnn*(n+1)/2;// fast way of calculating range (0..n).Sum()}/// Stackblur algorithm by Mario Klingemann/// Details here:/// http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html/// C++ implemenation base from:/// https://gist.github.com/benjamin9999/3809142/// http://www.antigrain.com/__code/include/agg_blur.h.html/// This version works only with RGBA colorstaticvoidStackBlur(Span<byte>src,///< input image dataintw,///< image widthinth,///< image heightintrowStride,uintradius///< blur intensity (should be in 2..254 range)){vararea=Vector128.Create(0,0,w,h);StackBlur(src,w,h,rowStride,radius,area);}staticvoidStackBlur(Span<byte>src,///< input image dataintw,///< image widthinth,///< image heightintrowStride,uintradius,///< blur intensity (should be in 2..254 range)Vector128<int>area){if(radius>254)return;if(radius<1)return;StackblurPass(src,w,h,rowStride,radius,vertical:false,area);StackblurPass(src,w,h,rowStride,radius,vertical:true,area);}}}

      Metadata

      Metadata

      Assignees

      Labels

      area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

      Type

      No type

      Projects

      No projects

        Milestone

        Relationships

        None yet

        Development

        No branches or pull requests

        Issue actions

        , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
        Skip to content

        Compiler overwrites wrong memory location in .NET 8 RC1 when using Unsafe.Add and Vector256<uint>[0] #92590

        Description

        @jwdj

        Description

        In a release build with .NET 8 RC1 the compiler overwrites memory it shouldn't touch.

        Input is a Span<byte> of pixels with layout RGBA, where every fourth byte is an alpha byte set to 255 (index 3, 7, 11, etc).
        The code only overwrites the RGB bytes (indexes 0, 1, 2, 4, 5, 6, 8, etc), but the compiler overwrites 3, 7, 11, etc too (with 0).
        This overwrite happens when a ref byte (3 bytes earlier) is overwritten with the first element of a Vector256

        Unsafe.Add(refdst_ptr,0)=(byte)blurredPixel0[0];// blurredPixel0 is a Vector256<uint> dst_ptr is ref byte// byte at dst_ptr + 3 is now overwritten with 0

        Below is this line in more context. I have tried to shorten the example code but the bug is very specific and is Vector256 related.

        Configuration

        • .NET 8.0 RC1
        • OS: Windows 10
        • Only tested with x64

        Regression?

        Yes, compared to .NET 7.0

        Other information

        The following configurations work fine:
        dotnet run StackBlur --framework net7.0 --configuration Release
        dotnet run StackBlur --framework net7.0 --configuration Debug
        dotnet run StackBlur --framework net8.0 --configuration Debug

        To reproduce the error (from PowerShell):
        $env:DOTNET_JitDisasm = 'StackblurPass'
        dotnet run StackBlur --framework net8.0 --configuration Release

        Here the project code StackBlur.csproj:

        <ProjectSdk="Microsoft.NET.Sdk">
        <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFrameworks>net7.0;net8.0</TargetFrameworks>
        <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup>
        </Project>

        And the source code Program.cs:
        The program is rather long but scroll down to where it is not indented to find the issue.

        usingSystem;usingSystem.Numerics;usingSystem.Runtime.Intrinsics;usingSystem.Runtime.Intrinsics.X86;usingSystem.Runtime.CompilerServices;usingSystem.Runtime.InteropServices;usingSystem.Diagnostics;namespaceApplication{classProgram{staticvoidMain(string[]args){Console.WriteLine(Environment.Version);constintWidth=1920;constintHeight=1080;Span<byte>pixels=newbyte[Width*Height*sizeof(uint)];for(inti=3;i<pixels.Length;i+=4){pixels[i]=255;// fill alpha channel}pixels.Fill(255);vararea=Vector128.Create(0,0,384,384);StackBlur(pixels,Width,Height,Width*sizeof(uint),radius:100,area);for(inti=3;i<pixels.Length;i+=4){if(pixels[i]!=255){Console.WriteLine("FAILED");return;}}}staticushort[]stackblur_mul=newushort[255]{512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259};staticushort[]stackblur_shr=newushort[255]{9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24};//[MethodImpl(MethodImplOptions.NoOptimization)] // also 'fixes' the issuestaticvoidStackblurPass(Span<byte>src,///< input image dataintwidth,///< image widthintheight,///< image heightintrowStride,uintradius,///< blur intensity (should be in 2..254 range)boolvertical,Vector128<int>area){uintmul_sum=stackblur_mul[radius];intshr_sum=stackblur_shr[radius];varareaX=area[0];varareaY=area[1];varareaWidth=area[2];varareaHeight=area[3];intstrideX=4;intstrideY=rowStride;intareaMinX=areaX>=0?areaX:0;intareaMinY=areaY>=0?areaY:0;intmaxX=width-1;intmaxY=height-1;intareaMaxX=areaX+areaWidth-1;intareaMaxY=areaY+areaHeight-1;if(areaMaxX>maxX)areaMaxX=maxX;if(areaMaxY>maxY)areaMaxY=maxY;areaMaxX-=areaMinX;areaMaxY-=areaMinY;if(vertical){// transpose (swap X and Y)(maxX,maxY)=(maxY,maxX);(strideX,strideY)=(strideY,strideX);(areaMinX,areaMinY)=(areaMinY,areaMinX);(areaMaxX,areaMaxY)=(areaMaxY,areaMaxX);}uintinitialSum=(uint)SumNumbers((int)radius+1);refvarsourcePtr=refsrc[areaMinX*strideX+areaMinY*strideY];if(Avx2.IsSupported&&Vector256.IsHardwareAccelerated){varidx0=Vector256.Create(0,1,2,1*strideY,1*strideY+1,1*strideY+2,2*strideY,2*strideY+1);varidx1=Vector256.Create(2*strideY+2,3*strideY,3*strideY+1,3*strideY+2,4*strideY,4*strideY+1,4*strideY+2,5*strideY);varidx2=Vector256.Create(5*strideY+1,5*strideY+2,6*strideY,6*strideY+1,6*strideY+2,7*strideY,7*strideY+1,7*strideY+2);varlowerByte=Vector256.Create(0xFFU);[MethodImpl(MethodImplOptions.AggressiveInlining)]unsafevoidFillVectorsFromPixels(refbytesrc,refVector256<uint>data0,refVector256<uint>data1,refVector256<uint>data2){varsrc_ptr=(uint*)Unsafe.AsPointer(refsrc);data0=Avx2.GatherVector256(src_ptr,idx0,1);data1=Avx2.GatherVector256(src_ptr,idx1,1);data2=Avx2.GatherVector256(src_ptr,idx2,1);data0=Avx2.And(data0,lowerByte);data1=Avx2.And(data1,lowerByte);data2=Avx2.And(data2,lowerByte);}Span<Vector256<uint>>stack0=stackallocVector256<uint>[(int)radius*2+1];Span<Vector256<uint>>stack1=stackallocVector256<uint>[(int)radius*2+1];Span<Vector256<uint>>stack2=stackallocVector256<uint>[(int)radius*2+1];for(inty=0;y<=areaMaxY;y+=8){varsum_in0=Vector256<uint>.Zero;varsum_in1=Vector256<uint>.Zero;varsum_in2=Vector256<uint>.Zero;vardata0=Vector256<uint>.Zero;vardata1=Vector256<uint>.Zero;vardata2=Vector256<uint>.Zero;refvarsrc_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// start of line (0,y)FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);varsum0=Vector256.Multiply(data0,initialSum);varsum1=Vector256.Multiply(data1,initialSum);varsum2=Vector256.Multiply(data2,initialSum);varsum_out0=Vector256.Multiply(data0,radius+1);varsum_out1=Vector256.Multiply(data1,radius+1);varsum_out2=Vector256.Multiply(data2,radius+1);foreach(refvarstack_ptrinstack0.Slice(0,(int)radius+1)){stack_ptr=data0;}foreach(refvarstack_ptrinstack1.Slice(0,(int)radius+1)){stack_ptr=data1;}foreach(refvarstack_ptrinstack2.Slice(0,(int)radius+1)){stack_ptr=data2;}for(inti=1;i<=radius;i++){if(i<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);}refvarstack_ptr=refstack0[(int)(i+radius)];stack_ptr=data0;stack_ptr=refstack1[(int)(i+radius)];stack_ptr=data1;stack_ptr=refstack2[(int)(i+radius)];stack_ptr=data2;sum0+=data0*(uint)(radius+1-i);sum1+=data1*(uint)(radius+1-i);sum2+=data2*(uint)(radius+1-i);sum_in0+=data0;sum_in1+=data1;sum_in2+=data2;}intsp=(int)radius;intxp=(int)radius;if(xp>areaMaxX)xp=areaMaxX;src_ptr=refUnsafe.Add(refsourcePtr,xp*strideX+y*strideY);refvardst_ptr=refUnsafe.Add(refsourcePtr,y*strideY);for(intx=0;x<=areaMaxX;x++){varblurredPixel0=Vector256.ShiftRightLogical(Vector256.Multiply(sum0,mul_sum),shr_sum);varblurredPixel1=Vector256.ShiftRightLogical(Vector256.Multiply(sum1,mul_sum),shr_sum);varblurredPixel2=Vector256.ShiftRightLogical(Vector256.Multiply(sum2,mul_sum),shr_sum);
        #if NET8_0_OR_GREATERConsole.WriteLine(Unsafe.Add(refdst_ptr,3).ToString("X"));// should print FF
        #endif
        Unsafe.Add(refdst_ptr,0)=(byte)blurredPixel0[0];// bug, should only write a single byte
        #if NET8_0_OR_GREATERConsole.WriteLine(Unsafe.Add(refdst_ptr,3).ToString("X"));// should print FF, not 00
        #endif
        Unsafe.Add(refdst_ptr,1)=(byte)blurredPixel0[1];Unsafe.Add(refdst_ptr,2)=(byte)blurredPixel0[2];Unsafe.Add(refdst_ptr,0+strideY)=(byte)blurredPixel0[3];Unsafe.Add(refdst_ptr,1+strideY)=(byte)blurredPixel0[4];Unsafe.Add(refdst_ptr,2+strideY)=(byte)blurredPixel0[5];Unsafe.Add(refdst_ptr,0+strideY*2)=(byte)blurredPixel0[6];Unsafe.Add(refdst_ptr,1+strideY*2)=(byte)blurredPixel0[7];Unsafe.Add(refdst_ptr,2+strideY*2)=(byte)blurredPixel1[0];Unsafe.Add(refdst_ptr,0+strideY*3)=(byte)blurredPixel1[1];Unsafe.Add(refdst_ptr,1+strideY*3)=(byte)blurredPixel1[2];Unsafe.Add(refdst_ptr,2+strideY*3)=(byte)blurredPixel1[3];Unsafe.Add(refdst_ptr,0+strideY*4)=(byte)blurredPixel1[4];Unsafe.Add(refdst_ptr,1+strideY*4)=(byte)blurredPixel1[5];Unsafe.Add(refdst_ptr,2+strideY*4)=(byte)blurredPixel1[6];Unsafe.Add(refdst_ptr,0+strideY*5)=(byte)blurredPixel1[7];Unsafe.Add(refdst_ptr,1+strideY*5)=(byte)blurredPixel2[0];Unsafe.Add(refdst_ptr,2+strideY*5)=(byte)blurredPixel2[1];Unsafe.Add(refdst_ptr,0+strideY*6)=(byte)blurredPixel2[2];Unsafe.Add(refdst_ptr,1+strideY*6)=(byte)blurredPixel2[3];Unsafe.Add(refdst_ptr,2+strideY*6)=(byte)blurredPixel2[4];Unsafe.Add(refdst_ptr,0+strideY*7)=(byte)blurredPixel2[5];Unsafe.Add(refdst_ptr,1+strideY*7)=(byte)blurredPixel2[6];Unsafe.Add(refdst_ptr,2+strideY*7)=(byte)blurredPixel2[7];dst_ptr=refUnsafe.Add(refdst_ptr,strideX);sum0-=sum_out0;sum1-=sum_out1;sum2-=sum_out2;intstack_start=sp+(int)radius+1;if(stack_start>=stack0.Length)stack_start-=stack0.Length;refvarstack_ptr0=refstack0[stack_start];sum_out0-=stack_ptr0;refvarstack_ptr1=refstack1[stack_start];sum_out1-=stack_ptr1;refvarstack_ptr2=refstack2[stack_start];sum_out2-=stack_ptr2;if(xp<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);++xp;}stack_ptr0=data0;sum_in0+=data0;sum0+=sum_in0;stack_ptr1=data1;sum_in1+=data1;sum1+=sum_in1;stack_ptr2=data2;sum_in2+=data2;sum2+=sum_in2;++sp;if(sp>=stack0.Length)sp=0;stack_ptr0=refstack0[sp];sum_out0+=stack_ptr0;sum_in0-=stack_ptr0;stack_ptr1=refstack1[sp];sum_out1+=stack_ptr1;sum_in1-=stack_ptr1;stack_ptr2=refstack2[sp];sum_out2+=stack_ptr2;sum_in2-=stack_ptr2;}}}else{[MethodImpl(MethodImplOptions.AggressiveInlining)]staticvoidFillVectorFromPixel(refVector128<uint>data,refbytesrc_ptr){data=Vector128.Create((uint)src_ptr,(uint)Unsafe.Add(refsrc_ptr,1),(uint)Unsafe.Add(refsrc_ptr,2),(uint)Unsafe.Add(refsrc_ptr,3));}Span<Vector128<uint>>stack=stackallocVector128<uint>[(int)radius*2+1];for(inty=0;y<=areaMaxY;y++){varsum_in=Vector128<uint>.Zero;refbytesrc_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// start of line (0,y)Vector128<uint>data=Vector128<uint>.Zero;FillVectorFromPixel(refdata,refsrc_ptr);varsum=Vector128.Multiply(data,initialSum);varsum_out=Vector128.Multiply(data,radius+1);foreach(refvarstack_ptrinstack.Slice(0,(int)radius+1)){stack_ptr=data;}for(inti=1;i<=radius;i++){if(i<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorFromPixel(refdata,refsrc_ptr);}refvarstack_ptr=refstack[(int)(i+radius)];stack_ptr=data;sum+=data*(uint)(radius+1-i);sum_in+=data;}intsp=(int)radius;intxp=(int)radius;if(xp>areaMaxX)xp=areaMaxX;src_ptr=refUnsafe.Add(refsourcePtr,xp*strideX+y*strideY);// img.pix_ptr(xp, y);refbytedst_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// img.pix_ptr(0, y);for(intx=0;x<=areaMaxX;x++){varblurredPixel=Vector128.ShiftRightLogical(Vector128.Multiply(sum,mul_sum),shr_sum);dst_ptr=(byte)blurredPixel[0];Unsafe.Add(refdst_ptr,1)=(byte)blurredPixel[1];Unsafe.Add(refdst_ptr,2)=(byte)blurredPixel[2];Unsafe.Add(refdst_ptr,3)=(byte)blurredPixel[3];dst_ptr=refUnsafe.Add(refdst_ptr,strideX);sum-=sum_out;intstack_start=sp+(int)radius+1;if(stack_start>=stack.Length)stack_start-=stack.Length;refvarstack_ptr=refstack[stack_start];sum_out-=stack_ptr;if(xp<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorFromPixel(refdata,refsrc_ptr);++xp;}stack_ptr=data;sum_in+=data;sum+=sum_in;++sp;if(sp>=stack.Length)sp=0;stack_ptr=refstack[sp];sum_out+=stack_ptr;sum_in-=stack_ptr;}}}}staticintSumNumbers(intn){returnn*(n+1)/2;// fast way of calculating range (0..n).Sum()}/// Stackblur algorithm by Mario Klingemann/// Details here:/// http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html/// C++ implemenation base from:/// https://gist.github.com/benjamin9999/3809142/// http://www.antigrain.com/__code/include/agg_blur.h.html/// This version works only with RGBA colorstaticvoidStackBlur(Span<byte>src,///< input image dataintw,///< image widthinth,///< image heightintrowStride,uintradius///< blur intensity (should be in 2..254 range)){vararea=Vector128.Create(0,0,w,h);StackBlur(src,w,h,rowStride,radius,area);}staticvoidStackBlur(Span<byte>src,///< input image dataintw,///< image widthinth,///< image heightintrowStride,uintradius,///< blur intensity (should be in 2..254 range)Vector128<int>area){if(radius>254)return;if(radius<1)return;StackblurPass(src,w,h,rowStride,radius,vertical:false,area);StackblurPass(src,w,h,rowStride,radius,vertical:true,area);}}}

        Metadata

        Metadata

        Assignees

        Labels

        area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

        Type

        No type

        Projects

        No projects

          Milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
          Skip to content

          Compiler overwrites wrong memory location in .NET 8 RC1 when using Unsafe.Add and Vector256<uint>[0] #92590

          Description

          @jwdj

          Description

          In a release build with .NET 8 RC1 the compiler overwrites memory it shouldn't touch.

          Input is a Span<byte> of pixels with layout RGBA, where every fourth byte is an alpha byte set to 255 (index 3, 7, 11, etc).
          The code only overwrites the RGB bytes (indexes 0, 1, 2, 4, 5, 6, 8, etc), but the compiler overwrites 3, 7, 11, etc too (with 0).
          This overwrite happens when a ref byte (3 bytes earlier) is overwritten with the first element of a Vector256

          Unsafe.Add(refdst_ptr,0)=(byte)blurredPixel0[0];// blurredPixel0 is a Vector256<uint> dst_ptr is ref byte// byte at dst_ptr + 3 is now overwritten with 0

          Below is this line in more context. I have tried to shorten the example code but the bug is very specific and is Vector256 related.

          Configuration

          • .NET 8.0 RC1
          • OS: Windows 10
          • Only tested with x64

          Regression?

          Yes, compared to .NET 7.0

          Other information

          The following configurations work fine:
          dotnet run StackBlur --framework net7.0 --configuration Release
          dotnet run StackBlur --framework net7.0 --configuration Debug
          dotnet run StackBlur --framework net8.0 --configuration Debug

          To reproduce the error (from PowerShell):
          $env:DOTNET_JitDisasm = 'StackblurPass'
          dotnet run StackBlur --framework net8.0 --configuration Release

          Here the project code StackBlur.csproj:

          <ProjectSdk="Microsoft.NET.Sdk">
          <PropertyGroup>
          <OutputType>Exe</OutputType>
          <TargetFrameworks>net7.0;net8.0</TargetFrameworks>
          <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup>
          </Project>

          And the source code Program.cs:
          The program is rather long but scroll down to where it is not indented to find the issue.

          usingSystem;usingSystem.Numerics;usingSystem.Runtime.Intrinsics;usingSystem.Runtime.Intrinsics.X86;usingSystem.Runtime.CompilerServices;usingSystem.Runtime.InteropServices;usingSystem.Diagnostics;namespaceApplication{classProgram{staticvoidMain(string[]args){Console.WriteLine(Environment.Version);constintWidth=1920;constintHeight=1080;Span<byte>pixels=newbyte[Width*Height*sizeof(uint)];for(inti=3;i<pixels.Length;i+=4){pixels[i]=255;// fill alpha channel}pixels.Fill(255);vararea=Vector128.Create(0,0,384,384);StackBlur(pixels,Width,Height,Width*sizeof(uint),radius:100,area);for(inti=3;i<pixels.Length;i+=4){if(pixels[i]!=255){Console.WriteLine("FAILED");return;}}}staticushort[]stackblur_mul=newushort[255]{512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259};staticushort[]stackblur_shr=newushort[255]{9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24};//[MethodImpl(MethodImplOptions.NoOptimization)] // also 'fixes' the issuestaticvoidStackblurPass(Span<byte>src,///< input image dataintwidth,///< image widthintheight,///< image heightintrowStride,uintradius,///< blur intensity (should be in 2..254 range)boolvertical,Vector128<int>area){uintmul_sum=stackblur_mul[radius];intshr_sum=stackblur_shr[radius];varareaX=area[0];varareaY=area[1];varareaWidth=area[2];varareaHeight=area[3];intstrideX=4;intstrideY=rowStride;intareaMinX=areaX>=0?areaX:0;intareaMinY=areaY>=0?areaY:0;intmaxX=width-1;intmaxY=height-1;intareaMaxX=areaX+areaWidth-1;intareaMaxY=areaY+areaHeight-1;if(areaMaxX>maxX)areaMaxX=maxX;if(areaMaxY>maxY)areaMaxY=maxY;areaMaxX-=areaMinX;areaMaxY-=areaMinY;if(vertical){// transpose (swap X and Y)(maxX,maxY)=(maxY,maxX);(strideX,strideY)=(strideY,strideX);(areaMinX,areaMinY)=(areaMinY,areaMinX);(areaMaxX,areaMaxY)=(areaMaxY,areaMaxX);}uintinitialSum=(uint)SumNumbers((int)radius+1);refvarsourcePtr=refsrc[areaMinX*strideX+areaMinY*strideY];if(Avx2.IsSupported&&Vector256.IsHardwareAccelerated){varidx0=Vector256.Create(0,1,2,1*strideY,1*strideY+1,1*strideY+2,2*strideY,2*strideY+1);varidx1=Vector256.Create(2*strideY+2,3*strideY,3*strideY+1,3*strideY+2,4*strideY,4*strideY+1,4*strideY+2,5*strideY);varidx2=Vector256.Create(5*strideY+1,5*strideY+2,6*strideY,6*strideY+1,6*strideY+2,7*strideY,7*strideY+1,7*strideY+2);varlowerByte=Vector256.Create(0xFFU);[MethodImpl(MethodImplOptions.AggressiveInlining)]unsafevoidFillVectorsFromPixels(refbytesrc,refVector256<uint>data0,refVector256<uint>data1,refVector256<uint>data2){varsrc_ptr=(uint*)Unsafe.AsPointer(refsrc);data0=Avx2.GatherVector256(src_ptr,idx0,1);data1=Avx2.GatherVector256(src_ptr,idx1,1);data2=Avx2.GatherVector256(src_ptr,idx2,1);data0=Avx2.And(data0,lowerByte);data1=Avx2.And(data1,lowerByte);data2=Avx2.And(data2,lowerByte);}Span<Vector256<uint>>stack0=stackallocVector256<uint>[(int)radius*2+1];Span<Vector256<uint>>stack1=stackallocVector256<uint>[(int)radius*2+1];Span<Vector256<uint>>stack2=stackallocVector256<uint>[(int)radius*2+1];for(inty=0;y<=areaMaxY;y+=8){varsum_in0=Vector256<uint>.Zero;varsum_in1=Vector256<uint>.Zero;varsum_in2=Vector256<uint>.Zero;vardata0=Vector256<uint>.Zero;vardata1=Vector256<uint>.Zero;vardata2=Vector256<uint>.Zero;refvarsrc_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// start of line (0,y)FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);varsum0=Vector256.Multiply(data0,initialSum);varsum1=Vector256.Multiply(data1,initialSum);varsum2=Vector256.Multiply(data2,initialSum);varsum_out0=Vector256.Multiply(data0,radius+1);varsum_out1=Vector256.Multiply(data1,radius+1);varsum_out2=Vector256.Multiply(data2,radius+1);foreach(refvarstack_ptrinstack0.Slice(0,(int)radius+1)){stack_ptr=data0;}foreach(refvarstack_ptrinstack1.Slice(0,(int)radius+1)){stack_ptr=data1;}foreach(refvarstack_ptrinstack2.Slice(0,(int)radius+1)){stack_ptr=data2;}for(inti=1;i<=radius;i++){if(i<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);}refvarstack_ptr=refstack0[(int)(i+radius)];stack_ptr=data0;stack_ptr=refstack1[(int)(i+radius)];stack_ptr=data1;stack_ptr=refstack2[(int)(i+radius)];stack_ptr=data2;sum0+=data0*(uint)(radius+1-i);sum1+=data1*(uint)(radius+1-i);sum2+=data2*(uint)(radius+1-i);sum_in0+=data0;sum_in1+=data1;sum_in2+=data2;}intsp=(int)radius;intxp=(int)radius;if(xp>areaMaxX)xp=areaMaxX;src_ptr=refUnsafe.Add(refsourcePtr,xp*strideX+y*strideY);refvardst_ptr=refUnsafe.Add(refsourcePtr,y*strideY);for(intx=0;x<=areaMaxX;x++){varblurredPixel0=Vector256.ShiftRightLogical(Vector256.Multiply(sum0,mul_sum),shr_sum);varblurredPixel1=Vector256.ShiftRightLogical(Vector256.Multiply(sum1,mul_sum),shr_sum);varblurredPixel2=Vector256.ShiftRightLogical(Vector256.Multiply(sum2,mul_sum),shr_sum);
          #if NET8_0_OR_GREATERConsole.WriteLine(Unsafe.Add(refdst_ptr,3).ToString("X"));// should print FF
          #endif
          Unsafe.Add(refdst_ptr,0)=(byte)blurredPixel0[0];// bug, should only write a single byte
          #if NET8_0_OR_GREATERConsole.WriteLine(Unsafe.Add(refdst_ptr,3).ToString("X"));// should print FF, not 00
          #endif
          Unsafe.Add(refdst_ptr,1)=(byte)blurredPixel0[1];Unsafe.Add(refdst_ptr,2)=(byte)blurredPixel0[2];Unsafe.Add(refdst_ptr,0+strideY)=(byte)blurredPixel0[3];Unsafe.Add(refdst_ptr,1+strideY)=(byte)blurredPixel0[4];Unsafe.Add(refdst_ptr,2+strideY)=(byte)blurredPixel0[5];Unsafe.Add(refdst_ptr,0+strideY*2)=(byte)blurredPixel0[6];Unsafe.Add(refdst_ptr,1+strideY*2)=(byte)blurredPixel0[7];Unsafe.Add(refdst_ptr,2+strideY*2)=(byte)blurredPixel1[0];Unsafe.Add(refdst_ptr,0+strideY*3)=(byte)blurredPixel1[1];Unsafe.Add(refdst_ptr,1+strideY*3)=(byte)blurredPixel1[2];Unsafe.Add(refdst_ptr,2+strideY*3)=(byte)blurredPixel1[3];Unsafe.Add(refdst_ptr,0+strideY*4)=(byte)blurredPixel1[4];Unsafe.Add(refdst_ptr,1+strideY*4)=(byte)blurredPixel1[5];Unsafe.Add(refdst_ptr,2+strideY*4)=(byte)blurredPixel1[6];Unsafe.Add(refdst_ptr,0+strideY*5)=(byte)blurredPixel1[7];Unsafe.Add(refdst_ptr,1+strideY*5)=(byte)blurredPixel2[0];Unsafe.Add(refdst_ptr,2+strideY*5)=(byte)blurredPixel2[1];Unsafe.Add(refdst_ptr,0+strideY*6)=(byte)blurredPixel2[2];Unsafe.Add(refdst_ptr,1+strideY*6)=(byte)blurredPixel2[3];Unsafe.Add(refdst_ptr,2+strideY*6)=(byte)blurredPixel2[4];Unsafe.Add(refdst_ptr,0+strideY*7)=(byte)blurredPixel2[5];Unsafe.Add(refdst_ptr,1+strideY*7)=(byte)blurredPixel2[6];Unsafe.Add(refdst_ptr,2+strideY*7)=(byte)blurredPixel2[7];dst_ptr=refUnsafe.Add(refdst_ptr,strideX);sum0-=sum_out0;sum1-=sum_out1;sum2-=sum_out2;intstack_start=sp+(int)radius+1;if(stack_start>=stack0.Length)stack_start-=stack0.Length;refvarstack_ptr0=refstack0[stack_start];sum_out0-=stack_ptr0;refvarstack_ptr1=refstack1[stack_start];sum_out1-=stack_ptr1;refvarstack_ptr2=refstack2[stack_start];sum_out2-=stack_ptr2;if(xp<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);++xp;}stack_ptr0=data0;sum_in0+=data0;sum0+=sum_in0;stack_ptr1=data1;sum_in1+=data1;sum1+=sum_in1;stack_ptr2=data2;sum_in2+=data2;sum2+=sum_in2;++sp;if(sp>=stack0.Length)sp=0;stack_ptr0=refstack0[sp];sum_out0+=stack_ptr0;sum_in0-=stack_ptr0;stack_ptr1=refstack1[sp];sum_out1+=stack_ptr1;sum_in1-=stack_ptr1;stack_ptr2=refstack2[sp];sum_out2+=stack_ptr2;sum_in2-=stack_ptr2;}}}else{[MethodImpl(MethodImplOptions.AggressiveInlining)]staticvoidFillVectorFromPixel(refVector128<uint>data,refbytesrc_ptr){data=Vector128.Create((uint)src_ptr,(uint)Unsafe.Add(refsrc_ptr,1),(uint)Unsafe.Add(refsrc_ptr,2),(uint)Unsafe.Add(refsrc_ptr,3));}Span<Vector128<uint>>stack=stackallocVector128<uint>[(int)radius*2+1];for(inty=0;y<=areaMaxY;y++){varsum_in=Vector128<uint>.Zero;refbytesrc_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// start of line (0,y)Vector128<uint>data=Vector128<uint>.Zero;FillVectorFromPixel(refdata,refsrc_ptr);varsum=Vector128.Multiply(data,initialSum);varsum_out=Vector128.Multiply(data,radius+1);foreach(refvarstack_ptrinstack.Slice(0,(int)radius+1)){stack_ptr=data;}for(inti=1;i<=radius;i++){if(i<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorFromPixel(refdata,refsrc_ptr);}refvarstack_ptr=refstack[(int)(i+radius)];stack_ptr=data;sum+=data*(uint)(radius+1-i);sum_in+=data;}intsp=(int)radius;intxp=(int)radius;if(xp>areaMaxX)xp=areaMaxX;src_ptr=refUnsafe.Add(refsourcePtr,xp*strideX+y*strideY);// img.pix_ptr(xp, y);refbytedst_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// img.pix_ptr(0, y);for(intx=0;x<=areaMaxX;x++){varblurredPixel=Vector128.ShiftRightLogical(Vector128.Multiply(sum,mul_sum),shr_sum);dst_ptr=(byte)blurredPixel[0];Unsafe.Add(refdst_ptr,1)=(byte)blurredPixel[1];Unsafe.Add(refdst_ptr,2)=(byte)blurredPixel[2];Unsafe.Add(refdst_ptr,3)=(byte)blurredPixel[3];dst_ptr=refUnsafe.Add(refdst_ptr,strideX);sum-=sum_out;intstack_start=sp+(int)radius+1;if(stack_start>=stack.Length)stack_start-=stack.Length;refvarstack_ptr=refstack[stack_start];sum_out-=stack_ptr;if(xp<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorFromPixel(refdata,refsrc_ptr);++xp;}stack_ptr=data;sum_in+=data;sum+=sum_in;++sp;if(sp>=stack.Length)sp=0;stack_ptr=refstack[sp];sum_out+=stack_ptr;sum_in-=stack_ptr;}}}}staticintSumNumbers(intn){returnn*(n+1)/2;// fast way of calculating range (0..n).Sum()}/// Stackblur algorithm by Mario Klingemann/// Details here:/// http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html/// C++ implemenation base from:/// https://gist.github.com/benjamin9999/3809142/// http://www.antigrain.com/__code/include/agg_blur.h.html/// This version works only with RGBA colorstaticvoidStackBlur(Span<byte>src,///< input image dataintw,///< image widthinth,///< image heightintrowStride,uintradius///< blur intensity (should be in 2..254 range)){vararea=Vector128.Create(0,0,w,h);StackBlur(src,w,h,rowStride,radius,area);}staticvoidStackBlur(Span<byte>src,///< input image dataintw,///< image widthinth,///< image heightintrowStride,uintradius,///< blur intensity (should be in 2..254 range)Vector128<int>area){if(radius>254)return;if(radius<1)return;StackblurPass(src,w,h,rowStride,radius,vertical:false,area);StackblurPass(src,w,h,rowStride,radius,vertical:true,area);}}}

          Metadata

          Metadata

          Assignees

          Labels

          area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

          Type

          No type

          Projects

          No projects

            Milestone

            Relationships

            None yet

            Development

            No branches or pull requests

            Issue actions

            , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
            Skip to content

            Compiler overwrites wrong memory location in .NET 8 RC1 when using Unsafe.Add and Vector256<uint>[0] #92590

            Description

            @jwdj

            Description

            In a release build with .NET 8 RC1 the compiler overwrites memory it shouldn't touch.

            Input is a Span<byte> of pixels with layout RGBA, where every fourth byte is an alpha byte set to 255 (index 3, 7, 11, etc).
            The code only overwrites the RGB bytes (indexes 0, 1, 2, 4, 5, 6, 8, etc), but the compiler overwrites 3, 7, 11, etc too (with 0).
            This overwrite happens when a ref byte (3 bytes earlier) is overwritten with the first element of a Vector256

            Unsafe.Add(refdst_ptr,0)=(byte)blurredPixel0[0];// blurredPixel0 is a Vector256<uint> dst_ptr is ref byte// byte at dst_ptr + 3 is now overwritten with 0

            Below is this line in more context. I have tried to shorten the example code but the bug is very specific and is Vector256 related.

            Configuration

            • .NET 8.0 RC1
            • OS: Windows 10
            • Only tested with x64

            Regression?

            Yes, compared to .NET 7.0

            Other information

            The following configurations work fine:
            dotnet run StackBlur --framework net7.0 --configuration Release
            dotnet run StackBlur --framework net7.0 --configuration Debug
            dotnet run StackBlur --framework net8.0 --configuration Debug

            To reproduce the error (from PowerShell):
            $env:DOTNET_JitDisasm = 'StackblurPass'
            dotnet run StackBlur --framework net8.0 --configuration Release

            Here the project code StackBlur.csproj:

            <ProjectSdk="Microsoft.NET.Sdk">
            <PropertyGroup>
            <OutputType>Exe</OutputType>
            <TargetFrameworks>net7.0;net8.0</TargetFrameworks>
            <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup>
            </Project>

            And the source code Program.cs:
            The program is rather long but scroll down to where it is not indented to find the issue.

            usingSystem;usingSystem.Numerics;usingSystem.Runtime.Intrinsics;usingSystem.Runtime.Intrinsics.X86;usingSystem.Runtime.CompilerServices;usingSystem.Runtime.InteropServices;usingSystem.Diagnostics;namespaceApplication{classProgram{staticvoidMain(string[]args){Console.WriteLine(Environment.Version);constintWidth=1920;constintHeight=1080;Span<byte>pixels=newbyte[Width*Height*sizeof(uint)];for(inti=3;i<pixels.Length;i+=4){pixels[i]=255;// fill alpha channel}pixels.Fill(255);vararea=Vector128.Create(0,0,384,384);StackBlur(pixels,Width,Height,Width*sizeof(uint),radius:100,area);for(inti=3;i<pixels.Length;i+=4){if(pixels[i]!=255){Console.WriteLine("FAILED");return;}}}staticushort[]stackblur_mul=newushort[255]{512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259};staticushort[]stackblur_shr=newushort[255]{9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24};//[MethodImpl(MethodImplOptions.NoOptimization)] // also 'fixes' the issuestaticvoidStackblurPass(Span<byte>src,///< input image dataintwidth,///< image widthintheight,///< image heightintrowStride,uintradius,///< blur intensity (should be in 2..254 range)boolvertical,Vector128<int>area){uintmul_sum=stackblur_mul[radius];intshr_sum=stackblur_shr[radius];varareaX=area[0];varareaY=area[1];varareaWidth=area[2];varareaHeight=area[3];intstrideX=4;intstrideY=rowStride;intareaMinX=areaX>=0?areaX:0;intareaMinY=areaY>=0?areaY:0;intmaxX=width-1;intmaxY=height-1;intareaMaxX=areaX+areaWidth-1;intareaMaxY=areaY+areaHeight-1;if(areaMaxX>maxX)areaMaxX=maxX;if(areaMaxY>maxY)areaMaxY=maxY;areaMaxX-=areaMinX;areaMaxY-=areaMinY;if(vertical){// transpose (swap X and Y)(maxX,maxY)=(maxY,maxX);(strideX,strideY)=(strideY,strideX);(areaMinX,areaMinY)=(areaMinY,areaMinX);(areaMaxX,areaMaxY)=(areaMaxY,areaMaxX);}uintinitialSum=(uint)SumNumbers((int)radius+1);refvarsourcePtr=refsrc[areaMinX*strideX+areaMinY*strideY];if(Avx2.IsSupported&&Vector256.IsHardwareAccelerated){varidx0=Vector256.Create(0,1,2,1*strideY,1*strideY+1,1*strideY+2,2*strideY,2*strideY+1);varidx1=Vector256.Create(2*strideY+2,3*strideY,3*strideY+1,3*strideY+2,4*strideY,4*strideY+1,4*strideY+2,5*strideY);varidx2=Vector256.Create(5*strideY+1,5*strideY+2,6*strideY,6*strideY+1,6*strideY+2,7*strideY,7*strideY+1,7*strideY+2);varlowerByte=Vector256.Create(0xFFU);[MethodImpl(MethodImplOptions.AggressiveInlining)]unsafevoidFillVectorsFromPixels(refbytesrc,refVector256<uint>data0,refVector256<uint>data1,refVector256<uint>data2){varsrc_ptr=(uint*)Unsafe.AsPointer(refsrc);data0=Avx2.GatherVector256(src_ptr,idx0,1);data1=Avx2.GatherVector256(src_ptr,idx1,1);data2=Avx2.GatherVector256(src_ptr,idx2,1);data0=Avx2.And(data0,lowerByte);data1=Avx2.And(data1,lowerByte);data2=Avx2.And(data2,lowerByte);}Span<Vector256<uint>>stack0=stackallocVector256<uint>[(int)radius*2+1];Span<Vector256<uint>>stack1=stackallocVector256<uint>[(int)radius*2+1];Span<Vector256<uint>>stack2=stackallocVector256<uint>[(int)radius*2+1];for(inty=0;y<=areaMaxY;y+=8){varsum_in0=Vector256<uint>.Zero;varsum_in1=Vector256<uint>.Zero;varsum_in2=Vector256<uint>.Zero;vardata0=Vector256<uint>.Zero;vardata1=Vector256<uint>.Zero;vardata2=Vector256<uint>.Zero;refvarsrc_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// start of line (0,y)FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);varsum0=Vector256.Multiply(data0,initialSum);varsum1=Vector256.Multiply(data1,initialSum);varsum2=Vector256.Multiply(data2,initialSum);varsum_out0=Vector256.Multiply(data0,radius+1);varsum_out1=Vector256.Multiply(data1,radius+1);varsum_out2=Vector256.Multiply(data2,radius+1);foreach(refvarstack_ptrinstack0.Slice(0,(int)radius+1)){stack_ptr=data0;}foreach(refvarstack_ptrinstack1.Slice(0,(int)radius+1)){stack_ptr=data1;}foreach(refvarstack_ptrinstack2.Slice(0,(int)radius+1)){stack_ptr=data2;}for(inti=1;i<=radius;i++){if(i<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);}refvarstack_ptr=refstack0[(int)(i+radius)];stack_ptr=data0;stack_ptr=refstack1[(int)(i+radius)];stack_ptr=data1;stack_ptr=refstack2[(int)(i+radius)];stack_ptr=data2;sum0+=data0*(uint)(radius+1-i);sum1+=data1*(uint)(radius+1-i);sum2+=data2*(uint)(radius+1-i);sum_in0+=data0;sum_in1+=data1;sum_in2+=data2;}intsp=(int)radius;intxp=(int)radius;if(xp>areaMaxX)xp=areaMaxX;src_ptr=refUnsafe.Add(refsourcePtr,xp*strideX+y*strideY);refvardst_ptr=refUnsafe.Add(refsourcePtr,y*strideY);for(intx=0;x<=areaMaxX;x++){varblurredPixel0=Vector256.ShiftRightLogical(Vector256.Multiply(sum0,mul_sum),shr_sum);varblurredPixel1=Vector256.ShiftRightLogical(Vector256.Multiply(sum1,mul_sum),shr_sum);varblurredPixel2=Vector256.ShiftRightLogical(Vector256.Multiply(sum2,mul_sum),shr_sum);
            #if NET8_0_OR_GREATERConsole.WriteLine(Unsafe.Add(refdst_ptr,3).ToString("X"));// should print FF
            #endif
            Unsafe.Add(refdst_ptr,0)=(byte)blurredPixel0[0];// bug, should only write a single byte
            #if NET8_0_OR_GREATERConsole.WriteLine(Unsafe.Add(refdst_ptr,3).ToString("X"));// should print FF, not 00
            #endif
            Unsafe.Add(refdst_ptr,1)=(byte)blurredPixel0[1];Unsafe.Add(refdst_ptr,2)=(byte)blurredPixel0[2];Unsafe.Add(refdst_ptr,0+strideY)=(byte)blurredPixel0[3];Unsafe.Add(refdst_ptr,1+strideY)=(byte)blurredPixel0[4];Unsafe.Add(refdst_ptr,2+strideY)=(byte)blurredPixel0[5];Unsafe.Add(refdst_ptr,0+strideY*2)=(byte)blurredPixel0[6];Unsafe.Add(refdst_ptr,1+strideY*2)=(byte)blurredPixel0[7];Unsafe.Add(refdst_ptr,2+strideY*2)=(byte)blurredPixel1[0];Unsafe.Add(refdst_ptr,0+strideY*3)=(byte)blurredPixel1[1];Unsafe.Add(refdst_ptr,1+strideY*3)=(byte)blurredPixel1[2];Unsafe.Add(refdst_ptr,2+strideY*3)=(byte)blurredPixel1[3];Unsafe.Add(refdst_ptr,0+strideY*4)=(byte)blurredPixel1[4];Unsafe.Add(refdst_ptr,1+strideY*4)=(byte)blurredPixel1[5];Unsafe.Add(refdst_ptr,2+strideY*4)=(byte)blurredPixel1[6];Unsafe.Add(refdst_ptr,0+strideY*5)=(byte)blurredPixel1[7];Unsafe.Add(refdst_ptr,1+strideY*5)=(byte)blurredPixel2[0];Unsafe.Add(refdst_ptr,2+strideY*5)=(byte)blurredPixel2[1];Unsafe.Add(refdst_ptr,0+strideY*6)=(byte)blurredPixel2[2];Unsafe.Add(refdst_ptr,1+strideY*6)=(byte)blurredPixel2[3];Unsafe.Add(refdst_ptr,2+strideY*6)=(byte)blurredPixel2[4];Unsafe.Add(refdst_ptr,0+strideY*7)=(byte)blurredPixel2[5];Unsafe.Add(refdst_ptr,1+strideY*7)=(byte)blurredPixel2[6];Unsafe.Add(refdst_ptr,2+strideY*7)=(byte)blurredPixel2[7];dst_ptr=refUnsafe.Add(refdst_ptr,strideX);sum0-=sum_out0;sum1-=sum_out1;sum2-=sum_out2;intstack_start=sp+(int)radius+1;if(stack_start>=stack0.Length)stack_start-=stack0.Length;refvarstack_ptr0=refstack0[stack_start];sum_out0-=stack_ptr0;refvarstack_ptr1=refstack1[stack_start];sum_out1-=stack_ptr1;refvarstack_ptr2=refstack2[stack_start];sum_out2-=stack_ptr2;if(xp<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);++xp;}stack_ptr0=data0;sum_in0+=data0;sum0+=sum_in0;stack_ptr1=data1;sum_in1+=data1;sum1+=sum_in1;stack_ptr2=data2;sum_in2+=data2;sum2+=sum_in2;++sp;if(sp>=stack0.Length)sp=0;stack_ptr0=refstack0[sp];sum_out0+=stack_ptr0;sum_in0-=stack_ptr0;stack_ptr1=refstack1[sp];sum_out1+=stack_ptr1;sum_in1-=stack_ptr1;stack_ptr2=refstack2[sp];sum_out2+=stack_ptr2;sum_in2-=stack_ptr2;}}}else{[MethodImpl(MethodImplOptions.AggressiveInlining)]staticvoidFillVectorFromPixel(refVector128<uint>data,refbytesrc_ptr){data=Vector128.Create((uint)src_ptr,(uint)Unsafe.Add(refsrc_ptr,1),(uint)Unsafe.Add(refsrc_ptr,2),(uint)Unsafe.Add(refsrc_ptr,3));}Span<Vector128<uint>>stack=stackallocVector128<uint>[(int)radius*2+1];for(inty=0;y<=areaMaxY;y++){varsum_in=Vector128<uint>.Zero;refbytesrc_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// start of line (0,y)Vector128<uint>data=Vector128<uint>.Zero;FillVectorFromPixel(refdata,refsrc_ptr);varsum=Vector128.Multiply(data,initialSum);varsum_out=Vector128.Multiply(data,radius+1);foreach(refvarstack_ptrinstack.Slice(0,(int)radius+1)){stack_ptr=data;}for(inti=1;i<=radius;i++){if(i<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorFromPixel(refdata,refsrc_ptr);}refvarstack_ptr=refstack[(int)(i+radius)];stack_ptr=data;sum+=data*(uint)(radius+1-i);sum_in+=data;}intsp=(int)radius;intxp=(int)radius;if(xp>areaMaxX)xp=areaMaxX;src_ptr=refUnsafe.Add(refsourcePtr,xp*strideX+y*strideY);// img.pix_ptr(xp, y);refbytedst_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// img.pix_ptr(0, y);for(intx=0;x<=areaMaxX;x++){varblurredPixel=Vector128.ShiftRightLogical(Vector128.Multiply(sum,mul_sum),shr_sum);dst_ptr=(byte)blurredPixel[0];Unsafe.Add(refdst_ptr,1)=(byte)blurredPixel[1];Unsafe.Add(refdst_ptr,2)=(byte)blurredPixel[2];Unsafe.Add(refdst_ptr,3)=(byte)blurredPixel[3];dst_ptr=refUnsafe.Add(refdst_ptr,strideX);sum-=sum_out;intstack_start=sp+(int)radius+1;if(stack_start>=stack.Length)stack_start-=stack.Length;refvarstack_ptr=refstack[stack_start];sum_out-=stack_ptr;if(xp<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorFromPixel(refdata,refsrc_ptr);++xp;}stack_ptr=data;sum_in+=data;sum+=sum_in;++sp;if(sp>=stack.Length)sp=0;stack_ptr=refstack[sp];sum_out+=stack_ptr;sum_in-=stack_ptr;}}}}staticintSumNumbers(intn){returnn*(n+1)/2;// fast way of calculating range (0..n).Sum()}/// Stackblur algorithm by Mario Klingemann/// Details here:/// http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html/// C++ implemenation base from:/// https://gist.github.com/benjamin9999/3809142/// http://www.antigrain.com/__code/include/agg_blur.h.html/// This version works only with RGBA colorstaticvoidStackBlur(Span<byte>src,///< input image dataintw,///< image widthinth,///< image heightintrowStride,uintradius///< blur intensity (should be in 2..254 range)){vararea=Vector128.Create(0,0,w,h);StackBlur(src,w,h,rowStride,radius,area);}staticvoidStackBlur(Span<byte>src,///< input image dataintw,///< image widthinth,///< image heightintrowStride,uintradius,///< blur intensity (should be in 2..254 range)Vector128<int>area){if(radius>254)return;if(radius<1)return;StackblurPass(src,w,h,rowStride,radius,vertical:false,area);StackblurPass(src,w,h,rowStride,radius,vertical:true,area);}}}

            Metadata

            Metadata

            Assignees

            Labels

            area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

            Type

            No type

            Projects

            No projects

              Milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              Compiler overwrites wrong memory location in .NET 8 RC1 when using Unsafe.Add and Vector256<uint>[0] #92590

              Description

              @jwdj

              Description

              In a release build with .NET 8 RC1 the compiler overwrites memory it shouldn't touch.

              Input is a Span<byte> of pixels with layout RGBA, where every fourth byte is an alpha byte set to 255 (index 3, 7, 11, etc).
              The code only overwrites the RGB bytes (indexes 0, 1, 2, 4, 5, 6, 8, etc), but the compiler overwrites 3, 7, 11, etc too (with 0).
              This overwrite happens when a ref byte (3 bytes earlier) is overwritten with the first element of a Vector256

              Unsafe.Add(refdst_ptr,0)=(byte)blurredPixel0[0];// blurredPixel0 is a Vector256<uint> dst_ptr is ref byte// byte at dst_ptr + 3 is now overwritten with 0

              Below is this line in more context. I have tried to shorten the example code but the bug is very specific and is Vector256 related.

              Configuration

              • .NET 8.0 RC1
              • OS: Windows 10
              • Only tested with x64

              Regression?

              Yes, compared to .NET 7.0

              Other information

              The following configurations work fine:
              dotnet run StackBlur --framework net7.0 --configuration Release
              dotnet run StackBlur --framework net7.0 --configuration Debug
              dotnet run StackBlur --framework net8.0 --configuration Debug

              To reproduce the error (from PowerShell):
              $env:DOTNET_JitDisasm = 'StackblurPass'
              dotnet run StackBlur --framework net8.0 --configuration Release

              Here the project code StackBlur.csproj:

              <ProjectSdk="Microsoft.NET.Sdk">
              <PropertyGroup>
              <OutputType>Exe</OutputType>
              <TargetFrameworks>net7.0;net8.0</TargetFrameworks>
              <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup>
              </Project>

              And the source code Program.cs:
              The program is rather long but scroll down to where it is not indented to find the issue.

              usingSystem;usingSystem.Numerics;usingSystem.Runtime.Intrinsics;usingSystem.Runtime.Intrinsics.X86;usingSystem.Runtime.CompilerServices;usingSystem.Runtime.InteropServices;usingSystem.Diagnostics;namespaceApplication{classProgram{staticvoidMain(string[]args){Console.WriteLine(Environment.Version);constintWidth=1920;constintHeight=1080;Span<byte>pixels=newbyte[Width*Height*sizeof(uint)];for(inti=3;i<pixels.Length;i+=4){pixels[i]=255;// fill alpha channel}pixels.Fill(255);vararea=Vector128.Create(0,0,384,384);StackBlur(pixels,Width,Height,Width*sizeof(uint),radius:100,area);for(inti=3;i<pixels.Length;i+=4){if(pixels[i]!=255){Console.WriteLine("FAILED");return;}}}staticushort[]stackblur_mul=newushort[255]{512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259};staticushort[]stackblur_shr=newushort[255]{9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24};//[MethodImpl(MethodImplOptions.NoOptimization)] // also 'fixes' the issuestaticvoidStackblurPass(Span<byte>src,///< input image dataintwidth,///< image widthintheight,///< image heightintrowStride,uintradius,///< blur intensity (should be in 2..254 range)boolvertical,Vector128<int>area){uintmul_sum=stackblur_mul[radius];intshr_sum=stackblur_shr[radius];varareaX=area[0];varareaY=area[1];varareaWidth=area[2];varareaHeight=area[3];intstrideX=4;intstrideY=rowStride;intareaMinX=areaX>=0?areaX:0;intareaMinY=areaY>=0?areaY:0;intmaxX=width-1;intmaxY=height-1;intareaMaxX=areaX+areaWidth-1;intareaMaxY=areaY+areaHeight-1;if(areaMaxX>maxX)areaMaxX=maxX;if(areaMaxY>maxY)areaMaxY=maxY;areaMaxX-=areaMinX;areaMaxY-=areaMinY;if(vertical){// transpose (swap X and Y)(maxX,maxY)=(maxY,maxX);(strideX,strideY)=(strideY,strideX);(areaMinX,areaMinY)=(areaMinY,areaMinX);(areaMaxX,areaMaxY)=(areaMaxY,areaMaxX);}uintinitialSum=(uint)SumNumbers((int)radius+1);refvarsourcePtr=refsrc[areaMinX*strideX+areaMinY*strideY];if(Avx2.IsSupported&&Vector256.IsHardwareAccelerated){varidx0=Vector256.Create(0,1,2,1*strideY,1*strideY+1,1*strideY+2,2*strideY,2*strideY+1);varidx1=Vector256.Create(2*strideY+2,3*strideY,3*strideY+1,3*strideY+2,4*strideY,4*strideY+1,4*strideY+2,5*strideY);varidx2=Vector256.Create(5*strideY+1,5*strideY+2,6*strideY,6*strideY+1,6*strideY+2,7*strideY,7*strideY+1,7*strideY+2);varlowerByte=Vector256.Create(0xFFU);[MethodImpl(MethodImplOptions.AggressiveInlining)]unsafevoidFillVectorsFromPixels(refbytesrc,refVector256<uint>data0,refVector256<uint>data1,refVector256<uint>data2){varsrc_ptr=(uint*)Unsafe.AsPointer(refsrc);data0=Avx2.GatherVector256(src_ptr,idx0,1);data1=Avx2.GatherVector256(src_ptr,idx1,1);data2=Avx2.GatherVector256(src_ptr,idx2,1);data0=Avx2.And(data0,lowerByte);data1=Avx2.And(data1,lowerByte);data2=Avx2.And(data2,lowerByte);}Span<Vector256<uint>>stack0=stackallocVector256<uint>[(int)radius*2+1];Span<Vector256<uint>>stack1=stackallocVector256<uint>[(int)radius*2+1];Span<Vector256<uint>>stack2=stackallocVector256<uint>[(int)radius*2+1];for(inty=0;y<=areaMaxY;y+=8){varsum_in0=Vector256<uint>.Zero;varsum_in1=Vector256<uint>.Zero;varsum_in2=Vector256<uint>.Zero;vardata0=Vector256<uint>.Zero;vardata1=Vector256<uint>.Zero;vardata2=Vector256<uint>.Zero;refvarsrc_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// start of line (0,y)FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);varsum0=Vector256.Multiply(data0,initialSum);varsum1=Vector256.Multiply(data1,initialSum);varsum2=Vector256.Multiply(data2,initialSum);varsum_out0=Vector256.Multiply(data0,radius+1);varsum_out1=Vector256.Multiply(data1,radius+1);varsum_out2=Vector256.Multiply(data2,radius+1);foreach(refvarstack_ptrinstack0.Slice(0,(int)radius+1)){stack_ptr=data0;}foreach(refvarstack_ptrinstack1.Slice(0,(int)radius+1)){stack_ptr=data1;}foreach(refvarstack_ptrinstack2.Slice(0,(int)radius+1)){stack_ptr=data2;}for(inti=1;i<=radius;i++){if(i<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);}refvarstack_ptr=refstack0[(int)(i+radius)];stack_ptr=data0;stack_ptr=refstack1[(int)(i+radius)];stack_ptr=data1;stack_ptr=refstack2[(int)(i+radius)];stack_ptr=data2;sum0+=data0*(uint)(radius+1-i);sum1+=data1*(uint)(radius+1-i);sum2+=data2*(uint)(radius+1-i);sum_in0+=data0;sum_in1+=data1;sum_in2+=data2;}intsp=(int)radius;intxp=(int)radius;if(xp>areaMaxX)xp=areaMaxX;src_ptr=refUnsafe.Add(refsourcePtr,xp*strideX+y*strideY);refvardst_ptr=refUnsafe.Add(refsourcePtr,y*strideY);for(intx=0;x<=areaMaxX;x++){varblurredPixel0=Vector256.ShiftRightLogical(Vector256.Multiply(sum0,mul_sum),shr_sum);varblurredPixel1=Vector256.ShiftRightLogical(Vector256.Multiply(sum1,mul_sum),shr_sum);varblurredPixel2=Vector256.ShiftRightLogical(Vector256.Multiply(sum2,mul_sum),shr_sum);
              #if NET8_0_OR_GREATERConsole.WriteLine(Unsafe.Add(refdst_ptr,3).ToString("X"));// should print FF
              #endif
              Unsafe.Add(refdst_ptr,0)=(byte)blurredPixel0[0];// bug, should only write a single byte
              #if NET8_0_OR_GREATERConsole.WriteLine(Unsafe.Add(refdst_ptr,3).ToString("X"));// should print FF, not 00
              #endif
              Unsafe.Add(refdst_ptr,1)=(byte)blurredPixel0[1];Unsafe.Add(refdst_ptr,2)=(byte)blurredPixel0[2];Unsafe.Add(refdst_ptr,0+strideY)=(byte)blurredPixel0[3];Unsafe.Add(refdst_ptr,1+strideY)=(byte)blurredPixel0[4];Unsafe.Add(refdst_ptr,2+strideY)=(byte)blurredPixel0[5];Unsafe.Add(refdst_ptr,0+strideY*2)=(byte)blurredPixel0[6];Unsafe.Add(refdst_ptr,1+strideY*2)=(byte)blurredPixel0[7];Unsafe.Add(refdst_ptr,2+strideY*2)=(byte)blurredPixel1[0];Unsafe.Add(refdst_ptr,0+strideY*3)=(byte)blurredPixel1[1];Unsafe.Add(refdst_ptr,1+strideY*3)=(byte)blurredPixel1[2];Unsafe.Add(refdst_ptr,2+strideY*3)=(byte)blurredPixel1[3];Unsafe.Add(refdst_ptr,0+strideY*4)=(byte)blurredPixel1[4];Unsafe.Add(refdst_ptr,1+strideY*4)=(byte)blurredPixel1[5];Unsafe.Add(refdst_ptr,2+strideY*4)=(byte)blurredPixel1[6];Unsafe.Add(refdst_ptr,0+strideY*5)=(byte)blurredPixel1[7];Unsafe.Add(refdst_ptr,1+strideY*5)=(byte)blurredPixel2[0];Unsafe.Add(refdst_ptr,2+strideY*5)=(byte)blurredPixel2[1];Unsafe.Add(refdst_ptr,0+strideY*6)=(byte)blurredPixel2[2];Unsafe.Add(refdst_ptr,1+strideY*6)=(byte)blurredPixel2[3];Unsafe.Add(refdst_ptr,2+strideY*6)=(byte)blurredPixel2[4];Unsafe.Add(refdst_ptr,0+strideY*7)=(byte)blurredPixel2[5];Unsafe.Add(refdst_ptr,1+strideY*7)=(byte)blurredPixel2[6];Unsafe.Add(refdst_ptr,2+strideY*7)=(byte)blurredPixel2[7];dst_ptr=refUnsafe.Add(refdst_ptr,strideX);sum0-=sum_out0;sum1-=sum_out1;sum2-=sum_out2;intstack_start=sp+(int)radius+1;if(stack_start>=stack0.Length)stack_start-=stack0.Length;refvarstack_ptr0=refstack0[stack_start];sum_out0-=stack_ptr0;refvarstack_ptr1=refstack1[stack_start];sum_out1-=stack_ptr1;refvarstack_ptr2=refstack2[stack_start];sum_out2-=stack_ptr2;if(xp<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);++xp;}stack_ptr0=data0;sum_in0+=data0;sum0+=sum_in0;stack_ptr1=data1;sum_in1+=data1;sum1+=sum_in1;stack_ptr2=data2;sum_in2+=data2;sum2+=sum_in2;++sp;if(sp>=stack0.Length)sp=0;stack_ptr0=refstack0[sp];sum_out0+=stack_ptr0;sum_in0-=stack_ptr0;stack_ptr1=refstack1[sp];sum_out1+=stack_ptr1;sum_in1-=stack_ptr1;stack_ptr2=refstack2[sp];sum_out2+=stack_ptr2;sum_in2-=stack_ptr2;}}}else{[MethodImpl(MethodImplOptions.AggressiveInlining)]staticvoidFillVectorFromPixel(refVector128<uint>data,refbytesrc_ptr){data=Vector128.Create((uint)src_ptr,(uint)Unsafe.Add(refsrc_ptr,1),(uint)Unsafe.Add(refsrc_ptr,2),(uint)Unsafe.Add(refsrc_ptr,3));}Span<Vector128<uint>>stack=stackallocVector128<uint>[(int)radius*2+1];for(inty=0;y<=areaMaxY;y++){varsum_in=Vector128<uint>.Zero;refbytesrc_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// start of line (0,y)Vector128<uint>data=Vector128<uint>.Zero;FillVectorFromPixel(refdata,refsrc_ptr);varsum=Vector128.Multiply(data,initialSum);varsum_out=Vector128.Multiply(data,radius+1);foreach(refvarstack_ptrinstack.Slice(0,(int)radius+1)){stack_ptr=data;}for(inti=1;i<=radius;i++){if(i<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorFromPixel(refdata,refsrc_ptr);}refvarstack_ptr=refstack[(int)(i+radius)];stack_ptr=data;sum+=data*(uint)(radius+1-i);sum_in+=data;}intsp=(int)radius;intxp=(int)radius;if(xp>areaMaxX)xp=areaMaxX;src_ptr=refUnsafe.Add(refsourcePtr,xp*strideX+y*strideY);// img.pix_ptr(xp, y);refbytedst_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// img.pix_ptr(0, y);for(intx=0;x<=areaMaxX;x++){varblurredPixel=Vector128.ShiftRightLogical(Vector128.Multiply(sum,mul_sum),shr_sum);dst_ptr=(byte)blurredPixel[0];Unsafe.Add(refdst_ptr,1)=(byte)blurredPixel[1];Unsafe.Add(refdst_ptr,2)=(byte)blurredPixel[2];Unsafe.Add(refdst_ptr,3)=(byte)blurredPixel[3];dst_ptr=refUnsafe.Add(refdst_ptr,strideX);sum-=sum_out;intstack_start=sp+(int)radius+1;if(stack_start>=stack.Length)stack_start-=stack.Length;refvarstack_ptr=refstack[stack_start];sum_out-=stack_ptr;if(xp<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorFromPixel(refdata,refsrc_ptr);++xp;}stack_ptr=data;sum_in+=data;sum+=sum_in;++sp;if(sp>=stack.Length)sp=0;stack_ptr=refstack[sp];sum_out+=stack_ptr;sum_in-=stack_ptr;}}}}staticintSumNumbers(intn){returnn*(n+1)/2;// fast way of calculating range (0..n).Sum()}/// Stackblur algorithm by Mario Klingemann/// Details here:/// http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html/// C++ implemenation base from:/// https://gist.github.com/benjamin9999/3809142/// http://www.antigrain.com/__code/include/agg_blur.h.html/// This version works only with RGBA colorstaticvoidStackBlur(Span<byte>src,///< input image dataintw,///< image widthinth,///< image heightintrowStride,uintradius///< blur intensity (should be in 2..254 range)){vararea=Vector128.Create(0,0,w,h);StackBlur(src,w,h,rowStride,radius,area);}staticvoidStackBlur(Span<byte>src,///< input image dataintw,///< image widthinth,///< image heightintrowStride,uintradius,///< blur intensity (should be in 2..254 range)Vector128<int>area){if(radius>254)return;if(radius<1)return;StackblurPass(src,w,h,rowStride,radius,vertical:false,area);StackblurPass(src,w,h,rowStride,radius,vertical:true,area);}}}

              Metadata

              Metadata

              Assignees

              Labels

              area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

              Type

              No type

              Projects

              No projects

                Milestone

                Relationships

                None yet

                Development

                No branches or pull requests

                Issue actions

                , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                Skip to content

                Compiler overwrites wrong memory location in .NET 8 RC1 when using Unsafe.Add and Vector256<uint>[0] #92590

                Description

                @jwdj

                Description

                In a release build with .NET 8 RC1 the compiler overwrites memory it shouldn't touch.

                Input is a Span<byte> of pixels with layout RGBA, where every fourth byte is an alpha byte set to 255 (index 3, 7, 11, etc).
                The code only overwrites the RGB bytes (indexes 0, 1, 2, 4, 5, 6, 8, etc), but the compiler overwrites 3, 7, 11, etc too (with 0).
                This overwrite happens when a ref byte (3 bytes earlier) is overwritten with the first element of a Vector256

                Unsafe.Add(refdst_ptr,0)=(byte)blurredPixel0[0];// blurredPixel0 is a Vector256<uint> dst_ptr is ref byte// byte at dst_ptr + 3 is now overwritten with 0

                Below is this line in more context. I have tried to shorten the example code but the bug is very specific and is Vector256 related.

                Configuration

                • .NET 8.0 RC1
                • OS: Windows 10
                • Only tested with x64

                Regression?

                Yes, compared to .NET 7.0

                Other information

                The following configurations work fine:
                dotnet run StackBlur --framework net7.0 --configuration Release
                dotnet run StackBlur --framework net7.0 --configuration Debug
                dotnet run StackBlur --framework net8.0 --configuration Debug

                To reproduce the error (from PowerShell):
                $env:DOTNET_JitDisasm = 'StackblurPass'
                dotnet run StackBlur --framework net8.0 --configuration Release

                Here the project code StackBlur.csproj:

                <ProjectSdk="Microsoft.NET.Sdk">
                <PropertyGroup>
                <OutputType>Exe</OutputType>
                <TargetFrameworks>net7.0;net8.0</TargetFrameworks>
                <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup>
                </Project>

                And the source code Program.cs:
                The program is rather long but scroll down to where it is not indented to find the issue.

                usingSystem;usingSystem.Numerics;usingSystem.Runtime.Intrinsics;usingSystem.Runtime.Intrinsics.X86;usingSystem.Runtime.CompilerServices;usingSystem.Runtime.InteropServices;usingSystem.Diagnostics;namespaceApplication{classProgram{staticvoidMain(string[]args){Console.WriteLine(Environment.Version);constintWidth=1920;constintHeight=1080;Span<byte>pixels=newbyte[Width*Height*sizeof(uint)];for(inti=3;i<pixels.Length;i+=4){pixels[i]=255;// fill alpha channel}pixels.Fill(255);vararea=Vector128.Create(0,0,384,384);StackBlur(pixels,Width,Height,Width*sizeof(uint),radius:100,area);for(inti=3;i<pixels.Length;i+=4){if(pixels[i]!=255){Console.WriteLine("FAILED");return;}}}staticushort[]stackblur_mul=newushort[255]{512,512,456,512,328,456,335,512,405,328,271,456,388,335,292,512,454,405,364,328,298,271,496,456,420,388,360,335,312,292,273,512,482,454,428,405,383,364,345,328,312,298,284,271,259,496,475,456,437,420,404,388,374,360,347,335,323,312,302,292,282,273,265,512,497,482,468,454,441,428,417,405,394,383,373,364,354,345,337,328,320,312,305,298,291,284,278,271,265,259,507,496,485,475,465,456,446,437,428,420,412,404,396,388,381,374,367,360,354,347,341,335,329,323,318,312,307,302,297,292,287,282,278,273,269,265,261,512,505,497,489,482,475,468,461,454,447,441,435,428,422,417,411,405,399,394,389,383,378,373,368,364,359,354,350,345,341,337,332,328,324,320,316,312,309,305,301,298,294,291,287,284,281,278,274,271,268,265,262,259,257,507,501,496,491,485,480,475,470,465,460,456,451,446,442,437,433,428,424,420,416,412,408,404,400,396,392,388,385,381,377,374,370,367,363,360,357,354,350,347,344,341,338,335,332,329,326,323,320,318,315,312,310,307,304,302,299,297,294,292,289,287,285,282,280,278,275,273,271,269,267,265,263,261,259};staticushort[]stackblur_shr=newushort[255]{9,11,12,13,13,14,14,15,15,15,15,16,16,16,16,17,17,17,17,17,17,17,18,18,18,18,18,18,18,18,18,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24,24};//[MethodImpl(MethodImplOptions.NoOptimization)] // also 'fixes' the issuestaticvoidStackblurPass(Span<byte>src,///< input image dataintwidth,///< image widthintheight,///< image heightintrowStride,uintradius,///< blur intensity (should be in 2..254 range)boolvertical,Vector128<int>area){uintmul_sum=stackblur_mul[radius];intshr_sum=stackblur_shr[radius];varareaX=area[0];varareaY=area[1];varareaWidth=area[2];varareaHeight=area[3];intstrideX=4;intstrideY=rowStride;intareaMinX=areaX>=0?areaX:0;intareaMinY=areaY>=0?areaY:0;intmaxX=width-1;intmaxY=height-1;intareaMaxX=areaX+areaWidth-1;intareaMaxY=areaY+areaHeight-1;if(areaMaxX>maxX)areaMaxX=maxX;if(areaMaxY>maxY)areaMaxY=maxY;areaMaxX-=areaMinX;areaMaxY-=areaMinY;if(vertical){// transpose (swap X and Y)(maxX,maxY)=(maxY,maxX);(strideX,strideY)=(strideY,strideX);(areaMinX,areaMinY)=(areaMinY,areaMinX);(areaMaxX,areaMaxY)=(areaMaxY,areaMaxX);}uintinitialSum=(uint)SumNumbers((int)radius+1);refvarsourcePtr=refsrc[areaMinX*strideX+areaMinY*strideY];if(Avx2.IsSupported&&Vector256.IsHardwareAccelerated){varidx0=Vector256.Create(0,1,2,1*strideY,1*strideY+1,1*strideY+2,2*strideY,2*strideY+1);varidx1=Vector256.Create(2*strideY+2,3*strideY,3*strideY+1,3*strideY+2,4*strideY,4*strideY+1,4*strideY+2,5*strideY);varidx2=Vector256.Create(5*strideY+1,5*strideY+2,6*strideY,6*strideY+1,6*strideY+2,7*strideY,7*strideY+1,7*strideY+2);varlowerByte=Vector256.Create(0xFFU);[MethodImpl(MethodImplOptions.AggressiveInlining)]unsafevoidFillVectorsFromPixels(refbytesrc,refVector256<uint>data0,refVector256<uint>data1,refVector256<uint>data2){varsrc_ptr=(uint*)Unsafe.AsPointer(refsrc);data0=Avx2.GatherVector256(src_ptr,idx0,1);data1=Avx2.GatherVector256(src_ptr,idx1,1);data2=Avx2.GatherVector256(src_ptr,idx2,1);data0=Avx2.And(data0,lowerByte);data1=Avx2.And(data1,lowerByte);data2=Avx2.And(data2,lowerByte);}Span<Vector256<uint>>stack0=stackallocVector256<uint>[(int)radius*2+1];Span<Vector256<uint>>stack1=stackallocVector256<uint>[(int)radius*2+1];Span<Vector256<uint>>stack2=stackallocVector256<uint>[(int)radius*2+1];for(inty=0;y<=areaMaxY;y+=8){varsum_in0=Vector256<uint>.Zero;varsum_in1=Vector256<uint>.Zero;varsum_in2=Vector256<uint>.Zero;vardata0=Vector256<uint>.Zero;vardata1=Vector256<uint>.Zero;vardata2=Vector256<uint>.Zero;refvarsrc_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// start of line (0,y)FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);varsum0=Vector256.Multiply(data0,initialSum);varsum1=Vector256.Multiply(data1,initialSum);varsum2=Vector256.Multiply(data2,initialSum);varsum_out0=Vector256.Multiply(data0,radius+1);varsum_out1=Vector256.Multiply(data1,radius+1);varsum_out2=Vector256.Multiply(data2,radius+1);foreach(refvarstack_ptrinstack0.Slice(0,(int)radius+1)){stack_ptr=data0;}foreach(refvarstack_ptrinstack1.Slice(0,(int)radius+1)){stack_ptr=data1;}foreach(refvarstack_ptrinstack2.Slice(0,(int)radius+1)){stack_ptr=data2;}for(inti=1;i<=radius;i++){if(i<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);}refvarstack_ptr=refstack0[(int)(i+radius)];stack_ptr=data0;stack_ptr=refstack1[(int)(i+radius)];stack_ptr=data1;stack_ptr=refstack2[(int)(i+radius)];stack_ptr=data2;sum0+=data0*(uint)(radius+1-i);sum1+=data1*(uint)(radius+1-i);sum2+=data2*(uint)(radius+1-i);sum_in0+=data0;sum_in1+=data1;sum_in2+=data2;}intsp=(int)radius;intxp=(int)radius;if(xp>areaMaxX)xp=areaMaxX;src_ptr=refUnsafe.Add(refsourcePtr,xp*strideX+y*strideY);refvardst_ptr=refUnsafe.Add(refsourcePtr,y*strideY);for(intx=0;x<=areaMaxX;x++){varblurredPixel0=Vector256.ShiftRightLogical(Vector256.Multiply(sum0,mul_sum),shr_sum);varblurredPixel1=Vector256.ShiftRightLogical(Vector256.Multiply(sum1,mul_sum),shr_sum);varblurredPixel2=Vector256.ShiftRightLogical(Vector256.Multiply(sum2,mul_sum),shr_sum);
                #if NET8_0_OR_GREATERConsole.WriteLine(Unsafe.Add(refdst_ptr,3).ToString("X"));// should print FF
                #endif
                Unsafe.Add(refdst_ptr,0)=(byte)blurredPixel0[0];// bug, should only write a single byte
                #if NET8_0_OR_GREATERConsole.WriteLine(Unsafe.Add(refdst_ptr,3).ToString("X"));// should print FF, not 00
                #endif
                Unsafe.Add(refdst_ptr,1)=(byte)blurredPixel0[1];Unsafe.Add(refdst_ptr,2)=(byte)blurredPixel0[2];Unsafe.Add(refdst_ptr,0+strideY)=(byte)blurredPixel0[3];Unsafe.Add(refdst_ptr,1+strideY)=(byte)blurredPixel0[4];Unsafe.Add(refdst_ptr,2+strideY)=(byte)blurredPixel0[5];Unsafe.Add(refdst_ptr,0+strideY*2)=(byte)blurredPixel0[6];Unsafe.Add(refdst_ptr,1+strideY*2)=(byte)blurredPixel0[7];Unsafe.Add(refdst_ptr,2+strideY*2)=(byte)blurredPixel1[0];Unsafe.Add(refdst_ptr,0+strideY*3)=(byte)blurredPixel1[1];Unsafe.Add(refdst_ptr,1+strideY*3)=(byte)blurredPixel1[2];Unsafe.Add(refdst_ptr,2+strideY*3)=(byte)blurredPixel1[3];Unsafe.Add(refdst_ptr,0+strideY*4)=(byte)blurredPixel1[4];Unsafe.Add(refdst_ptr,1+strideY*4)=(byte)blurredPixel1[5];Unsafe.Add(refdst_ptr,2+strideY*4)=(byte)blurredPixel1[6];Unsafe.Add(refdst_ptr,0+strideY*5)=(byte)blurredPixel1[7];Unsafe.Add(refdst_ptr,1+strideY*5)=(byte)blurredPixel2[0];Unsafe.Add(refdst_ptr,2+strideY*5)=(byte)blurredPixel2[1];Unsafe.Add(refdst_ptr,0+strideY*6)=(byte)blurredPixel2[2];Unsafe.Add(refdst_ptr,1+strideY*6)=(byte)blurredPixel2[3];Unsafe.Add(refdst_ptr,2+strideY*6)=(byte)blurredPixel2[4];Unsafe.Add(refdst_ptr,0+strideY*7)=(byte)blurredPixel2[5];Unsafe.Add(refdst_ptr,1+strideY*7)=(byte)blurredPixel2[6];Unsafe.Add(refdst_ptr,2+strideY*7)=(byte)blurredPixel2[7];dst_ptr=refUnsafe.Add(refdst_ptr,strideX);sum0-=sum_out0;sum1-=sum_out1;sum2-=sum_out2;intstack_start=sp+(int)radius+1;if(stack_start>=stack0.Length)stack_start-=stack0.Length;refvarstack_ptr0=refstack0[stack_start];sum_out0-=stack_ptr0;refvarstack_ptr1=refstack1[stack_start];sum_out1-=stack_ptr1;refvarstack_ptr2=refstack2[stack_start];sum_out2-=stack_ptr2;if(xp<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorsFromPixels(refsrc_ptr,refdata0,refdata1,refdata2);++xp;}stack_ptr0=data0;sum_in0+=data0;sum0+=sum_in0;stack_ptr1=data1;sum_in1+=data1;sum1+=sum_in1;stack_ptr2=data2;sum_in2+=data2;sum2+=sum_in2;++sp;if(sp>=stack0.Length)sp=0;stack_ptr0=refstack0[sp];sum_out0+=stack_ptr0;sum_in0-=stack_ptr0;stack_ptr1=refstack1[sp];sum_out1+=stack_ptr1;sum_in1-=stack_ptr1;stack_ptr2=refstack2[sp];sum_out2+=stack_ptr2;sum_in2-=stack_ptr2;}}}else{[MethodImpl(MethodImplOptions.AggressiveInlining)]staticvoidFillVectorFromPixel(refVector128<uint>data,refbytesrc_ptr){data=Vector128.Create((uint)src_ptr,(uint)Unsafe.Add(refsrc_ptr,1),(uint)Unsafe.Add(refsrc_ptr,2),(uint)Unsafe.Add(refsrc_ptr,3));}Span<Vector128<uint>>stack=stackallocVector128<uint>[(int)radius*2+1];for(inty=0;y<=areaMaxY;y++){varsum_in=Vector128<uint>.Zero;refbytesrc_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// start of line (0,y)Vector128<uint>data=Vector128<uint>.Zero;FillVectorFromPixel(refdata,refsrc_ptr);varsum=Vector128.Multiply(data,initialSum);varsum_out=Vector128.Multiply(data,radius+1);foreach(refvarstack_ptrinstack.Slice(0,(int)radius+1)){stack_ptr=data;}for(inti=1;i<=radius;i++){if(i<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorFromPixel(refdata,refsrc_ptr);}refvarstack_ptr=refstack[(int)(i+radius)];stack_ptr=data;sum+=data*(uint)(radius+1-i);sum_in+=data;}intsp=(int)radius;intxp=(int)radius;if(xp>areaMaxX)xp=areaMaxX;src_ptr=refUnsafe.Add(refsourcePtr,xp*strideX+y*strideY);// img.pix_ptr(xp, y);refbytedst_ptr=refUnsafe.Add(refsourcePtr,y*strideY);// img.pix_ptr(0, y);for(intx=0;x<=areaMaxX;x++){varblurredPixel=Vector128.ShiftRightLogical(Vector128.Multiply(sum,mul_sum),shr_sum);dst_ptr=(byte)blurredPixel[0];Unsafe.Add(refdst_ptr,1)=(byte)blurredPixel[1];Unsafe.Add(refdst_ptr,2)=(byte)blurredPixel[2];Unsafe.Add(refdst_ptr,3)=(byte)blurredPixel[3];dst_ptr=refUnsafe.Add(refdst_ptr,strideX);sum-=sum_out;intstack_start=sp+(int)radius+1;if(stack_start>=stack.Length)stack_start-=stack.Length;refvarstack_ptr=refstack[stack_start];sum_out-=stack_ptr;if(xp<areaMaxX){src_ptr=refUnsafe.Add(refsrc_ptr,strideX);FillVectorFromPixel(refdata,refsrc_ptr);++xp;}stack_ptr=data;sum_in+=data;sum+=sum_in;++sp;if(sp>=stack.Length)sp=0;stack_ptr=refstack[sp];sum_out+=stack_ptr;sum_in-=stack_ptr;}}}}staticintSumNumbers(intn){returnn*(n+1)/2;// fast way of calculating range (0..n).Sum()}/// Stackblur algorithm by Mario Klingemann/// Details here:/// http://www.quasimondo.com/StackBlurForCanvas/StackBlurDemo.html/// C++ implemenation base from:/// https://gist.github.com/benjamin9999/3809142/// http://www.antigrain.com/__code/include/agg_blur.h.html/// This version works only with RGBA colorstaticvoidStackBlur(Span<byte>src,///< input image dataintw,///< image widthinth,///< image heightintrowStride,uintradius///< blur intensity (should be in 2..254 range)){vararea=Vector128.Create(0,0,w,h);StackBlur(src,w,h,rowStride,radius,area);}staticvoidStackBlur(Span<byte>src,///< input image dataintw,///< image widthinth,///< image heightintrowStride,uintradius,///< blur intensity (should be in 2..254 range)Vector128<int>area){if(radius>254)return;if(radius<1)return;StackblurPass(src,w,h,rowStride,radius,vertical:false,area);StackblurPass(src,w,h,rowStride,radius,vertical:true,area);}}}

                Metadata

                Metadata

                Assignees

                Labels

                area-CodeGen-coreclrCLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

                Type

                No type

                Projects

                No projects

                  Milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions