Skip to content

Commit e13f082

Browse files
Add address and undefined sanitizers to CI
To have them pass: - sanitize test inputs to avoid situations that trigger undefined behavior - silent undefined sanitizer on slow batch_cast
1 parent f50ec39 commit e13f082

File tree

7 files changed

+70
-15
lines changed

7 files changed

+70
-15
lines changed

.github/workflows/sanitizer.yml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: sanitizer
2+
on: [push, pull_request]
3+
concurrency:
4+
group: ${{ github.workflow }}-${{ github.job }}-${{ github.ref }}
5+
cancel-in-progress: true
6+
defaults:
7+
run:
8+
shell: bash -l {0}
9+
jobs:
10+
build:
11+
runs-on: ubuntu-latest
12+
name: 'sanitizer - ${{ matrix.sanitizer }}'
13+
strategy:
14+
matrix:
15+
sanitizer:
16+
- address
17+
- undefined
18+
steps:
19+
- name: Checkout xsimd
20+
uses: actions/checkout@v3
21+
- name: Configure build
22+
run: |
23+
mkdir _build
24+
cd _build
25+
cmake .. -DBUILD_TESTS=ON \
26+
-DBUILD_BENCHMARK=ON \
27+
-DBUILD_EXAMPLES=ON \
28+
-DDOWNLOAD_DOCTEST=ON \
29+
-DCMAKE_BUILD_TYPE=Release \
30+
-DCMAKE_CXX_COMPILER=clang++ \
31+
-DCMAKE_CXX_FLAGS='-fsanitize=${{ matrix.sanitizer }}' \
32+
-G Ninja
33+
- name: Build
34+
run: ninja -C _build
35+
- name: Test
36+
run: |
37+
cd _build/test
38+
./test_xsimd

include/xsimd/arch/common/xsimd_common_math.hpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ namespace xsimd
116116
{
117117
return fast_cast(self, out, A {});
118118
}
119+
#if defined(__clang__) || __GNUC__
120+
template <class A, class T_out, class T_in>
121+
XSIMD_INLINE batch<T_out, A> batch_cast(batch<T_in, A> const& self, batch<T_out, A> const&, requires_arch<common>, with_slow_conversion) noexcept
122+
__attribute__((no_sanitize("undefined")));
123+
#endif
119124
template <class A, class T_out, class T_in>
120125
XSIMD_INLINE batch<T_out, A> batch_cast(batch<T_in, A> const& self, batch<T_out, A> const&, requires_arch<common>, with_slow_conversion) noexcept
121126
{
@@ -126,7 +131,8 @@ namespace xsimd
126131
alignas(A::alignment()) T_in buffer_in[batch_type_in::size];
127132
alignas(A::alignment()) T_out buffer_out[batch_type_out::size];
128133
self.store_aligned(&buffer_in[0]);
129-
std::copy(std::begin(buffer_in), std::end(buffer_in), std::begin(buffer_out));
134+
for (size_t i = 0; i < batch_type_in::size; ++i)
135+
buffer_out[i] = static_cast<T_out>(buffer_in[i]);
130136
return batch_type_out::load_aligned(buffer_out);
131137
}
132138

test/test_batch.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -879,12 +879,12 @@ struct batch_test
879879
for (size_t i = 0; i < size; ++i)
880880
{
881881
bool negative_lhs = std::is_signed<T>::value && (i % 2 == 1);
882-
lhs[i] = value_type(i) * (negative_lhs ? -10 : 10);
882+
lhs[i] = value_type(i) * (negative_lhs ? -3 : 3);
883883
if (lhs[i] == value_type(0))
884884
{
885885
lhs[i] += value_type(1);
886886
}
887-
rhs[i] = value_type(i) + value_type(4);
887+
rhs[i] = value_type(i) + value_type(2);
888888
}
889889
scalar = value_type(3);
890890
}

test/test_batch_cast.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ namespace detail
2929
is_convertible(T_in value)
3030
{
3131
int64_t signed_value = static_cast<int64_t>(value);
32-
return signed_value <= static_cast<int64_t>(std::numeric_limits<T_out>::max()) && signed_value >= static_cast<int64_t>(std::numeric_limits<T_out>::lowest());
32+
return signed_value < static_cast<int64_t>(std::numeric_limits<T_out>::max()) && signed_value >= static_cast<int64_t>(std::numeric_limits<T_out>::lowest());
3333
}
3434

