Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .github/workflows/c-api-linux.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
name: Build C-API shared library with g++ for Linux

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build-linux:
runs-on: ubuntu-latest
env:
NAME: x86simdsort

steps:
- uses: actions/checkout@v4

- name: Define variables
run: |
echo "LIBNAME=${NAME}-c-api" >> $GITHUB_ENV
echo "FOLDER=${NAME}-${GITHUB_SHA::8}" >> $GITHUB_ENV

- name: Install g++
run: |
sudo apt-get update
sudo apt-get install -y g++

- name: Build (Linux)
run: |
cd make-c-api
make all -j2

- name: Run executable (Linux sanity check)
run: |
cd make-c-api
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./ ./smoke.exe

- name: Package (Linux)
run: |
mkdir -p artifacts/${NAME}
cp make-c-api/*.{so,h} artifacts/${NAME}/
cd artifacts
tar -czvf ${NAME}-${GITHUB_SHA::8}.tgz ${NAME}

- name: Upload artifacts (Linux)
uses: actions/upload-artifact@v4
with:
name: c-api-linux-build
path: artifacts/*.tgz


71 changes: 71 additions & 0 deletions .github/workflows/c-api-win-msvc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
name: MSYS2 DLL + MSVC EXE

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build-windows-mingw:
runs-on: windows-latest
env:
NAME: x86simdsort

steps:
- name: Checkout sources
uses: actions/checkout@v4

- name: Define variables
run: |
echo "LIBNAME=${NAME}-c-api" >> $GITHUB_ENV
echo "FOLDER=${NAME}-${GITHUB_SHA::8}" >> $GITHUB_ENV

# Build DLL with MSYS2
- name: Install MSYS2
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
install: >-
mingw-w64-x86_64-toolchain
mingw-w64-x86_64-clang
make
zip

- name: Build DLL (MinGW/Clang)
shell: msys2 {0}
run: |
cd make-c-api
make all -j2 TARGET=DLL

- name: Package (Windows)
shell: msys2 {0}
run: |
mkdir -p artifacts/${NAME}
cp make-c-api/*.{lib,dll,h} artifacts/${NAME}/
cd artifacts
zip -r ${NAME}-${GITHUB_SHA::8}.zip ${NAME}

# --- Build EXE test with MSVC ---
- name: Configure MSVC environment
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64 # Or other architectures like x86, amd64_x86, etc.

- name: Build EXE with MSVC
run: |
cd make-c-api
cl /EHsc /O2 /arch:AVX2 /std:c++20 /GL /Gy /MD /nologo /DNDEBUG smoke.cpp x86simdsort-c-api.dll.lib /Fe:smoke.exe

# --- Run test ---
- name: Run EXE
shell: cmd
run: |
cd make-c-api
.\smoke.exe

- name: Upload artifacts (Windows)
uses: actions/upload-artifact@v4
with:
name: c-api-windows-build
path: artifacts/*.zip
45 changes: 45 additions & 0 deletions .github/workflows/c-api-win.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
name: Build C-API shared library with mingw-64-clang++ for Windows

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
build-windows-mingw:
runs-on: ubuntu-latest
env:
NAME: x86simdsort

steps:
- uses: actions/checkout@v4

- name: Define variables
run: |
echo "LIBNAME=${NAME}-c-api" >> $GITHUB_ENV
echo "FOLDER=${NAME}-${GITHUB_SHA::8}" >> $GITHUB_ENV

- name: Install MinGW-w64
run: |
sudo apt-get update
sudo apt install -y mingw-w64
sudo apt install -y clang

- name: Build (Windows cross-compile DLL)
run: |
cd make-c-api
make all -j2 TARGET=DLL

- name: Package (Windows)
run: |
mkdir -p artifacts/${NAME}
cp make-c-api/*.{lib,dll,h} artifacts/${NAME}/
cd artifacts
zip -r ${NAME}-${GITHUB_SHA::8}.zip ${NAME}

- name: Upload artifacts (Windows)
uses: actions/upload-artifact@v4
with:
name: c-api-windows-build
path: artifacts/*.zip
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,37 @@ xss_dep = xss.get_variable('x86simdsortcpp_dep')
For more detailed instructions please refer to Meson
[documentation](https://mesonbuild.com/Subprojects.html#using-a-subproject).

## Build with C-only API, targeting Windows and MSVC

The folder `make-c-api` contains a `Makefile` to build a shared library exporting only C-symbols.
This avoid potential conflicts of inconsistent mangling and calling convention across compilers.

Only a reduced set of functions are exported (`qselect`, `qsort`, `partial_qsort`, `keyvalue_qsort`, `keyvalue_partial_qsort`, `keyvalue_qselect`)
and only for a reduces set of types (`int32`, `uint32`, `int64`, `uint64`, `float`, `double`).

In the subfolder `make-c-api` a `Makefile` is provided which supports the following combinations of operating systems, compilers and targets:

```
+--------------+------------------+------------+----------------------+---------------------+
| Compilation | Compiler | Target | Files Generated | Build Command line |
| Platform | | Platform | | |
+--------------+------------------+------------+----------------------+---------------------+
| LINUX | g++ | LINUX | libx86simdsort.so | make |
+--------------+------------------+------------+----------------------+---------------------+
| CYGWIN | clang++ | CYGWIN | libx86simdsort.so | make |
+--------------+------------------+------------+----------------------+---------------------+
| ANY | mingw32 clang++ | Windows | x86simdsort.dll | make TARGET=DLL |
| | | | x86simdsort.dll.lib | |
+--------------+------------------+------------+----------------------+---------------------+
```

In all cases, exceptions and memory allocations never cross the shared library boundary.
In particular, if the target is a DLL, the library is self-contained, i.e. it does not depend on any other DLL.

Compilation is tested with clang 20 and gcc 15.

An include header `x86simdsort-c-api.h` and a `smoke.exe` executable test are also generated.

## Example usage

#### Sort an array of floats
Expand Down
29 changes: 29 additions & 0 deletions lib/c-api-def.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#include "c-api-headers.h"

LIBRARY x86simdsortcpp
EXPORTS

#define XSS_XI1(n,t) XSS_C_EXP_NAME1(qsort,n)
#define XSS_XF1(n,t) XSS_C_EXP_NAME1(qsort,n)
#include "c-api-x-macro1.h"

#define XSS_XI1(n,t) XSS_C_EXP_NAME1(qselect,n)
#define XSS_XF1(n,t) XSS_C_EXP_NAME1(qselect,n)
#include "c-api-x-macro1.h"

#define XSS_XI1(n,t) XSS_C_EXP_NAME1(partial_qsort,n)
#define XSS_XF1(n,t) XSS_C_EXP_NAME1(partial_qsort,n)
#include "c-api-x-macro1.h"


#define XSS_XI2(n1,t1, n2,t2) XSS_C_EXP_NAME2(keyvalue_qsort, n1, n2)
#define XSS_XF2(n1,t1, n2,t2) XSS_C_EXP_NAME2(keyvalue_qsort, n1, n2)
#include "c-api-x-macro2.h"

#define XSS_XI2(n1,t1, n2,t2) XSS_C_EXP_NAME2(keyvalue_qselect, n1, n2)
#define XSS_XF2(n1,t1, n2,t2) XSS_C_EXP_NAME2(keyvalue_qselect, n1, n2)
#include "c-api-x-macro2.h"

#define XSS_XI2(n1,t1, n2,t2) XSS_C_EXP_NAME2(keyvalue_partial_qsort, n1, n2)
#define XSS_XF2(n1,t1, n2,t2) XSS_C_EXP_NAME2(keyvalue_partial_qsort, n1, n2)
#include "c-api-x-macro2.h"
37 changes: 37 additions & 0 deletions lib/c-api-export.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#include "c-api-headers.h"

#ifdef XSS_EXPORTING
# if defined(__MINGW64__)
# define XSS_C_EXPORT __declspec(dllexport)
# else
# define XSS_C_EXPORT __attribute__((visibility("default")))
# endif
# define XSS_C_BODY(body) { try { body; return true; } catch(...) { return false; } }
#else
# define XSS_C_EXPORT XSS_DLL_IMPORT
# define XSS_C_BODY(body) ;
#endif

#define XSS_XI1(n,t) XSS_C_EXPORT XSS_QSORT_HEADER_INT(n,t) XSS_C_BODY(x86simdsort::qsort(ar, size, false, descending))
#define XSS_XF1(n,t) XSS_C_EXPORT XSS_QSORT_HEADER_FLT(n,t) XSS_C_BODY(x86simdsort::qsort(ar, size, hasnan, descending))
#include "c-api-x-macro1.h"

#define XSS_XI1(n,t) XSS_C_EXPORT XSS_QSELECT_HEADER_INT(n,t) XSS_C_BODY(x86simdsort::qselect(ar, k, size, false, descending))
#define XSS_XF1(n,t) XSS_C_EXPORT XSS_QSELECT_HEADER_FLT(n,t) XSS_C_BODY(x86simdsort::qselect(ar, k, size, hasnan, descending))
#include "c-api-x-macro1.h"

#define XSS_XI1(n,t) XSS_C_EXPORT XSS_QPSORT_HEADER_INT(n,t) XSS_C_BODY(x86simdsort::partial_qsort(ar, k, size, false, descending))
#define XSS_XF1(n,t) XSS_C_EXPORT XSS_QPSORT_HEADER_FLT(n,t) XSS_C_BODY(x86simdsort::partial_qsort(ar, k, size, hasnan, descending))
#include "c-api-x-macro1.h"

#define XSS_XI2(n1,t1, n2,t2) XSS_C_EXPORT XSS_QKVSORT_HEADER_INT(n1,t1,n2,t2) XSS_C_BODY(x86simdsort::keyvalue_qsort(keys, vals, size, false, descending))
#define XSS_XF2(n1,t1, n2,t2) XSS_C_EXPORT XSS_QKVSORT_HEADER_FLT(n1,t1,n2,t2) XSS_C_BODY(x86simdsort::keyvalue_qsort(keys, vals, size, hasnan, descending))
#include "c-api-x-macro2.h"

#define XSS_XI2(n1,t1, n2,t2) XSS_C_EXPORT XSS_QKVSEL_HEADER_INT(n1,t1,n2,t2) XSS_C_BODY(x86simdsort::keyvalue_select(keys, vals, k, size, false, descending))
#define XSS_XF2(n1,t1, n2,t2) XSS_C_EXPORT XSS_QKVSEL_HEADER_FLT(n1,t1,n2,t2) XSS_C_BODY(x86simdsort::keyvalue_select(keys, vals, k, size, hasnan, descending))
#include "c-api-x-macro2.h"

#define XSS_XI2(n1,t1, n2,t2) XSS_C_EXPORT XSS_QKVPSORT_HEADER_INT(n1,t1,n2,t2) XSS_C_BODY(x86simdsort::keyvalue_partial_sort(keys, vals, k, size, false, descending))
#define XSS_XF2(n1,t1, n2,t2) XSS_C_EXPORT XSS_QKVPSORT_HEADER_FLT(n1,t1,n2,t2) XSS_C_BODY(x86simdsort::keyvalue_partial_sort(keys, vals, k, size, hasnan, descending))
#include "c-api-x-macro2.h"
60 changes: 60 additions & 0 deletions lib/c-api-header-gen.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
COMMENT This is an auto-generated file
COMMENT This header is intended to be used for shared libraries (so or dll) compiled to the shared_c_api target

PRAGMA ifndef __X86_SIMD_SORT_C_API_H__
PRAGMA define __X86_SIMD_SORT_C_API_H__

EMPTYLINE
PRAGMA include <cstdint>
EMPTYLINE

PRAGMA define XSS_DLL_IMPORT

#include "c-api-headers.h"

EMPTYLINE
COMMENT DLL import declarations

PRAGMA ifdef __cplusplus
extern "C" {
PRAGMA endif
#include "c-api-export.h"
PRAGMA ifdef __cplusplus
} COMMENT extern "C"
PRAGMA endif

EMPTYLINE
PRAGMA ifdef __cplusplus
COMMENT C++ overloaded dispatchers
namespace xss
{

#define XSS_XI1(n,t) inline bool qsort(t* ar, uint64_t sz, bool desc) { return XSS_C_EXP_NAME1(qsort,n)( ar, sz, desc ); }
#define XSS_XF1(n,t) inline bool qsort(t* ar, uint64_t sz, bool hasnan, bool desc) { return XSS_C_EXP_NAME1(qsort,n)( ar, sz, hasnan, desc ); }
#include "c-api-x-macro1.h"

#define XSS_XI1(n,t) inline bool qselect(t* ar, uint64_t k, uint64_t sz, bool desc) { return XSS_C_EXP_NAME1(qselect,n)( ar, k, sz, desc ); }
#define XSS_XF1(n,t) inline bool qselect(t* ar, uint64_t k, uint64_t sz, bool hasnan, bool desc) { return XSS_C_EXP_NAME1(qselect,n)( ar, k, sz, hasnan, desc ); }
#include "c-api-x-macro1.h"

#define XSS_XI1(n,t) inline bool partial_qsort(t* ar, uint64_t k, uint64_t sz, bool desc) { return XSS_C_EXP_NAME1(partial_qsort,n)( ar, k, sz, desc ); }
#define XSS_XF1(n,t) inline bool partial_qsort(t* ar, uint64_t k, uint64_t sz, bool hasnan, bool desc) { return XSS_C_EXP_NAME1(partial_qsort,n)( ar, k, sz, hasnan, desc ); }
#include "c-api-x-macro1.h"

#define XSS_XI2(n1,t1, n2,t2) inline bool keyvalue_qsort(t1* keys, t2* vals, uint64_t sz, bool desc) { return XSS_C_EXP_NAME2(keyvalue_qsort,n1, n2)( keys, vals, sz, desc ); }
#define XSS_XF2(n1,t1, n2,t2) inline bool keyvalue_qsort(t1* keys, t2* vals, uint64_t sz, bool hasnan, bool desc) { return XSS_C_EXP_NAME2(keyvalue_qsort,n1, n2)( keys, vals, sz, hasnan, desc ); }
#include "c-api-x-macro2.h"

#define XSS_XI2(n1,t1, n2,t2) inline bool keyvalue_qselect(t1* keys, t2* vals, uint64_t k, uint64_t sz, bool desc) { return XSS_C_EXP_NAME2(keyvalue_qselect,n1, n2)( keys, vals, k, sz, desc ); }
#define XSS_XF2(n1,t1, n2,t2) inline bool keyvalue_qselect(t1* keys, t2* vals, uint64_t k, uint64_t sz, bool hasnan, bool desc) { return XSS_C_EXP_NAME2(keyvalue_qselect,n1, n2)( keys, vals, k, sz, hasnan, desc ); }
#include "c-api-x-macro2.h"

#define XSS_XI2(n1,t1, n2,t2) inline bool keyvalue_partial_qsort(t1* keys, t2* vals, uint64_t k, uint64_t sz, bool desc) { return XSS_C_EXP_NAME2(keyvalue_partial_qsort,n1, n2)( keys, vals, k, sz, desc ); }
#define XSS_XF2(n1,t1, n2,t2) inline bool keyvalue_partial_qsort(t1* keys, t2* vals, uint64_t k, uint64_t sz, bool hasnan, bool desc) { return XSS_C_EXP_NAME2(keyvalue_partial_qsort,n1, n2)( keys, vals, k, sz, hasnan, desc ); }
#include "c-api-x-macro2.h"

} COMMENT namespace xss

PRAGMA endif COMMENT ifdef __cplusplus
EMPTYLINE
PRAGMA endif COMMENT ifndef __X86_SIMD_SORT_C_API_H__
25 changes: 25 additions & 0 deletions lib/c-api-headers.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#pragma once

#define XSS_C_EXP_NAME1(name, ty) c_xss_##name##_##ty
#define XSS_C_EXP_NAME2(name, ty1, ty2) c_xss_##name##_##ty1##_##ty2

#define XSS_QSORT_HEADER_INT(n,t) bool XSS_C_EXP_NAME1(qsort, n)(t *ar, uint64_t size, bool descending)
#define XSS_QSORT_HEADER_FLT(n,t) bool XSS_C_EXP_NAME1(qsort, n)(t *ar, uint64_t size, bool hasnan, bool descending)

#define XSS_QSELECT_HEADER_INT(n,t) bool XSS_C_EXP_NAME1(qselect, n)(t *ar, uint64_t k, uint64_t size, bool descending)
#define XSS_QSELECT_HEADER_FLT(n,t) bool XSS_C_EXP_NAME1(qselect, n)(t *ar, uint64_t k, uint64_t size, bool hasnan, bool descending)

#define XSS_QPSORT_HEADER_INT(n,t) bool XSS_C_EXP_NAME1(partial_qsort, n)(t *ar, uint64_t k, uint64_t size, bool descending)
#define XSS_QPSORT_HEADER_FLT(n,t) bool XSS_C_EXP_NAME1(partial_qsort, n)(t *ar, uint64_t k, uint64_t size, bool hasnan, bool descending)

#define XSS_QKVSORT_HEADER_INT(n1,t1,n2,t2) bool XSS_C_EXP_NAME2(keyvalue_qsort, n1, n2)(t1 *keys, t2* vals, uint64_t size, bool descending)
#define XSS_QKVSORT_HEADER_FLT(n1,t1,n2,t2) bool XSS_C_EXP_NAME2(keyvalue_qsort, n1, n2)(t1 *keys, t2* vals, uint64_t size, bool hasnan, bool descending)

#define XSS_QKVSEL_HEADER_INT(n1,t1,n2,t2) bool XSS_C_EXP_NAME2(keyvalue_qselect, n1, n2)(t1 *keys, t2* vals, uint64_t k, uint64_t size, bool descending)
#define XSS_QKVSEL_HEADER_FLT(n1,t1,n2,t2) bool XSS_C_EXP_NAME2(keyvalue_qselect, n1, n2)(t1 *keys, t2* vals, uint64_t k, uint64_t size, bool hasnan, bool descending)

#define XSS_QKVPSORT_HEADER_INT(n1,t1,n2,t2) bool XSS_C_EXP_NAME2(keyvalue_partial_qsort, n1, n2)(t1 *keys, t2* vals, uint64_t k, uint64_t size, bool descending)
#define XSS_QKVPSORT_HEADER_FLT(n1,t1,n2,t2) bool XSS_C_EXP_NAME2(keyvalue_partial_qsort, n1, n2)(t1 *keys, t2* vals, uint64_t k, uint64_t size, bool hasnan, bool descending)



33 changes: 33 additions & 0 deletions lib/c-api-ver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#include "c-api-headers.h"

{
global:

#define XSS_XI1(n,t) XSS_C_EXP_NAME1(qsort,n);
#define XSS_XF1(n,t) XSS_C_EXP_NAME1(qsort,n);
#include "c-api-x-macro1.h"

#define XSS_XI1(n,t) XSS_C_EXP_NAME1(qselect,n);
#define XSS_XF1(n,t) XSS_C_EXP_NAME1(qselect,n);
#include "c-api-x-macro1.h"

#define XSS_XI1(n,t) XSS_C_EXP_NAME1(partial_qsort,n);
#define XSS_XF1(n,t) XSS_C_EXP_NAME1(partial_qsort,n);
#include "c-api-x-macro1.h"


#define XSS_XI2(n1,t1, n2,t2) XSS_C_EXP_NAME2(keyvalue_qsort, n1, n2);
#define XSS_XF2(n1,t1, n2,t2) XSS_C_EXP_NAME2(keyvalue_qsort, n1, n2);
#include "c-api-x-macro2.h"

#define XSS_XI2(n1,t1, n2,t2) XSS_C_EXP_NAME2(keyvalue_qselect, n1, n2);
#define XSS_XF2(n1,t1, n2,t2) XSS_C_EXP_NAME2(keyvalue_qselect, n1, n2);
#include "c-api-x-macro2.h"

#define XSS_XI2(n1,t1, n2,t2) XSS_C_EXP_NAME2(keyvalue_partial_qsort, n1, n2);
#define XSS_XF2(n1,t1, n2,t2) XSS_C_EXP_NAME2(keyvalue_partial_qsort, n1, n2);
#include "c-api-x-macro2.h"

local:
*;
};
Loading