I should probably spell out my thoughts here more. @arielb1 and I had a brief chat on IRC the other day and it seemed like we had slightly different thoughts so it’s worth discussing more widely. My general feeling is that we should expect to do optimizations on the MIR when it makes sense, and that after the static safety checking is done, we may want to edit the MIR in ways that would no longer type check (but which we believe to be sound).
I also think that, whenever it makes sense, we should do optimizations on the MIR rather than doing it “on the fly” during trans. I believe this will result in more readable code (because the MIR is simpler); more portable optimizations if we ever move beyond LLVM; and more debugable results, because we can easily dump the MIR at any point and observe what has been done (this is always hard with LLVM output, imo).
Some examples of things I would like to do on the MIR:
- Expose the dynamic drop flags as MIR temporaries. After borrowck is done, rewrite DROPs to drop only the paths they need to, and to take a boolean argument that is either a constant true or else a stack variable in the cases where dynamic drop is required.
- Constant propagation, particularly as it is useful for deciding what to promote (in LLVM) to static values etc.
- Removing temporaries and stripping out other assignments that serve no logical role (e.g., zero-sized types). However, we must be cautious around destructors here: that said, because calls to DROP and so forth are already inserted explicitly in the MIR, we may not have to worry in particular.
- example: rewriting
tmp0 = ..; tmp1 = ..; x = Foo { f1: tmp0, f2: tmp1 }; to x.f1 = ...; x.f2 = ...;
- Removing no-op coercions etc.