3535
template <class T_out, class T_in>
@@ -43,7 +43,7 @@ namespace detail
4343
inline typename std::enable_if<std::is_floating_point<T_in>::value && std::is_integral<T_out>::value, bool>::type
4444
is_convertible(T_in value)
4545
{
46-
return value <= static_cast<T_in>(std::numeric_limits<T_out>::max()) && value >= static_cast<T_in>(std::numeric_limits<T_out>::lowest());
46+
return value < static_cast<T_in>(std::numeric_limits<T_out>::max()) && value >= static_cast<T_in>(std::numeric_limits<T_out>::lowest());
4747
}
4848

4949
template <class T_out, class T_in>
@@ -328,12 +328,19 @@ struct batch_cast_test
328328
using B_common_in = xsimd::batch<T_in>;
329329
using B_common_out = xsimd::batch<T_out>;
330330

331-
T_in in_test_value = static_cast<T_in>(test_value);
331+
auto clamp = [](T v)
332+
{
333+
return static_cast<T_in>(xsimd::min(v, static_cast<T>(std::numeric_limits<T_in>::max() - 1)));
334+
};
335+
336+
T_in in_test_value = clamp(test_value);
332337
if (detail::is_convertible<T_out>(in_test_value))
333338
{
334339
B_common_out res = xsimd::batch_cast<T_out>(B_common_in(in_test_value));
335340
INFO(name);
336-
CHECK_SCALAR_EQ(res.get(0), static_cast<T_out>(in_test_value));
341+
T_out scalar_ref = static_cast<T_out>(in_test_value);
342+
T_out scalar_res = res.get(0);
343+
CHECK_SCALAR_EQ(scalar_ref, scalar_res);
337344
}
338345
}
339346

test/test_batch_int.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,8 @@ struct batch_int_test
239239
array_type expected;
240240
std::transform(lhs.cbegin(), lhs.cend(), expected.begin(),
241241
[nb_sh](const value_type& v)
242-
{ return v << nb_sh; });
243-
batch_type res = batch_lhs() << nb_sh;
242+
{ return xsimd::abs(v) << nb_sh; });
243+
batch_type res = abs(batch_lhs()) << nb_sh;
244244
INFO("batch << scalar");
245245
CHECK_BATCH_EQ(res, expected);
246246
}
@@ -249,8 +249,8 @@ struct batch_int_test
249249
array_type expected;
250250
std::transform(lhs.cbegin(), lhs.cend(), shift.cbegin(), expected.begin(),
251251
[](const value_type& l, const value_type& r)
252-
{ return l << r; });
253-
batch_type res = batch_lhs() << batch_shift();
252+
{ return xsimd::abs(l) << r; });
253+
batch_type res = abs(batch_lhs()) << batch_shift();
254254
INFO("batch << batch");
255255
CHECK_BATCH_EQ(res, expected);
256256
}

test/test_power.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ struct power_test
4040
for (size_t i = 0; i < nb_input; ++i)
4141
{
4242
zero_input[i] = 0;
43-
lhs_input[i] = value_type(i) / 4 + value_type(1.2) * std::sqrt(value_type(i + 0.25));
43+
lhs_input[i] = value_type(i / 4 + 1.2 * std::sqrt(i + 0.25));
4444
zlhs_input[i] = lhs_input[i] * (i % 2);
45-
rhs_input[i] = value_type(10.2) / (i + 2) + value_type(0.25);
45+
rhs_input[i] = value_type(10.2 / (i + 2) + 0.25);
4646
}
4747

4848
expected.resize(nb_input);

test/test_select.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,14 @@ struct select_test
3434
nb_input = size * 10000;
3535
lhs_input.resize(nb_input);
3636
rhs_input.resize(nb_input);
37+
auto clamp = [](double v)
38+
{
39+
return static_cast<value_type>(std::min(v, static_cast<double>(std::numeric_limits<value_type>::max())));
40+
};
3741
for (size_t i = 0; i < nb_input; ++i)
3842
{
39-
lhs_input[i] = value_type(i) / 4 + value_type(1.2) * std::sqrt(value_type(i + 0.25));
40-
rhs_input[i] = value_type(10.2) / (i + 2) + value_type(0.25);
43+
lhs_input[i] = clamp(i / 4 + 1.2 * std::sqrt(i + 0.25));
44+
rhs_input[i] = clamp(10.2 / (i + 2) + 0.25);
4145
}
4246
expected.resize(nb_input);
4347
res.resize(nb_input);

0 commit comments

Comments
 (0)