A FuncCursor works a like a layout cursor, but it holds a reference to
the entire function and lets you re-borrow the function reference.
Rewrite the dominator tree unit tests with a FuncCursor instead of a
layout cursor to demonstrate the difference. It avoids the constrained
lifetimes of the layout cursor in the tests.
Use an EncCursor instead of a layout cursor to keep track of the
current position in the function. Since the EncCursor holds a reference
to the whole IR function insteadof just the layout, we can rework how IR
borrowing works.
The Context data structure that's live during the spilling pass now owns
an EncCursor which in turn holds references to the function and ISA.
This means that we no longer need to pass around references to parts of
the ir::Function. We can no longer borrow any part of the IR function
across a context method call, but that turns out to be not necessary.
Add a new cursor module and define an EncCursor data type in it. An
EncCursor is a cursor that inserts instructions with a valid encoding
for the ISA. This is useful for passes generating code after
legalization.
Implement a builder interface via the new InstInserterBase trait such
that the EncCursor builders support with_result().
Use EncCursor in coalescing.rs instead of the layout cursor as a proof
of concept.
Leave the primary InstBuilderBase trait alone, but add an alternative
InstInserterBase trait that can be implemented instead by builders that
always allocate new instructions with dfg.make_inst().
Any implementation of InstInserterBase can be used as an instruction
builder by wrapping it in an InsertBuilder. The InsertBuilder type adds
additional functionality via the with_results() method which makes it
possible to override the result values on the instruction that is built.
The motivation for this shuffle is that the with_result() functionality
can now be reused by different kinds of instruction builders, as long as
they insert new instructions. So ReplaceBuilder doesn't get
with_results().
The Cursor navigation methods all just depend on the cursor's position
and layout reference. Make a CursorBase trait that provides access to
this information with methods and implement the navigation methods on
top of that.
This makes it possible to have multiple types implement the cursor
interface.
The dbg!() macro should behave like a function call in how it evaluates
its arguments, and captures by Rust closures are not fully compatible
with path-specific borrowing. Specifically:
let borrow = &mut obj.foo;
dbg!("{}", obj.bar);
would fail because the closure inside dbg!() would borrow all of obj
instead of just obj.foo.
Fix this by using the format_args!() macro to evaluate the dbg!()
arguments and produce an fmt::Arguments object which can be safely
passed to the thread-local closure for printing.
The arguments are still evaluated inside an if { .. } which
constant-folds away in release builds.