Remove more old backend ISA concepts (#3402)

This also paves the way for unifying TargetIsa and MachBackend, since now they map one to one. In theory the two traits could be merged, which would be nice to limit the number of total concepts. Also they have quite different responsibilities, so it might be fine to keep them separate.

Interestingly, this PR started as removing RegInfo from the TargetIsa trait since the adapter returned a dummy value there. From the fallout, noticed that all Display implementations didn't needed an ISA anymore (since these were only used to render ISA specific registers). Also the whole family of RegInfo / ValueLoc / RegUnit was exclusively used for the old backend, and these could be removed. Notably, some IR instructions needed to be removed, because they were using RegUnit too: this was the oddball of regfill / regmove / regspill / copy_special, which were IR instructions inserted by the old regalloc. Fare thee well!
This commit is contained in:
Benjamin Bouvier
2021-10-04 10:36:12 +02:00
committed by GitHub
parent 76afcab0c2
commit 43a86f14d5
71 changed files with 302 additions and 2059 deletions

View File

@@ -18,8 +18,6 @@ pub(crate) struct Formats {
pub(crate) call: Rc<InstructionFormat>, pub(crate) call: Rc<InstructionFormat>,
pub(crate) call_indirect: Rc<InstructionFormat>, pub(crate) call_indirect: Rc<InstructionFormat>,
pub(crate) cond_trap: Rc<InstructionFormat>, pub(crate) cond_trap: Rc<InstructionFormat>,
pub(crate) copy_special: Rc<InstructionFormat>,
pub(crate) copy_to_ssa: Rc<InstructionFormat>,
pub(crate) float_compare: Rc<InstructionFormat>, pub(crate) float_compare: Rc<InstructionFormat>,
pub(crate) float_cond: Rc<InstructionFormat>, pub(crate) float_cond: Rc<InstructionFormat>,
pub(crate) float_cond_trap: Rc<InstructionFormat>, pub(crate) float_cond_trap: Rc<InstructionFormat>,
@@ -37,9 +35,6 @@ pub(crate) struct Formats {
pub(crate) load_no_offset: Rc<InstructionFormat>, pub(crate) load_no_offset: Rc<InstructionFormat>,
pub(crate) multiary: Rc<InstructionFormat>, pub(crate) multiary: Rc<InstructionFormat>,
pub(crate) nullary: Rc<InstructionFormat>, pub(crate) nullary: Rc<InstructionFormat>,
pub(crate) reg_fill: Rc<InstructionFormat>,
pub(crate) reg_move: Rc<InstructionFormat>,
pub(crate) reg_spill: Rc<InstructionFormat>,
pub(crate) shuffle: Rc<InstructionFormat>, pub(crate) shuffle: Rc<InstructionFormat>,
pub(crate) stack_load: Rc<InstructionFormat>, pub(crate) stack_load: Rc<InstructionFormat>,
pub(crate) stack_store: Rc<InstructionFormat>, pub(crate) stack_store: Rc<InstructionFormat>,
@@ -283,33 +278,6 @@ impl Formats {
.imm(&imm.offset32) .imm(&imm.offset32)
.build(), .build(),
reg_move: Builder::new("RegMove")
.value()
.imm_with_name("src", &imm.regunit)
.imm_with_name("dst", &imm.regunit)
.build(),
copy_special: Builder::new("CopySpecial")
.imm_with_name("src", &imm.regunit)
.imm_with_name("dst", &imm.regunit)
.build(),
copy_to_ssa: Builder::new("CopyToSsa")
.imm_with_name("src", &imm.regunit)
.build(),
reg_spill: Builder::new("RegSpill")
.value()
.imm_with_name("src", &imm.regunit)
.imm_with_name("dst", &entities.stack_slot)
.build(),
reg_fill: Builder::new("RegFill")
.value()
.imm_with_name("src", &entities.stack_slot)
.imm_with_name("dst", &imm.regunit)
.build(),
trap: Builder::new("Trap").imm(&imm.trapcode).build(), trap: Builder::new("Trap").imm(&imm.trapcode).build(),
cond_trap: Builder::new("CondTrap").value().imm(&imm.trapcode).build(), cond_trap: Builder::new("CondTrap").value().imm(&imm.trapcode).build(),

View File

@@ -64,9 +64,6 @@ pub(crate) struct Immediates {
/// Flags for memory operations like `load` and `store`. /// Flags for memory operations like `load` and `store`.
pub memflags: OperandKind, pub memflags: OperandKind,
/// A register unit in the current target ISA.
pub regunit: OperandKind,
/// A trap code indicating the reason for trapping. /// A trap code indicating the reason for trapping.
/// ///
/// The Rust enum type also has a `User(u16)` variant for user-provided trap codes. /// The Rust enum type also has a `User(u16)` variant for user-provided trap codes.
@@ -149,8 +146,6 @@ impl Immediates {
}, },
memflags: new_imm("flags", "ir::MemFlags").with_doc("Memory operation flags"), memflags: new_imm("flags", "ir::MemFlags").with_doc("Memory operation flags"),
regunit: new_imm("regunit", "isa::RegUnit")
.with_doc("A register unit in the target ISA"),
trapcode: { trapcode: {
let mut trapcode_values = HashMap::new(); let mut trapcode_values = HashMap::new();
trapcode_values.insert("stk_ovf", "StackOverflow"); trapcode_values.insert("stk_ovf", "StackOverflow");

View File

@@ -1954,65 +1954,6 @@ pub(crate) fn define(
.operands_out(vec![sarg_t]), .operands_out(vec![sarg_t]),
); );
let src = &Operand::new("src", &imm.regunit);
let dst = &Operand::new("dst", &imm.regunit);
ig.push(
Inst::new(
"regmove",
r#"
Temporarily divert ``x`` from ``src`` to ``dst``.
This instruction moves the location of a value from one register to
another without creating a new SSA value. It is used by the register
allocator to temporarily rearrange register assignments in order to
satisfy instruction constraints.
The register diversions created by this instruction must be undone
before the value leaves the block. At the entry to a new block, all live
values must be in their originally assigned registers.
"#,
&formats.reg_move,
)
.operands_in(vec![x, src, dst])
.other_side_effects(true),
);
ig.push(
Inst::new(
"copy_special",
r#"
Copies the contents of ''src'' register to ''dst'' register.
This instructions copies the contents of one register to another
register without involving any SSA values. This is used for copying
special registers, e.g. copying the stack register to the frame
register in a function prologue.
"#,
&formats.copy_special,
)
.operands_in(vec![src, dst])
.other_side_effects(true),
);
ig.push(
Inst::new(
"copy_to_ssa",
r#"
Copies the contents of ''src'' register to ''a'' SSA name.
This instruction copies the contents of one register, regardless of its SSA name, to
another register, creating a new SSA name. In that sense it is a one-sided version
of ''copy_special''. This instruction is internal and should not be created by
Cranelift users.
"#,
&formats.copy_to_ssa,
)
.operands_in(vec![src])
.operands_out(vec![a])
.other_side_effects(true),
);
ig.push( ig.push(
Inst::new( Inst::new(
"copy_nop", "copy_nop",
@@ -2098,44 +2039,6 @@ pub(crate) fn define(
.operands_out(vec![f]), .operands_out(vec![f]),
); );
ig.push(
Inst::new(
"regspill",
r#"
Temporarily divert ``x`` from ``src`` to ``SS``.
This instruction moves the location of a value from a register to a
stack slot without creating a new SSA value. It is used by the register
allocator to temporarily rearrange register assignments in order to
satisfy instruction constraints.
See also `regmove`.
"#,
&formats.reg_spill,
)
.operands_in(vec![x, src, SS])
.other_side_effects(true),
);
ig.push(
Inst::new(
"regfill",
r#"
Temporarily divert ``x`` from ``SS`` to ``dst``.
This instruction moves the location of a value from a stack slot to a
register without creating a new SSA value. It is used by the register
allocator to temporarily rearrange register assignments in order to
satisfy instruction constraints.
See also `regmove`.
"#,
&formats.reg_fill,
)
.operands_in(vec![x, SS, dst])
.other_side_effects(true),
);
let N = let N =
&Operand::new("args", &entities.varargs).with_doc("Variable number of args for StackMap"); &Operand::new("args", &entities.varargs).with_doc("Variable number of args for StackMap");
@@ -2302,10 +2205,9 @@ pub(crate) fn define(
| of | * | Overflow | | of | * | Overflow |
| nof | * | No Overflow | | nof | * | No Overflow |
\* The unsigned version of overflow conditions have ISA-specific \* The unsigned version of overflow condition for add has ISA-specific semantics and thus
semantics and thus have been kept as methods on the TargetIsa trait as has been kept as a method on the TargetIsa trait as
[unsigned_add_overflow_condition][isa::TargetIsa::unsigned_add_overflow_condition] and [unsigned_add_overflow_condition][crate::isa::TargetIsa::unsigned_add_overflow_condition].
[unsigned_sub_overflow_condition][isa::TargetIsa::unsigned_sub_overflow_condition].
When this instruction compares integer vectors, it returns a boolean When this instruction compares integer vectors, it returns a boolean
vector of lane-wise comparisons. vector of lane-wise comparisons.

View File

@@ -15,9 +15,7 @@
//! `CodeSink::put*` methods, so the performance impact of the virtual callbacks is less severe. //! `CodeSink::put*` methods, so the performance impact of the virtual callbacks is less severe.
use super::{Addend, CodeInfo, CodeOffset, CodeSink, Reloc}; use super::{Addend, CodeInfo, CodeOffset, CodeSink, Reloc};
use crate::binemit::stack_map::StackMap; use crate::binemit::stack_map::StackMap;
use crate::ir::entities::Value; use crate::ir::{ConstantOffset, ExternalName, JumpTable, Opcode, SourceLoc, TrapCode};
use crate::ir::{ConstantOffset, ExternalName, Function, JumpTable, Opcode, SourceLoc, TrapCode};
use crate::isa::TargetIsa;
use core::ptr::write_unaligned; use core::ptr::write_unaligned;
/// A `CodeSink` that writes binary machine code directly into memory. /// A `CodeSink` that writes binary machine code directly into memory.
@@ -38,7 +36,6 @@ pub struct MemoryCodeSink<'a> {
offset: isize, offset: isize,
relocs: &'a mut dyn RelocSink, relocs: &'a mut dyn RelocSink,
traps: &'a mut dyn TrapSink, traps: &'a mut dyn TrapSink,
stack_maps: &'a mut dyn StackMapSink,
/// Information about the generated code and read-only data. /// Information about the generated code and read-only data.
pub info: CodeInfo, pub info: CodeInfo,
} }
@@ -54,7 +51,6 @@ impl<'a> MemoryCodeSink<'a> {
data: *mut u8, data: *mut u8,
relocs: &'a mut dyn RelocSink, relocs: &'a mut dyn RelocSink,
traps: &'a mut dyn TrapSink, traps: &'a mut dyn TrapSink,
stack_maps: &'a mut dyn StackMapSink,
) -> Self { ) -> Self {
Self { Self {
data, data,
@@ -67,7 +63,6 @@ impl<'a> MemoryCodeSink<'a> {
}, },
relocs, relocs,
traps, traps,
stack_maps,
} }
} }
} }
@@ -174,12 +169,6 @@ impl<'a> CodeSink for MemoryCodeSink<'a> {
self.info.total_size = self.offset(); self.info.total_size = self.offset();
} }
fn add_stack_map(&mut self, val_list: &[Value], func: &Function, isa: &dyn TargetIsa) {
let ofs = self.offset();
let stack_map = StackMap::from_values(&val_list, func, isa);
self.stack_maps.add_stack_map(ofs, stack_map);
}
fn add_call_site(&mut self, opcode: Opcode, loc: SourceLoc) { fn add_call_site(&mut self, opcode: Opcode, loc: SourceLoc) {
debug_assert!( debug_assert!(
opcode.is_call(), opcode.is_call(),

View File

@@ -11,7 +11,6 @@ pub use self::memorysink::{
TrapSink, TrapSink,
}; };
pub use self::stack_map::StackMap; pub use self::stack_map::StackMap;
use crate::ir::entities::Value;
use crate::ir::{ use crate::ir::{
ConstantOffset, ExternalName, Function, Inst, JumpTable, Opcode, SourceLoc, TrapCode, ConstantOffset, ExternalName, Function, Inst, JumpTable, Opcode, SourceLoc, TrapCode,
}; };
@@ -169,9 +168,6 @@ pub trait CodeSink {
/// Read-only data output is complete, we're done. /// Read-only data output is complete, we're done.
fn end_codegen(&mut self); fn end_codegen(&mut self);
/// Add a stack map at the current code offset.
fn add_stack_map(&mut self, _: &[Value], _: &Function, _: &dyn TargetIsa);
/// Add a call site for a call with the given opcode, returning at the current offset. /// Add a call site for a call with the given opcode, returning at the current offset.
fn add_call_site(&mut self, _: Opcode, _: SourceLoc) { fn add_call_site(&mut self, _: Opcode, _: SourceLoc) {
// Default implementation doesn't need to do anything. // Default implementation doesn't need to do anything.

View File

@@ -1,6 +1,4 @@
use crate::bitset::BitSet; use crate::bitset::BitSet;
use crate::ir;
use crate::isa::TargetIsa;
use alloc::vec::Vec; use alloc::vec::Vec;
type Num = u32; type Num = u32;
@@ -76,57 +74,6 @@ pub struct StackMap {
} }
impl StackMap { impl StackMap {
/// Create a `StackMap` based on where references are located on a
/// function's stack.
pub fn from_values(
args: &[ir::entities::Value],
func: &ir::Function,
isa: &dyn TargetIsa,
) -> Self {
let loc = &func.locations;
let mut live_ref_in_stack_slot = crate::HashSet::new();
// References can be in registers, and live registers values are pushed onto the stack before calls and traps.
// TODO: Implement register maps. If a register containing a reference is spilled and reused after a safepoint,
// it could contain a stale reference value if the garbage collector relocated the value.
for val in args {
if let Some(value_loc) = loc.get(*val) {
match *value_loc {
ir::ValueLoc::Stack(stack_slot) => {
live_ref_in_stack_slot.insert(stack_slot);
}
_ => {}
}
}
}
let stack = &func.stack_slots;
let info = func.stack_slots.layout_info.unwrap();
// Refer to the doc comment for `StackMap` above to understand the
// bitmap representation used here.
let map_size = (info.frame_size + info.inbound_args_size) as usize;
let word_size = isa.pointer_bytes() as usize;
let num_words = map_size / word_size;
let mut vec = alloc::vec::Vec::with_capacity(num_words);
vec.resize(num_words, false);
for (ss, ssd) in stack.iter() {
if !live_ref_in_stack_slot.contains(&ss)
|| ssd.kind == ir::stackslot::StackSlotKind::OutgoingArg
{
continue;
}
debug_assert!(ssd.size as usize == word_size);
let bytes_from_bottom = info.frame_size as i32 + ssd.offset.unwrap();
let words_from_bottom = (bytes_from_bottom as usize) / word_size;
vec[words_from_bottom] = true;
}
Self::from_slice(&vec)
}
/// Create a vec of Bitsets from a slice of bools. /// Create a vec of Bitsets from a slice of bools.
pub fn from_slice(vec: &[bool]) -> Self { pub fn from_slice(vec: &[bool]) -> Self {
let len = vec.len(); let len = vec.len();

View File

@@ -51,11 +51,11 @@ impl<'a> CFGPrinter<'a> {
for block in &self.func.layout { for block in &self.func.layout {
write!(w, " {} [shape=record, label=\"{{", block)?; write!(w, " {} [shape=record, label=\"{{", block)?;
crate::write::write_block_header(w, self.func, None, block, 4)?; crate::write::write_block_header(w, self.func, block, 4)?;
// Add all outgoing branch instructions to the label. // Add all outgoing branch instructions to the label.
for inst in self.func.layout.block_likely_branches(block) { for inst in self.func.layout.block_likely_branches(block) {
write!(w, " | <{}>", inst)?; write!(w, " | <{}>", inst)?;
PlainWriter.write_instruction(w, self.func, &aliases, None, inst, 0)?; PlainWriter.write_instruction(w, self.func, &aliases, inst, 0)?;
} }
writeln!(w, "}}\"]")? writeln!(w, "}}\"]")?
} }

View File

@@ -140,7 +140,7 @@ impl Context {
log::debug!( log::debug!(
"Compiling (opt level {:?}):\n{}", "Compiling (opt level {:?}):\n{}",
opt_level, opt_level,
self.func.display(isa) self.func.display()
); );
self.compute_cfg(); self.compute_cfg();
@@ -196,7 +196,7 @@ impl Context {
stack_maps: &mut dyn StackMapSink, stack_maps: &mut dyn StackMapSink,
) -> CodeInfo { ) -> CodeInfo {
let _tt = timing::binemit(); let _tt = timing::binemit();
let mut sink = MemoryCodeSink::new(mem, relocs, traps, stack_maps); let mut sink = MemoryCodeSink::new(mem, relocs, traps);
let result = self let result = self
.mach_compile_result .mach_compile_result
.as_ref() .as_ref()

View File

@@ -720,7 +720,7 @@ impl<'f> EncCursor<'f> {
/// ///
/// This is a convenience wrapper for the DFG equivalent. /// This is a convenience wrapper for the DFG equivalent.
pub fn display_inst(&self, inst: ir::Inst) -> ir::dfg::DisplayInst { pub fn display_inst(&self, inst: ir::Inst) -> ir::dfg::DisplayInst {
self.func.dfg.display_inst(inst, self.isa) self.func.dfg.display_inst(inst)
} }
} }

View File

@@ -7,7 +7,6 @@ use crate::ir;
use crate::ir::types; use crate::ir::types;
use crate::ir::{DataFlowGraph, InstructionData}; use crate::ir::{DataFlowGraph, InstructionData};
use crate::ir::{Inst, Opcode, Type, Value}; use crate::ir::{Inst, Opcode, Type, Value};
use crate::isa;
/// Base trait for instruction builders. /// Base trait for instruction builders.
/// ///

View File

@@ -10,7 +10,6 @@ use crate::ir::{
Block, FuncRef, Inst, SigRef, Signature, SourceLoc, Type, Value, ValueLabelAssignments, Block, FuncRef, Inst, SigRef, Signature, SourceLoc, Type, Value, ValueLabelAssignments,
ValueList, ValueListPool, ValueList, ValueListPool,
}; };
use crate::isa::TargetIsa;
use crate::packed_option::ReservedValue; use crate::packed_option::ReservedValue;
use crate::write::write_operands; use crate::write::write_operands;
use crate::HashMap; use crate::HashMap;
@@ -466,12 +465,8 @@ impl DataFlowGraph {
} }
/// Returns an object that displays `inst`. /// Returns an object that displays `inst`.
pub fn display_inst<'a, I: Into<Option<&'a dyn TargetIsa>>>( pub fn display_inst<'a>(&'a self, inst: Inst) -> DisplayInst<'a> {
&'a self, DisplayInst(self, inst)
inst: Inst,
isa: I,
) -> DisplayInst<'a> {
DisplayInst(self, isa.into(), inst)
} }
/// Get all value arguments on `inst` as a slice. /// Get all value arguments on `inst` as a slice.
@@ -657,7 +652,7 @@ impl DataFlowGraph {
old_value, old_value,
"{} wasn't detached from {}", "{} wasn't detached from {}",
old_value, old_value,
self.display_inst(inst, None) self.display_inst(inst)
); );
new_value new_value
} }
@@ -963,13 +958,12 @@ impl BlockData {
} }
/// Object that can display an instruction. /// Object that can display an instruction.
pub struct DisplayInst<'a>(&'a DataFlowGraph, Option<&'a dyn TargetIsa>, Inst); pub struct DisplayInst<'a>(&'a DataFlowGraph, Inst);
impl<'a> fmt::Display for DisplayInst<'a> { impl<'a> fmt::Display for DisplayInst<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let dfg = self.0; let dfg = self.0;
let isa = self.1; let inst = self.1;
let inst = self.2;
if let Some((first, rest)) = dfg.inst_results(inst).split_first() { if let Some((first, rest)) = dfg.inst_results(inst).split_first() {
write!(f, "{}", first)?; write!(f, "{}", first)?;
@@ -985,7 +979,7 @@ impl<'a> fmt::Display for DisplayInst<'a> {
} else { } else {
write!(f, "{}.{}", dfg[inst].opcode(), typevar)?; write!(f, "{}.{}", dfg[inst].opcode(), typevar)?;
} }
write_operands(f, dfg, isa, inst) write_operands(f, dfg, inst)
} }
} }
@@ -1150,10 +1144,7 @@ mod tests {
dfg.make_inst_results(inst, types::I32); dfg.make_inst_results(inst, types::I32);
assert_eq!(inst.to_string(), "inst0"); assert_eq!(inst.to_string(), "inst0");
assert_eq!( assert_eq!(dfg.display_inst(inst).to_string(), "v0 = iconst.i32 0");
dfg.display_inst(inst, None).to_string(),
"v0 = iconst.i32 0"
);
// Immutable reference resolution. // Immutable reference resolution.
{ {
@@ -1188,7 +1179,7 @@ mod tests {
code: TrapCode::User(0), code: TrapCode::User(0),
}; };
let inst = dfg.make_inst(idata); let inst = dfg.make_inst(idata);
assert_eq!(dfg.display_inst(inst, None).to_string(), "trap user0"); assert_eq!(dfg.display_inst(inst).to_string(), "trap user0");
// Result slice should be empty. // Result slice should be empty.
assert_eq!(dfg.inst_results(inst), &[]); assert_eq!(dfg.inst_results(inst), &[]);

View File

@@ -5,8 +5,8 @@
//! //!
//! This module declares the data types used to represent external functions and call signatures. //! This module declares the data types used to represent external functions and call signatures.
use crate::ir::{ArgumentLoc, ExternalName, SigRef, Type}; use crate::ir::{ExternalName, SigRef, Type};
use crate::isa::{CallConv, RegInfo, RegUnit}; use crate::isa::CallConv;
use crate::machinst::RelocDistance; use crate::machinst::RelocDistance;
use alloc::vec::Vec; use alloc::vec::Vec;
use core::fmt; use core::fmt;
@@ -50,11 +50,6 @@ impl Signature {
self.call_conv = call_conv; self.call_conv = call_conv;
} }
/// Return an object that can display `self` with correct register names.
pub fn display<'a, R: Into<Option<&'a RegInfo>>>(&'a self, regs: R) -> DisplaySignature<'a> {
DisplaySignature(self, regs.into())
}
/// Find the index of a presumed unique special-purpose parameter. /// Find the index of a presumed unique special-purpose parameter.
pub fn special_param_index(&self, purpose: ArgumentPurpose) -> Option<usize> { pub fn special_param_index(&self, purpose: ArgumentPurpose) -> Option<usize> {
self.params.iter().rposition(|arg| arg.purpose == purpose) self.params.iter().rposition(|arg| arg.purpose == purpose)
@@ -108,38 +103,29 @@ impl Signature {
} }
} }
/// Wrapper type capable of displaying a `Signature` with correct register names. fn write_list(f: &mut fmt::Formatter, args: &[AbiParam]) -> fmt::Result {
pub struct DisplaySignature<'a>(&'a Signature, Option<&'a RegInfo>);
fn write_list(f: &mut fmt::Formatter, args: &[AbiParam], regs: Option<&RegInfo>) -> fmt::Result {
match args.split_first() { match args.split_first() {
None => {} None => {}
Some((first, rest)) => { Some((first, rest)) => {
write!(f, "{}", first.display(regs))?; write!(f, "{}", first)?;
for arg in rest { for arg in rest {
write!(f, ", {}", arg.display(regs))?; write!(f, ", {}", arg)?;
} }
} }
} }
Ok(()) Ok(())
} }
impl<'a> fmt::Display for DisplaySignature<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "(")?;
write_list(f, &self.0.params, self.1)?;
write!(f, ")")?;
if !self.0.returns.is_empty() {
write!(f, " -> ")?;
write_list(f, &self.0.returns, self.1)?;
}
write!(f, " {}", self.0.call_conv)
}
}
impl fmt::Display for Signature { impl fmt::Display for Signature {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.display(None).fmt(f) write!(f, "(")?;
write_list(f, &self.params)?;
write!(f, ")")?;
if !self.returns.is_empty() {
write!(f, " -> ")?;
write_list(f, &self.returns)?;
}
write!(f, " {}", self.call_conv)
} }
} }
@@ -157,9 +143,6 @@ pub struct AbiParam {
/// Method for extending argument to a full register. /// Method for extending argument to a full register.
pub extension: ArgumentExtension, pub extension: ArgumentExtension,
/// ABI-specific location of this argument, or `Unassigned` for arguments that have not yet
/// been legalized.
pub location: ArgumentLoc,
/// Was the argument converted to pointer during legalization? /// Was the argument converted to pointer during legalization?
pub legalized_to_pointer: bool, pub legalized_to_pointer: bool,
} }
@@ -171,7 +154,6 @@ impl AbiParam {
value_type: vt, value_type: vt,
extension: ArgumentExtension::None, extension: ArgumentExtension::None,
purpose: ArgumentPurpose::Normal, purpose: ArgumentPurpose::Normal,
location: Default::default(),
legalized_to_pointer: false, legalized_to_pointer: false,
} }
} }
@@ -182,18 +164,6 @@ impl AbiParam {
value_type: vt, value_type: vt,
extension: ArgumentExtension::None, extension: ArgumentExtension::None,
purpose, purpose,
location: Default::default(),
legalized_to_pointer: false,
}
}
/// Create a parameter for a special-purpose register.
pub fn special_reg(vt: Type, purpose: ArgumentPurpose, regunit: RegUnit) -> Self {
Self {
value_type: vt,
extension: ArgumentExtension::None,
purpose,
location: ArgumentLoc::Reg(regunit),
legalized_to_pointer: false, legalized_to_pointer: false,
} }
} }
@@ -215,42 +185,23 @@ impl AbiParam {
..self ..self
} }
} }
/// Return an object that can display `self` with correct register names.
pub fn display<'a, R: Into<Option<&'a RegInfo>>>(&'a self, regs: R) -> DisplayAbiParam<'a> {
DisplayAbiParam(self, regs.into())
}
}
/// Wrapper type capable of displaying a `AbiParam` with correct register names.
pub struct DisplayAbiParam<'a>(&'a AbiParam, Option<&'a RegInfo>);
impl<'a> fmt::Display for DisplayAbiParam<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0.value_type)?;
if self.0.legalized_to_pointer {
write!(f, " ptr")?;
}
match self.0.extension {
ArgumentExtension::None => {}
ArgumentExtension::Uext => write!(f, " uext")?,
ArgumentExtension::Sext => write!(f, " sext")?,
}
if self.0.purpose != ArgumentPurpose::Normal {
write!(f, " {}", self.0.purpose)?;
}
if self.0.location.is_assigned() {
write!(f, " [{}]", self.0.location.display(self.1))?;
}
Ok(())
}
} }
impl fmt::Display for AbiParam { impl fmt::Display for AbiParam {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.display(None).fmt(f) write!(f, "{}", self.value_type)?;
if self.legalized_to_pointer {
write!(f, " ptr")?;
}
match self.extension {
ArgumentExtension::None => {}
ArgumentExtension::Uext => write!(f, " uext")?,
ArgumentExtension::Sext => write!(f, " sext")?,
}
if self.purpose != ArgumentPurpose::Normal {
write!(f, " {}", self.purpose)?;
}
Ok(())
} }
} }
@@ -519,15 +470,5 @@ mod tests {
sig.to_string(), sig.to_string(),
"(i32, i32x4) -> f32, b8 baldrdash_system_v" "(i32, i32x4) -> f32, b8 baldrdash_system_v"
); );
// Order does not matter.
sig.params[0].location = ArgumentLoc::Stack(24);
sig.params[1].location = ArgumentLoc::Stack(8);
// Writing ABI-annotated signatures.
assert_eq!(
sig.to_string(),
"(i32 [24], i32x4 [8]) -> f32, b8 baldrdash_system_v"
);
} }
} }

