{"action":"created","comment":{"url":"https://api.github.com/repos/rust-lang/rfcs/pulls/comments/176811779","pull_request_review_id":106587422,"id":176811779,"diff_hunk":"@@ -0,0 +1,1465 @@\n+- Feature Name: `portable_packed_vector_types`\n+- Start Date: (fill me in with today's date, YYYY-MM-DD)\n+- RFC PR: (leave this empty)\n+- Rust Issue: (leave this empty)\n+\n+# Summary\n+[summary]: #summary\n+\n+This RFC adds portable packed SIMD vector types up to 256-bit.\n+\n+Future RFCs will attempt to answer some of the unresolved questions and might\n+potentially cover extensions as they mature in `stdsimd`, like, for example,\n+portable memory gather and scatter operations, `m1xN` vector masks, masked\n+arithmetic/bitwise/shift operations, etc.\n+\n+# Motivation\n+[motivation]: #motivation\n+\n+The `std::arch` module exposes architecture-specific SIMD types like `_m128` - a\n+128-bit wide SIMD vector type. How these bits are interpreted depends on the intrinsic\n+being used. For example, let's sum 8 `f32`s values using the SSE4.1 facilities\n+in the `std::arch` module. This is one way to do it\n+([playground](https://play.rust-lang.org/?gist=165e2886b4883ec98d4e8bb4d6a32e22&version=nightly)):\n+\n+```rust\n+unsafe fn add_reduce(a: __m128, b: __m128) -> f32 {\n+    let c = _mm_hadd_ps(a, b);\n+    let c = _mm_hadd_ps(c, _mm_setzero_ps());\n+    let c = _mm_hadd_ps(c, _mm_setzero_ps());\n+    std::mem::transmute(_mm_extract_ps(c, 0))\n+}\n+\n+fn main() {\n+    unsafe {\n+        let a = _mm_set_ps(1., 2., 3., 4.);\n+        let b = _mm_set_ps(5., 6., 7., 8.);\n+        let r = add_reduce(a, b);\n+        assert_eq!(r, 36.);\n+    }\n+}\n+```\n+\n+Notice that:\n+\n+* one has to put some effort to extrapolate from `add_reduce`'s signature what\n+  types of vectors it actually expects: \"`add_reduce` takes 128-bit wide vectors and\n+  returns an `f32` therefore those 128-bit vectors _probably_ must contain 4 packed\n+  f32s because that's the only combination of `f32`s that fits in 128 bits!\"\n+  \n+* it requires a lot of `unsafe` code: the intrinsics are unsafe (which could be\n+  improved via [RFC2122](https://github.com/rust-lang/rfcs/pull/2212)), the\n+  intrinsic API relies on the user performing transmutes, constructing the\n+  vectors is unsafe because it needs to be done via intrinsic calls, etc.\n+\n+* it requires a lot of architecture specific knowledge: how the intrinsics are\n+  called, how they are used together\n+  \n+* this solution only works on `x86` or `x86_64` with SSE4.1 enabled, that is, it\n+  is not portable.\n+\n+With portable packed vector types, we can do much better\n+([playground](https://play.rust-lang.org/?gist=7fb4e3b6c711b5feb35533b50315a5fb&version=nightly)):\n+\n+```rust\n+fn main() {\n+    let a = f32x4::new(1., 2., 3., 4.);\n+    let b = f32x4::new(5., 6., 7., 8.);\n+    let r = (a + b).sum();\n+    assert_eq!(r, 36.);\n+}\n+```\n+\n+These types add zero-overhead over the architecture-specific types for the\n+operations that they support - if there is an architecture in which this does\n+not hold for some operation: the implementation has a bug.\n+\n+The motivation of this RFC is to provide reasonably high-level, reliable, and\n+portable access to common SIMD vector types and SIMD operations.\n+\n+At a higher level, the actual use cases for these specialty instructions are\n+boundless. SIMD intrinsics are used in graphics, multimedia, linear algebra,\n+scientific computing, games, cryptography, text search, machine learning, low\n+latency, and more. There are many crates in the Rust ecosystem using SIMD\n+intrinsics today, either through `stdsimd`, the `simd` crate, or both. Some\n+examples include:\n+\n+* [`encoding_rs`](https://github.com/hsivonen/encoding_rs) which uses the `simd`\n+  crate to assist with speedy decoding.\n+* [`bytecount`](https://github.com/llogiq/bytecount) which uses the `simd` crate\n+with AVX2 extensions to accelerate counting bytes.\n+* [`regex`](https://github.com/rust-lang/regex) which uses the `stdsimd` crate\n+with SSSE3 extensions to accelerate multiple substrings search and also its\n+parent crate `teddy`.\n+\n+However, providing portable SIMD algorithms for all application domains is not\n+the intent of this RFC.\n+\n+The purpose of this RFC is to provide users with vocabulary types and\n+fundamental operations that they can build upon in their own crates to\n+effectively implement SIMD algorithms in their respective application domains.\n+\n+These types are meant to be extended by users with portable (or nonportable) SIMD\n+operations in their own crates, for example, via extension traits or new types.\n+\n+The operations provided in this RFC are thus either:\n+\n+**fundamental**: that is, they build the foundation required to write\n+higher-level SIMD algorithms. These include, amongst others, instantiating\n+vector types, load/stores from memory, masks and branchless conditional\n+operations, and type casts and conversions.\n+\n+**required**: to be part of the std. These include backend-specific compiler\n+intrinsics that we might never want to stabilize as well as the implementation of\n+std library traits which, due to trait coherence, users cannot extend the vector\n+types with.\n+\n+# Guide-level explanation\n+[guide-level-explanation]: #guide-level-explanation\n+\n+This RFC extends Rust with **portable packed SIMD vector types**, a set of types\n+used to perform **explicit vectorization**:\n+\n+* **SIMD**: stands for Single Instruction, Multiple Data. This RFC uses this\n+  term in the context of hardware instruction set architectures (ISAs) to refer\n+  to:\n+    * SIMD instructions: instructions that (typically) perform operations on\n+      multiple values simultaneously, and\n+    * SIMD registers: the registers that the SIMD instructions take as operands.\n+      These registers (typically) store multiple values that are\n+      operated upon simultaneously by SIMD instructions.\n+\n+* **vector** types: types that abstract over memory stored in SIMD registers,\n+  allowing to transfer memory to/from the registers and performing operations\n+  directly on these registers.\n+\n+* **packed**: means that these vectors have a compile-time fixed size. It is\n+  the opposite of **scalable** or \"Cray vectors\", which are SIMD vector types\n+  with a dynamic size, that is, whose size is only known at run-time.\n+\n+* **explicit vectorization**: vectorization is the process of producing programs\n+  that operate on multiple values simultaneously (typically) using SIMD\n+  instructions and registers. Automatic vectorization is the process by which\n+  the Rust compiler is, in some cases, able to transform scalar Rust code, that\n+  is, code that does not use SIMD vector types, into machine code that does use\n+  SIMD registers and instructions automatically (without user intervention).\n+  Explicit vectorization is the process by which a Rust **user** manually writes\n+  Rust code that states what kind of SIMD registers are to be used and what SIMD\n+  instructions are executed on them.\n+\n+* **portable**: is the opposite of architecture-specific. These types work both\n+  correctly and efficiently on all architectures. They are a zero-overhead\n+  abstraction, that is, for the operations that these types support, one cannot\n+  write better code by hand (otherwise, it is an implementation bug).\n+  \n+* **masks**: are vector types used to **select** vector elements on which\n+  operations are to be performed. This selection is performed by setting or\n+  clearing the bits of the masks for a particular lane.\n+  \n+Packed vector types are denotes as follows: `{i,u,f,m}{lane_width}x{#lanes}`, so\n+that `i64x8` is a 512-bit vector with eight `i64` lanes and `f32x4` a 128-bit\n+vector with four `f32` lanes. Here:\n+\n+* **lane**: is the number of values of a particular type stored in a vector -\n+  the vector operations act on these values simultaneously.\n+  \n+* **lane width**: the bit width of a vector lane, that is, the bit width of\n+  the objects stored in the vector. For example, the type `f32` is 32-bits wide.\n+  \n+That is, the `m8x4` type is a 32-bit wide vector mask with 4 lanes containing an\n+8-bit wide mask each. Vector masks are mainly used to select the lanes on which\n+vector operations are performed. When a lane has all of its bits set to `true`,\n+that lane is \"selected\", and when a lane has all of its bits set to `false`,\n+that lane is \"not selected\". The following bit pattern is thus a valid\n+bit-pattern for the `m8x4` mask:\n+\n+> 00000000_11111111_00000000_11111111\n+\n+and it select two eight-bit wide lanes from a 32-bit wide vector type with four\n+lanes. The following bit-pattern is not, however, a valid value of the same mask\n+type:\n+\n+> 00000000_11111111_00000000_11110111\n+\n+because it does not satisfies the invariant that all bits of a lane must be\n+either set or cleared.\n+\n+Operations on vector types can be either:\n+\n+* **vertical**: that is, lane-wise. For example, `a + b` adds each lane of `a`\n+  with the corresponding lane of `b`, while `a.lt(b)` returns a boolean mask\n+  that indicates whether the less-than (`<`, `lt`) comparison returned `true` or\n+  `false` for each of the vector lanes. Most vertical operations are binary\n+  operations (they take two input vectors). These operations are typically very\n+  fast on most architectures and they are the most widely used in practice.\n+  \n+* **horizontal**: that is, along a single vector - they are unary operations.\n+  For example, `a.sum()` adds the elements of a vector together while\n+  `a.hmax()` returns the largest element in a vector. These operations\n+  (typically) translate to a sequence of multiple SIMD instructions on most\n+  architectures and are therefore slower. In many cases, they are, however,\n+  necessary.\n+  \n+## Example: Average\n+\n+The first example computes the arithmetic average of the elements in a list.\n+Sequentially, we would write using iterators as follows:\n+\n+```rust\n+/// Arithmetic average of the elements in `xs`.\n+fn average_seq(xs: &[f32]) -> f32 {\n+    if xs.len() > 0 {\n+        xs.iter().sum() / xs.len()\n+    } else {\n+        0.\n+    }\n+}\n+```\n+\n+The following implementation uses the 256-bit SIMD facilities provided by this\n+RFC. As the name suggests, it will be \"slow\":\n+\n+```rust\n+/// Computes the arithmetic average of the elements in the list.\n+///\n+/// # Panics\n+///\n+/// If `xs.len()` is not a multiple of `8`.\n+fn average_slow256(xs: &[f32]) -> f32 {\n+    // The 256-bit wide floating-point vector type is f32x8. To\n+    // avoid handling extra elements in this example we just panic.\n+    assert!(xs.len() % 8 == 0, \n+            \"input length `{}` is not a multiple of 8\", \n+            xs.len());\n+    \n+    let mut result = 0._f32;  // This is where we store the result\n+    \n+    // We iterate over the input slice with a step of `8` elements:\n+    for i in (0..xs.len()).step_by(8) {\n+        // First, we load the next `8` elements into an `f32x8`.\n+        // Since we haven't checked whether the input slice\n+        // is aligned to the alignment of `f32x8`, we perform\n+        // an unaligned memory load.\n+        let data = f32x8::load_unaligned(&xs[i..]);\n+\n+        // With the element in the vector, we perform an horizontal reduction\n+        // and add them to the result.\n+        result += data.sum();\n+    }\n+    result\n+}\n+```\n+\n+As mentioned this operation is \"slow\", why is that? The main issue is that, on\n+most architectures, horizontal reductions must perform a sequence of SIMD\n+operations while vertical operations typically require only a single\n+instruction.\n+\n+We can significantly improve the performance of our algorithm by writing it in\n+such a way that the number of horizontal reductions performed is reduced.\n+\n+```rust\n+fn average_fast256(xs: &[f32]) -> f32 {\n+    assert!(xs.len() % 8 == 0, \n+            \"input length `{}` is not a multiple of 8\", \n+            xs.len());\n+    \n+    // Our temporary result is now a f32x8 vector:\n+    let mut result = f32x8::splat(0.);\n+    for i in (0..xs.len()).step_by(8) {\n+        let data = f32x8::load_unaligned(&xs[i..]);\n+        // This adds the data elements to tour temporary result using \n+        // a vertical lane-wise simd operation - this is a single SIMD\n+        // instruction on most architectures.\n+        result += data; \n+    }\n+    // Perform a single horizontal reduction at the end:\n+    result.sum()\n+}\n+```\n+\n+The performance could by further improved by requiring the input data to be\n+aligned to a 16-byte boundary, and/or by handling the elements before the next 16-byte boundary in a special way.\n+\n+## Example: scalar-vector multiply even\n+\n+To showcase the mask and `select` API the following function multiplies the\n+even elements of a vector with a scalar:\n+\n+```rust\n+fn mul_even(a: f32, x: f32x4) -> f32x4 {\n+    // Create a vector mask for the even elements 0 and 2.\n+    // The vector mask API uses `bool`s to set or clear \n+    // all bits of a lane:\n+    let m = m32x4::new(true, false, true, false);\n+\n+    // Perform a full multiplication\n+    let r = f32x4::splat(a) * x;\n+    \n+    // Use the mask to select the even elements from the\n+    // multiplication result and the odd elements from\n+    // the input:\n+    m.select(r, x)\n+}\n+```\n+\n+## Example: 4x4 Matrix multiplication\n+\n+To showcase the `shuffle` API the following function implements 4x4 Matrix\n+multiply using 128-bit wide vectors.\n+\n+```rust\n+fn mul4x4(a: [f32x4; 4], b: [f32x4; 4]) -> [f32x4; 4] {\n+    let r = [f32x4::splat(0.); 4];\n+    \n+    for i in 0..4 {\n+        r[i] = \n+            a[0] * shuffle!(b[i], [0,0,0,0]) + \n+            a[1] * shuffle!(b[i], [1,1,1,1]) +\n+            a[2] * shuffle!(b[i], [2,2,2,2]) +\n+            a[3] * shuffle!(b[i], [3,3,3,3]);\n+    }\n+    r\n+}\n+```\n+  \n+# Reference-level explanation\n+[reference-level-explanation]: #reference-level-explanation\n+  \n+## Vector types\n+\n+The vector types are named according to the following scheme:\n+\n+> {element_type}{lane_width}x{number_of_lanes}\n+\n+where the following element types are introduced by this RFC:\n+\n+* `i`: signed integer\n+* `u`: unsigned integer\n+* `f`: float\n+* `m`: mask\n+\n+So that `u16x8` reads \"a SIMD vector of eight packed 16-bit wide unsigned\n+integers\". The width of a vector can be computed by multiplying the\n+`{lane_width}` times the `{number_of_lanes}`. For `u16x8`, 16 x 8 = 128, so\n+this vector type is 128 bits wide.\n+\n+This RFC proposes adding all vector types with sizes in range [16, 256] bit to\n+the `std::simd` module, that is:\n+\n+* 16-bit wide vectors: `i8x2`, `u8x2`, `m8x2`\n+* 32-bit wide vectors: `i8x4`, `u8x4`, `m8x4`, `i16x2`, `u16x2`,  `m16x2`\n+* 64-bit wide vectors: `i8x8`, `u8x8`, `m8x8`, `i16x4`, `u16x4`, `m16x4`,\n+  `i32x2`, `u32x2`, `f32x2`, `m32x2`\n+* 128-bit wide vectors: `i8x16`, u8x16`, `m8x16`, `i16x8`, `u16x8`, `m16x8`,\n+  `i32x4`, `u32x4`, `f32x4`, `m32x4`, `i64x2`, `u64x2`, `f64x2`, `m64x2`\n+* 256-bit wide vectors: `i8x32`, u8x32`, m8x32`, i16x16`, u16x16`, m16x16`,\n+  i32x8`, u32x8`, f32x8`, m32x8`, i64x4`, u64x4`, f64x4`, `m64x4`\n+\n+Note that this list is not comprehensive. In particular:\n+\n+* half-float `f16xN`: these vectors are supported in many architectures (ARM,\n+  AArch64, PowerPC64, RISC-V, MIPS, ...) but their support is blocked on Rust\n+  half-float support.\n+* AVX-512 vector types, not only 512-bit wide vector types, but also `m1xN`\n+  vector masks. These are blocked on `std::arch` AVX-512 support.\n+* other vector types: x86, AArch64, PowerPC and others include types like\n+  `i64x1`, `u64x1`, `f64x1`, `m64x1`, `i128x1`, `u128x1`, `m128x1`, ... These\n+  can be always added later as the need for these arises, potentially in combination with the stabilization of the `std::arch` intrinsics for those\n+  architectures.\n+\n+## API of portable packed SIMD vector types\n+\n+### Traits overview\n+\n+All vector types implement the following traits:\n+\n+* `Copy`\n+* `Clone`\n+* `Default`: zero-initializes the vector.\n+* `Debug`: formats the vector as `({}, {}, ...)`.\n+* `PartialEq<Self>`: performs a lane-wise comparison between two vectors and\n+  returns `true` if all lanes compare `true`. It is equivalent to\n+  `a.eq(b).all()`.\n+* `PartialOrd<Self>`: compares two vectors lexicographically.\n+* `From/Into` lossless casts between vectors with the same number of lanes.\n+\n+All signed integer, unsigned integer, and floating point vector types implement\n+the following traits:\n+\n+* `{Add,Sub,Mul,Div,Rem}<RHS=Self,Output=Self>`,\n+  `{Add,Sub,Mul,Div,Rem}Assign<RHS=Self>`: vertical (lane-wise) arithmetic\n+  operations.\n+\n+All signed and unsigned integer vectors and vector masks also implement:\n+\n+* `Eq`: equivalent to `PartialEq<Self>`\n+* `Ord`: equivalent to `PartialOrd<Self>`\n+* `Hash`: equivalent to `Hash` for `[element_type; number_of_elements]`.\n+* `fmt::LowerHex`/`fmt::UpperHex`: formats the vector as hexadecimal.\n+* `fmt::Octal`: formats the vector as an octal number.\n+* `fmt::Binary`: formats the vector as binary number.\n+* `Not<Output=Self>`: vertical (lane-wise) negation,\n+* `Bit{And,Or,Xor}<RHS=Self,Output=Self>`, `Bit{And,Or,Xor}Assign<RHS=Self>`:\n+  vertical (lane-wise) bitwise operations.\n+\n+All signed and unsigned integer vectors also implement:\n+\n+* `{Shl,Shr}<RHS=Self,Output=Self>`, `{Shl,Shr}Assign<RHS=Self>`: vertical\n+  (lane-wise) bit-shift operations.\n+\n+Note: While IEEE 754-2008 provides total ordering predicates for floating-point\n+numbers, Rust does not implement `Eq` and `Ord` for the `f32` and `f64`\n+primitive types. This RFC follows suit and does not propose to implement `Eq`\n+and `Ord` for vectors of floating-point types. Any future RFC that might want to\n+extend Rust with a total order for floats should extend the portable\n+floating-point vector types with it as well. See [this internal\n+thread](https://users.rust-lang.org/t/how-to-sort-a-vec-of-floats/2838/3) for\n+more information.\n+\n+### Inherent Methods\n+\n+#### Construction and element access\n+\n+All portable signed integer, unsigned integer, and floating-point vector types\n+implement the following methods:\n+\n+```rust\n+impl {element_type}{lane_width}x{number_of_lanes} {\n+/// Creates a new instance of the vector from `number_of_lanes` \n+/// values.\n+pub const fn new(args...: element_type) -> Self;\n+\n+/// Returns the number of vector lanes.\n+pub const fn lanes() -> usize;\n+\n+/// Constructs a new instance with each element initialized to\n+/// `value`.\n+pub const fn splat(value: element_type) -> Self;\n+\n+/// Extracts the value at `index`.\n+///\n+/// # Panics\n+///\n+/// If `index >= Self::lanes()`.\n+pub fn extract(self, index: usize) -> element_type;\n+\n+/// Extracts the value at `index`.\n+///\n+/// If `index >= Self::lanes()` the behavior is undefined.\n+pub unsafe fn extract_unchecked(self, index: usize) -> element_type;\n+\n+/// Returns a new vector where the value at `index` is replaced by `new_value`.\n+///\n+/// # Panics\n+///\n+/// If `index >= Self::lanes()`.\n+#[must_use = error-message]\n+pub fn replace(self, index: usize, new_value: $elem_ty) -> Self;\n+\n+/// Returns a new vector where the value at `index` is replaced by `new_value`.\n+#[must_use = error-message]\n+pub unsafe fn replace_unchecked(self, index: usize, \n+                                new_value: element_type) -> Self;\n+}\n+```\n+\n+#### Loads and Stores\n+\n+All portable vector types implement the following methods:\n+\n+```rust\n+impl {element_type}{lane_width}x{number_of_lanes} {\n+/// Writes the values of the vector to the `slice`.\n+///\n+/// # Panics\n+///\n+/// If `slice.len() < Self::lanes()` or `&slice[0]` is not\n+/// aligned to an `align_of::<Self>()` boundary.\n+pub fn store_aligned(self, slice: &mut [element_type]);\n+\n+/// Writes the values of the vector to the `slice`.\n+///\n+/// # Panics\n+///\n+/// If `slice.len() < Self::lanes()`.\n+pub fn store_unaligned(self, slice: &mut [element_type]);\n+\n+/// Writes the values of the vector to the `slice`.\n+///\n+/// # Precondition\n+///\n+/// If `slice.len() < Self::lanes()` or `&slice[0]` is not\n+/// aligned to an `align_of::<Self>()` boundary, the behavior is\n+/// undefined.\n+pub unsafe fn store_aligned_unchecked(self, slice: &mut [element_type]);\n+\n+/// Writes the values of the vector to the `slice`.\n+///\n+/// # Precondition\n+///\n+/// If `slice.len() < Self::lanes()` the behavior is undefined.\n+pub unsafe fn store_unaligned_unchecked(self, slice: &mut [element_type]);\n+\n+/// Instantiates a new vector with the values of the `slice`.\n+///\n+/// # Panics\n+///\n+/// If `slice.len() < Self::lanes()` or `&slice[0]` is not aligned\n+/// to an `align_of::<Self>()` boundary.\n+pub fn load_aligned(slice: &[element_type]) -> Self;\n+\n+/// Instantiates a new vector with the values of the `slice`.\n+///\n+/// # Panics\n+///\n+/// If `slice.len() < Self::lanes()`.\n+pub fn load_unaligned(slice: &[element_type]) -> Self;\n+\n+/// Instantiates a new vector with the values of the `slice`.\n+///\n+/// # Precondition\n+///\n+/// If `slice.len() < Self::lanes()` or `&slice[0]` is not aligned\n+/// to an `align_of::<Self>()` boundary, the behavior is undefined.\n+pub unsafe fn load_aligned_unchecked(slice: &[element_type]) -> Self;\n+\n+/// Instantiates a new vector with the values of the `slice`.\n+///\n+/// # Precondition\n+///\n+/// If `slice.len() < Self::lanes()` the behavior is undefined.\n+pub unsafe fn load_unaligned_unchecked(slice: &[element_type]) -> Self;\n+}\n+```\n+\n+#### Vertical arithmetic operations\n+\n+Vertical (lane-wise) arithmetic operations are provided by the following trait\n+implementations:\n+\n+* All signed integer, unsigned integer, and floating point vector types implement:\n+\n+    * `{Add,Sub,Mul,Div,Rem}<RHS=Self,Output=Self>`\n+    * `{Add,Sub,Mul,Div,Rem}Assign<RHS=Self>`\n+\n+* All signed and unsigned integer vectors also implement:\n+\n+    * `{Shl,Shr}<RHS=Self,Output=Self>`, `{Shl,Shr}Assign<RHS=Self>`: vertical\n+      (lane-wise) bit-shift operations.\n+\n+##### Integer vector semantics\n+\n+The behavior of these operations for integer vectors is the same as that of the\n+scalar integer types. That is: `panic!` on both overflow and division by zero if\n+`-C overflow-checks=on`.\n+  \n+##### Floating-point semantics\n+\n+The behavior of these operations for floating-point numbers is the same as that\n+of the scalar floating point types, that is, `+-INFINITY` on overflow, `NaN` on\n+division by zero, etc.\n+\n+#### Wrapping arithmetic operations\n+\n+All signed and unsigned integer vector types implement the whole set of `pub fn\n+wrapping_{add,sub,mul,div,rem}(self, Self) -> Self` methods which, on overflow,\n+produce the correct mathematical result modulo `2^n`.\n+\n+The `div` and `rem` method `panic!` on division by zero in debug mode.\n+\n+#### Unsafe wrapping arithmetic operations\n+\n+All signed and unsigned integer vectors implement\n+`pub unsafe fn wrapping_{div,rem}_unchecked(self, Self) -> Self`\n+methods which, on overflow, produce the correct mathematical result modulo `2^n`.\n+\n+If any of the vector elements is divided by zero the behavior is undefined.\n+\n+#### Binary `min`/`max` vertical operations\n+\n+All portable signed integer, unsigned integer, and floating-point vectors\n+implement the following methods:\n+\n+```rust\n+impl {element_type}{lane_width}x{number_of_lanes} {\n+/// Lane-wise `min`.\n+///\n+/// Returns a vector whose lanes contain the smallest \n+/// element of the corresponding lane of `self` and `other`.\n+pub fn min(self, other: Self) -> Self;\n+\n+/// Lane-wise `max`.\n+///\n+/// Returns a vector whose lanes contain the largest \n+/// element of the corresponding lane of `self` and `other`.\n+pub fn max(self, other: Self) -> Self;\n+}\n+```\n+\n+##### Floating-point semantics\n+\n+The floating-point semantics follow the semantics of `min` and `max` for the\n+scalar `f32` and `f64` types.\n+\n+#### Arithmetic reductions \n+\n+##### Integers\n+\n+All portable signed and unsigned integer vector types implement the following\n+methods:\n+\n+```rust\n+impl {element_type}{lane_width}x{number_of_lanes} {\n+/// Horizontal wrapping sum of the vector elements.\n+///\n+/// The intrinsic performs a tree-reduction of the vector elements.\n+/// That is, for a 4 element vector:\n+///\n+/// > (x0.wrapping_add(x1)).wrapping_add(x2.wrapping_add(x3))\n+///\n+/// If an operation overflows it returns the mathematical result\n+/// modulo `2^n` where `n` is the number of times it overflows.\n+pub fn wrapping_sum(self) -> element_type;\n+\n+/// Horizontal wrapping product of the vector elements.\n+///\n+/// The intrinsic performs a tree-reduction of the vector elements.\n+/// That is, for a 4 element vector:\n+///\n+/// > (x0.wrapping_mul(x1)).wrapping_mul(x2.wrapping_mul(x3))\n+///\n+/// If an operation overflows it returns the mathematical result\n+/// modulo `2^n` where `n` is the number of times it overflows.\n+pub fn wrapping_product(self) -> element_type;\n+}\n+```\n+\n+##### Floating-point\n+\n+All portable floating-point vector types implement the following methods:\n+\n+```rust\n+impl {element_type}{lane_width}x{number_of_lanes} {\n+/// Horizontal sum of the vector elements.\n+///\n+/// The intrinsic performs a tree-reduction of the vector elements.\n+/// That is, for a 8 element vector:\n+///\n+/// > ((x0 + x1) + (x2 + x3)) + ((x4 + x5) + (x6 + x7))\n+///\n+/// If one of the vector element is `NaN` the reduction returns\n+/// `NaN`. The resulting `NaN` is not required to be equal to any\n+/// of the `NaN`s in the vector.\n+pub fn sum(self) -> element_type;\n+\n+/// Horizontal product of the vector elements.\n+///\n+/// The intrinsic performs a tree-reduction of the vector elements.\n+/// That is, for an 8 element vector:\n+///\n+/// > ((x0 * x1) * (x2 * x3)) * ((x4 * x5) * (x6 * x7))\n+///\n+/// If one of the vector element is `NaN` the reduction returns\n+/// `NaN`. The resulting `NaN` is not required to be equal to any\n+/// of the `NaN`s in the vector.\n+pub fn product(self) -> element_type;\n+}\n+```\n+\n+#### Bitwise reductions \n+\n+All signed and unsigned integer vectors implement the following methods:\n+\n+```rust\n+impl {element_type}{lane_width}x{number_of_lanes} {\n+/// Horizontal bitwise `and` of the vector elements.\n+pub fn and(self) -> element_type;\n+\n+/// Horizontal bitwise `or` of the vector elements.\n+pub fn or(self) -> element_type;\n+\n+/// Horizontal bitwise `xor` of the vector elements.\n+pub fn xor(self) -> element_type;\n+}\n+```\n+\n+#### Min/Max reductions\n+\n+All portable signed integer, unsigned integer, and floating-point vector types\n+implement the following methods:\n+\n+```rust\n+impl {element_type}{lane_width}x{number_of_lanes} {\n+/// Value of the largest element in the vector.\n+///\n+/// # Floating-point\n+///\n+/// If the result contains `NaN`s the result is a \n+/// `NaN` that is not necessarily equal to any of \n+/// the `NaN`s in the vector.\n+pub fn hmax(self) -> element_type;\n+\n+/// Value of the smallest element in the vector.\n+///\n+/// # Floating-point\n+///\n+/// If the result contains `NaN`s the result is a \n+/// `NaN` that is not necessarily equal to any of \n+/// the `NaN`s in the vector.\n+pub fn hmin(self) -> element_type;\n+}\n+```\n+\n+Note: In this RFC, horizontal reductions are named according to the operation\n+they perform. When this name clashes with that of a vertical operation, the\n+horizontal reduction's name get's a `h`-prefix. In this case, the horizontal\n+`max` and `min` reductions clash with the vertical `a.max(b)` operation, and\n+therefore get a `h`-prefix. \n+\n+An alternative would be to prefix all horizontal operations with, for example,\n+the `h_`-prefix.\n+\n+#### Mask construction and element access\n+\n+```rust\n+impl m{lane_width}x{number_of_lanes} {\n+/// Creates a new vector mask from `number_of_lanes` boolean\n+/// values.\n+///\n+/// The values `true` and `false` respectively set and clear \n+/// the mask for a particular lane.\n+pub const fn new(args...: bool...) -> Self;\n+\n+/// Returns the number of vector lanes.\n+pub const fn lanes() -> usize;\n+\n+/// Constructs a new vector mask with all lane-wise \n+/// masks either set, if `value` equals `true`, or cleared, if \n+/// `value` equals `false`.\n+pub const fn splat(value: bool) -> Self;\n+\n+/// Returns `true` if the mask for the lane `index` is \n+/// set and `false` otherwise.\n+///\n+/// # Panics\n+///\n+/// If `index >= Self::lanes()`.\n+pub fn extract(self, index: usize) -> bool;\n+\n+/// Returns `true` if the mask for the lane `index` is \n+/// set and `false` otherwise.\n+///\n+/// If `index >= Self::lanes()` the behavior is undefined.\n+pub unsafe fn extract_unchecked(self, index: usize) -> bool;\n+\n+/// Returns a new vector mask where mask of the lane `index` is\n+/// set if `new_value` is `true` and cleared otherwise.\n+///\n+/// # Panics\n+///\n+/// If `index >= Self::lanes()`.\n+#[must_use = error-message]\n+pub fn replace(self, index: usize, new_value: bool) -> Self;\n+\n+/// Returns a new vector mask where mask of the lane `index` is\n+/// set if `new_value` is `true` and cleared otherwise.\n+///\n+/// If `index >= Self::lanes()` the behavior is undefined.\n+#[must_use = error-message]\n+pub unsafe fn replace_unchecked(self, index: usize, new_value: bool) -> Self;\n+}\n+```\n+\n+#### Mask reductions\n+\n+All vector masks implement the following methods:\n+\n+```rust\n+impl m{lane_width}x{number_of_lanes} {\n+/// Are \"all\" lanes `true`?\n+pub fn all(self) -> bool;\n+\n+/// Is \"any\" lane `true`?\n+pub fn any(self) -> bool;\n+\n+/// Are \"all\" lanes `false`?\n+pub fn none(self) -> bool;\n+}\n+```\n+\n+#### Mask vertical selection\n+\n+All vector masks implement the following method:\n+\n+```rust\n+impl m{lane_width}x{number_of_lanes} {\n+/// Lane-wise selection. \n+///\n+/// The lanes of the result for which the mask is `true` contain\n+/// the values of `a` while the remaining lanes contain the values of `b`.\n+pub fn select<T>(self, a: T, b: T) -> T\n+    where T::lanes() == number_of_lanes; // implementation-defined \n+}\n+```\n+\n+Note: how `where` clause is enforced is an implementation detail. `stdsimd`\n+implements this using a sealed trait:\n+\n+```rust\n+pub fn select<T>(self, a: T, b: T) -> T\n+    where T: SelectMask<Self>\n+}\n+```\n+\n+#### Vertical comparisions\n+\n+All vector types implement the following vertical (lane-wise) comparison methods\n+that returns a mask expressing the result.\n+\n+```rust\n+impl {element_type}{lane_width}x{number_of_lanes} {\n+/// Lane-wise equality comparison.\n+pub fn eq(self, other: $id) -> m{lane_width}x{number_of_lanes};\n+\n+/// Lane-wise inequality comparison.\n+pub fn ne(self, other: $id) -> m{lane_width}x{number_of_lanes};\n+\n+/// Lane-wise less-than comparison.\n+pub fn lt(self, other: $id) -> m{lane_width}x{number_of_lanes};\n+\n+/// Lane-wise less-than-or-equals comparison.\n+pub fn le(self, other: $id) -> m{lane_width}x{number_of_lanes};\n+\n+/// Lane-wise greater-than comparison.\n+pub fn gt(self, other: $id) -> m{lane_width}x{number_of_lanes};\n+\n+/// Lane-wise greater-than-or-equals comparison.\n+pub fn ge(self, other: $id) -> m{lane_width}x{number_of_lanes};\n+}\n+```\n+\n+For all vector types proposed in this RFC, the `{lane_width}` of the mask\n+matches that of the vector type. However, this will not be the case for the\n+AVX-512 vector types.\n+\n+##### Semantics for floating-point numbers\n+\n+The semantics of the lane-wise comparisons for floating point numbers are the\n+same as in the scalar case. \n+  \n+### Portable vector shuffles\n+\n+```\n+/// Shuffles vector elements.\n+std::simd::shuffle!(...);\n+```\n+\n+The `shuffle!` macro returns a new vector that contains a shuffle of the elements in\n+one or two input vectors. There are two versions:\n+\n+ * `shuffle!(vec, indices)`: one-vector version\n+ * `shuffle!(vec0, vec1, indices)`: two-vector version\n+\n+with the following preconditions:\n+\n+ * `vec`, `vec0`, and `vec1` must be portable packed SIMD vector types.\n+ * `vec0` and `vec1` must have the same type. \n+ * `indices` must be a `const` array of type `[usize; N]` where `N` is any\n+   power-of-two in range `(0, 2 * {vec,vec0,vec1}::lanes()]`.\n+ * the values of `indices` must be in range `[0, N)` for the one-vector version,\n+   and in range `[0, 2N)` for the two-vector version.","path":"text/0000-ppv.md","position":871,"original_position":871,"commit_id":"1762b5806ed96c44cdf4bf673f9f70cbd8adc9e7","original_commit_id":"1762b5806ed96c44cdf4bf673f9f70cbd8adc9e7","user":{"login":"gnzlbg","id":904614,"avatar_url":"https://avatars0.githubusercontent.com/u/904614?v=4","gravatar_id":"","url":"https://api.github.com/users/gnzlbg","html_url":"https://github.com/gnzlbg","followers_url":"https://api.github.com/users/gnzlbg/followers","following_url":"https://api.github.com/users/gnzlbg/following{/other_user}","gists_url":"https://api.github.com/users/gnzlbg/gists{/gist_id}","starred_url":"https://api.github.com/users/gnzlbg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gnzlbg/subscriptions","organizations_url":"https://api.github.com/users/gnzlbg/orgs","repos_url":"https://api.github.com/users/gnzlbg/repos","events_url":"https://api.github.com/users/gnzlbg/events{/privacy}","received_events_url":"https://api.github.com/users/gnzlbg/received_events","type":"User","site_admin":false},"body":"The line below this one states: \r\n\r\n> On precondition violation a type error is produced.\r\n\r\nThe current implementation produces a monomorphization time error, but that can be fixed.","created_at":"2018-03-23T17:38:32Z","updated_at":"2018-03-23T17:38:32Z","html_url":"https://github.com/rust-lang/rfcs/pull/2366#discussion_r176811779","pull_request_url":"https://api.github.com/repos/rust-lang/rfcs/pulls/2366","author_association":"CONTRIBUTOR","_links":{"self":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/comments/176811779"},"html":{"href":"https://github.com/rust-lang/rfcs/pull/2366#discussion_r176811779"},"pull_request":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/2366"}},"in_reply_to_id":176788398},"pull_request":{"url":"https://api.github.com/repos/rust-lang/rfcs/pulls/2366","id":176798108,"html_url":"https://github.com/rust-lang/rfcs/pull/2366","diff_url":"https://github.com/rust-lang/rfcs/pull/2366.diff","patch_url":"https://github.com/rust-lang/rfcs/pull/2366.patch","issue_url":"https://api.github.com/repos/rust-lang/rfcs/issues/2366","number":2366,"state":"open","locked":false,"title":"Portable packed SIMD vector types","user":{"login":"gnzlbg","id":904614,"avatar_url":"https://avatars0.githubusercontent.com/u/904614?v=4","gravatar_id":"","url":"https://api.github.com/users/gnzlbg","html_url":"https://github.com/gnzlbg","followers_url":"https://api.github.com/users/gnzlbg/followers","following_url":"https://api.github.com/users/gnzlbg/following{/other_user}","gists_url":"https://api.github.com/users/gnzlbg/gists{/gist_id}","starred_url":"https://api.github.com/users/gnzlbg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gnzlbg/subscriptions","organizations_url":"https://api.github.com/users/gnzlbg/orgs","repos_url":"https://api.github.com/users/gnzlbg/repos","events_url":"https://api.github.com/users/gnzlbg/events{/privacy}","received_events_url":"https://api.github.com/users/gnzlbg/received_events","type":"User","site_admin":false},"body":"This RFC extends Rust with portable packed SIMD vector types.\r\n\r\n[Rendered](https://github.com/gnzlbg/rfcs/blob/ppv/text/0000-ppv.md).\r\n\r\n--- \r\n\r\n#### Acknowledgments \r\n\r\nIt would not have happened without the invaluable contributions of @alexcrichton and @BurntSushi , the excellent feedback given by the many reviewers of the initial drafts: @sunfish, @nagisa, @eddyb, and @hsivonen, amongst others, all the people using `stdsimd`, filling issues, and submitting PRs, and @huonw's work on the `rust-lang-nursery/simd` crate. ","created_at":"2018-03-22T14:34:23Z","updated_at":"2018-03-23T17:38:32Z","closed_at":null,"merged_at":null,"merge_commit_sha":"115cfd8cbf5ad5cc41a5df92f6fd888d9aa10ca2","assignee":null,"assignees":[],"requested_reviewers":[],"requested_teams":[],"labels":[{"id":211667901,"url":"https://api.github.com/repos/rust-lang/rfcs/labels/T-libs","name":"T-libs","color":"bfd4f2","default":false}],"milestone":null,"commits_url":"https://api.github.com/repos/rust-lang/rfcs/pulls/2366/commits","review_comments_url":"https://api.github.com/repos/rust-lang/rfcs/pulls/2366/comments","review_comment_url":"https://api.github.com/repos/rust-lang/rfcs/pulls/comments{/number}","comments_url":"https://api.github.com/repos/rust-lang/rfcs/issues/2366/comments","statuses_url":"https://api.github.com/repos/rust-lang/rfcs/statuses/1762b5806ed96c44cdf4bf673f9f70cbd8adc9e7","head":{"label":"gnzlbg:ppv","ref":"ppv","sha":"1762b5806ed96c44cdf4bf673f9f70cbd8adc9e7","user":{"login":"gnzlbg","id":904614,"avatar_url":"https://avatars0.githubusercontent.com/u/904614?v=4","gravatar_id":"","url":"https://api.github.com/users/gnzlbg","html_url":"https://github.com/gnzlbg","followers_url":"https://api.github.com/users/gnzlbg/followers","following_url":"https://api.github.com/users/gnzlbg/following{/other_user}","gists_url":"https://api.github.com/users/gnzlbg/gists{/gist_id}","starred_url":"https://api.github.com/users/gnzlbg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gnzlbg/subscriptions","organizations_url":"https://api.github.com/users/gnzlbg/orgs","repos_url":"https://api.github.com/users/gnzlbg/repos","events_url":"https://api.github.com/users/gnzlbg/events{/privacy}","received_events_url":"https://api.github.com/users/gnzlbg/received_events","type":"User","site_admin":false},"repo":{"id":95458297,"name":"rfcs","full_name":"gnzlbg/rfcs","owner":{"login":"gnzlbg","id":904614,"avatar_url":"https://avatars0.githubusercontent.com/u/904614?v=4","gravatar_id":"","url":"https://api.github.com/users/gnzlbg","html_url":"https://github.com/gnzlbg","followers_url":"https://api.github.com/users/gnzlbg/followers","following_url":"https://api.github.com/users/gnzlbg/following{/other_user}","gists_url":"https://api.github.com/users/gnzlbg/gists{/gist_id}","starred_url":"https://api.github.com/users/gnzlbg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gnzlbg/subscriptions","organizations_url":"https://api.github.com/users/gnzlbg/orgs","repos_url":"https://api.github.com/users/gnzlbg/repos","events_url":"https://api.github.com/users/gnzlbg/events{/privacy}","received_events_url":"https://api.github.com/users/gnzlbg/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/gnzlbg/rfcs","description":"RFCs for changes to Rust","fork":true,"url":"https://api.github.com/repos/gnzlbg/rfcs","forks_url":"https://api.github.com/repos/gnzlbg/rfcs/forks","keys_url":"https://api.github.com/repos/gnzlbg/rfcs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/gnzlbg/rfcs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/gnzlbg/rfcs/teams","hooks_url":"https://api.github.com/repos/gnzlbg/rfcs/hooks","issue_events_url":"https://api.github.com/repos/gnzlbg/rfcs/issues/events{/number}","events_url":"https://api.github.com/repos/gnzlbg/rfcs/events","assignees_url":"https://api.github.com/repos/gnzlbg/rfcs/assignees{/user}","branches_url":"https://api.github.com/repos/gnzlbg/rfcs/branches{/branch}","tags_url":"https://api.github.com/repos/gnzlbg/rfcs/tags","blobs_url":"https://api.github.com/repos/gnzlbg/rfcs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/gnzlbg/rfcs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/gnzlbg/rfcs/git/refs{/sha}","trees_url":"https://api.github.com/repos/gnzlbg/rfcs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/gnzlbg/rfcs/statuses/{sha}","languages_url":"https://api.github.com/repos/gnzlbg/rfcs/languages","stargazers_url":"https://api.github.com/repos/gnzlbg/rfcs/stargazers","contributors_url":"https://api.github.com/repos/gnzlbg/rfcs/contributors","subscribers_url":"https://api.github.com/repos/gnzlbg/rfcs/subscribers","subscription_url":"https://api.github.com/repos/gnzlbg/rfcs/subscription","commits_url":"https://api.github.com/repos/gnzlbg/rfcs/commits{/sha}","git_commits_url":"https://api.github.com/repos/gnzlbg/rfcs/git/commits{/sha}","comments_url":"https://api.github.com/repos/gnzlbg/rfcs/comments{/number}","issue_comment_url":"https://api.github.com/repos/gnzlbg/rfcs/issues/comments{/number}","contents_url":"https://api.github.com/repos/gnzlbg/rfcs/contents/{+path}","compare_url":"https://api.github.com/repos/gnzlbg/rfcs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/gnzlbg/rfcs/merges","archive_url":"https://api.github.com/repos/gnzlbg/rfcs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/gnzlbg/rfcs/downloads","issues_url":"https://api.github.com/repos/gnzlbg/rfcs/issues{/number}","pulls_url":"https://api.github.com/repos/gnzlbg/rfcs/pulls{/number}","milestones_url":"https://api.github.com/repos/gnzlbg/rfcs/milestones{/number}","notifications_url":"https://api.github.com/repos/gnzlbg/rfcs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/gnzlbg/rfcs/labels{/name}","releases_url":"https://api.github.com/repos/gnzlbg/rfcs/releases{/id}","deployments_url":"https://api.github.com/repos/gnzlbg/rfcs/deployments","created_at":"2017-06-26T14:57:04Z","updated_at":"2017-06-25T15:26:08Z","pushed_at":"2018-03-23T15:12:23Z","git_url":"git://github.com/gnzlbg/rfcs.git","ssh_url":"git@github.com:gnzlbg/rfcs.git","clone_url":"https://github.com/gnzlbg/rfcs.git","svn_url":"https://github.com/gnzlbg/rfcs","homepage":"","size":5396,"stargazers_count":0,"watchers_count":0,"language":null,"has_issues":false,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"archived":false,"open_issues_count":0,"license":null,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"rust-lang:master","ref":"master","sha":"751ce5dc25d4f11b1db80922a8925604370239ca","user":{"login":"rust-lang","id":5430905,"avatar_url":"https://avatars1.githubusercontent.com/u/5430905?v=4","gravatar_id":"","url":"https://api.github.com/users/rust-lang","html_url":"https://github.com/rust-lang","followers_url":"https://api.github.com/users/rust-lang/followers","following_url":"https://api.github.com/users/rust-lang/following{/other_user}","gists_url":"https://api.github.com/users/rust-lang/gists{/gist_id}","starred_url":"https://api.github.com/users/rust-lang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rust-lang/subscriptions","organizations_url":"https://api.github.com/users/rust-lang/orgs","repos_url":"https://api.github.com/users/rust-lang/repos","events_url":"https://api.github.com/users/rust-lang/events{/privacy}","received_events_url":"https://api.github.com/users/rust-lang/received_events","type":"Organization","site_admin":false},"repo":{"id":17527989,"name":"rfcs","full_name":"rust-lang/rfcs","owner":{"login":"rust-lang","id":5430905,"avatar_url":"https://avatars1.githubusercontent.com/u/5430905?v=4","gravatar_id":"","url":"https://api.github.com/users/rust-lang","html_url":"https://github.com/rust-lang","followers_url":"https://api.github.com/users/rust-lang/followers","following_url":"https://api.github.com/users/rust-lang/following{/other_user}","gists_url":"https://api.github.com/users/rust-lang/gists{/gist_id}","starred_url":"https://api.github.com/users/rust-lang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/rust-lang/subscriptions","organizations_url":"https://api.github.com/users/rust-lang/orgs","repos_url":"https://api.github.com/users/rust-lang/repos","events_url":"https://api.github.com/users/rust-lang/events{/privacy}","received_events_url":"https://api.github.com/users/rust-lang/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/rust-lang/rfcs","description":"RFCs for changes to Rust","fork":false,"url":"https://api.github.com/repos/rust-lang/rfcs","forks_url":"https://api.github.com/repos/rust-lang/rfcs/forks","keys_url":"https://api.github.com/repos/rust-lang/rfcs/keys{/key_id}","collaborators_url":"https://api.github.com/repos/rust-lang/rfcs/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/rust-lang/rfcs/teams","hooks_url":"https://api.github.com/repos/rust-lang/rfcs/hooks","issue_events_url":"https://api.github.com/repos/rust-lang/rfcs/issues/events{/number}","events_url":"https://api.github.com/repos/rust-lang/rfcs/events","assignees_url":"https://api.github.com/repos/rust-lang/rfcs/assignees{/user}","branches_url":"https://api.github.com/repos/rust-lang/rfcs/branches{/branch}","tags_url":"https://api.github.com/repos/rust-lang/rfcs/tags","blobs_url":"https://api.github.com/repos/rust-lang/rfcs/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/rust-lang/rfcs/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/rust-lang/rfcs/git/refs{/sha}","trees_url":"https://api.github.com/repos/rust-lang/rfcs/git/trees{/sha}","statuses_url":"https://api.github.com/repos/rust-lang/rfcs/statuses/{sha}","languages_url":"https://api.github.com/repos/rust-lang/rfcs/languages","stargazers_url":"https://api.github.com/repos/rust-lang/rfcs/stargazers","contributors_url":"https://api.github.com/repos/rust-lang/rfcs/contributors","subscribers_url":"https://api.github.com/repos/rust-lang/rfcs/subscribers","subscription_url":"https://api.github.com/repos/rust-lang/rfcs/subscription","commits_url":"https://api.github.com/repos/rust-lang/rfcs/commits{/sha}","git_commits_url":"https://api.github.com/repos/rust-lang/rfcs/git/commits{/sha}","comments_url":"https://api.github.com/repos/rust-lang/rfcs/comments{/number}","issue_comment_url":"https://api.github.com/repos/rust-lang/rfcs/issues/comments{/number}","contents_url":"https://api.github.com/repos/rust-lang/rfcs/contents/{+path}","compare_url":"https://api.github.com/repos/rust-lang/rfcs/compare/{base}...{head}","merges_url":"https://api.github.com/repos/rust-lang/rfcs/merges","archive_url":"https://api.github.com/repos/rust-lang/rfcs/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/rust-lang/rfcs/downloads","issues_url":"https://api.github.com/repos/rust-lang/rfcs/issues{/number}","pulls_url":"https://api.github.com/repos/rust-lang/rfcs/pulls{/number}","milestones_url":"https://api.github.com/repos/rust-lang/rfcs/milestones{/number}","notifications_url":"https://api.github.com/repos/rust-lang/rfcs/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/rust-lang/rfcs/labels{/name}","releases_url":"https://api.github.com/repos/rust-lang/rfcs/releases{/id}","deployments_url":"https://api.github.com/repos/rust-lang/rfcs/deployments","created_at":"2014-03-07T21:29:00Z","updated_at":"2018-03-23T13:23:51Z","pushed_at":"2018-03-23T15:12:25Z","git_url":"git://github.com/rust-lang/rfcs.git","ssh_url":"git@github.com:rust-lang/rfcs.git","clone_url":"https://github.com/rust-lang/rfcs.git","svn_url":"https://github.com/rust-lang/rfcs","homepage":"http://rust-lang.github.io/rfcs/","size":51580,"stargazers_count":1391,"watchers_count":1391,"language":"Shell","has_issues":true,"has_projects":true,"has_downloads":true,"has_wiki":false,"has_pages":true,"forks_count":583,"mirror_url":null,"archived":false,"open_issues_count":710,"license":{"key":"other","name":"Other","spdx_id":null,"url":null},"forks":583,"open_issues":710,"watchers":1391,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/2366"},"html":{"href":"https://github.com/rust-lang/rfcs/pull/2366"},"issue":{"href":"https://api.github.com/repos/rust-lang/rfcs/issues/2366"},"comments":{"href":"https://api.github.com/repos/rust-lang/rfcs/issues/2366/comments"},"review_comments":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/2366/comments"},"review_comment":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/rust-lang/rfcs/pulls/2366/commits"},"statuses":{"href":"https://api.github.com/repos/rust-lang/rfcs/statuses/1762b5806ed96c44cdf4bf673f9f70cbd8adc9e7"}},"author_association":"CONTRIBUTOR"}}
{"actor":{"display_login":"gnzlbg"}}