Skip to content

Commit c8435f8

Browse files
abrownjlb6740
andcommitted
asm: implement write-only operands
This change adds support to the new assembler for write-only operands. This implementation appeared first in [bytecodealliance#10754] but is split out here to unblock implementation of instructions that require it: multiplication, conversions, moves, etc. This starts roughly the same as what was implemented for write-only XMMs in [bytecodealliance#10754] but includes support for write-only GPRs as well and generates the temporary registers which are needed. [bytecodealliance#10754]: bytecodealliance#10754 Co-authored-by: Johnnie Birch <[email protected]>
1 parent 35053d6 commit c8435f8

File tree

9 files changed

+130
-32
lines changed

9 files changed

+130
-32
lines changed

cranelift/assembler-x64/meta/src/dsl.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ pub use encoding::{
1313
Encoding, Group1Prefix, Group2Prefix, Group3Prefix, Group4Prefix, Opcodes, Prefixes, Rex,
1414
};
1515
pub use features::{Feature, Features, ALL_FEATURES};
16-
pub use format::{align, fmt, r, rw, sxl, sxq, sxw};
16+
pub use format::{align, fmt, r, rw, sxl, sxq, sxw, w};
1717
pub use format::{Extension, Format, Location, Mutability, Operand, OperandKind};
1818

1919
/// Abbreviated constructor for an x64 instruction.

cranelift/assembler-x64/meta/src/dsl/format.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ pub fn r(op: impl Into<Operand>) -> Operand {
5252
op
5353
}
5454

55+
/// An abbreviated constructor for a "write" operand.
56+
#[must_use]
57+
pub fn w(location: Location) -> Operand {
58+
Operand {
59+
location,
60+
mutability: Mutability::Write,
61+
extension: Extension::None,
62+
align: false,
63+
}
64+
}
65+
5566
/// An abbreviated constructor for a memory operand that requires alignment.
5667
pub fn align(location: Location) -> Operand {
5768
assert!(location.uses_memory());
@@ -423,6 +434,7 @@ pub enum OperandKind {
423434
pub enum Mutability {
424435
Read,
425436
ReadWrite,
437+
Write,
426438
}
427439

428440
impl Mutability {
@@ -432,6 +444,7 @@ impl Mutability {
432444
pub fn is_read(&self) -> bool {
433445
match self {
434446
Mutability::Read | Mutability::ReadWrite => true,
447+
Mutability::Write => false,
435448
}
436449
}
437450

@@ -441,7 +454,7 @@ impl Mutability {
441454
pub fn is_write(&self) -> bool {
442455
match self {
443456
Mutability::Read => false,
444-
Mutability::ReadWrite => true,
457+
Mutability::ReadWrite | Mutability::Write => true,
445458
}
446459
}
447460
}
@@ -457,6 +470,7 @@ impl core::fmt::Display for Mutability {
457470
match self {
458471
Self::Read => write!(f, "r"),
459472
Self::ReadWrite => write!(f, "rw"),
473+
Self::Write => write!(f, "w"),
460474
}
461475
}
462476
}

cranelift/assembler-x64/meta/src/generate/operand.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ impl dsl::Mutability {
8888
match self {
8989
dsl::Mutability::Read => "Read",
9090
dsl::Mutability::ReadWrite => "ReadWrite",
91+
dsl::Mutability::Write => "Write",
9192
}
9293
}
9394

@@ -96,6 +97,7 @@ impl dsl::Mutability {
9697
match self {
9798
dsl::Mutability::Read => "read",
9899
dsl::Mutability::ReadWrite => "read_write",
100+
dsl::Mutability::Write => "write",
99101
}
100102
}
101103
}

cranelift/assembler-x64/src/api.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,17 @@ pub trait Registers {
111111
/// An x64 general purpose register that may be read and written.
112112
type ReadWriteGpr: AsReg;
113113

114+
/// An x64 general purpose register that may be written.
115+
type WriteGpr: AsReg;
116+
114117
/// An x64 SSE register that may be read.
115118
type ReadXmm: AsReg;
116119

117120
/// An x64 SSE register that may be read and written.
118121
type ReadWriteXmm: AsReg;
122+
123+
/// An x64 SSE register that may be written.
124+
type WriteXmm: AsReg;
119125
}
120126

121127
/// Describe how to interact with an external register type.
@@ -159,20 +165,33 @@ pub trait RegisterVisitor<R: Registers> {
159165
fn read_gpr(&mut self, reg: &mut R::ReadGpr);
160166
/// Visit a read-write register.
161167
fn read_write_gpr(&mut self, reg: &mut R::ReadWriteGpr);
168+
/// Visit a write-only register.
169+
fn write_gpr(&mut self, reg: &mut R::WriteGpr);
170+
162171
/// Visit a read-only fixed register; this register can be modified in-place
163172
/// but must emit as the hardware encoding `enc`.
164173
fn fixed_read_gpr(&mut self, reg: &mut R::ReadGpr, enc: u8);
165174
/// Visit a read-write fixed register; this register can be modified
166175
/// in-place but must emit as the hardware encoding `enc`.
167176
fn fixed_read_write_gpr(&mut self, reg: &mut R::ReadWriteGpr, enc: u8);
177+
/// Visit a write-only fixed register; this register can be modified
178+
/// in-place but must emit as the hardware encoding `enc`.
179+
fn fixed_write_gpr(&mut self, reg: &mut R::WriteGpr, enc: u8);
180+
168181
/// Visit a read-only SSE register.
169182
fn read_xmm(&mut self, reg: &mut R::ReadXmm);
170183
/// Visit a read-write SSE register.
171184
fn read_write_xmm(&mut self, reg: &mut R::ReadWriteXmm);
185+
/// Visit a write-only SSE register.
186+
fn write_xmm(&mut self, reg: &mut R::WriteXmm);
187+
172188
/// Visit a read-only fixed SSE register; this register can be modified
173189
/// in-place but must emit as the hardware encoding `enc`.
174190
fn fixed_read_xmm(&mut self, reg: &mut R::ReadXmm, enc: u8);
175191
/// Visit a read-write fixed SSE register; this register can be modified
176192
/// in-place but must emit as the hardware encoding `enc`.
177193
fn fixed_read_write_xmm(&mut self, reg: &mut R::ReadWriteXmm, enc: u8);
194+
/// Visit a read-only fixed SSE register; this register can be modified
195+
/// in-place but must emit as the hardware encoding `enc`.
196+
fn fixed_write_xmm(&mut self, reg: &mut R::WriteXmm, enc: u8);
178197
}

cranelift/assembler-x64/src/fuzz.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,10 @@ pub struct FuzzRegs;
175175
impl Registers for FuzzRegs {
176176
type ReadGpr = FuzzReg;
177177
type ReadWriteGpr = FuzzReg;
178+
type WriteGpr = FuzzReg;
178179
type ReadXmm = FuzzReg;
179180
type ReadWriteXmm = FuzzReg;
181+
type WriteXmm = FuzzReg;
180182
}
181183

182184
/// A simple `u8` register type for fuzzing only.
@@ -240,8 +242,10 @@ pub trait RegistersArbitrary:
240242
Registers<
241243
ReadGpr: for<'a> Arbitrary<'a>,
242244
ReadWriteGpr: for<'a> Arbitrary<'a>,
245+
WriteGpr: for<'a> Arbitrary<'a>,
243246
ReadXmm: for<'a> Arbitrary<'a>,
244247
ReadWriteXmm: for<'a> Arbitrary<'a>,
248+
WriteXmm: for<'a> Arbitrary<'a>,
245249
>
246250
{
247251
}
@@ -251,8 +255,10 @@ where
251255
R: Registers,
252256
R::ReadGpr: for<'a> Arbitrary<'a>,
253257
R::ReadWriteGpr: for<'a> Arbitrary<'a>,
258+
R::WriteGpr: for<'a> Arbitrary<'a>,
254259
R::ReadXmm: for<'a> Arbitrary<'a>,
255260
R::ReadWriteXmm: for<'a> Arbitrary<'a>,
261+
R::WriteXmm: for<'a> Arbitrary<'a>,
256262
{
257263
}
258264

cranelift/assembler-x64/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
//! impl Registers for Regs {
1414
//! type ReadGpr = u8;
1515
//! type ReadWriteGpr = u8;
16+
//! type WriteGpr = u8;
1617
//! type ReadXmm = u8;
1718
//! type ReadWriteXmm = u8;
19+
//! type WriteXmm = u8;
1820
//! }
1921
//!
2022
//! // Then, build one of the `AND` instructions; this one operates on an

cranelift/codegen/meta/src/gen_asm.rs

Lines changed: 50 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
//! Generate the Cranelift-specific integration of the x64 assembler.
22
3-
use cranelift_assembler_x64_meta::dsl::{Format, Inst, Mutability, Operand, OperandKind};
3+
use cranelift_assembler_x64_meta::dsl::{
4+
format::RegClass, Format, Inst, Mutability, Operand, OperandKind,
5+
};
46
use cranelift_srcgen::{fmtln, Formatter};
57

8+
/// This factors out use of the assembler crate name.
9+
const ASM: &str = "cranelift_assembler_x64";
10+
611
/// Returns the Rust type used for the `IsleConstructorRaw` variants.
712
pub fn rust_param_raw(op: &Operand) -> String {
813
match op.location.kind() {
@@ -38,34 +43,42 @@ pub fn rust_convert_isle_to_assembler(op: &Operand) -> String {
3843
} else {
3944
"Imm"
4045
};
41-
format!("cranelift_assembler_x64::{ty}{bits}::new")
46+
format!("cranelift_assembler_x64::{ty}{bits}::new({loc})")
4247
}
4348
OperandKind::FixedReg(r) => {
4449
let reg = r.reg_class().unwrap().to_string().to_lowercase();
4550
match op.mutability {
46-
Mutability::Read => "cranelift_assembler_x64::Fixed".to_string(),
51+
Mutability::Read => format!("{ASM}::Fixed({r})"),
52+
Mutability::Write => {
53+
format!("{ASM}::Fixed(self.temp_writable_{reg}())")
54+
}
4755
Mutability::ReadWrite => {
48-
format!("self.convert_{reg}_to_assembler_fixed_read_write_{reg}")
56+
format!("self.convert_{reg}_to_assembler_fixed_read_write_{reg}({r})")
4957
}
5058
}
5159
}
5260
OperandKind::Reg(r) => {
5361
let reg = r.reg_class().unwrap();
5462
let reg_lower = reg.to_string().to_lowercase();
5563
match op.mutability {
56-
Mutability::Read => format!("cranelift_assembler_x64::{reg}::new"),
64+
Mutability::Read => {
65+
format!("{ASM}::{reg}::new({r})")
66+
}
67+
Mutability::Write => {
68+
format!("{ASM}::{reg}::new(self.temp_writable_{reg_lower}())")
69+
}
5770
Mutability::ReadWrite => {
58-
format!("self.convert_{reg_lower}_to_assembler_read_write_{reg_lower}")
71+
format!("self.convert_{reg_lower}_to_assembler_read_write_{reg_lower}({r})")
5972
}
6073
}
6174
}
62-
OperandKind::RegMem(r) => {
63-
let reg = r.reg_class().unwrap().to_string().to_lowercase();
75+
OperandKind::RegMem(rm) => {
76+
let reg = rm.reg_class().unwrap().to_string().to_lowercase();
6477
let mut_ = op.mutability.generate_snake_case();
6578
let align = if op.align { "_aligned" } else { "" };
66-
format!("self.convert_{reg}_mem_to_assembler_{mut_}_{reg}_mem{align}")
79+
format!("self.convert_{reg}_mem_to_assembler_{mut_}_{reg}_mem{align}({rm})")
6780
}
68-
OperandKind::Mem(_) => "self.convert_amode_to_assembler_amode".to_string(),
81+
OperandKind::Mem(mem) => format!("self.convert_amode_to_assembler_amode({mem})"),
6982
}
7083
}
7184

@@ -76,39 +89,35 @@ pub fn rust_convert_isle_to_assembler(op: &Operand) -> String {
7689
/// This function panics if the instruction has no operands.
7790
pub fn generate_macro_inst_fn(f: &mut Formatter, inst: &Inst) {
7891
let struct_name = inst.name();
79-
let params = inst
80-
.format
81-
.operands
82-
.iter()
83-
.filter(|o| o.mutability.is_read())
84-
.collect::<Vec<_>>();
92+
let operands = inst.format.operands.iter().collect::<Vec<_>>();
8593
let results = inst
8694
.format
8795
.operands
8896
.iter()
8997
.filter(|o| o.mutability.is_write())
9098
.collect::<Vec<_>>();
91-
let rust_params = params
99+
let rust_params = operands
92100
.iter()
101+
.filter(|o| o.mutability.is_read())
93102
.map(|o| format!("{}: {}", o.location, rust_param_raw(o)))
94103
.collect::<Vec<_>>()
95104
.join(", ");
96105
f.add_block(
97106
&format!("fn x64_{struct_name}_raw(&mut self, {rust_params}) -> AssemblerOutputs"),
98107
|f| {
99-
for o in params.iter() {
100-
let l = o.location;
101-
let cvt = rust_convert_isle_to_assembler(o);
102-
fmtln!(f, "let {l} = {cvt}({l});");
108+
for op in operands.iter() {
109+
let loc = op.location;
110+
let cvt = rust_convert_isle_to_assembler(op);
111+
fmtln!(f, "let {loc} = {cvt};");
103112
}
104-
let args = params
113+
let args = operands
105114
.iter()
106115
.map(|o| format!("{}.clone()", o.location))
107116
.collect::<Vec<_>>();
108117
let args = args.join(", ");
109118
fmtln!(
110119
f,
111-
"let inst = cranelift_assembler_x64::inst::{struct_name}::new({args}).into();"
120+
"let inst = {ASM}::inst::{struct_name}::new({args}).into();"
112121
);
113122
fmtln!(f, "let inst = MInst::External {{ inst }};");
114123

@@ -117,6 +126,17 @@ pub fn generate_macro_inst_fn(f: &mut Formatter, inst: &Inst) {
117126
[] => fmtln!(f, "SideEffectNoResult::Inst(inst)"),
118127
[one] => match one.mutability {
119128
Read => unreachable!(),
129+
Write => match one.location.kind() {
130+
// One write-only register output? Output the
131+
// instruction and that register.
132+
OperandKind::Reg(r) => {
133+
let ty = r.reg_class().unwrap().to_string();
134+
let var = ty.to_lowercase();
135+
fmtln!(f, "let {var} = {r}.as_ref().clone();");
136+
fmtln!(f, "AssemblerOutputs::Ret{ty} {{ inst, {var} }}");
137+
}
138+
_ => unimplemented!(),
139+
},
120140
ReadWrite => match one.location.kind() {
121141
OperandKind::Imm(_) => unreachable!(),
122142
// One read/write register output? Output the instruction
@@ -290,21 +310,21 @@ pub fn isle_constructors(format: &Format) -> Vec<IsleConstructor> {
290310
[] => unimplemented!("if you truly need this (and not a `SideEffect*`), add a `NoReturn` variant to `AssemblerOutputs`"),
291311
[one] => match one.mutability {
292312
Read => unreachable!(),
293-
ReadWrite => match one.location.kind() {
313+
ReadWrite | Write => match one.location.kind() {
294314
Imm(_) => unreachable!(),
295315
// One read/write register output? Output the instruction
296316
// and that register.
297-
Reg(r) | FixedReg(r) => match r.bits() {
298-
128 => vec![IsleConstructor::RetXmm],
299-
_ => vec![IsleConstructor::RetGpr],
317+
Reg(r) | FixedReg(r) => match r.reg_class().unwrap() {
318+
RegClass::Xmm => vec![IsleConstructor::RetXmm],
319+
RegClass::Gpr => vec![IsleConstructor::RetGpr],
300320
},
301321
// One read/write memory operand? Output a side effect.
302322
Mem(_) => vec![IsleConstructor::RetMemorySideEffect],
303323
// One read/write reg-mem output? We need constructors for
304324
// both variants.
305-
RegMem(rm) => match rm.bits() {
306-
128 => vec![IsleConstructor::RetXmm, IsleConstructor::RetMemorySideEffect],
307-
_ => vec![IsleConstructor::RetGpr, IsleConstructor::RetMemorySideEffect],
325+
RegMem(rm) => match rm.reg_class().unwrap() {
326+
RegClass::Xmm => vec![IsleConstructor::RetXmm, IsleConstructor::RetMemorySideEffect],
327+
RegClass::Gpr => vec![IsleConstructor::RetGpr, IsleConstructor::RetMemorySideEffect],
308328
},
309329
}
310330
},

cranelift/codegen/src/isa/x64/inst/external.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,10 @@ pub struct CraneliftRegisters;
1515
impl asm::Registers for CraneliftRegisters {
1616
type ReadGpr = Gpr;
1717
type ReadWriteGpr = PairedGpr;
18+
type WriteGpr = WritableGpr;
1819
type ReadXmm = Xmm;
1920
type ReadWriteXmm = PairedXmm;
21+
type WriteXmm = WritableXmm;
2022
}
2123

2224
/// A pair of registers, one for reading and one for writing.
@@ -288,6 +290,10 @@ impl<'a, T: OperandVisitor> asm::RegisterVisitor<CraneliftRegisters> for Regallo
288290
self.collector.reg_reuse_def(write, 0);
289291
}
290292

293+
fn write_gpr(&mut self, reg: &mut WritableGpr) {
294+
self.collector.reg_def(reg);
295+
}
296+
291297
fn fixed_read_gpr(&mut self, reg: &mut Gpr, enc: u8) {
292298
self.collector
293299
.reg_fixed_use(reg, fixed_reg(enc, RegClass::Int));
@@ -301,6 +307,11 @@ impl<'a, T: OperandVisitor> asm::RegisterVisitor<CraneliftRegisters> for Regallo
301307
.reg_fixed_def(write, fixed_reg(enc, RegClass::Int));
302308
}
303309

310+
fn fixed_write_gpr(&mut self, reg: &mut WritableGpr, enc: u8) {
311+
self.collector
312+
.reg_fixed_def(reg, fixed_reg(enc, RegClass::Int));
313+
}
314+
304315
fn read_xmm(&mut self, reg: &mut Xmm) {
305316
self.collector.reg_use(reg);
306317
}
@@ -311,6 +322,10 @@ impl<'a, T: OperandVisitor> asm::RegisterVisitor<CraneliftRegisters> for Regallo
311322
self.collector.reg_reuse_def(write, 0);
312323
}
313324

325+
fn write_xmm(&mut self, reg: &mut WritableXmm) {
326+
self.collector.reg_def(reg);
327+
}
328+
314329
fn fixed_read_xmm(&mut self, reg: &mut Xmm, enc: u8) {
315330
self.collector
316331
.reg_fixed_use(reg, fixed_reg(enc, RegClass::Float));
@@ -323,6 +338,11 @@ impl<'a, T: OperandVisitor> asm::RegisterVisitor<CraneliftRegisters> for Regallo
323338
self.collector
324339
.reg_fixed_def(write, fixed_reg(enc, RegClass::Float));
325340
}
341+
342+
fn fixed_write_xmm(&mut self, reg: &mut WritableXmm, enc: u8) {
343+
self.collector
344+
.reg_fixed_def(reg, fixed_reg(enc, RegClass::Float));
345+
}
326346
}
327347

328348
/// A helper for building a fixed register from its hardware encoding.

0 commit comments

Comments
 (0)