Skip to content

Commit

Permalink
Merge pull request #3717 from uweigand/s390x-branchtarget
Browse files Browse the repository at this point in the history
s390x: Refactor branch and jumptable emission
  • Loading branch information
cfallin authored Jan 25, 2022
2 parents 5fc01ba + cee00c6 commit ce63a11
Show file tree
Hide file tree
Showing 10 changed files with 731 additions and 870 deletions.
14 changes: 6 additions & 8 deletions cranelift/codegen/src/isa/s390x/inst.isle
Original file line number Diff line number Diff line change
Expand Up @@ -589,15 +589,15 @@

;; An unconditional branch.
(Jump
(dest BranchTarget))
(dest MachLabel))

;; A conditional branch. Contains two targets; at emission time, both are emitted, but
;; the MachBuffer knows to truncate the trailing branch if fallthrough. We optimize the
;; choice of taken/not_taken (inverting the branch polarity as needed) based on the
;; fallthrough at the time of lowering.
(CondBr
(taken BranchTarget)
(not_taken BranchTarget)
(taken MachLabel)
(not_taken MachLabel)
(cond Cond))

;; A conditional trap execute a `Trap` if the condition is true. This is
Expand All @@ -624,7 +624,7 @@
;;
;; See, e.g., the lowering of `trapif` (conditional trap) for an example.
(OneWayCondBr
(target BranchTarget)
(target MachLabel)
(cond Cond))

;; An indirect branch through a register, augmented with set of all
Expand All @@ -644,10 +644,8 @@
;; Jump-table sequence, as one compound instruction (see note in lower.rs
;; for rationale).
(JTSequence
(info BoxJTSequenceInfo)
(ridx Reg)
(rtmp1 WritableReg)
(rtmp2 WritableReg))
(targets VecMachLabel))

;; Load an inline symbol reference with RelocDistance::Far.
(LoadExtNameFar
Expand Down Expand Up @@ -680,8 +678,8 @@

(type BoxCallInfo (primitive BoxCallInfo))
(type BoxCallIndInfo (primitive BoxCallIndInfo))
(type MachLabel (primitive MachLabel))
(type VecMachLabel (primitive VecMachLabel))
(type BranchTarget (primitive BranchTarget))
(type BoxJTSequenceInfo (primitive BoxJTSequenceInfo))
(type BoxExternalName (primitive BoxExternalName))
(type ValueLabel (primitive ValueLabel))
Expand Down
54 changes: 2 additions & 52 deletions cranelift/codegen/src/isa/s390x/inst/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ pub enum MemArg {
},

/// PC-relative Reference to a label.
Label { target: BranchTarget },
Label { target: MachLabel },

/// PC-relative Reference to a near symbol.
Symbol {
Expand Down Expand Up @@ -182,47 +182,6 @@ impl Cond {
}
}

/// A branch target. Either unresolved (basic-block index) or resolved (offset
/// from end of current instruction).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BranchTarget {
/// An unresolved reference to a Label, as passed into
/// `lower_branch_group()`.
Label(MachLabel),
/// A fixed PC offset.
ResolvedOffset(i32),
}

impl BranchTarget {
/// Return the target's label, if it is a label-based target.
pub fn as_label(self) -> Option<MachLabel> {
match self {
BranchTarget::Label(l) => Some(l),
_ => None,
}
}

/// Return the target's offset, if specified, or zero if label-based.
pub fn as_ri_offset_or_zero(self) -> u16 {
let off = match self {
BranchTarget::ResolvedOffset(off) => off >> 1,
_ => 0,
};
assert!(off <= 0x7fff);
assert!(off >= -0x8000);
off as u16
}

/// Return the target's offset, if specified, or zero if label-based.
pub fn as_ril_offset_or_zero(self) -> u32 {
let off = match self {
BranchTarget::ResolvedOffset(off) => off >> 1,
_ => 0,
};
off as u32
}
}

impl PrettyPrint for MemArg {
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
match self {
Expand Down Expand Up @@ -270,7 +229,7 @@ impl PrettyPrint for MemArg {
}
}
}
&MemArg::Label { ref target } => target.show_rru(mb_rru),
&MemArg::Label { target } => target.to_string(),
&MemArg::Symbol {
ref name, offset, ..
} => format!("{} + {}", name, offset),
Expand Down Expand Up @@ -306,12 +265,3 @@ impl PrettyPrint for Cond {
s.to_string()
}
}

