Skip to content

Commit

Permalink
s390x: Add support for all remaining atomic operations (#3746)
Browse files Browse the repository at this point in the history
This adds support for all atomic operations that were unimplemented
so far in the s390x back end:
- atomic_rmw operations xchg, nand, smin, smax, umin, umax
- $I8 and $I16 versions of atomic_rmw and atomic_cas
- little endian versions of atomic_rmw and atomic_cas

All of these have to be implemented by a compare-and-swap loop;
and for the $I8 and $I16 versions the actual atomic instruction
needs to operate on the surrounding aligned 32-bit word.

Since we cannot emit new control flow during ISLE instruction
selection, these compare-and-swap loops are emitted as a single
meta-instruction to be expanded at emit time.

However, since there is a large number of different versions of
the loop required to implement all the above operations, I've
implemented a facility to allow specifying the loop bodies
from within ISLE after all, by creating a vector of MInst
structures that will be emitted as part of the meta-instruction.

There are still restrictions, in particular instructions that
are part of the loop body may not modify any virtual register.
But even so, this approach looks preferable to doing everything
in emit.rs.

A few instructions needed in those compare-and-swap loop bodies
were added as well, in particular the RxSBG family of instructions
as well as the LOAD REVERSED in-register byte-swap instructions.

This patch also adds filetest runtests to verify the semantics
of all operations, in particular the subword and little-endian
variants (those are currently only executed on s390x).
  • Loading branch information
uweigand authored Feb 8, 2022
1 parent 5cd97c0 commit 9c5c872
Show file tree
Hide file tree
Showing 21 changed files with 6,433 additions and 911 deletions.
2 changes: 0 additions & 2 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,6 @@ fn ignore(testsuite: &str, testname: &str, strategy: &str) -> bool {
// No simd support yet for s390x.
("simd", _) if platform_is_s390x() => return true,
("memory64", "simd") if platform_is_s390x() => return true,
// No full atomics support yet for s390x.
("memory64", "threads") if platform_is_s390x() => return true,
_ => {}
},
_ => panic!("unrecognized strategy"),
Expand Down
306 changes: 306 additions & 0 deletions cranelift/codegen/src/isa/s390x/inst.isle

Large diffs are not rendered by default.

117 changes: 117 additions & 0 deletions cranelift/codegen/src/isa/s390x/inst/emit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,28 @@ fn enc_rie_d(opcode: u16, r1: Reg, r3: Reg, i2: u16) -> [u8; 6] {
enc
}

/// RIEf-type instructions.
///
/// 47 39 35 31 23 15 7
/// opcode1 r1 r2 i3 i4 i5 opcode2
/// 40 36 32 24 16 8 0
///
fn enc_rie_f(opcode: u16, r1: Reg, r2: Reg, i3: u8, i4: u8, i5: u8) -> [u8; 6] {
let mut enc: [u8; 6] = [0; 6];
let opcode1 = ((opcode >> 8) & 0xff) as u8;
let opcode2 = (opcode & 0xff) as u8;
let r1 = machreg_to_gpr(r1) & 0x0f;
let r2 = machreg_to_gpr(r2) & 0x0f;

enc[0] = opcode1;
enc[1] = r1 << 4 | r2;
enc[2] = i3;
enc[3] = i4;
enc[4] = i5;
enc[5] = opcode2;
enc
}

/// RIEg-type instructions.
///
/// 47 39 35 31 15 7
Expand Down Expand Up @@ -1188,6 +1210,60 @@ impl MachInstEmit for Inst {
);
}

&Inst::RxSBG {
op,
rd,
rn,
start_bit,
end_bit,
rotate_amt,
} => {
let opcode = match op {
RxSBGOp::Insert => 0xec59, // RISBGN
RxSBGOp::And => 0xec54, // RNSBG
RxSBGOp::Or => 0xec56, // ROSBG
RxSBGOp::Xor => 0xec57, // RXSBG
};
put(
sink,
&enc_rie_f(
opcode,
rd.to_reg(),
rn,
start_bit,
end_bit,
(rotate_amt as u8) & 63,
),
);
}

&Inst::RxSBGTest {
op,
rd,
rn,
start_bit,
end_bit,
rotate_amt,
} => {
let opcode = match op {
RxSBGOp::And => 0xec54, // RNSBG
RxSBGOp::Or => 0xec56, // ROSBG
RxSBGOp::Xor => 0xec57, // RXSBG
_ => unreachable!(),
};
put(
sink,
&enc_rie_f(
opcode,
rd,
rn,
start_bit | 0x80,
end_bit,
(rotate_amt as u8) & 63,
),
);
}

