Runtime: Math/MathF.FusedMultiplyAdd/FusedMultiplyAdd not treated as intrinsics on ARM64

Created on 29 Jul 2020  路  15Comments  路  Source: dotnet/runtime

Most helpful comment

@echesakovMSFT ah, nvm, fixed via NI_Vector64_Create for double.
Full PR: https://github.com/dotnet/runtime/compare/master...EgorBo:arm64-math-fma?expand=1 now works for both float and double 馃檪

All 15 comments

I guess that code can be just enabled for arm with the only change NI_FMA_MultiplyAddScalar to NI_AdvSimd_FusedMultiplyAddScalar and it will work.

Optional: https://github.com/dotnet/coreclr/pull/27060 for AdvSimd.

Something like this: https://github.com/EgorBo/runtime-1/commit/0074845aceca270ca815b9668bfc32b33956d706

upd: ah, not that simple, NI_Vector128_CreateScalarUnsafe is xarch only atm

@echesakovMSFT @tannergooding

looks like AdvSimd.FusedMultiplyAddScalar is broken

[MethodImpl(MethodImplOptions.NoInlining)]
static float Test(float x, float y, float z)
{
    Vector64<float> vecX = Vector64.CreateScalarUnsafe(x);
    Vector64<float> vecY = Vector64.CreateScalarUnsafe(y);
    Vector64<float> vecZ = Vector64.CreateScalarUnsafe(z);
    Vector64<float> rez  = AdvSimd.FusedMultiplyAddScalar(vecX, vecY, vecZ);
    return rez.ToScalar();
}

Console.WriteLine(Test(2,4,6)) prints 26 but should be 14 (wrong order of operands here?)

Also, double version of this method just crashes:

[MethodImpl(MethodImplOptions.NoInlining)]
static double Test4(double x, double y, double z)
{
    Vector64<double> vecX = Vector64.Create(x);
    Vector64<double> vecY = Vector64.Create(y);
    Vector64<double> vecZ = Vector64.Create(z);
    Vector64<double> rez  = AdvSimd.FusedMultiplyAddScalar(vecX, vecY, vecZ);
    return rez.ToScalar();
}
Assert failure(PID 11202 [0x00002bc2], Thread: 11202 [0x2bc2]): Assertion failed '!"Didn't find a class handle for simdType"' in 'Repro:Test4(double,double,double):double' during 'Importation' (IL size 33)

    File: /home/egorb/runtime/src/coreclr/src/jit/hwintrinsic.cpp Line: 218

The operand order difference is expected. ARM64 takes them as addend, left, right rather then left, right, addend

@tannergooding I changed the order to GetEmitter()->emitIns_R_R_R_R(ins, emitSize, targetReg, op1Reg, op2Reg, op3Reg); re-compiled and got expected 14 for my sample

@tannergooding or are you saying I should reverse them when I call the C# API?

When constructing the intrinsic node, you should order them as op3, op1, op2, rather than as op1, op2, op3

I have a draft change in my private branch https://github.com/echesakovMSFT/runtime/tree/Arm64-Math-MathF-FusedMultiplyAdd that I did as a background task a while ago. I can resurrect the change and finish it if this is needed for 5.0.

Sorry guys, I was on a out-of-date branch, master works fine for me.

@EgorBo Are you gonna continue working on this or you want me to take over?

@jkotas @tannergooding Do you think this work belong to 5.0?

It should be a trivial change that helps boost perf for functions that use it, I imagine taking it would be worthwhile l.

Well, actually, no the both problems I listed ^ still here on fresh master.
Here is my draft: https://github.com/dotnet/runtime/compare/master...EgorBo:arm64-math-fma?expand=1
it works fine for float:

using System;
using System.Runtime.CompilerServices;

class Repro
{
    static void Main()
    {
        Console.WriteLine(Test_f(2,4,6));
        //Console.WriteLine(Test_d(2,4,6));
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static float Test_f(float x, float y, float z)
    {
        return MathF.FusedMultiplyAdd(x, y, z);
    }

    [MethodImpl(MethodImplOptions.NoInlining)]
    static double Test_d(double x, double y, double z)
    {
        return Math.FusedMultiplyAdd(x, y, z);
    }
}

Codegen for Test_f:

G_M29070_IG01:
        A9BF7BFD          stp     fp, lr, [sp,#-16]!
        910003FD          mov     fp, sp
                                                ;; bbWeight=1    PerfScore 1.50
G_M29070_IG02:
        1E204030          fmov    s16, s1
        1E204051          fmov    s17, s2
        1F004220          fmadd   s0, s17, s0, s16
                                                ;; bbWeight=1    PerfScore 4.00
G_M29070_IG03:
        A8C17BFD          ldp     fp, lr, [sp],#16
        D65F03C0          ret     lr
                                                ;; bbWeight=1    PerfScore 2.00

; Total bytes of code 28, prolog size 8, PerfScore 10.30, (MethodHash=16048e71) for method Repro:Test_f(float,float,float):float
; ============================================================

However, it crashes for Test_d

Assert failure(PID 4276 [0x000010b4], Thread: 4276 [0x10b4]): Assertion failed 'ins != INS_invalid' in 'Repro:Test_d(double,double,double):double' during 'Generate code' (IL size 9)

    File: /home/egorb/runtime/src/coreclr/src/jit/hwintrinsiccodegenarm64.cpp Line: 485

UPD oh, maybe NI_Vector64_CreateScalarUnsafe just doesn't support double and should be just NI_Vector64_Create
@echesakovMSFT feel free to take over 馃檪 I see your branch also handles FMA variations (depending on NEG).

However, it crashes for Test_d

Let me take a look

Marking as 5.0.

@echesakovMSFT ah, nvm, fixed via NI_Vector64_Create for double.
Full PR: https://github.com/dotnet/runtime/compare/master...EgorBo:arm64-math-fma?expand=1 now works for both float and double 馃檪

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jzabroski picture jzabroski  路  3Comments

GitAntoinee picture GitAntoinee  路  3Comments

EgorBo picture EgorBo  路  3Comments

chunseoklee picture chunseoklee  路  3Comments

bencz picture bencz  路  3Comments