Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 8 additions & 2 deletions lib/compiler-singlepass/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,23 @@ This crate contains a compiler implementation based on the Singlepass linear com
Supported targets:
- x86_64
- aarch64
- riscv64 (experimental)
- riscv64

## Usage

```rust
use wasmer::{Store, sys::EngineBuilder};
use wasmer_compiler_singlepass::Singlepass;

let compiler = Singlepass::new();
let mut compiler = Singlepass::new();
compiler.strict_memory_boundary_checks(true);
let mut store = Store::new(compiler);
```

`strict_memory_boundary_checks(true)` forces Singlepass to emit explicit
memory bounds checks for every memory access, including static memories that
would otherwise use the unchecked fast path.

*Note: you can find a [full working example using Singlepass compiler
here][example].*

Expand Down
9 changes: 5 additions & 4 deletions lib/compiler-singlepass/src/codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -835,10 +835,11 @@ impl<'a, M: Machine> FuncGen<'a, M> {
&mut self,
cb: F,
) -> Result<(), CompileError> {
let need_check = match self.memory_styles[MemoryIndex::new(0)] {
MemoryStyle::Static { .. } => false,
MemoryStyle::Dynamic { .. } => true,
};
let need_check = self.config.strict_memory_boundary_checks
|| match self.memory_styles[MemoryIndex::new(0)] {
MemoryStyle::Static { .. } => false,
MemoryStyle::Dynamic { .. } => true,
};

let offset = if self.module.num_imported_memories != 0 {
self.vmoffsets
Expand Down
17 changes: 17 additions & 0 deletions lib/compiler-singlepass/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,9 @@ impl SinglepassCallbacks {
pub struct Singlepass {
pub(crate) enable_nan_canonicalization: bool,

/// Forces strict memory boundary checks for every memory access.
pub(crate) strict_memory_boundary_checks: bool,

/// The middleware chain.
pub(crate) middlewares: Vec<Arc<dyn ModuleMiddleware>>,

Expand All @@ -92,12 +95,26 @@ impl Singlepass {
pub fn new() -> Self {
Self {
enable_nan_canonicalization: true,
strict_memory_boundary_checks: false,
middlewares: vec![],
callbacks: None,
num_threads: std::thread::available_parallelism().unwrap_or(NonZero::new(1).unwrap()),
}
}

/// Configure whether Singlepass should always emit strict memory boundary
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Shouldn't a major behavior change like this use the old behaviour by default?

We could switch the default with the next major version?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Given the change is tightly communicated with our customer, we can do it. Moreover, it's going to align with other Singlepass platforms.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Our customers are a limited subset of our users though.

In general I'm against significant behaviour changes in minor versions.

Guess should have @syrusakbary chime in.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Sure, that’s a good point! Keep in mind that the current RISC-V port of Singlepass is still considered experimental, so we may have some flexibility to make this change.

As for minor versions, it’s a bit tricky. On one hand, we aim to do a major release once a year, but in reality, many minor releases introduce breaking changes and arguably should be treated as major versions. This might be worth discussing further.

/// checks for memory accesses.
///
/// When enabled, Singlepass always requests explicit memory bounds checks
/// from `op_memory`, even for static memories that would normally rely
/// on the access violation signal.
pub fn strict_memory_boundary_checks(&mut self, enable: bool) -> &mut Self {
self.strict_memory_boundary_checks = enable;
self
}

/// Configure whether Singlepass should canonicalize NaNs during code
/// generation.
pub fn canonicalize_nans(&mut self, enable: bool) -> &mut Self {
self.enable_nan_canonicalization = enable;
self
Expand Down
Loading
Loading