Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the Verify mode #726

Merged
merged 17 commits into from
Feb 7, 2025
Merged

Add the Verify mode #726

merged 17 commits into from
Feb 7, 2025

Conversation

zhouwfang
Copy link
Member

@zhouwfang zhouwfang commented Jan 22, 2025

#46

@zhouwfang zhouwfang requested a review from ia0 as a code owner January 22, 2025 07:32
@zhouwfang zhouwfang changed the title Discuss the design for the Verify mode Discuss the Verify mode design Jan 22, 2025
crates/interpreter/src/valid.rs Outdated Show resolved Hide resolved
crates/interpreter/src/valid.rs Outdated Show resolved Hide resolved
crates/interpreter/src/valid.rs Outdated Show resolved Hide resolved
crates/interpreter/src/valid.rs Outdated Show resolved Hide resolved
@zhouwfang zhouwfang requested a review from ia0 January 27, 2025 03:49
@zhouwfang zhouwfang changed the title Discuss the Verify mode design Add the Verify mode Jan 27, 2025
Comment on lines 131 to 132
// expected_target.parser += delta_ip;
// expected_target.branch_table += delta_stp;
Copy link
Member Author

Choose a reason for hiding this comment

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

The remaining question is how to format and read the side table from the custom section of the WASM module.

In terms of the format, how about using the format that was intended for flash storage?

It may not be efficient to keep the whole side table in RAM. How about keeping a parser for it and reading the data on demand?

Copy link
Member Author

Choose a reason for hiding this comment

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

More precisely,

[Section ID: 0] 
[Section Size: ...]
[Section Name Length: 18]
[Section Name: "Wasefire-SideTable"] 
[Indices Array Size (LEB128): num_functions + 1] 
[Indices Array: (u16 * (num_functions + 1))]
[Metadata Array Size (LEB128): ...]
[Metadata Array: (u16 * ...)]

Copy link
Member

Choose a reason for hiding this comment

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

In terms of the format, how about using the format that was intended for flash storage?

Yes, that's the idea, even if we didn't have a proper parsing function yet. That's the format I wrote there (updated below):

side-table := index flatten-metadata (16-bits aligned)
index := u16 u16 ... u16 (N + 1 times where N is the number of functions in the module)
flatten-metadata := metadata metadata ... metadata (N times where `flatten-metadata[i]` starts at byte position `index[i]` and ends at `index[i + 1]`)
metadata := type:u16 parser:(u32 u32) branch-table
branch-table := branch branch ... branch (length is implicitly defined by `index[i + 1]` see above)
branch := delta-ip:u16 delta-stp:u16 val-cnt:u8 pop-cnt:u12

In particular, we shouldn't use LEB128 since u16 is enough and the benefit from using only one byte up to 127 is not worth it.

Copy link
Member

@ia0 ia0 left a comment

Choose a reason for hiding this comment

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

I've pushed a commit to better factorize end_label and br_label. It's not finished because similarly to the ValidMode::Branches, we also need ValidMode::BranchTable, which is either prepared or verified. In particular it must be read from ValidMode::patch_branch() for Verify.

@zhouwfang zhouwfang requested a review from ia0 February 3, 2025 05:25
crates/interpreter/src/valid.rs Show resolved Hide resolved
crates/interpreter/src/valid.rs Outdated Show resolved Hide resolved
crates/interpreter/src/valid.rs Outdated Show resolved Hide resolved
crates/interpreter/src/valid.rs Outdated Show resolved Hide resolved
crates/interpreter/src/valid.rs Outdated Show resolved Hide resolved
crates/interpreter/src/valid.rs Outdated Show resolved Hide resolved
crates/interpreter/src/valid.rs Outdated Show resolved Hide resolved
crates/interpreter/src/valid.rs Outdated Show resolved Hide resolved
Comment on lines 174 to 175
// metadata
Err(Invalid)
Copy link
Member Author

@zhouwfang zhouwfang Feb 6, 2025

Choose a reason for hiding this comment

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

It seems I couldn't return a mutable reference to metadata (local). (I had a design for returning by value.)

Copy link
Member

Choose a reason for hiding this comment

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

Yes, it's because Verify::BranchTable<'m> shouldn't be Metadata<'m> but some BranchTableView<'m> that contains a Metadata<'m> and a current branch index.

Copy link
Member Author

@zhouwfang zhouwfang Feb 7, 2025

Choose a reason for hiding this comment

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

I'm not sure I understand (the returned reference must be part of Self::SideTable) but I have a different solution. PTAL.

@zhouwfang zhouwfang requested a review from ia0 February 6, 2025 07:06
Copy link
Member

@ia0 ia0 left a comment

Choose a reason for hiding this comment

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

Thanks! I think it's getting there.

crates/interpreter/src/valid.rs Outdated Show resolved Hide resolved
crates/interpreter/src/side_table.rs Outdated Show resolved Hide resolved
crates/interpreter/src/parser.rs Outdated Show resolved Hide resolved
crates/interpreter/src/valid.rs Outdated Show resolved Hide resolved
Comment on lines 174 to 175
// metadata
Err(Invalid)
Copy link
Member