View File

@@ -10,10 +10,10 @@ use crate::ir::{
HeapData, Inst, InstructionData, JumpTable, JumpTableData, Opcode, SigRef, StackSlot, HeapData, Inst, InstructionData, JumpTable, JumpTableData, Opcode, SigRef, StackSlot,
StackSlotData, Table, TableData, StackSlotData, Table, TableData,
}; };
use crate::ir::{BlockOffsets, SourceLocs, StackSlots, ValueLocations}; use crate::ir::{BlockOffsets, SourceLocs, StackSlots};
use crate::ir::{DataFlowGraph, ExternalName, Layout, Signature}; use crate::ir::{DataFlowGraph, ExternalName, Layout, Signature};
use crate::ir::{JumpTableOffsets, JumpTables}; use crate::ir::{JumpTableOffsets, JumpTables};
use crate::isa::{CallConv, TargetIsa}; use crate::isa::CallConv;
use crate::value_label::ValueLabelsRanges; use crate::value_label::ValueLabelsRanges;
use crate::write::write_function; use crate::write::write_function;
#[cfg(feature = "enable-serde")] #[cfg(feature = "enable-serde")]
@@ -104,9 +104,6 @@ pub struct Function {
/// Layout of blocks and instructions in the function body. /// Layout of blocks and instructions in the function body.
pub layout: Layout, pub layout: Layout,
/// Location assigned to every value.
pub locations: ValueLocations,
/// Code offsets of the block headers. /// Code offsets of the block headers.
/// ///
/// This information is only transiently available after the `binemit::relax_branches` function /// This information is only transiently available after the `binemit::relax_branches` function
@@ -156,7 +153,6 @@ impl Function {
jump_tables: PrimaryMap::new(), jump_tables: PrimaryMap::new(),
dfg: DataFlowGraph::new(), dfg: DataFlowGraph::new(),
layout: Layout::new(), layout: Layout::new(),
locations: SecondaryMap::new(),
offsets: SecondaryMap::new(), offsets: SecondaryMap::new(),
jt_offsets: SecondaryMap::new(), jt_offsets: SecondaryMap::new(),
srclocs: SecondaryMap::new(), srclocs: SecondaryMap::new(),
@@ -176,7 +172,6 @@ impl Function {
self.jump_tables.clear(); self.jump_tables.clear();
self.dfg.clear(); self.dfg.clear();
self.layout.clear(); self.layout.clear();
self.locations.clear();
self.offsets.clear(); self.offsets.clear();
self.jt_offsets.clear(); self.jt_offsets.clear();
self.srclocs.clear(); self.srclocs.clear();
@@ -227,11 +222,8 @@ impl Function {
} }
/// Return an object that can display this function with correct ISA-specific annotations. /// Return an object that can display this function with correct ISA-specific annotations.
pub fn display<'a, I: Into<Option<&'a dyn TargetIsa>>>( pub fn display(&self) -> DisplayFunction<'_> {
&'a self, DisplayFunction(self, Default::default())
isa: I,
) -> DisplayFunction<'a> {
DisplayFunction(self, isa.into().into())
} }
/// Return an object that can display this function with correct ISA-specific annotations. /// Return an object that can display this function with correct ISA-specific annotations.
@@ -295,7 +287,7 @@ impl Function {
} }
_ => panic!( _ => panic!(
"Unexpected instruction {} having default destination", "Unexpected instruction {} having default destination",
self.dfg.display_inst(inst, None) self.dfg.display_inst(inst)
), ),
} }
} }
@@ -372,39 +364,27 @@ impl Function {
/// Additional annotations for function display. /// Additional annotations for function display.
#[derive(Default)] #[derive(Default)]
pub struct DisplayFunctionAnnotations<'a> { pub struct DisplayFunctionAnnotations<'a> {
/// Enable ISA annotations.
pub isa: Option<&'a dyn TargetIsa>,
/// Enable value labels annotations. /// Enable value labels annotations.
pub value_ranges: Option<&'a ValueLabelsRanges>, pub value_ranges: Option<&'a ValueLabelsRanges>,
} }
impl<'a> From<Option<&'a dyn TargetIsa>> for DisplayFunctionAnnotations<'a> {
fn from(isa: Option<&'a dyn TargetIsa>) -> DisplayFunctionAnnotations {
DisplayFunctionAnnotations {
isa,
value_ranges: None,
}
}
}
/// Wrapper type capable of displaying a `Function` with correct ISA annotations. /// Wrapper type capable of displaying a `Function` with correct ISA annotations.
pub struct DisplayFunction<'a>(&'a Function, DisplayFunctionAnnotations<'a>); pub struct DisplayFunction<'a>(&'a Function, DisplayFunctionAnnotations<'a>);
impl<'a> fmt::Display for DisplayFunction<'a> { impl<'a> fmt::Display for DisplayFunction<'a> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write_function(fmt, self.0, &self.1) write_function(fmt, self.0)
} }
} }
impl fmt::Display for Function { impl fmt::Display for Function {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write_function(fmt, self, &DisplayFunctionAnnotations::default()) write_function(fmt, self)
} }
} }
impl fmt::Debug for Function { impl fmt::Debug for Function {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
write_function(fmt, self, &DisplayFunctionAnnotations::default()) write_function(fmt, self)
} }
} }

View File

@@ -25,7 +25,6 @@ use crate::ir::{
trapcode::TrapCode, trapcode::TrapCode,
types, Block, FuncRef, JumpTable, MemFlags, SigRef, StackSlot, Type, Value, types, Block, FuncRef, JumpTable, MemFlags, SigRef, StackSlot, Type, Value,
}; };
use crate::isa;
/// Some instructions use an external list of argument values because there is not enough space in /// Some instructions use an external list of argument values because there is not enough space in
/// the 16-byte `InstructionData` struct. These value lists are stored in a memory pool in /// the 16-byte `InstructionData` struct. These value lists are stored in a memory pool in

View File

@@ -1,10 +1,6 @@
//! Naming well-known routines in the runtime library. //! Naming well-known routines in the runtime library.
use crate::ir::{ use crate::ir::{types, ExternalName, FuncRef, Function, Opcode, Type};
types, AbiParam, ArgumentPurpose, ExtFuncData, ExternalName, FuncRef, Function, Opcode,
Signature, Type,
};
use crate::isa::{CallConv, RegUnit, TargetIsa};
use core::fmt; use core::fmt;
use core::str::FromStr; use core::str::FromStr;
#[cfg(feature = "enable-serde")] #[cfg(feature = "enable-serde")]
@@ -169,14 +165,8 @@ impl LibCall {
/// Get a function reference for the probestack function in `func`. /// Get a function reference for the probestack function in `func`.
/// ///
/// If there is an existing reference, use it, otherwise make a new one. /// If there is an existing reference, use it, otherwise make a new one.
pub fn get_probestack_funcref( pub fn get_probestack_funcref(func: &mut Function) -> Option<FuncRef> {
func: &mut Function,
reg_type: Type,
arg_reg: RegUnit,
isa: &dyn TargetIsa,
) -> FuncRef {
find_funcref(LibCall::Probestack, func) find_funcref(LibCall::Probestack, func)
.unwrap_or_else(|| make_funcref_for_probestack(func, reg_type, arg_reg, isa))
} }
/// Get the existing function reference for `libcall` in `func` if it exists. /// Get the existing function reference for `libcall` in `func` if it exists.
@@ -196,38 +186,6 @@ fn find_funcref(libcall: LibCall, func: &Function) -> Option<FuncRef> {
None None
} }
/// Create a funcref for `LibCall::Probestack`.
fn make_funcref_for_probestack(
func: &mut Function,
reg_type: Type,
arg_reg: RegUnit,
isa: &dyn TargetIsa,
) -> FuncRef {
let mut sig = Signature::new(CallConv::Probestack);
let rax = AbiParam::special_reg(reg_type, ArgumentPurpose::Normal, arg_reg);
sig.params.push(rax);
if !isa.flags().probestack_func_adjusts_sp() {
sig.returns.push(rax);
}
make_funcref(LibCall::Probestack, func, sig, isa)
}
/// Create a funcref for `libcall`.
fn make_funcref(
libcall: LibCall,
func: &mut Function,
sig: Signature,
isa: &dyn TargetIsa,
) -> FuncRef {
let sigref = func.import_signature(sig);
func.import_function(ExtFuncData {
name: ExternalName::LibCall(libcall),
signature: sigref,
colocated: isa.flags().use_colocated_libcalls(),
})
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@@ -22,7 +22,6 @@ pub mod stackslot;
mod table; mod table;
mod trapcode; mod trapcode;
pub mod types; pub mod types;
mod valueloc;
#[cfg(feature = "enable-serde")] #[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@@ -57,16 +56,12 @@ pub use crate::ir::stackslot::{StackLayoutInfo, StackSlotData, StackSlotKind, St
pub use crate::ir::table::TableData; pub use crate::ir::table::TableData;
pub use crate::ir::trapcode::TrapCode; pub use crate::ir::trapcode::TrapCode;
pub use crate::ir::types::Type; pub use crate::ir::types::Type;
pub use crate::ir::valueloc::{ArgumentLoc, ValueLoc};
pub use crate::value_label::LabelValueLoc; pub use crate::value_label::LabelValueLoc;
pub use cranelift_codegen_shared::condcodes; pub use cranelift_codegen_shared::condcodes;
use crate::binemit; use crate::binemit;
use crate::entity::{entity_impl, PrimaryMap, SecondaryMap}; use crate::entity::{entity_impl, PrimaryMap, SecondaryMap};
/// Map of value locations.
pub type ValueLocations = SecondaryMap<Value, ValueLoc>;
/// Map of jump tables. /// Map of jump tables.
pub type JumpTables = PrimaryMap<JumpTable, JumpTableData>; pub type JumpTables = PrimaryMap<JumpTable, JumpTableData>;

View File

@@ -1,166 +0,0 @@
//! Value locations.
//!
//! The register allocator assigns every SSA value to either a register or a stack slot. This
//! assignment is represented by a `ValueLoc` object.
use crate::ir::StackSlot;
use crate::isa::{RegInfo, RegUnit};
use core::fmt;
#[cfg(feature = "enable-serde")]
use serde::{Deserialize, Serialize};
/// Value location.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum ValueLoc {
/// This value has not been assigned to a location yet.
Unassigned,
/// Value is assigned to a register.
Reg(RegUnit),
/// Value is assigned to a stack slot.
Stack(StackSlot),
}
impl Default for ValueLoc {
fn default() -> Self {
Self::Unassigned
}
}
impl ValueLoc {
/// Is this an assigned location? (That is, not `Unassigned`).
pub fn is_assigned(self) -> bool {
match self {
Self::Unassigned => false,
_ => true,
}
}
/// Get the register unit of this location, or panic.
pub fn unwrap_reg(self) -> RegUnit {
match self {
Self::Reg(ru) => ru,
_ => panic!("unwrap_reg expected register, found {:?}", self),
}
}
/// Get the stack slot of this location, or panic.
pub fn unwrap_stack(self) -> StackSlot {
match self {
Self::Stack(ss) => ss,
_ => panic!("unwrap_stack expected stack slot, found {:?}", self),
}
}
/// Return an object that can display this value location, using the register info from the
/// target ISA.
pub fn display<'a, R: Into<Option<&'a RegInfo>>>(self, regs: R) -> DisplayValueLoc<'a> {
DisplayValueLoc(self, regs.into())
}
}
/// Displaying a `ValueLoc` correctly requires the associated `RegInfo` from the target ISA.
/// Without the register info, register units are simply show as numbers.
///
/// The `DisplayValueLoc` type can display the contained `ValueLoc`.
pub struct DisplayValueLoc<'a>(ValueLoc, Option<&'a RegInfo>);
impl<'a> fmt::Display for DisplayValueLoc<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
ValueLoc::Unassigned => write!(f, "-"),
ValueLoc::Reg(ru) => match self.1 {
Some(regs) => write!(f, "{}", regs.display_regunit(ru)),
None => write!(f, "%{}", ru),
},
ValueLoc::Stack(ss) => write!(f, "{}", ss),
}
}
}
/// Function argument location.
///
/// The ABI specifies how arguments are passed to a function, and where return values appear after
/// the call. Just like a `ValueLoc`, function arguments can be passed in registers or on the
/// stack.
///
/// Function arguments on the stack are accessed differently for the incoming arguments to the
/// current function and the outgoing arguments to a called external function. For this reason,
/// the location of stack arguments is described as an offset into the array of function arguments
/// on the stack.
///
/// An `ArgumentLoc` can be translated to a `ValueLoc` only when we know if we're talking about an
/// incoming argument or an outgoing argument.
///
/// - For stack arguments, different `StackSlot` entities are used to represent incoming and
/// outgoing arguments.
/// - For register arguments, there is usually no difference, but if we ever add support for a
/// register-window ISA like SPARC, register arguments would also need to be translated.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
pub enum ArgumentLoc {
/// This argument has not been assigned to a location yet.
Unassigned,
/// Argument is passed in a register.
Reg(RegUnit),
/// Argument is passed on the stack, at the given byte offset into the argument array.
Stack(i32),
}
impl Default for ArgumentLoc {
fn default() -> Self {
Self::Unassigned
}
}
impl ArgumentLoc {
/// Is this an assigned location? (That is, not `Unassigned`).
pub fn is_assigned(self) -> bool {
match self {
Self::Unassigned => false,
_ => true,
}
}
/// Is this a register location?
pub fn is_reg(self) -> bool {
match self {
Self::Reg(_) => true,
_ => false,
}
}
/// Is this a stack location?
pub fn is_stack(self) -> bool {
match self {
Self::Stack(_) => true,
_ => false,
}
}
/// Return an object that can display this argument location, using the register info from the
/// target ISA.
pub fn display<'a, R: Into<Option<&'a RegInfo>>>(self, regs: R) -> DisplayArgumentLoc<'a> {
DisplayArgumentLoc(self, regs.into())
}
}
/// Displaying a `ArgumentLoc` correctly requires the associated `RegInfo` from the target ISA.
/// Without the register info, register units are simply show as numbers.
///
/// The `DisplayArgumentLoc` type can display the contained `ArgumentLoc`.
pub struct DisplayArgumentLoc<'a>(ArgumentLoc, Option<&'a RegInfo>);
impl<'a> fmt::Display for DisplayArgumentLoc<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
ArgumentLoc::Unassigned => write!(f, "-"),
ArgumentLoc::Reg(ru) => match self.1 {
Some(regs) => write!(f, "{}", regs.display_regunit(ru)),
None => write!(f, "%{}", ru),
},
ArgumentLoc::Stack(offset) => write!(f, "{}", offset),
}
}
}

View File

