Skip to content

Commit 7145dc5

Browse files
Merge pull request #299 from rust-lang/interleave-one
Fix interleave/deinterleave for vectors with only one lane
2 parents 691c8b2 + 5f70664 commit 7145dc5

File tree

2 files changed

+41
-45
lines changed

2 files changed

+41
-45
lines changed

crates/core_simd/src/swizzle.rs

Lines changed: 27 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -265,16 +265,13 @@ where
265265

266266
/// Interleave two vectors.
267267
///
268-
/// Produces two vectors with lanes taken alternately from `self` and `other`.
268+
/// The resulting vectors contain lanes taken alternatively from `self` and `other`, first
269+
/// filling the first result, and then the second.
269270
///
270-
/// The first result contains the first `LANES / 2` lanes from `self` and `other`,
271-
/// alternating, starting with the first lane of `self`.
272-
///
273-
/// The second result contains the last `LANES / 2` lanes from `self` and `other`,
274-
/// alternating, starting with the lane `LANES / 2` from the start of `self`.
271+
/// The reverse of this operation is [`Simd::deinterleave`].
275272
///
276273
/// ```
277-
/// #![feature(portable_simd)]
274+
/// # #![feature(portable_simd)]
278275
/// # use core::simd::Simd;
279276
/// let a = Simd::from_array([0, 1, 2, 3]);
280277
/// let b = Simd::from_array([4, 5, 6, 7]);
@@ -285,29 +282,17 @@ where
285282
#[inline]
286283
#[must_use = "method returns a new vector and does not mutate the original inputs"]
287284
pub fn interleave(self, other: Self) -> (Self, Self) {
288-
const fn lo<const LANES: usize>() -> [Which; LANES] {
289-
let mut idx = [Which::First(0); LANES];
290-
let mut i = 0;
291-
while i < LANES {
292-
let offset = i / 2;
293-
idx[i] = if i % 2 == 0 {
294-
Which::First(offset)
295-
} else {
296-
Which::Second(offset)
297-
};
298-
i += 1;
299-
}
300-
idx
301-
}
302-
const fn hi<const LANES: usize>() -> [Which; LANES] {
285+
const fn interleave<const LANES: usize>(high: bool) -> [Which; LANES] {
303286
let mut idx = [Which::First(0); LANES];
304287
let mut i = 0;
305288
while i < LANES {
306-
let offset = (LANES + i) / 2;
307-
idx[i] = if i % 2 == 0 {
308-
Which::First(offset)
289+
// Treat the source as a concatenated vector
290+
let dst_index = if high { i + LANES } else { i };
291+
let src_index = dst_index / 2 + (dst_index % 2) * LANES;
292+
idx[i] = if src_index < LANES {
293+
Which::First(src_index)
309294
} else {
310-
Which::Second(offset)
295+
Which::Second(src_index % LANES)
311296
};
312297
i += 1;
313298
}
@@ -318,11 +303,11 @@ where
318303
struct Hi;
319304

320305
impl<const LANES: usize> Swizzle2<LANES, LANES> for Lo {
321-
const INDEX: [Which; LANES] = lo::<LANES>();
306+
const INDEX: [Which; LANES] = interleave::<LANES>(false);
322307
}
323308

324309
impl<const LANES: usize> Swizzle2<LANES, LANES> for Hi {
325-
const INDEX: [Which; LANES] = hi::<LANES>();
310+
const INDEX: [Which; LANES] = interleave::<LANES>(true);
326311
}
327312

328313
(Lo::swizzle2(self, other), Hi::swizzle2(self, other))
@@ -336,8 +321,10 @@ where
336321
/// The second result takes every other lane of `self` and then `other`, starting with
337322
/// the second lane.
338323
///
324+
/// The reverse of this operation is [`Simd::interleave`].
325+
///
339326
/// ```
340-
/// #![feature(portable_simd)]
327+
/// # #![feature(portable_simd)]
341328
/// # use core::simd::Simd;
342329
/// let a = Simd::from_array([0, 4, 1, 5]);
343330
/// let b = Simd::from_array([2, 6, 3, 7]);
@@ -348,22 +335,17 @@ where
348335
#[inline]
349336
#[must_use = "method returns a new vector and does not mutate the original inputs"]
350337
pub fn deinterleave(self, other: Self) -> (Self, Self) {
351-
const fn even<const LANES: usize>() -> [Which; LANES] {
352-
let mut idx = [Which::First(0); LANES];
353-
let mut i = 0;
354-
while i < LANES / 2 {
355-
idx[i] = Which::First(2 * i);
356-
idx[i + LANES / 2] = Which::Second(2 * i);
357-
i += 1;
358-
}
359-
idx
360-
}
361-
const fn odd<const LANES: usize>() -> [Which; LANES] {
338+
const fn deinterleave<const LANES: usize>(second: bool) -> [Which; LANES] {
362339
let mut idx = [Which::First(0); LANES];
363340
let mut i = 0;
364-
while i < LANES / 2 {
365-
idx[i] = Which::First(2 * i + 1);
366-
idx[i + LANES / 2] = Which::Second(2 * i + 1);
341+
while i < LANES {
342+
// Treat the source as a concatenated vector
343+
let src_index = i * 2 + second as usize;
344+
idx[i] = if src_index < LANES {
345+
Which::First(src_index)
346+
} else {
347+
Which::Second(src_index % LANES)
348+
};
367349
i += 1;
368350
}
369351
idx
@@ -373,11 +355,11 @@ where
373355
struct Odd;
374356

375357
impl<const LANES: usize> Swizzle2<LANES, LANES> for Even {
376-
const INDEX: [Which; LANES] = even::<LANES>();
358+
const INDEX: [Which; LANES] = deinterleave::<LANES>(false);
377359
}
378360

379361
impl<const LANES: usize> Swizzle2<LANES, LANES> for Odd {
380-
const INDEX: [Which; LANES] = odd::<LANES>();
362+
const INDEX: [Which; LANES] = deinterleave::<LANES>(true);
381363
}
382364

383365
(Even::swizzle2(self, other), Odd::swizzle2(self, other))

crates/core_simd/tests/swizzle.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,17 @@ fn interleave() {
6060
assert_eq!(even, a);
6161
assert_eq!(odd, b);
6262
}
63+
64+
// portable-simd#298
65+
#[test]
66+
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
67+
fn interleave_one() {
68+
let a = Simd::from_array([0]);
69+
let b = Simd::from_array([1]);
70+
let (lo, hi) = a.interleave(b);
71+
assert_eq!(lo.to_array(), [0]);
72+
assert_eq!(hi.to_array(), [1]);
73+
let (even, odd) = lo.deinterleave(hi);
74+
assert_eq!(even, a);
75+
assert_eq!(odd, b);
76+
}

0 commit comments

Comments
 (0)