Choose a reason for hiding this comment

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

Yes, it's because Verify::BranchTable<'m> shouldn't be Metadata<'m> but some BranchTableView<'m> that contains a Metadata<'m> and a current branch index.

crates/interpreter/src/valid.rs Outdated Show resolved Hide resolved
crates/interpreter/src/valid.rs Outdated Show resolved Hide resolved
@@ -928,7 +1053,7 @@ impl<'a, 'm, M: ValidMode> Expr<'a, 'm, M> {
fn branch(&self) -> SideTableBranch<'m> {
SideTableBranch {
parser: self.parser.save(),
branch_table: self.branch_table.save(),
branch_table: self.branch_table.as_ref().map_or(0, |x| x.next_index()),
Copy link
Member

Choose a reason for hiding this comment

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

It doesn't work to unwrap? I would expect branch to be only called outside const context.

Copy link
Member Author

Choose a reason for hiding this comment

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

No, it could be None. I guess it is because branch_target() is called for Verify.

Copy link
Member

Choose a reason for hiding this comment

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

It's because of the End instruction for consts. I'm fixing it in the review commit.

type Branches<'m> = Vec<SideTableBranch<'m>>;
type BranchTable<'m> = Vec<BranchTableEntry>;
type SideTable<'m> = Vec<MetadataEntry>;
type Result = Vec<MetadataEntry>;
Copy link
Member

Choose a reason for hiding this comment

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

We could probably have this be Vec<u8> ultimately (and represent the content of the custom section).

Copy link
Member Author

Choose a reason for hiding this comment

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

Can we convert it in merge() in zhouwfang#8? Please also take a look at the design in that PR. Thanks!

Copy link
Member

Choose a reason for hiding this comment

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

Yes, we could do it in merge too. Ideally this would be an opaque type to the user. Something called SideTable.

@zhouwfang zhouwfang requested a review from ia0 February 7, 2025 05:02
Copy link
Member

@ia0 ia0 left a comment

Choose a reason for hiding this comment

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

Thanks! We can do the rest in followup PRs.

pub struct SideTableView<'m> {
pub func_idx: usize,
pub indices: &'m [u16], // including 0 and the length of metadata_array
pub metadata: &'m [u16],
Copy link
Member

Choose a reason for hiding this comment

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

We should change those to u8. We can't guarantee those will be aligned in flash. This could be done in another PR though. Let's add a TODO.

}

fn parse_side_table_field<'m>(parser: &mut crate::valid::Parser<'m>) -> Result<&'m [u16], Error> {
let len = parse_u16(parser.save()) as usize;
Copy link
Member

Choose a reason for hiding this comment

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

We shouldn't parse like WASM bytecode. The format can simply be #726 (comment). This assumes we know the number of functions, which we can cheaply get from the length of the function section. We could decide to store it in the side table though, but we only need this number. We don't need 2 lengths for each slice, and we don't need them to be in bytes since we know the size of the elements.

impl<'m> SideTable<'m> {
fn metadata(&self, func_idx: usize) -> Metadata<'m> {
impl<'m> SideTableView<'m> {
pub fn new(parser: &mut crate::valid::Parser<'m>) -> Result<Self, Error> {
Copy link
Member

Choose a reason for hiding this comment

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

We shouldn't take a parser as argument. See other comment. We should just take the binary data and the number of functions. We could simplify by not taking the number of functions and storing it in the binary data as the first field (u16 should be enough).

fn stitch_branch(
&mut self, source: SideTableBranch<'m>, target: SideTableBranch<'m>,
) -> CheckResult;

Copy link
Member

Choose a reason for hiding this comment

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

nit: let's also pack those declarations by removing the empty lines

type Branches<'m> = Vec<SideTableBranch<'m>>;
type BranchTable<'m> = Vec<BranchTableEntry>;
type SideTable<'m> = Vec<MetadataEntry>;
type Result = Vec<MetadataEntry>;
Copy link
Member

Choose a reason for hiding this comment

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

Yes, we could do it in merge too. Ideally this would be an opaque type to the user. Something called SideTable.

side_table.func_idx += 1;
check(
side_table.branch_table_view.type_idx() == type_idx
&& side_table.branch_table_view.parser_range() == parser_range,
Copy link
Member

Choose a reason for hiding this comment

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

nit: let's have 2 checks instead of a conjunction (simpler to read and more precise panic in debug mode)

@@ -928,7 +1053,7 @@ impl<'a, 'm, M: ValidMode> Expr<'a, 'm, M> {
fn branch(&self) -> SideTableBranch<'m> {
SideTableBranch {
parser: self.parser.save(),
branch_table: self.branch_table.save(),
branch_table: self.branch_table.as_ref().map_or(0, |x| x.next_index()),
Copy link
Member

Choose a reason for hiding this comment

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

It's because of the End instruction for consts. I'm fixing it in the review commit.

Ok(source)
}

fn allocate_branch(&mut self) {}
Copy link
Member

Choose a reason for hiding this comment

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

We should do something here and below.

@ia0 ia0 merged commit e16faf8 into google:dev/fast-interp Feb 7, 2025
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants