Skip to content

Commit bce7f20

Browse files
authored
meta: move all ISLE-related generated code to cranelift-codegen-meta (bytecodealliance#10352)
* meta: move all ISLE-related generated code to `cranelift-codegen-meta` Previously, the new `cranelift-assembler-x64` crate would generate the assembler Rust code, the ISLE integration code, and the ISLE's accompanying Rust macro all in the same crate. Because these last two files were generated in the assembler's `OUT_DIR`, they caused issues for deterministic builds that expect those files in `cranelift-codegen`'s `OUT_DIR` (see bytecodealliance#10348). This change fixes that by moving all of the assembler's generated integration code, the ISLE instruction definitions and their accompanying Rust macro, to the `cranelift-codegen-meta` crate. This has the added advantage that `cranelift-assembler-x64` now can focus exclusively on being an assembler and not being concerned with the ISLE integration, which is substantial. * Move lint `allow` to problem site Apparently allowing unused variables for unimplemented parts of fixed register instructions will not work at the macro scope; this moves the `allow` to the offending line. * Remove `rustfmt` of generated assembler code
1 parent e8d5e3a commit bce7f20

File tree

16 files changed

+507
-494
lines changed

16 files changed

+507
-494
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cranelift/assembler-x64/build.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,7 @@ fn main() {
99

1010
let out_dir = env::var("OUT_DIR").expect("The OUT_DIR environment variable must be set");
1111
let out_dir = Path::new(&out_dir);
12-
let built_files = [
13-
meta::generate_rust_assembler(out_dir, "assembler.rs"),
14-
meta::generate_isle_macro(out_dir, "assembler-isle-macro.rs"),
15-
meta::generate_isle_definitions(out_dir, "assembler-definitions.isle"),
16-
];
12+
let built_files = [meta::generate_rust_assembler(out_dir, "assembler.rs")];
1713

1814
// Generating this additional bit of Rust is necessary for listing the
1915
// generated files.

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

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -31,63 +31,6 @@ pub fn rust_assembler(f: &mut Formatter, insts: &[dsl::Inst]) {
3131
dsl::Feature::generate_enum(f);
3232
}
3333

34-
/// Generate the `isle_assembler_methods!` macro.
35-
pub fn isle_macro(f: &mut Formatter, insts: &[dsl::Inst]) {
36-
fmtln!(f, "#[macro_export]");
37-
fmtln!(f, "macro_rules! isle_assembler_methods {{");
38-
f.indent(|f| {
39-
fmtln!(f, "() => {{");
40-
f.indent(|f| {
41-
for inst in insts {
42-
inst.generate_isle_macro(f);
43-
}
44-
});
45-
fmtln!(f, "}};");
46-
});
47-
fmtln!(f, "}}");
48-
}
49-
50-
/// Generate the ISLE definitions that match the `isle_assembler_methods!` macro
51-
/// above.
52-
pub fn isle_definitions(f: &mut Formatter, insts: &[dsl::Inst]) {
53-
fmtln!(f, "(type AssemblerOutputs (enum");
54-
fmtln!(f, " ;; Used for instructions that have ISLE `SideEffect`s (memory stores, traps,");
55-
fmtln!(f, " ;; etc.) and do not return a `Value`.");
56-
fmtln!(f, " (SideEffect (inst MInst))");
57-
fmtln!(f, " ;; Used for instructions that return a GPR (including `GprMem` variants with");
58-
fmtln!(f, " ;; a GPR as the first argument).");
59-
fmtln!(f, " (RetGpr (inst MInst) (gpr Gpr))");
60-
fmtln!(f, " ;; Used for instructions that return an XMM register.");
61-
fmtln!(f, " (RetXmm (inst MInst) (xmm Xmm))");
62-
fmtln!(f, " ;; TODO: eventually add more variants for multi-return, XMM, etc.; see");
63-
fmtln!(f, " ;; https://github.com/bytecodealliance/wasmtime/pull/10276");
64-
fmtln!(f, "))");
65-
f.empty_line();
66-
67-
fmtln!(f, ";; Directly emit instructions that return a GPR.");
68-
fmtln!(f, "(decl emit_ret_gpr (AssemblerOutputs) Gpr)");
69-
fmtln!(f, "(rule (emit_ret_gpr (AssemblerOutputs.RetGpr inst gpr))");
70-
fmtln!(f, " (let ((_ Unit (emit inst))) gpr))");
71-
f.empty_line();
72-
73-
fmtln!(f, ";; Directly emit instructions that return an XMM register.");
74-
fmtln!(f, "(decl emit_ret_xmm (AssemblerOutputs) Xmm)");
75-
fmtln!(f, "(rule (emit_ret_xmm (AssemblerOutputs.RetXmm inst xmm))");
76-
fmtln!(f, " (let ((_ Unit (emit inst))) xmm))");
77-
f.empty_line();
78-
79-
fmtln!(f, ";; Pass along the side-effecting instruction for later emission.");
80-
fmtln!(f, "(decl defer_side_effect (AssemblerOutputs) SideEffectNoResult)");
81-
fmtln!(f, "(rule (defer_side_effect (AssemblerOutputs.SideEffect inst))");
82-
fmtln!(f, " (SideEffectNoResult.Inst inst))");
83-
f.empty_line();
84-
85-
for inst in insts {
86-
inst.generate_isle_definition(f);
87-
f.empty_line();
88-
}
89-
}
90-
9134
/// `enum Inst { ... }`
9235
fn generate_inst_enum(f: &mut Formatter, insts: &[dsl::Inst]) {
9336
fmtln!(f, "#[doc(hidden)]");

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

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
44
use super::{fmtln, Formatter};
55
use crate::dsl;
6-
use crate::generate::inst::IsleConstructor;
76

87
impl dsl::Format {
98
/// Re-order the Intel-style operand order to accommodate ATT-style
@@ -226,45 +225,6 @@ impl dsl::Format {
226225
}
227226
}
228227
}
229-
230-
/// Returns the ISLE constructors that are going to be used when generating
231-
/// this instruction.
232-
///
233-
/// Note that one instruction might need multiple constructors, such as one
234-
/// for operating on memory and one for operating on registers.
235-
pub fn isle_constructors(&self) -> Vec<IsleConstructor> {
236-
use dsl::Mutability::*;
237-
use dsl::OperandKind::*;
238-
239-
let write_operands = self
240-
.operands
241-
.iter()
242-
.filter(|o| o.mutability.is_write())
243-
.collect::<Vec<_>>();
244-
match &write_operands[..] {
245-
[] => unimplemented!("if you truly need this (and not a `SideEffect*`), add a `NoReturn` variant to `AssemblerOutputs`"),
246-
[one] => match one.mutability {
247-
Read => unreachable!(),
248-
ReadWrite => match one.location.kind() {
249-
Imm(_) => unreachable!(),
250-
FixedReg(_) => vec![IsleConstructor::RetGpr],
251-
// One read/write register output? Output the instruction
252-
// and that register.
253-
Reg(r) => match r.bits() {
254-
128 => vec![IsleConstructor::RetXmm],
255-
_ => vec![IsleConstructor::RetGpr],
256-
},
257-
// One read/write reg-mem output? We need constructors for
258-
// both variants.
259-
RegMem(rm) => match rm.bits() {
260-
128 => vec![IsleConstructor::RetXmm, IsleConstructor::RetMemorySideEffect],
261-
_ => vec![IsleConstructor::RetGpr, IsleConstructor::RetMemorySideEffect],
262-
},
263-
}
264-
},
265-
other => panic!("unsupported number of write operands {other:?}"),
266-
}
267-
}
268228
}
269229

270230
impl dsl::Rex {
Lines changed: 0 additions & 233 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
use super::{fmtln, generate_derive, generate_derive_arbitrary_bounds, Formatter};
22
use crate::dsl;
3-
use crate::dsl::OperandKind;
43

54
impl dsl::Inst {
65
/// `struct <inst> { <op>: Reg, <op>: Reg, ... }`
@@ -229,240 +228,8 @@ impl dsl::Inst {
229228
});
230229
});
231230
}
232-
233-
/// `fn x64_<inst>(&mut self, <params>) -> Inst<R> { ... }`
234-
///
235-
/// # Panics
236-
///
237-
/// This function panics if the instruction has no operands.
238-
pub fn generate_isle_macro(&self, f: &mut Formatter) {
239-
let struct_name = self.name();
240-
let params = self
241-
.format
242-
.operands
243-
.iter()
244-
.filter(|o| o.mutability.is_read())
245-
// FIXME(#10238) don't filter out fixed regs here
246-
.filter(|o| !matches!(o.location.kind(), OperandKind::FixedReg(_)))
247-
.collect::<Vec<_>>();
248-
let results = self
249-
.format
250-
.operands
251-
.iter()
252-
.filter(|o| o.mutability.is_write())
253-
.collect::<Vec<_>>();
254-
let rust_params = params
255-
.iter()
256-
.map(|o| format!("{}: {}", o.location, o.rust_param_raw()))
257-
.collect::<Vec<_>>()
258-
.join(", ");
259-
f.add_block(
260-
&format!("fn x64_{struct_name}_raw(&mut self, {rust_params}) -> AssemblerOutputs"),
261-
|f| {
262-
for o in params.iter() {
263-
let l = o.location;
264-
match o.rust_convert_isle_to_assembler() {
265-
Some(cvt) => fmtln!(f, "let {l} = {cvt}({l});"),
266-
None => fmtln!(f, "let {l} = {l}.clone();"),
267-
}
268-
}
269-
let args = params
270-
.iter()
271-
.map(|o| format!("{}.clone()", o.location))
272-
.collect::<Vec<_>>();
273-
let args = args.join(", ");
274-
fmtln!(f, "let inst = cranelift_assembler_x64::inst::{struct_name}::new({args}).into();");
275-
fmtln!(f, "let inst = MInst::External {{ inst }};");
276-
277-
use dsl::Mutability::*;
278-
match results.as_slice() {
279-
[] => fmtln!(f, "SideEffectNoResult::Inst(inst)"),
280-
[one] => match one.mutability {
281-
Read => unreachable!(),
282-
ReadWrite => match one.location.kind() {
283-
OperandKind::Imm(_) => unreachable!(),
284-
// FIXME(#10238)
285-
OperandKind::FixedReg(_) => fmtln!(f, "todo!()"),
286-
// One read/write register output? Output the instruction
287-
// and that register.
288-
OperandKind::Reg(r) => match r.bits() {
289-
128 => {
290-
fmtln!(f, "let xmm = {}.as_ref().write.to_reg();", results[0].location);
291-
fmtln!(f, "AssemblerOutputs::RetXmm {{ inst, xmm }}")
292-
}
293-
_ => {
294-
fmtln!(f, "let gpr = {}.as_ref().write.to_reg();", results[0].location);
295-
fmtln!(f, "AssemblerOutputs::RetGpr {{ inst, gpr }}")
296-
}
297-
},
298-
// One read/write regmem output? We need to output
299-
// everything and it'll internally disambiguate which was
300-
// emitted (e.g. the mem variant or the register variant).
301-
OperandKind::RegMem(_) => {
302-
assert_eq!(results.len(), 1);
303-
let l = results[0].location;
304-
f.add_block(&format!("match {l}"), |f| match l.bits() {
305-
128 => {
306-
f.add_block("asm::XmmMem::Xmm(reg) => ", |f| {
307-
fmtln!(f, "let xmm = reg.write.to_reg();");
308-
fmtln!(f, "AssemblerOutputs::RetXmm {{ inst, xmm }} ");
309-
});
310-
f.add_block("asm::XmmMem::Mem(_) => ", |f| {
311-
fmtln!(f, "AssemblerOutputs::SideEffect {{ inst }} ");
312-
});
313-
}
314-
_ => {
315-
f.add_block("asm::GprMem::Gpr(reg) => ", |f| {
316-
fmtln!(f, "let gpr = reg.write.to_reg();");
317-
fmtln!(f, "AssemblerOutputs::RetGpr {{ inst, gpr }} ")
318-
});
319-
f.add_block("asm::GprMem::Mem(_) => ", |f| {
320-
fmtln!(f, "AssemblerOutputs::SideEffect {{ inst }} ");
321-
});
322-
}
323-
});
324-
}
325-
},
326-
},
327-
_ => panic!("instruction has more than one result"),
328-
}
329-
},
330-
);
331-
}
332-
333-
/// Generate a "raw" constructor that simply constructs, but does not emit
334-
/// the assembly instruction:
335-
///
336-
/// ```text
337-
/// (decl x64_<inst>_raw (<params>) AssemblerOutputs)
338-
/// (extern constructor x64_<inst>_raw x64_<inst>_raw)
339-
/// ```
340-
///
341-
/// Using the "raw" constructor, we also generate "emitter" constructors
342-
/// (see [`IsleConstructor`]). E.g., instructions that write to a register
343-
/// will return the register:
344-
///
345-
/// ```text
346-
/// (decl x64_<inst> (<params>) Gpr)
347-
/// (rule (x64_<inst> <params>) (emit_ret_gpr (x64_<inst>_raw <params>)))
348-
/// ```
349-
///
350-
/// For instructions that write to memory, we also generate an "emitter"
351-
/// constructor with the `_mem` suffix:
352-
///
353-
/// ```text
354-
/// (decl x64_<inst>_mem (<params>) SideEffectNoResult)
355-
/// (rule (x64_<inst>_mem <params>) (defer_side_effect (x64_<inst>_raw <params>)))
356-
/// ```
357-
///
358-
/// # Panics
359-
///
360-
/// This function panics if the instruction has no operands.
361-
pub fn generate_isle_definition(&self, f: &mut Formatter) {
362-
// First declare the "raw" constructor which is implemented in Rust
363-
// with `generate_isle_macro` above. This is an "extern" constructor
364-
// with relatively raw types. This is not intended to be used by
365-
// general lowering rules in ISLE.
366-
let struct_name = self.name();
367-
let raw_name = format!("x64_{struct_name}_raw");
368-
let params = self
369-
.format
370-
.operands
371-
.iter()
372-
.filter(|o| o.mutability.is_read())
373-
// FIXME(#10238) don't filter out fixed regs here
374-
.filter(|o| !matches!(o.location.kind(), OperandKind::FixedReg(_)))
375-
.collect::<Vec<_>>();
376-
let raw_param_tys = params
377-
.iter()
378-
.map(|o| o.isle_param_raw())
379-
.collect::<Vec<_>>()
380-
.join(" ");
381-
fmtln!(f, "(decl {raw_name} ({raw_param_tys}) AssemblerOutputs)");
382-
fmtln!(f, "(extern constructor {raw_name} {raw_name})");
383-
384-
// Next, for each "emitter" ISLE constructor being generated, synthesize
385-
// a pure-ISLE constructor which delegates appropriately to the `*_raw`
386-
// constructor above.
387-
//
388-
// The main purpose of these constructors is to have faithful type
389-
// signatures for the SSA nature of VCode/ISLE, effectively translating
390-
// x64's type system to ISLE/VCode's type system.
391-
for ctor in self.format.isle_constructors() {
392-
let suffix = ctor.suffix();
393-
let rule_name = format!("x64_{struct_name}{suffix}");
394-
let result_ty = ctor.result_ty();
395-
let param_tys = params
396-
.iter()
397-
.map(|o| o.isle_param_for_ctor(ctor))
398-
.collect::<Vec<_>>()
399-
.join(" ");
400-
let param_names = params
401-
.iter()
402-
.map(|o| o.location.to_string())
403-
.collect::<Vec<_>>()
404-
.join(" ");
405-
let convert = ctor.conversion_constructor();
406-
407-
fmtln!(f, "(decl {rule_name} ({param_tys}) {result_ty})");
408-
fmtln!(f, "(rule ({rule_name} {param_names}) ({convert} ({raw_name} {param_names})))");
409-
}
410-
}
411231
}
412232