&Inst::UnaryRR { op, rd, rn } => {
match op {
UnaryOp::Abs32 => {
Expand Down Expand Up @@ -1222,6 +1298,14 @@ impl MachInstEmit for Inst {
let opcode = 0xb9e1; // POPCNT
put(sink, &enc_rrf_cde(opcode, rd.to_reg(), rn, 8, 0));
}
UnaryOp::BSwap32 => {
let opcode = 0xb91f; // LRVR
put(sink, &enc_rre(opcode, rd.to_reg(), rn));
}
UnaryOp::BSwap64 => {
let opcode = 0xb90f; // LRVRG
put(sink, &enc_rre(opcode, rd.to_reg(), rn));
}
}
}

Expand Down Expand Up @@ -1406,6 +1490,39 @@ impl MachInstEmit for Inst {
state,
);
}
&Inst::Loop { ref body, cond } => {
// This sequence is *one* instruction in the vcode, and is expanded only here at
// emission time, because it requires branching to internal labels.
let loop_label = sink.get_label();
let done_label = sink.get_label();

// Emit label at the start of the loop.
sink.bind_label(loop_label);

for inst in (&body).into_iter() {
match &inst {
// Replace a CondBreak with a branch to done_label.
&Inst::CondBreak { cond } => {
let inst = Inst::OneWayCondBr {
target: done_label,
cond: *cond,
};
inst.emit(sink, emit_info, state);
}
_ => inst.emit(sink, emit_info, state),
};
}

let inst = Inst::OneWayCondBr {
target: loop_label,
cond,
};
inst.emit(sink, emit_info, state);

// Emit label at the end of the loop.
sink.bind_label(done_label);
}
&Inst::CondBreak { .. } => unreachable!(), // Only valid inside a Loop.
&Inst::AtomicCas32 { rd, rn, ref mem } | &Inst::AtomicCas64 { rd, rn, ref mem } => {
let (opcode_rs, opcode_rsy) = match self {
&Inst::AtomicCas32 { .. } => (Some(0xba), Some(0xeb14)), // CS(Y)
Expand Down
131 changes: 131 additions & 0 deletions cranelift/codegen/src/isa/s390x/inst/emit_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1478,6 +1478,24 @@ fn test_s390x_binemit() {
"B9E1801A",
"popcnt %r1, %r10, 8",
));
insns.push((
Inst::UnaryRR {
op: UnaryOp::BSwap32,
rd: writable_gpr(1),
rn: gpr(10),
},
"B91F001A",
"lrvr %r1, %r10",
));
insns.push((
Inst::UnaryRR {
op: UnaryOp::BSwap64,
rd: writable_gpr(1),
rn: gpr(10),
},
"B90F001A",
"lrvgr %r1, %r10",
));

insns.push((
Inst::CmpRR {
Expand Down Expand Up @@ -2410,6 +2428,91 @@ fn test_s390x_binemit() {
"srag %r4, %r5, 63(%r6)",
));

insns.push((
Inst::RxSBG {
op: RxSBGOp::Insert,
rd: writable_gpr(4),
rn: gpr(5),
start_bit: 8,
end_bit: 32,
rotate_amt: -16,
},
"EC4508203059",
"risbgn %r4, %r5, 8, 32, 48",
));
insns.push((
Inst::RxSBG {
op: RxSBGOp::And,
rd: writable_gpr(4),
rn: gpr(5),
start_bit: 8,
end_bit: 32,
rotate_amt: 63,
},
"EC4508203F54",
"rnsbg %r4, %r5, 8, 32, 63",
));
insns.push((
Inst::RxSBG {
op: RxSBGOp::Or,
rd: writable_gpr(4),
rn: gpr(5),
start_bit: 8,
end_bit: 32,
rotate_amt: 63,
},
"EC4508203F56",
"rosbg %r4, %r5, 8, 32, 63",
));
insns.push((
Inst::RxSBG {
op: RxSBGOp::Xor,
rd: writable_gpr(4),
rn: gpr(5),
start_bit: 8,
end_bit: 32,
rotate_amt: 63,
},
"EC4508203F57",
"rxsbg %r4, %r5, 8, 32, 63",
));
insns.push((
Inst::RxSBGTest {
op: RxSBGOp::And,
rd: gpr(4),
rn: gpr(5),
start_bit: 8,
end_bit: 32,
rotate_amt: 63,
},
"EC4588203F54",
"rnsbg %r4, %r5, 136, 32, 63",
));
insns.push((
Inst::RxSBGTest {
op: RxSBGOp::Or,
rd: gpr(4),
rn: gpr(5),
start_bit: 8,
end_bit: 32,
rotate_amt: 63,
},
"EC4588203F56",
"rosbg %r4, %r5, 136, 32, 63",
));
insns.push((
Inst::RxSBGTest {
op: RxSBGOp::Xor,
rd: gpr(4),
rn: gpr(5),
start_bit: 8,
end_bit: 32,
rotate_amt: 63,
},
"EC4588203F57",
"rxsbg %r4, %r5, 136, 32, 63",
));

insns.push((
Inst::AtomicRmw {
alu_op: ALUOp::Add32,
Expand Down Expand Up @@ -6699,6 +6802,34 @@ fn test_s390x_binemit() {
"jno 6 ; trap",
));

insns.push((
Inst::Loop {
body: vec![
Inst::CmpRR {
op: CmpOp::CmpS32,
rn: gpr(2),
rm: gpr(3),
},
Inst::CondBreak {
cond: Cond::from_mask(13),
},
Inst::AtomicCas32 {
rd: writable_gpr(4),
rn: gpr(5),
mem: MemArg::BXD12 {
base: gpr(6),
index: zero_reg(),
disp: UImm12::maybe_from_u64(0).unwrap(),
flags: MemFlags::trusted(),
},
},
],
cond: Cond::from_mask(6),
},
"1923C0D400000008BA456000C064FFFFFFFA",
"0: cr %r2, %r3 ; jgnh 1f ; cs %r4, %r5, 0(%r6) ; jglh 0b ; 1:",
));

insns.push((
Inst::FpuMove32 {
rd: writable_fpr(8),
Expand Down
Loading

0 comments on commit 9c5c872

Please sign in to comment.