Skip to content

Conversation

@michelledaviest
Copy link
Contributor

Added trait PrintDecorator to add hooks to print information after inst, before and after blocks and before and after functions. Added default implementations for these hooks so Waffle's display function output doesn't change with this addition. I had all internal waffle logging calls to display take in a NOPPrintDecorator that does not print anything.

Copy link
Member

@cfallin cfallin left a comment

Choose a reason for hiding this comment

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

Thanks for this! This is basically what I had imagined, with one detail changed: I don't think we'll want a "no-op decorator", but instead let's make the distinction at the API level (and leave the existing methods unchanged). Otherwise looks great. Thanks!

/// Hooks to print information after inst, before and after blocks
/// and before and after functions.
pub trait PrintDecorator {
fn after_inst(&self, _value: super::Value, _f: &mut fmt::Formatter) -> fmt::Result {
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 a tiny bit silly in this case (since the purpose is fairly apparent) but I like to stick to a style of mandatory doc comments on all items that are pub -- could we add those here too? Something like:

/// Print arbitrary text after an instruction.
///
/// Invoked after every instruction in a block. The instruction has already been printed on its own line; this method can print content on the line below the operator, if desired.

and likewise below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

For clarity, the after_inst instruction prints information on the same line as the operator after the operator has been printed.

pub(crate) indent: &'a str,
pub(crate) verbose: bool,
pub(crate) module: Option<&'a Module<'a>>,
pub(crate) decorator: &'a PD,
Copy link
Member

Choose a reason for hiding this comment

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

Here, Option<&'a PD> if we do as above

.collect::<Vec<_>>()
.join(", "),
)?;
writeln!(f, "")?;
Copy link
Member

Choose a reason for hiding this comment

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

any particular reason to split out the newline like this?

Copy link
Member

Choose a reason for hiding this comment

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

(looking below: ah, is a decorator call missing? or is this for symmetry with the below?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was a change leftover from refactoring. I've changed it to a writeln! call.

Copy link
Member

@cfallin cfallin left a comment

Choose a reason for hiding this comment

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

Thanks! This is close -- just a few more tweaks!

new_body.recompute_edges();

log::trace!("After duplication:\n{}\n", new_body.display("| ", None));
log::trace!("After duplication:\n{}\n", new_body.display("| ", None,));
Copy link
Member

Choose a reason for hiding this comment

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

Spurious comma here? (Syntactically valid but probably an artifact of refactor)

src/ir/module.rs Outdated
/// printing is described in Decorator.
pub fn display_with_decorators<'b, PD: PrintDecorator>(
&'b self,
decorators: HashMap<Func, &'b PD>,
Copy link
Member

Choose a reason for hiding this comment

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

I just realized there's a cleaner way to do this that doesn't require allocating a HashMap up front and passing ownership -- could we instead parameterize also on F: FnMut(Func) -> PD and take f: F?

In other words I think it might be good to make this generic with a callback that the caller provides to get the decorator for any given function; FnMut so it can lazily create it if needed. And would prefer to return a PD by value but feel free to do Cow<'b, PD> instead to give the caller the option of either by-value or by-borrow; I don't want the API to require one to create and store these elsewhere though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can't have it be a FnMut since Display only takes in a &self.
So my ModuleDisplay now looks like below. I made the function return an Option so I could pass in None for all functions in the normal display function.

pub struct ModuleDisplay<'a, PD: PrintDecorator, F: Fn(Func) -> Option<PD>> {
    pub(crate) module: &'a Module<'a>,
    pub(crate) decorators: F,
}

However, I'm having trouble implementing the display function for Module in a way that doesn't expose these type parameters to the user. Ideally I am able to have both the PrintDecorator and function returning the decorator be impl, like so:

pub fn display<'b>(
    &'b self,
) -> ModuleDisplay<'b, impl PrintDecorator, impl Fn(Func) -> Option<impl PrintDecorator>>
where
    'b: 'a,
{
    ModuleDisplay::<NOPPrintDecorator, dyn Fn(Func) -> Option<NOPPrintDecorator>> {
        module: self,
        decorators: |x: Func| None,
    }
}

However, this doesn't work since the trait Sized is not implemented for dyn Fn(ir::Func) -> std::option::Option<display::NOPPrintDecorator>. I am able to get no type errors by exposing the generics to the user but ideally we don't want that...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The display_with_decorator looks like this:

pub fn display_with_decorator<'b, PD: PrintDecorator, F: Fn(Func) -> Option<PD>>(
    &'b self,
    decorators: F,
) -> ModuleDisplay<'b, PD, F>
where
   'b: 'a,
{
    ModuleDisplay {
        module: self,
        decorators: decorators,
    }
}

Copy link
Member

Choose a reason for hiding this comment

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

Does the first variant above work without the type ascription (ModuleDisplay { ... } expression only)? Or alternately, impl Fn rather than dyn Fn?

We're definitely getting into the weeds of the Rust type system here but there has to be a way to make this work!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So impl Fn doesn't work because impl isn't allowed in paths (? idk what a path is). impl Trait is only allowed in arguments and return types of functions and methods.

Removing the type ascription works, however, we now get the error expected Option<impl PrintDecorator>, found Option<dyn PrintDecorator>. I think the problem is that it doesn't know that the function we're passing in will return a PrintDecorator which we have already impl'd.

pub fn display<'b>(
    &'b self,
) -> ModuleDisplay<'b, impl PrintDecorator, impl Fn(Func) -> Option<PrintDecorator>>
where
    'b: 'a,
{
    ModuleDisplay {
        module: self,
        decorators: |x: Func| -> Option<NOPPrintDecorator> { None },
    }
}  

Copy link
Member

Choose a reason for hiding this comment

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

OK, one more idea: can we use a Box<dyn Fn(...) -> ...> type for decorators and avoid the type parameter in ModuleDisplay altogether?

Copy link
Member

Choose a reason for hiding this comment

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

(or Option<Box<dyn Fn...>> if we want to avoid the allocation in the common case)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It works!! Or at least it type checks. I'll push after I test it against my solver.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Verified this works with my generators.

@cfallin cfallin merged commit 3377199 into bytecodealliance:main Nov 25, 2024
3 checks passed
@cfallin
Copy link
Member

cfallin commented Nov 25, 2024

@michelledaviest do you need a point-release for this or are you working off a fork or repo internally? (It's easy to do, just curious about urgency)

@michelledaviest
Copy link
Contributor Author

I'm working off this fork so no rush

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