-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Add generic/typed take_struct
imports + ops
#11713
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
base: main
Are you sure you want to change the base?
Conversation
Subscribe to Label Actioncc @fitzgen
This issue or pull request has been labeled: "fuzzing"
Thus the following users have been cc'd because of the following labels:
To subscribe or unsubscribe from this label, edit the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good with the below addressed, thanks!
const STRUCT_BASE: u32 = 5; | ||
const TYPED_FN_BASE: u32 = 4; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not necessary in this PR, but it would probably be easier/cleaner down the line to switch from constants to using the wasm_encoder::{Type,Func}Section::len
methods to keep track of these things, so that we don't need to update these constants every time we unconditionally add a new type/function.
https://docs.rs/wasm-encoder/latest/wasm_encoder/struct.TypeSection.html#method.len
|
||
for i in 0..struct_count { | ||
let concrete = STRUCT_BASE + i; | ||
let ty_idx = typed_ft_base + i; // |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Empty comment at the end of line?
let name = format!("take_struct_{concrete}"); | ||
typed_names.push(name); | ||
imports.import( | ||
"", | ||
typed_names.last().unwrap().as_str(), | ||
EntityType::Function(ty_idx), | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can avoid the wonky .last()
stuff by pushing the name after defining the import:
let name = format!("take_struct_{concrete}"); | |
typed_names.push(name); | |
imports.import( | |
"", | |
typed_names.last().unwrap().as_str(), | |
EntityType::Function(ty_idx), | |
); | |
let name = format!("take_struct_{concrete}"); | |
imports.import( | |
"", | |
&name, | |
EntityType::Function(ty_idx), | |
); | |
typed_names.push(name); |
let mut typed_names: Vec<String> = Vec::new(); | ||
|
||
for i in 0..struct_count { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would be nice to have a single-sentence comment for readers here describing what this code is doing, so that they don't have to scrutinize it to figure out what is going on. Something like
For each of our concrete struct types, define a function import that takes an argument of that concrete type.
// Define the "run" function export. | ||
let mut functions = FunctionSection::new(); | ||
functions.function(1); | ||
|
||
let mut exports = ExportSection::new(); | ||
exports.export("run", ExportKind::Func, 3); | ||
let imported_fn_count: u32 = 4 + struct_count; | ||
exports.export("run", ExportKind::Func, imported_fn_count); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned earlier, things get a bit simpler and less brittle if we use the len
functions on the sections that define index spaces:
exports.export("run", ExportKind::Func, imported_fn_count); | |
let mut functions = FunctionSection::new(); | |
let mut exports = ExportSection::new(); | |
// Define the "run" function export. | |
let run_index = functions.len(); | |
functions.function(1); | |
exports.export("run", ExportKind::Func, run_index); |
I think it is worth doing this to avoid adding more opaque <constant> + <count>
incantations.
func.instruction(&Instruction::StructNew(x + 5)); | ||
func.instruction(&Instruction::Call(take_structref_idx)); | ||
} | ||
Self::TakeStructCall(x) => { | ||
func.instruction(&Instruction::StructNew(x + 5)); | ||
func.instruction(&Instruction::Call(take_structref_idx)); | ||
} | ||
Self::TakeTypedStructCall(x) => { | ||
let s = STRUCT_BASE + x; | ||
let f = TYPED_FN_BASE + x; | ||
func.instruction(&Instruction::StructNew(s)); | ||
func.instruction(&Instruction::Call(f)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nothing to change in this PR, but I want to point out that these emitted instruction sequences are not our ideal end state. We want to separate struct.new
from various call
s and drop
s and have the existing TableOp::Drop
be used to drop struct refs and we want the take_*
calls to take a struct ref from the stack, not create a new one to pass to the import. This will require updating the abstract stack representation to track types, however, so it should be left to a follow up PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I agree!
Thanks @fitzgen. Working on them! |
structref
argument to our Wasm binary skeleton(type (;4;) (func (param (ref any))))
is called afterStructNew
op instead ofDrop
to observe that specific struct and also maintain stack discipline.cc @fitzgen @eeide