@@ -1407,7 +1407,7 @@ pub(crate) fn lower_i64x2_mul<C: LowerCtx<I = Inst>>(c: &mut C, insn: IRInst) {
// rd = |dg+ch|be+af||dg+ch|be+af| // rd = |dg+ch|be+af||dg+ch|be+af|
c.emit(Inst::VecRRR { c.emit(Inst::VecRRR {
alu_op: VecALUOp::Addp, alu_op: VecALUOp::Addp,
rd: rd, rd,
rn: rd.to_reg(), rn: rd.to_reg(),
rm: rd.to_reg(), rm: rd.to_reg(),
size: VectorSize::Size32x4, size: VectorSize::Size32x4,

View File

@@ -482,9 +482,9 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
ctx.emit(Inst::AluRRRR { ctx.emit(Inst::AluRRRR {
alu_op: ALUOp3::MSub64, alu_op: ALUOp3::MSub64,
rd: rd, rd,
rn: rd.to_reg(), rn: rd.to_reg(),
rm: rm, rm,
ra: rn, ra: rn,
}); });
} else { } else {
@@ -2165,16 +2165,11 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
Opcode::Spill Opcode::Spill
| Opcode::Fill | Opcode::Fill
| Opcode::FillNop | Opcode::FillNop
| Opcode::Regmove
| Opcode::CopySpecial
| Opcode::CopyToSsa
| Opcode::CopyNop | Opcode::CopyNop
| Opcode::AdjustSpDown | Opcode::AdjustSpDown
| Opcode::AdjustSpUpImm | Opcode::AdjustSpUpImm
| Opcode::AdjustSpDownImm | Opcode::AdjustSpDownImm
| Opcode::IfcmpSp | Opcode::IfcmpSp => {
| Opcode::Regspill
| Opcode::Regfill => {
panic!("Unused opcode should not be encountered."); panic!("Unused opcode should not be encountered.");
} }
@@ -2412,7 +2407,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
alu_op: VecALUOp::Umaxp, alu_op: VecALUOp::Umaxp,
rd: tmp, rd: tmp,
rn: rm, rn: rm,
rm: rm, rm,
size, size,
}); });
} else { } else {
@@ -2835,9 +2830,9 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
let rm = put_input_in_reg(ctx, inputs[1], NarrowValueMode::None); let rm = put_input_in_reg(ctx, inputs[1], NarrowValueMode::None);
ctx.emit(Inst::VecRRR { ctx.emit(Inst::VecRRR {
alu_op: VecALUOp::Addp, alu_op: VecALUOp::Addp,
rd: rd, rd,
rn: rn, rn,
rm: rm, rm,
size: VectorSize::from_ty(ty), size: VectorSize::from_ty(ty),
}); });
} }
@@ -3446,7 +3441,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
ctx.emit(Inst::FpuRRR { ctx.emit(Inst::FpuRRR {
fpu_op: choose_32_64(in_ty, FPUOp2::Min32, FPUOp2::Min64), fpu_op: choose_32_64(in_ty, FPUOp2::Min32, FPUOp2::Min64),
rd: rtmp2, rd: rtmp2,
rn: rn, rn,
rm: rtmp1.to_reg(), rm: rtmp1.to_reg(),
}); });
if in_bits == 32 { if in_bits == 32 {
@@ -3468,7 +3463,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
} }
} }
if in_bits == 32 { if in_bits == 32 {
ctx.emit(Inst::FpuCmp32 { rn: rn, rm: rn }); ctx.emit(Inst::FpuCmp32 { rn, rm: rn });
ctx.emit(Inst::FpuCSel32 { ctx.emit(Inst::FpuCSel32 {
rd: rtmp2, rd: rtmp2,
rn: rtmp1.to_reg(), rn: rtmp1.to_reg(),
@@ -3476,7 +3471,7 @@ pub(crate) fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
cond: Cond::Ne, cond: Cond::Ne,
}); });
} else { } else {
ctx.emit(Inst::FpuCmp64 { rn: rn, rm: rn }); ctx.emit(Inst::FpuCmp64 { rn, rm: rn });
ctx.emit(Inst::FpuCSel64 { ctx.emit(Inst::FpuCSel64 {
rd: rtmp2, rd: rtmp2,
rn: rtmp1.to_reg(), rn: rtmp1.to_reg(),

View File

@@ -11,7 +11,6 @@ use crate::machinst::{
use crate::result::CodegenResult; use crate::result::CodegenResult;
use crate::settings as shared_settings; use crate::settings as shared_settings;
use alloc::{boxed::Box, vec::Vec}; use alloc::{boxed::Box, vec::Vec};
use core::hash::{Hash, Hasher};
use regalloc::{PrettyPrint, RealRegUniverse}; use regalloc::{PrettyPrint, RealRegUniverse};
use target_lexicon::{Aarch64Architecture, Architecture, Triple}; use target_lexicon::{Aarch64Architecture, Architecture, Triple};
@@ -111,11 +110,6 @@ impl MachBackend for AArch64Backend {
self.isa_flags.iter().collect() self.isa_flags.iter().collect()
} }
fn hash_all_flags(&self, mut hasher: &mut dyn Hasher) {
self.flags.hash(&mut hasher);
self.isa_flags.hash(&mut hasher);
}
fn reg_universe(&self) -> &RealRegUniverse { fn reg_universe(&self) -> &RealRegUniverse {
&self.reg_universe &self.reg_universe
} }
@@ -126,13 +120,6 @@ impl MachBackend for AArch64Backend {
IntCC::UnsignedGreaterThanOrEqual IntCC::UnsignedGreaterThanOrEqual
} }
fn unsigned_sub_overflow_condition(&self) -> IntCC {
// unsigned `<`; this corresponds to the carry flag cleared on aarch64, which happens on
// underflow of a subtract (aarch64 follows a carry-cleared-on-borrow convention, the
// opposite of x86).
IntCC::UnsignedLessThan
}
#[cfg(feature = "unwind")] #[cfg(feature = "unwind")]
fn emit_unwind_info( fn emit_unwind_info(
&self, &self,

View File

@@ -11,7 +11,6 @@ use crate::result::CodegenResult;
use crate::settings; use crate::settings;
use alloc::{boxed::Box, vec::Vec}; use alloc::{boxed::Box, vec::Vec};
use core::hash::{Hash, Hasher};
use regalloc::{PrettyPrint, RealRegUniverse}; use regalloc::{PrettyPrint, RealRegUniverse};
use target_lexicon::{Architecture, ArmArchitecture, Triple}; use target_lexicon::{Architecture, ArmArchitecture, Triple};
@@ -101,10 +100,6 @@ impl MachBackend for Arm32Backend {
Vec::new() Vec::new()
} }
fn hash_all_flags(&self, mut hasher: &mut dyn Hasher) {
self.flags.hash(&mut hasher);
}
fn reg_universe(&self) -> &RealRegUniverse { fn reg_universe(&self) -> &RealRegUniverse {
&self.reg_universe &self.reg_universe
} }
@@ -114,11 +109,6 @@ impl MachBackend for Arm32Backend {
IntCC::UnsignedGreaterThanOrEqual IntCC::UnsignedGreaterThanOrEqual
} }
fn unsigned_sub_overflow_condition(&self) -> IntCC {
// Carry flag clear.
IntCC::UnsignedLessThan
}
fn text_section_builder(&self, num_funcs: u32) -> Box<dyn TextSectionBuilder> { fn text_section_builder(&self, num_funcs: u32) -> Box<dyn TextSectionBuilder> {
Box::new(MachTextSectionBuilder::<inst::Inst>::new(num_funcs)) Box::new(MachTextSectionBuilder::<inst::Inst>::new(num_funcs))
} }

View File

@@ -8,83 +8,6 @@
//! are satisfied. //! are satisfied.
use crate::binemit::CodeOffset; use crate::binemit::CodeOffset;
use crate::ir::ValueLoc;
use crate::isa::{RegClass, RegUnit};
/// Register constraint for a single value operand or instruction result.
#[derive(PartialEq, Debug)]
pub struct OperandConstraint {
/// The kind of constraint.
pub kind: ConstraintKind,
/// The register class of the operand.
///
/// This applies to all kinds of constraints, but with slightly different meaning.
pub regclass: RegClass,
}
impl OperandConstraint {
/// Check if this operand constraint is satisfied by the given value location.
/// For tied constraints, this only checks the register class, not that the
/// counterpart operand has the same value location.
pub fn satisfied(&self, loc: ValueLoc) -> bool {
match self.kind {
ConstraintKind::Reg | ConstraintKind::Tied(_) => {
if let ValueLoc::Reg(reg) = loc {
self.regclass.contains(reg)
} else {
false
}
}
ConstraintKind::FixedReg(reg) | ConstraintKind::FixedTied(reg) => {
loc == ValueLoc::Reg(reg) && self.regclass.contains(reg)
}
ConstraintKind::Stack => {
if let ValueLoc::Stack(_) = loc {
true
} else {
false
}
}
}
}
}
/// The different kinds of operand constraints.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ConstraintKind {
/// This operand or result must be a register from the given register class.
Reg,
/// This operand or result must be a fixed register.
///
/// The constraint's `regclass` field is the top-level register class containing the fixed
/// register.
FixedReg(RegUnit),
/// This result value must use the same register as an input value operand.
///
/// The associated number is the index of the input value operand this result is tied to. The
/// constraint's `regclass` field is the same as the tied operand's register class.
///
/// When an (in, out) operand pair is tied, this constraint kind appears in both the `ins` and
/// the `outs` arrays. The constraint for the in operand is `Tied(out)`, and the constraint for
/// the out operand is `Tied(in)`.
Tied(u8),
/// This operand must be a fixed register, and it has a tied counterpart.
///
/// This works just like `FixedReg`, but additionally indicates that there are identical
/// input/output operands for this fixed register. For an input operand, this means that the
/// value will be clobbered by the instruction
FixedTied(RegUnit),
/// This operand must be a value in a stack slot.
///
/// The constraint's `regclass` field is the register class that would normally be used to load
/// and store values of this type.
Stack,
}
/// Constraints on the range of a branch instruction. /// Constraints on the range of a branch instruction.
/// ///

View File

@@ -44,8 +44,7 @@
//! concurrent function compilations. //! concurrent function compilations.
pub use crate::isa::call_conv::CallConv; pub use crate::isa::call_conv::CallConv;
pub use crate::isa::constraints::{BranchRange, ConstraintKind, OperandConstraint}; pub use crate::isa::constraints::BranchRange;
pub use crate::isa::registers::{regs_overlap, RegClass, RegClassIndex, RegInfo, RegUnit};
pub use crate::isa::stack::{StackBase, StackBaseMask, StackRef}; pub use crate::isa::stack::{StackBase, StackBaseMask, StackRef};
use crate::flowgraph; use crate::flowgraph;
@@ -57,10 +56,8 @@ use crate::result::CodegenResult;
use crate::settings; use crate::settings;
use crate::settings::SetResult; use crate::settings::SetResult;
use alloc::{boxed::Box, vec::Vec}; use alloc::{boxed::Box, vec::Vec};
use core::any::Any;
use core::fmt; use core::fmt;
use core::fmt::{Debug, Formatter}; use core::fmt::{Debug, Formatter};
use core::hash::Hasher;
use target_lexicon::{triple, Architecture, OperatingSystem, PointerWidth, Triple}; use target_lexicon::{triple, Architecture, OperatingSystem, PointerWidth, Triple};
// This module is made public here for benchmarking purposes. No guarantees are // This module is made public here for benchmarking purposes. No guarantees are
@@ -81,7 +78,6 @@ pub mod unwind;
mod call_conv; mod call_conv;
mod constraints; mod constraints;
pub mod registers;
mod stack; mod stack;
#[cfg(test)] #[cfg(test)]
@@ -220,8 +216,9 @@ impl TargetFrontendConfig {
} }
} }
/// Methods that are specialized to a target ISA. Implies a Display trait that shows the /// Methods that are specialized to a target ISA.
/// shared flags, as well as any isa-specific flags. ///
/// Implies a Display trait that shows the shared flags, as well as any ISA-specific flags.
pub trait TargetIsa: fmt::Display + Send + Sync { pub trait TargetIsa: fmt::Display + Send + Sync {
/// Get the name of this ISA. /// Get the name of this ISA.
fn name(&self) -> &'static str; fn name(&self) -> &'static str;
@@ -235,85 +232,6 @@ pub trait TargetIsa: fmt::Display + Send + Sync {
/// Get the ISA-dependent flag values that were used to make this trait object. /// Get the ISA-dependent flag values that were used to make this trait object.
fn isa_flags(&self) -> Vec<settings::Value>; fn isa_flags(&self) -> Vec<settings::Value>;
/// Hashes all flags, both ISA-independent and ISA-specific, into the
/// specified hasher.
fn hash_all_flags(&self, hasher: &mut dyn Hasher);
/// Get the default calling convention of this target.
fn default_call_conv(&self) -> CallConv {
CallConv::triple_default(self.triple())
}
/// Get the endianness of this ISA.
fn endianness(&self) -> ir::Endianness {
match self.triple().endianness().unwrap() {
target_lexicon::Endianness::Little => ir::Endianness::Little,
target_lexicon::Endianness::Big => ir::Endianness::Big,
}
}
/// Returns the code (text) section alignment for this ISA.
fn code_section_alignment(&self) -> u64 {
use target_lexicon::*;
match (self.triple().operating_system, self.triple().architecture) {
(
OperatingSystem::MacOSX { .. }
| OperatingSystem::Darwin
| OperatingSystem::Ios
| OperatingSystem::Tvos,
Architecture::Aarch64(..),
) => 0x4000,
_ => 0x1000,
}
}
/// Get the pointer type of this ISA.
fn pointer_type(&self) -> ir::Type {
ir::Type::int(u16::from(self.pointer_bits())).unwrap()
}
/// Get the width of pointers on this ISA.
fn pointer_width(&self) -> PointerWidth {
self.triple().pointer_width().unwrap()
}
/// Get the width of pointers on this ISA, in units of bits.
fn pointer_bits(&self) -> u8 {
self.pointer_width().bits()
}
/// Get the width of pointers on this ISA, in units of bytes.
fn pointer_bytes(&self) -> u8 {
self.pointer_width().bytes()
}
/// Get the information needed by frontends producing Cranelift IR.
fn frontend_config(&self) -> TargetFrontendConfig {
TargetFrontendConfig {
default_call_conv: self.default_call_conv(),
pointer_width: self.pointer_width(),
}
}
/// Does the CPU implement scalar comparisons using a CPU flags register?
fn uses_cpu_flags(&self) -> bool {
false
}
/// Does the CPU implement multi-register addressing?
fn uses_complex_addresses(&self) -> bool {
false
}
/// Get a data structure describing the registers in this ISA.
fn register_info(&self) -> RegInfo;
#[cfg(feature = "unwind")]
/// Map a Cranelift register to its corresponding DWARF register.
fn map_dwarf_register(&self, _: RegUnit) -> Result<u16, RegisterMappingError> {
Err(RegisterMappingError::UnsupportedArchitecture)
}
#[cfg(feature = "unwind")] #[cfg(feature = "unwind")]
/// Map a regalloc::Reg to its corresponding DWARF register. /// Map a regalloc::Reg to its corresponding DWARF register.
fn map_regalloc_reg_to_dwarf(&self, _: ::regalloc::Reg) -> Result<u16, RegisterMappingError> { fn map_regalloc_reg_to_dwarf(&self, _: ::regalloc::Reg) -> Result<u16, RegisterMappingError> {
@@ -323,21 +241,6 @@ pub trait TargetIsa: fmt::Display + Send + Sync {
/// IntCC condition for Unsigned Addition Overflow (Carry). /// IntCC condition for Unsigned Addition Overflow (Carry).
fn unsigned_add_overflow_condition(&self) -> ir::condcodes::IntCC; fn unsigned_add_overflow_condition(&self) -> ir::condcodes::IntCC;
/// IntCC condition for Unsigned Subtraction Overflow (Borrow/Carry).
fn unsigned_sub_overflow_condition(&self) -> ir::condcodes::IntCC;
/// Returns the flavor of unwind information emitted for this target.
fn unwind_info_kind(&self) -> UnwindInfoKind {
match self.triple().operating_system {
#[cfg(feature = "unwind")]
OperatingSystem::Windows => UnwindInfoKind::Windows,
#[cfg(feature = "unwind")]
_ => UnwindInfoKind::SystemV,
#[cfg(not(feature = "unwind"))]
_ => UnwindInfoKind::None,
}
}
/// Creates unwind information for the function. /// Creates unwind information for the function.
/// ///
/// Returns `None` if there is no unwind information for the function. /// Returns `None` if there is no unwind information for the function.
@@ -363,10 +266,77 @@ pub trait TargetIsa: fmt::Display + Send + Sync {
fn get_mach_backend(&self) -> Option<&dyn MachBackend> { fn get_mach_backend(&self) -> Option<&dyn MachBackend> {
None None
} }
}
/// Return an [Any] reference for downcasting to the ISA-specific implementation of this trait /// Methods implemented for free for target ISA!
/// with `isa.as_any().downcast_ref::<isa::foo::Isa>()`. impl<'a> dyn TargetIsa + 'a {
fn as_any(&self) -> &dyn Any; /// Get the default calling convention of this target.
pub fn default_call_conv(&self) -> CallConv {
CallConv::triple_default(self.triple())
}
/// Get the endianness of this ISA.
pub fn endianness(&self) -> ir::Endianness {
match self.triple().endianness().unwrap() {
target_lexicon::Endianness::Little => ir::Endianness::Little,
target_lexicon::Endianness::Big => ir::Endianness::Big,
}
}
/// Returns the code (text) section alignment for this ISA.
pub fn code_section_alignment(&self) -> u64 {
use target_lexicon::*;
match (self.triple().operating_system, self.triple().architecture) {
(
OperatingSystem::MacOSX { .. }
| OperatingSystem::Darwin
| OperatingSystem::Ios
| OperatingSystem::Tvos,
Architecture::Aarch64(..),
) => 0x4000,
_ => 0x1000,
}
}
/// Get the pointer type of this ISA.
pub fn pointer_type(&self) -> ir::Type {
ir::Type::int(u16::from(self.pointer_bits())).unwrap()
}
/// Get the width of pointers on this ISA.
pub(crate) fn pointer_width(&self) -> PointerWidth {
self.triple().pointer_width().unwrap()
}
/// Get the width of pointers on this ISA, in units of bits.
pub fn pointer_bits(&self) -> u8 {
self.pointer_width().bits()
}
/// Get the width of pointers on this ISA, in units of bytes.
pub fn pointer_bytes(&self) -> u8 {
self.pointer_width().bytes()
}
/// Get the information needed by frontends producing Cranelift IR.
pub fn frontend_config(&self) -> TargetFrontendConfig {
TargetFrontendConfig {
default_call_conv: self.default_call_conv(),
pointer_width: self.pointer_width(),
}
}
/// Returns the flavor of unwind information emitted for this target.
pub(crate) fn unwind_info_kind(&self) -> UnwindInfoKind {
match self.triple().operating_system {
#[cfg(feature = "unwind")]
OperatingSystem::Windows => UnwindInfoKind::Windows,
#[cfg(feature = "unwind")]
_ => UnwindInfoKind::SystemV,
#[cfg(not(feature = "unwind"))]
_ => UnwindInfoKind::None,
}
}
} }
impl Debug for &dyn TargetIsa { impl Debug for &dyn TargetIsa {

View File

@@ -1,360 +0,0 @@
//! Data structures describing the registers in an ISA.
use crate::entity::EntityRef;
use core::fmt;
/// Register units are the smallest units of register allocation.
///
/// Normally there is a 1-1 correspondence between registers and register units, but when an ISA
/// has aliasing registers, the aliasing can be modeled with registers that cover multiple
/// register units.
///
/// The register allocator will enforce that each register unit only gets used for one thing.
pub type RegUnit = u16;
/// A bit mask indexed by register classes.
///
/// The size of this type is determined by the ISA with the most register classes.
pub type RegClassMask = u32;
/// A bit mask indexed by register units.
///
/// The size of this type is determined by the target ISA that has the most register units defined.
/// Currently that is arm32 which has 64+16 units.
pub type RegUnitMask = [RegClassMask; 3];
/// The register units in a target ISA are divided into disjoint register banks. Each bank covers a
/// contiguous range of register units.
///
/// The `RegBank` struct provides a static description of a register bank.
pub struct RegBank {
/// The name of this register bank as defined in the ISA's DSL definition.
pub name: &'static str,
/// The first register unit in this bank.
pub first_unit: RegUnit,
/// The total number of register units in this bank.
pub units: RegUnit,
/// Array of specially named register units. This array can be shorter than the number of units
/// in the bank.
pub names: &'static [&'static str],
/// Name prefix to use for those register units in the bank not covered by the `names` array.
/// The remaining register units will be named this prefix followed by their decimal offset in
/// the bank. So with a prefix `r`, registers will be named `r8`, `r9`, ...
pub prefix: &'static str,
/// Index of the first top-level register class in this bank.
pub first_toprc: usize,
/// Number of top-level register classes in this bank.
///
/// The top-level register classes in a bank are guaranteed to be numbered sequentially from
/// `first_toprc`, and all top-level register classes across banks come before any sub-classes.
pub num_toprcs: usize,
/// Is register pressure tracking enabled for this bank?
pub pressure_tracking: bool,
}
impl RegBank {
/// Does this bank contain `regunit`?
fn contains(&self, regunit: RegUnit) -> bool {
regunit >= self.first_unit && regunit - self.first_unit < self.units
}
/// Try to parse a regunit name. The name is not expected to begin with `%`.
fn parse_regunit(&self, name: &str) -> Option<RegUnit> {
match self.names.iter().position(|&x| x == name) {
Some(offset) => {
// This is one of the special-cased names.
Some(offset as RegUnit)
}
None => {
// Try a regular prefixed name.
if name.starts_with(self.prefix) {
name[self.prefix.len()..].parse().ok()
} else {
None
}
}
}
.and_then(|offset| {
if offset < self.units {
Some(offset + self.first_unit)
} else {
None
}
})
}
/// Write `regunit` to `w`, assuming that it belongs to this bank.
/// All regunits are written with a `%` prefix.
fn write_regunit(&self, f: &mut fmt::Formatter, regunit: RegUnit) -> fmt::Result {
let offset = regunit - self.first_unit;
assert!(offset < self.units);
if (offset as usize) < self.names.len() {
write!(f, "%{}", self.names[offset as usize])
} else {
write!(f, "%{}{}", self.prefix, offset)
}
}
}
/// A register class reference.
///
/// All register classes are statically defined in tables generated from the meta descriptions.
pub type RegClass = &'static RegClassData;
/// Data about a register class.
///
/// A register class represents a subset of the registers in a bank. It describes the set of
/// permitted registers for a register operand in a given encoding of an instruction.
///
/// A register class can be a subset of another register class. The top-level register classes are
/// disjoint.
pub struct RegClassData {
/// The name of the register class.
pub name: &'static str,
/// The index of this class in the ISA's RegInfo description.
pub index: u8,
/// How many register units to allocate per register.
pub width: u8,
/// Index of the register bank this class belongs to.
pub bank: u8,
/// Index of the top-level register class contains this one.
pub toprc: u8,
/// The first register unit in this class.
pub first: RegUnit,
/// Bit-mask of sub-classes of this register class, including itself.
///
/// Bits correspond to RC indexes.
pub subclasses: RegClassMask,
/// Mask of register units in the class. If `width > 1`, the mask only has a bit set for the
/// first register unit in each allocatable register.
pub mask: RegUnitMask,
/// The global `RegInfo` instance containing this register class.
pub info: &'static RegInfo,
/// The "pinned" register of the associated register bank.
///
/// This register must be non-volatile (callee-preserved) and must not be the fixed
/// output register of any instruction.
pub pinned_reg: Option<RegUnit>,
}
impl RegClassData {
/// Get the register class index corresponding to the intersection of `self` and `other`.
///
/// This register class is guaranteed to exist if the register classes overlap. If the register
/// classes don't overlap, returns `None`.
pub fn intersect_index(&self, other: RegClass) -> Option<RegClassIndex> {
// Compute the set of common subclasses.
let mask = self.subclasses & other.subclasses;
if mask == 0 {
// No overlap.
None
} else {
// Register class indexes are topologically ordered, so the largest common subclass has
// the smallest index.
Some(RegClassIndex(mask.trailing_zeros() as u8))
}
}
/// Get the intersection of `self` and `other`.
pub fn intersect(&self, other: RegClass) -> Option<RegClass> {
self.intersect_index(other).map(|rci| self.info.rc(rci))
}
/// Returns true if `other` is a subclass of this register class.
/// A register class is considered to be a subclass of itself.
pub fn has_subclass<RCI: Into<RegClassIndex>>(&self, other: RCI) -> bool {
self.subclasses & (1 << other.into().0) as u32 != 0
}
/// Get the top-level register class containing this class.
pub fn toprc(&self) -> RegClass {
self.info.rc(RegClassIndex(self.toprc))
}
/// Get a specific register unit in this class.
pub fn unit(&self, offset: usize) -> RegUnit {
let uoffset = offset * usize::from(self.width);
self.first + uoffset as RegUnit
}
/// Does this register class contain `regunit`?
pub fn contains(&self, regunit: RegUnit) -> bool {
self.mask[(regunit / 32) as usize] & (1u32 << (regunit % 32) as u32) != 0
}
/// If the pinned register is used, is the given regunit the pinned register of this class?
#[inline]
pub fn is_pinned_reg(&self, enabled: bool, regunit: RegUnit) -> bool {
enabled
&& self
.pinned_reg
.map_or(false, |pinned_reg| pinned_reg == regunit)
}
/// Calculate the index of the register inside the class.
pub fn index_of(&self, regunit: RegUnit) -> u16 {
assert!(
self.contains(regunit),
"the {} register class does not contain {}",
self.name,
regunit
);
regunit - self.first
}
}
impl fmt::Display for RegClassData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.name)
}
}
impl fmt::Debug for RegClassData {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(self.name)
}
}
/// Within an ISA, register classes are uniquely identified by their index.
impl PartialEq for RegClassData {
fn eq(&self, other: &Self) -> bool {
self.index == other.index
}
}
/// A small reference to a register class.
///
/// Use this when storing register classes in compact data structures. The `RegInfo::rc()` method
/// can be used to get the real register class reference back.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct RegClassIndex(u8);
impl EntityRef for RegClassIndex {
fn new(idx: usize) -> Self {
Self(idx as u8)
}
fn index(self) -> usize {
usize::from(self.0)
}
}
impl From<RegClass> for RegClassIndex {
fn from(rc: RegClass) -> Self {
Self(rc.index)
}
}
impl fmt::Display for RegClassIndex {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "rci{}", self.0)
}
}
/// Test of two registers overlap.
///
/// A register is identified as a `(RegClass, RegUnit)` pair. The register class is needed to
/// determine the width (in regunits) of the register.
pub fn regs_overlap(rc1: RegClass, reg1: RegUnit, rc2: RegClass, reg2: RegUnit) -> bool {
let end1 = reg1 + RegUnit::from(rc1.width);
let end2 = reg2 + RegUnit::from(rc2.width);
!(end1 <= reg2 || end2 <= reg1)
}
/// Information about the registers in an ISA.
///
/// The `RegUnit` data structure collects all relevant static information about the registers in an
/// ISA.
#[derive(Clone)]
pub struct RegInfo {
/// All register banks, ordered by their `first_unit`. The register banks are disjoint, but
/// there may be holes of unused register unit numbers between banks due to alignment.
pub banks: &'static [RegBank],
/// All register classes ordered topologically so a sub-class always follows its parent.
pub classes: &'static [RegClass],
}
impl RegInfo {
/// Get the register bank holding `regunit`.
pub fn bank_containing_regunit(&self, regunit: RegUnit) -> Option<&RegBank> {
// We could do a binary search, but most ISAs have only two register banks...
self.banks.iter().find(|b| b.contains(regunit))
}
/// Try to parse a regunit name. The name is not expected to begin with `%`.
pub fn parse_regunit(&self, name: &str) -> Option<RegUnit> {
self.banks
.iter()
.filter_map(|b| b.parse_regunit(name))
.next()
}
/// Make a temporary object that can display a register unit.
pub fn display_regunit(&self, regunit: RegUnit) -> DisplayRegUnit {
DisplayRegUnit {
regunit,
reginfo: self,
}
}
/// Get the register class corresponding to `idx`.
pub fn rc(&self, idx: RegClassIndex) -> RegClass {
self.classes[idx.index()]
}
/// Get the top-level register class containing the `idx` class.
pub fn toprc(&self, idx: RegClassIndex) -> RegClass {
self.classes[self.rc(idx).toprc as usize]
}
}
/// Temporary object that holds enough information to print a register unit.
pub struct DisplayRegUnit<'a> {
regunit: RegUnit,
reginfo: &'a RegInfo,
}
impl<'a> fmt::Display for DisplayRegUnit<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.reginfo.bank_containing_regunit(self.regunit) {
Some(b) => b.write_regunit(f, self.regunit),
None => write!(f, "%INVALID{}", self.regunit),
}
}
}
#[test]
fn assert_sizes() {
use cranelift_codegen_shared::constants;
use std::mem::size_of;
// In these tests, size_of returns number of bytes: we actually want the number of bits, so
// multiply these by 8.
assert!(
(size_of::<RegClassMask>() * 8) <= constants::MAX_NUM_REG_CLASSES,
"need to bump MAX_NUM_REG_CLASSES or change RegClassMask type"
);
assert!(
constants::MAX_NUM_REG_CLASSES < (1 << (size_of::<RegClassIndex>() * 8)),
"need to change RegClassIndex's type to a wider type"
);
}

