about summary refs log tree commit diff
path: root/src/tools/rust-analyzer/lib
diff options
context:
space:
mode:
authorLaurențiu Nicola <lnicola@dend.ro>2024-01-21 16:53:06 +0200
committerLaurențiu Nicola <lnicola@dend.ro>2024-01-21 16:53:06 +0200
commit595b4c3c32cb3b2ba10ab4b7a656daddc64e0858 (patch)
treee3b92951139521dd315a986fa99cfde7c03e10f6 /src/tools/rust-analyzer/lib
parentfa404339c9821b9c61661d63326d95e354b9753f (diff)
parenta9116523604c998e7781f60d3b5a6f586e0414a9 (diff)
downloadrust-595b4c3c32cb3b2ba10ab4b7a656daddc64e0858.tar.gz
rust-595b4c3c32cb3b2ba10ab4b7a656daddc64e0858.zip
Merge commit 'a9116523604c998e7781f60d3b5a6f586e0414a9' into sync-from-ra
Diffstat (limited to 'src/tools/rust-analyzer/lib')
-rw-r--r--src/tools/rust-analyzer/lib/la-arena/src/map.rs2
-rw-r--r--src/tools/rust-analyzer/lib/line-index/src/lib.rs113
-rw-r--r--src/tools/rust-analyzer/lib/lsp-server/src/lib.rs18
3 files changed, 124 insertions, 9 deletions
diff --git a/src/tools/rust-analyzer/lib/la-arena/src/map.rs b/src/tools/rust-analyzer/lib/la-arena/src/map.rs
index 750f345b539..c6a43d8f9a6 100644
--- a/src/tools/rust-analyzer/lib/la-arena/src/map.rs
+++ b/src/tools/rust-analyzer/lib/la-arena/src/map.rs
@@ -252,6 +252,8 @@ where
 {
     /// Ensures a value is in the entry by inserting the default value if empty, and returns a mutable reference
     /// to the value in the entry.
+    // BUG this clippy lint is a false positive
+    #[allow(clippy::unwrap_or_default)]
     pub fn or_default(self) -> &'a mut V {
         self.or_insert_with(Default::default)
     }
diff --git a/src/tools/rust-analyzer/lib/line-index/src/lib.rs b/src/tools/rust-analyzer/lib/line-index/src/lib.rs
index 58f266d67f6..1ab62e99235 100644
--- a/src/tools/rust-analyzer/lib/line-index/src/lib.rs
+++ b/src/tools/rust-analyzer/lib/line-index/src/lib.rs
@@ -227,6 +227,22 @@ fn analyze_source_file_dispatch(
     }
 }
 
+#[cfg(target_arch = "aarch64")]
+fn analyze_source_file_dispatch(
+    src: &str,
+    lines: &mut Vec<TextSize>,
+    multi_byte_chars: &mut IntMap<u32, Vec<WideChar>>,
+) {
+    if std::arch::is_aarch64_feature_detected!("neon") {
+        // SAFETY: NEON support was checked
+        unsafe {
+            analyze_source_file_neon(src, lines, multi_byte_chars);
+        }
+    } else {
+        analyze_source_file_generic(src, src.len(), TextSize::from(0), lines, multi_byte_chars);
+    }
+}
+
 /// Checks 16 byte chunks of text at a time. If the chunk contains
 /// something other than printable ASCII characters and newlines, the
 /// function falls back to the generic implementation. Otherwise it uses
@@ -322,7 +338,102 @@ unsafe fn analyze_source_file_sse2(
     }
 }
 
-#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
+#[target_feature(enable = "neon")]
+#[cfg(target_arch = "aarch64")]
+#[inline]
+// See https://community.arm.com/arm-community-blogs/b/infrastructure-solutions-blog/posts/porting-x86-vector-bitmask-optimizations-to-arm-neon
+//
+// The mask is a 64-bit integer, where each 4-bit corresponds to a u8 in the
+// input vector. The least significant 4 bits correspond to the first byte in
+// the vector.
+unsafe fn move_mask(v: std::arch::aarch64::uint8x16_t) -> u64 {
+    use std::arch::aarch64::*;
+
+    let nibble_mask = vshrn_n_u16(vreinterpretq_u16_u8(v), 4);
+    vget_lane_u64(vreinterpret_u64_u8(nibble_mask), 0)
+}
+
+#[target_feature(enable = "neon")]
+#[cfg(target_arch = "aarch64")]
+unsafe fn analyze_source_file_neon(
+    src: &str,
+    lines: &mut Vec<TextSize>,
+    multi_byte_chars: &mut IntMap<u32, Vec<WideChar>>,
+) {
+    use std::arch::aarch64::*;
+
+    const CHUNK_SIZE: usize = 16;
+
+    let src_bytes = src.as_bytes();
+
+    let chunk_count = src.len() / CHUNK_SIZE;
+
+    let newline = vdupq_n_s8(b'\n' as i8);
+
+    // This variable keeps track of where we should start decoding a
+    // chunk. If a multi-byte character spans across chunk boundaries,
+    // we need to skip that part in the next chunk because we already
+    // handled it.
+    let mut intra_chunk_offset = 0;
+
+    for chunk_index in 0..chunk_count {
+        let ptr = src_bytes.as_ptr() as *const i8;
+        let chunk = vld1q_s8(ptr.add(chunk_index * CHUNK_SIZE));
+
+        // For character in the chunk, see if its byte value is < 0, which
+        // indicates that it's part of a UTF-8 char.
+        let multibyte_test = vcltzq_s8(chunk);
+        // Create a bit mask from the comparison results.
+        let multibyte_mask = move_mask(multibyte_test);
+
+        // If the bit mask is all zero, we only have ASCII chars here:
+        if multibyte_mask == 0 {
+            assert!(intra_chunk_offset == 0);
+
+            // Check for newlines in the chunk
+            let newlines_test = vceqq_s8(chunk, newline);
+            let mut newlines_mask = move_mask(newlines_test);
+
+            // If the bit mask is not all zero, there are newlines in this chunk.
+            if newlines_mask != 0 {
+                let output_offset = TextSize::from((chunk_index * CHUNK_SIZE + 1) as u32);
+
+                while newlines_mask != 0 {
+                    let trailing_zeros = newlines_mask.trailing_zeros();
+                    let index = trailing_zeros / 4;
+
+                    lines.push(TextSize::from(index) + output_offset);
+
+                    // Clear the current 4-bit, so we can find the next one.
+                    newlines_mask &= (!0xF) << trailing_zeros;
+                }
+            }
+            continue;
+        }
+
+        let scan_start = chunk_index * CHUNK_SIZE + intra_chunk_offset;
+        intra_chunk_offset = analyze_source_file_generic(
+            &src[scan_start..],
+            CHUNK_SIZE - intra_chunk_offset,
+            TextSize::from(scan_start as u32),
+            lines,
+            multi_byte_chars,
+        );
+    }
+
+    let tail_start = chunk_count * CHUNK_SIZE + intra_chunk_offset;
+    if tail_start < src.len() {
+        analyze_source_file_generic(
+            &src[tail_start..],
+            src.len() - tail_start,
+            TextSize::from(tail_start as u32),
+            lines,
+            multi_byte_chars,
+        );
+    }
+}
+
+#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))]
 // The target (or compiler version) does not support SSE2 ...
 fn analyze_source_file_dispatch(
     src: &str,
diff --git a/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs b/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs
index 6b732d47029..f717f8e0d4b 100644
--- a/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs
+++ b/src/tools/rust-analyzer/lib/lsp-server/src/lib.rs
@@ -184,9 +184,9 @@ impl Connection {
             };
         }
 
-        return Err(ProtocolError::new(String::from(
+        Err(ProtocolError::new(String::from(
             "Initialization has been aborted during initialization",
-        )));
+        )))
     }
 
     /// Finishes the initialization process by sending an `InitializeResult` to the client
@@ -244,9 +244,9 @@ impl Connection {
             }
         }
 
-        return Err(ProtocolError::new(String::from(
+        Err(ProtocolError::new(String::from(
             "Initialization has been aborted during initialization",
-        )));
+        )))
     }
 
     /// Initialize the connection. Sends the server capabilities
@@ -358,12 +358,14 @@ impl Connection {
                 )))
             }
             Err(RecvTimeoutError::Timeout) => {
-                return Err(ProtocolError::new(format!("timed out waiting for exit notification")))
+                return Err(ProtocolError::new(
+                    "timed out waiting for exit notification".to_string(),
+                ))
             }
             Err(RecvTimeoutError::Disconnected) => {
-                return Err(ProtocolError::new(format!(
-                    "channel disconnected waiting for exit notification"
-                )))
+                return Err(ProtocolError::new(
+                    "channel disconnected waiting for exit notification".to_string(),
+                ))
             }
         }
         Ok(true)