Browse Source

pulley: superinstructions for pushing/popping list of registers (#9099)

* pulley: add `push` and `pop` instructions

Add `xpush{32, 64}` and `xpop{32, 64}` for pushing/popping XRegs from the stack,
and `push_frame`/`pop_frame` for saving/restoring LR and FP in function
prologue/epilogue.

Copyright (c) 2024, Arm Limited.

Signed-off-by: Karl Meakin <karl.meakin@arm.com>

* cranelift-bitset: more impls for `ScalarBitset`

Implement `Arbitrary` for `ScalarBitset`.

Also implement `DoubleEndedIterator` and `ExactSizeIterator` for `Iter`.
The `pop_min` method was added to help implement `DoubleEndedIterator`,
and `pop` was renamed to `pop_max` for consistency.

Copyright (c) 2024, Arm Limited.

Signed-off-by: Karl Meakin <karl.meakin@arm.com>

* pulley: add instructions for pushing/popping list of registers

Add `xpush{32,64}_many` and `xpop{32,64}_many` for pushing/popping any
combination of all 32 XRegs.

Copyright (c) 2024, Arm Limited.

Signed-off-by: Karl Meakin <karl.meakin@arm.com>

---------

Signed-off-by: Karl Meakin <karl.meakin@arm.com>
pull/9166/head
Karl Meakin 3 months ago
committed by GitHub
parent
commit
ff92e7afd1
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 2
      Cargo.lock
  2. 2
      cranelift/bitset/Cargo.toml
  3. 95
      cranelift/bitset/src/scalar.rs
  4. 3
      cranelift/filetests/filetests/isa/pulley32/get_stack_pointer.clif
  5. 7
      cranelift/filetests/filetests/isa/pulley32/trap.clif
  6. 2
      cranelift/filetests/filetests/isa/pulley64/get_stack_pointer.clif
  7. 7
      cranelift/filetests/filetests/isa/pulley64/trap.clif
  8. 3
      pulley/Cargo.toml
  9. 19
      pulley/README.md
  10. 5
      pulley/fuzz/src/interp.rs
  11. 20
      pulley/src/decode.rs
  12. 25
      pulley/src/disas.rs
  13. 15
      pulley/src/encode.rs
  14. 83
      pulley/src/interp.rs
  15. 23
      pulley/src/lib.rs
  16. 4
      pulley/src/op.rs
  17. 98
      pulley/src/regs.rs
  18. 89
      pulley/tests/all/disas.rs
  19. 2
      scripts/publish.rs

2
Cargo.lock

@ -584,6 +584,7 @@ dependencies = [
name = "cranelift-bitset"
version = "0.112.0"
dependencies = [
"arbitrary",
"serde",
"serde_derive",
]
@ -2145,6 +2146,7 @@ name = "pulley-interpreter"
version = "0.1.0"
dependencies = [
"arbitrary",
"cranelift-bitset",
"env_logger",
"log",
"sptr",

2
cranelift/bitset/Cargo.toml

@ -13,8 +13,10 @@ rust-version.workspace = true
workspace = true
[dependencies]
arbitrary = { workspace = true, optional = true }
serde = { workspace = true, optional = true }
serde_derive = { workspace = true, optional = true }
[features]
enable-serde = ["dep:serde", "dep:serde_derive"]
arbitrary = ["dep:arbitrary"]

95
cranelift/bitset/src/scalar.rs

@ -350,6 +350,33 @@ where
self.0 = T::from(0);
}
/// Remove and return the smallest value in the bitset.
///
/// # Example
///
/// ```
/// use cranelift_bitset::ScalarBitSet;
///
/// let mut bitset = ScalarBitSet::<u64>::new();
///
/// bitset.insert(0);
/// bitset.insert(24);
/// bitset.insert(13);
/// bitset.insert(36);
///
/// assert_eq!(bitset.pop_min(), Some(0));
/// assert_eq!(bitset.pop_min(), Some(13));
/// assert_eq!(bitset.pop_min(), Some(24));
/// assert_eq!(bitset.pop_min(), Some(36));
/// assert_eq!(bitset.pop_min(), None);
/// ```
#[inline]
pub fn pop_min(&mut self) -> Option<u8> {
let min = self.min()?;
self.remove(min);
Some(min)
}
/// Remove and return the largest value in the bitset.
///
/// # Example
@ -364,14 +391,14 @@ where
/// bitset.insert(13);
/// bitset.insert(36);
///
/// assert_eq!(bitset.pop(), Some(36));
/// assert_eq!(bitset.pop(), Some(24));
/// assert_eq!(bitset.pop(), Some(13));
/// assert_eq!(bitset.pop(), Some(0));
/// assert_eq!(bitset.pop(), None);
/// assert_eq!(bitset.pop_max(), Some(36));
/// assert_eq!(bitset.pop_max(), Some(24));
/// assert_eq!(bitset.pop_max(), Some(13));
/// assert_eq!(bitset.pop_max(), Some(0));
/// assert_eq!(bitset.pop_max(), None);
/// ```
#[inline]
pub fn pop(&mut self) -> Option<u8> {
pub fn pop_max(&mut self) -> Option<u8> {
let max = self.max()?;
self.remove(max);
Some(max)
@ -458,11 +485,8 @@ where
/// );
/// ```
#[inline]
pub fn iter(&self) -> Iter<T> {
Iter {
value: self.0,
index: 0,
}
pub fn iter(self) -> Iter<T> {
Iter { bitset: self }
}
}
@ -494,6 +518,12 @@ where
}
}
impl<T: ScalarBitSetStorage> From<T> for ScalarBitSet<T> {
fn from(bits: T) -> Self {
Self(bits)
}
}
/// A trait implemented by all integers that can be used as the backing storage
/// for a [`ScalarBitSet`].
///
@ -550,8 +580,7 @@ impl_storage!(usize);
/// An iterator over the elements in a [`ScalarBitSet`].
pub struct Iter<T> {
value: T,
index: u8,
bitset: ScalarBitSet<T>,
}
impl<T> Iterator for Iter<T>
@ -562,14 +591,36 @@ where
#[inline]
fn next(&mut self) -> Option<u8> {
if self.value == T::from(0) {
None
} else {
let trailing_zeros = self.value.trailing_zeros();
let elem = self.index + trailing_zeros;
self.index += trailing_zeros + 1;
self.value = self.value >> (trailing_zeros + 1);
Some(elem)
}
self.bitset.pop_min()
}
}
impl<T> DoubleEndedIterator for Iter<T>
where
T: ScalarBitSetStorage,
{
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.bitset.pop_max()
}
}
impl<T> ExactSizeIterator for Iter<T>
where
T: ScalarBitSetStorage,
{
#[inline]
fn len(&self) -> usize {
usize::from(self.bitset.len())
}
}
#[cfg(feature = "arbitrary")]
impl<'a, T> arbitrary::Arbitrary<'a> for ScalarBitSet<T>
where
T: ScalarBitSetStorage + arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
T::arbitrary(u).map(Self)
}
}

3
cranelift/filetests/filetests/isa/pulley32/get_stack_pointer.clif

@ -13,6 +13,5 @@ block0:
; ret
;
; Disassembled:
; 0: 33 02 00 00 get_sp x0
; 0: 3d 02 00 00 get_sp x0
; 4: 00 ret

7
cranelift/filetests/filetests/isa/pulley32/trap.clif

@ -11,7 +11,7 @@ block0:
; trap // code = User(0)
;
; Disassembled:
; 0: 33 00 00 trap
; 0: 3d 00 00 trap
function %trapnz(i64) {
block0(v0: i64):
@ -36,7 +36,7 @@ block0(v0: i64):
; 3: 14 03 0c xeq64 x3, x0, x3
; 6: 03 03 07 00 00 00 br_if x3, 0x7 // target = 0xd
; c: 00 ret
; d: 33 00 00 trap
; d: 3d 00 00 trap
function %trapz(i64) {
block0(v0: i64):
@ -61,5 +61,4 @@ block0(v0: i64):
; 3: 14 03 0c xeq64 x3, x0, x3
; 6: 04 03 07 00 00 00 br_if_not x3, 0x7 // target = 0xd
; c: 00 ret
; d: 33 00 00 trap
; d: 3d 00 00 trap

2
cranelift/filetests/filetests/isa/pulley64/get_stack_pointer.clif

@ -13,5 +13,5 @@ block0:
; ret
;
; Disassembled:
; 0: 33 02 00 00 get_sp x0
; 0: 3d 02 00 00 get_sp x0
; 4: 00 ret

7
cranelift/filetests/filetests/isa/pulley64/trap.clif

@ -11,7 +11,7 @@ block0:
; trap // code = User(0)
;
; Disassembled:
; 0: 33 00 00 trap
; 0: 3d 00 00 trap
function %trapnz(i64) {
block0(v0: i64):
@ -36,7 +36,7 @@ block0(v0: i64):
; 3: 14 03 0c xeq64 x3, x0, x3
; 6: 03 03 07 00 00 00 br_if x3, 0x7 // target = 0xd
; c: 00 ret
; d: 33 00 00 trap
; d: 3d 00 00 trap
function %trapz(i64) {
block0(v0: i64):
@ -61,5 +61,4 @@ block0(v0: i64):
; 3: 14 03 0c xeq64 x3, x0, x3
; 6: 04 03 07 00 00 00 br_if_not x3, 0x7 // target = 0xd
; c: 00 ret
; d: 33 00 00 trap
; d: 3d 00 00 trap

3
pulley/Cargo.toml

@ -14,6 +14,7 @@ workspace = true
[dependencies]
arbitrary = { workspace = true, optional = true }
cranelift-bitset = { workspace = true }
log = { workspace = true }
sptr = { workspace = true }
@ -22,7 +23,7 @@ env_logger = { workspace = true }
[features]
std = []
arbitrary = ["dep:arbitrary", "arbitrary/derive", "std"]
arbitrary = ["dep:arbitrary", "arbitrary/derive", "std", "cranelift-bitset/arbitrary"]
encode = []
decode = []
disas = ["decode"]

19
pulley/README.md

@ -47,27 +47,16 @@ to change, instructions to appear and disappear, and APIs to be overhauled.
Here is the disassembly of `f(a, b) = a + b` in Pulley today:
```
0: 0e 1a f0 xconst8 x26, -16
3: 12 7b 6b xadd32 sp, sp, x26
6: 2c 1b 08 1c store64_offset8 sp, 8, lr
a: 2a 1b 1d store64 sp, fp
d: 0b 1d 1b xmov fp, sp
10: 12 00 04 xadd32 x0, x0, x1
13: 0b 1b 1d xmov sp, fp
16: 25 1c 1b 08 load64_offset8 lr, sp, 8
1a: 22 1d 1b load64 fp, sp
1d: 0e 1a 10 xconst8 x26, 16
20: 12 7b 6b xadd32 sp, sp, x26
23: 00 ret
0: 2f push_frame
1: 12 00 04 xadd32 x0, x0, x1
4: 30 pop_frame
5: 00 ret
```
Note that there are a number of things that could be improved here:
* We could avoid allocating and deallocating a stack frame because this function's
body doesn't use any stack slots.
* We could collapse the whole prologue and epilogue instruction sequences into
super-instructions, since they are identical (modulo the frame size immediate)
for all functions.
As mentioned above, Pulley is very much a work in progress.

5
pulley/fuzz/src/interp.rs

@ -105,6 +105,11 @@ fn op_is_safe_for_fuzzing(op: &Op) -> bool {
| Op::Xslteq32(Xslteq32 { operands, .. })
| Op::Xult32(Xult32 { operands, .. })
| Op::Xulteq32(Xulteq32 { operands, .. }) => !operands.dst.is_special(),
Op::PushFrame(_) | Op::PopFrame(_) => false,
Op::XPush32(_) | Op::XPush64(_) => false,
Op::XPop32(_) | Op::XPop64(_) => false,
Op::XPush32Many(_) | Op::XPush64Many(_) => false,
Op::XPop32Many(_) | Op::XPop64Many(_) => false,
}
}

20
pulley/src/decode.rs

@ -1,6 +1,8 @@
//! Decoding support for pulley bytecode.
use alloc::vec::Vec;
use cranelift_bitset::scalar::ScalarBitSetStorage;
use cranelift_bitset::ScalarBitSet;
use crate::imms::*;
use crate::opcode::*;
@ -384,6 +386,24 @@ impl<R: Reg> Decode for BinaryOperands<R> {
}
}
impl<S: Decode + ScalarBitSetStorage> Decode for ScalarBitSet<S> {
fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
where
T: BytecodeStream,
{
S::decode(bytecode).map(ScalarBitSet::from)
}
}
impl<R: Reg + Decode> Decode for RegSet<R> {
fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
where
T: BytecodeStream,
{
ScalarBitSet::decode(bytecode).map(Self::from)
}
}
/// A Pulley bytecode decoder.
///
/// Does not materialize bytecode instructions, instead all decoding methods are

25
pulley/src/disas.rs

@ -127,13 +127,26 @@ impl Disas for PcRelOffset {
}
}
impl<R: Disas> Disas for BinaryOperands<R> {
fn disas(&self, position: usize, disas: &mut String) {
self.dst.disas(position, disas);
write!(disas, ", ").unwrap();
self.src1.disas(position, disas);
fn disas_list<T: Disas>(position: usize, disas: &mut String, iter: impl IntoIterator<Item = T>) {
let mut iter = iter.into_iter();
let Some(first) = iter.next() else { return };
first.disas(position, disas);
for item in iter {
write!(disas, ", ").unwrap();
self.src2.disas(position, disas);
item.disas(position, disas);
}
}
impl<R: Reg + Disas> Disas for BinaryOperands<R> {
fn disas(&self, position: usize, disas: &mut String) {
disas_list(position, disas, [self.dst, self.src1, self.src2])
}
}
impl<R: Reg + Disas> Disas for RegSet<R> {
fn disas(&self, position: usize, disas: &mut String) {
disas_list(position, disas, *self)
}
}

15
pulley/src/encode.rs

@ -87,7 +87,7 @@ impl Encode for XReg {
where
E: Extend<u8>,
{
sink.extend(core::iter::once(u8::try_from(self.index()).unwrap()));
sink.extend(core::iter::once(self.to_u8()));
}
}
@ -96,7 +96,7 @@ impl Encode for FReg {
where
E: Extend<u8>,
{
sink.extend(core::iter::once(u8::try_from(self.index()).unwrap()));
sink.extend(core::iter::once(self.to_u8()));
}
}
@ -105,7 +105,7 @@ impl Encode for VReg {
where
E: Extend<u8>,
{
sink.extend(core::iter::once(u8::try_from(self.index()).unwrap()));
sink.extend(core::iter::once(self.to_u8()));
}
}
@ -127,6 +127,15 @@ impl<R: Reg> Encode for BinaryOperands<R> {
}
}
impl<R: Reg + Encode> Encode for RegSet<R> {
fn encode<E>(&self, sink: &mut E)
where
E: Extend<u8>,
{
self.to_bitset().0.encode(sink);
}
}
macro_rules! impl_encoders {
(
$(

83
pulley/src/interp.rs

@ -569,6 +569,21 @@ impl MachineState {
state
}
/// `*sp = val; sp += size_of::<T>()`
fn push<T>(&mut self, val: T) {
let sp = self[XReg::sp].get_ptr::<T>();
unsafe { sp.write_unaligned(val) }
self[XReg::sp].set_ptr(sp.wrapping_add(1));
}
/// `ret = *sp; sp -= size_of::<T>()`
fn pop<T>(&mut self) -> T {
let sp = self[XReg::sp].get_ptr::<T>();
let val = unsafe { sp.read_unaligned() };
self[XReg::sp].set_ptr(sp.wrapping_sub(1));
val
}
}
enum Continuation {
@ -988,6 +1003,74 @@ impl OpVisitor for InterpreterVisitor<'_> {
Continuation::Continue
}
fn xpush32(&mut self, src: XReg) -> Self::Return {
self.state.push(self.state[src].get_u32());
Continuation::Continue
}
fn xpush32_many(&mut self, srcs: RegSet<XReg>) -> Self::Return {
for src in srcs {
self.xpush32(src);
}
Continuation::Continue
}
fn xpush64(&mut self, src: XReg) -> Self::Return {
self.state.push(self.state[src].get_u64());
Continuation::Continue
}
fn xpush64_many(&mut self, srcs: RegSet<XReg>) -> Self::Return {
for src in srcs {
self.xpush64(src);
}
Continuation::Continue
}
fn xpop32(&mut self, dst: XReg) -> Self::Return {
let val = self.state.pop();
self.state[dst].set_u32(val);
Continuation::Continue
}
fn xpop32_many(&mut self, dsts: RegSet<XReg>) -> Self::Return {
for dst in dsts.into_iter().rev() {
self.xpop32(dst);
}
Continuation::Continue
}
fn xpop64(&mut self, dst: XReg) -> Self::Return {
let val = self.state.pop();
self.state[dst].set_u64(val);
Continuation::Continue
}
fn xpop64_many(&mut self, dsts: RegSet<XReg>) -> Self::Return {
for dst in dsts.into_iter().rev() {
self.xpop64(dst);
}
Continuation::Continue
}
/// `push lr; push fp; fp = sp`
fn push_frame(&mut self) -> Self::Return {
self.state.push(self.state[XReg::lr].get_ptr::<u8>());
self.state.push(self.state[XReg::fp].get_ptr::<u8>());
self.state[XReg::fp] = self.state[XReg::sp];
Continuation::Continue
}
/// `sp = fp; pop fp; pop lr`
fn pop_frame(&mut self) -> Self::Return {
self.state[XReg::sp] = self.state[XReg::fp];
let fp = self.state.pop();
let lr = self.state.pop();
self.state[XReg::fp].set_ptr::<u8>(fp);
self.state[XReg::lr].set_ptr::<u8>(lr);
Continuation::Continue
}
fn bitcast_int_from_float_32(&mut self, dst: XReg, src: FReg) -> Self::Return {
let val = self.state[src].get_f32();
self.state[dst].set_u64(u32::from_ne_bytes(val.to_ne_bytes()).into());

23
pulley/src/lib.rs

@ -131,6 +131,29 @@ macro_rules! for_each_op {
/// `*(ptr + sign_extend(offset64)) = src`
store64_offset64 = Store64Offset64 { ptr: XReg, offset: i64, src: XReg };
/// `push lr; push fp; fp = sp`
push_frame = PushFrame ;
/// `sp = fp; pop fp; pop lr`
pop_frame = PopFrame ;
/// `*sp = low32(src); sp += 4`
xpush32 = XPush32 { src: XReg };
/// `for src in srcs { xpush32 src }`
xpush32_many = XPush32Many { srcs: RegSet<XReg> };
/// `*sp = src; sp += 8`
xpush64 = XPush64 { src: XReg };
/// `for src in srcs { xpush64 src }`
xpush64_many = XPush64Many { srcs: RegSet<XReg> };
/// `*dst = *sp; sp -= 4`
xpop32 = XPop32 { dst: XReg };
/// `for dst in dsts.rev() { xpop32 dst }`
xpop32_many = XPop32Many { dsts: RegSet<XReg> };
/// `*dst = *sp; sp -= 8`
xpop64 = XPop64 { dst: XReg };
/// `for dst in dsts.rev() { xpop64 dst }`
xpop64_many = XPop64Many { dsts: RegSet<XReg> };
/// `low32(dst) = bitcast low32(src) as i32`
bitcast_int_from_float_32 = BitcastIntFromFloat32 { dst: XReg, src: FReg };
/// `dst = bitcast src as i64`

4
pulley/src/op.rs

@ -15,7 +15,7 @@ macro_rules! define_op {
/// This type is useful for debugging, writing tests, etc... but is not
/// actually ever used by the interpreter, encoder, or decoder, all of
/// which avoid materializing ops.
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum Op {
$(
@ -28,7 +28,7 @@ macro_rules! define_op {
$(
$( #[$attr] )*
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct $name { $(
$(

98
pulley/src/regs.rs

@ -1,8 +1,11 @@
//! Pulley registers.
use core::hash::Hash;
use core::marker::PhantomData;
use core::{fmt, ops::Range};
use cranelift_bitset::ScalarBitSet;
/// Trait for common register operations.
pub trait Reg: Sized + Copy + Eq + Ord + Hash + Into<AnyReg> + fmt::Debug + fmt::Display {
/// Range of valid register indices.
@ -159,9 +162,9 @@ impl fmt::Debug for AnyReg {
pub struct BinaryOperands<R> {
/// The destination register, packed in bits 0..5.
pub dst: R,
/// The frist source register, packed in bits 5..10.
/// The first source register, packed in bits 5..10.
pub src1: R,
/// The frist source register, packed in bits 10..15.
/// The second source register, packed in bits 10..15.
pub src2: R,
}
@ -193,6 +196,97 @@ impl<R: Reg> BinaryOperands<R> {
}
}
/// A set of registers, packed into a 32-bit bitset.
pub struct RegSet<R> {
bitset: ScalarBitSet<u32>,
phantom: PhantomData<R>,
}
impl<R: Reg> RegSet<R> {
/// Create a `RegSet` from a `ScalarBitSet`.
pub fn from_bitset(bitset: ScalarBitSet<u32>) -> Self {
Self {
bitset,
phantom: PhantomData,
}
}
/// Convert a `RegSet` into a `ScalarBitSet`.
pub fn to_bitset(self) -> ScalarBitSet<u32> {
self.bitset
}
}
impl<R: Reg> From<ScalarBitSet<u32>> for RegSet<R> {
fn from(bitset: ScalarBitSet<u32>) -> Self {
Self {
bitset,
phantom: PhantomData,
}
}
}
impl<R: Reg> Into<ScalarBitSet<u32>> for RegSet<R> {
fn into(self) -> ScalarBitSet<u32> {
self.bitset
}
}
impl<R: Reg> IntoIterator for RegSet<R> {
type Item = R;
type IntoIter = core::iter::FilterMap<cranelift_bitset::scalar::Iter<u32>, fn(u8) -> Option<R>>;
fn into_iter(self) -> Self::IntoIter {
self.bitset.into_iter().filter_map(R::new)
}
}
impl<R: Reg> FromIterator<R> for RegSet<R> {
fn from_iter<I: IntoIterator<Item = R>>(iter: I) -> Self {
let mut set = ScalarBitSet::new();
for reg in iter {
set.insert(reg.to_u8());
}
RegSet::from(set)
}
}
impl<R: Reg> Default for RegSet<R> {
fn default() -> Self {
Self {
bitset: Default::default(),
phantom: Default::default(),
}
}
}
impl<R: Reg> Copy for RegSet<R> {}
impl<R: Reg> Clone for RegSet<R> {
fn clone(&self) -> Self {
*self
}
}
impl<R: Reg> PartialEq for RegSet<R> {
fn eq(&self, other: &Self) -> bool {
self.bitset == other.bitset
}
}
impl<R: Reg> Eq for RegSet<R> {}
impl<R: Reg> fmt::Debug for RegSet<R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_set().entries(self.into_iter()).finish()
}
}
#[cfg(feature = "arbitrary")]
impl<'a, R: Reg> arbitrary::Arbitrary<'a> for RegSet<R> {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
ScalarBitSet::arbitrary(u).map(Self::from)
}
}
#[cfg(test)]
mod tests {
use super::*;

89
pulley/tests/all/disas.rs

@ -29,29 +29,36 @@ fn simple() {
assert_disas(
&[
// Prologue.
Op::Xconst8(Xconst8 {
dst: XReg::x26,
imm: -16i8,
}),
Op::PushFrame(PushFrame {}),
// Function body.
Op::Xadd32(Xadd32 {
operands: BinaryOperands {
dst: XReg::sp,
src1: XReg::sp,
src2: XReg::x26,
dst: XReg::x0,
src1: XReg::x0,
src2: XReg::x1,
},
}),
Op::Store64Offset8(Store64Offset8 {
ptr: XReg::sp,
offset: 8,
src: XReg::lr,
}),
Op::Store64(Store64 {
ptr: XReg::sp,
src: XReg::fp,
}),
Op::Xmov(Xmov {
dst: XReg::fp,
src: XReg::sp,
// Epilogue.
Op::PopFrame(PopFrame {}),
Op::Ret(Ret {}),
],
r#"
0: 2f push_frame
1: 12 00 04 xadd32 x0, x0, x1
4: 30 pop_frame
5: 00 ret
"#,
);
}
#[test]
fn push_pop_many() {
assert_disas(
&[
// Prologue.
Op::PushFrame(PushFrame {}),
Op::XPush32Many(XPush32Many {
srcs: RegSet::from_iter([XReg::x0, XReg::x1, XReg::x2, XReg::x3, XReg::x4]),
}),
// Function body.
Op::Xadd32(Xadd32 {
@ -62,45 +69,19 @@ fn simple() {
},
}),
// Epilogue.
Op::Xmov(Xmov {
dst: XReg::sp,
src: XReg::fp,
}),
Op::Load64Offset8(Load64Offset8 {
dst: XReg::lr,
ptr: XReg::sp,
offset: 8,
}),
Op::Load64(Load64 {
dst: XReg::fp,
ptr: XReg::sp,
}),
Op::Xconst8(Xconst8 {
dst: XReg::x26,
imm: 16,
}),
Op::Xadd32(Xadd32 {
operands: BinaryOperands {
dst: XReg::sp,
src1: XReg::sp,
src2: XReg::x26,
},
Op::XPop32Many(XPop32Many {
dsts: RegSet::from_iter([XReg::x0, XReg::x1, XReg::x2, XReg::x3, XReg::x4]),
}),
Op::PopFrame(PopFrame {}),
Op::Ret(Ret {}),
],
r#"
0: 0e 1a f0 xconst8 x26, -16
3: 12 7b 6b xadd32 sp, sp, x26
6: 2c 1b 08 1c store64_offset8 sp, 8, lr
a: 2a 1b 1d store64 sp, fp
d: 0b 1d 1b xmov fp, sp
10: 12 00 04 xadd32 x0, x0, x1
13: 0b 1b 1d xmov sp, fp
16: 25 1c 1b 08 load64_offset8 lr, sp, 8
1a: 22 1d 1b load64 fp, sp
1d: 0e 1a 10 xconst8 x26, 16
20: 12 7b 6b xadd32 sp, sp, x26
23: 00 ret
0: 2f push_frame
1: 32 1f 00 00 00 xpush32_many x0, x1, x2, x3, x4
6: 12 00 04 xadd32 x0, x0, x1
9: 36 1f 00 00 00 xpop32_many x0, x1, x2, x3, x4
e: 30 pop_frame
f: 00 ret
"#,
);
}

2
scripts/publish.rs

@ -18,9 +18,9 @@ use std::time::Duration;
// note that this list must be topologically sorted by dependencies
const CRATES_TO_PUBLISH: &[&str] = &[
// pulley
"cranelift-bitset",
"pulley-interpreter",
// cranelift
"cranelift-bitset",
"cranelift-isle",
"cranelift-entity",
"wasmtime-types",

Loading…
Cancel
Save