Currently, the SLS hardening pass is run before the machine outliner,
which means that the outliner creates new functions and calls which do
not have the SLS hardening applied.
The fix for this is to move the SLS passes to after the outliner, as has
recently been done for the return address signing pass.
This also avoids a bug where the SLS outliner emits code with
instructions after a return, which the outliner doesn't correctly
handle.
This results in the heuristics used by the outliner being wrong, since
it doesn't know about the extra instructions the SLS pass will add to
every call and return. That's not a correctness problem, so I'll update
them in a separate patch.
Previous summary:
The SLS hardening pass inserts barrier instructions after return
instructions, so it was causing the machine outliner to fail to notice
return blocks, so it could outline at places where LR is live. The fix
for this is to check all terminator instructions when deciding if a
block ends in a return, not just the least one.
I think there's a deeper issue that we don't correctly track the
liveness of LR around function returns. This appears to be deliberate,
according to the comment on RET_ReallyLR in AArch64InstrInfo.td. I tried
changing this to make LR an implicit operand of all returns, but haven't
found a way to do that which doesn't cause a large number of MIR
verifier failures.
I think there's also an issue here that the outliner can create code
which does not have the SLS barrier instructions after a tail-call.
@Kristof, do you know if the SLS pass could be moved to after the
outliner to fix this, or does it need to happen as early as it does?
I guess this change is a side-effect of moving the AArch64IndirectThunks pass later in the pipeline. But I cannot easily guess what exactly triggers this change in behavior. I wonder if you happen to know?