1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use std::mem;

pub trait MULX {
    fn mulx(self, Self, &mut Self) -> Self;
}

impl MULX for u8 {
    fn mulx(self, y: u8, p: &mut u8) -> u8 {
        let result: u16 = (self as u16) * (y as u16);
        *p = (result >> 8) as u8;
        result as u8
    }
}

impl MULX for u16 {
    fn mulx(self, y: u16, p: &mut u16) -> u16 {
        let result: u32 = (self as u32) * (y as u32);
        *p = (result >> 16) as u16;
        result as u16
    }
}

impl MULX for u32 {
    fn mulx(self, y: u32, p: &mut u32) -> u32 {
        let result: u64 = (self as u64) * (y as u64);
        *p = (result >> 32) as u32;
        result as u32
    }
}

impl MULX for u64 {
    fn mulx(self, y: u64, hi: &mut u64) -> u64 {
        let result: u128 = (self as u128) * (y as u128);
        *hi = (result >> 64) as u64;
        result as u64
    }
}

impl MULX for usize {
    fn mulx(self, y: usize, p: &mut usize) -> usize {
        match mem::size_of::<usize>() * 8 {
            8 => (self as u8).mulx(y as u8, unsafe { mem::transmute(p) }) as usize,
            16 => (self as u16).mulx(y as u16, unsafe { mem::transmute(p) }) as usize,
            32 => (self as u32).mulx(y as u32, unsafe { mem::transmute(p) }) as usize,
            64 => (self as u64).mulx(y as u64, unsafe { mem::transmute(p) }) as usize,
            _ => unreachable!(),
        }
    }
}

impl MULX for i8 {
    fn mulx(self, y: i8, p: &mut i8) -> i8 {
        (self as u8).mulx(y as u8, unsafe { mem::transmute(p) }) as i8
    }
}

impl MULX for i16 {
    fn mulx(self, y: i16, p: &mut i16) -> i16 {
        (self as u16).mulx(y as u16, unsafe { mem::transmute(p) }) as i16
    }
}

impl MULX for i32 {
    fn mulx(self, y: i32, p: &mut i32) -> i32 {
        (self as u32).mulx(y as u32, unsafe { mem::transmute(p) }) as i32
    }
}

impl MULX for i64 {
    fn mulx(self, y: i64, p: &mut i64) -> i64 {
        (self as u64).mulx(y as u64, unsafe { mem::transmute(p) }) as i64
    }
}


impl MULX for isize {
    fn mulx(self, y: isize, p: &mut isize) -> isize {
        (self as usize).mulx(y as usize, unsafe { mem::transmute(p) }) as isize
    }
}

/// Unsigned multiply without affecting flags.
///
/// Unsigned multiplication of `x` with `y` storing the high half of the result
/// in `p` and returning the low half of the result.
///
/// # Intrinsics (when available BMI2)
///
/// [`MULX`](http://www.felixcloutier.com/x86/MULX.html): Unsigned Multiply
/// Without Affecting Flags (supports 32/64 bit registers).
///
/// # Examples
///
/// ```
/// use bitintr::bmi2::mulx;
///
/// { // 8-bit
///   let a: u8 = 128;
///   let b: u8 = 128;
///   let mut hi: u8 = 0;
///   let lo: u8 = mulx(a, b, &mut hi);
///   // result = 16384 = 0b0100_0000_0000_0000u16
///   //                    ^~hi~~~~~ ^~lo~~~~~
///   assert_eq!(lo, 0b0000_0000);
///   assert_eq!(hi, 0b0100_0000);
/// }
/// { // 16-bit
///   let a: u16 = 65_500;
///   let b: u16 = 65_500;
///   let mut hi: u16 = 0;
///   let lo: u16 = mulx(a, b, &mut hi);
///   // result = 4290250000 = 0b0b1111_1111_1011_1000_0000_0101_0001_0000u32
///   //                           ^~hi~~~~~~~~~~~~~~~ ^~lo~~~~~~~~~~~~~~~
///   assert_eq!(lo, 0b0000_0101_0001_0000);
///   assert_eq!(hi, 0b1111_1111_1011_1000);
/// }
/// { // 32-bit
///   let a: u32 = 4_294_967_200;
///   let b: u32 = 2;
///   let mut hi: u32 = 0;
///   let lo: u32 = mulx(a, b, &mut hi);
///   // result = 8589934400
///   //        = 0b0b0001_1111_1111_1111_1111_1111_1111_0100_0000u64
///   //              ^~hi ^~lo~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
///   assert_eq!(lo, 0b1111_1111_1111_1111_1111_1111_0100_0000u32);
///   assert_eq!(hi, 0b0001u32);
/// }
/// { // 64-bit
///   let a: u64 = 9_223_372_036_854_775_800;
///   let b: u64 = 100;
///   let mut hi: u64 = 0;
///   let lo: u64 = mulx(a, b, &mut hi);
///   // result = 922337203685477580000
///   //        = 0b0b00110001_11111111_11111111_11111111_11111111_11111111_11111111_11111100_11100000u128
///   //         ^~hi~~~~~~~~~ ^~lo~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
///   assert_eq!(lo, 0b11111111_11111111_11111111_11111111_11111111_11111111_11111100_11100000u64);
///   assert_eq!(hi, 0b00110001u64);
/// }
/// ```
pub fn mulx<T: MULX>(x: T, y: T, hi: &mut T) -> T {
    x.mulx(y, hi)
}