Address review comments:

- Undo temporary changes to default features (`all-arch`) and a
  signal-handler test.
- Remove `SIGTRAP` handler: no longer needed now that we've found an
  "undefined opcode" option on ARM64.
- Rename pp.rs to pretty_print.rs in machinst/.
- Only use empty stack-probe on non-x86. As per a comment in
  rust-lang/compiler-builtins [1], LLVM only supports stack probes on
  x86 and x86-64. Thus, on any other CPU architecture, we cannot refer
  to `__rust_probestack`, because it does not exist.
- Rename arm64 to aarch64.
- Use `target` directive in vcode filetests.
- Run the flags verifier, but without encinfo, when using new backends.
- Clean up warning overrides.
- Fix up use of casts: use u32::from(x) and siblings when possible,
  u32::try_from(x).unwrap() when not, to avoid silent truncation.
- Take immutable `Function` borrows as input; we don't actually
  mutate the input IR.
- Lots of other miscellaneous cleanups.

[1] cae3e6ea23/src/probestack.rs (L39)
This commit is contained in:
Chris Fallin
2020-04-15 16:31:44 -07:00
parent 3de504c24c
commit 48cf2c2f50
49 changed files with 1550 additions and 1544 deletions

View File

@@ -1,15 +1,17 @@
//! ABI definitions.
use crate::ir;
use crate::ir::StackSlot;
use crate::machinst::*;
use crate::settings;
use regalloc::{Reg, Set, SpillSlot, VirtualReg, Writable};
use regalloc::{Reg, Set, SpillSlot, Writable};
/// Trait implemented by an object that tracks ABI-related state (e.g., stack
/// layout) and can generate code while emitting the *body* of a function.
pub trait ABIBody<I: VCodeInst> {
pub trait ABIBody {
/// The instruction type for the ISA associated with this ABI.
type I: VCodeInst;
/// Get the liveins of the function.
fn liveins(&self) -> Set<RealReg>;
@@ -27,17 +29,19 @@ pub trait ABIBody<I: VCodeInst> {
/// Generate an instruction which copies an argument to a destination
/// register.
fn gen_copy_arg_to_reg(&self, idx: usize, into_reg: Writable<Reg>) -> I;
fn gen_copy_arg_to_reg(&self, idx: usize, into_reg: Writable<Reg>) -> Self::I;
/// Generate an instruction which copies a source register to a return
/// value slot.
fn gen_copy_reg_to_retval(&self, idx: usize, from_reg: Reg) -> I;
fn gen_copy_reg_to_retval(&self, idx: usize, from_reg: Reg) -> Self::I;
/// Generate a return instruction.
fn gen_ret(&self) -> I;
fn gen_ret(&self) -> Self::I;
/// Generate an epilogue placeholder.
fn gen_epilogue_placeholder(&self) -> I;
/// Generate an epilogue placeholder. The returned instruction should return `true` from
/// `is_epilogue_placeholder()`; this is used to indicate to the lowering driver when
/// the epilogue should be inserted.
fn gen_epilogue_placeholder(&self) -> Self::I;
// -----------------------------------------------------------------
// Every function above this line may only be called pre-regalloc.
@@ -56,32 +60,32 @@ pub trait ABIBody<I: VCodeInst> {
fn load_stackslot(
&self,
slot: StackSlot,
offset: usize,
offset: u32,
ty: Type,
into_reg: Writable<Reg>,
) -> I;
) -> Self::I;
/// Store to a stackslot.
fn store_stackslot(&self, slot: StackSlot, offset: usize, ty: Type, from_reg: Reg) -> I;
fn store_stackslot(&self, slot: StackSlot, offset: u32, ty: Type, from_reg: Reg) -> Self::I;
/// Load from a spillslot.
fn load_spillslot(&self, slot: SpillSlot, ty: Type, into_reg: Writable<Reg>) -> I;
fn load_spillslot(&self, slot: SpillSlot, ty: Type, into_reg: Writable<Reg>) -> Self::I;
/// Store to a spillslot.
fn store_spillslot(&self, slot: SpillSlot, ty: Type, from_reg: Reg) -> I;
fn store_spillslot(&self, slot: SpillSlot, ty: Type, from_reg: Reg) -> Self::I;
/// Generate a prologue, post-regalloc. This should include any stack
/// frame or other setup necessary to use the other methods (`load_arg`,
/// `store_retval`, and spillslot accesses.) |self| is mutable so that we
/// `store_retval`, and spillslot accesses.) `self` is mutable so that we
/// can store information in it which will be useful when creating the
/// epilogue.
fn gen_prologue(&mut self, flags: &settings::Flags) -> Vec<I>;
fn gen_prologue(&mut self, flags: &settings::Flags) -> Vec<Self::I>;
/// Generate an epilogue, post-regalloc. Note that this must generate the
/// actual return instruction (rather than emitting this in the lowering
/// logic), because the epilogue code comes before the return and the two are
/// likely closely related.
fn gen_epilogue(&self, flags: &settings::Flags) -> Vec<I>;
fn gen_epilogue(&self, flags: &settings::Flags) -> Vec<Self::I>;
/// Returns the full frame size for the given function, after prologue emission has run. This
/// comprises the spill space, incoming argument space, alignment padding, etc.
@@ -91,10 +95,10 @@ pub trait ABIBody<I: VCodeInst> {
fn get_spillslot_size(&self, rc: RegClass, ty: Type) -> u32;
/// Generate a spill.
fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, ty: Type) -> I;
fn gen_spill(&self, to_slot: SpillSlot, from_reg: RealReg, ty: Type) -> Self::I;
/// Generate a reload (fill).
fn gen_reload(&self, to_reg: Writable<RealReg>, from_slot: SpillSlot, ty: Type) -> I;
fn gen_reload(&self, to_reg: Writable<RealReg>, from_slot: SpillSlot, ty: Type) -> Self::I;
}
/// Trait implemented by an object that tracks ABI-related state and can
@@ -111,22 +115,25 @@ pub trait ABIBody<I: VCodeInst> {
/// and retval copies, and attach the register use/def info to the call.
///
/// This trait is thus provided for convenience to the backends.
pub trait ABICall<I: VCodeInst> {
pub trait ABICall {
/// The instruction type for the ISA associated with this ABI.
type I: VCodeInst;
/// Get the number of arguments expected.
fn num_args(&self) -> usize;
/// Save the clobbered registers.
/// Copy an argument value from a source register, prior to the call.
fn gen_copy_reg_to_arg(&self, idx: usize, from_reg: Reg) -> I;
fn gen_copy_reg_to_arg(&self, idx: usize, from_reg: Reg) -> Self::I;
/// Copy a return value into a destination register, after the call returns.
fn gen_copy_retval_to_reg(&self, idx: usize, into_reg: Writable<Reg>) -> I;
fn gen_copy_retval_to_reg(&self, idx: usize, into_reg: Writable<Reg>) -> Self::I;
/// Pre-adjust the stack, prior to argument copies and call.
fn gen_stack_pre_adjust(&self) -> Vec<I>;
fn gen_stack_pre_adjust(&self) -> Vec<Self::I>;
/// Post-adjust the satck, after call return and return-value copies.
fn gen_stack_post_adjust(&self) -> Vec<I>;
fn gen_stack_post_adjust(&self) -> Vec<Self::I>;
/// Generate the call itself.
///
@@ -138,5 +145,5 @@ pub trait ABICall<I: VCodeInst> {
/// registers are also logically defs, but should never be read; their
/// values are "defined" (to the regalloc) but "undefined" in every other
/// sense.)
fn gen_call(&self) -> Vec<I>;
fn gen_call(&self) -> Vec<Self::I>;
}

View File

@@ -4,9 +4,12 @@ use crate::binemit;
use crate::ir;
use crate::isa::{EncInfo, Encoding, Encodings, Legalize, RegClass, RegInfo, TargetIsa};
use crate::machinst::*;
use crate::regalloc::{RegDiversions, RegisterSet};
use crate::regalloc::RegisterSet;
use crate::settings::Flags;
#[cfg(feature = "testing_hooks")]
use crate::regalloc::RegDiversions;
use std::borrow::Cow;
use std::fmt;
use target_lexicon::Triple;
@@ -30,7 +33,11 @@ impl TargetIsaAdapter {
impl fmt::Display for TargetIsaAdapter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "MachBackend")
f.debug_struct("MachBackend")
.field("name", &self.backend.name())
.field("triple", &self.backend.triple())
.field("flags", &format!("{}", self.backend.flags()))
.finish()
}
}

View File

@@ -1,6 +1,7 @@
//! Computation of basic block order in emitted code.
use crate::machinst::*;
use regalloc::{BlockIx, Function};
/// Simple reverse postorder-based block order emission.
///
@@ -29,9 +30,8 @@ impl BlockRPO {
}
}
let (start, end) = &vcode.block_ranges[block as usize];
for i in *start..*end {
if vcode.insts[i as usize].is_epilogue_placeholder() {
for i in vcode.block_insns(BlockIx::new(block)) {
if vcode.get_insn(i).is_epilogue_placeholder() {
debug_assert!(self.deferred_last.is_none());
self.deferred_last = Some(block);
return;

View File

@@ -7,14 +7,13 @@ use crate::timing;
use log::debug;
use regalloc::{allocate_registers, RegAllocAlgorithm};
use std::env;
/// Compile the given function down to VCode with allocated registers, ready
/// for binary emission.
pub fn compile<B: LowerBackend>(
f: &mut Function,
f: &Function,
b: &B,
abi: Box<dyn ABIBody<B::MInst>>,
abi: Box<dyn ABIBody<I = B::MInst>>,
flags: &settings::Flags,
) -> VCode<B::MInst>
where
@@ -28,18 +27,8 @@ where
debug!("vcode from lowering: \n{}", vcode.show_rru(Some(universe)));
// Perform register allocation.
let algorithm = match env::var("REGALLOC") {
Ok(str) => match str.as_str() {
"lsrac" => RegAllocAlgorithm::LinearScanChecked,
"lsra" => RegAllocAlgorithm::LinearScan,
// to wit: btc doesn't mean "bitcoin" here
"btc" => RegAllocAlgorithm::BacktrackingChecked,
_ => RegAllocAlgorithm::Backtracking,
},
// By default use backtracking, which is the fastest.
Err(_) => RegAllocAlgorithm::Backtracking,
};
// TODO: select register allocation algorithm from flags.
let algorithm = RegAllocAlgorithm::Backtracking;
let result = {
let _tt = timing::regalloc();
allocate_registers(
@@ -70,7 +59,5 @@ where
vcode.show_rru(Some(universe))
);
//println!("{}\n", vcode.show_rru(Some(&B::MInst::reg_universe())));
vcode
}

View File

@@ -2,39 +2,37 @@
//! to machine instructions with virtual registers. This is *almost* the final
//! machine code, except for register allocation.
use crate::binemit::CodeSink;
use crate::dce::has_side_effect;
use crate::entity::SecondaryMap;
use crate::inst_predicates::has_side_effect;
use crate::ir::instructions::BranchInfo;
use crate::ir::{
Block, ExternalName, Function, GlobalValueData, Inst, InstructionData, MemFlags, Opcode,
Signature, SourceLoc, Type, Value, ValueDef,
};
use crate::isa::registers::RegUnit;
use crate::machinst::{
ABIBody, BlockIndex, MachInst, MachInstEmit, VCode, VCodeBuilder, VCodeInst,
};
use crate::machinst::{ABIBody, BlockIndex, VCode, VCodeBuilder, VCodeInst};
use crate::num_uses::NumUses;
use regalloc::Function as RegallocFunction;
use regalloc::{RealReg, Reg, RegClass, Set, VirtualReg, Writable};
use regalloc::{Reg, RegClass, Set, VirtualReg, Writable};
use alloc::boxed::Box;
use alloc::vec::Vec;
use log::debug;
use smallvec::SmallVec;
use std::collections::VecDeque;
use std::ops::Range;
/// A context that machine-specific lowering code can use to emit lowered instructions. This is the
/// view of the machine-independent per-function lowering context that is seen by the machine
/// backend.
pub trait LowerCtx<I> {
pub trait LowerCtx {
/// The instruction type for which this lowering framework is instantiated.
type I;
/// Get the instdata for a given IR instruction.
fn data(&self, ir_inst: Inst) -> &InstructionData;
/// Get the controlling type for a polymorphic IR instruction.
fn ty(&self, ir_inst: Inst) -> Type;
/// Emit a machine instruction.
fn emit(&mut self, mach_inst: I);
fn emit(&mut self, mach_inst: Self::I);
/// Indicate that an IR instruction has been merged, and so one of its
/// uses is gone (replaced by uses of the instruction's inputs). This
/// helps the lowering algorithm to perform on-the-fly DCE, skipping over
@@ -87,11 +85,11 @@ pub trait LowerBackend {
/// Lower a single instruction. Instructions are lowered in reverse order.
/// This function need not handle branches; those are always passed to
/// `lower_branch_group` below.
fn lower<C: LowerCtx<Self::MInst>>(&self, ctx: &mut C, inst: Inst);
fn lower<C: LowerCtx<I = Self::MInst>>(&self, ctx: &mut C, inst: Inst);
/// Lower a block-terminating group of branches (which together can be seen as one
/// N-way branch), given a vcode BlockIndex for each target.
fn lower_branch_group<C: LowerCtx<Self::MInst>>(
fn lower_branch_group<C: LowerCtx<I = Self::MInst>>(
&self,
ctx: &mut C,
insts: &[Inst],
@@ -103,22 +101,22 @@ pub trait LowerBackend {
/// Machine-independent lowering driver / machine-instruction container. Maintains a correspondence
/// from original Inst to MachInsts.
pub struct Lower<'a, I: VCodeInst> {
// The function to lower.
/// The function to lower.
f: &'a Function,
// Lowered machine instructions.
/// Lowered machine instructions.
vcode: VCodeBuilder<I>,
// Number of active uses (minus `dec_use()` calls by backend) of each instruction.
/// Number of active uses (minus `dec_use()` calls by backend) of each instruction.
num_uses: SecondaryMap<Inst, u32>,
// Mapping from `Value` (SSA value in IR) to virtual register.
/// Mapping from `Value` (SSA value in IR) to virtual register.
value_regs: SecondaryMap<Value, Reg>,
// Return-value vregs.
/// Return-value vregs.
retval_regs: Vec<Reg>,
// Next virtual register number to allocate.
/// Next virtual register number to allocate.
next_vreg: u32,
}
@@ -144,7 +142,7 @@ enum GenerateReturn {
impl<'a, I: VCodeInst> Lower<'a, I> {
/// Prepare a new lowering context for the given IR function.
pub fn new(f: &'a Function, abi: Box<dyn ABIBody<I>>) -> Lower<'a, I> {
pub fn new(f: &'a Function, abi: Box<dyn ABIBody<I = I>>) -> Lower<'a, I> {
let mut vcode = VCodeBuilder::new(abi);
let num_uses = NumUses::compute(f).take_uses();
@@ -244,7 +242,9 @@ impl<'a, I: VCodeInst> Lower<'a, I> {
let mut succs: SmallVec<[Block; 16]> = SmallVec::new();
for inst in self.f.layout.block_insts(b) {
if self.f.dfg[inst].opcode().is_branch() {
succs.extend(branch_targets(self.f, b, inst).into_iter());
visit_branch_targets(self.f, b, inst, |succ| {
succs.push(succ);
});
}
}
for succ in succs.into_iter() {
@@ -264,17 +264,14 @@ impl<'a, I: VCodeInst> Lower<'a, I> {
/// Lower the function.
pub fn lower<B: LowerBackend<MInst = I>>(mut self, backend: &B) -> VCode<I> {
// Find all reachable blocks.
let mut bbs = self.find_reachable_bbs();
// Work backward (reverse block order, reverse through each block), skipping insns with zero
// uses.
bbs.reverse();
let bbs = self.find_reachable_bbs();
// This records a Block-to-BlockIndex map so that branch targets can be resolved.
let mut next_bindex = self.vcode.init_bb_map(&bbs[..]);
// Allocate a separate BlockIndex for each control-flow instruction so that we can create
// the edge blocks later. Each entry for a control-flow inst is the edge block; the list
// has (cf-inst, edge block, orig block) tuples.
// has (control flow inst, edge block, orig block) tuples.
let mut edge_blocks_by_inst: SecondaryMap<Inst, Vec<BlockIndex>> =
SecondaryMap::with_default(vec![]);
let mut edge_blocks: Vec<(Inst, BlockIndex, Block)> = vec![];
@@ -282,7 +279,9 @@ impl<'a, I: VCodeInst> Lower<'a, I> {
debug!("about to lower function: {:?}", self.f);
debug!("bb map: {:?}", self.vcode.blocks_by_bb());
for bb in bbs.iter() {
// Work backward (reverse block order, reverse through each block), skipping insns with zero
// uses.
for bb in bbs.iter().rev() {
for inst in self.f.layout.block_insts(*bb) {
let op = self.f.dfg[inst].opcode();
if op.is_branch() {
@@ -293,9 +292,9 @@ impl<'a, I: VCodeInst> Lower<'a, I> {
edge_blocks_by_inst[inst].push(edge_block);
edge_blocks.push((inst, edge_block, next_bb));
};
for succ in branch_targets(self.f, *bb, inst).into_iter() {
visit_branch_targets(self.f, *bb, inst, |succ| {
add_succ(succ);
}
});
}
}
}
@@ -303,7 +302,9 @@ impl<'a, I: VCodeInst> Lower<'a, I> {
for bb in bbs.iter() {
debug!("lowering bb: {}", bb);
// If this is a return block, produce the return value setup.
// If this is a return block, produce the return value setup. N.B.: this comes
// *before* the below because it must occur *after* any other instructions, and
// instructions are lowered in reverse order.
let last_insn = self.f.layout.block_insts(*bb).last().unwrap();
let last_insn_opcode = self.f.dfg[last_insn].opcode();
if last_insn_opcode.is_return() {
@@ -513,7 +514,9 @@ impl<'a, I: VCodeInst> Lower<'a, I> {
}
}
impl<'a, I: VCodeInst> LowerCtx<I> for Lower<'a, I> {
impl<'a, I: VCodeInst> LowerCtx for Lower<'a, I> {
type I = I;
/// Get the instdata for a given IR instruction.
fn data(&self, ir_inst: Inst) -> &InstructionData {
&self.f.dfg[ir_inst]
@@ -695,29 +698,23 @@ impl<'a, I: VCodeInst> LowerCtx<I> for Lower<'a, I> {
}
}
fn branch_targets(f: &Function, block: Block, inst: Inst) -> SmallVec<[Block; 16]> {
let mut ret = SmallVec::new();
fn visit_branch_targets<F: FnMut(Block)>(f: &Function, block: Block, inst: Inst, mut visit: F) {
if f.dfg[inst].opcode() == Opcode::Fallthrough {
ret.push(f.layout.next_block(block).unwrap());
visit(f.layout.next_block(block).unwrap());
} else {
match &f.dfg[inst] {
&InstructionData::Jump { destination, .. }
| &InstructionData::Branch { destination, .. }
| &InstructionData::BranchInt { destination, .. }
| &InstructionData::BranchIcmp { destination, .. }
| &InstructionData::BranchFloat { destination, .. } => {
ret.push(destination);
match f.dfg[inst].analyze_branch(&f.dfg.value_lists) {
BranchInfo::NotABranch => {}
BranchInfo::SingleDest(dest, _) => {
visit(dest);
}
&InstructionData::BranchTable {
destination, table, ..
} => {
ret.push(destination);
for dest in f.jump_tables[table].as_slice() {
ret.push(*dest);
BranchInfo::Table(table, maybe_dest) => {
if let Some(dest) = maybe_dest {
visit(dest);
}
for &dest in f.jump_tables[table].as_slice() {
visit(dest);
}
}
_ => {}
}
}
ret
}

View File

@@ -17,105 +17,97 @@
//! (N.B.: though we show the VCode separately at each stage, the passes
//! mutate the VCode in place; these are not separate copies of the code.)
//!
//! | ir::Function (SSA IR, machine-independent opcodes)
//! | |
//! | | [lower]
//! | |
//! | VCode<arch_backend::Inst> (machine instructions:
//! | | - mostly virtual registers.
//! | | - cond branches in two-target form.
//! | | - branch targets are block indices.
//! | | - in-memory constants held by insns,
//! | | with unknown offsets.
//! | | - critical edges (actually all edges)
//! | | are split.)
//! | | [regalloc]
//! | |
//! | VCode<arch_backend::Inst> (machine instructions:
//! | | - all real registers.
//! | | - new instruction sequence returned
//! | | out-of-band in RegAllocResult.
//! | | - instruction sequence has spills,
//! | | reloads, and moves inserted.
//! | | - other invariants same as above.)
//! | |
//! | | [preamble/postamble]
//! | |
//! | VCode<arch_backend::Inst> (machine instructions:
//! | | - stack-frame size known.
//! | | - out-of-band instruction sequence
//! | | has preamble prepended to entry
//! | | block, and postamble injected before
//! | | every return instruction.
//! | | - all symbolic stack references to
//! | | stackslots and spillslots are resolved
//! | | to concrete FP-offset mem addresses.)
//! | | [block/insn ordering]
//! | |
//! | VCode<arch_backend::Inst> (machine instructions:
//! | | - vcode.final_block_order is filled in.
//! | | - new insn sequence from regalloc is
//! | | placed back into vcode and block
//! | | boundaries are updated.)
//! | | [redundant branch/block
//! | | removal]
//! | |
//! | VCode<arch_backend::Inst> (machine instructions:
//! | | - all blocks that were just an
//! | | unconditional branch are removed.)
//! | |
//! | | [branch finalization
//! | | (fallthroughs)]
//! | |
//! | VCode<arch_backend::Inst> (machine instructions:
//! | | - all branches are in lowered one-
//! | | target form, but targets are still
//! | | block indices.)
//! | |
//! | | [branch finalization
//! | | (offsets)]
//! | |
//! | VCode<arch_backend::Inst> (machine instructions:
//! | | - all branch offsets from start of
//! | | function are known, and all branches
//! | | have resolved-offset targets.)
//! | |
//! | | [MemArg finalization]
//! | |
//! | VCode<arch_backend::Inst> (machine instructions:
//! | | - all MemArg references to the constant
//! | | pool are replaced with offsets.
//! | | - all constant-pool data is collected
//! | | in the VCode.)
//! | |
//! | | [binary emission]
//! | |
//! | Vec<u8> (machine code!)
//! |
//! ```plain
//!
//! ir::Function (SSA IR, machine-independent opcodes)
//! |
//! | [lower]
//! |
//! VCode<arch_backend::Inst> (machine instructions:
//! | - mostly virtual registers.
//! | - cond branches in two-target form.
//! | - branch targets are block indices.
//! | - in-memory constants held by insns,
//! | with unknown offsets.
//! | - critical edges (actually all edges)
//! | are split.)
//! | [regalloc]
//! |
//! VCode<arch_backend::Inst> (machine instructions:
//! | - all real registers.
//! | - new instruction sequence returned
//! | out-of-band in RegAllocResult.
//! | - instruction sequence has spills,
//! | reloads, and moves inserted.
//! | - other invariants same as above.)
//! |
//! | [preamble/postamble]
//! |
//! VCode<arch_backend::Inst> (machine instructions:
//! | - stack-frame size known.
//! | - out-of-band instruction sequence
//! | has preamble prepended to entry
//! | block, and postamble injected before
//! | every return instruction.
//! | - all symbolic stack references to
//! | stackslots and spillslots are resolved
//! | to concrete FP-offset mem addresses.)
//! | [block/insn ordering]
//! |
//! VCode<arch_backend::Inst> (machine instructions:
//! | - vcode.final_block_order is filled in.
//! | - new insn sequence from regalloc is
//! | placed back into vcode and block
//! | boundaries are updated.)
//! | [redundant branch/block
//! | removal]
//! |
//! VCode<arch_backend::Inst> (machine instructions:
//! | - all blocks that were just an
//! | unconditional branch are removed.)
//! |
//! | [branch finalization
//! | (fallthroughs)]
//! |
//! VCode<arch_backend::Inst> (machine instructions:
//! | - all branches are in lowered one-
//! | target form, but targets are still
//! | block indices.)
//! |
//! | [branch finalization
//! | (offsets)]
//! |
//! VCode<arch_backend::Inst> (machine instructions:
//! | - all branch offsets from start of
//! | function are known, and all branches
//! | have resolved-offset targets.)
//! |
//! | [MemArg finalization]
//! |
//! VCode<arch_backend::Inst> (machine instructions:
//! | - all MemArg references to the constant
//! | pool are replaced with offsets.
//! | - all constant-pool data is collected
//! | in the VCode.)
//! |
//! | [binary emission]
//! |
//! Vec<u8> (machine code!)
//!
//! ```
#![allow(unused_imports)]
use crate::binemit::{
CodeInfo, CodeOffset, CodeSink, MemoryCodeSink, RelocSink, StackmapSink, TrapSink,
};
use crate::entity::EntityRef;
use crate::binemit::{CodeInfo, CodeOffset};
use crate::entity::SecondaryMap;
use crate::ir::condcodes::IntCC;
use crate::ir::ValueLocations;
use crate::ir::{DataFlowGraph, Function, Inst, Opcode, Type, Value};
use crate::isa::RegUnit;
use crate::ir::{Function, Type};
use crate::result::CodegenResult;
use crate::settings::Flags;
use crate::HashMap;
use alloc::boxed::Box;
use alloc::vec::Vec;
use core::fmt::Debug;
use core::iter::Sum;
use regalloc::Map as RegallocMap;
use regalloc::RegUsageCollector;
use regalloc::{RealReg, RealRegUniverse, Reg, RegClass, SpillSlot, VirtualReg, Writable};
use smallvec::SmallVec;
use std::hash::Hash;
use std::string::String;
use target_lexicon::Triple;
@@ -129,8 +121,8 @@ pub mod blockorder;
pub use blockorder::*;
pub mod abi;
pub use abi::*;
pub mod pp;
pub use pp::*;
pub mod pretty_print;
pub use pretty_print::*;
pub mod sections;
pub use sections::*;
pub mod adapter;
@@ -255,10 +247,10 @@ impl MachCompileResult {
/// Top-level machine backend trait, which wraps all monomorphized code and
/// allows a virtual call from the machine-independent `Function::compile()`.
pub trait MachBackend {
/// Compile the given function. Consumes the function.
/// Compile the given function.
fn compile_function(
&self,
func: Function,
func: &Function,
want_disasm: bool,
) -> CodegenResult<MachCompileResult>;

View File

@@ -3,7 +3,7 @@
//! simultaneously, so we buffer the result in memory and hand off to the
//! caller at the end of compilation.
use crate::binemit::{Addend, CodeOffset, CodeSink, Reloc, RelocSink, StackmapSink, TrapSink};
use crate::binemit::{Addend, CodeOffset, CodeSink, Reloc};
use crate::ir::{ExternalName, Opcode, SourceLoc, TrapCode};
use alloc::vec::Vec;
@@ -104,28 +104,31 @@ pub trait MachSectionOutput {
/// Add 2 bytes to the section.
fn put2(&mut self, value: u16) {
self.put1((value & 0xff) as u8);
self.put1(((value >> 8) & 0xff) as u8);
let [b0, b1] = value.to_le_bytes();
self.put1(b0);
self.put1(b1);
}
/// Add 4 bytes to the section.
fn put4(&mut self, value: u32) {
self.put1((value & 0xff) as u8);
self.put1(((value >> 8) & 0xff) as u8);
self.put1(((value >> 16) & 0xff) as u8);
self.put1(((value >> 24) & 0xff) as u8);
let [b0, b1, b2, b3] = value.to_le_bytes();
self.put1(b0);
self.put1(b1);
self.put1(b2);
self.put1(b3);
}
/// Add 8 bytes to the section.
fn put8(&mut self, value: u64) {
self.put1((value & 0xff) as u8);
self.put1(((value >> 8) & 0xff) as u8);
self.put1(((value >> 16) & 0xff) as u8);
self.put1(((value >> 24) & 0xff) as u8);
self.put1(((value >> 32) & 0xff) as u8);
self.put1(((value >> 40) & 0xff) as u8);
self.put1(((value >> 48) & 0xff) as u8);
self.put1(((value >> 56) & 0xff) as u8);
let [b0, b1, b2, b3, b4, b5, b6, b7] = value.to_le_bytes();
self.put1(b0);
self.put1(b1);
self.put1(b2);
self.put1(b3);
self.put1(b4);
self.put1(b5);
self.put1(b6);
self.put1(b7);
}
/// Add a slice of bytes to the section.

View File

@@ -17,7 +17,6 @@
//! See the main module comment in `mod.rs` for more details on the VCode-based
//! backend pipeline.
use crate::binemit::Reloc;
use crate::ir;
use crate::machinst::*;
use crate::settings;
@@ -32,7 +31,6 @@ use log::debug;
use smallvec::SmallVec;
use std::fmt;
use std::iter;
use std::ops::Index;
use std::string::String;
/// Index referring to an instruction in VCode.
@@ -59,13 +57,13 @@ pub struct VCode<I: VCodeInst> {
vreg_types: Vec<Type>,
/// Lowered machine instructions in order corresponding to the original IR.
pub insts: Vec<I>,
insts: Vec<I>,
/// Entry block.
entry: BlockIndex,
/// Block instruction indices.
pub block_ranges: Vec<(InsnIndex, InsnIndex)>,
block_ranges: Vec<(InsnIndex, InsnIndex)>,
/// Block successors: index range in the successor-list below.
block_succ_range: Vec<(usize, usize)>,
@@ -94,7 +92,7 @@ pub struct VCode<I: VCodeInst> {
code_size: CodeOffset,
/// ABI object.
abi: Box<dyn ABIBody<I>>,
abi: Box<dyn ABIBody<I = I>>,
}
/// A builder for a VCode function body. This builder is designed for the
@@ -128,7 +126,7 @@ pub struct VCodeBuilder<I: VCodeInst> {
impl<I: VCodeInst> VCodeBuilder<I> {
/// Create a new VCodeBuilder.
pub fn new(abi: Box<dyn ABIBody<I>>) -> VCodeBuilder<I> {
pub fn new(abi: Box<dyn ABIBody<I = I>>) -> VCodeBuilder<I> {
let vcode = VCode::new(abi);
VCodeBuilder {
vcode,
@@ -139,7 +137,7 @@ impl<I: VCodeInst> VCodeBuilder<I> {
}
/// Access the ABI object.
pub fn abi(&mut self) -> &mut dyn ABIBody<I> {
pub fn abi(&mut self) -> &mut dyn ABIBody<I = I> {
&mut *self.vcode.abi
}
@@ -282,7 +280,7 @@ fn is_trivial_jump_block<I: VCodeInst>(vcode: &VCode<I>, block: BlockIndex) -> O
impl<I: VCodeInst> VCode<I> {
/// New empty VCode.
fn new(abi: Box<dyn ABIBody<I>>) -> VCode<I> {
fn new(abi: Box<dyn ABIBody<I = I>>) -> VCode<I> {
VCode {
liveins: abi.liveins(),
liveouts: abi.liveouts(),
@@ -472,10 +470,10 @@ impl<I: VCodeInst> VCode<I> {
// Compute block offsets.
let mut code_section = MachSectionSize::new(0);
let mut block_offsets = vec![0; self.num_blocks()];
for block in &self.final_block_order {
for &block in &self.final_block_order {
code_section.offset = I::align_basic_block(code_section.offset);
block_offsets[*block as usize] = code_section.offset;
let (start, end) = self.block_ranges[*block as usize];
block_offsets[block as usize] = code_section.offset;
let (start, end) = self.block_ranges[block as usize];
for iix in start..end {
self.insts[iix as usize].emit(&mut code_section);
}
@@ -490,9 +488,9 @@ impl<I: VCodeInst> VCode<I> {
// it (so forward references are now possible), and (ii) mutates the
// instructions.
let mut code_section = MachSectionSize::new(0);
for block in &self.final_block_order {
for &block in &self.final_block_order {
code_section.offset = I::align_basic_block(code_section.offset);
let (start, end) = self.block_ranges[*block as usize];
let (start, end) = self.block_ranges[block as usize];
for iix in start..end {
self.insts[iix as usize]
.with_block_offsets(code_section.offset, &self.final_block_offsets[..]);
@@ -510,7 +508,7 @@ impl<I: VCodeInst> VCode<I> {
let code_idx = sections.add_section(0, self.code_size);
let code_section = sections.get_section(code_idx);
for block in &self.final_block_order {
for &block in &self.final_block_order {
let new_offset = I::align_basic_block(code_section.cur_offset_from_start());
while new_offset > code_section.cur_offset_from_start() {
// Pad with NOPs up to the aligned block offset.
@@ -519,7 +517,7 @@ impl<I: VCodeInst> VCode<I> {
}
assert_eq!(code_section.cur_offset_from_start(), new_offset);
let (start, end) = self.block_ranges[*block as usize];
let (start, end) = self.block_ranges[block as usize];
for iix in start..end {
self.insts[iix as usize].emit(code_section);
}
@@ -639,9 +637,6 @@ impl<I: VCodeInst> RegallocFunction for VCode<I> {
}
}
// N.B.: Debug impl assumes that VCode has already been through all compilation
// passes, and so has a final block order and offsets.
impl<I: VCodeInst> fmt::Debug for VCode<I> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "VCode_Debug {{")?;
@@ -665,22 +660,21 @@ impl<I: VCodeInst> fmt::Debug for VCode<I> {
}
}
// Pretty-printing with `RealRegUniverse` context.
/// Pretty-printing with `RealRegUniverse` context.
impl<I: VCodeInst + ShowWithRRU> ShowWithRRU for VCode<I> {
fn show_rru(&self, mb_rru: Option<&RealRegUniverse>) -> String {
use crate::alloc::string::ToString;
use std::fmt::Write;
// Calculate an order in which to display the blocks. This is the same
// as final_block_order, but also includes blocks which are in the
// representation but not in final_block_order.
let mut display_order = Vec::<usize>::new();
// First display blocks in |final_block_order|
// First display blocks in `final_block_order`
for bix in &self.final_block_order {
assert!((*bix as usize) < self.num_blocks());
display_order.push(*bix as usize);
}
// Now also take care of those not listed in |final_block_order|.
// Now also take care of those not listed in `final_block_order`.
// This is quadratic, but it's also debug-only code.
for bix in 0..self.num_blocks() {
if display_order.contains(&bix) {
@@ -690,48 +684,46 @@ impl<I: VCodeInst + ShowWithRRU> ShowWithRRU for VCode<I> {
}
let mut s = String::new();
s = s + &format!("VCode_ShowWithRRU {{{{");
s = s + &"\n".to_string();
s = s + &format!(" Entry block: {}", self.entry);
s = s + &"\n".to_string();
s = s + &format!(" Final block order: {:?}", self.final_block_order);
s = s + &"\n".to_string();
write!(&mut s, "VCode_ShowWithRRU {{{{\n").unwrap();
write!(&mut s, " Entry block: {}\n", self.entry).unwrap();
write!(
&mut s,
" Final block order: {:?}\n",
self.final_block_order
)
.unwrap();
for i in 0..self.num_blocks() {
let block = display_order[i];
let omitted =
(if !self.final_block_order.is_empty() && i >= self.final_block_order.len() {
"** OMITTED **"
} else {
""
})
.to_string();
let omitted = if !self.final_block_order.is_empty() && i >= self.final_block_order.len()
{
"** OMITTED **"
} else {
""
};
s = s + &format!("Block {}: {}", block, omitted);
s = s + &"\n".to_string();
write!(&mut s, "Block {}: {}\n", block, omitted).unwrap();
if let Some(bb) = self.bindex_to_bb(block as BlockIndex) {
s = s + &format!(" (original IR block: {})\n", bb);
write!(&mut s, " (original IR block: {})\n", bb).unwrap();
}
for succ in self.succs(block as BlockIndex) {
s = s + &format!(" (successor: Block {})", succ);
s = s + &"\n".to_string();
write!(&mut s, " (successor: Block {})\n", succ).unwrap();
}
let (start, end) = self.block_ranges[block];
s = s + &format!(" (instruction range: {} .. {})", start, end);
s = s + &"\n".to_string();
write!(&mut s, " (instruction range: {} .. {})\n", start, end).unwrap();
for inst in start..end {
s = s + &format!(
" Inst {}: {}",
write!(
&mut s,
" Inst {}: {}\n",
inst,
self.insts[inst as usize].show_rru(mb_rru)
);
s = s + &"\n".to_string();
)
.unwrap();
}
}
s = s + &format!("}}}}");
s = s + &"\n".to_string();
write!(&mut s, "}}}}\n").unwrap();
s
}