Conversation
|
@fitzgen I know this maybe a lot here but what I need the most is to know whether this solution makes sense or do we need something else like maybe adding d PODs as GcOps and to Stack or something else. |
Subscribe to Label Actioncc @fitzgen DetailsThis 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 |
fitzgen
left a comment
There was a problem hiding this comment.
Thanks! Sorry that it took a little while to review this one.
| let idx = usize::try_from(ctx.rng().gen_u32()) | ||
| .expect("failed to convert rng output to usize") |
There was a problem hiding this comment.
This can just .unwrap(): usize::try_from(my_u32).unwrap() is common enough that it doesn't need a diagnostic message and it also shouldn't ever fail on our supported architectures anyway. Same with the other instances of this here.
| let idx = usize::try_from(ctx.rng().gen_u32()) | ||
| .expect("failed to convert rng output to usize") | ||
| % FieldType::ALL.len(); |
There was a problem hiding this comment.
Also this pattern is probably a little more straight forward as:
let idx = ctx.rng().gen_index(FieldType::ALL.len());
let idx = u32::try_from(idx).unwrap();And maybe even better deduplicated as a fn FieldType::random(rng: &mut mutatis::Rng) -> u32 helper function.
| composite_type: wasm_encoder::CompositeType { | ||
| inner: wasm_encoder::CompositeInnerType::Struct( | ||
| wasm_encoder::StructType { | ||
| fields: fields.into_boxed_slice(), |
There was a problem hiding this comment.
You should be able to just collect the fields into a Box<[_]> in the first place rather than collect and then call into_boxed_slice().
| GcOp::StructNew { | ||
| type_index, | ||
| externref_from_stack: consumed, | ||
| } |
There was a problem hiding this comment.
I'm a bit confused here: why are we special-casing externref-typed fields here? Why are they different from i32-typed fields, for example? In either case, we need an operand of each field's type so I don't understand why we're not handling them uniformly.
There was a problem hiding this comment.
My thinking was that reusing an externref or concretely typed references already on the stack may be more interesting. For example values from table.set or table.get. Since externref is GC related I wanted to distinguish it from something like i32.
P.S. I do not like special cases. If this is not useful we can synthesize externref too. We can also add them as StackTypes or GcOps. But this may just generate and discard values and waste mutation and execution cycles.
There was a problem hiding this comment.
I will choose the first one and address the feedback. I can make PRs in the future if this turns out to be non-ideal.
Update Field mutation randomness Update boxing in emitting binary
|
Thanks @fitzgen ! Ready for the next round of reviews |
Structs with fields
StructNew: Consumes
ExternRefvalues from the operand stack for ref-typed fields instead of always synthesizingref.null extern. POD fields still get default values. The consumed count is tracked inexternref_from_stackand used during encoding via scratch locals to interleave stack values with defaults in field declaration order. POD constants do not need to come from the stack since we can always synthesize them, butexternref(and concrete struct ref types in the future) should be consumed from the stack when available. This is implemented as a special case in the fixup loop rather than changing the#[operands()]attribute or introducing new stack value types, because the number of ref fields a struct will consume is not known statically since it depends on the struct type's field layout. The rest of the ops use static operand and result types so making them dynamic would be a larger refactor with little benefit. When the top of the abstract stack has consecutiveExternRefvalues, fixup pops them and records the count inexternref_from_stack, letting the encoding know how many live values to retrieve vialocal.set/local.getinstead of synthesizingref.null extern.StructSet: Consumes one
ExternReffrom the stack when the target mutable field is a ref type, using it as the field value instead ofref.null extern. During fixup, it finds the first mutable field starting fromfield_index(scanning forward, wrapping around) and sets it. if no mutable field exists, the struct ref is discarded.StructGet/StructSet null guards: Both now check for null refs before accessing fields (
ref.is_null+if/else), preventing null reference traps.Other changes
StructFieldmutations.+cc @fitzgen @eeide