diff --git a/Makefile.am b/Makefile.am index 3c3671620..05e534feb 100644 --- a/Makefile.am +++ b/Makefile.am @@ -81,12 +81,8 @@ libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_SOURCES = png.c pngerror.c\ png.h pngconf.h pngdebug.h pnginfo.h pngpriv.h pngstruct.h pngusr.dfa if PNG_ARM_NEON - libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_SOURCES += arm/arm_init.c \ - if PNG_ARM_NEON_INTRINSICS - arm/filter_neon_intrinsics.c - else - arm/filter_neon.S - endif + libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_SOURCES += arm/arm_init.c\ + arm/filter_neon.S arm/filter_neon_intrinsics.c endif nodist_libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_SOURCES = pnglibconf.h diff --git a/arm/filter_neon.S b/arm/filter_neon.S index b8aef1053..d77ba59ca 100644 --- a/arm/filter_neon.S +++ b/arm/filter_neon.S @@ -20,6 +20,12 @@ .section .note.GNU-stack,"",%progbits /* mark stack as non-executable */ #endif +/* Assembler NEON support - only works for 32-bit ARM (i.e. it does not work for + * ARM64). The code in arm/filter_neon_intrinsics.c supports ARM64, however it + * only works if -mfpu=neon is specified on the GCC command line. + */ +#ifndef __ARM_NEON__ /* else use arm/filter_neon_intrinsics.c */ + #ifdef PNG_READ_SUPPORTED #if PNG_ARM_NEON_OPT > 0 @@ -235,3 +241,4 @@ func png_read_filter_row_paeth3_neon, export=1 endfunc #endif /* PNG_ARM_NEON_OPT > 0 */ #endif /* PNG_READ_SUPPORTED */ +#endif /* !__ARM_NEON__ */ diff --git a/arm/filter_neon_intrinsics.c b/arm/filter_neon_intrinsics.c index 334c1f5c1..9f15cd574 100644 --- a/arm/filter_neon_intrinsics.c +++ b/arm/filter_neon_intrinsics.c @@ -14,11 +14,30 @@ #include "pngpriv.h" -#ifdef PNG_READ_SUPPORTED -#if PNG_ARM_NEON_OPT > 0 +/* This code requires -mfpu=neon on the command line: */ +#ifdef __ARM_NEON__ /* else use arm/filter_neon.S */ #include +/* libpng row pointers are not necessarily aligned to any particular boundary, + * however this code will only work with appropriate alignment. arm/arm_init.c + * checks for this (and will not compile unless it is done), this code uses + * variants of png_aligncast to avoid compiler warnings. + */ +#define png_ptr(type,pointer) png_aligncast(type *,pointer) +#define png_ptrc(type,pointer) png_aligncastconst(const type *,pointer) + +/* The following relies on a variable 'temp_pointer' being declared with type + * 'type'. This is written this way just to hide the GCC strict aliasing + * warning; note that the code is safe because there never is an alias between + * the input and output pointers. + */ +#define png_ldr(type,pointer)\ + (temp_pointer = png_ptr(type,pointer), *temp_pointer) + +#ifdef PNG_READ_SUPPORTED +#if PNG_ARM_NEON_OPT > 0 + void png_read_filter_row_up_neon(png_row_infop row_info, png_bytep row, png_const_bytep prev_row) @@ -27,10 +46,10 @@ png_read_filter_row_up_neon(png_row_infop row_info, png_bytep row, png_bytep rp_stop = row + row_info->rowbytes; png_const_bytep pp = prev_row; - uint8x16_t qrp, qpp; - for (; rp != rp_stop; rp += 16, pp += 16) { + uint8x16_t qrp, qpp; + qrp = vld1q_u8(rp); qpp = vld1q_u8(pp); qrp = vaddq_u8(qrp, qpp); @@ -38,24 +57,25 @@ png_read_filter_row_up_neon(png_row_infop row_info, png_bytep row, } } -void inline +void png_read_filter_row_sub3_neon(png_row_infop row_info, png_bytep row, png_const_bytep prev_row) { png_bytep rp = row; png_bytep rp_stop = row + row_info->rowbytes; - PNG_UNUSED(prev_row) + uint8x16_t vtmp = vld1q_u8(rp); + uint8x8x2_t *vrpt = png_ptr(uint8x8x2_t, &vtmp); + uint8x8x2_t vrp = *vrpt; - uint8x8_t vtmp1, vtmp2; uint8x8x4_t vdest; vdest.val[3] = vdup_n_u8(0); - uint8x16_t vtmp = vld1q_u8(rp); - uint8x8x2_t vrp = *((uint8x8x2_t *)(&vtmp)); - for (; rp != rp_stop;) { + uint8x8_t vtmp1, vtmp2; + uint32x2_t *temp_pointer; + vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3); vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]); vtmp2 = vext_u8(vrp.val[0], vrp.val[1], 6); @@ -66,17 +86,20 @@ png_read_filter_row_sub3_neon(png_row_infop row_info, png_bytep row, vdest.val[3] = vadd_u8(vdest.val[2], vtmp1); vtmp = vld1q_u8(rp + 12); - vrp = *((uint8x8x2_t *)(&vtmp)); + vrpt = png_ptr(uint8x8x2_t, &vtmp); + vrp = *vrpt; - vst1_lane_u32((uint32_t *)rp, *((uint32x2_t *)(&vdest.val[0])), 0); + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0); rp += 3; - vst1_lane_u32((uint32_t *)rp, *((uint32x2_t *)(&vdest.val[1])), 0); + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0); rp += 3; - vst1_lane_u32((uint32_t *)rp, *((uint32x2_t *)(&vdest.val[2])), 0); + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0); rp += 3; - vst1_lane_u32((uint32_t *)rp, *((uint32x2_t *)(&vdest.val[3])), 0); + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0); rp += 3; } + + PNG_UNUSED(prev_row) } void @@ -86,22 +109,24 @@ png_read_filter_row_sub4_neon(png_row_infop row_info, png_bytep row, png_bytep rp = row; png_bytep rp_stop = row + row_info->rowbytes; - PNG_UNUSED(prev_row) - uint8x8x4_t vdest; vdest.val[3] = vdup_n_u8(0); for (; rp != rp_stop; rp += 16) { - uint32x2x4_t vtmp = vld4_u32((uint32_t *)rp); - uint8x8x4_t vrp = *((uint8x8x4_t *)(&vtmp)); + uint32x2x4_t vtmp = vld4_u32(png_ptr(uint32_t,rp)); + uint8x8x4_t *vrpt = png_ptr(uint8x8x4_t,&vtmp); + uint8x8x4_t vrp = *vrpt; + uint32x2x4_t *temp_pointer; vdest.val[0] = vadd_u8(vdest.val[3], vrp.val[0]); vdest.val[1] = vadd_u8(vdest.val[0], vrp.val[1]); vdest.val[2] = vadd_u8(vdest.val[1], vrp.val[2]); vdest.val[3] = vadd_u8(vdest.val[2], vrp.val[3]); - vst4_lane_u32((uint32_t *)rp, *((uint32x2x4_t *)(&vdest)), 0); + vst4_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2x4_t,&vdest), 0); } + + PNG_UNUSED(prev_row) } void @@ -113,18 +138,27 @@ png_read_filter_row_avg3_neon(png_row_infop row_info, png_bytep row, png_bytep rp_stop = row + row_info->rowbytes; uint8x16_t vtmp; - uint8x8x2_t vrp, vpp; - uint8x8_t vtmp1, vtmp2, vtmp3; + uint8x8x2_t *vrpt; + uint8x8x2_t vrp; uint8x8x4_t vdest; vdest.val[3] = vdup_n_u8(0); vtmp = vld1q_u8(rp); - vrp = *((uint8x8x2_t *)(&vtmp)); + vrpt = png_ptr(uint8x8x2_t,&vtmp); + vrp = *vrpt; for (; rp != rp_stop; pp += 12) { + uint8x8_t vtmp1, vtmp2, vtmp3; + + uint8x8x2_t *vppt; + uint8x8x2_t vpp; + + uint32x2_t *temp_pointer; + vtmp = vld1q_u8(pp); - vpp = *((uint8x8x2_t *)(&vtmp)); + vppt = png_ptr(uint8x8x2_t,&vtmp); + vpp = *vppt; vtmp1 = vext_u8(vrp.val[0], vrp.val[1], 3); vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]); @@ -139,7 +173,8 @@ png_read_filter_row_avg3_neon(png_row_infop row_info, png_bytep row, vtmp1 = vext_u8(vrp.val[1], vrp.val[1], 1); vtmp = vld1q_u8(rp + 12); - vrp = *((uint8x8x2_t *)(&vtmp)); + vrpt = png_ptr(uint8x8x2_t,&vtmp); + vrp = *vrpt; vdest.val[2] = vhadd_u8(vdest.val[1], vtmp2); vdest.val[2] = vadd_u8(vdest.val[2], vtmp3); @@ -149,13 +184,13 @@ png_read_filter_row_avg3_neon(png_row_infop row_info, png_bytep row, vdest.val[3] = vhadd_u8(vdest.val[2], vtmp2); vdest.val[3] = vadd_u8(vdest.val[3], vtmp1); - vst1_lane_u32((uint32_t *)rp, *((uint32x2_t *)(&vdest.val[0])), 0); + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0); rp += 3; - vst1_lane_u32((uint32_t *)rp, *((uint32x2_t *)(&vdest.val[1])), 0); + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0); rp += 3; - vst1_lane_u32((uint32_t *)rp, *((uint32x2_t *)(&vdest.val[2])), 0); + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0); rp += 3; - vst1_lane_u32((uint32_t *)rp, *((uint32x2_t *)(&vdest.val[3])), 0); + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0); rp += 3; } } @@ -168,17 +203,22 @@ png_read_filter_row_avg4_neon(png_row_infop row_info, png_bytep row, png_bytep rp_stop = row + row_info->rowbytes; png_const_bytep pp = prev_row; - uint32x2x4_t vtmp; - uint8x8x4_t vrp, vpp; uint8x8x4_t vdest; vdest.val[3] = vdup_n_u8(0); for (; rp != rp_stop; rp += 16, pp += 16) { - vtmp = vld4_u32((uint32_t *)rp); - vrp = *((uint8x8x4_t *)(&vtmp)); - vtmp = vld4_u32((uint32_t *)pp); - vpp = *((uint8x8x4_t *)(&vtmp)); + uint32x2x4_t vtmp; + uint8x8x4_t *vrpt, *vppt; + uint8x8x4_t vrp, vpp; + uint32x2x4_t *temp_pointer; + + vtmp = vld4_u32(png_ptr(uint32_t,rp)); + vrpt = png_ptr(uint8x8x4_t,&vtmp); + vrp = *vrpt; + vtmp = vld4_u32(png_ptrc(uint32_t,pp)); + vppt = png_ptr(uint8x8x4_t,&vtmp); + vpp = *vppt; vdest.val[0] = vhadd_u8(vdest.val[3], vpp.val[0]); vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]); @@ -189,11 +229,11 @@ png_read_filter_row_avg4_neon(png_row_infop row_info, png_bytep row, vdest.val[3] = vhadd_u8(vdest.val[2], vpp.val[3]); vdest.val[3] = vadd_u8(vdest.val[3], vrp.val[3]); - vst4_lane_u32((uint32_t *)rp, *((uint32x2x4_t *)(&vdest)), 0); + vst4_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2x4_t,&vdest), 0); } } -uint8x8_t +static uint8x8_t paeth(uint8x8_t a, uint8x8_t b, uint8x8_t c) { uint8x8_t d, e; @@ -229,19 +269,26 @@ png_read_filter_row_paeth3_neon(png_row_infop row_info, png_bytep row, png_bytep rp_stop = row + row_info->rowbytes; uint8x16_t vtmp; - uint8x8x2_t vrp, vpp; - uint8x8_t vtmp1, vtmp2, vtmp3; + uint8x8x2_t *vrpt; + uint8x8x2_t vrp; uint8x8_t vlast = vdup_n_u8(0); uint8x8x4_t vdest; vdest.val[3] = vdup_n_u8(0); vtmp = vld1q_u8(rp); - vrp = *((uint8x8x2_t *)(&vtmp)); + vrpt = png_ptr(uint8x8x2_t,&vtmp); + vrp = *vrpt; for (; rp != rp_stop; pp += 12) { + uint8x8x2_t *vppt; + uint8x8x2_t vpp; + uint8x8_t vtmp1, vtmp2, vtmp3; + uint32x2_t *temp_pointer; + vtmp = vld1q_u8(pp); - vpp = *((uint8x8x2_t *)(&vtmp)); + vppt = png_ptr(uint8x8x2_t,&vtmp); + vpp = *vppt; vdest.val[0] = paeth(vdest.val[3], vpp.val[0], vlast); vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]); @@ -260,20 +307,21 @@ png_read_filter_row_paeth3_neon(png_row_infop row_info, png_bytep row, vtmp2 = vext_u8(vpp.val[1], vpp.val[1], 1); vtmp = vld1q_u8(rp + 12); - vrp = *((uint8x8x2_t *)(&vtmp)); + vrpt = png_ptr(uint8x8x2_t,&vtmp); + vrp = *vrpt; vdest.val[3] = paeth(vdest.val[2], vtmp2, vtmp3); vdest.val[3] = vadd_u8(vdest.val[3], vtmp1); vlast = vtmp2; - vst1_lane_u32((uint32_t *)rp, *((uint32x2_t *)(&vdest.val[0])), 0); + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[0]), 0); rp += 3; - vst1_lane_u32((uint32_t *)rp, *((uint32x2_t *)(&vdest.val[1])), 0); + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[1]), 0); rp += 3; - vst1_lane_u32((uint32_t *)rp, *((uint32x2_t *)(&vdest.val[2])), 0); + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[2]), 0); rp += 3; - vst1_lane_u32((uint32_t *)rp, *((uint32x2_t *)(&vdest.val[3])), 0); + vst1_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2_t,&vdest.val[3]), 0); rp += 3; } } @@ -286,18 +334,23 @@ png_read_filter_row_paeth4_neon(png_row_infop row_info, png_bytep row, png_bytep rp_stop = row + row_info->rowbytes; png_const_bytep pp = prev_row; - uint32x2x4_t vtmp; - uint8x8x4_t vrp, vpp; uint8x8_t vlast = vdup_n_u8(0); uint8x8x4_t vdest; vdest.val[3] = vdup_n_u8(0); for (; rp != rp_stop; rp += 16, pp += 16) { - vtmp = vld4_u32((uint32_t *)rp); - vrp = *((uint8x8x4_t *)(&vtmp)); - vtmp = vld4_u32((uint32_t *)pp); - vpp = *((uint8x8x4_t *)(&vtmp)); + uint32x2x4_t vtmp; + uint8x8x4_t *vrpt, *vppt; + uint8x8x4_t vrp, vpp; + uint32x2x4_t *temp_pointer; + + vtmp = vld4_u32(png_ptr(uint32_t,rp)); + vrpt = png_ptr(uint8x8x4_t,&vtmp); + vrp = *vrpt; + vtmp = vld4_u32(png_ptrc(uint32_t,pp)); + vppt = png_ptr(uint8x8x4_t,&vtmp); + vpp = *vppt; vdest.val[0] = paeth(vdest.val[3], vpp.val[0], vlast); vdest.val[0] = vadd_u8(vdest.val[0], vrp.val[0]); @@ -310,9 +363,10 @@ png_read_filter_row_paeth4_neon(png_row_infop row_info, png_bytep row, vlast = vpp.val[3]; - vst4_lane_u32((uint32_t *)rp, *((uint32x2x4_t *)(&vdest)), 0); + vst4_lane_u32(png_ptr(uint32_t,rp), png_ldr(uint32x2x4_t,&vdest), 0); } } #endif /* PNG_ARM_NEON_OPT > 0 */ #endif /* PNG_READ_SUPPORTED */ +#endif /* __ARM_NEON__ */ diff --git a/configure.ac b/configure.ac index a7bcf3ba8..1a6019298 100644 --- a/configure.ac +++ b/configure.ac @@ -248,13 +248,13 @@ AC_ARG_ENABLE([arm-neon], [Disable ARM Neon optimizations]) # Prevent inclusion of the assembler files below: enable_arm_neon=no;; - check|check-intrinsics) + check) AC_DEFINE([PNG_ARM_NEON_CHECK_SUPPORTED], [], [Check for ARM Neon support at run-time]);; - api|api-intrinsics) + api) AC_DEFINE([PNG_ARM_NEON_API_SUPPORTED], [], [Turn on ARM Neon optimizations at run-time]);; - yes|on|yes-intrinsics|on-intrinsics) + yes|on) AC_DEFINE([PNG_ARM_NEON_OPT], [2], [Enable ARM Neon optimizations]) AC_MSG_WARN([--enable-arm-neon: please specify 'check' or 'api', if] @@ -275,16 +275,6 @@ AM_CONDITIONAL([PNG_ARM_NEON], *) test "$enable_arm_neon" != '';; esac]) -case "$enable_arm_neon" in - *-intrinsics) - use_arm_intrinsics='yes';; - *) - use_arm_intrinsics='no';; -esac - -AM_CONDITIONAL([PNG_ARM_NEON_INTRINSICS], - [test "$use_arm_intrinsics" = 'yes']) - AC_MSG_NOTICE([[Extra options for compiler: $PNG_COPTS]]) # Config files, substituting as above