View File

@@ -661,16 +661,7 @@ fn lower_icmp_to_flags<C: LowerCtx<I = Inst>>(
(false, true) => NarrowValueMode::SignExtend64, (false, true) => NarrowValueMode::SignExtend64,
(false, false) => NarrowValueMode::ZeroExtend64, (false, false) => NarrowValueMode::ZeroExtend64,
}; };
let inputs = [ let inputs = [InsnInput { insn, input: 0 }, InsnInput { insn, input: 1 }];
InsnInput {
insn: insn,
input: 0,
},
InsnInput {
insn: insn,
input: 1,
},
];
let ty = ctx.input_ty(insn, 0); let ty = ctx.input_ty(insn, 0);
let rn = put_input_in_reg(ctx, inputs[0], narrow_mode); let rn = put_input_in_reg(ctx, inputs[0], narrow_mode);
if is_signed { if is_signed {
@@ -759,16 +750,7 @@ fn lower_icmp_to_flags<C: LowerCtx<I = Inst>>(
fn lower_fcmp_to_flags<C: LowerCtx<I = Inst>>(ctx: &mut C, insn: IRInst) { fn lower_fcmp_to_flags<C: LowerCtx<I = Inst>>(ctx: &mut C, insn: IRInst) {
let ty = ctx.input_ty(insn, 0); let ty = ctx.input_ty(insn, 0);
let bits = ty_bits(ty); let bits = ty_bits(ty);
let inputs = [ let inputs = [InsnInput { insn, input: 0 }, InsnInput { insn, input: 1 }];
InsnInput {
insn: insn,
input: 0,
},
InsnInput {
insn: insn,
input: 1,
},
];
let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None); let rn = put_input_in_reg(ctx, inputs[0], NarrowValueMode::None);
let rm = put_input_in_reg(ctx, inputs[1], NarrowValueMode::None); let rm = put_input_in_reg(ctx, inputs[1], NarrowValueMode::None);
match bits { match bits {
@@ -2909,17 +2891,12 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
Opcode::Spill Opcode::Spill
| Opcode::Fill | Opcode::Fill
| Opcode::FillNop | Opcode::FillNop
| Opcode::Regmove
| Opcode::CopySpecial
| Opcode::CopyToSsa
| Opcode::CopyNop | Opcode::CopyNop
| Opcode::AdjustSpDown | Opcode::AdjustSpDown
| Opcode::AdjustSpUpImm | Opcode::AdjustSpUpImm
| Opcode::AdjustSpDownImm | Opcode::AdjustSpDownImm
| Opcode::DummySargT | Opcode::DummySargT
| Opcode::IfcmpSp | Opcode::IfcmpSp => {
| Opcode::Regspill
| Opcode::Regfill => {
panic!("Unused opcode should not be encountered."); panic!("Unused opcode should not be encountered.");
} }
@@ -3117,7 +3094,7 @@ fn lower_branch<C: LowerCtx<I = Inst>>(
info: Box::new(JTSequenceInfo { info: Box::new(JTSequenceInfo {
default_target, default_target,
targets: jt_targets, targets: jt_targets,
targets_for_term: targets_for_term, targets_for_term,
}), }),
}); });
} }

View File

@@ -14,7 +14,6 @@ use crate::result::CodegenResult;
use crate::settings as shared_settings; use crate::settings as shared_settings;
use alloc::{boxed::Box, vec::Vec}; use alloc::{boxed::Box, vec::Vec};
use core::hash::{Hash, Hasher};
use regalloc::{PrettyPrint, RealRegUniverse, Reg}; use regalloc::{PrettyPrint, RealRegUniverse, Reg};
use target_lexicon::{Architecture, Triple}; use target_lexicon::{Architecture, Triple};
@@ -114,11 +113,6 @@ impl MachBackend for S390xBackend {
self.isa_flags.iter().collect() self.isa_flags.iter().collect()
} }
fn hash_all_flags(&self, mut hasher: &mut dyn Hasher) {
self.flags.hash(&mut hasher);
self.isa_flags.hash(&mut hasher);
}
fn reg_universe(&self) -> &RealRegUniverse { fn reg_universe(&self) -> &RealRegUniverse {
&self.reg_universe &self.reg_universe
} }
@@ -132,10 +126,6 @@ impl MachBackend for S390xBackend {
IntCC::UnsignedGreaterThan IntCC::UnsignedGreaterThan
} }
fn unsigned_sub_overflow_condition(&self) -> IntCC {
unimplemented!()
}
#[cfg(feature = "unwind")] #[cfg(feature = "unwind")]
fn emit_unwind_info( fn emit_unwind_info(
&self, &self,

View File

@@ -2,9 +2,7 @@
#![allow(dead_code)] #![allow(dead_code)]
use crate::binemit::{Addend, CodeOffset, CodeSink, Reloc}; use crate::binemit::{Addend, CodeOffset, CodeSink, Reloc};
use crate::ir::Value; use crate::ir::{ConstantOffset, ExternalName, JumpTable, Opcode, SourceLoc, TrapCode};
use crate::ir::{ConstantOffset, ExternalName, Function, JumpTable, Opcode, SourceLoc, TrapCode};
use crate::isa::TargetIsa;
use alloc::vec::Vec; use alloc::vec::Vec;
use std::string::String; use std::string::String;
@@ -80,7 +78,5 @@ impl CodeSink for TestCodeSink {
fn end_codegen(&mut self) {} fn end_codegen(&mut self) {}
fn add_stack_map(&mut self, _val_list: &[Value], _func: &Function, _isa: &dyn TargetIsa) {}
fn add_call_site(&mut self, _opcode: Opcode, _srcloc: SourceLoc) {} fn add_call_site(&mut self, _opcode: Opcode, _srcloc: SourceLoc) {}
} }

View File

@@ -6865,16 +6865,11 @@ fn lower_insn_to_regs<C: LowerCtx<I = Inst>>(
Opcode::Spill Opcode::Spill
| Opcode::Fill | Opcode::Fill
| Opcode::FillNop | Opcode::FillNop
| Opcode::Regmove
| Opcode::CopySpecial
| Opcode::CopyToSsa
| Opcode::CopyNop | Opcode::CopyNop
| Opcode::AdjustSpDown | Opcode::AdjustSpDown
| Opcode::AdjustSpUpImm | Opcode::AdjustSpUpImm
| Opcode::AdjustSpDownImm | Opcode::AdjustSpDownImm
| Opcode::IfcmpSp | Opcode::IfcmpSp
| Opcode::Regspill
| Opcode::Regfill
| Opcode::Copy | Opcode::Copy
| Opcode::DummySargT => { | Opcode::DummySargT => {
panic!("Unused opcode should not be encountered."); panic!("Unused opcode should not be encountered.");

View File

@@ -15,7 +15,6 @@ use crate::machinst::{
use crate::result::CodegenResult; use crate::result::CodegenResult;
use crate::settings::{self as shared_settings, Flags}; use crate::settings::{self as shared_settings, Flags};
use alloc::{boxed::Box, vec::Vec}; use alloc::{boxed::Box, vec::Vec};
use core::hash::{Hash, Hasher};
use regalloc::{PrettyPrint, RealRegUniverse, Reg}; use regalloc::{PrettyPrint, RealRegUniverse, Reg};
use target_lexicon::Triple; use target_lexicon::Triple;
@@ -95,11 +94,6 @@ impl MachBackend for X64Backend {
self.x64_flags.iter().collect() self.x64_flags.iter().collect()
} }
fn hash_all_flags(&self, mut hasher: &mut dyn Hasher) {
self.flags.hash(&mut hasher);
self.x64_flags.hash(&mut hasher);
}
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
"x64" "x64"
} }
@@ -118,12 +112,6 @@ impl MachBackend for X64Backend {
IntCC::UnsignedLessThan IntCC::UnsignedLessThan
} }
fn unsigned_sub_overflow_condition(&self) -> IntCC {
// unsigned `<`; this corresponds to the carry flag set on x86, which
// indicates a sub has underflowed (carry is borrow for subtract).
IntCC::UnsignedLessThan
}
#[cfg(feature = "unwind")] #[cfg(feature = "unwind")]
fn emit_unwind_info( fn emit_unwind_info(
&self, &self,

View File

@@ -24,7 +24,7 @@ pub fn expand_global_value(
debug_assert_eq!(opcode, ir::Opcode::GlobalValue); debug_assert_eq!(opcode, ir::Opcode::GlobalValue);
global_value global_value
} }
_ => panic!("Wanted global_value: {}", func.dfg.display_inst(inst, None)), _ => panic!("Wanted global_value: {}", func.dfg.display_inst(inst)),
}; };
match func.global_values[gv] { match func.global_values[gv] {

View File

@@ -27,7 +27,7 @@ pub fn expand_heap_addr(
debug_assert_eq!(opcode, ir::Opcode::HeapAddr); debug_assert_eq!(opcode, ir::Opcode::HeapAddr);
(heap, arg, u64::from(imm)) (heap, arg, u64::from(imm))
} }
_ => panic!("Wanted heap_addr: {}", func.dfg.display_inst(inst, None)), _ => panic!("Wanted heap_addr: {}", func.dfg.display_inst(inst)),
}; };
match func.heaps[heap].style { match func.heaps[heap].style {

View File

@@ -40,7 +40,7 @@ pub fn simple_legalize(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa:
} => (arg, imm), } => (arg, imm),
_ => panic!( _ => panic!(
concat!("Expected ", stringify!($from), ": {}"), concat!("Expected ", stringify!($from), ": {}"),
$pos.func.dfg.display_inst($inst, None) $pos.func.dfg.display_inst($inst)
), ),
}; };
let ty = $pos.func.dfg.value_type(arg); let ty = $pos.func.dfg.value_type(arg);
@@ -57,7 +57,7 @@ pub fn simple_legalize(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa:
} => (arg, imm), } => (arg, imm),
_ => panic!( _ => panic!(
concat!("Expected ", stringify!($from), ": {}"), concat!("Expected ", stringify!($from), ": {}"),
$pos.func.dfg.display_inst($inst, None) $pos.func.dfg.display_inst($inst)
), ),
}; };
let imm = $pos.ins().iconst($ty, imm); let imm = $pos.ins().iconst($ty, imm);
@@ -106,10 +106,7 @@ pub fn simple_legalize(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa:
arg, arg,
imm, imm,
} => (arg, imm), } => (arg, imm),
_ => panic!( _ => panic!("Expected irsub_imm: {}", pos.func.dfg.display_inst(inst)),
"Expected irsub_imm: {}",
pos.func.dfg.display_inst(inst, None)
),
}; };
let ty = pos.func.dfg.value_type(arg); let ty = pos.func.dfg.value_type(arg);
let imm = pos.ins().iconst(ty, imm); let imm = pos.ins().iconst(ty, imm);
@@ -131,10 +128,7 @@ pub fn simple_legalize(func: &mut ir::Function, cfg: &mut ControlFlowGraph, isa:
arg, arg,
imm, imm,
} => (cond, arg, imm), } => (cond, arg, imm),
_ => panic!( _ => panic!("Expected ircmp_imm: {}", pos.func.dfg.display_inst(inst)),
"Expected ircmp_imm: {}",
pos.func.dfg.display_inst(inst, None)
),
}; };
let ty = pos.func.dfg.value_type(x); let ty = pos.func.dfg.value_type(x);
let y = pos.ins().iconst(ty, y); let y = pos.ins().iconst(ty, y);
@@ -170,11 +164,11 @@ fn expand_cond_trap(
trapz = match opcode { trapz = match opcode {
ir::Opcode::Trapz => true, ir::Opcode::Trapz => true,
ir::Opcode::Trapnz | ir::Opcode::ResumableTrapnz => false, ir::Opcode::Trapnz | ir::Opcode::ResumableTrapnz => false,
_ => panic!("Expected cond trap: {}", func.dfg.display_inst(inst, None)), _ => panic!("Expected cond trap: {}", func.dfg.display_inst(inst)),
}; };
(arg, code, opcode) (arg, code, opcode)
} }
_ => panic!("Expected cond trap: {}", func.dfg.display_inst(inst, None)), _ => panic!("Expected cond trap: {}", func.dfg.display_inst(inst)),
}; };
// Split the block after `inst`: // Split the block after `inst`:
@@ -250,7 +244,7 @@ fn expand_br_icmp(
destination, destination,
args.as_slice(&func.dfg.value_lists)[2..].to_vec(), args.as_slice(&func.dfg.value_lists)[2..].to_vec(),
), ),
_ => panic!("Expected br_icmp {}", func.dfg.display_inst(inst, None)), _ => panic!("Expected br_icmp {}", func.dfg.display_inst(inst)),
}; };
let old_block = func.layout.pp_block(inst); let old_block = func.layout.pp_block(inst);
@@ -284,10 +278,7 @@ fn expand_stack_load(
stack_slot, stack_slot,
offset, offset,
} => (stack_slot, offset), } => (stack_slot, offset),
_ => panic!( _ => panic!("Expected stack_load: {}", pos.func.dfg.display_inst(inst)),
"Expected stack_load: {}",
pos.func.dfg.display_inst(inst, None)
),
}; };
let addr = pos.ins().stack_addr(addr_ty, stack_slot, offset); let addr = pos.ins().stack_addr(addr_ty, stack_slot, offset);
@@ -316,10 +307,7 @@ fn expand_stack_store(
stack_slot, stack_slot,
offset, offset,
} => (arg, stack_slot, offset), } => (arg, stack_slot, offset),
_ => panic!( _ => panic!("Expected stack_store: {}", pos.func.dfg.display_inst(inst)),
"Expected stack_store: {}",
pos.func.dfg.display_inst(inst, None)
),
}; };
let addr = pos.ins().stack_addr(addr_ty, stack_slot, offset); let addr = pos.ins().stack_addr(addr_ty, stack_slot, offset);

View File

@@ -28,7 +28,7 @@ pub fn expand_table_addr(
debug_assert_eq!(opcode, ir::Opcode::TableAddr); debug_assert_eq!(opcode, ir::Opcode::TableAddr);
(table, arg, offset) (table, arg, offset)
} }
_ => panic!("Wanted table_addr: {}", func.dfg.display_inst(inst, None)), _ => panic!("Wanted table_addr: {}", func.dfg.display_inst(inst)),
}; };
dynamic_addr(inst, table, index, element_offset, func); dynamic_addr(inst, table, index, element_offset, func);

View File

@@ -1,14 +1,13 @@
//! Adapter for a `MachBackend` to implement the `TargetIsa` trait. //! Adapter for a `MachBackend` to implement the `TargetIsa` trait.
use crate::ir; use crate::ir;
use crate::isa::{RegInfo, TargetIsa}; use crate::isa::TargetIsa;
use crate::machinst::*; use crate::machinst::*;
use crate::settings::{self, Flags}; use crate::settings::{self, Flags};
#[cfg(feature = "unwind")] #[cfg(feature = "unwind")]
use crate::isa::unwind::systemv::RegisterMappingError; use crate::isa::unwind::systemv::RegisterMappingError;
use core::any::Any;
use std::fmt; use std::fmt;
use target_lexicon::Triple; use target_lexicon::Triple;
@@ -56,18 +55,6 @@ impl TargetIsa for TargetIsaAdapter {
self.backend.isa_flags() self.backend.isa_flags()
} }
fn hash_all_flags(&self, hasher: &mut dyn Hasher) {
self.backend.hash_all_flags(hasher);
}
fn register_info(&self) -> RegInfo {
// Called from function's Display impl, so we need a stub here.
RegInfo {
banks: &[],
classes: &[],
}
}
fn get_mach_backend(&self) -> Option<&dyn MachBackend> { fn get_mach_backend(&self) -> Option<&dyn MachBackend> {
Some(&*self.backend) Some(&*self.backend)
} }
@@ -76,10 +63,6 @@ impl TargetIsa for TargetIsaAdapter {
self.backend.unsigned_add_overflow_condition() self.backend.unsigned_add_overflow_condition()
} }
fn unsigned_sub_overflow_condition(&self) -> ir::condcodes::IntCC {
self.backend.unsigned_sub_overflow_condition()
}
#[cfg(feature = "unwind")] #[cfg(feature = "unwind")]
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> { fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
self.backend.create_systemv_cie() self.backend.create_systemv_cie()
@@ -89,8 +72,4 @@ impl TargetIsa for TargetIsaAdapter {
fn map_regalloc_reg_to_dwarf(&self, r: Reg) -> Result<u16, RegisterMappingError> { fn map_regalloc_reg_to_dwarf(&self, r: Reg) -> Result<u16, RegisterMappingError> {
self.backend.map_reg_to_dwarf(r) self.backend.map_reg_to_dwarf(r)
} }
fn as_any(&self) -> &dyn Any {
self as &dyn Any
}
} }

View File

