Skip to content

FMA codegen #11260

Closed
Closed

Description

@helloguo

These two functions are doing the same thing. The only difference is the order of parameters of Fma.MultiplyAdd. However, the codegen of the second function is not as good as the first one. Maybe worth looking at this to see if the JIT can optimize it.

static unsafe void foo2(float scale, float[] src, float[] dst, int count)
{
    fixed (float* psrc = &src[0])
    fixed (float* pdst = &dst[0])
    {
        float* pSrcCurrent = psrc;
        float* pDstCurrent = pdst;
        float* pEnd = pdst + count;

        Vector256<float> scaleVector256 = Avx.SetAllVector256(scale);

        while (pDstCurrent + 8 <= pEnd)
        {
            Vector256<float> dstVector = Avx.LoadVector256(pDstCurrent);
            dstVector = Fma.MultiplyAdd(Avx.LoadVector256(pSrcCurrent), scaleVector256, dstVector);
            Avx.Store(pDstCurrent, dstVector);

            pSrcCurrent += 8;
            pDstCurrent += 8;
        }
    }
}

static unsafe void foo3(float scale, float[] src, float[] dst, int count)
{
    fixed (float* psrc = &src[0])
    fixed (float* pdst = &dst[0])
    {
        float* pSrcCurrent = psrc;
        float* pDstCurrent = pdst;
        float* pEnd = pdst + count;

        Vector256<float> scaleVector256 = Avx.SetAllVector256(scale);

        while (pDstCurrent + 8 <= pEnd)
        {
            Vector256<float> dstVector = Avx.LoadVector256(pDstCurrent);
            dstVector = Fma.MultiplyAdd(scaleVector256, Avx.LoadVector256(pSrcCurrent), dstVector);
            Avx.Store(pDstCurrent, dstVector);

            pSrcCurrent += 8;
            pDstCurrent += 8;
        }
    }
}

Codegen of foo2:
foo2

Codegen of foo3:
foo3

category:cq
theme:register-allocator
skill-level:expert
cost:medium

