diff --git a/ci_config.json b/ci_config.json index 09e40e707..fd77caf37 100644 --- a/ci_config.json +++ b/ci_config.json @@ -1733,6 +1733,14 @@ "tinyfsm:build_examples=true" ] }, + "volk": { + "build_options": [ + "volk:tests=enabled" + ], + "python_packages": [ + "mako" + ] + }, "wayland": { "_comment": [ "- relies on Linux-specific APIs", diff --git a/releases.json b/releases.json index 74caf9a1b..a3bcbf92e 100644 --- a/releases.json +++ b/releases.json @@ -5000,6 +5000,14 @@ "0.1.3-1" ] }, + "volk": { + "dependency_names": [ + "volk" + ], + "versions": [ + "3.3.0-1" + ] + }, "vorbis": { "dependency_names": [ "vorbis", diff --git a/subprojects/packagefiles/volk/cpu_features/meson.build b/subprojects/packagefiles/volk/cpu_features/meson.build new file mode 100644 index 000000000..8489d2dba --- /dev/null +++ b/subprojects/packagefiles/volk/cpu_features/meson.build @@ -0,0 +1,47 @@ +cpu_features_args = ['-DSTACK_LINE_READER_BUFFER_SIZE=1024'] +cpu_features_deps = [] +if host_machine.system() == 'darwin' + cpu_features_args += '-DHAVE_SYSCTLBYNAME' +endif + +cpu_features_sources = files( + 'src/filesystem.c', + 'src/impl_aarch64_linux_or_android.c', + 'src/impl_aarch64_macos_or_iphone.c', + 'src/impl_aarch64_windows.c', + 'src/impl_arm_linux_or_android.c', + 'src/impl_loongarch_linux.c', + 'src/impl_mips_linux_or_android.c', + 'src/impl_ppc_linux.c', + 'src/impl_riscv_linux.c', + 'src/impl_s390x_linux.c', + 'src/impl_x86_freebsd.c', + 'src/impl_x86_linux_or_android.c', + 'src/impl_x86_macos.c', + 'src/impl_x86_windows.c', + 'src/stack_line_reader.c', + 'src/string_view.c', +) + +if not cpu_is_x86 and host_machine.system() != 'windows' + cpu_features_sources += files('src/hwcaps.c') + if cc.has_header_symbol('sys/auxv.h', 'getauxval') + cpu_features_args += '-DHAVE_STRONG_GETAUXVAL' + elif cc.has_header('dlfcn.h') + cpu_features_args += '-DHAVE_DLFCN_H' + cpu_features_deps += dependency( + 'dl', + required: false, + ) + endif +endif + +cpu_features_lib = static_library( + 'cpu_features', + cpu_features_sources, + c_args: cpu_features_args, + include_directories: include_directories('include', 'include/internal'), + dependencies: cpu_features_deps, +) + +volk_incs += include_directories('include') diff --git a/subprojects/packagefiles/volk/include/volk/meson.build b/subprojects/packagefiles/volk/include/volk/meson.build new file mode 100644 index 000000000..39cb5c551 --- /dev/null +++ b/subprojects/packagefiles/volk/include/volk/meson.build @@ -0,0 +1,46 @@ +version_conf = configuration_data() +version_conf.set('VERSION_INFO_MAJOR_VERSION', version_parts[0]) +version_conf.set('VERSION_INFO_MINOR_VERSION', version_parts[1]) +version_conf.set('VERSION_INFO_MAINT_VERSION', version_parts[2]) +version_conf.set( + 'VOLK_VERSION_DECIMAL', + version_parts[0].to_int() * 10000 + version_parts[1].to_int() * 100 + version_parts[2].to_int(), +) + +volk_version_h = configure_file( + input: 'volk_version.h.in', + output: 'volk_version.h', + configuration: version_conf, + install_dir: get_option('includedir') / 'volk', +) + +volk_gen_headers = [] +foreach name : ['volk', 'volk_cpu', 'volk_config_fixed', 'volk_typedefs'] + volk_gen_headers += custom_target( + input: meson.project_source_root() / 'tmpl' / (name + '.tmpl.h'), + output: name + '.h', + command: tmpl_command, + depend_files: tmpl_depends, + install: true, + install_dir: get_option('includedir') / 'volk', + ) +endforeach + +install_headers( + 'constants.h', + 'saturation_arithmetic.h', + 'volk_alloc.hh', + 'volk_avx2_fma_intrinsics.h', + 'volk_avx2_intrinsics.h', + 'volk_avx512_intrinsics.h', + 'volk_avx_intrinsics.h', + 'volk_common.h', + 'volk_complex.h', + 'volk_malloc.h', + 'volk_neon_intrinsics.h', + 'volk_prefs.h', + 'volk_sse3_intrinsics.h', + 'volk_sse_intrinsics.h', + kernel_headers, + subdir: 'volk', +) diff --git a/subprojects/packagefiles/volk/lib/meson.build b/subprojects/packagefiles/volk/lib/meson.build new file mode 100644 index 000000000..58b0c6d2f --- /dev/null +++ b/subprojects/packagefiles/volk/lib/meson.build @@ -0,0 +1,197 @@ +lib_incs = [volk_incs, include_directories('.')] +lib_args = volk_args + ['-Dvolk_EXPORTS'] +tmpl_dir = meson.project_source_root() / 'tmpl' + +volk_machines_h = custom_target( + input: tmpl_dir / 'volk_machines.tmpl.h', + output: 'volk_machines.h', + command: tmpl_command, + depend_files: tmpl_depends, +) + +volk_sources = [volk_machines_h, volk_gen_headers] +foreach name : ['volk', 'volk_cpu', 'volk_machines'] + volk_sources += custom_target( + input: tmpl_dir / (name + '.tmpl.c'), + output: name + '.' + src_ext, + command: tmpl_command, + depend_files: tmpl_depends, + ) +endforeach + +foreach name : ['volk_prefs', 'volk_rank_archs', 'volk_malloc'] + if is_msvc + volk_sources += configure_file( + input: name + '.c', + output: name + '.cc', + copy: true, + ) + else + volk_sources += files(name + '.c') + endif +endforeach + +# Each machine is compiled with the flags of its archs +volk_link_whole = [] +machine_defs = [] +compiler_info = [] +foreach machine : available_machines + flags = machine_flags[machine] + machine_src = custom_target( + input: tmpl_dir / 'volk_machine_xxx.tmpl.c', + output: 'volk_machine_@0@.@1@'.format(machine, src_ext), + command: tmpl_command + [machine], + depend_files: tmpl_depends, + ) + volk_link_whole += static_library( + 'volk_machine_' + machine, + machine_src, + volk_machines_h, + volk_gen_headers, + c_args: lib_args + flags, + cpp_args: lib_args + flags, + include_directories: lib_incs, + gnu_symbol_visibility: 'hidden', + ) + machine_defs += '-DLV_MACHINE_' + machine.to_upper() + compiler_info += ':::'.join(machine, compiler_name, ' '.join(flags)) +endforeach + +if 'neonv7' in available_archs + neon_asm = [] + foreach name : run_command( + glob_command, + meson.project_source_root() / 'kernels' / 'volk' / 'asm' / 'neon', + '*.s', + check: true, + ).stdout().strip().split(';') + neon_asm += files('..' / 'kernels' / 'volk' / 'asm' / 'neon' / name) + endforeach + volk_link_whole += static_library( + 'volk_neon_asm', + neon_asm, + c_args: arch_flags['neonv7'], + ) +endif + +if cpu_family == 'riscv64' + foreach name : run_command( + glob_command, + meson.project_source_root() / 'kernels' / 'volk' / 'asm' / 'riscv', + '*.s', + check: true, + ).stdout().strip().split(';') + volk_sources += files('..' / 'kernels' / 'volk' / 'asm' / 'riscv' / name) + endforeach +endif + +if have_orc + foreach name : run_command( + glob_command, + meson.project_source_root() / 'kernels' / 'volk' / 'asm' / 'orc', + '*.orc', + check: true, + ).stdout().strip().split(';') + volk_sources += custom_target( + input: '..' / 'kernels' / 'volk' / 'asm' / 'orc' / name, + output: '@BASENAME@.' + src_ext, + command: [ + orcc, + '--include', + 'math.h', + '--implementation', + '--include', + 'volk/volk_complex.h', + '-o', + '@OUTPUT@', + '@INPUT@', + ], + ) + endforeach +endif + +constants_conf = configuration_data() +constants_conf.set('prefix', get_option('prefix').replace('\\', '\\\\')) +constants_conf.set('VERSION', meson.project_version()) +constants_conf.set( + 'cmake_c_compiler_version', + '@0@ @1@'.format(cc.get_id(), cc.version()), +) +constants_conf.set('COMPILER_INFO', '\\n'.join(compiler_info)) +constants_conf.set('available_machines', ';'.join(available_machines)) +volk_sources += configure_file( + input: 'constants.c.in', + output: 'constants.' + src_ext, + configuration: constants_conf, +) + +if use_cpu_features + volk_link_whole += cpu_features_lib +endif + +volk_lib = library( + 'volk', + volk_sources, + c_args: lib_args + machine_defs, + cpp_args: lib_args + machine_defs, + include_directories: lib_incs, + link_whole: volk_link_whole, + dependencies: [ + cc.find_library( + 'm', + required: false, + ), + cpu_features_deps, + orc_dep, + ], + gnu_symbol_visibility: 'hidden', + version: meson.project_version(), + soversion: soversion, + install: true, +) + +# Otherwise VOLK_API is __declspec(dllimport), which a static library can't satisfy +volk_dep_args = [] +if is_msvc and get_option('default_library') == 'static' + volk_dep_args += '-Dvolk_EXPORTS' +endif + +volk_dep = declare_dependency( + compile_args: volk_dep_args, + link_with: volk_lib, + include_directories: volk_public_incs, + sources: volk_gen_headers, +) +meson.override_dependency('volk', volk_dep) + +fmt_dep = dependency( + 'fmt', + required: get_option('tests'), +) +if fmt_dep.found() + test_incs = lib_incs + if is_msvc + # Shadows cmake/msvc/sys/time.h, which clang-cl rejects + test_incs = [include_directories('msvc'), test_incs] + endif + volk_test_all = executable( + 'volk_test_all', + 'testqa.cc', + 'qa_utils.cc', + cpp_args: volk_args, + include_directories: test_incs, + dependencies: [volk_dep, fmt_dep], + ) + foreach kernel : kernel_names + # Its tolerance is at the accuracy limit of the non-FMA SIMD kernels, which + # makes it flaky; fixed upstream after 3.3.0 (gnuradio/volk@58ef9ce) + if kernel == 'volk_32f_log2_32f' + continue + endif + test( + kernel, + volk_test_all, + args: kernel, + ) + endforeach +endif diff --git a/subprojects/packagefiles/volk/lib/msvc/sys/time.h b/subprojects/packagefiles/volk/lib/msvc/sys/time.h new file mode 100644 index 000000000..6daa6e139 --- /dev/null +++ b/subprojects/packagefiles/volk/lib/msvc/sys/time.h @@ -0,0 +1,10 @@ +/* + * Upstream's cmake/msvc/sys/time.h does #include < time.h >, which clang-cl + * rejects. qa_utils.cc only includes for CLOCKS_PER_SEC. + */ +#ifndef VOLK_WRAP_SYS_TIME_H +#define VOLK_WRAP_SYS_TIME_H + +#include + +#endif diff --git a/subprojects/packagefiles/volk/meson.build b/subprojects/packagefiles/volk/meson.build new file mode 100644 index 000000000..0ca32b541 --- /dev/null +++ b/subprojects/packagefiles/volk/meson.build @@ -0,0 +1,352 @@ +project( + 'volk', + 'c', + 'cpp', + version: '3.3.0', + license: 'LGPL-3.0-or-later AND Apache-2.0', + meson_version: '>=0.63.0', + default_options: ['cpp_std=c++17'], +) + +cc = meson.get_compiler('c') +py = import('python').find_installation( + modules: ['mako'], +) + +version_parts = meson.project_version().split('.') +soversion = '@0@.@1@'.format(version_parts[0], version_parts[1]) + +# MSVC lacks C99 complex types, so upstream compiles everything as C++ there +is_msvc = cc.get_argument_syntax() == 'msvc' +src_ext = is_msvc ? 'cc' : 'c' + +# Compiler names used by gen/archs.xml +if is_msvc + compiler_name = 'msvc' +elif cc.get_id() == 'gcc' + compiler_name = 'gnu' +else + compiler_name = cc.get_id() +endif + +cpu_family = host_machine.cpu_family() +cpu_is_x86 = cpu_family in ['x86', 'x86_64'] +is_64bit = cc.sizeof('void*') == 8 + +volk_args = [] +foreach h : ['cpuid.h', 'intrin.h', 'fenv.h', 'dlfcn.h'] + if cc.has_header(h) + volk_args += '-DHAVE_@0@'.format(h.underscorify().to_upper()) + endif +endforeach +if ( + host_machine.system() == 'darwin' + or not cc.has_header_symbol('stdlib.h', 'aligned_alloc') +) + if cc.has_header_symbol('stdlib.h', 'posix_memalign') + volk_args += '-DHAVE_POSIX_MEMALIGN' + endif +endif + +# GCC does not align the stack for AVX spills on Windows +# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54412 +if cc.get_id() == 'gcc' and host_machine.system() in ['windows', 'cygwin'] + volk_args += cc.get_supported_arguments('-Wa,-muse-unaligned-vector-move') +endif + +volk_public_incs = include_directories('include', 'kernels') +volk_incs = [volk_public_incs] +if is_msvc + volk_args += [ + '-D_USE_MATH_DEFINES', + '-DHAVE_CONFIG_H', + '/wo4309', + '/wd4752', + '/wo4273', + '/wo4838', + ] + volk_incs += include_directories('cmake/msvc') +endif + +use_cpu_features = cpu_family.startswith('mips') or cpu_family in [ + 'arm', + 'aarch64', + 'x86', + 'x86_64', + 'ppc', + 'ppc64', + 'riscv32', + 'riscv64', +] +cpu_features_deps = [] +if use_cpu_features + subdir('cpu_features') + volk_args += '-DVOLK_CPU_FEATURES=1' +endif + +orc_dep = dependency( + 'orc-0.4', + version: '>0.4.11', + method: 'pkg-config', + required: get_option('orc'), +) +have_orc = false +if orc_dep.found() + orcc = find_program( + 'orcc', + native: true, + required: get_option('orc'), + ) + have_orc = orcc.found() +endif + +######################################################################## +# Determine the archs supported by the compiler (lib/CMakeLists.txt) +######################################################################## +compile_utils = files('gen/volk_compile_utils.py') + +have_rvv_intrinsics = cc.compiles( + files('cmake/Checks/check-rvv-intrinsics.c'), + name: 'RVV intrinsics', +) + +available_archs = [] +arch_flags = {} +arch_flag_lines = run_command( + py, + compile_utils, + '--mode', + 'arch_flags', + '--compiler', + compiler_name, + check: true, +).stdout().strip().split(';') +foreach line : arch_flag_lines + fields = line.split(',') + arch = fields[0] + flags = [] + foreach i : range(1, fields.length()) + flags += fields[i] + endforeach + arch_flags += { + arch: flags, + } + + if arch.startswith('rv') and not have_rvv_intrinsics + continue + endif + if flags.length() > 0 + if not cc.has_multi_arguments(flags) + continue + endif + # Upstream uses a link check, which also rejects unusable float ABIs + if not cc.links( + 'int main(void) { return 0; }', + args: flags, + name: '@0@ flags'.format(arch), + ) + continue + endif + endif + available_archs += arch +endforeach + +overruled_archs = [] +if cpu_is_x86 + if not is_64bit and 'avx' in available_archs + if not cc.links( + '''#include + int main(void) { __m128 a; __m64 b; __m128 foo = _mm_cvtpi32_ps(a, b); return 0; }''', + args: arch_flags['avx'], + name: '_mm_cvtpi32_ps with AVX', + ) + overruled_archs += 'avx' + endif + endif +else + overruled_archs += [ + '3dnow', + 'mmx', + 'popcount', + 'fma', + 'sse', + 'sse2', + 'sse3', + 'ssse3', + 'sse4_a', + 'sse4_1', + 'sse4_2', + 'avx', + 'avx2', + 'avx512f', + 'avx512cd', + 'avx512dq', + ] +endif + +if cc.compiles( + '''#include + int main(void) { uint8_t *dest; uint8x8_t res; vst1_u8(dest, res); }''', + args: cc.get_supported_arguments('-Wno-uninitialized'), + name: 'NEON intrinsics', +) + if not cc.compiles( + '''#include + int main(void) { __VOLK_ASM("vrev32.8 q0, q0"); }''', + include_directories: volk_incs, + name: 'neonv7 assembly', + ) + overruled_archs += 'neonv7' + endif + if not cc.compiles( + '''#include + int main(void) { __VOLK_ASM("sub v1.4s,v1.4s,v1.4s"); }''', + include_directories: volk_incs, + name: 'neonv8 assembly', + ) + overruled_archs += 'neonv8' + endif +else + overruled_archs += ['neon', 'neonv7', 'neonv8'] +endif + +if not have_orc + overruled_archs += 'orc' +endif + +if cpu_is_x86 + overruled_archs += is_64bit ? '32' : '64' + if is_msvc + if is_64bit + overruled_archs += 'mmx' + endif + # built-in for MSVC > 2013 + available_archs += ['sse', 'sse2'] + endif +endif + +if cpu_family != 'riscv64' + overruled_archs += 'riscv64' +endif + +archs = [] +foreach arch : available_archs + if arch not in overruled_archs + archs += arch + endif +endforeach +available_archs = archs +message('Available architectures: ' + ' '.join(available_archs)) + +######################################################################## +# Determine the machines to build for the available archs +######################################################################## +available_machines = run_command( + py, + compile_utils, + '--mode', + 'machines', + '--archs', + ';'.join(available_archs), + check: true, +).stdout().strip().split(';') + +# A machine is redundant when a superset of it (e.g. with orc) is available +foreach arch : ['mmx', 'orc', '64', '32'] + redundant_machines = [] + foreach machine : available_machines + machine_no_arch = machine.replace('_' + arch, '') + if machine_no_arch != machine + redundant_machines += machine_no_arch + endif + endforeach + machines = [] + foreach machine : available_machines + if machine not in redundant_machines + machines += machine + endif + endforeach + available_machines = machines +endforeach +message('Available machines: ' + ' '.join(available_machines)) + +machine_flags = {} +foreach machine : available_machines + flags = [] + # MSVC needs no flags for intrinsics, but clang-cl does, and takes clang's + if cc.get_id() != 'msvc' + flags = run_command( + py, + compile_utils, + '--mode', + 'machine_flags', + '--machine', + machine, + '--compiler', + cc.get_id() == 'clang-cl' ? 'clang' : compiler_name, + check: true, + ).stdout().strip().split() + endif + # At -O0, -funsafe-math-optimizations makes GCC load -0.0f as +0.0f, which + # breaks the sign masks of the NEON sin/cos kernels + if ( + cc.get_id() == 'gcc' + and get_option('optimization') in ['plain', '0'] + and '-funsafe-math-optimizations' in flags + ) + flags += ['-fsigned-zeros', '-fno-associative-math'] + endif + machine_flags += { + machine: flags, + } +endforeach + +######################################################################## +# Code generation +######################################################################## +# Upstream globs the kernels and assembly sources +glob_command = [ + py, + '-c', + 'import pathlib, sys; print(";".join(sorted(p.name for p in pathlib.Path(sys.argv[1]).glob(sys.argv[2]))))', +] + +kernel_names = [] +kernel_headers = [] +foreach name : run_command( + glob_command, + meson.current_source_dir() / 'kernels' / 'volk', + '*.h', + check: true, +).stdout().strip().split(';') + kernel_names += name.replace('.h', '') + kernel_headers += files('kernels' / 'volk' / name) +endforeach + +tmpl_command = [ + py, + files('gen/volk_tmpl_utils.py'), + '--input', + '@INPUT@', + '--output', + '@OUTPUT@', +] +tmpl_depends = kernel_headers + files( + 'gen/archs.xml', + 'gen/machines.xml', + 'gen/volk_arch_defs.py', + 'gen/volk_compile_utils.py', + 'gen/volk_kernel_defs.py', + 'gen/volk_machine_defs.py', + 'gen/volk_tmpl_utils.py', +) + +subdir('include/volk') +subdir('lib') + +pkg = import('pkgconfig') +pkg.generate( + volk_lib, + description: 'VOLK: Vector Optimized Library of Kernels', + extra_cflags: volk_dep_args, +) diff --git a/subprojects/packagefiles/volk/meson_options.txt b/subprojects/packagefiles/volk/meson_options.txt new file mode 100644 index 000000000..fe1040c3b --- /dev/null +++ b/subprojects/packagefiles/volk/meson_options.txt @@ -0,0 +1,10 @@ +option( + 'orc', + type: 'feature', + description: 'Build kernels using the ORC compiler', +) +option( + 'tests', + type: 'feature', + description: 'Build the QA tests (requires fmt)', +) diff --git a/subprojects/volk.wrap b/subprojects/volk.wrap new file mode 100644 index 000000000..afff20e57 --- /dev/null +++ b/subprojects/volk.wrap @@ -0,0 +1,9 @@ +[wrap-file] +directory = volk-3.3.0 +source_url = https://github.com/gnuradio/volk/releases/download/v3.3.0/volk-3.3.0.tar.xz +source_filename = volk-3.3.0.tar.xz +source_hash = 10e9c26c8d9362f5cb14a58752116f4268dc107505a1d7f50efe4ba06d90982b +patch_directory = volk + +[provide] +dependency_names = volk diff --git a/tools/sanity_checks.py b/tools/sanity_checks.py index 82ddb0cd5..f846d6d57 100755 --- a/tools/sanity_checks.py +++ b/tools/sanity_checks.py @@ -176,6 +176,9 @@ 'makedef.py', 'stddef.h.in', }, + 'volk': { + 'time.h', + }, 'zlib-ng': { 'get-version.py', 'process-zconf.py',