@@ -1641,10 +1641,9 @@ impl<I: VCodeInst> TextSectionBuilder for MachTextSectionBuilder<I> {
#[cfg(all(test, feature = "arm64"))] #[cfg(all(test, feature = "arm64"))]
mod test { mod test {
use super::*; use super::*;
use crate::ir::{ConstantOffset, Function, JumpTable, Value}; use crate::ir::{ConstantOffset, JumpTable};
use crate::isa::aarch64::inst::xreg; use crate::isa::aarch64::inst::xreg;
use crate::isa::aarch64::inst::{BranchTarget, CondBrKind, EmitInfo, Inst}; use crate::isa::aarch64::inst::{BranchTarget, CondBrKind, EmitInfo, Inst};
use crate::isa::TargetIsa;
use crate::machinst::MachInstEmit; use crate::machinst::MachInstEmit;
use crate::settings; use crate::settings;
use std::default::Default; use std::default::Default;
@@ -2076,7 +2075,6 @@ mod test {
fn begin_jumptables(&mut self) {} fn begin_jumptables(&mut self) {}
fn begin_rodata(&mut self) {} fn begin_rodata(&mut self) {}
fn end_codegen(&mut self) {} fn end_codegen(&mut self) {}
fn add_stack_map(&mut self, _: &[Value], _: &Function, _: &dyn TargetIsa) {}
fn add_call_site(&mut self, op: Opcode, _: SourceLoc) { fn add_call_site(&mut self, op: Opcode, _: SourceLoc) {
self.callsites.push((self.offset, op)); self.callsites.push((self.offset, op));
} }

View File

@@ -69,7 +69,6 @@ use crate::value_label::ValueLabelsRanges;
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::vec::Vec; use alloc::vec::Vec;
use core::fmt::Debug; use core::fmt::Debug;
use core::hash::Hasher;
use cranelift_entity::PrimaryMap; use cranelift_entity::PrimaryMap;
use regalloc::RegUsageCollector; use regalloc::RegUsageCollector;
use regalloc::{ use regalloc::{
@@ -389,9 +388,6 @@ pub trait MachBackend {
/// Get the ISA-dependent flag values that were used to make this trait object. /// Get the ISA-dependent flag values that were used to make this trait object.
fn isa_flags(&self) -> Vec<settings::Value>; fn isa_flags(&self) -> Vec<settings::Value>;
/// Hashes all flags, both ISA-independent and ISA-dependent, into the specified hasher.
fn hash_all_flags(&self, hasher: &mut dyn Hasher);
/// Return triple for this backend. /// Return triple for this backend.
fn triple(&self) -> Triple; fn triple(&self) -> Triple;
@@ -405,10 +401,6 @@ pub trait MachBackend {
/// Condition that will be true when an IaddIfcout overflows. /// Condition that will be true when an IaddIfcout overflows.
fn unsigned_add_overflow_condition(&self) -> IntCC; fn unsigned_add_overflow_condition(&self) -> IntCC;
/// Machine-specific condcode info needed by TargetIsa.
/// Condition that will be true when an IsubIfcout overflows.
fn unsigned_sub_overflow_condition(&self) -> IntCC;
/// Produces unwind info based on backend results. /// Produces unwind info based on backend results.
#[cfg(feature = "unwind")] #[cfg(feature = "unwind")]
fn emit_unwind_info( fn emit_unwind_info(

View File

@@ -4,7 +4,6 @@ use crate::entity::SecondaryMap;
use crate::ir; use crate::ir;
use crate::ir::entities::{AnyEntity, Block, Inst, Value}; use crate::ir::entities::{AnyEntity, Block, Inst, Value};
use crate::ir::function::Function; use crate::ir::function::Function;
use crate::isa::TargetIsa;
use crate::result::CodegenError; use crate::result::CodegenError;
use crate::verifier::{VerifierError, VerifierErrors}; use crate::verifier::{VerifierError, VerifierErrors};
use crate::write::{decorate_function, FuncWriter, PlainWriter}; use crate::write::{decorate_function, FuncWriter, PlainWriter};
@@ -17,7 +16,6 @@ use core::fmt::Write;
/// Pretty-print a verifier error. /// Pretty-print a verifier error.
pub fn pretty_verifier_error<'a>( pub fn pretty_verifier_error<'a>(
func: &ir::Function, func: &ir::Function,
isa: Option<&dyn TargetIsa>,
func_w: Option<Box<dyn FuncWriter + 'a>>, func_w: Option<Box<dyn FuncWriter + 'a>>,
errors: VerifierErrors, errors: VerifierErrors,
) -> String { ) -> String {
@@ -29,7 +27,6 @@ pub fn pretty_verifier_error<'a>(
&mut PrettyVerifierError(func_w.unwrap_or_else(|| Box::new(PlainWriter)), &mut errors), &mut PrettyVerifierError(func_w.unwrap_or_else(|| Box::new(PlainWriter)), &mut errors),
&mut w, &mut w,
func, func,
&isa.into(),
) )
.unwrap(); .unwrap();
@@ -51,11 +48,10 @@ impl<'a> FuncWriter for PrettyVerifierError<'a> {
&mut self, &mut self,
w: &mut dyn Write, w: &mut dyn Write,
func: &Function, func: &Function,
isa: Option<&dyn TargetIsa>,
block: Block, block: Block,
indent: usize, indent: usize,
) -> fmt::Result { ) -> fmt::Result {
pretty_block_header_error(w, func, isa, block, indent, &mut *self.0, self.1) pretty_block_header_error(w, func, block, indent, &mut *self.0, self.1)
} }
fn write_instruction( fn write_instruction(
@@ -63,11 +59,10 @@ impl<'a> FuncWriter for PrettyVerifierError<'a> {
w: &mut dyn Write, w: &mut dyn Write,
func: &Function, func: &Function,
aliases: &SecondaryMap<Value, Vec<Value>>, aliases: &SecondaryMap<Value, Vec<Value>>,
isa: Option<&dyn TargetIsa>,
inst: Inst, inst: Inst,
indent: usize, indent: usize,
) -> fmt::Result { ) -> fmt::Result {
pretty_instruction_error(w, func, aliases, isa, inst, indent, &mut *self.0, self.1) pretty_instruction_error(w, func, aliases, inst, indent, &mut *self.0, self.1)
} }
fn write_entity_definition( fn write_entity_definition(
@@ -85,14 +80,13 @@ impl<'a> FuncWriter for PrettyVerifierError<'a> {
fn pretty_block_header_error( fn pretty_block_header_error(
w: &mut dyn Write, w: &mut dyn Write,
func: &Function, func: &Function,
isa: Option<&dyn TargetIsa>,
cur_block: Block, cur_block: Block,
indent: usize, indent: usize,
func_w: &mut dyn FuncWriter, func_w: &mut dyn FuncWriter,
errors: &mut Vec<VerifierError>, errors: &mut Vec<VerifierError>,
) -> fmt::Result { ) -> fmt::Result {
let mut s = String::new(); let mut s = String::new();
func_w.write_block_header(&mut s, func, isa, cur_block, indent)?; func_w.write_block_header(&mut s, func, cur_block, indent)?;
write!(w, "{}", s)?; write!(w, "{}", s)?;
// TODO: Use drain_filter here when it gets stabilized // TODO: Use drain_filter here when it gets stabilized
@@ -124,14 +118,13 @@ fn pretty_instruction_error(
w: &mut dyn Write, w: &mut dyn Write,
func: &Function, func: &Function,
aliases: &SecondaryMap<Value, Vec<Value>>, aliases: &SecondaryMap<Value, Vec<Value>>,
isa: Option<&dyn TargetIsa>,
cur_inst: Inst, cur_inst: Inst,
indent: usize, indent: usize,
func_w: &mut dyn FuncWriter, func_w: &mut dyn FuncWriter,
errors: &mut Vec<VerifierError>, errors: &mut Vec<VerifierError>,
) -> fmt::Result { ) -> fmt::Result {
let mut s = String::new(); let mut s = String::new();
func_w.write_instruction(&mut s, func, aliases, isa, cur_inst, indent)?; func_w.write_instruction(&mut s, func, aliases, cur_inst, indent)?;
write!(w, "{}", s)?; write!(w, "{}", s)?;
// TODO: Use drain_filter here when it gets stabilized // TODO: Use drain_filter here when it gets stabilized
@@ -218,9 +211,9 @@ fn print_error(w: &mut dyn Write, err: VerifierError) -> fmt::Result {
} }
/// Pretty-print a Cranelift error. /// Pretty-print a Cranelift error.
pub fn pretty_error(func: &ir::Function, isa: Option<&dyn TargetIsa>, err: CodegenError) -> String { pub fn pretty_error(func: &ir::Function, err: CodegenError) -> String {
if let CodegenError::Verifier(e) = err { if let CodegenError::Verifier(e) = err {
pretty_verifier_error(func, isa, None, e) pretty_verifier_error(func, None, e)
} else { } else {
err.to_string() err.to_string()
} }

View File

@@ -31,7 +31,7 @@ pub fn eliminate_unreachable_code(
// Remove all instructions from `block`. // Remove all instructions from `block`.
while let Some(inst) = pos.func.layout.first_inst(block) { while let Some(inst) = pos.func.layout.first_inst(block) {
log::trace!(" - {}", pos.func.dfg.display_inst(inst, None)); log::trace!(" - {}", pos.func.dfg.display_inst(inst));
pos.func.layout.remove_inst(inst); pos.func.layout.remove_inst(inst);
} }

View File

@@ -65,9 +65,8 @@ use crate::ir;
use crate::ir::entities::AnyEntity; use crate::ir::entities::AnyEntity;
use crate::ir::instructions::{BranchInfo, CallInfo, InstructionFormat, ResolvedConstraint}; use crate::ir::instructions::{BranchInfo, CallInfo, InstructionFormat, ResolvedConstraint};
use crate::ir::{ use crate::ir::{
types, ArgumentLoc, ArgumentPurpose, Block, Constant, FuncRef, Function, GlobalValue, Inst, types, ArgumentPurpose, Block, Constant, FuncRef, Function, GlobalValue, Inst, InstructionData,
InstructionData, JumpTable, Opcode, SigRef, StackSlot, StackSlotKind, Type, Value, ValueDef, JumpTable, Opcode, SigRef, StackSlot, Type, Value, ValueDef, ValueList,
ValueList, ValueLoc,
}; };
use crate::isa::TargetIsa; use crate::isa::TargetIsa;
use crate::iterators::IteratorExtras; use crate::iterators::IteratorExtras;
@@ -319,7 +318,7 @@ impl<'a> Verifier<'a> {
/// Determine a contextual error string for an instruction. /// Determine a contextual error string for an instruction.
#[inline] #[inline]
fn context(&self, inst: Inst) -> String { fn context(&self, inst: Inst) -> String {
self.func.dfg.display_inst(inst, self.isa).to_string() self.func.dfg.display_inst(inst).to_string()
} }
// Check for: // Check for:
@@ -700,12 +699,6 @@ impl<'a> Verifier<'a> {
TableAddr { table, .. } => { TableAddr { table, .. } => {
self.verify_table(inst, table, errors)?; self.verify_table(inst, table, errors)?;
} }
RegSpill { dst, .. } => {
self.verify_stack_slot(inst, dst, errors)?;
}
RegFill { src, .. } => {
self.verify_stack_slot(inst, src, errors)?;
}
LoadComplex { ref args, .. } => { LoadComplex { ref args, .. } => {
self.verify_value_list(inst, args, errors)?; self.verify_value_list(inst, args, errors)?;
} }
@@ -775,9 +768,6 @@ impl<'a> Verifier<'a> {
| IntSelect { .. } | IntSelect { .. }
| Load { .. } | Load { .. }
| Store { .. } | Store { .. }
| RegMove { .. }
| CopySpecial { .. }
| CopyToSsa { .. }
| Trap { .. } | Trap { .. }
| CondTrap { .. } | CondTrap { .. }
| IntCondTrap { .. } | IntCondTrap { .. }
@@ -1377,7 +1367,6 @@ impl<'a> Verifier<'a> {
.iter() .iter()
.map(|a| a.value_type); .map(|a| a.value_type);
self.typecheck_variable_args_iterator(inst, arg_types, errors)?; self.typecheck_variable_args_iterator(inst, arg_types, errors)?;
self.check_outgoing_args(inst, sig_ref, errors)?;
} }
CallInfo::Indirect(sig_ref, _) => { CallInfo::Indirect(sig_ref, _) => {
let arg_types = self.func.dfg.signatures[sig_ref] let arg_types = self.func.dfg.signatures[sig_ref]
@@ -1385,7 +1374,6 @@ impl<'a> Verifier<'a> {
.iter() .iter()
.map(|a| a.value_type); .map(|a| a.value_type);
self.typecheck_variable_args_iterator(inst, arg_types, errors)?; self.typecheck_variable_args_iterator(inst, arg_types, errors)?;
self.check_outgoing_args(inst, sig_ref, errors)?;
} }
CallInfo::NotACall => {} CallInfo::NotACall => {}
} }
@@ -1427,7 +1415,7 @@ impl<'a> Verifier<'a> {
self.context(inst), self.context(inst),
format!( format!(
"mismatched argument count for `{}`: got {}, expected {}", "mismatched argument count for `{}`: got {}, expected {}",
self.func.dfg.display_inst(inst, None), self.func.dfg.display_inst(inst),
variable_args.len(), variable_args.len(),
i, i,
), ),
@@ -1436,77 +1424,6 @@ impl<'a> Verifier<'a> {
Ok(()) Ok(())
} }
/// Check the locations assigned to outgoing call arguments.
///
/// When a signature has been legalized, all values passed as outgoing arguments on the stack
/// must be assigned to a matching `OutgoingArg` stack slot.
fn check_outgoing_args(
&self,
inst: Inst,
sig_ref: SigRef,
errors: &mut VerifierErrors,
) -> VerifierStepResult<()> {
let sig = &self.func.dfg.signatures[sig_ref];
let args = self.func.dfg.inst_variable_args(inst);
let expected_args = &sig.params[..];
for (&arg, &abi) in args.iter().zip(expected_args) {
// Value types have already been checked by `typecheck_variable_args_iterator()`.
if let ArgumentLoc::Stack(offset) = abi.location {
let arg_loc = self.func.locations[arg];
if let ValueLoc::Stack(ss) = arg_loc {
// Argument value is assigned to a stack slot as expected.
self.verify_stack_slot(inst, ss, errors)?;
let slot = &self.func.stack_slots[ss];
if slot.kind != StackSlotKind::OutgoingArg {
return errors.fatal((
inst,
self.context(inst),
format!(
"Outgoing stack argument {} in wrong stack slot: {} = {}",
arg, ss, slot,
),
));
}
if slot.offset != Some(offset) {
return errors.fatal((
inst,
self.context(inst),
format!(
"Outgoing stack argument {} should have offset {}: {} = {}",
arg, offset, ss, slot,
),
));
}
if abi.purpose == ArgumentPurpose::StructArgument(slot.size) {
} else if slot.size != abi.value_type.bytes() {
return errors.fatal((
inst,
self.context(inst),
format!(
"Outgoing stack argument {} wrong size for {}: {} = {}",
arg, abi.value_type, ss, slot,
),
));
}
} else {
let reginfo = self.isa.map(|i| i.register_info());
return errors.fatal((
inst,
self.context(inst),
format!(
"Outgoing stack argument {} in wrong location: {}",
arg,
arg_loc.display(reginfo.as_ref())
),
));
}
}
}
Ok(())
}
fn typecheck_return(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult<()> { fn typecheck_return(&self, inst: Inst, errors: &mut VerifierErrors) -> VerifierStepResult<()> {
if self.func.dfg[inst].opcode().is_return() { if self.func.dfg[inst].opcode().is_return() {
let args = self.func.dfg.inst_variable_args(inst); let args = self.func.dfg.inst_variable_args(inst);
@@ -1668,22 +1585,6 @@ impl<'a> Verifier<'a> {
"copy_nop src and dst types must be the same", "copy_nop src and dst types must be the same",
)); ));
} }
let src_loc = self.func.locations[arg];
let dst_loc = self.func.locations[dst_val];
let locs_ok = match (src_loc, dst_loc) {
(ValueLoc::Stack(src_slot), ValueLoc::Stack(dst_slot)) => src_slot == dst_slot,
_ => false,
};
if !locs_ok {
return errors.fatal((
inst,
self.context(inst),
format!(
"copy_nop must refer to identical stack slots, but found {:?} vs {:?}",
src_loc, dst_loc,
),
));
}
} }
Ok(()) Ok(())
} }
@@ -1903,7 +1804,7 @@ impl<'a> Verifier<'a> {
if !errors.is_empty() { if !errors.is_empty() {
log::warn!( log::warn!(
"Found verifier errors in function:\n{}", "Found verifier errors in function:\n{}",
pretty_verifier_error(self.func, None, None, errors.clone()) pretty_verifier_error(self.func, None, errors.clone())
); );
} }

View File

@@ -5,10 +5,7 @@
use crate::entity::SecondaryMap; use crate::entity::SecondaryMap;
use crate::ir::entities::AnyEntity; use crate::ir::entities::AnyEntity;
use crate::ir::{ use crate::ir::{Block, DataFlowGraph, Function, Inst, SigRef, Type, Value, ValueDef};
Block, DataFlowGraph, DisplayFunctionAnnotations, Function, Inst, SigRef, Type, Value, ValueDef,
};
use crate::isa::{RegInfo, TargetIsa};
use crate::packed_option::ReservedValue; use crate::packed_option::ReservedValue;
use alloc::string::String; use alloc::string::String;
use alloc::vec::Vec; use alloc::vec::Vec;
@@ -21,7 +18,6 @@ pub trait FuncWriter {
&mut self, &mut self,
w: &mut dyn Write, w: &mut dyn Write,
func: &Function, func: &Function,
isa: Option<&dyn TargetIsa>,
block: Block, block: Block,
indent: usize, indent: usize,
) -> fmt::Result; ) -> fmt::Result;
@@ -32,28 +28,17 @@ pub trait FuncWriter {
w: &mut dyn Write, w: &mut dyn Write,
func: &Function, func: &Function,
aliases: &SecondaryMap<Value, Vec<Value>>, aliases: &SecondaryMap<Value, Vec<Value>>,
isa: Option<&dyn TargetIsa>,
inst: Inst, inst: Inst,
indent: usize, indent: usize,
) -> fmt::Result; ) -> fmt::Result;
/// Write the preamble to `w`. By default, this uses `write_entity_definition`. /// Write the preamble to `w`. By default, this uses `write_entity_definition`.
fn write_preamble( fn write_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> {
&mut self, self.super_preamble(w, func)
w: &mut dyn Write,
func: &Function,
regs: Option<&RegInfo>,
) -> Result<bool, fmt::Error> {
self.super_preamble(w, func, regs)
} }
/// Default impl of `write_preamble` /// Default impl of `write_preamble`
fn super_preamble( fn super_preamble(&mut self, w: &mut dyn Write, func: &Function) -> Result<bool, fmt::Error> {
&mut self,
w: &mut dyn Write,
func: &Function,
regs: Option<&RegInfo>,
) -> Result<bool, fmt::Error> {
let mut any = false; let mut any = false;
for (ss, slot) in func.stack_slots.iter() { for (ss, slot) in func.stack_slots.iter() {
@@ -84,7 +69,7 @@ pub trait FuncWriter {
// signatures. // signatures.
for (sig, sig_data) in &func.dfg.signatures { for (sig, sig_data) in &func.dfg.signatures {
any = true; any = true;
self.write_entity_definition(w, func, sig.into(), &sig_data.display(regs))?; self.write_entity_definition(w, func, sig.into(), &sig_data)?;
} }
for (fnref, ext_func) in &func.dfg.ext_funcs { for (fnref, ext_func) in &func.dfg.ext_funcs {
@@ -145,33 +130,27 @@ impl FuncWriter for PlainWriter {
w: &mut dyn Write, w: &mut dyn Write,
func: &Function, func: &Function,
aliases: &SecondaryMap<Value, Vec<Value>>, aliases: &SecondaryMap<Value, Vec<Value>>,
isa: Option<&dyn TargetIsa>,
inst: Inst, inst: Inst,
indent: usize, indent: usize,
) -> fmt::Result { ) -> fmt::Result {
write_instruction(w, func, aliases, isa, inst, indent) write_instruction(w, func, aliases, inst, indent)
} }
fn write_block_header( fn write_block_header(
&mut self, &mut self,
w: &mut dyn Write, w: &mut dyn Write,
func: &Function, func: &Function,
isa: Option<&dyn TargetIsa>,
block: Block, block: Block,
indent: usize, indent: usize,
) -> fmt::Result { ) -> fmt::Result {
write_block_header(w, func, isa, block, indent) write_block_header(w, func, block, indent)
} }
} }
/// Write `func` to `w` as equivalent text. /// Write `func` to `w` as equivalent text.
/// Use `isa` to emit ISA-dependent annotations. /// Use `isa` to emit ISA-dependent annotations.
pub fn write_function( pub fn write_function(w: &mut dyn Write, func: &Function) -> fmt::Result {
w: &mut dyn Write, decorate_function(&mut PlainWriter, w, func)
func: &Function,
annotations: &DisplayFunctionAnnotations,
) -> fmt::Result {
decorate_function(&mut PlainWriter, w, func, annotations)
} }
/// Create a reverse-alias map from a value to all aliases having that value as a direct target /// Create a reverse-alias map from a value to all aliases having that value as a direct target
@@ -193,21 +172,17 @@ pub fn decorate_function<FW: FuncWriter>(
func_w: &mut FW, func_w: &mut FW,
w: &mut dyn Write, w: &mut dyn Write,
func: &Function, func: &Function,
annotations: &DisplayFunctionAnnotations,
) -> fmt::Result { ) -> fmt::Result {
let regs = annotations.isa.map(TargetIsa::register_info);
let regs = regs.as_ref();
write!(w, "function ")?; write!(w, "function ")?;
write_spec(w, func, regs)?; write_spec(w, func)?;
writeln!(w, " {{")?; writeln!(w, " {{")?;
let aliases = alias_map(func); let aliases = alias_map(func);
let mut any = func_w.write_preamble(w, func, regs)?; let mut any = func_w.write_preamble(w, func)?;
for block in &func.layout { for block in &func.layout {
if any { if any {
writeln!(w)?; writeln!(w)?;
} }
decorate_block(func_w, w, func, &aliases, annotations, block)?; decorate_block(func_w, w, func, &aliases, block)?;
any = true; any = true;
} }
writeln!(w, "}}") writeln!(w, "}}")
@@ -217,27 +192,16 @@ pub fn decorate_function<FW: FuncWriter>(
// //
// Function spec. // Function spec.
fn write_spec(w: &mut dyn Write, func: &Function, regs: Option<&RegInfo>) -> fmt::Result { fn write_spec(w: &mut dyn Write, func: &Function) -> fmt::Result {
write!(w, "{}{}", func.name, func.signature.display(regs)) write!(w, "{}{}", func.name, func.signature)
} }
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// //
// Basic blocks // Basic blocks
fn write_arg( fn write_arg(w: &mut dyn Write, func: &Function, arg: Value) -> fmt::Result {
w: &mut dyn Write, write!(w, "{}: {}", arg, func.dfg.value_type(arg))
func: &Function,
regs: Option<&RegInfo>,
arg: Value,
) -> fmt::Result {
write!(w, "{}: {}", arg, func.dfg.value_type(arg))?;
let loc = func.locations[arg];
if loc.is_assigned() {
write!(w, " [{}]", loc.display(regs))?
}
Ok(())
} }
/// Write out the basic block header, outdented: /// Write out the basic block header, outdented:
@@ -249,28 +213,24 @@ fn write_arg(
pub fn write_block_header( pub fn write_block_header(
w: &mut dyn Write, w: &mut dyn Write,
func: &Function, func: &Function,
isa: Option<&dyn TargetIsa>,
block: Block, block: Block,
indent: usize, indent: usize,
) -> fmt::Result { ) -> fmt::Result {
// The `indent` is the instruction indentation. block headers are 4 spaces out from that. // The `indent` is the instruction indentation. block headers are 4 spaces out from that.
write!(w, "{1:0$}{2}", indent - 4, "", block)?; write!(w, "{1:0$}{2}", indent - 4, "", block)?;
let regs = isa.map(TargetIsa::register_info);
let regs = regs.as_ref();
let mut args = func.dfg.block_params(block).iter().cloned(); let mut args = func.dfg.block_params(block).iter().cloned();
match args.next() { match args.next() {
None => return writeln!(w, ":"), None => return writeln!(w, ":"),
Some(arg) => { Some(arg) => {
write!(w, "(")?; write!(w, "(")?;
write_arg(w, func, regs, arg)?; write_arg(w, func, arg)?;
} }
} }
// Remaining arguments. // Remaining arguments.
for arg in args { for arg in args {
write!(w, ", ")?; write!(w, ", ")?;
write_arg(w, func, regs, arg)?; write_arg(w, func, arg)?;
} }
writeln!(w, "):") writeln!(w, "):")
} }
@@ -280,20 +240,18 @@ fn decorate_block<FW: FuncWriter>(
w: &mut dyn Write, w: &mut dyn Write,
func: &Function, func: &Function,
aliases: &SecondaryMap<Value, Vec<Value>>, aliases: &SecondaryMap<Value, Vec<Value>>,
annotations: &DisplayFunctionAnnotations,
block: Block, block: Block,
) -> fmt::Result { ) -> fmt::Result {
// Indent all instructions if any srclocs are present. // Indent all instructions if any srclocs are present.
let indent = if func.srclocs.is_empty() { 4 } else { 36 }; let indent = if func.srclocs.is_empty() { 4 } else { 36 };
let isa = annotations.isa;
func_w.write_block_header(w, func, isa, block, indent)?; func_w.write_block_header(w, func, block, indent)?;
for a in func.dfg.block_params(block).iter().cloned() { for a in func.dfg.block_params(block).iter().cloned() {
write_value_aliases(w, aliases, a, indent)?; write_value_aliases(w, aliases, a, indent)?;
} }
for inst in func.layout.block_insts(block) { for inst in func.layout.block_insts(block) {
func_w.write_instruction(w, func, aliases, isa, inst, indent)?; func_w.write_instruction(w, func, aliases, inst, indent)?;
} }
Ok(()) Ok(())
@@ -359,7 +317,6 @@ fn write_instruction(
w: &mut dyn Write, w: &mut dyn Write,
func: &Function, func: &Function,
aliases: &SecondaryMap<Value, Vec<Value>>, aliases: &SecondaryMap<Value, Vec<Value>>,
isa: Option<&dyn TargetIsa>,
inst: Inst, inst: Inst,
indent: usize, indent: usize,
) -> fmt::Result { ) -> fmt::Result {
@@ -397,7 +354,7 @@ fn write_instruction(
None => write!(w, "{}", opcode)?, None => write!(w, "{}", opcode)?,
} }
write_operands(w, &func.dfg, isa, inst)?; write_operands(w, &func.dfg, inst)?;
writeln!(w)?; writeln!(w)?;
// Value aliases come out on lines after the instruction defining the referent. // Value aliases come out on lines after the instruction defining the referent.
@@ -408,12 +365,7 @@ fn write_instruction(
} }
/// Write the operands of `inst` to `w` with a prepended space. /// Write the operands of `inst` to `w` with a prepended space.
pub fn write_operands( pub fn write_operands(w: &mut dyn Write, dfg: &DataFlowGraph, inst: Inst) -> fmt::Result {
w: &mut dyn Write,
dfg: &DataFlowGraph,
isa: Option<&dyn TargetIsa>,
inst: Inst,
) -> fmt::Result {
let pool = &dfg.value_lists; let pool = &dfg.value_lists;
use crate::ir::instructions::InstructionData::*; use crate::ir::instructions::InstructionData::*;
match dfg[inst] { match dfg[inst] {
@@ -582,57 +534,6 @@ pub fn write_operands(
offset offset
) )
} }
RegMove { arg, src, dst, .. } => {
if let Some(isa) = isa {
let regs = isa.register_info();
write!(
w,
" {}, {} -> {}",
arg,
regs.display_regunit(src),
regs.display_regunit(dst)
)
} else {
write!(w, " {}, %{} -> %{}", arg, src, dst)
}
}
CopySpecial { src, dst, .. } => {
if let Some(isa) = isa {
let regs = isa.register_info();
write!(
w,
" {} -> {}",
regs.display_regunit(src),
regs.display_regunit(dst)
)
} else {
write!(w, " %{} -> %{}", src, dst)
}
}
CopyToSsa { src, .. } => {
if let Some(isa) = isa {
let regs = isa.register_info();
write!(w, " {}", regs.display_regunit(src))
} else {
write!(w, " %{}", src)
}
}
RegSpill { arg, src, dst, .. } => {
if let Some(isa) = isa {
let regs = isa.register_info();
write!(w, " {}, {} -> {}", arg, regs.display_regunit(src), dst)
} else {
write!(w, " {}, %{} -> {}", arg, src, dst)
}
}
RegFill { arg, src, dst, .. } => {
if let Some(isa) = isa {
let regs = isa.register_info();
write!(w, " {}, {} -> {}", arg, src, regs.display_regunit(dst))
} else {
write!(w, " {}, {} -> %{}", arg, src, dst)
}
}
Trap { code, .. } => write!(w, " {}", code), Trap { code, .. } => write!(w, " {}", code),
CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code), CondTrap { arg, code, .. } => write!(w, " {}, {}", arg, code),
IntCondTrap { IntCondTrap {

View File

@@ -884,10 +884,6 @@ are assigned to registers or stack slots.
This approach permits SSA form to be preserved throughout the register This approach permits SSA form to be preserved throughout the register
allocation pass and beyond. allocation pass and beyond.
Register values can be temporarily diverted to other registers by the
`regmove` instruction, and to and from stack slots by `regspill`
and `regfill`.
## Instruction groups ## Instruction groups
All of the shared instructions are part of the `base` instruction All of the shared instructions are part of the `base` instruction

View File

@@ -7,9 +7,6 @@ block0(v0: i32):
jump block1(v0) jump block1(v0)
block1(v1: i32): block1(v1: i32):
regmove.i32 v0, %x10 -> %x20
; check: block1(v1: i32):
; check: regmove.i32 v0, %x10 -> %x20
v2 = iconst.i32 1 v2 = iconst.i32 1
brz v1, block3(v1) brz v1, block3(v1)
jump block2 jump block2

View File

@@ -183,42 +183,6 @@ block0(v1: i32):
; nextln: store_complex v3, v1+v2 ; nextln: store_complex v3, v1+v2
; nextln: store_complex v3, v1+v2+1 ; nextln: store_complex v3, v1+v2+1
; Register diversions.
; This test file has no ISA, so we can unly use register unit numbers.
function %diversion(i32) {
ss0 = spill_slot 4
block0(v1: i32):
regmove v1, %10 -> %20
regmove v1, %20 -> %10
regspill v1, %10 -> ss0
regfill v1, ss0 -> %10
return
}
; sameln: function %diversion(i32) fast {
; nextln: ss0 = spill_slot 4
; check: block0(v1: i32):
; nextln: regmove v1, %10 -> %20
; nextln: regmove v1, %20 -> %10
; nextln: regspill v1, %10 -> ss0
; nextln: regfill v1, ss0 -> %10
; nextln: return
; nextln: }
; Register copies.
function %copy_special() {
block0:
copy_special %10 -> %20
copy_special %20 -> %10
return
}
; sameln: function %copy_special() fast {
; nextln: block0:
; nextln: copy_special %10 -> %20
; nextln: copy_special %20 -> %10
; nextln: return
; nextln: }
function %cond_traps(i32) { function %cond_traps(i32) {
block0(v0: i32): block0(v0: i32):
trapz v0, stk_ovf trapz v0, stk_ovf

View File

@@ -1,15 +1,5 @@
test simple-gvn test simple-gvn
function %other_side_effects(i32) -> i32 {
block0(v0: i32):
regmove v0, %10 -> %20
regmove v0, %10 -> %20
regmove v0, %20 -> %10
; check: regmove v0, %10 -> %20
; check: regmove v0, %10 -> %20
return v0
}
function %differing_typevars() -> i64 { function %differing_typevars() -> i64 {
block0: block0:
v0 = iconst.i32 7 v0 = iconst.i32 7

View File

@@ -51,7 +51,6 @@ mod test_run;
mod test_safepoint; mod test_safepoint;
mod test_simple_gvn; mod test_simple_gvn;
mod test_simple_preopt; mod test_simple_preopt;
mod test_stack_maps;
mod test_unwind; mod test_unwind;
mod test_verifier; mod test_verifier;
@@ -127,7 +126,6 @@ fn new_subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn subtest::SubTest>
"safepoint" => test_safepoint::subtest(parsed), "safepoint" => test_safepoint::subtest(parsed),
"simple-gvn" => test_simple_gvn::subtest(parsed), "simple-gvn" => test_simple_gvn::subtest(parsed),
"simple_preopt" => test_simple_preopt::subtest(parsed), "simple_preopt" => test_simple_preopt::subtest(parsed),
"stack_maps" => test_stack_maps::subtest(parsed),
"unwind" => test_unwind::subtest(parsed), "unwind" => test_unwind::subtest(parsed),
"verifier" => test_verifier::subtest(parsed), "verifier" => test_verifier::subtest(parsed),
_ => anyhow::bail!("unknown test command '{}'", parsed.command), _ => anyhow::bail!("unknown test command '{}'", parsed.command),
@@ -136,9 +134,8 @@ fn new_subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn subtest::SubTest>
fn pretty_anyhow_error( fn pretty_anyhow_error(
func: &cranelift_codegen::ir::Function, func: &cranelift_codegen::ir::Function,
isa: Option<&dyn cranelift_codegen::isa::TargetIsa>,
err: cranelift_codegen::CodegenError, err: cranelift_codegen::CodegenError,
) -> anyhow::Error { ) -> anyhow::Error {
let s = cranelift_codegen::print_errors::pretty_error(func, isa, err); let s = cranelift_codegen::print_errors::pretty_error(func, err);
anyhow::anyhow!("{}", s) anyhow::anyhow!("{}", s)
} }

View File

@@ -179,9 +179,8 @@ fn run_one_test<'a>(
// Should we run the verifier before this test? // Should we run the verifier before this test?
if !context.verified && test.needs_verifier() { if !context.verified && test.needs_verifier() {
verify_function(&func, context.flags_or_isa()).map_err(|errors| { verify_function(&func, context.flags_or_isa())
anyhow::anyhow!("{}", pretty_verifier_error(&func, isa, None, errors)) .map_err(|errors| anyhow::anyhow!("{}", pretty_verifier_error(&func, None, errors)))?;
})?;
context.verified = true; context.verified = true;
} }

View File

@@ -31,6 +31,6 @@ impl SubTest for TestCat {
} }
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> { fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
subtest::run_filecheck(&func.display(context.isa).to_string(), context) subtest::run_filecheck(&func.display().to_string(), context)
} }
} }

View File

@@ -6,7 +6,6 @@ use crate::subtest::{run_filecheck, Context, SubTest};
use cranelift_codegen; use cranelift_codegen;
use cranelift_codegen::binemit::{self, CodeInfo}; use cranelift_codegen::binemit::{self, CodeInfo};
use cranelift_codegen::ir; use cranelift_codegen::ir;
use cranelift_codegen::isa;
use cranelift_reader::TestCommand; use cranelift_reader::TestCommand;
use log::info; use log::info;
use std::borrow::Cow; use std::borrow::Cow;
@@ -43,12 +42,12 @@ impl SubTest for TestCompile {
let CodeInfo { total_size, .. } = comp_ctx let CodeInfo { total_size, .. } = comp_ctx
.compile(isa) .compile(isa)
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, e))?; .map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, e))?;
info!( info!(
"Generated {} bytes of code:\n{}", "Generated {} bytes of code:\n{}",
total_size, total_size,
comp_ctx.func.display(isa) comp_ctx.func.display()
); );
let disasm = comp_ctx let disasm = comp_ctx
@@ -102,11 +101,4 @@ impl binemit::CodeSink for SizeSink {
fn begin_jumptables(&mut self) {} fn begin_jumptables(&mut self) {}
fn begin_rodata(&mut self) {} fn begin_rodata(&mut self) {}
fn end_codegen(&mut self) {} fn end_codegen(&mut self) {}
fn add_stack_map(
&mut self,
_: &[ir::entities::Value],
_: &ir::Function,
_: &dyn isa::TargetIsa,
) {
}
} }

View File

@@ -37,9 +37,9 @@ impl SubTest for TestDCE {
comp_ctx.compute_loop_analysis(); comp_ctx.compute_loop_analysis();
comp_ctx comp_ctx
.dce(context.flags_or_isa()) .dce(context.flags_or_isa())
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, Into::into(e)))?; .map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, Into::into(e)))?;
let text = comp_ctx.func.display(context.isa).to_string(); let text = comp_ctx.func.display().to_string();
run_filecheck(&text, context) run_filecheck(&text, context)
} }
} }

View File

@@ -39,9 +39,9 @@ impl SubTest for TestLegalizer {
comp_ctx.compute_cfg(); comp_ctx.compute_cfg();
comp_ctx comp_ctx
.legalize(isa) .legalize(isa)
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, e))?; .map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, e))?;
let text = comp_ctx.func.display(Some(isa)).to_string(); let text = comp_ctx.func.display().to_string();
run_filecheck(&text, context) run_filecheck(&text, context)
} }
} }

View File

@@ -42,9 +42,9 @@ impl SubTest for TestLICM {
comp_ctx.compute_loop_analysis(); comp_ctx.compute_loop_analysis();
comp_ctx comp_ctx
.licm(isa) .licm(isa)
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, Into::into(e)))?; .map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, Into::into(e)))?;
let text = comp_ctx.func.display(context.isa).to_string(); let text = comp_ctx.func.display().to_string();
run_filecheck(&text, context) run_filecheck(&text, context)
} }
} }