Activity

  1. helloguo commented on Oct 18, 2018

    @helloguo
    Author
  2. tannergooding commented on Oct 18, 2018

    @tannergooding
    Member

    CC. @CarolEidt, it looks like there is some additional register shuffling going on.

  3. tannergooding commented on Oct 18, 2018

    @tannergooding
    Member

    it is unnecessarily shuffling ymm0 to ymm2 before the call and ymm2 to ymm1 after the call.

  4. fiigii commented on Oct 18, 2018

    @fiigii
    Contributor

    The first unnecessarily shuffling (ymm0 to ymm2) seems from emitter

    void emitter::emitIns_SIMD_R_R_R_AR(
        instruction ins, emitAttr attr, regNumber targetReg, regNumber op1Reg, regNumber op2Reg, regNumber base)
    {
        assert(IsFMAInstruction(ins));
        assert(UseVEXEncoding());
    
        if (op1Reg != targetReg)
        {
            // Ensure we aren't overwriting op2
            assert(op2Reg != targetReg);
    
            emitIns_R_R(INS_movaps, attr, targetReg, op1Reg);
        }
    
        emitIns_R_R_AR(ins, attr, targetReg, op2Reg, base, 0);
    }

    and the second one may be from delayfree?

  5. fiigii commented on Oct 18, 2018

    @fiigii
    Contributor

    The current FMA instruction selection/RA is not optimal, but we need more scenarios to improve it.
    @tannergooding As I know, you are going to use HW intrinsic for some math lib, which would be a great opportunity.

  6. tannergooding commented on Oct 18, 2018

    @tannergooding
    Member

    @fiigii, I don't think the issue is with the emitter, but rather the register allocator deciding that the source and destination cannot be the same here. Maybe I missed something in LSRA or in the containment checks for one of the FMA cases?

  7. tannergooding commented on Oct 18, 2018

    @tannergooding
    Member

    At least as of right now, we try to ensure at least one of the operands is contained, preferencing 3, then 2, then 1: https://github.com/dotnet/coreclr/blob/master/src/jit/lowerxarch.cpp#L3006

    We then try to ensure that the correct delay free uses are setup in LSRA: https://github.com/dotnet/coreclr/blob/master/src/jit/lsraxarch.cpp#L2516

    And finally just emit the appropriate encoding (based on which are is contained) in codegen: https://github.com/dotnet/coreclr/blob/master/src/jit/hwintrinsiccodegenxarch.cpp#L2410

  8. tannergooding commented on Oct 18, 2018

    @tannergooding
    Member

    Perhaps a possible fix here is telling the register allocator which operand ends up being the RMW operand.... @CarolEidt, do we have a good way to do that (is just setting tgtPref the right thing to do here?)

  9. transferred this issue fromdotnet/coreclron Jan 31, 2020
  10. added this to the Future milestone on Jan 31, 2020
  11. CarolEidt commented on Oct 12, 2020

    @CarolEidt
    Contributor

    This is really more of a register allocation (and Lowering) issue. I've reclassified this as register-allocator theme, but it may be that it has been addressed already.

  12. kevinjwz commented on Oct 21, 2020

    @kevinjwz

    Another example I recently encountered:

    unsafe static void MulKernel2x8(double* C,double* A,double* B,uint A_cols)
    {
        Vector256<double> sum00, sum01, sum10, sum11;
    
        sum00 = sum01 = sum10 = sum11 = default;
    
        for (uint k = 0; k < A_cols; k += 1) {
            Vector256<double> va, vb0, vb1;
            vb0 = Avx.LoadVector256(B);
            vb1 = Avx.LoadVector256(B + 4);
            va = Avx.BroadcastScalarToVector256(A);
            sum00 = Fma.MultiplyAdd(va,vb0,sum00);
            sum01 = Fma.MultiplyAdd(va,vb1,sum01);
            va = Avx.BroadcastScalarToVector256(A + 1);
            sum10 = Fma.MultiplyAdd(va,vb0,sum10);
            sum11 = Fma.MultiplyAdd(va,vb1,sum11);
            A += 2;
            B += 8;
        }
        Avx.Store(C,sum00);
        Avx.Store(C + 4,sum01);
        Avx.Store(C + 8,sum10);
        Avx.Store(C + 12,sum11);
    }

    The compiled loop body:

    vmovupd     ymm4,ymmword ptr [r8]  
    vmovupd     ymm5,ymmword ptr [r8+20h]
    vbroadcastsd ymm6,mmword ptr [rdx]  
    vmovaps     ymm7,ymm6  
    vfmadd213pd ymm7,ymm4,ymm3  
    vmovaps     ymm3,ymm7  
    vfmadd213pd ymm6,ymm5,ymm2  
    vmovaps     ymm2,ymm6  
    vbroadcastsd ymm6,mmword ptr [rdx+8] 
    vfmadd213pd ymm4,ymm6,ymm1  
    vmovaps     ymm1,ymm4  
    vfmadd213pd ymm5,ymm6,ymm0  
    vmovaps     ymm0,ymm5  
    add         rdx,10h  
    add         r8,40h  

    Ideal ouput:

    vmovupd     ymm4,ymmword ptr [r8]  
    vmovupd     ymm5,ymmword ptr [r8+20h]
    vbroadcastsd ymm6,mmword ptr [rdx]  
    vfmadd231pd ymm3,ymm6,ymm4  
    vfmadd231pd ymm2,ymm6,ymm5  
    vbroadcastsd ymm6,mmword ptr [rdx+8] 
    vfmadd231pd ymm1,ymm6,ymm4  
    vfmadd231pd ymm0,ymm6,ymm5  
    add         rdx,10h  
    add         r8,40h  

    It seems vfmadd231pd is hardly selected when both 3 operands are not from memory. I think this "mul and sum" pattern should be recognized.
    BTW, why didn't we simply add Fma.MultiplyAdd231, etc?

  13. modified the milestones: Future, 6.0.0 on Nov 14, 2020
  14. CarolEidt commented on Nov 14, 2020

    @CarolEidt
    Contributor

    This should be at least be analyzed; it may have been fixed e.g. by dotnet/coreclr/pull/19429

  15. 1 remaining item

  16. added
    needs-further-triageIssue has been initially triaged, but needs deeper consideration or reconsideration
    on Mar 23, 2021
  17. removed
    needs-further-triageIssue has been initially triaged, but needs deeper consideration or reconsideration
    on Jun 7, 2021
  18. echesakov commented on Jul 26, 2021

    @echesakov
    Contributor

    Moving to 7.0.0 since it's not a bug.

  19. modified the milestones: 6.0.0, 7.0.0 on Jul 26, 2021
  20. echesakov commented on Mar 15, 2022

    @echesakov
    Contributor

    Un-assigning myself
    cc @BruceForstall

  21. removed their assignment
    on Mar 15, 2022
  22. modified the milestones: 7.0.0, Future on Mar 16, 2022
  23. tannergooding commented on Feb 5, 2024

    @tannergooding
    Member

    This has been mostly resolved and we now generate identical code in most cases. There is still a possibility of an extra movaps in some cases, but that's a more general register allocator consideration and no longer specific to FMA

  24. locked and limited conversation to collaborators on Mar 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

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

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions