summaryrefslogtreecommitdiff
path: root/yjit/src/asm/arm64/arg
diff options
context:
space:
mode:
authorAlan Wu <XrXr@users.noreply.github.com>2022-10-06 18:41:38 -0400
committerGitHub <noreply@github.com>2022-10-06 18:41:38 -0400
commit43e87c7e8ab5cbf253c8f11daf9c8ad4bc3d7b3e (patch)
tree7696c3ee5c3e54abe7bfaccf763645291e8d909b /yjit/src/asm/arm64/arg
parentfa2e1b67e548cb5653b66909a2bc3d6b9eae98e3 (diff)
YJIT: fix ARM64 bitmask encoding for 32 bit registers (#6503)
For logical instructions such as AND, there is a constraint that the N part of the bitmask immediate must be 0. We weren't respecting this condition previously and were silently emitting undefined instructions. Check for this condition in the assembler and tweak the backend to correctly detect whether a number could be encoded as an immediate in a 32 bit logical instruction. Due to the nature of the immediate encoding, the same numeric value encodes differently depending on the size of the register the instruction works on. We currently don't have cases where we use 32 bit immediates but we ran into this encoding issue during development.
Notes
Notes: Merged-By: maximecb <maximecb@ruby-lang.org>
Diffstat (limited to 'yjit/src/asm/arm64/arg')
-rw-r--r--yjit/src/asm/arm64/arg/bitmask_imm.rs57
1 files changed, 51 insertions, 6 deletions
diff --git a/yjit/src/asm/arm64/arg/bitmask_imm.rs b/yjit/src/asm/arm64/arg/bitmask_imm.rs
index 54a6e6c344..d569828a24 100644
--- a/yjit/src/asm/arm64/arg/bitmask_imm.rs
+++ b/yjit/src/asm/arm64/arg/bitmask_imm.rs
@@ -72,13 +72,31 @@ impl TryFrom<u64> for BitmaskImmediate {
}
}
-impl From<BitmaskImmediate> for u32 {
+impl BitmaskImmediate {
+ /// Attempt to make a BitmaskImmediate for a 32 bit register.
+ /// The result has N==0, which is required for some 32-bit instructions.
+ /// Note that the exact same BitmaskImmediate produces different values
+ /// depending on the size of the target register.
+ pub fn new_32b_reg(value: u32) -> Result<Self, ()> {
+ // The same bit pattern replicated to u64
+ let value = value as u64;
+ let replicated: u64 = (value << 32) | value;
+ let converted = Self::try_from(replicated);
+ if let Ok(ref imm) = converted {
+ assert_eq!(0, imm.n);
+ }
+
+ converted
+ }
+}
+
+impl BitmaskImmediate {
/// Encode a bitmask immediate into a 32-bit value.
- fn from(bitmask: BitmaskImmediate) -> Self {
+ pub fn encode(self) -> u32 {
0
- | ((bitmask.n as u32) << 12)
- | ((bitmask.immr as u32) << 6)
- | (bitmask.imms as u32)
+ | ((self.n as u32) << 12)
+ | ((self.immr as u32) << 6)
+ | (self.imms as u32)
}
}
@@ -96,7 +114,7 @@ mod tests {
#[test]
fn test_negative() {
let bitmask: BitmaskImmediate = (-9_i64 as u64).try_into().unwrap();
- let encoded: u32 = bitmask.into();
+ let encoded: u32 = bitmask.encode();
assert_eq!(7998, encoded);
}
@@ -207,4 +225,31 @@ mod tests {
let bitmask = BitmaskImmediate::try_from(u64::MAX);
assert!(matches!(bitmask, Err(())));
}
+
+ #[test]
+ fn test_all_valid_32b_pattern() {
+ let mut patterns = vec![];
+ for pattern_size in [2, 4, 8, 16, 32_u64] {
+ for ones_count in 1..pattern_size {
+ for rotation in 0..pattern_size {
+ let ones = (1_u64 << ones_count) - 1;
+ let rotated = (ones >> rotation) |
+ ((ones & ((1 << rotation) - 1)) << (pattern_size - rotation));
+ let mut replicated = rotated;
+ let mut shift = pattern_size;
+ while shift < 32 {
+ replicated |= replicated << shift;
+ shift *= 2;
+ }
+ let replicated: u32 = replicated.try_into().unwrap();
+ assert!(BitmaskImmediate::new_32b_reg(replicated).is_ok());
+ patterns.push(replicated);
+ }
+ }
+ }
+ patterns.sort();
+ patterns.dedup();
+ // Up to {size}-1 ones, and a total of {size} possible rotations.
+ assert_eq!(1*2 + 3*4 + 7*8 + 15*16 + 31*32, patterns.len());
+ }
}