Skip to content

Commit

Permalink
Pulley: tail calls (#9251)
Browse files Browse the repository at this point in the history
* Document host return address and extract to const

Copyright (c) 2024, Arm Limited.

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

* Replace `Continutation` with `ControlFlow`

Copyright (c) 2024, Arm Limited.

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

* Tail recursive interpreter loop

Copyright (c) 2024, Arm Limited.

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

* Delete visitor based interpreter implementation

Copyright (c) 2024, Arm Limited.

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

* Move pulley tail call CI step to a job that uses nightly rust

* Record the PC at which a trap occurred

Not the initial PC passed into `run`

---------

Signed-off-by: Karl Meakin <karl.meakin@arm.com>
Co-authored-by: Nick Fitzgerald <fitzgen@gmail.com>
  • Loading branch information
Kmeakin and fitzgen authored Oct 7, 2024
1 parent 818966f commit 623a821
Show file tree
Hide file tree
Showing 7 changed files with 790 additions and 609 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,13 @@ jobs:
with:
toolchain: wasmtime-ci-pinned-nightly

# Check that `pulley-interpreter` compiles with tail calls enabled. Don't
# actually run the tests with tail calls enabled, because they are not yet
# implemented in rustc and cause an ICE.
- run: cargo check -p pulley-interpreter
env:
RUSTFLAGS: "--cfg pulley_tail_calls"

# Ensure that fuzzers still build.
#
# Install the OCaml packages necessary for fuzz targets that use the
Expand Down
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,9 @@ unused_import_braces = 'warn'
unused-lifetimes = 'warn'
unused-macro-rules = 'warn'

# Don't warn about unknown cfg condition in `#[cfg(pulley_tail_calls)]`
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(pulley_tail_calls)'] }

[workspace.lints.clippy]
# The default set of lints in Clippy is viewed as "too noisy" right now so
# they're all turned off by default. Selective lints are then enabled below as
Expand Down
2 changes: 1 addition & 1 deletion pulley/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ arbitrary = ["dep:arbitrary", "arbitrary/derive", "std", "cranelift-bitset/arbit
encode = []
decode = []
disas = ["decode"]
interp = ["decode"]
interp = ["decode", "encode"]

[package.metadata.docs.rs]
all-features = true
79 changes: 78 additions & 1 deletion pulley/src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,8 @@ impl BytecodeStream for UnsafeBytecodeStream {

/// Anything that can be decoded from a bytecode stream, e.g. opcodes,
/// immediates, registers, etc...
trait Decode: Sized {
pub trait Decode: Sized {
/// Decode this type from the given bytecode stream.
fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
where
T: BytecodeStream;
Expand Down Expand Up @@ -377,6 +378,32 @@ impl Decode for PcRelOffset {
}
}

impl Decode for Opcode {
fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
where
T: BytecodeStream,
{
let byte = u8::decode(bytecode)?;
match Opcode::new(byte) {
Some(v) => Ok(v),
None => Err(bytecode.invalid_opcode(byte)),
}
}
}

impl Decode for ExtendedOpcode {
fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
where
T: BytecodeStream,
{
let word = u16::decode(bytecode)?;
match ExtendedOpcode::new(word) {
Some(v) => Ok(v),
None => Err(bytecode.invalid_extended_opcode(word)),
}
}
}

impl<R: Reg> Decode for BinaryOperands<R> {
fn decode<T>(bytecode: &mut T) -> Result<Self, T::Error>
where
Expand Down Expand Up @@ -655,3 +682,53 @@ macro_rules! define_extended_decoder {
};
}
for_each_extended_op!(define_extended_decoder);

/// Unwrap a `Result<T, Uninhabited>`.
/// Always succeeds, since `Uninhabited` is uninhabited.
pub fn unwrap_uninhabited<T>(res: Result<T, Uninhabited>) -> T {
match res {
Ok(ok) => ok,

// Nightly rust warns that this pattern is unreachable, but stable rust
// doesn't.
#[allow(unreachable_patterns)]
Err(err) => match err {},
}
}

/// Functions for decoding the operands of an instruction, assuming the opcode
/// has already been decoded.
pub mod operands {
use super::*;

macro_rules! define_operands_decoder {
(
$(
$( #[$attr:meta] )*
$snake_name:ident = $name:ident $( {
$(
$( #[$field_attr:meta] )*
$field:ident : $field_ty:ty
),*
} )? ;
)*
) => {
$(
#[allow(unused_variables)]
#[allow(missing_docs)]
pub fn $snake_name<T: BytecodeStream>(pc: &mut T) -> Result<($($($field_ty,)*)?), T::Error> {
Ok((($($((<$field_ty>::decode(pc))?,)*)?)))
}
)*
};
}

for_each_op!(define_operands_decoder);

#[allow(missing_docs)]
pub fn extended<T: BytecodeStream>(pc: &mut T) -> Result<(ExtendedOpcode,), T::Error> {
Ok((ExtendedOpcode::decode(pc)?,))
}

for_each_extended_op!(define_operands_decoder);
}
Loading

0 comments on commit 623a821

Please sign in to comment.