Skip to content
Merged
Changes from 2 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
12526db
fix: aot traits
Maillew Oct 22, 2025
ec1bc3b
chore: fmt
Maillew Oct 22, 2025
d7a2979
feat: example of using AOT traits, needs code clean up
Maillew Oct 23, 2025
e596f98
chore: fix macros to call overwritten trait methods
Maillew Oct 23, 2025
13ce3e2
chore: typo
Maillew Oct 23, 2025
12f5b6f
chore: typo x2
Maillew Oct 23, 2025
c85d74d
feat: aot jump example
Maillew Oct 28, 2025
264fb3b
chore: precommit hook changes
Maillew Oct 28, 2025
f804542
chore: codspeed benchmarks for pr to feat/aot (#2183)
GunaDD Oct 29, 2025
2351865
feat: separating into new AotExecutor Trait; trait bound and compile …
Maillew Oct 29, 2025
e6a7bdf
feat: executor wrapper, trait bounds
Maillew Oct 29, 2025
699a1bc
chore: revert typo
Maillew Oct 29, 2025
9c80f70
fix: trait bound issues
Maillew Oct 29, 2025
bc2c37e
chore: enable extension workflow tests for aot
Maillew Oct 29, 2025
f9607ef
Merge remote-tracking branch 'origin/feat/aot' into feat/aot-traits
Maillew Oct 29, 2025
262adfd
fix: jalr test missing imports + debug statement clean up
Maillew Oct 29, 2025
22fbe7b
chore: missingi imports
Maillew Oct 29, 2025
82ae792
chore: remove debug statements + update cargo for ecc tests
Maillew Oct 29, 2025
d2ae80e
fix: failing ecc test. NOTE JALR IS INCORRECT, diagnosing
Maillew Oct 29, 2025
18a0127
chore: typo
Maillew Oct 29, 2025
5777172
chore: testing sumn
Maillew Oct 29, 2025
da98543
feat: traits
Maillew Oct 30, 2025
c4b03d5
chore: clean up workflow
Maillew Oct 30, 2025
8b75845
chore: artifact cleanup
Maillew Oct 30, 2025
e52f204
fix: return Result<string> for generate_x86_asm
Maillew Oct 30, 2025
3ee5d93
chore: remove x86 for base alu, jalr
Maillew Oct 30, 2025
0a3a159
chore: fix reverted files
Maillew Oct 30, 2025
26a24b5
chore: example cleanup
Maillew Oct 30, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions crates/vm/src/arch/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,22 @@ pub enum StaticProgramError {
ExecutorNotFound { opcode: VmOpcode },
}

#[cfg(feature = "aot")]
#[derive(Error, Debug)]
pub enum AotError {
#[error("AOT compilation not supported for this opcode")]
NotSupported,

#[error("No executor found for opcode {0}")]
NoExecutorFound(VmOpcode),

#[error("Invalid instruction format")]
InvalidInstruction,

#[error("Other AOT error: {0}")]
Other(String),
}

/// Function pointer for interpreter execution with function signature `(pre_compute, instret, pc,
/// arg, exec_state)`. The `pre_compute: &[u8]` is a pre-computed buffer of data
/// corresponding to a single instruction. The contents of `pre_compute` are determined from the
Expand Down Expand Up @@ -154,6 +170,18 @@ pub trait Executor<F> {
) -> Result<Handler<F, Ctx>, StaticProgramError>
where
Ctx: ExecutionCtxTrait;

#[cfg(feature = "aot")]
fn supports_aot_for_opcode(&self, opcode: VmOpcode) -> bool {
false
}
}

#[cfg(feature = "aot")]
pub trait AotExecutor<F>: Executor<F> {
/// Generate x86 assembly for the given instruction. Preconditions: Opcode must be supported by
/// AOT
fn generate_x86_asm(&self, inst: &Instruction<F>) -> Result<String, AotError>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need pc to jump to a static label?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can discuss this with @GunaDD as well, but in the current example, extern_handler returns the updated PC value to register rax, and there is post-processing x86 assembly to determine where to jump next using the jump table. we can continue this convention that it should be on the onus of the generated x86 to write the next PC value to rax, and then it should be able to use any type of label

Copy link
Contributor

@nyunyunyunyu nyunyunyunyu Oct 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it means we need to read the jump table based on rax, which brings some extra overheads. If you know pc, for most jumps you can use only 1 instruction to jump into asm_run_pc_base_<pc>(sry the name might be inaccurate) at compile time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, if thats the case we can pivot so that this function is responsible for handling the updated PC value, and modify the fallback case that uses extern_handler to handle PC changes as well for consistency

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment to show how generated ASMs look like and what the responsibility is? I feel we are not on the same page on that.

I supposed it generates:

balabala
call on_suspend
<exit if suspend>
<instruction execution logic>
<pc/context update>
<jump to next instruction>

@GunaDD mentioned the jump instruction for the dynamic case is:

    lea rdx, [rip + map_pc_base]
    movsxd rcx, [rdx + r13]
    add rcx, rdx
    jmp rcx

However if the jump point can be known at AOT compile time, we only need:

  jmp asm_execute_pc_<next_pc>

But we need to know the current PC in order to compute next_pc

}

/// Trait for metered execution via a host interpreter. The trait methods provide the methods to
Expand Down Expand Up @@ -189,6 +217,18 @@ pub trait MeteredExecutor<F> {
) -> Result<Handler<F, Ctx>, StaticProgramError>
where
Ctx: MeteredExecutionCtxTrait;

#[cfg(feature = "aot")]
fn supports_aot_for_opcode(&self, opcode: VmOpcode) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need assembles for the fallback operations anyway. Can we have a trait(AotFallback) for the fallback operations and generate a default implementation for structs implemented AotFallback? This will give us more flexibility if we want to override the implementation(e.g. a different way to preserve registers).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sounds good, i can add this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nyunyunyunyu I think it may be a better idea to have the default implementations for fallback be feature-gated within the respective Executor and MeteredExecutor traits, instead of as a separate trait, since there shouldn't be a case where a struct has the AotFallback trait, but not the regular Executor trait, since the fallback requires the Executor trait to exist anyways

Copy link
Contributor

@nyunyunyunyu nyunyunyunyu Oct 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thought is we could have something like this:

pub trait AotFallback: Executor + MeteredExecutor {}  
impl<F, E: AotFallback> AotExecutor for E {
  fn generate_x86_asm(&self, inst: &Instruction<F>) -> Result<String, AotError> {
    ...
  }
}

When you want a default implementation for an instruction X. You only need to:

impl AotFallback for X {}

false
}
}

#[cfg(feature = "aot")]
pub trait AotMeteredExecutor<F>: MeteredExecutor<F> {
/// Generate x86 assembly for the given instruction. Preconditions: Opcode must be supported by
/// AOT
fn generate_x86_asm(&self, inst: &Instruction<F>) -> Result<String, AotError>;
}

/// Trait for preflight execution via a host interpreter. The trait methods allow execution of
Expand Down
Loading