View File

@@ -37,8 +37,8 @@ impl SubTest for TestPreopt {
comp_ctx.compute_cfg(); comp_ctx.compute_cfg();
comp_ctx comp_ctx
.preopt(isa) .preopt(isa)
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, Into::into(e)))?; .map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, Into::into(e)))?;
let text = &comp_ctx.func.display(isa).to_string(); let text = &comp_ctx.func.display().to_string();
log::debug!("After peepmatic-based simple_preopt:\n{}", text); log::debug!("After peepmatic-based simple_preopt:\n{}", text);
// Only actually run the filecheck if peepmatic is enabled, because it // Only actually run the filecheck if peepmatic is enabled, because it

View File

@@ -40,9 +40,9 @@ impl SubTest for TestPreopt {
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned()); let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
optimize(&mut comp_ctx, isa) optimize(&mut comp_ctx, isa)
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, Into::into(e)))?; .map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, Into::into(e)))?;
let text = comp_ctx.func.display(context.isa).to_string(); let text = comp_ctx.func.display().to_string();
run_filecheck(&text, context) run_filecheck(&text, context)
} }
} }

View File

@@ -25,10 +25,10 @@ impl SubTest for TestSafepoint {
comp_ctx.compute_cfg(); comp_ctx.compute_cfg();
comp_ctx comp_ctx
.legalize(isa) .legalize(isa)
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, e))?; .map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, e))?;
comp_ctx.compute_domtree(); comp_ctx.compute_domtree();
let text = comp_ctx.func.display(context.isa).to_string(); let text = comp_ctx.func.display().to_string();
run_filecheck(&text, context) run_filecheck(&text, context)
} }
} }

View File

@@ -36,9 +36,9 @@ impl SubTest for TestSimpleGVN {
comp_ctx.flowgraph(); comp_ctx.flowgraph();
comp_ctx comp_ctx
.simple_gvn(context.flags_or_isa()) .simple_gvn(context.flags_or_isa())
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, Into::into(e)))?; .map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, Into::into(e)))?;
let text = comp_ctx.func.display(context.isa).to_string(); let text = comp_ctx.func.display().to_string();
run_filecheck(&text, context) run_filecheck(&text, context)
} }
} }

View File

@@ -38,8 +38,8 @@ impl SubTest for TestSimplePreopt {
comp_ctx.compute_cfg(); comp_ctx.compute_cfg();
comp_ctx comp_ctx
.preopt(isa) .preopt(isa)
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, e))?; .map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, e))?;
let text = &comp_ctx.func.display(isa).to_string(); let text = &comp_ctx.func.display().to_string();
log::debug!("After simple_preopt:\n{}", text); log::debug!("After simple_preopt:\n{}", text);
// Only actually run the filecheck if peepmatic is *not* enabled, // Only actually run the filecheck if peepmatic is *not* enabled,

View File

@@ -1,109 +0,0 @@
use crate::subtest::{run_filecheck, Context, SubTest};
use cranelift_codegen::binemit::{self, Addend, CodeOffset, CodeSink, Reloc, StackMap};
use cranelift_codegen::ir::*;
use cranelift_codegen::isa::TargetIsa;
use cranelift_reader::TestCommand;
use std::borrow::Cow;
use std::fmt::Write;
struct TestStackMaps;
pub fn subtest(parsed: &TestCommand) -> anyhow::Result<Box<dyn SubTest>> {
assert_eq!(parsed.command, "stack_maps");
if !parsed.options.is_empty() {
anyhow::bail!("No options allowed on {}", parsed);
}
Ok(Box::new(TestStackMaps))
}
impl SubTest for TestStackMaps {
fn name(&self) -> &'static str {
"stack_maps"
}
fn run(&self, func: Cow<Function>, context: &Context) -> anyhow::Result<()> {
let mut comp_ctx = cranelift_codegen::Context::for_function(func.into_owned());
comp_ctx
.compile(context.isa.expect("`test stack_maps` requires an isa"))
.map_err(|e| crate::pretty_anyhow_error(&comp_ctx.func, context.isa, e))?;
let mut sink = TestStackMapsSink::default();
// TODO remove entirely? seems a bit meaningless now
binemit::emit_function(
&comp_ctx.func,
|func, inst, sink, isa| {
if func.dfg[inst].opcode() == Opcode::Safepoint {
writeln!(&mut sink.text, "{}", func.dfg.display_inst(inst, isa)).unwrap();
}
},
&mut sink,
context.isa.expect("`test stack_maps` requires an isa"),
);
let mut text = comp_ctx.func.display(context.isa).to_string();
text.push('\n');
text.push_str("Stack maps:\n");
text.push('\n');
text.push_str(&sink.text);
run_filecheck(&text, context)
}
}
#[derive(Default)]
struct TestStackMapsSink {
offset: u32,
text: String,
}
impl CodeSink for TestStackMapsSink {
fn offset(&self) -> CodeOffset {
self.offset
}
fn put1(&mut self, _: u8) {
self.offset += 1;
}
fn put2(&mut self, _: u16) {
self.offset += 2;
}
fn put4(&mut self, _: u32) {
self.offset += 4;
}
fn put8(&mut self, _: u64) {
self.offset += 8;
}
fn reloc_external(&mut self, _: SourceLoc, _: Reloc, _: &ExternalName, _: Addend) {}
fn reloc_constant(&mut self, _: Reloc, _: ConstantOffset) {}
fn reloc_jt(&mut self, _: Reloc, _: JumpTable) {}
fn trap(&mut self, _: TrapCode, _: SourceLoc) {}
fn begin_jumptables(&mut self) {}
fn begin_rodata(&mut self) {}
fn end_codegen(&mut self) {}
fn add_stack_map(&mut self, val_list: &[Value], func: &Function, isa: &dyn TargetIsa) {
let map = StackMap::from_values(&val_list, func, isa);
writeln!(&mut self.text, " - mapped words: {}", map.mapped_words()).unwrap();
write!(&mut self.text, " - live: [").unwrap();
let mut needs_comma_space = false;
for i in 0..(map.mapped_words() as usize) {
if map.get_bit(i) {
if needs_comma_space {
write!(&mut self.text, ", ").unwrap();
}
needs_comma_space = true;
write!(&mut self.text, "{}", i).unwrap();
}
}
writeln!(&mut self.text, "]").unwrap();
}
}

View File

