Skip to content

Conversation

@khagankhan
Copy link
Contributor

  • Add a function import that takes a structref argument to our Wasm binary skeleton
  • Add a fuzzer op that calls that new function
  • Add a function import for each defined struct type that takes a reference of that type specifically
  • Add a fuzzer op that calls these new typed functions
  • Generic (type (;4;) (func (param (ref any)))) is called after StructNew op instead of Drop to observe that specific struct and also maintain stack discipline.
  • Typed versions are called with specific structs of the types

cc @fitzgen @eeide

@khagankhan khagankhan requested a review from a team as a code owner September 18, 2025 23:04
@khagankhan khagankhan requested review from alexcrichton and removed request for a team September 18, 2025 23:04
@github-actions github-actions bot added the fuzzing Issues related to our fuzzing infrastructure label Sep 19, 2025
@github-actions
Copy link

Subscribe to Label Action

cc @fitzgen

This issue or pull request has been labeled: "fuzzing"

Thus the following users have been cc'd because of the following labels:

  • fitzgen: fuzzing

To subscribe or unsubscribe from this label, edit the .github/subscribe-to-label.json configuration file.

Learn more.

Copy link
Member

@fitzgen fitzgen left a 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!

Comment on lines 22 to 23
const STRUCT_BASE: u32 = 5;
const TYPED_FN_BASE: u32 = 4;
Copy link
Member

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; //
Copy link
Member

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?

Comment on lines 264 to 270
let name = format!("take_struct_{concrete}");
typed_names.push(name);
imports.import(
"",
typed_names.last().unwrap().as_str(),
EntityType::Function(ty_idx),
);
Copy link
Member

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:

Suggested change
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);

Comment on lines 259 to 261
let mut typed_names: Vec<String> = Vec::new();

for i in 0..struct_count {
Copy link
Member

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.

Comment on lines 296 to 302
// 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);
Copy link
Member

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:

Suggested change
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.

Comment on lines 698 to 709
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));
Copy link
Member

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 calls and drops 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I agree!

@khagankhan
Copy link
Contributor Author

Thanks @fitzgen. Working on them!

@khagankhan
Copy link
Contributor Author

@fitzgen @eeide
Ready for review
Changes

  • Split table_ops.rs into separate modules and renamed to GcOps where applicable
  • Addressed prior review feedback
  • Ran the fuzzer for ~1 hour and fixed generation bugs discovered

@fitzgen
Copy link
Member

fitzgen commented Oct 20, 2025

@khagankhan can you delay the split into multiple modules for a follow up PR that contains nothing but the code motion? Otherwise it makes reading and interpreting the diff very hard, as a reviewer.

@khagankhan
Copy link
Contributor Author

@fitzgen I can do that. However, if you want I can point the code that needs review (that changed other than names)

@fitzgen
Copy link
Member

fitzgen commented Oct 20, 2025

I'd prefer to just review one change at a time instead of multiple things, so please split up the PR. Thanks!

@khagankhan
Copy link
Contributor Author

Sure thing! o7

khan22 and others added 3 commits October 23, 2025 16:42
This reverts commit 0634e51.
…Update TableOps to GcOps as well as other related methods or DSs"

This reverts commit e1d2bd0.
@khagankhan
Copy link
Contributor Author

@fitzgen Reverted! Ready for review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fuzzing Issues related to our fuzzing infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants