Release Emilia Pinball


Magic Sam

Forever Homebrew
Joined
Aug 10, 2007
Messages
2,501
Age
40
Location
Penguin Village
Hi all :)

@levi : same thing here. I also thought "-mfloat-abi=softfp" meant there is no "real" hardware FPU and thus floating point operations were done via software emulation, while "hardfp" meant true hardware support...

Looks like I was all wrong (as usual) :p

Cheers, Magic Sam
 

ptitSeb

Serial Porter
Joined
Aug 15, 2012
Messages
9,195
Age
49
Location
France, near Lyon
Yep, it's a confusing name...

To wrapup:
-mfloat-abi=soft => All software mode
-mfloat-abi=softfp => Hardware FP used, but using general register / stack as parameters for function, making this mode ABI compatible (i.e. Application Binary Interface, meaning it just work without needing to recompile) with "soft" mode.
-mfloat-abi=hard => Hardware FP used, and parameter for function can use FP register, making this mode not-ABI compatible with the 2 previous one (but can be faster, because you don't need to copy from/to general register or stack).
 

Magic Sam

Forever Homebrew
Joined
Aug 10, 2007
Messages
2,501
Age
40
Location
Penguin Village
@ptitSeb : thanks for the explanation :)

According to GCC 6.2 manual:
-mfpu=name
This specifies what floating-point hardware (or hardware emulation) is available on the target. Permissible names are: ‘vfp’, ‘vfpv3’, ‘vfpv3-fp16’, ‘vfpv3-d16’, ‘vfpv3-d16-fp16’, ‘vfpv3xd’, ‘vfpv3xd-fp16’, ‘neon’, ‘neon-fp16’, ‘vfpv4’, ‘vfpv4-d16’, ‘fpv4-sp-d16’, ‘neon-vfpv4’, ‘fpv5-d16’, ‘fpv5-sp-d16’, ‘fp-armv8’, ‘neon-fp-armv8’ and ‘crypto-neon-fp-armv8’.

If -msoft-float is specified this specifies the format of floating-point values.

If the selected floating-point hardware includes the NEON extension (e.g. -mfpu=‘neon’), note that floating-point operations are not generated by GCC's auto-vectorization pass unless -funsafe-math-optimizations is also specified. This is because NEON hardware does not fully implement the IEEE 754 standard for floating-point arithmetic (in particular denormal values are treated as zero), so the use of NEON instructions may lead to a loss of precision.
If I understand correctly, if one specifies -mfpu=neon with -funsafe-math-optimizations (as in Code:Blocks by default) then NEON code is automatically generated by GCC, and one doesn't have to fiddle around with ARM's intrinsics, right ?

What about all the other permissible values (vfp, vfpv3, etc...) ? Do you know how they perform compared to -mfpu=neon ?

Thanks, Magic Sam
 

ptitSeb

Serial Porter
Joined
Aug 15, 2012
Messages
9,195
Age
49
Location
France, near Lyon
-mfpu=neon activate NEON code. But VFPv3 will still be used when needed (for double math for example).

And yes, to activate certain operation to be done in NEON, you need -funsafe-math-optimizations (or -ffast-math that include it, -Ofast that is -O3 -ffast-math), because NEON is not IEEE certified, so some borderline case implying infinite will not be correct. Also, NEON doen't have a divide operation. Using unsafe math, you calculate 1.0f/X (NEON has some helper operation that can calculate an aproximate value quickly) and multiply by the value... Things like that.

After that, GCC try to vectorize, but you can still use intrinsec or inline asm and try doing a better job than GCC (GCC is improving, but rethinking some loop to be natively SIMD always win).
[doublepost=1479482052,1479479156][/doublepost]@Magic Sam : if you want to see by yourself.

Go to https://godbolt.org/
Select ARM gcc 5.4 and use typical pandora flags "-mcpu=cortex-a8 -mfpu=neon -mfloat-abi=softfp -Ofast"

Copy paste this code (it's from gl4es, one of the Array function, slightly adapted so it compile by itself)
Code:
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

#define NEON

void *copy_gl_pointer_color_bgra(void *ptr, size_t width, size_t skip, size_t count) {
    // this one only convert from BGRA (unsigned byte) to RGBA FLOAT
    unsigned char* src = (unsigned char*)ptr;
    int stride = 0;

    if (! src || !(count-skip))
        return NULL;
                        
    if (! stride)
        stride = 4;

    void* out = malloc(4*sizeof(float)*(count-skip));
    float* dst = (float*)out;
    src += skip*(stride);

    static const float d = 1.0f/255.0f;
    for (int i=skip; i<count; i++) {
        #if defined(NEON) && defined(__ARM_NEON__)
        int lsrc = *(int*)src;
        lsrc = (lsrc&0xff00ff00) | ((lsrc&0x00ff0000)>>16) | ((lsrc&0x000000ff)<<16);
        asm volatile (
        "vmov           s12, %1              \n\t"   // because you cannot vmovl.u8 d6, s11
        "vmovl.u8       q3, d6               \n\t"   // Expand to 16-bit (so unsetuped s13 is expanded in d7)
        "vmovl.u16      q3, d6               \n\t"   // Expand to 32-bit, ignoring expanded d7
        "vcvt.f32.u32   q3, q3               \n\t"   // Convert to float
        "vmul.f32       q3, q3, %y2          \n\t"   // Normlize
        "vst1.f32       {q3}, [%0]!          \n\t"   // Store, next
        :"+r"(dst) :"w"(lsrc), "w"(d)
        : "q3", "memory"
        );
        #else
        const unsigned char b = src[0], g = src[1], r = src[2], a = src[3];
        *dst++ = r*d;
        *dst++ = g*d;
        *dst++ = b*d;
        *dst++ = a*d;
        #endif
        src+=stride;
    }
    return out;
}

And look at the assembly out...
Then comment the "#define NEON" and see the new code (and see how the code seems to have much more instruction)...
 
Top