Merge pull request #2565 from cfallin/debug-value-labels
Detailed debug-info (DWARF) support in new backends (initially x64).
This commit is contained in:
8
.github/workflows/main.yml
vendored
8
.github/workflows/main.yml
vendored
@@ -292,6 +292,14 @@ jobs:
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
|
||||
# Test debug (DWARF) related functionality on new backend.
|
||||
- run: |
|
||||
sudo apt-get update && sudo apt-get install -y gdb
|
||||
cargo test --features experimental_x64 test_debug_dwarf -- --ignored --test-threads 1 --test debug::
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
|
||||
# Build and test lightbeam. Note that
|
||||
# Lightbeam tests fail right now, but we don't want to block on that.
|
||||
- run: cargo build --package lightbeam
|
||||
|
||||
@@ -74,7 +74,7 @@ all-arch = [
|
||||
]
|
||||
|
||||
# For dependent crates that want to serialize some parts of cranelift
|
||||
enable-serde = ["serde"]
|
||||
enable-serde = ["serde", "regalloc/enable-serde"]
|
||||
|
||||
# Allow snapshotting regalloc test cases. Useful only to report bad register
|
||||
# allocation failures, or for regalloc.rs developers.
|
||||
|
||||
@@ -473,6 +473,7 @@ impl Context {
|
||||
Ok(build_value_labels_ranges::<ComparableSourceLoc>(
|
||||
&self.func,
|
||||
&self.regalloc,
|
||||
self.mach_compile_result.as_ref(),
|
||||
isa,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ pub use crate::ir::table::TableData;
|
||||
pub use crate::ir::trapcode::TrapCode;
|
||||
pub use crate::ir::types::Type;
|
||||
pub use crate::ir::valueloc::{ArgumentLoc, ValueLoc};
|
||||
pub use crate::value_label::LabelValueLoc;
|
||||
pub use cranelift_codegen_shared::condcodes;
|
||||
|
||||
use crate::binemit;
|
||||
|
||||
@@ -2371,6 +2371,9 @@ impl MachInstEmit for Inst {
|
||||
sink.bind_label(jump_around_label);
|
||||
}
|
||||
}
|
||||
&Inst::ValueLabelMarker { .. } => {
|
||||
// Nothing; this is only used to compute debug info.
|
||||
}
|
||||
}
|
||||
|
||||
let end_off = sink.cur_offset();
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::binemit::CodeOffset;
|
||||
use crate::ir::types::{
|
||||
B1, B128, B16, B32, B64, B8, F32, F64, FFLAGS, I128, I16, I32, I64, I8, I8X16, IFLAGS, R32, R64,
|
||||
};
|
||||
use crate::ir::{ExternalName, MemFlags, Opcode, SourceLoc, TrapCode, Type};
|
||||
use crate::ir::{ExternalName, MemFlags, Opcode, SourceLoc, TrapCode, Type, ValueLabel};
|
||||
use crate::isa::CallConv;
|
||||
use crate::machinst::*;
|
||||
use crate::{settings, CodegenError, CodegenResult};
|
||||
@@ -1210,6 +1210,12 @@ pub enum Inst {
|
||||
/// The needed space before the next deadline.
|
||||
needed_space: CodeOffset,
|
||||
},
|
||||
|
||||
/// A definition of a value label.
|
||||
ValueLabelMarker {
|
||||
reg: Reg,
|
||||
label: ValueLabel,
|
||||
},
|
||||
}
|
||||
|
||||
fn count_zero_half_words(mut value: u64, num_half_words: u8) -> usize {
|
||||
@@ -2017,6 +2023,9 @@ fn aarch64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
|
||||
memarg_regs(mem, collector);
|
||||
}
|
||||
&Inst::VirtualSPOffsetAdj { .. } => {}
|
||||
&Inst::ValueLabelMarker { reg, .. } => {
|
||||
collector.add_use(reg);
|
||||
}
|
||||
&Inst::EmitIsland { .. } => {}
|
||||
}
|
||||
}
|
||||
@@ -2767,6 +2776,9 @@ fn aarch64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
|
||||
}
|
||||
&mut Inst::VirtualSPOffsetAdj { .. } => {}
|
||||
&mut Inst::EmitIsland { .. } => {}
|
||||
&mut Inst::ValueLabelMarker { ref mut reg, .. } => {
|
||||
map_use(mapper, reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2962,6 +2974,17 @@ impl MachInst for Inst {
|
||||
fn ref_type_regclass(_: &settings::Flags) -> RegClass {
|
||||
RegClass::I64
|
||||
}
|
||||
|
||||
fn gen_value_label_marker(label: ValueLabel, reg: Reg) -> Self {
|
||||
Inst::ValueLabelMarker { label, reg }
|
||||
}
|
||||
|
||||
fn defines_value_label(&self) -> Option<(ValueLabel, Reg)> {
|
||||
match self {
|
||||
Inst::ValueLabelMarker { label, reg } => Some((*label, *reg)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
@@ -4071,6 +4094,10 @@ impl Inst {
|
||||
format!("virtual_sp_offset_adjust {}", offset)
|
||||
}
|
||||
&Inst::EmitIsland { needed_space } => format!("emit_island {}", needed_space),
|
||||
|
||||
&Inst::ValueLabelMarker { label, reg } => {
|
||||
format!("value_label {:?}, {}", label, reg.show_rru(mb_rru))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ impl MachBackend for AArch64Backend {
|
||||
frame_size,
|
||||
disasm,
|
||||
unwind_info,
|
||||
value_labels_ranges: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,7 @@ impl MachBackend for Arm32Backend {
|
||||
frame_size,
|
||||
disasm,
|
||||
unwind_info: None,
|
||||
value_labels_ranges: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -325,6 +325,12 @@ pub trait TargetIsa: fmt::Display + Send + Sync {
|
||||
Err(RegisterMappingError::UnsupportedArchitecture)
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
/// Map a regalloc::Reg to its corresponding DWARF register.
|
||||
fn map_regalloc_reg_to_dwarf(&self, _: ::regalloc::Reg) -> Result<u16, RegisterMappingError> {
|
||||
Err(RegisterMappingError::UnsupportedArchitecture)
|
||||
}
|
||||
|
||||
/// Returns an iterator over legal encodings for the instruction.
|
||||
fn legal_encodings<'a>(
|
||||
&'a self,
|
||||
|
||||
@@ -3029,6 +3029,10 @@ pub(crate) fn emit(
|
||||
sink.put1(0xff);
|
||||
sink.put1(0x17);
|
||||
}
|
||||
|
||||
Inst::ValueLabelMarker { .. } => {
|
||||
// Nothing; this is only used to compute debug info.
|
||||
}
|
||||
}
|
||||
|
||||
state.clear_post_insn();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! This module defines x86_64-specific machine instruction types.
|
||||
|
||||
use crate::binemit::{CodeOffset, StackMap};
|
||||
use crate::ir::{types, ExternalName, Opcode, SourceLoc, TrapCode, Type};
|
||||
use crate::ir::{types, ExternalName, Opcode, SourceLoc, TrapCode, Type, ValueLabel};
|
||||
use crate::isa::x64::abi::X64ABIMachineSpec;
|
||||
use crate::isa::x64::settings as x64_settings;
|
||||
use crate::isa::CallConv;
|
||||
@@ -484,6 +484,9 @@ pub enum Inst {
|
||||
/// A Mach-O TLS symbol access. Returns address of the TLS
|
||||
/// symbol in rax.
|
||||
MachOTlsGetAddr { symbol: ExternalName },
|
||||
|
||||
/// A definition of a value label.
|
||||
ValueLabelMarker { reg: Reg, label: ValueLabel },
|
||||
}
|
||||
|
||||
pub(crate) fn low32_will_sign_extend_to_64(x: u64) -> bool {
|
||||
@@ -544,7 +547,8 @@ impl Inst {
|
||||
| Inst::XmmMinMaxSeq { .. }
|
||||
| Inst::XmmUninitializedValue { .. }
|
||||
| Inst::ElfTlsGetAddr { .. }
|
||||
| Inst::MachOTlsGetAddr { .. } => None,
|
||||
| Inst::MachOTlsGetAddr { .. }
|
||||
| Inst::ValueLabelMarker { .. } => None,
|
||||
|
||||
// These use dynamic SSE opcodes.
|
||||
Inst::GprToXmm { op, .. }
|
||||
@@ -1800,6 +1804,10 @@ impl PrettyPrint for Inst {
|
||||
Inst::MachOTlsGetAddr { ref symbol } => {
|
||||
format!("macho_tls_get_addr {:?}", symbol)
|
||||
}
|
||||
|
||||
Inst::ValueLabelMarker { label, reg } => {
|
||||
format!("value_label {:?}, {}", label, reg.show_rru(mb_rru))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2071,6 +2079,10 @@ fn x64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) {
|
||||
collector.add_def(reg);
|
||||
}
|
||||
}
|
||||
|
||||
Inst::ValueLabelMarker { reg, .. } => {
|
||||
collector.add_use(*reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2446,6 +2458,8 @@ fn x64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
|
||||
dst.map_uses(mapper);
|
||||
}
|
||||
|
||||
Inst::ValueLabelMarker { ref mut reg, .. } => map_use(mapper, reg),
|
||||
|
||||
Inst::Ret
|
||||
| Inst::EpiloguePlaceholder
|
||||
| Inst::JmpKnown { .. }
|
||||
@@ -2536,6 +2550,25 @@ impl MachInst for Inst {
|
||||
}
|
||||
}
|
||||
|
||||
fn stack_op_info(&self) -> Option<MachInstStackOpInfo> {
|
||||
match self {
|
||||
Self::VirtualSPOffsetAdj { offset } => Some(MachInstStackOpInfo::NomSPAdj(*offset)),
|
||||
Self::MovRM {
|
||||
size: 8,
|
||||
src,
|
||||
dst: SyntheticAmode::NominalSPOffset { simm32 },
|
||||
} => Some(MachInstStackOpInfo::StoreNomSPOff(*src, *simm32 as i64)),
|
||||
Self::Mov64MR {
|
||||
src: SyntheticAmode::NominalSPOffset { simm32 },
|
||||
dst,
|
||||
} => Some(MachInstStackOpInfo::LoadNomSPOff(
|
||||
dst.to_reg(),
|
||||
*simm32 as i64,
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn gen_move(dst_reg: Writable<Reg>, src_reg: Reg, ty: Type) -> Inst {
|
||||
let rc_dst = dst_reg.to_reg().get_class();
|
||||
let rc_src = src_reg.get_class();
|
||||
@@ -2710,6 +2743,17 @@ impl MachInst for Inst {
|
||||
RegClass::I64
|
||||
}
|
||||
|
||||
fn gen_value_label_marker(label: ValueLabel, reg: Reg) -> Self {
|
||||
Inst::ValueLabelMarker { label, reg }
|
||||
}
|
||||
|
||||
fn defines_value_label(&self) -> Option<(ValueLabel, Reg)> {
|
||||
match self {
|
||||
Inst::ValueLabelMarker { label, reg } => Some((*label, *reg)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
type LabelUse = LabelUse;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,13 +4,14 @@ use self::inst::EmitInfo;
|
||||
|
||||
use super::TargetIsa;
|
||||
use crate::ir::{condcodes::IntCC, Function};
|
||||
use crate::isa::unwind::systemv::RegisterMappingError;
|
||||
use crate::isa::x64::{inst::regs::create_reg_universe_systemv, settings as x64_settings};
|
||||
use crate::isa::Builder as IsaBuilder;
|
||||
use crate::machinst::{compile, MachBackend, MachCompileResult, TargetIsaAdapter, VCode};
|
||||
use crate::result::CodegenResult;
|
||||
use crate::settings::{self as shared_settings, Flags};
|
||||
use alloc::boxed::Box;
|
||||
use regalloc::{PrettyPrint, RealRegUniverse};
|
||||
use regalloc::{PrettyPrint, RealRegUniverse, Reg};
|
||||
use target_lexicon::Triple;
|
||||
|
||||
mod abi;
|
||||
@@ -60,6 +61,7 @@ impl MachBackend for X64Backend {
|
||||
let buffer = buffer.finish();
|
||||
let frame_size = vcode.frame_size();
|
||||
let unwind_info = vcode.unwind_info()?;
|
||||
let value_labels_ranges = vcode.value_labels_ranges()?;
|
||||
|
||||
let disasm = if want_disasm {
|
||||
Some(vcode.show_rru(Some(&create_reg_universe_systemv(flags))))
|
||||
@@ -72,6 +74,7 @@ impl MachBackend for X64Backend {
|
||||
frame_size,
|
||||
disasm,
|
||||
unwind_info,
|
||||
value_labels_ranges,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -127,6 +130,11 @@ impl MachBackend for X64Backend {
|
||||
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
||||
Some(inst::unwind::systemv::create_cie())
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
fn map_reg_to_dwarf(&self, reg: Reg) -> Result<u16, RegisterMappingError> {
|
||||
inst::unwind::systemv::map_reg(reg).map(|reg| reg.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new `isa::Builder`.
|
||||
|
||||
@@ -10,6 +10,9 @@ use crate::settings::Flags;
|
||||
#[cfg(feature = "testing_hooks")]
|
||||
use crate::regalloc::RegDiversions;
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
use crate::isa::unwind::systemv::RegisterMappingError;
|
||||
|
||||
use core::any::Any;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt;
|
||||
@@ -134,6 +137,11 @@ impl TargetIsa for TargetIsaAdapter {
|
||||
self.backend.create_systemv_cie()
|
||||
}
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
fn map_regalloc_reg_to_dwarf(&self, r: Reg) -> Result<u16, RegisterMappingError> {
|
||||
self.backend.map_reg_to_dwarf(r)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self as &dyn Any
|
||||
}
|
||||
|
||||
500
cranelift/codegen/src/machinst/debug.rs
Normal file
500
cranelift/codegen/src/machinst/debug.rs
Normal file
@@ -0,0 +1,500 @@
|
||||
//! Debug info analysis: computes value-label ranges from value-label markers in
|
||||
//! generated VCode.
|
||||
//!
|
||||
//! We "reverse-engineer" debug info like this because it is far more reliable
|
||||
//! than generating it while emitting code and keeping it in sync.
|
||||
//!
|
||||
//! This works by (i) observing "value-label marker" instructions, which are
|
||||
//! semantically just an assignment from a register to a "value label" (which
|
||||
//! one can think of as another register; they represent, e.g., Wasm locals) at
|
||||
//! a certain point in the code, and (ii) observing loads and stores to the
|
||||
//! stack and register moves.
|
||||
//!
|
||||
//! We track, at every program point, the correspondence between each value
|
||||
//! label and *all* locations in which it resides. E.g., if it is stored to the
|
||||
//! stack, we remember that it is in both a register and the stack slot; but if
|
||||
//! the register is later overwritten, then we have it just in the stack slot.
|
||||
//! This allows us to avoid false-positives observing loads/stores that we think
|
||||
//! are spillslots but really aren't.
|
||||
//!
|
||||
//! We do a standard forward dataflow analysis to compute this info.
|
||||
|
||||
use crate::ir::ValueLabel;
|
||||
use crate::machinst::*;
|
||||
use crate::value_label::{LabelValueLoc, ValueLabelsRanges, ValueLocRange};
|
||||
use log::trace;
|
||||
use regalloc::{Reg, RegUsageCollector};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::hash::Hash;
|
||||
|
||||
/// Location of a labeled value: in a register or in a stack slot. Note that a
|
||||
/// value may live in more than one location; `AnalysisInfo` maps each
|
||||
/// value-label to multiple `ValueLoc`s.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
enum ValueLoc {
|
||||
Reg(Reg),
|
||||
/// Nominal-SP offset.
|
||||
Stack(i64),
|
||||
}
|
||||
|
||||
impl From<ValueLoc> for LabelValueLoc {
|
||||
fn from(v: ValueLoc) -> Self {
|
||||
match v {
|
||||
ValueLoc::Reg(r) => LabelValueLoc::Reg(r),
|
||||
ValueLoc::Stack(off) => LabelValueLoc::SPOffset(off),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueLoc {
|
||||
fn is_reg(self) -> bool {
|
||||
match self {
|
||||
ValueLoc::Reg(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
fn is_stack(self) -> bool {
|
||||
match self {
|
||||
ValueLoc::Stack(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Mappings at one program point.
|
||||
#[derive(Clone, Debug)]
|
||||
struct AnalysisInfo {
|
||||
/// Nominal SP relative to real SP. If `None`, then the offset is
|
||||
/// indeterminate (i.e., we merged to the lattice 'bottom' element). This
|
||||
/// should not happen in well-formed code.
|
||||
nominal_sp_offset: Option<i64>,
|
||||
/// Forward map from labeled values to sets of locations.
|
||||
label_to_locs: HashMap<ValueLabel, HashSet<ValueLoc>>,
|
||||
/// Reverse map for each register indicating the value it holds, if any.
|
||||
reg_to_label: HashMap<Reg, ValueLabel>,
|
||||
/// Reverse map for each stack offset indicating the value it holds, if any.
|
||||
stack_to_label: HashMap<i64, ValueLabel>,
|
||||
}
|
||||
|
||||
/// Get the registers written (mod'd or def'd) by a machine instruction.
|
||||
fn get_inst_writes<M: MachInst>(m: &M) -> Vec<Reg> {
|
||||
// TODO: expose this part of regalloc.rs's interface publicly.
|
||||
let mut vecs = RegUsageCollector::get_empty_reg_vecs_test_framework_only(false);
|
||||
let mut coll = RegUsageCollector::new(&mut vecs);
|
||||
m.get_regs(&mut coll);
|
||||
vecs.defs.extend(vecs.mods.into_iter());
|
||||
vecs.defs
|
||||
}
|
||||
|
||||
impl AnalysisInfo {
|
||||
/// Create a new analysis state. This is the "top" lattice element at which
|
||||
/// the fixpoint dataflow analysis starts.
|
||||
fn new() -> Self {
|
||||
AnalysisInfo {
|
||||
nominal_sp_offset: Some(0),
|
||||
label_to_locs: HashMap::new(),
|
||||
reg_to_label: HashMap::new(),
|
||||
stack_to_label: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove all locations for a given labeled value. Used when the labeled
|
||||
/// value is redefined (so old values become stale).
|
||||
fn clear_label(&mut self, label: ValueLabel) {
|
||||
if let Some(locs) = self.label_to_locs.remove(&label) {
|
||||
for loc in locs {
|
||||
match loc {
|
||||
ValueLoc::Reg(r) => {
|
||||
self.reg_to_label.remove(&r);
|
||||
}
|
||||
ValueLoc::Stack(off) => {
|
||||
self.stack_to_label.remove(&off);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a label from a register, if any. Used, e.g., if the register is
|
||||
/// overwritten.
|
||||
fn clear_reg(&mut self, reg: Reg) {
|
||||
if let Some(label) = self.reg_to_label.remove(®) {
|
||||
if let Some(locs) = self.label_to_locs.get_mut(&label) {
|
||||
locs.remove(&ValueLoc::Reg(reg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a label from a stack offset, if any. Used, e.g., when the stack
|
||||
/// slot is overwritten.
|
||||
fn clear_stack_off(&mut self, off: i64) {
|
||||
if let Some(label) = self.stack_to_label.remove(&off) {
|
||||
if let Some(locs) = self.label_to_locs.get_mut(&label) {
|
||||
locs.remove(&ValueLoc::Stack(off));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Indicate that a labeled value is newly defined and its new value is in
|
||||
/// `reg`.
|
||||
fn def_label_at_reg(&mut self, label: ValueLabel, reg: Reg) {
|
||||
self.clear_label(label);
|
||||
self.label_to_locs
|
||||
.entry(label)
|
||||
.or_insert_with(|| HashSet::new())
|
||||
.insert(ValueLoc::Reg(reg));
|
||||
self.reg_to_label.insert(reg, label);
|
||||
}
|
||||
|
||||
/// Process a store from a register to a stack slot (offset).
|
||||
fn store_reg(&mut self, reg: Reg, off: i64) {
|
||||
self.clear_stack_off(off);
|
||||
if let Some(label) = self.reg_to_label.get(®) {
|
||||
if let Some(locs) = self.label_to_locs.get_mut(label) {
|
||||
locs.insert(ValueLoc::Stack(off));
|
||||
}
|
||||
self.stack_to_label.insert(off, *label);
|
||||
}
|
||||
}
|
||||
|
||||
/// Process a load from a stack slot (offset) to a register.
|
||||
fn load_reg(&mut self, reg: Reg, off: i64) {
|
||||
self.clear_reg(reg);
|
||||
if let Some(&label) = self.stack_to_label.get(&off) {
|
||||
if let Some(locs) = self.label_to_locs.get_mut(&label) {
|
||||
locs.insert(ValueLoc::Reg(reg));
|
||||
}
|
||||
self.reg_to_label.insert(reg, label);
|
||||
}
|
||||
}
|
||||
|
||||
/// Process a move from one register to another.
|
||||
fn move_reg(&mut self, to: Reg, from: Reg) {
|
||||
self.clear_reg(to);
|
||||
if let Some(&label) = self.reg_to_label.get(&from) {
|
||||
if let Some(locs) = self.label_to_locs.get_mut(&label) {
|
||||
locs.insert(ValueLoc::Reg(to));
|
||||
}
|
||||
self.reg_to_label.insert(to, label);
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the analysis state w.r.t. an instruction's effects. Given the
|
||||
/// state just before `inst`, this method updates `self` to be the state
|
||||
/// just after `inst`.
|
||||
fn step<M: MachInst>(&mut self, inst: &M) {
|
||||
for write in get_inst_writes(inst) {
|
||||
self.clear_reg(write);
|
||||
}
|
||||
if let Some((label, reg)) = inst.defines_value_label() {
|
||||
self.def_label_at_reg(label, reg);
|
||||
}
|
||||
match inst.stack_op_info() {
|
||||
Some(MachInstStackOpInfo::LoadNomSPOff(reg, offset)) => {
|
||||
self.load_reg(reg, offset + self.nominal_sp_offset.unwrap());
|
||||
}
|
||||
Some(MachInstStackOpInfo::StoreNomSPOff(reg, offset)) => {
|
||||
self.store_reg(reg, offset + self.nominal_sp_offset.unwrap());
|
||||
}
|
||||
Some(MachInstStackOpInfo::NomSPAdj(offset)) => {
|
||||
if self.nominal_sp_offset.is_some() {
|
||||
self.nominal_sp_offset = Some(self.nominal_sp_offset.unwrap() + offset);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
if let Some((to, from)) = inst.is_move() {
|
||||
let to = to.to_reg();
|
||||
self.move_reg(to, from);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait used to implement the dataflow analysis' meet (intersect) function
|
||||
/// onthe `AnalysisInfo` components. For efficiency, this is implemented as a
|
||||
/// mutation on the LHS, rather than a pure functional operation.
|
||||
trait IntersectFrom {
|
||||
fn intersect_from(&mut self, other: &Self) -> IntersectResult;
|
||||
}
|
||||
|
||||
/// Result of an intersection operation. Indicates whether the mutated LHS
|
||||
/// (which becomes the intersection result) differs from the original LHS. Also
|
||||
/// indicates if the value has become "empty" and should be removed from a
|
||||
/// parent container, if any.
|
||||
struct IntersectResult {
|
||||
/// Did the intersection change the LHS input (the one that was mutated into
|
||||
/// the result)? This is needed to drive the fixpoint loop; when no more
|
||||
/// changes occur, then we have converted.
|
||||
changed: bool,
|
||||
/// Is the resulting value "empty"? This can be used when a container, such
|
||||
/// as a map, holds values of this (intersection result) type; when
|
||||
/// `is_empty` is true for the merge of the values at a particular key, we
|
||||
/// can remove that key from the merged (intersected) result. This is not
|
||||
/// necessary for analysis correctness but reduces the memory and runtime
|
||||
/// cost of the fixpoint loop.
|
||||
is_empty: bool,
|
||||
}
|
||||
|
||||
impl IntersectFrom for AnalysisInfo {
|
||||
fn intersect_from(&mut self, other: &Self) -> IntersectResult {
|
||||
let mut changed = false;
|
||||
changed |= self
|
||||
.nominal_sp_offset
|
||||
.intersect_from(&other.nominal_sp_offset)
|
||||
.changed;
|
||||
changed |= self
|
||||
.label_to_locs
|
||||
.intersect_from(&other.label_to_locs)
|
||||
.changed;
|
||||
changed |= self
|
||||
.reg_to_label
|
||||
.intersect_from(&other.reg_to_label)
|
||||
.changed;
|
||||
changed |= self
|
||||
.stack_to_label
|
||||
.intersect_from(&other.stack_to_label)
|
||||
.changed;
|
||||
IntersectResult {
|
||||
changed,
|
||||
is_empty: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<K, V> IntersectFrom for HashMap<K, V>
|
||||
where
|
||||
K: Copy + Eq + Hash,
|
||||
V: IntersectFrom,
|
||||
{
|
||||
/// Intersection for hashmap: remove keys that are not in both inputs;
|
||||
/// recursively intersect values for keys in common.
|
||||
fn intersect_from(&mut self, other: &Self) -> IntersectResult {
|
||||
let mut changed = false;
|
||||
let mut remove_keys = vec![];
|
||||
for k in self.keys() {
|
||||
if !other.contains_key(k) {
|
||||
remove_keys.push(*k);
|
||||
}
|
||||
}
|
||||
for k in &remove_keys {
|
||||
changed = true;
|
||||
self.remove(k);
|
||||
}
|
||||
|
||||
remove_keys.clear();
|
||||
for k in other.keys() {
|
||||
if let Some(v) = self.get_mut(k) {
|
||||
let result = v.intersect_from(other.get(k).unwrap());
|
||||
changed |= result.changed;
|
||||
if result.is_empty {
|
||||
remove_keys.push(*k);
|
||||
}
|
||||
}
|
||||
}
|
||||
for k in &remove_keys {
|
||||
changed = true;
|
||||
self.remove(k);
|
||||
}
|
||||
|
||||
IntersectResult {
|
||||
changed,
|
||||
is_empty: self.len() == 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T> IntersectFrom for HashSet<T>
|
||||
where
|
||||
T: Copy + Eq + Hash,
|
||||
{
|
||||
/// Intersection for hashset: just take the set intersection.
|
||||
fn intersect_from(&mut self, other: &Self) -> IntersectResult {
|
||||
let mut changed = false;
|
||||
let mut remove = vec![];
|
||||
for val in self.iter() {
|
||||
if !other.contains(val) {
|
||||
remove.push(*val);
|
||||
}
|
||||
}
|
||||
for val in remove {
|
||||
changed = true;
|
||||
self.remove(&val);
|
||||
}
|
||||
|
||||
IntersectResult {
|
||||
changed,
|
||||
is_empty: self.len() == 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl IntersectFrom for ValueLabel {
|
||||
// Intersection for labeled value: remove if not equal. This is equivalent
|
||||
// to a three-level lattice with top, bottom, and unordered set of
|
||||
// individual labels in between.
|
||||
fn intersect_from(&mut self, other: &Self) -> IntersectResult {
|
||||
IntersectResult {
|
||||
changed: false,
|
||||
is_empty: *self != *other,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T> IntersectFrom for Option<T>
|
||||
where
|
||||
T: Copy + Eq,
|
||||
{
|
||||
/// Intersectino for Option<T>: recursively intersect if both `Some`, else
|
||||
/// `None`.
|
||||
fn intersect_from(&mut self, other: &Self) -> IntersectResult {
|
||||
let mut changed = false;
|
||||
if !(self.is_some() && other.is_some() && self == other) {
|
||||
changed = true;
|
||||
*self = None;
|
||||
}
|
||||
IntersectResult {
|
||||
changed,
|
||||
is_empty: self.is_none(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Compute the value-label ranges (locations for program-point ranges for
|
||||
/// labeled values) from a given `VCode` compilation result.
|
||||
///
|
||||
/// In order to compute this information, we perform a dataflow analysis on the
|
||||
/// machine code. To do so, and translate the results into a form usable by the
|
||||
/// debug-info consumers, we need to know two additional things:
|
||||
///
|
||||
/// - The machine-code layout (code offsets) of the instructions. DWARF is
|
||||
/// encoded in terms of instruction *ends* (and we reason about value
|
||||
/// locations at program points *after* instructions, to match this), so we
|
||||
/// take an array `inst_ends`, giving us code offsets for each instruction's
|
||||
/// end-point. (Note that this is one *past* the last byte; so a 4-byte
|
||||
/// instruction at offset 0 has an end offset of 4.)
|
||||
///
|
||||
/// - The locations of the labels to which branches will jump. Branches can tell
|
||||
/// us about their targets in terms of `MachLabel`s, but we don't know where
|
||||
/// those `MachLabel`s will be placed in the linear array of instructions. We
|
||||
/// take the array `label_insn_index` to provide this info: for a label with
|
||||
/// index `l`, `label_insn_index[l]` is the index of the instruction before
|
||||
/// which that label is bound.
|
||||
pub(crate) fn compute<I: VCodeInst>(
|
||||
insts: &[I],
|
||||
inst_ends: &[u32],
|
||||
label_insn_index: &[u32],
|
||||
) -> ValueLabelsRanges {
|
||||
let inst_start = |idx: usize| if idx == 0 { 0 } else { inst_ends[idx - 1] };
|
||||
|
||||
trace!("compute: insts =");
|
||||
for i in 0..insts.len() {
|
||||
trace!(" #{} end: {} -> {:?}", i, inst_ends[i], insts[i]);
|
||||
}
|
||||
trace!("label_insn_index: {:?}", label_insn_index);
|
||||
|
||||
// Info at each block head, indexed by label.
|
||||
let mut block_starts: HashMap<u32, AnalysisInfo> = HashMap::new();
|
||||
|
||||
// Initialize state at entry.
|
||||
block_starts.insert(0, AnalysisInfo::new());
|
||||
|
||||
// Worklist: label indices for basic blocks.
|
||||
let mut worklist = Vec::new();
|
||||
let mut worklist_set = HashSet::new();
|
||||
worklist.push(0);
|
||||
worklist_set.insert(0);
|
||||
|
||||
while !worklist.is_empty() {
|
||||
let block = worklist.pop().unwrap();
|
||||
worklist_set.remove(&block);
|
||||
|
||||
let mut state = block_starts.get(&block).unwrap().clone();
|
||||
trace!("at block {} -> state: {:?}", block, state);
|
||||
// Iterate for each instruction in the block (we break at the first
|
||||
// terminator we see).
|
||||
let mut index = label_insn_index[block as usize];
|
||||
while index < insts.len() as u32 {
|
||||
state.step(&insts[index as usize]);
|
||||
trace!(" -> inst #{}: {:?}", index, insts[index as usize]);
|
||||
trace!(" --> state: {:?}", state);
|
||||
|
||||
let term = insts[index as usize].is_term();
|
||||
if term.is_term() {
|
||||
for succ in term.get_succs() {
|
||||
trace!(" SUCCESSOR block {}", succ.get());
|
||||
if let Some(succ_state) = block_starts.get_mut(&succ.get()) {
|
||||
trace!(" orig state: {:?}", succ_state);
|
||||
if succ_state.intersect_from(&state).changed {
|
||||
if worklist_set.insert(succ.get()) {
|
||||
worklist.push(succ.get());
|
||||
}
|
||||
trace!(" (changed)");
|
||||
}
|
||||
trace!(" new state: {:?}", succ_state);
|
||||
} else {
|
||||
// First time seeing this block
|
||||
block_starts.insert(succ.get(), state.clone());
|
||||
worklist.push(succ.get());
|
||||
worklist_set.insert(succ.get());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Now iterate over blocks one last time, collecting
|
||||
// value-label locations.
|
||||
|
||||
let mut value_labels_ranges: ValueLabelsRanges = HashMap::new();
|
||||
for block in 0..label_insn_index.len() {
|
||||
let start_index = label_insn_index[block];
|
||||
let end_index = if block == label_insn_index.len() - 1 {
|
||||
insts.len() as u32
|
||||
} else {
|
||||
label_insn_index[block + 1]
|
||||
};
|
||||
let block = block as u32;
|
||||
let mut state = block_starts.get(&block).unwrap().clone();
|
||||
for index in start_index..end_index {
|
||||
let offset = inst_start(index as usize);
|
||||
let end = inst_ends[index as usize];
|
||||
state.step(&insts[index as usize]);
|
||||
|
||||
for (label, locs) in &state.label_to_locs {
|
||||
trace!(" inst {} has label {:?} -> locs {:?}", index, label, locs);
|
||||
// Find an appropriate loc: a register if possible, otherwise pick the first stack
|
||||
// loc.
|
||||
let reg = locs.iter().cloned().find(|l| l.is_reg());
|
||||
let loc = reg.or_else(|| locs.iter().cloned().find(|l| l.is_stack()));
|
||||
if let Some(loc) = loc {
|
||||
let loc = LabelValueLoc::from(loc);
|
||||
let list = value_labels_ranges.entry(*label).or_insert_with(|| vec![]);
|
||||
// If the existing location list for this value-label is
|
||||
// either empty, or has an end location that does not extend
|
||||
// to the current offset, then we have to append a new
|
||||
// entry. Otherwise, we can extend the current entry.
|
||||
//
|
||||
// Note that `end` is one past the end of the instruction;
|
||||
// it appears that `end` is exclusive, so a mapping valid at
|
||||
// offset 5 will have start = 5, end = 6.
|
||||
if list
|
||||
.last()
|
||||
.map(|last| last.end <= offset || last.loc != loc)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
list.push(ValueLocRange {
|
||||
loc,
|
||||
start: end,
|
||||
end: end + 1,
|
||||
});
|
||||
} else {
|
||||
list.last_mut().unwrap().end = end + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trace!("ret: {:?}", value_labels_ranges);
|
||||
value_labels_ranges
|
||||
}
|
||||
@@ -13,6 +13,7 @@ use crate::ir::instructions::BranchInfo;
|
||||
use crate::ir::{
|
||||
ArgumentPurpose, Block, Constant, ConstantData, ExternalName, Function, GlobalValueData, Inst,
|
||||
InstructionData, MemFlags, Opcode, Signature, SourceLoc, Type, Value, ValueDef,
|
||||
ValueLabelAssignments, ValueLabelStart,
|
||||
};
|
||||
use crate::machinst::{
|
||||
writable_value_regs, ABICallee, BlockIndex, BlockLoweringOrder, LoweredBlock, MachLabel, VCode,
|
||||
@@ -24,7 +25,7 @@ use alloc::vec::Vec;
|
||||
use core::convert::TryInto;
|
||||
use log::debug;
|
||||
use regalloc::{Reg, StackmapRequestInfo, Writable};
|
||||
use smallvec::SmallVec;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::fmt::Debug;
|
||||
|
||||
/// An "instruction color" partitions CLIF instructions by side-effecting ops.
|
||||
@@ -492,6 +493,14 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
||||
}
|
||||
|
||||
fn gen_retval_setup(&mut self, gen_ret_inst: GenerateReturn) {
|
||||
// Hack: to keep `vmctx` alive, if it exists, we emit a value label here
|
||||
// for it if debug info is requested. This ensures that it exists either
|
||||
// in a register or spillslot throughout the entire function body, and
|
||||
// allows for a better debugging experience.
|
||||
if let Some(vmctx_val) = self.f.special_param(ArgumentPurpose::VMContext) {
|
||||
self.emit_value_label_marks_for_value(vmctx_val);
|
||||
}
|
||||
|
||||
let retval_regs = self.retval_regs.clone();
|
||||
for (i, regs) in retval_regs.into_iter().enumerate() {
|
||||
let regs = writable_value_regs(regs);
|
||||
@@ -725,6 +734,9 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
||||
if has_side_effect || value_needed {
|
||||
debug!("lowering: inst {}: {:?}", inst, self.f.dfg[inst]);
|
||||
backend.lower(self, inst)?;
|
||||
// Emit value-label markers if needed, to later recover debug
|
||||
// mappings.
|
||||
self.emit_value_label_markers_for_inst(inst);
|
||||
}
|
||||
if data.opcode().is_return() {
|
||||
// Return: handle specially, using ABI-appropriate sequence.
|
||||
@@ -744,6 +756,80 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_value_labels<'a>(&'a self, val: Value, depth: usize) -> Option<&'a [ValueLabelStart]> {
|
||||
if let Some(ref values_labels) = self.f.dfg.values_labels {
|
||||
debug!(
|
||||
"get_value_labels: val {} -> {} -> {:?}",
|
||||
val,
|
||||
self.f.dfg.resolve_aliases(val),
|
||||
values_labels.get(&self.f.dfg.resolve_aliases(val))
|
||||
);
|
||||
let val = self.f.dfg.resolve_aliases(val);
|
||||
match values_labels.get(&val) {
|
||||
Some(&ValueLabelAssignments::Starts(ref list)) => Some(&list[..]),
|
||||
Some(&ValueLabelAssignments::Alias { value, .. }) if depth < 10 => {
|
||||
self.get_value_labels(value, depth + 1)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_value_label_marks_for_value(&mut self, val: Value) {
|
||||
let mut markers: SmallVec<[I; 4]> = smallvec![];
|
||||
let regs = self.value_regs[val];
|
||||
if regs.len() > 1 {
|
||||
return;
|
||||
}
|
||||
let reg = regs.only_reg().unwrap();
|
||||
|
||||
if let Some(label_starts) = self.get_value_labels(val, 0) {
|
||||
let labels = label_starts
|
||||
.iter()
|
||||
.map(|&ValueLabelStart { label, .. }| label)
|
||||
.collect::<FxHashSet<_>>();
|
||||
for label in labels {
|
||||
debug!(
|
||||
"value labeling: defines val {:?} -> reg {:?} -> label {:?}",
|
||||
val, reg, label,
|
||||
);
|
||||
markers.push(I::gen_value_label_marker(label, reg));
|
||||
}
|
||||
}
|
||||
for marker in markers {
|
||||
self.emit(marker);
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_value_label_markers_for_inst(&mut self, inst: Inst) {
|
||||
if self.f.dfg.values_labels.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
debug!(
|
||||
"value labeling: srcloc {}: inst {}",
|
||||
self.srcloc(inst),
|
||||
inst
|
||||
);
|
||||
for &val in self.f.dfg.inst_results(inst) {
|
||||
self.emit_value_label_marks_for_value(val);
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_value_label_markers_for_block_args(&mut self, block: Block) {
|
||||
if self.f.dfg.values_labels.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
debug!("value labeling: block {}", block);
|
||||
for &arg in self.f.dfg.block_params(block) {
|
||||
self.emit_value_label_marks_for_value(arg);
|
||||
}
|
||||
self.finish_ir_inst(SourceLoc::default());
|
||||
}
|
||||
|
||||
fn finish_ir_inst(&mut self, loc: SourceLoc) {
|
||||
// `bb_insts` is kept in reverse order, so emit the instructions in
|
||||
// reverse order.
|
||||
@@ -885,6 +971,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
||||
// Original block body.
|
||||
if let Some(bb) = lb.orig_block() {
|
||||
self.lower_clif_block(backend, bb)?;
|
||||
self.emit_value_label_markers_for_block_args(bb);
|
||||
}
|
||||
// In-edge phi moves.
|
||||
if let Some((pred, inst, succ)) = lb.in_edge() {
|
||||
|
||||
@@ -52,45 +52,9 @@
|
||||
//! | - 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]
|
||||
//! | [binary emission via MachBuffer
|
||||
//! | with streaming branch resolution/simplification]
|
||||
//! |
|
||||
//! Vec<u8> (machine code!)
|
||||
//!
|
||||
@@ -98,11 +62,11 @@
|
||||
|
||||
use crate::binemit::{CodeInfo, CodeOffset, StackMap};
|
||||
use crate::ir::condcodes::IntCC;
|
||||
use crate::ir::{Function, SourceLoc, Type};
|
||||
use crate::ir::{Function, SourceLoc, Type, ValueLabel};
|
||||
use crate::isa::unwind::input as unwind_input;
|
||||
use crate::result::CodegenResult;
|
||||
use crate::settings::Flags;
|
||||
|
||||
use crate::value_label::ValueLabelsRanges;
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use core::fmt::Debug;
|
||||
@@ -111,10 +75,13 @@ use regalloc::RegUsageCollector;
|
||||
use regalloc::{
|
||||
RealReg, RealRegUniverse, Reg, RegClass, RegUsageMapper, SpillSlot, VirtualReg, Writable,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::string::String;
|
||||
use target_lexicon::Triple;
|
||||
|
||||
#[cfg(feature = "unwind")]
|
||||
use crate::isa::unwind::systemv::RegisterMappingError;
|
||||
|
||||
pub mod lower;
|
||||
pub use lower::*;
|
||||
pub mod vcode;
|
||||
@@ -137,6 +104,7 @@ pub mod inst_common;
|
||||
pub use inst_common::*;
|
||||
pub mod valueregs;
|
||||
pub use valueregs::*;
|
||||
pub mod debug;
|
||||
|
||||
/// A machine instruction.
|
||||
pub trait MachInst: Clone + Debug {
|
||||
@@ -163,6 +131,11 @@ pub trait MachInst: Clone + Debug {
|
||||
true
|
||||
}
|
||||
|
||||
/// If this is a load or store to the stack, return that info.
|
||||
fn stack_op_info(&self) -> Option<MachInstStackOpInfo> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Generate a move.
|
||||
fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Self;
|
||||
|
||||
@@ -223,6 +196,17 @@ pub trait MachInst: Clone + Debug {
|
||||
/// be dependent on compilation flags.
|
||||
fn ref_type_regclass(_flags: &Flags) -> RegClass;
|
||||
|
||||
/// Does this instruction define a ValueLabel? Returns the `Reg` whose value
|
||||
/// becomes the new value of the `ValueLabel` after this instruction.
|
||||
fn defines_value_label(&self) -> Option<(ValueLabel, Reg)> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Create a marker instruction that defines a value label.
|
||||
fn gen_value_label_marker(_label: ValueLabel, _reg: Reg) -> Self {
|
||||
Self::gen_nop(0)
|
||||
}
|
||||
|
||||
/// A label-use kind: a type that describes the types of label references that
|
||||
/// can occur in an instruction.
|
||||
type LabelUse: MachInstLabelUse;
|
||||
@@ -285,6 +269,35 @@ pub enum MachTerminator<'a> {
|
||||
Indirect(&'a [MachLabel]),
|
||||
}
|
||||
|
||||
impl<'a> MachTerminator<'a> {
|
||||
/// Get the successor labels named in a `MachTerminator`.
|
||||
pub fn get_succs(&self) -> SmallVec<[MachLabel; 2]> {
|
||||
let mut ret = smallvec![];
|
||||
match self {
|
||||
&MachTerminator::Uncond(l) => {
|
||||
ret.push(l);
|
||||
}
|
||||
&MachTerminator::Cond(l1, l2) => {
|
||||
ret.push(l1);
|
||||
ret.push(l2);
|
||||
}
|
||||
&MachTerminator::Indirect(ls) => {
|
||||
ret.extend(ls.iter().cloned());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
/// Is this a terminator?
|
||||
pub fn is_term(&self) -> bool {
|
||||
match self {
|
||||
MachTerminator::None => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait describing the ability to encode a MachInst into binary machine code.
|
||||
pub trait MachInstEmit: MachInst {
|
||||
/// Persistent state carried across `emit` invocations.
|
||||
@@ -330,6 +343,8 @@ pub struct MachCompileResult {
|
||||
pub disasm: Option<String>,
|
||||
/// Unwind info.
|
||||
pub unwind_info: Option<unwind_input::UnwindInfo<Reg>>,
|
||||
/// Debug info: value labels to registers/stackslots at code offsets.
|
||||
pub value_labels_ranges: Option<ValueLabelsRanges>,
|
||||
}
|
||||
|
||||
impl MachCompileResult {
|
||||
@@ -386,13 +401,17 @@ pub trait MachBackend {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
/// Machine-specific condcode info needed by TargetIsa.
|
||||
/// Creates a new System V Common Information Entry for the ISA.
|
||||
#[cfg(feature = "unwind")]
|
||||
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
||||
// By default, an ISA cannot create a System V CIE
|
||||
None
|
||||
}
|
||||
/// Maps a regalloc::Reg to a DWARF register number.
|
||||
#[cfg(feature = "unwind")]
|
||||
fn map_reg_to_dwarf(&self, _: Reg) -> Result<u16, RegisterMappingError> {
|
||||
Err(RegisterMappingError::UnsupportedArchitecture)
|
||||
}
|
||||
}
|
||||
|
||||
/// Expected unwind info type.
|
||||
@@ -431,3 +450,15 @@ pub trait UnwindInfoGenerator<I: MachInstEmit> {
|
||||
context: UnwindInfoContext<I>,
|
||||
) -> CodegenResult<Option<unwind_input::UnwindInfo<Reg>>>;
|
||||
}
|
||||
|
||||
/// Info about an operation that loads or stores from/to the stack.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum MachInstStackOpInfo {
|
||||
/// Load from an offset from the nominal stack pointer into the given reg.
|
||||
LoadNomSPOff(Reg, i64),
|
||||
/// Store to an offset from the nominal stack pointer from the given reg.
|
||||
StoreNomSPOff(Reg, i64),
|
||||
/// Adjustment of nominal-SP up or down. This value is added to subsequent
|
||||
/// offsets in loads/stores above to produce real-SP offsets.
|
||||
NomSPAdj(i64),
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ use crate::ir::{self, types, Constant, ConstantData, SourceLoc};
|
||||
use crate::machinst::*;
|
||||
use crate::settings;
|
||||
use crate::timing;
|
||||
|
||||
use regalloc::Function as RegallocFunction;
|
||||
use regalloc::Set as RegallocSet;
|
||||
use regalloc::{
|
||||
@@ -110,11 +109,19 @@ pub struct VCode<I: VCodeInst> {
|
||||
/// Ranges for prologue and epilogue instructions.
|
||||
prologue_epilogue_ranges: Option<(InsnRange, Box<[InsnRange]>)>,
|
||||
|
||||
/// Instruction end offsets
|
||||
insts_layout: RefCell<(Vec<u32>, u32)>,
|
||||
/// Do we generate debug info?
|
||||
generate_debug_info: bool,
|
||||
|
||||
/// Instruction end offsets, instruction indices at each label, and total
|
||||
/// buffer size. Only present if `generate_debug_info` is set.
|
||||
insts_layout: RefCell<(Vec<u32>, Vec<u32>, u32)>,
|
||||
|
||||
/// Constants.
|
||||
constants: VCodeConstants,
|
||||
|
||||
/// Are any debug value-labels present? If not, we can skip the
|
||||
/// post-emission analysis.
|
||||
has_value_labels: bool,
|
||||
}
|
||||
|
||||
/// A builder for a VCode function body. This builder is designed for the
|
||||
@@ -157,7 +164,13 @@ impl<I: VCodeInst> VCodeBuilder<I> {
|
||||
constants: VCodeConstants,
|
||||
) -> VCodeBuilder<I> {
|
||||
let reftype_class = I::ref_type_regclass(abi.flags());
|
||||
let vcode = VCode::new(abi, emit_info, block_order, constants);
|
||||
let vcode = VCode::new(
|
||||
abi,
|
||||
emit_info,
|
||||
block_order,
|
||||
constants,
|
||||
/* generate_debug_info = */ true,
|
||||
);
|
||||
let stack_map_info = StackmapRequestInfo {
|
||||
reftype_class,
|
||||
reftyped_vregs: vec![],
|
||||
@@ -242,6 +255,9 @@ impl<I: VCodeInst> VCodeBuilder<I> {
|
||||
}
|
||||
}
|
||||
}
|
||||
if insn.defines_value_label().is_some() {
|
||||
self.vcode.has_value_labels = true;
|
||||
}
|
||||
self.vcode.insts.push(insn);
|
||||
self.vcode.srclocs.push(self.cur_srcloc);
|
||||
if is_safepoint {
|
||||
@@ -296,6 +312,7 @@ impl<I: VCodeInst> VCode<I> {
|
||||
emit_info: I::Info,
|
||||
block_order: BlockLoweringOrder,
|
||||
constants: VCodeConstants,
|
||||
generate_debug_info: bool,
|
||||
) -> VCode<I> {
|
||||
VCode {
|
||||
liveins: abi.liveins(),
|
||||
@@ -314,8 +331,10 @@ impl<I: VCodeInst> VCode<I> {
|
||||
safepoint_insns: vec![],
|
||||
safepoint_slots: vec![],
|
||||
prologue_epilogue_ranges: None,
|
||||
insts_layout: RefCell::new((vec![], 0)),
|
||||
generate_debug_info,
|
||||
insts_layout: RefCell::new((vec![], vec![], 0)),
|
||||
constants,
|
||||
has_value_labels: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -484,7 +503,8 @@ impl<I: VCodeInst> VCode<I> {
|
||||
buffer.reserve_labels_for_blocks(self.num_blocks() as BlockIndex);
|
||||
buffer.reserve_labels_for_constants(&self.constants);
|
||||
|
||||
let mut insts_layout = vec![0; self.insts.len()];
|
||||
let mut inst_ends = vec![0; self.insts.len()];
|
||||
let mut label_insn_iix = vec![0; self.num_blocks()];
|
||||
|
||||
let mut safepoint_idx = 0;
|
||||
let mut cur_srcloc = None;
|
||||
@@ -500,6 +520,7 @@ impl<I: VCodeInst> VCode<I> {
|
||||
|
||||
let (start, end) = self.block_ranges[block as usize];
|
||||
buffer.bind_label(MachLabel::from_block(block));
|
||||
label_insn_iix[block as usize] = start;
|
||||
for iix in start..end {
|
||||
let srcloc = self.srclocs[iix as usize];
|
||||
if cur_srcloc != Some(srcloc) {
|
||||
@@ -526,7 +547,19 @@ impl<I: VCodeInst> VCode<I> {
|
||||
|
||||
self.insts[iix as usize].emit(&mut buffer, &self.emit_info, &mut state);
|
||||
|
||||
insts_layout[iix as usize] = buffer.cur_offset();
|
||||
if self.generate_debug_info {
|
||||
// Buffer truncation may have happened since last inst append; trim inst-end
|
||||
// layout info as appropriate.
|
||||
let l = &mut inst_ends[0..iix as usize];
|
||||
for end in l.iter_mut().rev() {
|
||||
if *end > buffer.cur_offset() {
|
||||
*end = buffer.cur_offset();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
inst_ends[iix as usize] = buffer.cur_offset();
|
||||
}
|
||||
}
|
||||
|
||||
if cur_srcloc.is_some() {
|
||||
@@ -553,7 +586,16 @@ impl<I: VCodeInst> VCode<I> {
|
||||
buffer.defer_constant(label, data.alignment(), data.as_slice(), u32::max_value());
|
||||
}
|
||||
|
||||
*self.insts_layout.borrow_mut() = (insts_layout, buffer.cur_offset());
|
||||
if self.generate_debug_info {
|
||||
for end in inst_ends.iter_mut().rev() {
|
||||
if *end > buffer.cur_offset() {
|
||||
*end = buffer.cur_offset();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*self.insts_layout.borrow_mut() = (inst_ends, label_insn_iix, buffer.cur_offset());
|
||||
}
|
||||
|
||||
buffer
|
||||
}
|
||||
@@ -567,13 +609,27 @@ impl<I: VCodeInst> VCode<I> {
|
||||
let context = UnwindInfoContext {
|
||||
insts: &self.insts,
|
||||
insts_layout: &layout.0,
|
||||
len: layout.1,
|
||||
len: layout.2,
|
||||
prologue: prologue.clone(),
|
||||
epilogues,
|
||||
};
|
||||
I::UnwindInfo::create_unwind_info(context)
|
||||
}
|
||||
|
||||
/// Generates value-label ranges.
|
||||
pub fn value_labels_ranges(&self) -> crate::result::CodegenResult<Option<ValueLabelsRanges>> {
|
||||
if !self.has_value_labels {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let layout = &self.insts_layout.borrow();
|
||||
Ok(Some(debug::compute(
|
||||
&self.insts,
|
||||
&layout.0[..],
|
||||
&layout.1[..],
|
||||
)))
|
||||
}
|
||||
|
||||
/// Get the IR block for a BlockIndex, if one exists.
|
||||
pub fn bindex_to_bb(&self, block: BlockIndex) -> Option<ir::Block> {
|
||||
self.block_order.lowered_order()[block as usize].orig_block()
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
use crate::ir::{Function, SourceLoc, Value, ValueLabel, ValueLabelAssignments, ValueLoc};
|
||||
use crate::isa::TargetIsa;
|
||||
use crate::machinst::MachCompileResult;
|
||||
use crate::regalloc::{Context, RegDiversions};
|
||||
use crate::HashMap;
|
||||
use alloc::collections::BTreeMap;
|
||||
use alloc::vec::Vec;
|
||||
use core::cmp::Ordering;
|
||||
use core::convert::From;
|
||||
use core::iter::Iterator;
|
||||
use core::ops::Bound::*;
|
||||
use core::ops::Deref;
|
||||
use regalloc::Reg;
|
||||
|
||||
#[cfg(feature = "enable-serde")]
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -17,13 +20,31 @@ use serde::{Deserialize, Serialize};
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
pub struct ValueLocRange {
|
||||
/// The ValueLoc containing a ValueLabel during this range.
|
||||
pub loc: ValueLoc,
|
||||
pub loc: LabelValueLoc,
|
||||
/// The start of the range. It is an offset in the generated code.
|
||||
pub start: u32,
|
||||
/// The end of the range. It is an offset in the generated code.
|
||||
pub end: u32,
|
||||
}
|
||||
|
||||
/// The particular location for a value.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||
pub enum LabelValueLoc {
|
||||
/// Old-backend location: RegUnit, StackSlot, or Unassigned.
|
||||
ValueLoc(ValueLoc),
|
||||
/// New-backend Reg.
|
||||
Reg(Reg),
|
||||
/// New-backend offset from stack pointer.
|
||||
SPOffset(i64),
|
||||
}
|
||||
|
||||
impl From<ValueLoc> for LabelValueLoc {
|
||||
fn from(v: ValueLoc) -> Self {
|
||||
LabelValueLoc::ValueLoc(v)
|
||||
}
|
||||
}
|
||||
|
||||
/// Resulting map of Value labels and their ranges/locations.
|
||||
pub type ValueLabelsRanges = HashMap<ValueLabel, Vec<ValueLocRange>>;
|
||||
|
||||
@@ -86,14 +107,18 @@ where
|
||||
pub fn build_value_labels_ranges<T>(
|
||||
func: &Function,
|
||||
regalloc: &Context,
|
||||
mach_compile_result: Option<&MachCompileResult>,
|
||||
isa: &dyn TargetIsa,
|
||||
) -> ValueLabelsRanges
|
||||
where
|
||||
T: From<SourceLoc> + Deref<Target = SourceLoc> + Ord + Copy,
|
||||
{
|
||||
// FIXME(#1523): New-style backend does not yet have debug info.
|
||||
if isa.get_mach_backend().is_some() {
|
||||
return HashMap::new();
|
||||
if mach_compile_result.is_some() && mach_compile_result.unwrap().value_labels_ranges.is_some() {
|
||||
return mach_compile_result
|
||||
.unwrap()
|
||||
.value_labels_ranges
|
||||
.clone()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let values_labels = build_value_labels_index::<T>(func);
|
||||
@@ -113,7 +138,7 @@ where
|
||||
.entry(label)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(ValueLocRange {
|
||||
loc,
|
||||
loc: loc.into(),
|
||||
start: range.0,
|
||||
end: range.1,
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::ir::{
|
||||
};
|
||||
use crate::isa::{RegInfo, TargetIsa};
|
||||
use crate::packed_option::ReservedValue;
|
||||
use crate::value_label::ValueLabelsRanges;
|
||||
use crate::value_label::{LabelValueLoc, ValueLabelsRanges};
|
||||
use crate::HashSet;
|
||||
use alloc::string::String;
|
||||
use alloc::vec::Vec;
|
||||
@@ -278,11 +278,13 @@ pub fn write_block_header(
|
||||
writeln!(w, "):")
|
||||
}
|
||||
|
||||
fn write_valueloc(w: &mut dyn Write, loc: ValueLoc, regs: &RegInfo) -> fmt::Result {
|
||||
fn write_valueloc(w: &mut dyn Write, loc: LabelValueLoc, regs: &RegInfo) -> fmt::Result {
|
||||
match loc {
|
||||
ValueLoc::Reg(r) => write!(w, "{}", regs.display_regunit(r)),
|
||||
ValueLoc::Stack(ss) => write!(w, "{}", ss),
|
||||
ValueLoc::Unassigned => write!(w, "?"),
|
||||
LabelValueLoc::ValueLoc(ValueLoc::Reg(r)) => write!(w, "{}", regs.display_regunit(r)),
|
||||
LabelValueLoc::ValueLoc(ValueLoc::Stack(ss)) => write!(w, "{}", ss),
|
||||
LabelValueLoc::ValueLoc(ValueLoc::Unassigned) => write!(w, "?"),
|
||||
LabelValueLoc::Reg(r) => write!(w, "{:?}", r),
|
||||
LabelValueLoc::SPOffset(off) => write!(w, "[sp+{}]", off),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ use std::collections::{HashMap, HashSet};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::rc::Rc;
|
||||
use wasmtime_environ::entity::EntityRef;
|
||||
use wasmtime_environ::ir::{StackSlots, ValueLabel, ValueLabelsRanges, ValueLoc};
|
||||
use wasmtime_environ::ir::{LabelValueLoc, StackSlots, ValueLabel, ValueLabelsRanges, ValueLoc};
|
||||
use wasmtime_environ::isa::TargetIsa;
|
||||
use wasmtime_environ::wasm::{get_vmctx_value_label, DefinedFuncIndex};
|
||||
use wasmtime_environ::ModuleMemoryOffset;
|
||||
@@ -131,27 +131,24 @@ impl CompiledExpression {
|
||||
const X86_64_STACK_OFFSET: i64 = 16;
|
||||
|
||||
fn translate_loc(
|
||||
loc: ValueLoc,
|
||||
loc: LabelValueLoc,
|
||||
frame_info: Option<&FunctionFrameInfo>,
|
||||
isa: &dyn TargetIsa,
|
||||
add_stack_value: bool,
|
||||
) -> Result<Option<Vec<u8>>> {
|
||||
Ok(match loc {
|
||||
ValueLoc::Reg(reg) if add_stack_value => {
|
||||
LabelValueLoc::ValueLoc(ValueLoc::Reg(reg)) => {
|
||||
let machine_reg = isa.map_dwarf_register(reg)?;
|
||||
let mut writer = ExpressionWriter::new();
|
||||
writer.write_op_reg(machine_reg)?;
|
||||
if add_stack_value {
|
||||
writer.write_op_reg(machine_reg)?;
|
||||
} else {
|
||||
writer.write_op_breg(machine_reg)?;
|
||||
writer.write_sleb128(0)?;
|
||||
}
|
||||
Some(writer.into_vec())
|
||||
}
|
||||
ValueLoc::Reg(reg) => {
|
||||
assert!(!add_stack_value);
|
||||
let machine_reg = isa.map_dwarf_register(reg)?;
|
||||
let mut writer = ExpressionWriter::new();
|
||||
writer.write_op_breg(machine_reg)?;
|
||||
writer.write_sleb128(0)?;
|
||||
Some(writer.into_vec())
|
||||
}
|
||||
ValueLoc::Stack(ss) => {
|
||||
LabelValueLoc::ValueLoc(ValueLoc::Stack(ss)) => {
|
||||
if let Some(frame_info) = frame_info {
|
||||
if let Some(ss_offset) = frame_info.stack_slots[ss].offset {
|
||||
let mut writer = ExpressionWriter::new();
|
||||
@@ -165,6 +162,27 @@ fn translate_loc(
|
||||
}
|
||||
None
|
||||
}
|
||||
LabelValueLoc::Reg(r) => {
|
||||
let machine_reg = isa.map_regalloc_reg_to_dwarf(r)?;
|
||||
let mut writer = ExpressionWriter::new();
|
||||
if add_stack_value {
|
||||
writer.write_op_reg(machine_reg)?;
|
||||
} else {
|
||||
writer.write_op_breg(machine_reg)?;
|
||||
writer.write_sleb128(0)?;
|
||||
}
|
||||
Some(writer.into_vec())
|
||||
}
|
||||
LabelValueLoc::SPOffset(off) => {
|
||||
let mut writer = ExpressionWriter::new();
|
||||
writer.write_op_breg(X86_64::RSP.0)?;
|
||||
writer.write_sleb128(off)?;
|
||||
if !add_stack_value {
|
||||
writer.write_op(gimli::constants::DW_OP_deref)?;
|
||||
}
|
||||
return Ok(Some(writer.into_vec()));
|
||||
}
|
||||
|
||||
_ => None,
|
||||
})
|
||||
}
|
||||
@@ -172,13 +190,13 @@ fn translate_loc(
|
||||
fn append_memory_deref(
|
||||
buf: &mut Vec<u8>,
|
||||
frame_info: &FunctionFrameInfo,
|
||||
vmctx_loc: ValueLoc,
|
||||
vmctx_loc: LabelValueLoc,
|
||||
isa: &dyn TargetIsa,
|
||||
) -> Result<bool> {
|
||||
let mut writer = ExpressionWriter::new();
|
||||
// FIXME for imported memory
|
||||
match vmctx_loc {
|
||||
ValueLoc::Reg(vmctx_reg) => {
|
||||
LabelValueLoc::ValueLoc(ValueLoc::Reg(vmctx_reg)) => {
|
||||
let reg = isa.map_dwarf_register(vmctx_reg)? as u8;
|
||||
writer.write_u8(gimli::constants::DW_OP_breg0.0 + reg)?;
|
||||
let memory_offset = match frame_info.vmctx_memory_offset() {
|
||||
@@ -189,7 +207,7 @@ fn append_memory_deref(
|
||||
};
|
||||
writer.write_sleb128(memory_offset)?;
|
||||
}
|
||||
ValueLoc::Stack(ss) => {
|
||||
LabelValueLoc::ValueLoc(ValueLoc::Stack(ss)) => {
|
||||
if let Some(ss_offset) = frame_info.stack_slots[ss].offset {
|
||||
writer.write_op_breg(X86_64::RBP.0)?;
|
||||
writer.write_sleb128(ss_offset as i64 + X86_64_STACK_OFFSET)?;
|
||||
@@ -207,6 +225,31 @@ fn append_memory_deref(
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
LabelValueLoc::Reg(r) => {
|
||||
let reg = isa.map_regalloc_reg_to_dwarf(r)?;
|
||||
writer.write_op_breg(reg)?;
|
||||
let memory_offset = match frame_info.vmctx_memory_offset() {
|
||||
Some(offset) => offset,
|
||||
None => {
|
||||
return Ok(false);
|
||||
}
|
||||
};
|
||||
writer.write_sleb128(memory_offset)?;
|
||||
}
|
||||
LabelValueLoc::SPOffset(off) => {
|
||||
writer.write_op_breg(X86_64::RSP.0)?;
|
||||
writer.write_sleb128(off)?;
|
||||
writer.write_op(gimli::constants::DW_OP_deref)?;
|
||||
writer.write_op(gimli::constants::DW_OP_consts)?;
|
||||
let memory_offset = match frame_info.vmctx_memory_offset() {
|
||||
Some(offset) => offset,
|
||||
None => {
|
||||
return Ok(false);
|
||||
}
|
||||
};
|
||||
writer.write_sleb128(memory_offset)?;
|
||||
writer.write_op(gimli::constants::DW_OP_plus)?;
|
||||
}
|
||||
_ => {
|
||||
return Ok(false);
|
||||
}
|
||||
@@ -468,7 +511,7 @@ where
|
||||
let _ = code_chunk; // suppresses warning for final flush
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
// Find all landing pads by scanning bytes, do not care about
|
||||
// false location at this moment.
|
||||
// Looks hacky but it is fast; does not need to be really exact.
|
||||
@@ -653,7 +696,7 @@ struct CachedValueLabelRange {
|
||||
func_index: DefinedFuncIndex,
|
||||
start: usize,
|
||||
end: usize,
|
||||
label_location: HashMap<ValueLabel, ValueLoc>,
|
||||
label_location: HashMap<ValueLabel, LabelValueLoc>,
|
||||
}
|
||||
|
||||
struct ValueLabelRangesBuilder<'a, 'b> {
|
||||
@@ -1179,7 +1222,7 @@ mod tests {
|
||||
fn create_mock_value_ranges() -> (ValueLabelsRanges, (ValueLabel, ValueLabel, ValueLabel)) {
|
||||
use std::collections::HashMap;
|
||||
use wasmtime_environ::entity::EntityRef;
|
||||
use wasmtime_environ::ir::{ValueLoc, ValueLocRange};
|
||||
use wasmtime_environ::ir::{LabelValueLoc, ValueLoc, ValueLocRange};
|
||||
let mut value_ranges = HashMap::new();
|
||||
let value_0 = ValueLabel::new(0);
|
||||
let value_1 = ValueLabel::new(1);
|
||||
@@ -1187,7 +1230,7 @@ mod tests {
|
||||
value_ranges.insert(
|
||||
value_0,
|
||||
vec![ValueLocRange {
|
||||
loc: ValueLoc::Unassigned,
|
||||
loc: LabelValueLoc::ValueLoc(ValueLoc::Unassigned),
|
||||
start: 0,
|
||||
end: 25,
|
||||
}],
|
||||
@@ -1195,7 +1238,7 @@ mod tests {
|
||||
value_ranges.insert(
|
||||
value_1,
|
||||
vec![ValueLocRange {
|
||||
loc: ValueLoc::Unassigned,
|
||||
loc: LabelValueLoc::ValueLoc(ValueLoc::Unassigned),
|
||||
start: 5,
|
||||
end: 30,
|
||||
}],
|
||||
@@ -1204,12 +1247,12 @@ mod tests {
|
||||
value_2,
|
||||
vec![
|
||||
ValueLocRange {
|
||||
loc: ValueLoc::Unassigned,
|
||||
loc: LabelValueLoc::ValueLoc(ValueLoc::Unassigned),
|
||||
start: 0,
|
||||
end: 10,
|
||||
},
|
||||
ValueLocRange {
|
||||
loc: ValueLoc::Unassigned,
|
||||
loc: LabelValueLoc::ValueLoc(ValueLoc::Unassigned),
|
||||
start: 20,
|
||||
end: 30,
|
||||
},
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
pub mod ir {
|
||||
pub use cranelift_codegen::binemit::{Reloc, StackMap};
|
||||
pub use cranelift_codegen::ir::{
|
||||
types, AbiParam, ArgumentPurpose, JumpTableOffsets, LibCall, Signature, SourceLoc,
|
||||
StackSlots, TrapCode, Type, ValueLabel, ValueLoc,
|
||||
types, AbiParam, ArgumentPurpose, JumpTableOffsets, LabelValueLoc, LibCall, Signature,
|
||||
SourceLoc, StackSlots, TrapCode, Type, ValueLabel, ValueLoc,
|
||||
};
|
||||
pub use cranelift_codegen::{ValueLabelsRanges, ValueLocRange};
|
||||
}
|
||||
|
||||
@@ -137,7 +137,11 @@ check: exited with status
|
||||
#[ignore]
|
||||
#[cfg(all(
|
||||
any(target_os = "linux", target_os = "macos"),
|
||||
target_pointer_width = "64"
|
||||
target_pointer_width = "64",
|
||||
// Ignore test on new backend. The value this is looking for is
|
||||
// not available at the point that the breakpoint is set when
|
||||
// compiled by the new backend.
|
||||
not(feature = "experimental_x64"),
|
||||
))]
|
||||
pub fn test_debug_dwarf_ptr() -> Result<()> {
|
||||
let output = lldb_with_script(
|
||||
|
||||
@@ -114,7 +114,11 @@ check: DW_AT_decl_line (10)
|
||||
#[cfg(all(
|
||||
any(target_os = "linux", target_os = "macos"),
|
||||
target_arch = "x86_64",
|
||||
target_pointer_width = "64"
|
||||
target_pointer_width = "64",
|
||||
// Ignore test on new backend. This is a specific test with hardcoded
|
||||
// offsets and the new backend compiles the return basic-block at a different
|
||||
// offset, causing mismatches.
|
||||
not(feature = "experimental_x64"),
|
||||
))]
|
||||
fn test_debug_dwarf5_translate_lines() -> Result<()> {
|
||||
check_line_program(
|
||||
|
||||
Reference in New Issue
Block a user