@@ -4,14 +4,13 @@ use crate::variable::Variable;
use cranelift_codegen::cursor::{Cursor, FuncCursor}; use cranelift_codegen::cursor::{Cursor, FuncCursor};
use cranelift_codegen::entity::{EntitySet, SecondaryMap}; use cranelift_codegen::entity::{EntitySet, SecondaryMap};
use cranelift_codegen::ir; use cranelift_codegen::ir;
use cranelift_codegen::ir::function::DisplayFunction;
use cranelift_codegen::ir::{ use cranelift_codegen::ir::{
types, AbiParam, Block, DataFlowGraph, ExtFuncData, ExternalName, FuncRef, Function, types, AbiParam, Block, DataFlowGraph, ExtFuncData, ExternalName, FuncRef, Function,
GlobalValue, GlobalValueData, Heap, HeapData, Inst, InstBuilder, InstBuilderBase, GlobalValue, GlobalValueData, Heap, HeapData, Inst, InstBuilder, InstBuilderBase,
InstructionData, JumpTable, JumpTableData, LibCall, MemFlags, SigRef, Signature, StackSlot, InstructionData, JumpTable, JumpTableData, LibCall, MemFlags, SigRef, Signature, StackSlot,
StackSlotData, Type, Value, ValueLabel, ValueLabelAssignments, ValueLabelStart, StackSlotData, Type, Value, ValueLabel, ValueLabelAssignments, ValueLabelStart,
}; };
use cranelift_codegen::isa::{TargetFrontendConfig, TargetIsa}; use cranelift_codegen::isa::TargetFrontendConfig;
use cranelift_codegen::packed_option::PackedOption; use cranelift_codegen::packed_option::PackedOption;
/// Structure used for translating a series of functions into Cranelift IR. /// Structure used for translating a series of functions into Cranelift IR.
@@ -481,7 +480,7 @@ impl<'a> FunctionBuilder<'a> {
// Iterate manually to provide more helpful error messages. // Iterate manually to provide more helpful error messages.
for block in self.func_ctx.blocks.keys() { for block in self.func_ctx.blocks.keys() {
if let Err((inst, _msg)) = self.func.is_block_basic(block) { if let Err((inst, _msg)) = self.func.is_block_basic(block) {
let inst_str = self.func.dfg.display_inst(inst, None); let inst_str = self.func.dfg.display_inst(inst);
panic!("{} failed basic block invariants on {}", block, inst_str); panic!("{} failed basic block invariants on {}", block, inst_str);
} }
} }
@@ -579,15 +578,6 @@ impl<'a> FunctionBuilder<'a> {
pub fn is_filled(&self) -> bool { pub fn is_filled(&self) -> bool {
self.func_ctx.blocks[self.position.unwrap()].filled self.func_ctx.blocks[self.position.unwrap()].filled
} }
/// Returns a displayable object for the function as it is.
///
/// Useful for debug purposes. Use it with `None` for standard printing.
// Clippy thinks the lifetime that follows is needless, but rustc needs it
#[cfg_attr(feature = "cargo-clippy", allow(clippy::needless_lifetimes))]
pub fn display<'b, I: Into<Option<&'b dyn TargetIsa>>>(&'b self, isa: I) -> DisplayFunction {
self.func.display(isa)
}
} }
/// Helper functions /// Helper functions
@@ -955,7 +945,7 @@ mod tests {
let flags = settings::Flags::new(settings::builder()); let flags = settings::Flags::new(settings::builder());
// println!("{}", func.display(None)); // println!("{}", func.display(None));
if let Err(errors) = verify_function(&func, &flags) { if let Err(errors) = verify_function(&func, &flags) {
panic!("{}\n{}", func.display(None), errors) panic!("{}\n{}", func.display(), errors)
} }
} }
@@ -1009,7 +999,7 @@ mod tests {
} }
assert_eq!( assert_eq!(
func.display(None).to_string(), func.display().to_string(),
"function %sample() -> i32 system_v { "function %sample() -> i32 system_v {
sig0 = (i64, i64, i64) system_v sig0 = (i64, i64, i64) system_v
fn0 = %Memcpy sig0 fn0 = %Memcpy sig0
@@ -1065,7 +1055,7 @@ block0:
} }
assert_eq!( assert_eq!(
func.display(None).to_string(), func.display().to_string(),
"function %sample() -> i32 system_v { "function %sample() -> i32 system_v {
block0: block0:
v4 = iconst.i64 0 v4 = iconst.i64 0
@@ -1119,7 +1109,7 @@ block0:
} }
assert_eq!( assert_eq!(
func.display(None).to_string(), func.display().to_string(),
"function %sample() -> i32 system_v { "function %sample() -> i32 system_v {
sig0 = (i64, i64, i64) system_v sig0 = (i64, i64, i64) system_v
fn0 = %Memcpy sig0 fn0 = %Memcpy sig0
@@ -1164,7 +1154,7 @@ block0:
} }
assert_eq!( assert_eq!(
func.display(None).to_string(), func.display().to_string(),
"function %sample() -> i32 system_v { "function %sample() -> i32 system_v {
block0: block0:
v2 = iconst.i64 0 v2 = iconst.i64 0
@@ -1204,7 +1194,7 @@ block0:
} }
assert_eq!( assert_eq!(
func.display(None).to_string(), func.display().to_string(),
"function %sample() -> i32 system_v { "function %sample() -> i32 system_v {
sig0 = (i64, i32, i64) system_v sig0 = (i64, i32, i64) system_v
fn0 = %Memset sig0 fn0 = %Memset sig0
@@ -1253,7 +1243,7 @@ block0:
} }
assert_eq!( assert_eq!(
func.display(None).to_string(), func.display().to_string(),
"function %sample() -> i8x16, b8x16, f32x4 system_v { "function %sample() -> i8x16, b8x16, f32x4 system_v {
const0 = 0x00000000000000000000000000000000 const0 = 0x00000000000000000000000000000000

View File

@@ -156,7 +156,7 @@
//! //!
//! let flags = settings::Flags::new(settings::builder()); //! let flags = settings::Flags::new(settings::builder());
//! let res = verify_function(&func, &flags); //! let res = verify_function(&func, &flags);
//! println!("{}", func.display(None)); //! println!("{}", func.display());
//! if let Err(errors) = res { //! if let Err(errors) = res {
//! panic!("{}", errors); //! panic!("{}", errors);
//! } //! }

View File

@@ -452,16 +452,11 @@ where
Opcode::Fill => unimplemented!("Fill"), Opcode::Fill => unimplemented!("Fill"),
Opcode::FillNop => assign(arg(0)?), Opcode::FillNop => assign(arg(0)?),
Opcode::DummySargT => unimplemented!("DummySargT"), Opcode::DummySargT => unimplemented!("DummySargT"),
Opcode::Regmove => ControlFlow::Continue,
Opcode::CopySpecial => ControlFlow::Continue,
Opcode::CopyToSsa => assign(arg(0)?),
Opcode::CopyNop => unimplemented!("CopyNop"), Opcode::CopyNop => unimplemented!("CopyNop"),
Opcode::AdjustSpDown => unimplemented!("AdjustSpDown"), Opcode::AdjustSpDown => unimplemented!("AdjustSpDown"),
Opcode::AdjustSpUpImm => unimplemented!("AdjustSpUpImm"), Opcode::AdjustSpUpImm => unimplemented!("AdjustSpUpImm"),
Opcode::AdjustSpDownImm => unimplemented!("AdjustSpDownImm"), Opcode::AdjustSpDownImm => unimplemented!("AdjustSpDownImm"),
Opcode::IfcmpSp => unimplemented!("IfcmpSp"), Opcode::IfcmpSp => unimplemented!("IfcmpSp"),
Opcode::Regspill => unimplemented!("Regspill"),
Opcode::Regfill => unimplemented!("Regfill"),
Opcode::Safepoint => unimplemented!("Safepoint"), Opcode::Safepoint => unimplemented!("Safepoint"),
Opcode::Icmp => assign(icmp( Opcode::Icmp => assign(icmp(
ctrl_ty, ctrl_ty,

View File

@@ -634,7 +634,7 @@ impl Module for JITModule {
trap_sink: &mut dyn TrapSink, trap_sink: &mut dyn TrapSink,
stack_map_sink: &mut dyn StackMapSink, stack_map_sink: &mut dyn StackMapSink,
) -> ModuleResult<ModuleCompiledFunction> { ) -> ModuleResult<ModuleCompiledFunction> {
info!("defining function {}: {}", id, ctx.func.display(self.isa())); info!("defining function {}: {}", id, ctx.func.display());
let CodeInfo { let CodeInfo {
total_size: code_size, total_size: code_size,
.. ..

View File

@@ -149,7 +149,7 @@ fn switch_error() {
Ok(_) => {} Ok(_) => {}
Err(err) => { Err(err) => {
let pretty_error = let pretty_error =
cranelift_codegen::print_errors::pretty_verifier_error(&func, None, None, err); cranelift_codegen::print_errors::pretty_verifier_error(&func, None, err);
panic!("pretty_error:\n{}", pretty_error); panic!("pretty_error:\n{}", pretty_error);
} }
} }

View File

@@ -310,11 +310,7 @@ impl Module for ObjectModule {
trap_sink: &mut dyn TrapSink, trap_sink: &mut dyn TrapSink,
stack_map_sink: &mut dyn StackMapSink, stack_map_sink: &mut dyn StackMapSink,
) -> ModuleResult<ModuleCompiledFunction> { ) -> ModuleResult<ModuleCompiledFunction> {
info!( info!("defining function {}: {}", func_id, ctx.func.display());
"defining function {}: {}",
func_id,
ctx.func.display(self.isa())
);
let CodeInfo { let CodeInfo {
total_size: code_size, total_size: code_size,
.. ..

View File

@@ -142,7 +142,7 @@ fn switch_error() {
Ok(_) => {} Ok(_) => {}
Err(err) => { Err(err) => {
let pretty_error = let pretty_error =
cranelift_codegen::print_errors::pretty_verifier_error(&func, None, None, err); cranelift_codegen::print_errors::pretty_verifier_error(&func, None, err);
panic!("pretty_error:\n{}", pretty_error); panic!("pretty_error:\n{}", pretty_error);
} }
} }

View File

@@ -17,12 +17,12 @@ use cranelift_codegen::ir::instructions::{InstructionData, InstructionFormat, Va
use cranelift_codegen::ir::types::INVALID; use cranelift_codegen::ir::types::INVALID;
use cranelift_codegen::ir::types::*; use cranelift_codegen::ir::types::*;
use cranelift_codegen::ir::{ use cranelift_codegen::ir::{
AbiParam, ArgumentExtension, ArgumentLoc, ArgumentPurpose, Block, Constant, ConstantData, AbiParam, ArgumentExtension, ArgumentPurpose, Block, Constant, ConstantData, ExtFuncData,
ExtFuncData, ExternalName, FuncRef, Function, GlobalValue, GlobalValueData, Heap, HeapData, ExternalName, FuncRef, Function, GlobalValue, GlobalValueData, Heap, HeapData, HeapStyle,
HeapStyle, JumpTable, JumpTableData, MemFlags, Opcode, SigRef, Signature, StackSlot, JumpTable, JumpTableData, MemFlags, Opcode, SigRef, Signature, StackSlot, StackSlotData,
StackSlotData, StackSlotKind, Table, TableData, Type, Value, ValueLoc, StackSlotKind, Table, TableData, Type, Value,
}; };
use cranelift_codegen::isa::{self, CallConv, RegUnit, TargetIsa}; use cranelift_codegen::isa::{self, CallConv};
use cranelift_codegen::packed_option::ReservedValue; use cranelift_codegen::packed_option::ReservedValue;
use cranelift_codegen::{settings, settings::Configurable, timing}; use cranelift_codegen::{settings, settings::Configurable, timing};
use smallvec::SmallVec; use smallvec::SmallVec;
@@ -154,7 +154,7 @@ pub fn parse_test<'a>(text: &'a str, options: ParseOptions<'a>) -> ParseResult<T
parser.claim_gathered_comments(AnyEntity::Function); parser.claim_gathered_comments(AnyEntity::Function);
let preamble_comments = parser.take_comments(); let preamble_comments = parser.take_comments();
let functions = parser.parse_function_list(isa_spec.unique_isa())?; let functions = parser.parse_function_list()?;
Ok(TestFile { Ok(TestFile {
commands, commands,
@@ -229,26 +229,19 @@ pub struct Parser<'a> {
} }
/// Context for resolving references when parsing a single function. /// Context for resolving references when parsing a single function.
struct Context<'a> { struct Context {
function: Function, function: Function,
map: SourceMap, map: SourceMap,
/// Aliases to resolve once value definitions are known. /// Aliases to resolve once value definitions are known.
aliases: Vec<Value>, aliases: Vec<Value>,
/// Reference to the unique_isa for things like parsing target-specific instruction encoding
/// information. This is only `Some` if exactly one set of `isa` directives were found in the
/// prologue (it is valid to have directives for multiple different targets, but in that case
/// we couldn't know which target the provided encodings are intended for)
unique_isa: Option<&'a dyn TargetIsa>,
} }
impl<'a> Context<'a> { impl Context {
fn new(f: Function, unique_isa: Option<&'a dyn TargetIsa>) -> Self { fn new(f: Function) -> Self {
Self { Self {
function: f, function: f,
map: SourceMap::new(), map: SourceMap::new(),
unique_isa,
aliases: Vec::new(), aliases: Vec::new(),
} }
} }
@@ -948,27 +941,6 @@ impl<'a> Parser<'a> {
} }
} }
// Match and consume a register unit either by number `%15` or by name `%rax`.
fn match_regunit(&mut self, isa: Option<&dyn TargetIsa>) -> ParseResult<RegUnit> {
if let Some(Token::Name(name)) = self.token() {
self.consume();
match isa {
Some(isa) => isa
.register_info()
.parse_regunit(name)
.ok_or_else(|| self.error("invalid register name")),
None => name
.parse()
.map_err(|_| self.error("invalid register number")),
}
} else {
match isa {
Some(isa) => err!(self.loc, "Expected {} register unit", isa.name()),
None => err!(self.loc, "Expected register unit number"),
}
}
}
/// Parse an optional source location. /// Parse an optional source location.
/// ///
/// Return an optional source location if no real location is present. /// Return an optional source location if no real location is present.
@@ -1188,13 +1160,10 @@ impl<'a> Parser<'a> {
/// Parse a list of function definitions. /// Parse a list of function definitions.
/// ///
/// This is the top-level parse function matching the whole contents of a file. /// This is the top-level parse function matching the whole contents of a file.
pub fn parse_function_list( pub fn parse_function_list(&mut self) -> ParseResult<Vec<(Function, Details<'a>)>> {
&mut self,
unique_isa: Option<&dyn TargetIsa>,
) -> ParseResult<Vec<(Function, Details<'a>)>> {
let mut list = Vec::new(); let mut list = Vec::new();
while self.token().is_some() { while self.token().is_some() {
list.push(self.parse_function(unique_isa)?); list.push(self.parse_function()?);
} }
if let Some(err) = self.lex_error { if let Some(err) = self.lex_error {
return match err { return match err {
@@ -1208,10 +1177,7 @@ impl<'a> Parser<'a> {
// //
// function ::= * "function" name signature "{" preamble function-body "}" // function ::= * "function" name signature "{" preamble function-body "}"
// //
fn parse_function( fn parse_function(&mut self) -> ParseResult<(Function, Details<'a>)> {
&mut self,
unique_isa: Option<&dyn TargetIsa>,
) -> ParseResult<(Function, Details<'a>)> {
// Begin gathering comments. // Begin gathering comments.
// Make sure we don't include any comments before the `function` keyword. // Make sure we don't include any comments before the `function` keyword.
self.token(); self.token();
@@ -1226,9 +1192,9 @@ impl<'a> Parser<'a> {
let name = self.parse_external_name()?; let name = self.parse_external_name()?;
// function ::= "function" name * signature "{" preamble function-body "}" // function ::= "function" name * signature "{" preamble function-body "}"
let sig = self.parse_signature(unique_isa)?; let sig = self.parse_signature()?;
let mut ctx = Context::new(Function::with_name_signature(name, sig), unique_isa); let mut ctx = Context::new(Function::with_name_signature(name, sig));
// function ::= "function" name signature * "{" preamble function-body "}" // function ::= "function" name signature * "{" preamble function-body "}"
self.match_token(Token::LBrace, "expected '{' before function body")?; self.match_token(Token::LBrace, "expected '{' before function body")?;
@@ -1298,18 +1264,18 @@ impl<'a> Parser<'a> {
// //
// signature ::= * "(" [paramlist] ")" ["->" retlist] [callconv] // signature ::= * "(" [paramlist] ")" ["->" retlist] [callconv]
// //
fn parse_signature(&mut self, unique_isa: Option<&dyn TargetIsa>) -> ParseResult<Signature> { fn parse_signature(&mut self) -> ParseResult<Signature> {
// Calling convention defaults to `fast`, but can be changed. // Calling convention defaults to `fast`, but can be changed.
let mut sig = Signature::new(self.default_calling_convention); let mut sig = Signature::new(self.default_calling_convention);
self.match_token(Token::LPar, "expected function signature: ( args... )")?; self.match_token(Token::LPar, "expected function signature: ( args... )")?;
// signature ::= "(" * [abi-param-list] ")" ["->" retlist] [callconv] // signature ::= "(" * [abi-param-list] ")" ["->" retlist] [callconv]
if self.token() != Some(Token::RPar) { if self.token() != Some(Token::RPar) {
sig.params = self.parse_abi_param_list(unique_isa)?; sig.params = self.parse_abi_param_list()?;
} }
self.match_token(Token::RPar, "expected ')' after function arguments")?; self.match_token(Token::RPar, "expected ')' after function arguments")?;
if self.optional(Token::Arrow) { if self.optional(Token::Arrow) {
sig.returns = self.parse_abi_param_list(unique_isa)?; sig.returns = self.parse_abi_param_list()?;
} }
// The calling convention is optional. // The calling convention is optional.
@@ -1330,26 +1296,23 @@ impl<'a> Parser<'a> {
// //
// paramlist ::= * param { "," param } // paramlist ::= * param { "," param }
// //
fn parse_abi_param_list( fn parse_abi_param_list(&mut self) -> ParseResult<Vec<AbiParam>> {
&mut self,
unique_isa: Option<&dyn TargetIsa>,
) -> ParseResult<Vec<AbiParam>> {
let mut list = Vec::new(); let mut list = Vec::new();
// abi-param-list ::= * abi-param { "," abi-param } // abi-param-list ::= * abi-param { "," abi-param }
list.push(self.parse_abi_param(unique_isa)?); list.push(self.parse_abi_param()?);
// abi-param-list ::= abi-param * { "," abi-param } // abi-param-list ::= abi-param * { "," abi-param }
while self.optional(Token::Comma) { while self.optional(Token::Comma) {
// abi-param-list ::= abi-param { "," * abi-param } // abi-param-list ::= abi-param { "," * abi-param }
list.push(self.parse_abi_param(unique_isa)?); list.push(self.parse_abi_param()?);
} }
Ok(list) Ok(list)
} }
// Parse a single argument type with flags. // Parse a single argument type with flags.
fn parse_abi_param(&mut self, unique_isa: Option<&dyn TargetIsa>) -> ParseResult<AbiParam> { fn parse_abi_param(&mut self) -> ParseResult<AbiParam> {
// abi-param ::= * type { flag } [ argumentloc ] // abi-param ::= * type { flag } [ argumentloc ]
let mut arg = AbiParam::new(self.match_type("expected parameter type")?); let mut arg = AbiParam::new(self.match_type("expected parameter type")?);
@@ -1377,53 +1340,9 @@ impl<'a> Parser<'a> {
self.consume(); self.consume();
} }
// abi-param ::= type { flag } * [ argumentloc ]
arg.location = self.parse_argument_location(unique_isa)?;
Ok(arg) Ok(arg)
} }
// Parse an argument location specifier; either a register or a byte offset into the stack.
fn parse_argument_location(
&mut self,
unique_isa: Option<&dyn TargetIsa>,
) -> ParseResult<ArgumentLoc> {
// argumentloc ::= '[' regname | uimm32 ']'
if self.optional(Token::LBracket) {
let result = match self.token() {
Some(Token::Name(name)) => {
self.consume();
if let Some(isa) = unique_isa {
isa.register_info()
.parse_regunit(name)
.map(ArgumentLoc::Reg)
.ok_or_else(|| self.error("invalid register name"))
} else {
err!(self.loc, "argument location requires exactly one isa")
}
}
Some(Token::Integer(_)) => {
let offset = self.match_imm32("expected stack argument byte offset")?;
Ok(ArgumentLoc::Stack(offset))
}
Some(Token::Minus) => {
self.consume();
Ok(ArgumentLoc::Unassigned)
}
_ => err!(self.loc, "expected argument location"),
};
self.match_token(
Token::RBracket,
"expected ']' to end argument location annotation",
)?;
result
} else {
Ok(ArgumentLoc::Unassigned)
}
}
// Parse the function preamble. // Parse the function preamble.
// //
// preamble ::= * { preamble-decl } // preamble ::= * { preamble-decl }
@@ -1460,10 +1379,9 @@ impl<'a> Parser<'a> {
} }
Some(Token::SigRef(..)) => { Some(Token::SigRef(..)) => {
self.start_gathering_comments(); self.start_gathering_comments();
self.parse_signature_decl(ctx.unique_isa) self.parse_signature_decl().and_then(|(sig, dat)| {
.and_then(|(sig, dat)| { ctx.add_sig(sig, dat, self.loc, self.default_calling_convention)
ctx.add_sig(sig, dat, self.loc, self.default_calling_convention) })
})
} }
Some(Token::FuncRef(..)) => { Some(Token::FuncRef(..)) => {
self.start_gathering_comments(); self.start_gathering_comments();
@@ -1746,13 +1664,10 @@ impl<'a> Parser<'a> {
// //
// signature-decl ::= SigRef(sigref) "=" signature // signature-decl ::= SigRef(sigref) "=" signature
// //
fn parse_signature_decl( fn parse_signature_decl(&mut self) -> ParseResult<(SigRef, Signature)> {
&mut self,
unique_isa: Option<&dyn TargetIsa>,
) -> ParseResult<(SigRef, Signature)> {
let sig = self.match_sig("expected signature number: sig«n»")?; let sig = self.match_sig("expected signature number: sig«n»")?;
self.match_token(Token::Equal, "expected '=' in signature decl")?; self.match_token(Token::Equal, "expected '=' in signature decl")?;
let data = self.parse_signature(unique_isa)?; let data = self.parse_signature()?;
// Collect any trailing comments. // Collect any trailing comments.
self.token(); self.token();
@@ -1787,7 +1702,7 @@ impl<'a> Parser<'a> {
let data = match self.token() { let data = match self.token() {
Some(Token::LPar) => { Some(Token::LPar) => {
// function-decl ::= FuncRef(fnref) "=" ["colocated"] name * signature // function-decl ::= FuncRef(fnref) "=" ["colocated"] name * signature
let sig = self.parse_signature(ctx.unique_isa)?; let sig = self.parse_signature()?;
let sigref = ctx.function.import_signature(sig); let sigref = ctx.function.import_signature(sig);
ctx.map ctx.map
.def_entity(sigref.into(), loc) .def_entity(sigref.into(), loc)
@@ -1981,7 +1896,6 @@ impl<'a> Parser<'a> {
_ => false, _ => false,
} { } {
let srcloc = self.optional_srcloc()?; let srcloc = self.optional_srcloc()?;
let result_locations = self.parse_instruction_encoding(ctx)?;
// We need to parse instruction results here because they are shared // We need to parse instruction results here because they are shared
// between the parsing of value aliases and the parsing of instructions. // between the parsing of value aliases and the parsing of instructions.
@@ -2002,10 +1916,10 @@ impl<'a> Parser<'a> {
} }
Some(Token::Equal) => { Some(Token::Equal) => {
self.consume(); self.consume();
self.parse_instruction(&results, srcloc, result_locations, ctx, block)?; self.parse_instruction(&results, srcloc, ctx, block)?;
} }
_ if !results.is_empty() => return err!(self.loc, "expected -> or ="), _ if !results.is_empty() => return err!(self.loc, "expected -> or ="),
_ => self.parse_instruction(&results, srcloc, result_locations, ctx, block)?, _ => self.parse_instruction(&results, srcloc, ctx, block)?,
} }
} }
@@ -2057,77 +1971,9 @@ impl<'a> Parser<'a> {
ctx.function.dfg.append_block_param_for_parser(block, t, v); ctx.function.dfg.append_block_param_for_parser(block, t, v);
ctx.map.def_value(v, v_location)?; ctx.map.def_value(v, v_location)?;
// block-param ::= Value(v) ":" Type(t) * arg-loc?
if self.optional(Token::LBracket) {
let loc = self.parse_value_location(ctx)?;
ctx.function.locations[v] = loc;
self.match_token(Token::RBracket, "expected ']' after value location")?;
}
Ok(()) Ok(())
} }
fn parse_value_location(&mut self, ctx: &Context) -> ParseResult<ValueLoc> {
match self.token() {
Some(Token::StackSlot(src_num)) => {
self.consume();
let ss = match StackSlot::with_number(src_num) {
None => {
return err!(
self.loc,
"attempted to use invalid stack slot ss{}",
src_num
);
}
Some(ss) => ss,
};
ctx.check_ss(ss, self.loc)?;
Ok(ValueLoc::Stack(ss))
}
Some(Token::Name(name)) => {
self.consume();
if let Some(isa) = ctx.unique_isa {
isa.register_info()
.parse_regunit(name)
.map(ValueLoc::Reg)
.ok_or_else(|| self.error("invalid register value location"))
} else {
err!(self.loc, "value location requires exactly one isa")
}
}
Some(Token::Minus) => {
self.consume();
Ok(ValueLoc::Unassigned)
}
_ => err!(self.loc, "invalid value location"),
}
}
fn parse_instruction_encoding(&mut self, ctx: &Context) -> ParseResult<Option<Vec<ValueLoc>>> {
let mut result_locations = None;
// encoding ::= "[" encoding_literal result_locations "]"
if self.optional(Token::LBracket) {
// result_locations ::= ("," ( "-" | names ) )?
// names ::= Name { "," Name }
let mut results = Vec::new();
results.push(self.parse_value_location(ctx)?);
while self.optional(Token::Comma) {
results.push(self.parse_value_location(ctx)?);
}
result_locations = Some(results);
self.match_token(
Token::RBracket,
"expected ']' to terminate instruction encoding",
)?;
}
Ok(result_locations)
}
// Parse instruction results and return them. // Parse instruction results and return them.
// //
// inst-results ::= Value(v) { "," Value(v) } // inst-results ::= Value(v) { "," Value(v) }
@@ -2202,7 +2048,6 @@ impl<'a> Parser<'a> {
&mut self, &mut self,
results: &[Value], results: &[Value],
srcloc: ir::SourceLoc, srcloc: ir::SourceLoc,
result_locations: Option<Vec<ValueLoc>>,
ctx: &mut Context, ctx: &mut Context,
block: Block, block: Block,
) -> ParseResult<()> { ) -> ParseResult<()> {
@@ -2266,30 +2111,6 @@ impl<'a> Parser<'a> {
); );
} }
if let Some(ref result_locations) = result_locations {
if results.len() != result_locations.len() {
return err!(
self.loc,
"instruction produces {} result values, but {} locations were \
specified",
results.len(),
result_locations.len()
);
}
}
if let Some(result_locations) = result_locations {
for (&value, loc) in ctx
.function
.dfg
.inst_results(inst)
.iter()
.zip(result_locations)
{
ctx.function.locations[value] = loc;
}
}
// Collect any trailing comments. // Collect any trailing comments.
self.token(); self.token();
self.claim_gathered_comments(inst); self.claim_gathered_comments(inst);
@@ -3104,60 +2925,6 @@ impl<'a> Parser<'a> {
offset, offset,
} }
} }
InstructionFormat::RegMove => {
let arg = self.match_value("expected SSA value operand")?;
self.match_token(Token::Comma, "expected ',' between operands")?;
let src = self.match_regunit(ctx.unique_isa)?;
self.match_token(Token::Arrow, "expected '->' between register units")?;
let dst = self.match_regunit(ctx.unique_isa)?;
InstructionData::RegMove {
opcode,
arg,
src,
dst,
}
}
InstructionFormat::CopySpecial => {
let src = self.match_regunit(ctx.unique_isa)?;
self.match_token(Token::Arrow, "expected '->' between register units")?;
let dst = self.match_regunit(ctx.unique_isa)?;
InstructionData::CopySpecial { opcode, src, dst }
}
InstructionFormat::CopyToSsa => InstructionData::CopyToSsa {
opcode,
src: self.match_regunit(ctx.unique_isa)?,
},
InstructionFormat::RegSpill => {
let arg = self.match_value("expected SSA value operand")?;
self.match_token(Token::Comma, "expected ',' between operands")?;
let src = self.match_regunit(ctx.unique_isa)?;
self.match_token(Token::Arrow, "expected '->' before destination stack slot")?;
let dst = self.match_ss("expected stack slot number: ss«n»")?;
ctx.check_ss(dst, self.loc)?;
InstructionData::RegSpill {
opcode,
arg,
src,
dst,
}
}
InstructionFormat::RegFill => {
let arg = self.match_value("expected SSA value operand")?;
self.match_token(Token::Comma, "expected ',' between operands")?;
let src = self.match_ss("expected stack slot number: ss«n»")?;
ctx.check_ss(src, self.loc)?;
self.match_token(
Token::Arrow,
"expected '->' before destination register units",
)?;
let dst = self.match_regunit(ctx.unique_isa)?;
InstructionData::RegFill {
opcode,
arg,
src,
dst,
}
}
InstructionFormat::Trap => { InstructionFormat::Trap => {
let code = self.match_enum("expected trap code")?; let code = self.match_enum("expected trap code")?;
InstructionData::Trap { opcode, code } InstructionData::Trap { opcode, code }
@@ -3258,7 +3025,7 @@ mod tests {
#[test] #[test]
fn argument_type() { fn argument_type() {
let mut p = Parser::new("i32 sext"); let mut p = Parser::new("i32 sext");
let arg = p.parse_abi_param(None).unwrap(); let arg = p.parse_abi_param().unwrap();
assert_eq!(arg.value_type, types::I32); assert_eq!(arg.value_type, types::I32);
assert_eq!(arg.extension, ArgumentExtension::Sext); assert_eq!(arg.extension, ArgumentExtension::Sext);
assert_eq!(arg.purpose, ArgumentPurpose::Normal); assert_eq!(arg.purpose, ArgumentPurpose::Normal);
@@ -3266,7 +3033,7 @@ mod tests {
location, location,
message, message,
is_warning, is_warning,
} = p.parse_abi_param(None).unwrap_err(); } = p.parse_abi_param().unwrap_err();
assert_eq!(location.line_number, 1); assert_eq!(location.line_number, 1);
assert_eq!(message, "expected parameter type"); assert_eq!(message, "expected parameter type");
assert!(!is_warning); assert!(!is_warning);
@@ -3282,7 +3049,7 @@ mod tests {
v1 = iadd_imm v3, 17 v1 = iadd_imm v3, 17
}", }",
) )
.parse_function(None) .parse_function()
.unwrap(); .unwrap();
assert_eq!(func.name.to_string(), "%qux"); assert_eq!(func.name.to_string(), "%qux");
let v4 = details.map.lookup_str("v4").unwrap(); let v4 = details.map.lookup_str("v4").unwrap();
@@ -3300,13 +3067,13 @@ mod tests {
#[test] #[test]
fn signature() { fn signature() {
let sig = Parser::new("()system_v").parse_signature(None).unwrap(); let sig = Parser::new("()system_v").parse_signature().unwrap();
assert_eq!(sig.params.len(), 0); assert_eq!(sig.params.len(), 0);
assert_eq!(sig.returns.len(), 0); assert_eq!(sig.returns.len(), 0);
assert_eq!(sig.call_conv, CallConv::SystemV); assert_eq!(sig.call_conv, CallConv::SystemV);
let sig2 = Parser::new("(i8 uext, f32, f64, i32 sret) -> i32 sext, f64 baldrdash_system_v") let sig2 = Parser::new("(i8 uext, f32, f64, i32 sret) -> i32 sext, f64 baldrdash_system_v")
.parse_signature(None) .parse_signature()
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
sig2.to_string(), sig2.to_string(),
@@ -3316,12 +3083,12 @@ mod tests {
// Old-style signature without a calling convention. // Old-style signature without a calling convention.
assert_eq!( assert_eq!(
Parser::new("()").parse_signature(None).unwrap().to_string(), Parser::new("()").parse_signature().unwrap().to_string(),
"() fast" "() fast"
); );
assert_eq!( assert_eq!(
Parser::new("() notacc") Parser::new("() notacc")
.parse_signature(None) .parse_signature()
.unwrap_err() .unwrap_err()
.to_string(), .to_string(),
"1: unknown calling convention: notacc" "1: unknown calling convention: notacc"
@@ -3330,21 +3097,21 @@ mod tests {
// `void` is not recognized as a type by the lexer. It should not appear in files. // `void` is not recognized as a type by the lexer. It should not appear in files.
assert_eq!( assert_eq!(
Parser::new("() -> void") Parser::new("() -> void")
.parse_signature(None) .parse_signature()
.unwrap_err() .unwrap_err()
.to_string(), .to_string(),
"1: expected parameter type" "1: expected parameter type"
); );
assert_eq!( assert_eq!(
Parser::new("i8 -> i8") Parser::new("i8 -> i8")
.parse_signature(None) .parse_signature()
.unwrap_err() .unwrap_err()
.to_string(), .to_string(),
"1: expected function signature: ( args... )" "1: expected function signature: ( args... )"
); );
assert_eq!( assert_eq!(
Parser::new("(i8 -> i8") Parser::new("(i8 -> i8")
.parse_signature(None) .parse_signature()
.unwrap_err() .unwrap_err()
.to_string(), .to_string(),
"1: expected ')' after function arguments" "1: expected ')' after function arguments"
@@ -3359,7 +3126,7 @@ mod tests {
ss1 = spill_slot 1 ss1 = spill_slot 1
}", }",
) )
.parse_function(None) .parse_function()
.unwrap(); .unwrap();
assert_eq!(func.name.to_string(), "%foo"); assert_eq!(func.name.to_string(), "%foo");
let mut iter = func.stack_slots.keys(); let mut iter = func.stack_slots.keys();
@@ -3383,7 +3150,7 @@ mod tests {
ss1 = spill_slot 1 ss1 = spill_slot 1
}", }",
) )
.parse_function(None) .parse_function()
.unwrap_err() .unwrap_err()
.to_string(), .to_string(),
"3: duplicate entity: ss1" "3: duplicate entity: ss1"
@@ -3398,7 +3165,7 @@ mod tests {
block4(v3: i32): block4(v3: i32):
}", }",
) )
.parse_function(None) .parse_function()
.unwrap(); .unwrap();
assert_eq!(func.name.to_string(), "%blocks"); assert_eq!(func.name.to_string(), "%blocks");
@@ -3425,7 +3192,7 @@ mod tests {
block0: block0:
return 2", return 2",
) )
.parse_function(None) .parse_function()
.unwrap_err(); .unwrap_err();
assert_eq!(location.line_number, 3); assert_eq!(location.line_number, 3);
@@ -3443,7 +3210,7 @@ mod tests {
"function %a() { "function %a() {
block100000:", block100000:",
) )
.parse_function(None) .parse_function()
.unwrap_err(); .unwrap_err();
assert_eq!(location.line_number, 2); assert_eq!(location.line_number, 2);
@@ -3462,7 +3229,7 @@ mod tests {
jt0 = jump_table [] jt0 = jump_table []
jt0 = jump_table []", jt0 = jump_table []",
) )
.parse_function(None) .parse_function()
.unwrap_err(); .unwrap_err();
assert_eq!(location.line_number, 3); assert_eq!(location.line_number, 3);
@@ -3481,7 +3248,7 @@ mod tests {
ss0 = explicit_slot 8 ss0 = explicit_slot 8
ss0 = explicit_slot 8", ss0 = explicit_slot 8",
) )
.parse_function(None) .parse_function()
.unwrap_err(); .unwrap_err();
assert_eq!(location.line_number, 3); assert_eq!(location.line_number, 3);
@@ -3500,7 +3267,7 @@ mod tests {
gv0 = vmctx gv0 = vmctx
gv0 = vmctx", gv0 = vmctx",
) )
.parse_function(None) .parse_function()
.unwrap_err(); .unwrap_err();
assert_eq!(location.line_number, 3); assert_eq!(location.line_number, 3);
@@ -3519,7 +3286,7 @@ mod tests {
heap0 = static gv0, min 0x1000, bound 0x10_0000, offset_guard 0x1000 heap0 = static gv0, min 0x1000, bound 0x10_0000, offset_guard 0x1000
heap0 = static gv0, min 0x1000, bound 0x10_0000, offset_guard 0x1000", heap0 = static gv0, min 0x1000, bound 0x10_0000, offset_guard 0x1000",
) )
.parse_function(None) .parse_function()
.unwrap_err(); .unwrap_err();
assert_eq!(location.line_number, 3); assert_eq!(location.line_number, 3);
@@ -3538,7 +3305,7 @@ mod tests {
sig0 = () sig0 = ()
sig0 = ()", sig0 = ()",
) )
.parse_function(None) .parse_function()
.unwrap_err(); .unwrap_err();
assert_eq!(location.line_number, 3); assert_eq!(location.line_number, 3);
@@ -3558,7 +3325,7 @@ mod tests {
fn0 = %foo sig0 fn0 = %foo sig0
fn0 = %foo sig0", fn0 = %foo sig0",
) )
.parse_function(None) .parse_function()
.unwrap_err(); .unwrap_err();
assert_eq!(location.line_number, 4); assert_eq!(location.line_number, 4);
@@ -3580,7 +3347,7 @@ mod tests {
} ; Trailing. } ; Trailing.
; More trailing.", ; More trailing.",
) )
.parse_function(None) .parse_function()
.unwrap(); .unwrap();
assert_eq!(func.name.to_string(), "%comment"); assert_eq!(func.name.to_string(), "%comment");
assert_eq!(comments.len(), 8); // no 'before' comment. assert_eq!(comments.len(), 8); // no 'before' comment.
@@ -3682,7 +3449,7 @@ mod tests {
trap int_divz trap int_divz
}", }",
) )
.parse_function(None) .parse_function()
.unwrap() .unwrap()
.0; .0;
assert_eq!(func.name.to_string(), "u1:2"); assert_eq!(func.name.to_string(), "u1:2");
@@ -3694,7 +3461,7 @@ mod tests {
trap stk_ovf trap stk_ovf
}", }",
); );
assert!(parser.parse_function(None).is_err()); assert!(parser.parse_function().is_err());
// Incomplete function names should not be valid: // Incomplete function names should not be valid:
let mut parser = Parser::new( let mut parser = Parser::new(
@@ -3703,7 +3470,7 @@ mod tests {
trap int_ovf trap int_ovf
}", }",
); );
assert!(parser.parse_function(None).is_err()); assert!(parser.parse_function().is_err());
let mut parser = Parser::new( let mut parser = Parser::new(
"function u0() system_v { "function u0() system_v {
@@ -3711,7 +3478,7 @@ mod tests {
trap int_ovf trap int_ovf
}", }",
); );
assert!(parser.parse_function(None).is_err()); assert!(parser.parse_function().is_err());
let mut parser = Parser::new( let mut parser = Parser::new(
"function u0:() system_v { "function u0:() system_v {
@@ -3719,7 +3486,7 @@ mod tests {
trap int_ovf trap int_ovf
}", }",
); );
assert!(parser.parse_function(None).is_err()); assert!(parser.parse_function().is_err());
} }
#[test] #[test]
@@ -3732,14 +3499,14 @@ mod tests {
// By default the parser will use the fast calling convention if none is specified. // By default the parser will use the fast calling convention if none is specified.
let mut parser = Parser::new(code); let mut parser = Parser::new(code);
assert_eq!( assert_eq!(
parser.parse_function(None).unwrap().0.signature.call_conv, parser.parse_function().unwrap().0.signature.call_conv,
CallConv::Fast CallConv::Fast
); );
// However, we can specify a different calling convention to be the default. // However, we can specify a different calling convention to be the default.
let mut parser = Parser::new(code).with_default_calling_convention(CallConv::Cold); let mut parser = Parser::new(code).with_default_calling_convention(CallConv::Cold);
assert_eq!( assert_eq!(
parser.parse_function(None).unwrap().0.signature.call_conv, parser.parse_function().unwrap().0.signature.call_conv,
CallConv::Cold CallConv::Cold
); );
} }

View File

@@ -570,18 +570,6 @@ impl Mutator for RemoveUnusedEntities {
.push(inst); .push(inst);
} }
InstructionData::RegSpill { dst, .. } => {
stack_slot_usage_map
.entry(dst)
.or_insert_with(Vec::new)
.push(inst);
}
InstructionData::RegFill { src, .. } => {
stack_slot_usage_map
.entry(src)
.or_insert_with(Vec::new)
.push(inst);
}
_ => {} _ => {}
} }
} }
@@ -599,12 +587,6 @@ impl Mutator for RemoveUnusedEntities {
| InstructionData::StackStore { stack_slot, .. } => { | InstructionData::StackStore { stack_slot, .. } => {
*stack_slot = new_stack_slot; *stack_slot = new_stack_slot;
} }
InstructionData::RegSpill { dst, .. } => {
*dst = new_stack_slot;
}
InstructionData::RegFill { src, .. } => {
*src = new_stack_slot;
}
_ => unreachable!(), _ => unreachable!(),
} }
} }

View File

@@ -80,12 +80,10 @@ fn handle_module(options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa) -
// Compile and encode the result to machine code. // Compile and encode the result to machine code.
let code_info = context let code_info = context
.compile_and_emit(isa, &mut mem, &mut relocs, &mut traps, &mut stack_maps) .compile_and_emit(isa, &mut mem, &mut relocs, &mut traps, &mut stack_maps)
.map_err(|err| { .map_err(|err| anyhow::anyhow!("{}", pretty_error(&context.func, err)))?;
anyhow::anyhow!("{}", pretty_error(&context.func, Some(isa), err))
})?;
if options.print { if options.print {
println!("{}", context.func.display(isa)); println!("{}", context.func.display());
} }
if options.disasm { if options.disasm {

View File

@@ -220,7 +220,7 @@ fn handle_module(options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa) -
{ {
println!("; Exported as \"{}\"", export_name); println!("; Exported as \"{}\"", export_name);
} }
println!("{}", context.func.display(None)); println!("{}", context.func.display());
vprintln!(options.verbose, ""); vprintln!(options.verbose, "");
} }
let _ = terminal.reset(); let _ = terminal.reset();
@@ -263,15 +263,12 @@ fn handle_module(options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa) -
let mut stack_maps = PrintStackMaps::new(options.print); let mut stack_maps = PrintStackMaps::new(options.print);
if options.check_translation { if options.check_translation {
if let Err(errors) = context.verify(fisa) { if let Err(errors) = context.verify(fisa) {
anyhow::bail!( anyhow::bail!("{}", pretty_verifier_error(&context.func, None, errors));
"{}",
pretty_verifier_error(&context.func, fisa.isa, None, errors)
);
} }
} else { } else {
let code_info = context let code_info = context
.compile_and_emit(isa, &mut mem, &mut relocs, &mut traps, &mut stack_maps) .compile_and_emit(isa, &mut mem, &mut relocs, &mut traps, &mut stack_maps)
.map_err(|err| anyhow::anyhow!("{}", pretty_error(&context.func, fisa.isa, err)))?; .map_err(|err| anyhow::anyhow!("{}", pretty_error(&context.func, err)))?;
if options.print_size { if options.print_size {
println!( println!(
@@ -321,7 +318,6 @@ fn handle_module(options: &Options, path: &Path, name: &str, fisa: FlagsOrIsa) -
println!( println!(
"{}", "{}",
context.func.display_with(DisplayFunctionAnnotations { context.func.display_with(DisplayFunctionAnnotations {
isa: fisa.isa,
value_ranges: value_ranges.as_ref(), value_ranges: value_ranges.as_ref(),
}) })
); );

View File

@@ -329,7 +329,7 @@ mod tests {
trans trans
.translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env()) .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env())
.unwrap(); .unwrap();
debug!("{}", ctx.func.display(None)); debug!("{}", ctx.func.display());
ctx.verify(&flags).unwrap(); ctx.verify(&flags).unwrap();
} }
@@ -368,7 +368,7 @@ mod tests {
trans trans
.translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env()) .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env())
.unwrap(); .unwrap();
debug!("{}", ctx.func.display(None)); debug!("{}", ctx.func.display());
ctx.verify(&flags).unwrap(); ctx.verify(&flags).unwrap();
} }
@@ -411,7 +411,7 @@ mod tests {
trans trans
.translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env()) .translate_body(&mut validator, body, &mut ctx.func, &mut runtime.func_env())
.unwrap(); .unwrap();
debug!("{}", ctx.func.display(None)); debug!("{}", ctx.func.display());
ctx.verify(&flags).unwrap(); ctx.verify(&flags).unwrap();
} }

View File

@@ -98,7 +98,7 @@ fn handle_module(data: Vec<u8>, flags: &Flags, return_mode: ReturnMode) {
for func in dummy_environ.info.function_bodies.values() { for func in dummy_environ.info.function_bodies.values() {
verifier::verify_function(func, flags) verifier::verify_function(func, flags)
.map_err(|errors| panic!("{}", pretty_verifier_error(func, None, None, errors))) .map_err(|errors| panic!("{}", pretty_verifier_error(func, None, errors)))
.unwrap(); .unwrap();
} }
} }

View File

@@ -172,13 +172,11 @@ impl wasmtime_environ::Compiler for Compiler {
&mut trap_sink, &mut trap_sink,
&mut stack_map_sink, &mut stack_map_sink,
) )
.map_err(|error| { .map_err(|error| CompileError::Codegen(pretty_error(&context.func, error)))?;
CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
})?;
let unwind_info = context.create_unwind_info(isa).map_err(|error| { let unwind_info = context
CompileError::Codegen(pretty_error(&context.func, Some(isa), error)) .create_unwind_info(isa)
})?; .map_err(|error| CompileError::Codegen(pretty_error(&context.func, error)))?;
let address_transform = let address_transform =
self.get_function_address_map(&context, &input, code_buf.len() as u32); self.get_function_address_map(&context, &input, code_buf.len() as u32);
@@ -534,13 +532,11 @@ impl Compiler {
&mut trap_sink, &mut trap_sink,
&mut stack_map_sink, &mut stack_map_sink,
) )
.map_err(|error| { .map_err(|error| CompileError::Codegen(pretty_error(&context.func, error)))?;
CompileError::Codegen(pretty_error(&context.func, Some(isa), error))
})?;
let unwind_info = context.create_unwind_info(isa).map_err(|error| { let unwind_info = context
CompileError::Codegen(pretty_error(&context.func, Some(isa), error)) .create_unwind_info(isa)
})?; .map_err(|error| CompileError::Codegen(pretty_error(&context.func, error)))?;
Ok(CompiledFunction { Ok(CompiledFunction {
body: code_buf, body: code_buf,