Skip to content

Commit

Permalink
Pulley: Add memory access instructions with 64-bit offsets
Browse files Browse the repository at this point in the history
I had trimmed these instructions from the original upstreaming of the Pulley
interpreter because I had mistakenly believed that they were unused. Turns out
they are needed for Cranelift's Pulley backend to allow for lowering certain
address modes to a single instruction. The alternative, lowering the address
modes to a sequence of instructions, would be a bit annoying and these
instructions seem generally useful.
  • Loading branch information
fitzgen committed Aug 7, 2024
1 parent 28ea648 commit 59958c6
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 0 deletions.
12 changes: 12 additions & 0 deletions pulley/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ macro_rules! for_each_op {
/// `dst = load64(ptr + offset8)`
load64_offset8 = Load64Offset8 { dst: XReg, ptr: XReg, offset: i8 };

/// `dst = zero_extend(load32(ptr + offset64))`
load32_u_offset64 = Load32UOffset64 { dst: XReg, ptr: XReg, offset: i64 };
/// `dst = sign_extend(load32(ptr + offset64))`
load32_s_offset64 = Load32SOffset64 { dst: XReg, ptr: XReg, offset: i64 };
/// `dst = load64(ptr + offset64)`
load64_offset64 = Load64Offset64 { dst: XReg, ptr: XReg, offset: i64 };

/// `*ptr = low32(src)`
store32 = Store32 { ptr: XReg, src: XReg };
/// `*ptr = src`
Expand All @@ -123,6 +130,11 @@ macro_rules! for_each_op {
/// `*(ptr + sign_extend(offset8)) = src`
store64_offset8 = Store64Offset8 { ptr: XReg, offset: i8, src: XReg };

/// `*(ptr + sign_extend(offset64)) = low32(src)`
store32_offset64 = Store32SOffset64 { ptr: XReg, offset: i64, src: XReg };
/// `*(ptr + sign_extend(offset64)) = src`
store64_offset64 = Store64Offset64 { ptr: XReg, offset: i64, src: XReg };

/// `low32(dst) = bitcast low32(src) as i32`
bitcast_int_from_float_32 = BitcastIntFromFloat32 { dst: XReg, src: FReg };
/// `dst = bitcast src as i64`
Expand Down
192 changes: 192 additions & 0 deletions pulley/tests/all/interp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,112 @@ fn load64_offset8() {
}
}

#[test]
fn load32_u_offset64() {
let a = UnsafeCell::new([11u32, 22]);
let b = UnsafeCell::new([33u32, 44]);
let c = UnsafeCell::new([55u32, 66]);
let d = UnsafeCell::new([i32::MIN as u32, i32::MAX as u32]);

for (expected, addr, offset) in [
(11, a.get(), 0),
(22, a.get(), 4),
(33, b.get(), 0),
(44, b.get(), 4),
(55, c.get(), 0),
(66, c.get(), 4),
(i32::MIN as u32 as u64, d.get(), 0),
(i32::MAX as u32 as u64, d.get(), 4),
] {
unsafe {
assert_one(
[
(x(0), Val::from(0x1234567812345678u64)),
(x(1), Val::from(addr.cast::<u8>())),
],
Load32UOffset64 {
dst: x(0),
ptr: x(1),
offset,
},
x(0),
expected,
);
}
}
}

#[test]
fn load32_s_offset64() {
let a = UnsafeCell::new([11u32, 22]);
let b = UnsafeCell::new([33u32, 44]);
let c = UnsafeCell::new([55u32, 66]);
let d = UnsafeCell::new([-1i32 as u32, i32::MAX as u32]);

for (expected, addr, offset) in [
(11, a.get(), 0),
(22, a.get(), 4),
(33, b.get(), 0),
(44, b.get(), 4),
(55, c.get(), 0),
(55, unsafe { c.get().byte_add(4) }, -4),
(66, c.get(), 4),
(-1i64 as u64, d.get(), 0),
(i32::MAX as u32 as u64, d.get(), 4),
] {
unsafe {
assert_one(
[
(x(0), Val::from(0x1234567812345678u64)),
(x(1), Val::from(addr.cast::<u8>())),
],
Load32SOffset64 {
dst: x(0),
ptr: x(1),
offset,
},
x(0),
expected,
);
}
}
}

#[test]
fn load64_offset64() {
let a = UnsafeCell::new([11u64, 22]);
let b = UnsafeCell::new([33u64, 44]);
let c = UnsafeCell::new([55u64, 66]);
let d = UnsafeCell::new([-1i64 as u64, i64::MAX as u64]);

for (expected, addr, offset) in [
(11, a.get(), 0),
(22, a.get(), 8),
(33, b.get(), 0),
(44, b.get(), 8),
(55, c.get(), 0),
(66, c.get(), 8),
(-1i64 as u64, d.get(), 0),
(i64::MAX as u64, d.get(), 8),
] {
unsafe {
assert_one(
[
(x(0), Val::from(0x1234567812345678u64)),
(x(1), Val::from(addr)),
],
Load64Offset64 {
dst: x(0),
ptr: x(1),
offset,
},
x(0),
expected,
);
}
}
}

#[test]
fn store32() {
let a = UnsafeCell::new([0x12u8, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78]);
Expand Down Expand Up @@ -859,6 +965,92 @@ fn store64_offset8() {
assert_eq!(c, expected);
}

#[test]
fn store32_offset64() {
let a = UnsafeCell::new([0x12u8, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78]);
let b = UnsafeCell::new([0x12u8, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78]);
let c = UnsafeCell::new([0x12u8, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78]);

unsafe {
for (val, addr, offset) in [
(0x11111111u32, a.get(), 0),
(0x22222222, b.get(), 4),
(0x33333333, c.get(), 2),
] {
let val = val as u64;
assert_one(
[(x(0), Val::from(addr)), (x(1), Val::from(val))],
Store32SOffset64 {
ptr: x(0),
src: x(1),
offset,
},
x(1),
val,
);
}
}

let a = u64::from_be_bytes(a.into_inner());
let expected = 0x1111111112345678u64;
eprintln!("expected(a) = {expected:#018x}");
eprintln!("actual(a) = {a:#018x}");
assert_eq!(a, expected);

let b = u64::from_be_bytes(b.into_inner());
let expected = 0x1234567822222222u64;
eprintln!("expected(b) = {expected:#018x}");
eprintln!("actual(b) = {b:#018x}");
assert_eq!(b, expected);

let c = u64::from_be_bytes(c.into_inner());
let expected = 0x1234333333335678u64;
eprintln!("expected(c) = {expected:#018x}");
eprintln!("actual(c) = {c:#018x}");
assert_eq!(c, expected);
}

#[test]
fn store64_offset64() {
let a = UnsafeCell::new([0x1234567812345678, 0x1234567812345678, 0x1234567812345678]);

unsafe {
for (val, addr, offset) in [
(0x1111111111111111u64, a.get(), 0),
(0x2222222222222222, a.get(), 8),
(0x3333333333333333, a.get(), 16),
] {
assert_one(
[(x(0), Val::from(addr)), (x(1), Val::from(val))],
Store64Offset64 {
ptr: x(0),
src: x(1),
offset,
},
x(1),
val,
);
}
}

let [a, b, c] = a.into_inner();

let expected = 0x1111111111111111u64;
eprintln!("expected(a) = {expected:#018x}");
eprintln!("actual(a) = {a:#018x}");
assert_eq!(a, expected);

let expected = 0x2222222222222222u64;
eprintln!("expected(b) = {expected:#018x}");
eprintln!("actual(b) = {b:#018x}");
assert_eq!(b, expected);

let expected = 0x3333333333333333u64;
eprintln!("expected(c) = {expected:#018x}");
eprintln!("actual(c) = {c:#018x}");
assert_eq!(c, expected);
}

#[test]
fn bitcast_int_from_float_32() {
for val in [
Expand Down

0 comments on commit 59958c6

Please sign in to comment.