AArch64: avoid branches with explicit offsets at lowering stage.
In discussions with @bnjbvr, it came up that generating `OneWayCondBr`s with explicit, hardcoded PC-offsets as part of lowered instruction sequences is actually unsafe, because the register allocator *might* insert a spill or reload into the middle of our sequence. We were careful about this in some cases but somehow missed that it was a general restriction. Conceptually, all inter-instruction references should be via labels at the VCode level; explicit offsets are only ever known at emission time, and resolved by the `MachBuffer`. To allow for conditional trap checks without modifying the CFG (as seen by regalloc) during lowering, this PR instead adds a `TrapIf` pseudo-instruction that conditionally skips a single embedded trap instruction. It lowers to the same `condbr label ; trap ; label: ...` sequence, but without the hardcoded branch-target offset in the lowering code.
This commit is contained in:
@@ -1471,12 +1471,20 @@ impl MachInstEmit for Inst {
|
||||
}
|
||||
sink.put4(enc_jump26(0b000101, not_taken.as_offset26_or_zero()));
|
||||
}
|
||||
&Inst::OneWayCondBr { target, kind } => {
|
||||
&Inst::TrapIf { kind, trap_info } => {
|
||||
// condbr KIND, LABEL
|
||||
let off = sink.cur_offset();
|
||||
if let Some(l) = target.as_label() {
|
||||
sink.use_label_at_offset(off, l, LabelUse::Branch19);
|
||||
}
|
||||
sink.put4(enc_conditional_br(target, kind));
|
||||
let label = sink.get_label();
|
||||
sink.put4(enc_conditional_br(
|
||||
BranchTarget::Label(label),
|
||||
kind.invert(),
|
||||
));
|
||||
sink.use_label_at_offset(off, label, LabelUse::Branch19);
|
||||
// udf
|
||||
let trap = Inst::Udf { trap_info };
|
||||
trap.emit(sink, flags, state);
|
||||
// LABEL:
|
||||
sink.bind_label(label);
|
||||
}
|
||||
&Inst::IndirectBr { rn, .. } => {
|
||||
sink.put4(enc_br(rn));
|
||||
@@ -1515,6 +1523,17 @@ impl MachInstEmit for Inst {
|
||||
// emission time, because we cannot allow the regalloc to insert spills/reloads in
|
||||
// the middle; we depend on hardcoded PC-rel addressing below.
|
||||
|
||||
// Branch to default when condition code from prior comparison indicates.
|
||||
let br = enc_conditional_br(info.default_target, CondBrKind::Cond(Cond::Hs));
|
||||
// No need to inform the sink's branch folding logic about this branch, because it
|
||||
// will not be merged with any other branch, flipped, or elided (it is not preceded
|
||||
// or succeeded by any other branch). Just emit it with the label use.
|
||||
let default_br_offset = sink.cur_offset();
|
||||
if let BranchTarget::Label(l) = info.default_target {
|
||||
sink.use_label_at_offset(default_br_offset, l, LabelUse::Branch19);
|
||||
}
|
||||
sink.put4(br);
|
||||
|
||||
// Save index in a tmp (the live range of ridx only goes to start of this
|
||||
// sequence; rtmp1 or rtmp2 may overwrite it).
|
||||
let inst = Inst::gen_move(rtmp2, ridx, I64);
|
||||
|
||||
Reference in New Issue
Block a user