impl PrettyPrint for BranchTarget {
fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String {
match self {
&BranchTarget::Label(label) => format!("label{:?}", label.get()),
&BranchTarget::ResolvedOffset(off) => format!("{}", off),
}
}
}
130 changes: 35 additions & 95 deletions cranelift/codegen/src/isa/s390x/inst/emit.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! S390x ISA: binary code emission.
use crate::binemit::{Reloc, StackMap};
use crate::ir::condcodes::IntCC;
use crate::ir::MemFlags;
use crate::ir::{SourceLoc, TrapCode};
use crate::isa::s390x::inst::*;
Expand Down Expand Up @@ -153,14 +152,9 @@ pub fn mem_emit(
&enc_rxy(opcode_rxy.unwrap(), rd, base, index, disp.bits()),
);
}
&MemArg::Label { ref target } => {
if let Some(l) = target.as_label() {
sink.use_label_at_offset(sink.cur_offset(), l, LabelUse::BranchRIL);
}
put(
sink,
&enc_ril_b(opcode_ril.unwrap(), rd, target.as_ril_offset_or_zero()),
);
&MemArg::Label { target } => {
sink.use_label_at_offset(sink.cur_offset(), target, LabelUse::BranchRIL);
put(sink, &enc_ril_b(opcode_ril.unwrap(), rd, 0));
}
&MemArg::Symbol {
ref name, offset, ..
Expand Down Expand Up @@ -1904,60 +1898,43 @@ impl MachInstEmit for Inst {
&Inst::EpiloguePlaceholder => {
// Noop; this is just a placeholder for epilogues.
}
&Inst::Jump { ref dest } => {
&Inst::Jump { dest } => {
let off = sink.cur_offset();
// Indicate that the jump uses a label, if so, so that a fixup can occur later.
if let Some(l) = dest.as_label() {
sink.use_label_at_offset(off, l, LabelUse::BranchRIL);
sink.add_uncond_branch(off, off + 6, l);
}
sink.use_label_at_offset(off, dest, LabelUse::BranchRIL);
sink.add_uncond_branch(off, off + 6, dest);
// Emit the jump itself.
let opcode = 0xc04; // BCRL
put(sink, &enc_ril_c(opcode, 15, dest.as_ril_offset_or_zero()));
put(sink, &enc_ril_c(opcode, 15, 0));
}
&Inst::IndirectBr { rn, .. } => {
let opcode = 0x07; // BCR
put(sink, &enc_rr(opcode, gpr(15), rn));
}
&Inst::CondBr {
ref taken,
ref not_taken,
taken,
not_taken,
cond,
} => {
let opcode = 0xc04; // BCRL

// Conditional part first.
let cond_off = sink.cur_offset();
if let Some(l) = taken.as_label() {
sink.use_label_at_offset(cond_off, l, LabelUse::BranchRIL);
let inverted = &enc_ril_c(opcode, cond.invert().bits(), 0);
sink.add_cond_branch(cond_off, cond_off + 6, l, inverted);
}
put(
sink,
&enc_ril_c(opcode, cond.bits(), taken.as_ril_offset_or_zero()),
);
sink.use_label_at_offset(cond_off, taken, LabelUse::BranchRIL);
let inverted = &enc_ril_c(opcode, cond.invert().bits(), 0);
sink.add_cond_branch(cond_off, cond_off + 6, taken, inverted);
put(sink, &enc_ril_c(opcode, cond.bits(), 0));

// Unconditional part next.
let uncond_off = sink.cur_offset();
if let Some(l) = not_taken.as_label() {
sink.use_label_at_offset(uncond_off, l, LabelUse::BranchRIL);
sink.add_uncond_branch(uncond_off, uncond_off + 6, l);
}
put(
sink,
&enc_ril_c(opcode, 15, not_taken.as_ril_offset_or_zero()),
);
sink.use_label_at_offset(uncond_off, not_taken, LabelUse::BranchRIL);
sink.add_uncond_branch(uncond_off, uncond_off + 6, not_taken);
put(sink, &enc_ril_c(opcode, 15, 0));
}
&Inst::OneWayCondBr { ref target, cond } => {
&Inst::OneWayCondBr { target, cond } => {
let opcode = 0xc04; // BCRL
if let Some(l) = target.as_label() {
sink.use_label_at_offset(sink.cur_offset(), l, LabelUse::BranchRIL);
}
put(
sink,
&enc_ril_c(opcode, cond.bits(), target.as_ril_offset_or_zero()),
);
sink.use_label_at_offset(sink.cur_offset(), target, LabelUse::BranchRIL);
put(sink, &enc_ril_c(opcode, cond.bits(), 0));
}
&Inst::Nop0 => {}
&Inst::Nop2 => {
Expand All @@ -1984,86 +1961,49 @@ impl MachInstEmit for Inst {
let srcloc = state.cur_srcloc();
put_with_trap(sink, &enc_e(0x0000), srcloc, trap_code);
}
&Inst::JTSequence {
ridx,
rtmp1,
rtmp2,
ref info,
..
} => {
&Inst::JTSequence { ridx, ref targets } => {
let table_label = sink.get_label();

// This sequence is *one* instruction in the vcode, and is expanded only here at
// emission time, because we cannot allow the regalloc to insert spills/reloads in
// the middle; we depend on hardcoded PC-rel addressing below.

// Bounds-check index and branch to default.
let inst = Inst::CmpRUImm32 {
op: CmpOp::CmpL64,
rn: ridx,
imm: info.targets.len() as u32,
};
inst.emit(sink, emit_info, state);
let inst = Inst::OneWayCondBr {
target: info.default_target,
cond: Cond::from_intcc(IntCC::UnsignedGreaterThanOrEqual),
};
inst.emit(sink, emit_info, state);

// Set rtmp2 to index scaled by entry size.
let inst = Inst::ShiftRR {
shift_op: ShiftOp::LShL64,
rd: rtmp2,
rn: ridx,
shift_imm: 2,
shift_reg: zero_reg(),
};
inst.emit(sink, emit_info, state);

// Set rtmp1 to address of jump table.
// Set temp register to address of jump table.
let rtmp = writable_spilltmp_reg();
let inst = Inst::LoadAddr {
rd: rtmp1,
rd: rtmp,
mem: MemArg::Label {
target: BranchTarget::Label(table_label),
target: table_label,
},
};
inst.emit(sink, emit_info, state);

// Set rtmp2 to value loaded out of jump table.
let inst = Inst::Load64SExt32 {
rd: rtmp2,
mem: MemArg::reg_plus_reg(rtmp1.to_reg(), rtmp2.to_reg(), MemFlags::trusted()),
};
inst.emit(sink, emit_info, state);

// Set rtmp1 to target address (rtmp1 + rtmp2).
let inst = Inst::AluRRR {
alu_op: ALUOp::Add64,
rd: rtmp1,
rn: rtmp1.to_reg(),
rm: rtmp2.to_reg(),
// Set temp to target address by adding the value of the jump table entry.
let inst = Inst::AluRX {
alu_op: ALUOp::Add64Ext32,
rd: rtmp,
mem: MemArg::reg_plus_reg(rtmp.to_reg(), ridx, MemFlags::trusted()),
};
inst.emit(sink, emit_info, state);

// Branch to computed address. (`targets` here is only used for successor queries
// and is not needed for emission.)
let inst = Inst::IndirectBr {
rn: rtmp1.to_reg(),
rn: rtmp.to_reg(),
targets: vec![],
};
inst.emit(sink, emit_info, state);

// Emit jump table (table of 32-bit offsets).
// The first entry is the default target, which is not emitted
// into the jump table, so we skip it here. It is only in the
// list so MachTerminator will see the potential target.
sink.bind_label(table_label);
let jt_off = sink.cur_offset();
for &target in info.targets.iter() {
for &target in targets.iter().skip(1) {
let word_off = sink.cur_offset();
let off_into_table = word_off - jt_off;
sink.use_label_at_offset(
word_off,
target.as_label().unwrap(),
LabelUse::PCRel32,
);
sink.use_label_at_offset(word_off, target, LabelUse::PCRel32);
sink.put4(off_into_table.swap_bytes());
}

Expand Down
Loading

0 comments on commit ce63a11

Please sign in to comment.