libpng: Add support for RISC-V Vector Extension
Manfred Schlaegl <[email protected]> Thu, 16 Dec 2021 15:19:23 +0100
| Newsgroups | gmane.comp.graphics.png.devel |
|---|---|
| Message-ID | <[email protected]> |
Hello,
The long awaited RISC-V Vector Extension (RVV) is finally in
ratification.[1] The attached patches for libpng16 add support
for RVV optimized png filter types to libpng:
* 0001-INSTALL-added-missing-contrib-content.patch
* Contains a simple fix in INSTALL but is necessary for
applying the main patch
* 0002-libpng-Add-support-for-RISC-V-Vector-Extension.patch
* Support for RISC-V Vector Extension
There is also a fork on github: https://github.com/mschlaegl/libpng_rvv
Development and performance evaluation of the filter type
implementations (up, sub, avg and paeth) was done on Allwinner D1 with
RVV 0.7.1 using RVVRadar. Details on that can be found in an article
published on github.[2]
The integration in libpng was done very similarly as for other
architectures (ARM NEON, MIPS MSA, ...): There exists a configure
switch "riscv-vector" which can be set to no/off, check, api, yes/on.
And there is also run-time checking code (like for ARM NEON,
MIPS MSA, ...).
More important are the differences:
1. We introduce a separate configure switch "riscv-vector-compat"
2. We use inline assembler instead of compiler intrinsics
Both is done for the same reason: To support multiple RVV versions.
As mentioned above, RVV 1.0 is under ratification now. But there are
earlier draft versions which are sporadically already implemented in
real existing hardware. One example is the Allwinner D1 with RVV 0.7.1
which was used for performance evaluation and optimization of these
filter implementations.[2] We therefore decided to support the
following RVV versions: 0.7.1, 0.8, 0.9, 0.10 and 1.0.
Unfortunately, there is no easy way to detect the RVV version
supported by the toolchain. It was therefore necessary to add an
additional configure switch "riscv-vector-compat" with following
behavior:
* not-set/off(default): RVV release 1.0
* set to "0.10": RVV draft 0.10
* set to "0.9": RVV draft 0.9
* set to "0.8": RVV draft 0.8
* set to "0.7.1": RVV draft 0.7.1
Furthermore compiler intrinsics were not available for all supported
RVV versions. For this reason we had to use inline assembler instead
of compiler intrinsics.
The filter implementations for all supported RVV versions were tested
in the following setups:
RVV | Branch of | Test system
Version | riscv-gnu-toolchain[3] |
---------------------------------------------------------------------
1.0 | "basic-rvv" | qemu_sifive [4]
0.10 | "rvv-intrinsic" | qemu_sifive [4]
0.9 | "rvv-0.9.x" | Allwinner D1 (binary compatible)
0.8 | "rvv-0.8.x" | Allwinner D1 (binary compatible)
0.7.1 | "rvv-0.7.1" | Allwinner D1 (RVV 0.7.1)
References:
-----------
[1] https://riscv.org/announcements/2021/12/riscv-ratifies-15-new-specifications/
[2] https://github.com/mschlaegl/libpng_rvv-doc/blob/main/README.md
[3] https://github.com/riscv-collab/riscv-gnu-toolchain
[4] https://github.com/sifive/qemu/tree/rvv-1.0-upstream-v10
_______________________________________________
png-mng-implement mailing list
png-mng-implement-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f@public.gmane.org
https://lists.sourceforge.net/lists/listinfo/png-mng-implement
0001-INSTALL-added-missing-contrib-content.patch
(text/x-patch, 1 KB)
From 9ae37aee1cdc8b9973981d6a349f67c846a4d587 Mon Sep 17 00:00:00 2001 From: Manfred SCHLAEGL <[email protected]> Date: Wed, 22 Sep 2021 15:52:00 +0200 Subject: [PATCH 1/2] INSTALL: added missing contrib content --- INSTALL | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/INSTALL b/INSTALL index 4c1702251..ee73ecd7e 100644 --- a/INSTALL +++ b/INSTALL @@ -134,8 +134,9 @@ Your directory structure should look like this: libpng-config.in, aclocal.m4, config.h.in, config.sub, depcomp, install-sh, mkinstalldirs, test-pngtest.sh contrib - arm-neon, conftest, examples, gregbook, libtests, pngminim, - pngminus, pngsuite, tools, visupng + arm-neon, conftest, examples, gregbook, libtests, mips-msa, + oss-fuzz, pngminim, pngminus, pngsuite, powerpc-vsx, README.txt, + testpngs, tools, visupng projects cbuilder5, owatcom, visualc71, vstudio, xcode scripts -- 2.30.2
0002-libpng-Add-support-for-RISC-V-Vector-Extension.patch
(text/x-patch, 52.1 KB)
From 56e0354b5373d205f2c38b412665538d1880b0f5 Mon Sep 17 00:00:00 2001 From: Manfred SCHLAEGL <[email protected]> Date: Wed, 22 Sep 2021 15:51:13 +0200 Subject: [PATCH 2/2] libpng: Add support for RISC-V Vector Extension The long awaited RISC-V Vector Extension (RVV) is finally in ratification.[1] With this we add support for RVV optimized png filter types to libpng. Development and performance evaluation of the filter type implementations (up, sub, avg and paeth) was done on Allwinner D1 with RVV 0.7.1 using RVVRadar. Details on that can be found in an article published on github.[2] The integration in libpng was done very similarly as for other architectures (ARM NEON, MIPS MSA, ...): There exists a configure switch "riscv-vector" which can be set to no/off, check, api, yes/on. And there is also run-time checking code (like for ARM NEON, MIPS MSA, ...). More important are the differences: 1. We introduce a separate configure switch "riscv-vector-compat" 2. We use inline assembler instead of compiler intrinsics Both is done for the same reason: To support multiple RVV versions. As mentioned above, RVV 1.0 is under ratification now. But there are earlier draft versions which are sporadically already implemented in real existing hardware. One example is the Allwinner D1 with RVV 0.7.1 which was used for performance evaluation and optimization of these filter implementations.[2] We therefore decided to support the following RVV versions: 0.7.1, 0.8, 0.9, 0.10 and 1.0. Unfortunately, there is no easy way to detect the RVV version supported by the toolchain. It was therefore necessary to add an additional configure switch "riscv-vector-compat" with following behavior: * not-set/off(default): RVV release 1.0 * set to "0.10": RVV draft 0.10 * set to "0.9": RVV draft 0.9 * set to "0.8": RVV draft 0.8 * set to "0.7.1": RVV draft 0.7.1 Furthermore compiler intrinsics were not available for all supported RVV versions. For this reason we had to use inline assembler instead of compiler intrinsics. The filter implementations for all supported RVV versions were tested in the following setups: RVV | Branch of | Test system Version | riscv-gnu-toolchain[3] | --------------------------------------------------------------------- 1.0 | "basic-rvv" | qemu_sifive [4] 0.10 | "rvv-intrinsic" | qemu_sifive [4] 0.9 | "rvv-0.9.x" | Allwinner D1 (binary compatible) 0.8 | "rvv-0.8.x" | Allwinner D1 (binary compatible) 0.7.1 | "rvv-0.7.1" | Allwinner D1 (RVV 0.7.1) References: ----------- [1] https://riscv.org/announcements/2021/12/riscv-ratifies-15-new-specifications/ [2] https://github.com/mschlaegl/libpng_rvv-doc/blob/main/README.md [3] https://github.com/riscv-collab/riscv-gnu-toolchain [4] https://github.com/sifive/qemu/tree/rvv-1.0-upstream-v10 --- CMakeLists.txt | 30 +++ INSTALL | 10 +- Makefile.am | 5 + Makefile.in | 50 ++++- README | 2 + config.h.in | 13 ++ configure | 120 +++++++++++ configure.ac | 84 ++++++++ contrib/riscv-vector/README | 82 +++++++ contrib/riscv-vector/linux.c | 83 ++++++++ png.h | 5 +- pngpriv.h | 29 +++ riscv/filter_vector_intrinsics.c | 354 +++++++++++++++++++++++++++++++ riscv/riscv_init.c | 128 +++++++++++ scripts/makefile.linux-opt | 5 +- 15 files changed, 985 insertions(+), 15 deletions(-) create mode 100644 contrib/riscv-vector/README create mode 100644 contrib/riscv-vector/linux.c create mode 100644 riscv/filter_vector_intrinsics.c create mode 100644 riscv/riscv_init.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 42ff0f902..1588e2c58 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -163,6 +163,30 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "mipsel*" OR endif() endif() +# Set definitions and sources for RISC-V. +if(CMAKE_SYSTEM_PROCESSOR MATCHES "^riscv*") + set(PNG_RISCV_VECTOR_POSSIBLE_VALUES check on off) + set(PNG_RISCV_VECTOR "check" + CACHE STRING "Enable RISC-V Vector optimizations: check|on|off; check is default") + set_property(CACHE PNG_RISCV_VECTOR + PROPERTY STRINGS ${PNG_RISCV_VECTOR_POSSIBLE_VALUES}) + list(FIND PNG_RISCV_VECTOR_POSSIBLE_VALUES ${PNG_RISCV_VECTOR} index) + if(index EQUAL -1) + message(FATAL_ERROR "PNG_RISCV_VECTOR must be one of [${PNG_RISCV_VECTOR_POSSIBLE_VALUES}]") + elseif(NOT ${PNG_RISCV_VECTOR} STREQUAL "off") + set(libpng_riscv_sources + riscv/riscv_init.c + riscv/filter_vector_intrinsics.c) + if(${PNG_RISCV_VECTOR} STREQUAL "on") + add_definitions(-DPNG_RISCV_VECTOR_OPT=2) + elseif(${PNG_RISCV_VECTOR} STREQUAL "check") + add_definitions(-DPNG_RISCV_VECTOR_CHECK_SUPPORTED) + endif() + else() + add_definitions(-DPNG_RISCV_VECTOR_OPT=0) + endif() +endif() + else(PNG_HARDWARE_OPTIMIZATIONS) # Set definitions and sources for ARM. @@ -189,6 +213,11 @@ if(CMAKE_SYSTEM_PROCESSOR MATCHES "mipsel*" OR add_definitions(-DPNG_MIPS_MSA_OPT=0) endif() +# Set definitions and sources for RISC-V. +if(CMAKE_SYSTEM_PROCESSOR MATCHES "^riscv") + add_definitions(-DPNG_RISCV_VECTOR_OPT=0) +endif() + endif(PNG_HARDWARE_OPTIMIZATIONS) # Set PNG_LIB_NAME. @@ -488,6 +517,7 @@ set(libpng_sources ${libpng_intel_sources} ${libpng_mips_sources} ${libpng_powerpc_sources} + ${libpng_riscv_sources} ) set(pngtest_sources pngtest.c diff --git a/INSTALL b/INSTALL index ee73ecd7e..72965037b 100644 --- a/INSTALL +++ b/INSTALL @@ -136,7 +136,7 @@ Your directory structure should look like this: contrib arm-neon, conftest, examples, gregbook, libtests, mips-msa, oss-fuzz, pngminim, pngminus, pngsuite, powerpc-vsx, README.txt, - testpngs, tools, visupng + riscv-vector, testpngs, tools, visupng projects cbuilder5, owatcom, visualc71, vstudio, xcode scripts @@ -293,6 +293,7 @@ such as one of --enable-mips-msa=yes --enable-intel-sse=yes --enable-powerpc-vsx=yes + --enable-riscv-vector=yes or enable them all at once with @@ -305,6 +306,7 @@ or more of CPPFLAGS += "-DPNG_MIPS_MSA" CPPFLAGS += "-DPNG_INTEL_SSE" CPPFLAGS += "-DPNG_POWERPC_VSX" + CPPFLAGS += "-DPNG_RISCV_VECTOR" See for example scripts/makefile.linux-opt @@ -321,13 +323,15 @@ to disable a particular one, or via compiler-command options such as CPPFLAGS += "-DPNG_ARM_NEON_OPT=0, -DPNG_MIPS_MSA_OPT=0, - -DPNG_INTEL_SSE_OPT=0, -DPNG_POWERPC_VSX_OPT=0" + -DPNG_INTEL_SSE_OPT=0, -DPNG_POWERPC_VSX_OPT=0, + -DPNG_RISCV_VECTOR_OPT=0" If you are using cmake, hardware optimizations are "on" by default. To disable them, use cmake . -DPNG_ARM_NEON=no -DPNG_INTEL_SSE=no \ - -DPNG_MIPS_MSA=no -DPNG_POWERPC_VSX=no + -DPNG_MIPS_MSA=no -DPNG_POWERPC_VSX=no \ + -DPNG_RISCV_VECTOR=no or disable them all at once with diff --git a/Makefile.am b/Makefile.am index f21107e65..92126ba36 100644 --- a/Makefile.am +++ b/Makefile.am @@ -113,6 +113,11 @@ libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_SOURCES += powerpc/powerpc_init.c\ powerpc/filter_vsx_intrinsics.c endif +if PNG_RISCV_VECTOR +libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_SOURCES += riscv/riscv_init.c\ + riscv/filter_vector_intrinsics.c +endif + nodist_libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_SOURCES = pnglibconf.h libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_LDFLAGS = -no-undefined -export-dynamic \ diff --git a/Makefile.in b/Makefile.in index 31a82d25a..bf2c327e9 100644 --- a/Makefile.in +++ b/Makefile.in @@ -118,13 +118,16 @@ bin_PROGRAMS = pngfix$(EXEEXT) png-fix-itxt$(EXEEXT) @PNG_POWERPC_VSX_TRUE@am__append_5 = powerpc/powerpc_init.c\ @PNG_POWERPC_VSX_TRUE@ powerpc/filter_vsx_intrinsics.c +@PNG_RISCV_VECTOR_TRUE@am__append_6 = riscv/riscv_init.c\ +@PNG_RISCV_VECTOR_TRUE@ riscv/filter_vector_intrinsics.c + # Versioned symbols and restricted exports -@HAVE_LD_VERSION_SCRIPT_TRUE@@HAVE_SOLARIS_LD_TRUE@am__append_6 = -Wl,-M -Wl,libpng.vers -@HAVE_LD_VERSION_SCRIPT_TRUE@@HAVE_SOLARIS_LD_FALSE@am__append_7 = -Wl,--version-script=libpng.vers +@HAVE_LD_VERSION_SCRIPT_TRUE@@HAVE_SOLARIS_LD_TRUE@am__append_7 = -Wl,-M -Wl,libpng.vers +@HAVE_LD_VERSION_SCRIPT_TRUE@@HAVE_SOLARIS_LD_FALSE@am__append_8 = -Wl,--version-script=libpng.vers # Only restricted exports when possible -@HAVE_LD_VERSION_SCRIPT_FALSE@am__append_8 = -export-symbols libpng.sym -@DO_PNG_PREFIX_TRUE@am__append_9 = -DPNG_PREFIX='@PNG_PREFIX@' +@HAVE_LD_VERSION_SCRIPT_FALSE@am__append_9 = -export-symbols libpng.sym +@DO_PNG_PREFIX_TRUE@am__append_10 = -DPNG_PREFIX='@PNG_PREFIX@' subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/scripts/libtool.m4 \ @@ -187,7 +190,8 @@ am__libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_SOURCES_DIST = png.c \ arm/palette_neon_intrinsics.c mips/mips_init.c \ mips/filter_msa_intrinsics.c intel/intel_init.c \ intel/filter_sse2_intrinsics.c powerpc/powerpc_init.c \ - powerpc/filter_vsx_intrinsics.c + powerpc/filter_vsx_intrinsics.c riscv/riscv_init.c \ + riscv/filter_vector_intrinsics.c am__dirstamp = $(am__leading_dot)dirstamp @PNG_ARM_NEON_TRUE@am__objects_1 = arm/arm_init.lo arm/filter_neon.lo \ @PNG_ARM_NEON_TRUE@ arm/filter_neon_intrinsics.lo \ @@ -198,11 +202,14 @@ am__dirstamp = $(am__leading_dot)dirstamp @PNG_INTEL_SSE_TRUE@ intel/filter_sse2_intrinsics.lo @PNG_POWERPC_VSX_TRUE@am__objects_4 = powerpc/powerpc_init.lo \ @PNG_POWERPC_VSX_TRUE@ powerpc/filter_vsx_intrinsics.lo +@PNG_RISCV_VECTOR_TRUE@am__objects_5 = riscv/riscv_init.lo \ +@PNG_RISCV_VECTOR_TRUE@ riscv/filter_vector_intrinsics.lo am_libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_OBJECTS = png.lo pngerror.lo \ pngget.lo pngmem.lo pngpread.lo pngread.lo pngrio.lo \ pngrtran.lo pngrutil.lo pngset.lo pngtrans.lo pngwio.lo \ pngwrite.lo pngwtran.lo pngwutil.lo $(am__objects_1) \ - $(am__objects_2) $(am__objects_3) $(am__objects_4) + $(am__objects_2) $(am__objects_3) $(am__objects_4) \ + $(am__objects_5) nodist_libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_OBJECTS = libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_OBJECTS = \ $(am_libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_OBJECTS) \ @@ -283,7 +290,9 @@ am__depfiles_remade = ./$(DEPDIR)/png.Plo ./$(DEPDIR)/pngerror.Plo \ mips/$(DEPDIR)/filter_msa_intrinsics.Plo \ mips/$(DEPDIR)/mips_init.Plo \ powerpc/$(DEPDIR)/filter_vsx_intrinsics.Plo \ - powerpc/$(DEPDIR)/powerpc_init.Plo + powerpc/$(DEPDIR)/powerpc_init.Plo \ + riscv/$(DEPDIR)/filter_vector_intrinsics.Plo \ + riscv/$(DEPDIR)/riscv_init.Plo am__mv = mv -f CPPASCOMPILE = $(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS) @@ -767,12 +776,12 @@ libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_SOURCES = png.c pngerror.c \ pngrutil.c pngset.c pngtrans.c pngwio.c pngwrite.c pngwtran.c \ pngwutil.c png.h pngconf.h pngdebug.h pnginfo.h pngpriv.h \ pngstruct.h pngusr.dfa $(am__append_2) $(am__append_3) \ - $(am__append_4) $(am__append_5) + $(am__append_4) $(am__append_5) $(am__append_6) nodist_libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_SOURCES = pnglibconf.h libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_LDFLAGS = -no-undefined \ -export-dynamic -version-number \ @PNGLIB_MAJOR@@PNGLIB_MINOR@:@PNGLIB_RELEASE@:0 \ - $(am__append_6) $(am__append_7) $(am__append_8) + $(am__append_7) $(am__append_8) $(am__append_9) @HAVE_LD_VERSION_SCRIPT_FALSE@libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_DEPENDENCIES = libpng.sym @HAVE_LD_VERSION_SCRIPT_TRUE@libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_DEPENDENCIES = libpng.vers pkginclude_HEADERS = png.h pngconf.h @@ -808,7 +817,7 @@ SUFFIXES = .chk .out SYMBOL_CFLAGS = -DPNGLIB_LIBNAME='PNG@PNGLIB_MAJOR@@PNGLIB_MINOR@_0' \ -DPNGLIB_VERSION='@PNGLIB_VERSION@' \ -DSYMBOL_PREFIX='$(SYMBOL_PREFIX)' -DPNG_NO_USE_READ_MACROS \ - -DPNG_BUILDING_SYMBOL_TABLE $(am__append_9) + -DPNG_BUILDING_SYMBOL_TABLE $(am__append_10) # EXT_LIST is a list of the possibly library directory extensions, this exists # because we can't find a good way of discovering the file extensions that are @@ -1006,6 +1015,16 @@ powerpc/powerpc_init.lo: powerpc/$(am__dirstamp) \ powerpc/$(DEPDIR)/$(am__dirstamp) powerpc/filter_vsx_intrinsics.lo: powerpc/$(am__dirstamp) \ powerpc/$(DEPDIR)/$(am__dirstamp) +riscv/$(am__dirstamp): + @$(MKDIR_P) riscv + @: > riscv/$(am__dirstamp) +riscv/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) riscv/$(DEPDIR) + @: > riscv/$(DEPDIR)/$(am__dirstamp) +riscv/riscv_init.lo: riscv/$(am__dirstamp) \ + riscv/$(DEPDIR)/$(am__dirstamp) +riscv/filter_vector_intrinsics.lo: riscv/$(am__dirstamp) \ + riscv/$(DEPDIR)/$(am__dirstamp) libpng@PNGLIB_MAJOR@@[email protected]: $(libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_OBJECTS) $(libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_DEPENDENCIES) $(EXTRA_libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_DEPENDENCIES) $(AM_V_CCLD)$(libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_LINK) -rpath $(libdir) $(libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_OBJECTS) $(libpng@PNGLIB_MAJOR@@PNGLIB_MINOR@_la_LIBADD) $(LIBS) @@ -1122,6 +1141,8 @@ mostlyclean-compile: -rm -f mips/*.lo -rm -f powerpc/*.$(OBJEXT) -rm -f powerpc/*.lo + -rm -f riscv/*.$(OBJEXT) + -rm -f riscv/*.lo distclean-compile: -rm -f *.tab.c @@ -1160,6 +1181,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@mips/$(DEPDIR)/mips_init.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@powerpc/$(DEPDIR)/filter_vsx_intrinsics.Plo@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@powerpc/$(DEPDIR)/powerpc_init.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@riscv/$(DEPDIR)/filter_vector_intrinsics.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@riscv/$(DEPDIR)/riscv_init.Plo@am__quote@ # am--include-marker $(am__depfiles_remade): @$(MKDIR_P) $(@D) @@ -1224,6 +1247,7 @@ clean-libtool: -rm -rf intel/.libs intel/_libs -rm -rf mips/.libs mips/_libs -rm -rf powerpc/.libs powerpc/_libs + -rm -rf riscv/.libs riscv/_libs distclean-libtool: -rm -f libtool config.lt @@ -2040,6 +2064,8 @@ distclean-generic: -rm -f mips/$(am__dirstamp) -rm -f powerpc/$(DEPDIR)/$(am__dirstamp) -rm -f powerpc/$(am__dirstamp) + -rm -f riscv/$(DEPDIR)/$(am__dirstamp) + -rm -f riscv/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @@ -2090,6 +2116,8 @@ distclean: distclean-am -rm -f mips/$(DEPDIR)/mips_init.Plo -rm -f powerpc/$(DEPDIR)/filter_vsx_intrinsics.Plo -rm -f powerpc/$(DEPDIR)/powerpc_init.Plo + -rm -f riscv/$(DEPDIR)/filter_vector_intrinsics.Plo + -rm -f riscv/$(DEPDIR)/riscv_init.Plo -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-libtool distclean-tags @@ -2175,6 +2203,8 @@ maintainer-clean: maintainer-clean-am -rm -f mips/$(DEPDIR)/mips_init.Plo -rm -f powerpc/$(DEPDIR)/filter_vsx_intrinsics.Plo -rm -f powerpc/$(DEPDIR)/powerpc_init.Plo + -rm -f riscv/$(DEPDIR)/filter_vector_intrinsics.Plo + -rm -f riscv/$(DEPDIR)/riscv_init.Plo -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic diff --git a/README b/README index f46a59972..d01059a9f 100644 --- a/README +++ b/README @@ -146,9 +146,11 @@ Files in this distribution: pngwutil.c => Write utility functions arm => Contains optimized code for the ARM platform powerpc => Contains optimized code for the PowerPC platform + riscv => Contains optimized code for the RISC-V platform contrib => Contributions arm-neon => Optimized code for ARM-NEON platform powerpc-vsx => Optimized code for POWERPC-VSX platform + riscv-vector => Optimized code for RISC-V Vector platform examples => Example programs gregbook => source code for PNG reading and writing, from Greg Roelofs' "PNG: The Definitive Guide", diff --git a/config.h.in b/config.h.in index 2931048bf..594bd449a 100644 --- a/config.h.in +++ b/config.h.in @@ -96,6 +96,19 @@ /* Enable POWERPC VSX optimizations */ #undef PNG_POWERPC_VSX_OPT +/* Turn on RISC-V Vector optimizations at run-time */ +#undef PNG_RISCV_VECTOR_API_SUPPORTED + +/* Check for RISC-V Vector support at run-time */ +#undef PNG_RISCV_VECTOR_CHECK_SUPPORTED + +/* Enable RISC-V Vector optimizations */ +#undef PNG_RISCV_VECTOR_OPT + +/* Enable RISC-V Vector compatibility for drafts 0.7.1, 0.8, 0.9, 0.10 + * and release 1.0 */ +#undef PNG_RISCV_VECTOR_COMPAT + /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS diff --git a/configure b/configure index 574bfaeb5..fd0aa8ee5 100755 --- a/configure +++ b/configure @@ -635,6 +635,8 @@ ac_includes_default="\ ac_subst_vars='am__EXEEXT_FALSE am__EXEEXT_TRUE LTLIBOBJS +PNG_RISCV_VECTOR_FALSE +PNG_RISCV_VECTOR_TRUE PNG_POWERPC_VSX_FALSE PNG_POWERPC_VSX_TRUE PNG_INTEL_SSE_FALSE @@ -818,6 +820,8 @@ enable_arm_neon enable_mips_msa enable_intel_sse enable_powerpc_vsx +enable_riscv_vector +enable_riscv_vector_compat ' ac_precious_vars='build_alias host_alias @@ -1520,6 +1524,24 @@ Optional Features: default, enable by a call to png_set_option yes/on: turn on unconditionally. If not specified: determined by the compiler. + --enable-riscv-vector Enable RISC-V Vector optimizations: =no/off, check, + api, yes/on: no/off: disable the optimizations; + check: use internal checking code (experimental); + api: disable by default, enable by a call to + png_set_option; yes/on: turn on unconditionally. + Note: The compiler must support riscv-vector (e.g. + -march=rv64gv). If not specified: determined by the + compiler. + --enable-riscv-vector-compat + Enable compatibility for RISC-V Vector drafts 0.7.1, + 0.8, 0.9, or 0.10 (only used if RISC-V Vector + optimizations are enabled): =no/off, 0.7.1, 0.8, + 0.9, 0.10: no/off: Support RISC-V Vector release + 1.0; 0.7.1: Support RISC-V Vector draft 0.7.1; 0.8: + Support RISC-V Vector draft 0.8; 0.9: Support RISC-V + Vector draft 0.9; 0.10: Support RISC-V Vector draft + 0.10. If not specified: off (Support RISC-V Vector + release 1.0). Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -13517,6 +13539,10 @@ $as_echo "#define PNG_POWERPC_VSX_OPT 0" >>confdefs.h $as_echo "#define PNG_INTEL_SSE_OPT 0" >>confdefs.h + enable_riscv_vector=no + +$as_echo "#define PNG_RISCV_VECTOR_OPT 0" >>confdefs.h + ;; *) # allow enabling hardware optimization on any system: @@ -13544,6 +13570,12 @@ $as_echo "#define PNG_INTEL_SSE_OPT 1" >>confdefs.h $as_echo "#define PNG_POWERPC_VSX_OPT 2" >>confdefs.h + ;; + riscv*) + enable_riscv_vector=yes + +$as_echo "#define PNG_RISCV_VECTOR_OPT 2" >>confdefs.h + ;; esac ;; @@ -13762,6 +13794,90 @@ else fi +# RISC-V +# === +# +# RISC-V Vector support. + +# Check whether --enable-riscv-vector was given. +if test "${enable_riscv_vector+set}" = set; then : + enableval=$enable_riscv_vector; case "$enableval" in + no|off) + # disable the default enabling on __RISCV_VECTOR__ systems: + +$as_echo "#define PNG_RISCV_VECTOR_OPT 0" >>confdefs.h + + # Prevent inclusion of the assembler files below: + enable_riscv_vector=no;; + check) + +$as_echo "#define PNG_RISCV_VECTOR_CHECK_SUPPORTED /**/" >>confdefs.h +;; + api) + +$as_echo "#define PNG_RISCV_VECTOR_API_SUPPORTED /**/" >>confdefs.h +;; + yes|on) + +$as_echo "#define PNG_RISCV_VECTOR_OPT 2" >>confdefs.h + + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --enable-riscv_vector: please specify 'check' or 'api', if + you want the optimizations unconditionally pass '-march=rv...v' (e.g. '-march=rv64gv') + to the compiler." >&5 +$as_echo "$as_me: WARNING: --enable-riscv_vector: please specify 'check' or 'api', if + you want the optimizations unconditionally pass '-march=rv...v' (e.g. '-march=rv64gv') + to the compiler." >&2;};; + *) + as_fn_error $? "--enable-riscv-vector=${enable_riscv_vector}: invalid value" "$LINENO" 5 + esac +fi + + +# compatibility to RVV draft 0.7.1, 0.8, 0.9, 0.10 and release 1.0 +# Check whether --enable-riscv-vector-compat was given. +if test "${enable_riscv_vector_compat+set}" = set; then : + enableval=$enable_riscv_vector_compat; case "$enableval" in + no|off);; + yes|on) + as_fn_error $? "--enable-riscv-vector-compat: a specific draft version must be given" "$LINENO" 5;; + "0.7.1") + +$as_echo "#define PNG_RISCV_VECTOR_COMPAT 7" >>confdefs.h +;; + "0.8") + +$as_echo "#define PNG_RISCV_VECTOR_COMPAT 8" >>confdefs.h +;; + "0.9") + +$as_echo "#define PNG_RISCV_VECTOR_COMPAT 9" >>confdefs.h +;; + "0.10") + +$as_echo "#define PNG_RISCV_VECTOR_COMPAT 10" >>confdefs.h +;; + *) + as_fn_error $? "--enable-riscv-vector-compat=${enable_riscv_vector_compat}: invalid value" "$LINENO" 5 + esac +fi + + +# Add RISC-V specific files to all builds where the host_cpu is riscv ('riscv*') or +# where RISC-V optimizations were explicitly requested (this allows a fallback if a +# future host CPU does not match 'riscv*') + + if test "$enable_riscv_vector" != 'no' && + case "$host_cpu" in + riscv*) :;; + *) test "$enable_riscv_vector" != '';; + esac; then + PNG_RISCV_VECTOR_TRUE= + PNG_RISCV_VECTOR_FALSE='#' +else + PNG_RISCV_VECTOR_TRUE='#' + PNG_RISCV_VECTOR_FALSE= +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: Extra options for compiler: $PNG_COPTS" >&5 $as_echo "$as_me: Extra options for compiler: $PNG_COPTS" >&6;} @@ -13957,6 +14073,10 @@ if test -z "${PNG_POWERPC_VSX_TRUE}" && test -z "${PNG_POWERPC_VSX_FALSE}"; then as_fn_error $? "conditional \"PNG_POWERPC_VSX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${PNG_RISCV_VECTOR_TRUE}" && test -z "${PNG_RISCV_VECTOR_FALSE}"; then + as_fn_error $? "conditional \"PNG_RISCV_VECTOR\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 diff --git a/configure.ac b/configure.ac index 17a6a2fbe..78a1428af 100644 --- a/configure.ac +++ b/configure.ac @@ -320,6 +320,9 @@ AC_ARG_ENABLE([hardware-optimizations], enable_intel_sse=no AC_DEFINE([PNG_INTEL_SSE_OPT], [0], [Disable INTEL_SSE optimizations]) + enable_riscv_vector=no + AC_DEFINE([PNG_RISCV_VECTOR_OPT], [0], + [Disable RISCV_VECTOR optimizations]) ;; *) # allow enabling hardware optimization on any system: @@ -344,6 +347,11 @@ AC_ARG_ENABLE([hardware-optimizations], AC_DEFINE([PNG_POWERPC_VSX_OPT], [2], [Enable POWERPC VSX optimizations]) ;; + riscv*) + enable_riscv_vector=yes + AC_DEFINE([PNG_RISCV_VECTOR_OPT], [2], + [Enable RISCV VECTOR optimizations]) + ;; esac ;; esac]) @@ -521,6 +529,82 @@ AM_CONDITIONAL([PNG_POWERPC_VSX], powerpc*|ppc64*) :;; esac]) +# RISC-V +# === +# +# RISC-V Vector support. + +AC_ARG_ENABLE([riscv-vector], + AS_HELP_STRING([[[--enable-riscv-vector]]], + [Enable RISC-V Vector optimizations: =no/off, check, api, yes/on:] + [no/off: disable the optimizations; check: use internal checking code] + [(experimental); api: disable by default, enable by] + [a call to png_set_option; yes/on: turn on unconditionally.] + [Note: The compiler must support riscv-vector (e.g. -march=rv64gv).] + [If not specified: determined by the compiler.]), + [case "$enableval" in + no|off) + # disable the default enabling on __RISCV_VECTOR__ systems: + AC_DEFINE([PNG_RISCV_VECTOR_OPT], [0], + [Disable RISC-V Vector optimizations]) + # Prevent inclusion of the assembler files below: + enable_riscv_vector=no;; + check) + AC_DEFINE([PNG_RISCV_VECTOR_CHECK_SUPPORTED], [], + [Check for RISC-V Vector support at run-time]);; + api) + AC_DEFINE([PNG_RISCV_VECTOR_API_SUPPORTED], [], + [Turn on RISC-V Vector optimizations at run-time]);; + yes|on) + AC_DEFINE([PNG_RISCV_VECTOR_OPT], [2], + [Enable RISC-V Vector optimizations]) + AC_MSG_WARN([--enable-riscv_vector: please specify 'check' or 'api', if] + [you want the optimizations unconditionally pass '-march=rv...v' (e.g. '-march=rv64gv')] + [to the compiler.]);; + *) + AC_MSG_ERROR([--enable-riscv-vector=${enable_riscv_vector}: invalid value]) + esac]) + +# compatibility to RVV draft 0.7.1, 0.8, 0.9, 0.10 and release 1.0 +AC_ARG_ENABLE([riscv-vector-compat], + AS_HELP_STRING([[[--enable-riscv-vector-compat]]], + [Enable compatibility for RISC-V Vector drafts 0.7.1, 0.8, 0.9, or 0.10 (only used if RISC-V Vector optimizations are enabled): =no/off, 0.7.1, 0.8, 0.9, 0.10:] + [no/off: Support RISC-V Vector release 1.0;] + [0.7.1: Support RISC-V Vector draft 0.7.1;] + [0.8: Support RISC-V Vector draft 0.8;] + [0.9: Support RISC-V Vector draft 0.9;] + [0.10: Support RISC-V Vector draft 0.10.] + [If not specified: off (Support RISC-V Vector release 1.0).]), + [case "$enableval" in + no|off);; + yes|on) + AC_MSG_ERROR([--enable-riscv-vector-compat: a specific draft version must be given]);; + "0.7.1") + AC_DEFINE([PNG_RISCV_VECTOR_COMPAT], [7], + [Support RISC-V Vector draft 0.7.1]);; + "0.8") + AC_DEFINE([PNG_RISCV_VECTOR_COMPAT], [8], + [Support RISC-V Vector draft 0.8]);; + "0.9") + AC_DEFINE([PNG_RISCV_VECTOR_COMPAT], [9], + [Support RISC-V Vector draft 0.9]);; + "0.10") + AC_DEFINE([PNG_RISCV_VECTOR_COMPAT], [10], + [Support RISC-V Vector draft 0.10]);; + *) + AC_MSG_ERROR([--enable-riscv-vector-compat=${enable_riscv_vector_compat}: invalid value]) + esac]) + +# Add RISC-V specific files to all builds where the host_cpu is riscv ('riscv*') or +# where RISC-V optimizations were explicitly requested (this allows a fallback if a +# future host CPU does not match 'riscv*') + +AM_CONDITIONAL([PNG_RISCV_VECTOR], + [test "$enable_riscv_vector" != 'no' && + case "$host_cpu" in + riscv*) :;; + *) test "$enable_riscv_vector" != '';; + esac]) AC_MSG_NOTICE([[Extra options for compiler: $PNG_COPTS]]) diff --git a/contrib/riscv-vector/README b/contrib/riscv-vector/README new file mode 100644 index 000000000..57cb0fd9e --- /dev/null +++ b/contrib/riscv-vector/README @@ -0,0 +1,82 @@ +OPERATING SYSTEM SPECIFIC RISC-V VECTOR DETECTION +------------------------------------------------- + +Detection of the ability to execute RISC-V Vector on an RISC-V processor requires +operating system support. (The information is not available in user mode.) + +HOW TO USE THIS +--------------- + +This directory contains C code fragments that can be included in riscv/riscv_init.c +by setting the macro PNG_RISCV_VECTOR_FILE to the file name in "" or <> at build +time. This setting is not recorded in pnglibconf.h and can be changed simply by +rebuilding riscv/vector_init.o with the required macro definition. + +For any of this code to be used the RISC-V Vector code must be enabled and run time +checks must be supported. I.e.: + +#if PNG_RISCV_VECTOR_OPT > 0 +#ifdef PNG_RISCV_VECTOR_CHECK_SUPPORTED + +This is done in a 'configure' build by passing configure the argument: + + --enable-riscv-vector=check + +Furthermore the compiler must have enabled support for the RISC-V Vector extension. +(e.g. -march=rv64gv) + +FILE FORMAT +----------- + +Each file documents its testing status as of the last time it was tested (which +may have been a long time ago): + +STATUS: one of: + SUPPORTED: This indicates that the file is included in the regularly + performed test builds and bugs are fixed when discovered. + COMPILED: This indicates that the code did compile at least once. See the + more detailed description for the extent to which the result was + successful. + TESTED: This means the code was fully compiled into the libpng test programs + and these were run at least once. + +BUG REPORTS: an email address to which to send reports of problems + +The file is a fragment of C code. It should not define any 'extern' symbols; +everything should be static. It must define the function: + +static int png_have_vector(png_structp png_ptr); + +That function must return 1 if RISC-V Vector instructions are supported, 0 if not. +It must not execute png_error unless it detects a bug. A png_error will prevent +the reading of the PNG and in the future, writing too. + +BUG REPORTS +----------- + +If you mail a bug report for any file that is not SUPPORTED there may only be +limited response. Consider fixing it and sending a patch to fix the problem - +this is more likely to result in action. + +CONTRIBUTIONS +------------- + +You may send contributions of new implementations to [email protected] Please write code in strict C90 C where +possible. Obviously OS dependencies are to be expected. If you submit code you +must have the authors permission and it must have a license that is acceptable +to the current maintainer; in particular that license must permit modification +and redistribution. + +Please try to make the contribution a single file and give the file a clear and +unambiguous name that identifies the target OS. If multiple files really are +required put them all in a sub-directory. + +You must also be prepared to handle bug reports from users of the code, either +by joining the png-mng-implement mailing list or by providing an email for the +"BUG REPORTS" entry or both. Please make sure that the header of the file +contains the STATUS and BUG REPORTS fields as above. + +Please list the OS requirements as precisely as possible. Ideally you should +also list the environment in which the code has been tested and certainly list +any environments where you suspect it might not work. diff --git a/contrib/riscv-vector/linux.c b/contrib/riscv-vector/linux.c new file mode 100644 index 000000000..056798f5a --- /dev/null +++ b/contrib/riscv-vector/linux.c @@ -0,0 +1,83 @@ + +/* contrib/riscv-vector/linux.c + * + * Copyright (c) 2021 Manfred Schlaegl + * Copyright (c) 2020 Cosmin Truta + * Copyright (c) 2016 Glenn Randers-Pehrson + * Written by Manfred Schlaegl, October 2021. + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + * + * SEE contrib/riscv-vector/README before reporting bugs + * + * STATUS: SUPPORTED + * BUG REPORTS: [email protected] + * + * png_have_vector implemented for Linux by reading the widely available + * pseudo-file /proc/cpuinfo. + * + * This code is strict ANSI-C and is probably moderately portable; it does + * however use <stdio.h>, <string.h> and it assumes that /proc/cpuinfo is + * never localized. + */ + +#include <stdio.h> +#include <string.h> + +#define MAX_LINE_SIZE 256 + +static int +png_have_vector(png_structp png_ptr) +{ + int ret = 0; + + FILE *f = fopen("/proc/cpuinfo", "rb"); + if (f == NULL) { +#ifdef PNG_WARNINGS_SUPPORTED + png_warning(png_ptr, "/proc/cpuinfo open failed"); +#endif + return 0; + } + + while(!feof(f)) + { + char line[MAX_LINE_SIZE]; + + /* read line */ + int i = 0; + while (i < (MAX_LINE_SIZE - 1)) + { + char ch = fgetc(f); + if (ch == '\n' || ch == EOF) + break; + line[i++] = ch; + } + line[i] = '\0'; + + /* does line start with "isa"? */ + if (strncmp("isa", line, 3) != 0) + continue; + + /* find value starting with "rv" */ + char *val = strstr(line, "rv"); + if (val == NULL) + continue; + + /* skip "rv" */ + val += 2; + + /* check for vector 'v' */ + val = strchr(line, 'v'); + if (val != NULL) + { + /* found */ + ret = 1; + break; + } + } + + fclose(f); + return ret; +} diff --git a/png.h b/png.h index 4731d0777..f13e6ce21 100644 --- a/png.h +++ b/png.h @@ -3211,7 +3211,10 @@ PNG_EXPORT(245, int, png_image_write_to_memory, (png_imagep image, void *memory, #ifdef PNG_POWERPC_VSX_API_SUPPORTED # define PNG_POWERPC_VSX 10 /* HARDWARE: PowerPC VSX SIMD instructions supported */ #endif -#define PNG_OPTION_NEXT 12 /* Next option - numbers must be even */ +#ifdef PNG_RISCV_VECTOR_API_SUPPORTED +# define PNG_RISCV_VECTOR 12 /* HARDWARE: RISC-V Vector extension supported */ +#endif +#define PNG_OPTION_NEXT 14 /* Next option - numbers must be even */ /* Return values: NOTE: there are four values and 'off' is *not* zero */ #define PNG_OPTION_UNSET 0 /* Unset - defaults to off */ diff --git a/pngpriv.h b/pngpriv.h index 583c26f9b..f440d00ac 100644 --- a/pngpriv.h +++ b/pngpriv.h @@ -270,6 +270,18 @@ # define PNG_POWERPC_VSX_IMPLEMENTATION 1 #endif +#ifndef PNG_RISCV_VECTOR_OPT +# if defined(__riscv_vector) +# define PNG_RISCV_VECTOR_OPT 2 +# else +# define PNG_RISCV_VECTOR_OPT 0 +# endif +#endif + +#if PNG_RISCV_VECTOR_OPT > 0 +# define PNG_FILTER_OPTIMIZATIONS png_init_filter_functions_vector +# define PNG_RISCV_VECTOR_IMPLEMENTATION 1 // intrinsics implementation only +#endif /* Is this a build of a DLL where compilation of the object modules requires * different preprocessor settings to those required for a simple library? If @@ -1364,6 +1376,23 @@ PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_sse2,(png_row_infop row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); #endif +#if PNG_RISCV_VECTOR_IMPLEMENTATION > 0 +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_up_vector,(png_row_infop row_info, + png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub3_vector,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_sub4_vector,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg3_vector,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_avg4_vector,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth3_vector,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +PNG_INTERNAL_FUNCTION(void,png_read_filter_row_paeth4_vector,(png_row_infop + row_info, png_bytep row, png_const_bytep prev_row),PNG_EMPTY); +#endif + /* Choose the best filter to use and filter the row data */ PNG_INTERNAL_FUNCTION(void,png_write_find_filter,(png_structrp png_ptr, png_row_infop row_info),PNG_EMPTY); diff --git a/riscv/filter_vector_intrinsics.c b/riscv/filter_vector_intrinsics.c new file mode 100644 index 000000000..f23521637 --- /dev/null +++ b/riscv/filter_vector_intrinsics.c @@ -0,0 +1,354 @@ + +/* filter_vector_intrinsics.c - Vector extension optimised filter functions + * + * Copyright (c) 2021 Manfred Schlaegl + * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2016 Glenn Randers-Pehrson + * Written by Manfred Schlaegl, October 2021. + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +#include <stdio.h> +#include <stdint.h> +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +/* This code requires -march containing 'v' on the command line: */ +#if PNG_RISCV_VECTOR_IMPLEMENTATION == 1 /* intrinsics code from pngpriv.h */ + +#if PNG_RISCV_VECTOR_OPT > 0 + + +/* Mapping of asm mnemonics for different RISC-V Vector versions */ +#if PNG_RISCV_VECTOR_COMPAT == 7 +/* RISC-V Vector draft 0.7.1 */ +#define VLE8_V "vlbu.v" +#define VSE8_V "vsb.v" +#define VNSRL_WI "vnsrl.vi" + +#elif PNG_RISCV_VECTOR_COMPAT == 8 +/* RISC-V Vector draft 0.8 */ +#define VLE8_V "vlbu.v" +#define VSE8_V "vsb.v" +#define VNSRL_WI "vnsrl.wi" + +#elif ( \ + ( PNG_RISCV_VECTOR_COMPAT == 9 ) || \ + ( PNG_RISCV_VECTOR_COMPAT == 10 ) || \ + ( !defined(PNG_RISCV_VECTOR_COMPAT) ) \ +) +/* RISC-V Vector drafts 0.9, 0.10 and release 1.0 */ +#define VLE8_V "vle8.v" +#define VSE8_V "vse8.v" +#define VNSRL_WI "vnsrl.wi" + +#else +#error "Invalid value for PNG_RISCV_VECTOR_COMPAT!" +#endif /* PNG_RISCV_VECTOR_COMPAT */ + + +/* + * The implementation implicitly assumes VLEN >= 32bit(bpp=4) which is valid + * according to + * * riscv-v-spec-0.7.1/0.8.1/0.9 -- Chapter 2 ("VLEN >= SLEN >= 32") + * * riscv-v-spec-1.0 -- Chapter 18 + */ + +void png_read_filter_row_up_vector(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + size_t rowbytes = row_info->rowbytes; + unsigned int vl = 0; + + /* + * row: | x | + * prev_row: | b | + * + * b = b + x + * + * b .. [v0-v7](e8) + * x .. [v8-v15](e8) + */ + + while (rowbytes) { + asm volatile ("vsetvli %0, %1, e8, m8" : "=r" (vl) : "r" (rowbytes)); + + /* b = *row */ + asm volatile (VLE8_V" v0, (%0)" : : "r" (row)); + + /* x = *prev_row */ + asm volatile (VLE8_V" v8, (%0)" : : "r" (prev_row)); + prev_row += vl; + + /* b = b + x */ + asm volatile ("vadd.vv v0, v0, v8"); + + /* *row = b */ + asm volatile (VSE8_V" v0, (%0)" : : "r" (row)); + row += vl; + + rowbytes -= vl; + } +} + + +static inline void png_read_filter_row_sub_vector(png_row_infop row_info, png_bytep row, + unsigned int bpp) +{ + png_bytep rp_end = row + row_info->rowbytes; + + /* + * row: | a | x | + * + * a = a + x + * + * a .. [v0](e8) + * x .. [v8](e8) + */ + + asm volatile ("vsetvli zero, %0, e8, m1" : : "r" (bpp)); + + /* a = *row */ + asm volatile (VLE8_V" v0, (%0)" : : "r" (row)); + row += bpp; + + while (row < rp_end) { + + /* x = *row */ + asm volatile (VLE8_V" v8, (%0)" : : "r" (row)); + /* a = a + x */ + asm volatile ("vadd.vv v0, v0, v8"); + + /* *row = a */ + asm volatile (VSE8_V" v0, (%0)" : : "r" (row)); + row += bpp; + } +} + +void png_read_filter_row_sub3_vector(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_read_filter_row_sub_vector(row_info, row, 3); +} + +void png_read_filter_row_sub4_vector(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_read_filter_row_sub_vector(row_info, row, 4); +} + + +static inline void png_read_filter_row_avg_vector(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row, unsigned int bpp) +{ + png_bytep rp_end = row + row_info->rowbytes; + + /* + * row: | a | x | + * prev_row: | | b | + * + * a .. [v2](e8) + * b .. [v4](e8) + * x .. [v8](e8) + * tmp .. [v12-v13](e16) + */ + + /* first pixel */ + + asm volatile ("vsetvli zero, %0, e8, m1" : : "r" (bpp)); + + /* b = *prev_row */ + asm volatile (VLE8_V" v4, (%0)" : : "r" (prev_row)); + prev_row += bpp; + + /* x = *row */ + asm volatile (VLE8_V" v8, (%0)" : : "r" (row)); + + /* b = b / 2 */ + asm volatile ("vsrl.vi v4, v4, 1"); + /* a = x + b */ + asm volatile ("vadd.vv v2, v4, v8"); + + /* *row = a */ + asm volatile (VSE8_V" v2, (%0)" : : "r" (row)); + row += bpp; + + + /* remaining pixels */ + + while (row < rp_end) { + + /* b = *prev_row */ + asm volatile (VLE8_V" v4, (%0)" : : "r" (prev_row)); + prev_row += bpp; + + /* x = *row */ + asm volatile (VLE8_V" v8, (%0)" : : "r" (row)); + + /* tmp = a + b */ + asm volatile ("vwaddu.vv v12, v2, v4"); /* add with widening */ + /* a = tmp/2 */ + asm volatile (VNSRL_WI" v2, v12, 1"); /* divide/shift with narrowing */ + /* a += x */ + asm volatile ("vadd.vv v2, v2, v8"); + + /* *row = a */ + asm volatile (VSE8_V" v2, (%0)" : : "r" (row)); + row += bpp; + } +} + +void png_read_filter_row_avg3_vector(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_read_filter_row_avg_vector(row_info, row, prev_row, 3); +} + +void png_read_filter_row_avg4_vector(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_read_filter_row_avg_vector(row_info, row, prev_row, 4); +} + + +void png_read_filter_row_paeth_vector(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row, unsigned int bpp) +{ + png_bytep rp_end = row + row_info->rowbytes; + + /* + * row: | a | x | + * prev_row: | c | b | + * + * mask .. [v0] + * a .. [v2](e8) + * b .. [v4](e8) + * c .. [v6](e8) + * x .. [v8](e8) + * p .. [v12-v13](e16) + * pa .. [v16-v17](e16) + * pb .. [v20-v21](e16) + * pc .. [v24-v25](e16) + * tmpmask ..[v31] + */ + + + /* first pixel */ + + asm volatile ("vsetvli zero, %0, e8, m1" : : "r" (bpp)); + + /* a = *row + *prev_row */ + asm volatile (VLE8_V" v2, (%0)" : : "r" (row)); + asm volatile (VLE8_V" v6, (%0)" : : "r" (prev_row)); + prev_row += bpp; + asm volatile ("vadd.vv v2, v2, v6"); + + /* *row = a */ + asm volatile (VSE8_V" v2, (%0)" : : "r" (row)); + row += bpp; + + + /* remaining pixels */ + + while (row < rp_end) { + + /* b = *prev_row */ + asm volatile (VLE8_V" v4, (%0)" : : "r" (prev_row)); + prev_row += bpp; + + /* x = *row */ + asm volatile (VLE8_V" v8, (%0)" : : "r" (row)); + + /* sub (widening to 16bit) */ + /* p = b - c */ + asm volatile ("vwsubu.vv v12, v4, v6"); + /* pc = a - c */ + asm volatile ("vwsubu.vv v24, v2, v6"); + + /* switch to widened */ + asm volatile ("vsetvli zero, %0, e16, m2" : : "r" (bpp)); + + /* pa = abs(p) -> pa = p < 0 ? -p : p */ + asm volatile ("vmv.v.v v16, v12"); /* pa = p */ + asm volatile ("vmslt.vx v0, v16, zero"); /* set mask[i] if pa[i] < 0 */ + asm volatile ("vrsub.vx v16, v16, zero, v0.t"); /* invert negative values in pa; vd[i] = 0 - vs2[i] (if mask[i]) + * could be replaced by vneg in rvv >= 1.0 + */ + + /* pb = abs(p) -> pb = pc < 0 ? -pc : pc */ + asm volatile ("vmv.v.v v20, v24"); /* pb = pc */ + asm volatile ("vmslt.vx v0, v20, zero"); /* set mask[i] if pc[i] < 0 */ + asm volatile ("vrsub.vx v20, v20, zero, v0.t"); /* invert negative values in pb; vd[i] = 0 - vs2[i] (if mask[i]) + * could be replaced by vneg in rvv >= 1.0 + */ + + /* pc = abs(p + pc) -> pc = (p + pc) < 0 ? -(p + pc) : p + pc */ + asm volatile ("vadd.vv v24, v24, v12"); /* pc = p + pc */ + asm volatile ("vmslt.vx v0, v24, zero"); /* set mask[i] if pc[i] < 0 */ + asm volatile ("vrsub.vx v24, v24, zero, v0.t"); /* invert negative values in pc; vd[i] = 0 - vs2[i] (if mask[i]) + * could be replaced by vneg in rvv >= 1.0 + */ + + /* + * if (pb < pa) { + * pa = pb; + * a = b; (see (*1)) + * } + */ + asm volatile ("vmslt.vv v0, v20, v16"); /* set mask[i] if pb[i] < pa[i] */ + asm volatile ("vmerge.vvm v16, v16, v20, v0"); /* pa[i] = pb[i] (if mask[i]) */ + + + /* + * if (pc < pa) + * a = c; (see (*2)) + */ + asm volatile ("vmslt.vv v31, v24, v16"); /* set tmpmask[i] if pc[i] < pa[i] */ + + /* switch to narrow */ + asm volatile ("vsetvli zero, %0, e8, m1" : : "r" (bpp)); + + /* (*1) */ + asm volatile ("vmerge.vvm v2, v2, v4, v0"); /* a = b (if mask[i]) */ + + /* (*2) */ + asm volatile ("vmand.mm v0, v31, v31"); /* mask = tmpmask + * vmand works for rvv 0.7 up to 1.0 + * could be replaced by vmcpy in 0.7.1/0.8.1 + * or vmmv.m in 1.0 + */ + asm volatile ("vmerge.vvm v2, v2, v6, v0"); /* a = c (if mask[i]) */ + + /* a += x */ + asm volatile ("vadd.vv v2, v2, v8"); + + /* *row = a */ + asm volatile (VSE8_V" v2, (%0)" : : "r" (row)); + row += bpp; + + + /* prepare next iteration (prev_row is already in a) */ + /* c = b */ + asm volatile ("vmv.v.v v6, v4"); + } +} + +void png_read_filter_row_paeth3_vector(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_read_filter_row_paeth_vector(row_info, row, prev_row, 3); +} + +void png_read_filter_row_paeth4_vector(png_row_infop row_info, png_bytep row, + png_const_bytep prev_row) +{ + png_read_filter_row_paeth_vector(row_info, row, prev_row, 4); +} + +#endif /* PNG_RISCV_VECTOR_OPT > 0 */ +#endif /* PNG_RISCV_VECTOR_IMPLEMENTATION == 1 (intrinsics) */ +#endif /* READ */ diff --git a/riscv/riscv_init.c b/riscv/riscv_init.c new file mode 100644 index 000000000..182968600 --- /dev/null +++ b/riscv/riscv_init.c @@ -0,0 +1,128 @@ + +/* riscv_init.c - Vector optimised filter functions + * + * Copyright (c) 2021 Manfred Schlaegl + * Copyright (c) 2018 Cosmin Truta + * Copyright (c) 2014,2016 Glenn Randers-Pehrson + * Written by Manfred Schlaegl, October 2021. + * + * This code is released under the libpng license. + * For conditions of distribution and use, see the disclaimer + * and license in png.h + */ + +/* Below, after checking __linux__, various non-C90 POSIX 1003.1 functions are + * called. + */ +#define _POSIX_SOURCE 1 + +#include "../pngpriv.h" + +#ifdef PNG_READ_SUPPORTED + +#if PNG_RISCV_VECTOR_OPT > 0 +#ifdef PNG_RISCV_VECTOR_CHECK_SUPPORTED /* Do run-time checks */ +/* WARNING: it is strongly recommended that you do not build libpng with + * run-time checks for CPU features if at all possible. In the case of the RISC-V + * Vector instructions there is no processor-specific way of detecting the + * presence of the required support, therefore run-time detection is extremely + * OS specific. + * + * You may set the macro PNG_RISCV_VECTOR_FILE to the file name of file containing + * a fragment of C source code which defines the png_have_vector function. There + * may be number of implementations in contrib/riscv-vector, but the only one that + * has partial support is contrib/riscv-vector/linux.c - a generic Linux + * implementation which reads /proc/cpufino. + */ +#ifndef PNG_RISCV_VECTOR_FILE +# ifdef __linux__ +# define PNG_RISCV_VECTOR_FILE "contrib/riscv-vector/linux.c" +# endif +#endif + +#ifdef PNG_RISCV_VECTOR_FILE + +#include <signal.h> /* for sig_atomic_t */ +static int png_have_vector(png_structp png_ptr); +#include PNG_RISCV_VECTOR_FILE + +#else /* PNG_RISCV_VECTOR_FILE */ +# error "PNG_RISCV_VECTOR_FILE undefined: no support for run-time RISC-V Vector checks" +#endif /* PNG_RISCV_VECTOR_FILE */ +#endif /* PNG_RISCV_VECTOR_CHECK_SUPPORTED */ + +void +png_init_filter_functions_vector(png_structp pp, unsigned int bpp) +{ + /* The switch statement is compiled in for RISCV_VECTOR_API, the call to + * png_have_vector is compiled in for RISCV_VECTOR_CHECK. If both are defined + * the check is only performed if the API has not set the Vector option on + * or off explicitly. In this case the check controls what happens. + */ + png_debug(1, "in png_init_filter_functions_vector"); +#ifdef PNG_RISCV_VECTOR_API_SUPPORTED + switch ((pp->options >> PNG_RISCV_VECTOR) & 3) + { + case PNG_OPTION_UNSET: + /* Allow the run-time check to execute if it has been enabled - + * thus both API and CHECK can be turned on. If it isn't supported + * this case will fall through to the 'default' below, which just + * returns. + */ +#endif /* PNG_RISCV_VECTOR_API_SUPPORTED */ +#ifdef PNG_RISCV_VECTOR_CHECK_SUPPORTED + { + static volatile sig_atomic_t no_vector = -1; /* not checked */ + + if (no_vector < 0) + no_vector = !png_have_vector(pp); + + if (no_vector) + return; + } +#ifdef PNG_RISCV_VECTOR_API_SUPPORTED + break; +#endif +#endif /* PNG_RISCV_VECTOR_CHECK_SUPPORTED */ + +#ifdef PNG_RISCV_VECTOR_API_SUPPORTED + default: /* OFF or INVALID */ + return; + + case PNG_OPTION_ON: + /* Option turned on */ + break; + } +#endif + + /* IMPORTANT: any new external functions used here must be declared using + * PNG_INTERNAL_FUNCTION in ../pngpriv.h. This is required so that the + * 'prefix' option to configure works: + * + * ./configure --with-libpng-prefix=foobar_ + * + * Verify you have got this right by running the above command, doing a build + * and examining pngprefix.h; it must contain a #define for every external + * function you add. (Notice that this happens automatically for the + * initialization function.) + */ + pp->read_filter[PNG_FILTER_VALUE_UP-1] = png_read_filter_row_up_vector; + + if (bpp == 3) + { + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub3_vector; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg3_vector; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = + png_read_filter_row_paeth3_vector; + } + + else if (bpp == 4) + { + pp->read_filter[PNG_FILTER_VALUE_SUB-1] = png_read_filter_row_sub4_vector; + pp->read_filter[PNG_FILTER_VALUE_AVG-1] = png_read_filter_row_avg4_vector; + pp->read_filter[PNG_FILTER_VALUE_PAETH-1] = + png_read_filter_row_paeth4_vector; + } +} +#endif /* PNG_RISCV_VECTOR_OPT > 0 */ +#endif /* READ */ diff --git a/scripts/makefile.linux-opt b/scripts/makefile.linux-opt index d2619f9e9..0f2d11670 100644 --- a/scripts/makefile.linux-opt +++ b/scripts/makefile.linux-opt @@ -49,6 +49,7 @@ CPPFLAGS += -DPNG_ARM_NEON CPPFLAGS += -DPNG_MIPS_MSA CPPFLAGS += -DPNG_INTEL_SSE CPPFLAGS += -DPNG_POWERPC_VSX +CPPFLAGS += -DPNG_RISCV_VECTOR CFLAGS=-W -Wall -O3 -funroll-loops # $(WARNMORE) -g CFLAGS += -ansi -pedantic @@ -87,7 +88,8 @@ OBJS = png.o pngerror.o pngget.o pngmem.o pngpread.o \ arm/arm_init.o arm/filter_neon_intrinsics.o \ intel/intel_init.o intel/filter_sse2_intrinsics.o \ mips/mips_init.o mips/filter_msa_intrinsics.o \ - powerpc/powerpc_init.o powerpc/filter_vsx_intrinsics.o + powerpc/powerpc_init.o powerpc/filter_vsx_intrinsics.o \ + riscv/riscv_init.o OBJSDLL = $(OBJS:.o=.pic.o) @@ -254,5 +256,6 @@ mips/mips_init.o mips/mips_init.pic.o: pngpriv.h mips/filter_msa_intrinsics.o mips/filter_msa_intrinsics.pic.o: pngpriv.h powerpc/powerpc_init.o powerpc/powerpc_init.pic.o: pngpriv.h powerpc/filter_vsx_intrinsics.o powerpc/filter_vsx_intrinsics.pic.o: pngpriv.h +riscv/riscv_init.o riscv/riscv_init.o: pngpriv.h pngtest.o: png.h pngconf.h pnglibconf.h -- 2.30.2