413233
fn comma_join<I: Into<String>>(items: impl Iterator<Item = I>) -> String {
414234
items.map(Into::into).collect::<Vec<_>>().join(", ")
415235
}
416-
417-
/// Different kinds of ISLE constructors generated for a particular instruction.
418-
///
419-
/// One instruction may generate a single constructor or multiple constructors.
420-
/// For example an instruction that writes its result to a register will
421-
/// generate only a single constructor. An instruction where the destination
422-
/// read/write operand is `GprMem` will generate two constructors though, one
423-
/// for memory and one for in registers.
424-
#[derive(Copy, Clone, Debug)]
425-
pub enum IsleConstructor {
426-
/// This constructor only produces a side effect, meaning that the
427-
/// instruction does not produce results in registers. This may produce
428-
/// a result in memory, however.
429-
RetMemorySideEffect,
430-
431-
/// This constructor produces a `Gpr` value, meaning that it will write the
432-
/// result to a `Gpr`.
433-
RetGpr,
434-
435-
/// This constructor produces an `Xmm` value, meaning that it will write the
436-
/// result to an `Xmm`.
437-
RetXmm,
438-
}
439-
440-
impl IsleConstructor {
441-
/// Returns the result type, in ISLE, that this constructor generates.
442-
pub fn result_ty(&self) -> &'static str {
443-
match self {
444-
IsleConstructor::RetMemorySideEffect => "SideEffectNoResult",
445-
IsleConstructor::RetGpr => "Gpr",
446-
IsleConstructor::RetXmm => "Xmm",
447-
}
448-
}
449-
450-
/// Returns the constructor used to convert an `AssemblerOutput` into the
451-
/// type returned by [`Self::result_ty`].
452-
pub fn conversion_constructor(&self) -> &'static str {
453-
match self {
454-
IsleConstructor::RetMemorySideEffect => "defer_side_effect",
455-
IsleConstructor::RetGpr => "emit_ret_gpr",
456-
IsleConstructor::RetXmm => "emit_ret_xmm",
457-
}
458-
}
459-
460-
/// Returns the suffix used in the ISLE constructor name.
461-
pub fn suffix(&self) -> &'static str {
462-
match self {
463-
IsleConstructor::RetMemorySideEffect => "_mem",
464-
IsleConstructor::RetGpr => "",
465-
IsleConstructor::RetXmm => "",
466-
}
467-
}
468-
}

0 commit comments

Comments
 (0)