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:
|
env:
|
||||||
RUST_BACKTRACE: 1
|
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
|
# Build and test lightbeam. Note that
|
||||||
# Lightbeam tests fail right now, but we don't want to block on that.
|
# Lightbeam tests fail right now, but we don't want to block on that.
|
||||||
- run: cargo build --package lightbeam
|
- run: cargo build --package lightbeam
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ all-arch = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
# For dependent crates that want to serialize some parts of cranelift
|
# 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
|
# Allow snapshotting regalloc test cases. Useful only to report bad register
|
||||||
# allocation failures, or for regalloc.rs developers.
|
# allocation failures, or for regalloc.rs developers.
|
||||||
|
|||||||
@@ -473,6 +473,7 @@ impl Context {
|
|||||||
Ok(build_value_labels_ranges::<ComparableSourceLoc>(
|
Ok(build_value_labels_ranges::<ComparableSourceLoc>(
|
||||||
&self.func,
|
&self.func,
|
||||||
&self.regalloc,
|
&self.regalloc,
|
||||||
|
self.mach_compile_result.as_ref(),
|
||||||
isa,
|
isa,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ pub use crate::ir::table::TableData;
|
|||||||
pub use crate::ir::trapcode::TrapCode;
|
pub use crate::ir::trapcode::TrapCode;
|
||||||
pub use crate::ir::types::Type;
|
pub use crate::ir::types::Type;
|
||||||
pub use crate::ir::valueloc::{ArgumentLoc, ValueLoc};
|
pub use crate::ir::valueloc::{ArgumentLoc, ValueLoc};
|
||||||
|
pub use crate::value_label::LabelValueLoc;
|
||||||
pub use cranelift_codegen_shared::condcodes;
|
pub use cranelift_codegen_shared::condcodes;
|
||||||
|
|
||||||
use crate::binemit;
|
use crate::binemit;
|
||||||
|
|||||||
@@ -2371,6 +2371,9 @@ impl MachInstEmit for Inst {
|
|||||||
sink.bind_label(jump_around_label);
|
sink.bind_label(jump_around_label);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
&Inst::ValueLabelMarker { .. } => {
|
||||||
|
// Nothing; this is only used to compute debug info.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let end_off = sink.cur_offset();
|
let end_off = sink.cur_offset();
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use crate::binemit::CodeOffset;
|
|||||||
use crate::ir::types::{
|
use crate::ir::types::{
|
||||||
B1, B128, B16, B32, B64, B8, F32, F64, FFLAGS, I128, I16, I32, I64, I8, I8X16, IFLAGS, R32, R64,
|
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::isa::CallConv;
|
||||||
use crate::machinst::*;
|
use crate::machinst::*;
|
||||||
use crate::{settings, CodegenError, CodegenResult};
|
use crate::{settings, CodegenError, CodegenResult};
|
||||||
@@ -1210,6 +1210,12 @@ pub enum Inst {
|
|||||||
/// The needed space before the next deadline.
|
/// The needed space before the next deadline.
|
||||||
needed_space: CodeOffset,
|
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 {
|
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);
|
memarg_regs(mem, collector);
|
||||||
}
|
}
|
||||||
&Inst::VirtualSPOffsetAdj { .. } => {}
|
&Inst::VirtualSPOffsetAdj { .. } => {}
|
||||||
|
&Inst::ValueLabelMarker { reg, .. } => {
|
||||||
|
collector.add_use(reg);
|
||||||
|
}
|
||||||
&Inst::EmitIsland { .. } => {}
|
&Inst::EmitIsland { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2767,6 +2776,9 @@ fn aarch64_map_regs<RUM: RegUsageMapper>(inst: &mut Inst, mapper: &RUM) {
|
|||||||
}
|
}
|
||||||
&mut Inst::VirtualSPOffsetAdj { .. } => {}
|
&mut Inst::VirtualSPOffsetAdj { .. } => {}
|
||||||
&mut Inst::EmitIsland { .. } => {}
|
&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 {
|
fn ref_type_regclass(_: &settings::Flags) -> RegClass {
|
||||||
RegClass::I64
|
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)
|
format!("virtual_sp_offset_adjust {}", offset)
|
||||||
}
|
}
|
||||||
&Inst::EmitIsland { needed_space } => format!("emit_island {}", needed_space),
|
&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,
|
frame_size,
|
||||||
disasm,
|
disasm,
|
||||||
unwind_info,
|
unwind_info,
|
||||||
|
value_labels_ranges: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ impl MachBackend for Arm32Backend {
|
|||||||
frame_size,
|
frame_size,
|
||||||
disasm,
|
disasm,
|
||||||
unwind_info: None,
|
unwind_info: None,
|
||||||
|
value_labels_ranges: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -325,6 +325,12 @@ pub trait TargetIsa: fmt::Display + Send + Sync {
|
|||||||
Err(RegisterMappingError::UnsupportedArchitecture)
|
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.
|
/// Returns an iterator over legal encodings for the instruction.
|
||||||
fn legal_encodings<'a>(
|
fn legal_encodings<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
|
|||||||
@@ -3029,6 +3029,10 @@ pub(crate) fn emit(
|
|||||||
sink.put1(0xff);
|
sink.put1(0xff);
|
||||||
sink.put1(0x17);
|
sink.put1(0x17);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Inst::ValueLabelMarker { .. } => {
|
||||||
|
// Nothing; this is only used to compute debug info.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
state.clear_post_insn();
|
state.clear_post_insn();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
//! This module defines x86_64-specific machine instruction types.
|
//! This module defines x86_64-specific machine instruction types.
|
||||||
|
|
||||||
use crate::binemit::{CodeOffset, StackMap};
|
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::abi::X64ABIMachineSpec;
|
||||||
use crate::isa::x64::settings as x64_settings;
|
use crate::isa::x64::settings as x64_settings;
|
||||||
use crate::isa::CallConv;
|
use crate::isa::CallConv;
|
||||||
@@ -484,6 +484,9 @@ pub enum Inst {
|
|||||||
/// A Mach-O TLS symbol access. Returns address of the TLS
|
/// A Mach-O TLS symbol access. Returns address of the TLS
|
||||||
/// symbol in rax.
|
/// symbol in rax.
|
||||||
MachOTlsGetAddr { symbol: ExternalName },
|
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 {
|
pub(crate) fn low32_will_sign_extend_to_64(x: u64) -> bool {
|
||||||
@@ -544,7 +547,8 @@ impl Inst {
|
|||||||
| Inst::XmmMinMaxSeq { .. }
|
| Inst::XmmMinMaxSeq { .. }
|
||||||
| Inst::XmmUninitializedValue { .. }
|
| Inst::XmmUninitializedValue { .. }
|
||||||
| Inst::ElfTlsGetAddr { .. }
|
| Inst::ElfTlsGetAddr { .. }
|
||||||
| Inst::MachOTlsGetAddr { .. } => None,
|
| Inst::MachOTlsGetAddr { .. }
|
||||||
|
| Inst::ValueLabelMarker { .. } => None,
|
||||||
|
|
||||||
// These use dynamic SSE opcodes.
|
// These use dynamic SSE opcodes.
|
||||||
Inst::GprToXmm { op, .. }
|
Inst::GprToXmm { op, .. }
|
||||||
@@ -1800,6 +1804,10 @@ impl PrettyPrint for Inst {
|
|||||||
Inst::MachOTlsGetAddr { ref symbol } => {
|
Inst::MachOTlsGetAddr { ref symbol } => {
|
||||||
format!("macho_tls_get_addr {:?}", 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);
|
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);
|
dst.map_uses(mapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Inst::ValueLabelMarker { ref mut reg, .. } => map_use(mapper, reg),
|
||||||
|
|
||||||
Inst::Ret
|
Inst::Ret
|
||||||
| Inst::EpiloguePlaceholder
|
| Inst::EpiloguePlaceholder
|
||||||
| Inst::JmpKnown { .. }
|
| 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 {
|
fn gen_move(dst_reg: Writable<Reg>, src_reg: Reg, ty: Type) -> Inst {
|
||||||
let rc_dst = dst_reg.to_reg().get_class();
|
let rc_dst = dst_reg.to_reg().get_class();
|
||||||
let rc_src = src_reg.get_class();
|
let rc_src = src_reg.get_class();
|
||||||
@@ -2710,6 +2743,17 @@ impl MachInst for Inst {
|
|||||||
RegClass::I64
|
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;
|
type LabelUse = LabelUse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,14 @@ use self::inst::EmitInfo;
|
|||||||
|
|
||||||
use super::TargetIsa;
|
use super::TargetIsa;
|
||||||
use crate::ir::{condcodes::IntCC, Function};
|
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::x64::{inst::regs::create_reg_universe_systemv, settings as x64_settings};
|
||||||
use crate::isa::Builder as IsaBuilder;
|
use crate::isa::Builder as IsaBuilder;
|
||||||
use crate::machinst::{compile, MachBackend, MachCompileResult, TargetIsaAdapter, VCode};
|
use crate::machinst::{compile, MachBackend, MachCompileResult, TargetIsaAdapter, VCode};
|
||||||
use crate::result::CodegenResult;
|
use crate::result::CodegenResult;
|
||||||
use crate::settings::{self as shared_settings, Flags};
|
use crate::settings::{self as shared_settings, Flags};
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use regalloc::{PrettyPrint, RealRegUniverse};
|
use regalloc::{PrettyPrint, RealRegUniverse, Reg};
|
||||||
use target_lexicon::Triple;
|
use target_lexicon::Triple;
|
||||||
|
|
||||||
mod abi;
|
mod abi;
|
||||||
@@ -60,6 +61,7 @@ impl MachBackend for X64Backend {
|
|||||||
let buffer = buffer.finish();
|
let buffer = buffer.finish();
|
||||||
let frame_size = vcode.frame_size();
|
let frame_size = vcode.frame_size();
|
||||||
let unwind_info = vcode.unwind_info()?;
|
let unwind_info = vcode.unwind_info()?;
|
||||||
|
let value_labels_ranges = vcode.value_labels_ranges()?;
|
||||||
|
|
||||||
let disasm = if want_disasm {
|
let disasm = if want_disasm {
|
||||||
Some(vcode.show_rru(Some(&create_reg_universe_systemv(flags))))
|
Some(vcode.show_rru(Some(&create_reg_universe_systemv(flags))))
|
||||||
@@ -72,6 +74,7 @@ impl MachBackend for X64Backend {
|
|||||||
frame_size,
|
frame_size,
|
||||||
disasm,
|
disasm,
|
||||||
unwind_info,
|
unwind_info,
|
||||||
|
value_labels_ranges,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,6 +130,11 @@ impl MachBackend for X64Backend {
|
|||||||
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
||||||
Some(inst::unwind::systemv::create_cie())
|
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`.
|
/// Create a new `isa::Builder`.
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ use crate::settings::Flags;
|
|||||||
#[cfg(feature = "testing_hooks")]
|
#[cfg(feature = "testing_hooks")]
|
||||||
use crate::regalloc::RegDiversions;
|
use crate::regalloc::RegDiversions;
|
||||||
|
|
||||||
|
#[cfg(feature = "unwind")]
|
||||||
|
use crate::isa::unwind::systemv::RegisterMappingError;
|
||||||
|
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
@@ -134,6 +137,11 @@ impl TargetIsa for TargetIsaAdapter {
|
|||||||
self.backend.create_systemv_cie()
|
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 {
|
fn as_any(&self) -> &dyn Any {
|
||||||
self as &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::{
|
use crate::ir::{
|
||||||
ArgumentPurpose, Block, Constant, ConstantData, ExternalName, Function, GlobalValueData, Inst,
|
ArgumentPurpose, Block, Constant, ConstantData, ExternalName, Function, GlobalValueData, Inst,
|
||||||
InstructionData, MemFlags, Opcode, Signature, SourceLoc, Type, Value, ValueDef,
|
InstructionData, MemFlags, Opcode, Signature, SourceLoc, Type, Value, ValueDef,
|
||||||
|
ValueLabelAssignments, ValueLabelStart,
|
||||||
};
|
};
|
||||||
use crate::machinst::{
|
use crate::machinst::{
|
||||||
writable_value_regs, ABICallee, BlockIndex, BlockLoweringOrder, LoweredBlock, MachLabel, VCode,
|
writable_value_regs, ABICallee, BlockIndex, BlockLoweringOrder, LoweredBlock, MachLabel, VCode,
|
||||||
@@ -24,7 +25,7 @@ use alloc::vec::Vec;
|
|||||||
use core::convert::TryInto;
|
use core::convert::TryInto;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use regalloc::{Reg, StackmapRequestInfo, Writable};
|
use regalloc::{Reg, StackmapRequestInfo, Writable};
|
||||||
use smallvec::SmallVec;
|
use smallvec::{smallvec, SmallVec};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
/// An "instruction color" partitions CLIF instructions by side-effecting ops.
|
/// 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) {
|
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();
|
let retval_regs = self.retval_regs.clone();
|
||||||
for (i, regs) in retval_regs.into_iter().enumerate() {
|
for (i, regs) in retval_regs.into_iter().enumerate() {
|
||||||
let regs = writable_value_regs(regs);
|
let regs = writable_value_regs(regs);
|
||||||
@@ -725,6 +734,9 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
|||||||
if has_side_effect || value_needed {
|
if has_side_effect || value_needed {
|
||||||
debug!("lowering: inst {}: {:?}", inst, self.f.dfg[inst]);
|
debug!("lowering: inst {}: {:?}", inst, self.f.dfg[inst]);
|
||||||
backend.lower(self, 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() {
|
if data.opcode().is_return() {
|
||||||
// Return: handle specially, using ABI-appropriate sequence.
|
// Return: handle specially, using ABI-appropriate sequence.
|
||||||
@@ -744,6 +756,80 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
|||||||
Ok(())
|
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) {
|
fn finish_ir_inst(&mut self, loc: SourceLoc) {
|
||||||
// `bb_insts` is kept in reverse order, so emit the instructions in
|
// `bb_insts` is kept in reverse order, so emit the instructions in
|
||||||
// reverse order.
|
// reverse order.
|
||||||
@@ -885,6 +971,7 @@ impl<'func, I: VCodeInst> Lower<'func, I> {
|
|||||||
// Original block body.
|
// Original block body.
|
||||||
if let Some(bb) = lb.orig_block() {
|
if let Some(bb) = lb.orig_block() {
|
||||||
self.lower_clif_block(backend, bb)?;
|
self.lower_clif_block(backend, bb)?;
|
||||||
|
self.emit_value_label_markers_for_block_args(bb);
|
||||||
}
|
}
|
||||||
// In-edge phi moves.
|
// In-edge phi moves.
|
||||||
if let Some((pred, inst, succ)) = lb.in_edge() {
|
if let Some((pred, inst, succ)) = lb.in_edge() {
|
||||||
|
|||||||
@@ -52,45 +52,9 @@
|
|||||||
//! | - all symbolic stack references to
|
//! | - all symbolic stack references to
|
||||||
//! | stackslots and spillslots are resolved
|
//! | stackslots and spillslots are resolved
|
||||||
//! | to concrete FP-offset mem addresses.)
|
//! | to concrete FP-offset mem addresses.)
|
||||||
//! | [block/insn ordering]
|
|
||||||
//! |
|
//! |
|
||||||
//! VCode<arch_backend::Inst> (machine instructions:
|
//! | [binary emission via MachBuffer
|
||||||
//! | - vcode.final_block_order is filled in.
|
//! | with streaming branch resolution/simplification]
|
||||||
//! | - 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!)
|
//! Vec<u8> (machine code!)
|
||||||
//!
|
//!
|
||||||
@@ -98,11 +62,11 @@
|
|||||||
|
|
||||||
use crate::binemit::{CodeInfo, CodeOffset, StackMap};
|
use crate::binemit::{CodeInfo, CodeOffset, StackMap};
|
||||||
use crate::ir::condcodes::IntCC;
|
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::isa::unwind::input as unwind_input;
|
||||||
use crate::result::CodegenResult;
|
use crate::result::CodegenResult;
|
||||||
use crate::settings::Flags;
|
use crate::settings::Flags;
|
||||||
|
use crate::value_label::ValueLabelsRanges;
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
@@ -111,10 +75,13 @@ use regalloc::RegUsageCollector;
|
|||||||
use regalloc::{
|
use regalloc::{
|
||||||
RealReg, RealRegUniverse, Reg, RegClass, RegUsageMapper, SpillSlot, VirtualReg, Writable,
|
RealReg, RealRegUniverse, Reg, RegClass, RegUsageMapper, SpillSlot, VirtualReg, Writable,
|
||||||
};
|
};
|
||||||
use smallvec::SmallVec;
|
use smallvec::{smallvec, SmallVec};
|
||||||
use std::string::String;
|
use std::string::String;
|
||||||
use target_lexicon::Triple;
|
use target_lexicon::Triple;
|
||||||
|
|
||||||
|
#[cfg(feature = "unwind")]
|
||||||
|
use crate::isa::unwind::systemv::RegisterMappingError;
|
||||||
|
|
||||||
pub mod lower;
|
pub mod lower;
|
||||||
pub use lower::*;
|
pub use lower::*;
|
||||||
pub mod vcode;
|
pub mod vcode;
|
||||||
@@ -137,6 +104,7 @@ pub mod inst_common;
|
|||||||
pub use inst_common::*;
|
pub use inst_common::*;
|
||||||
pub mod valueregs;
|
pub mod valueregs;
|
||||||
pub use valueregs::*;
|
pub use valueregs::*;
|
||||||
|
pub mod debug;
|
||||||
|
|
||||||
/// A machine instruction.
|
/// A machine instruction.
|
||||||
pub trait MachInst: Clone + Debug {
|
pub trait MachInst: Clone + Debug {
|
||||||
@@ -163,6 +131,11 @@ pub trait MachInst: Clone + Debug {
|
|||||||
true
|
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.
|
/// Generate a move.
|
||||||
fn gen_move(to_reg: Writable<Reg>, from_reg: Reg, ty: Type) -> Self;
|
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.
|
/// be dependent on compilation flags.
|
||||||
fn ref_type_regclass(_flags: &Flags) -> RegClass;
|
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
|
/// A label-use kind: a type that describes the types of label references that
|
||||||
/// can occur in an instruction.
|
/// can occur in an instruction.
|
||||||
type LabelUse: MachInstLabelUse;
|
type LabelUse: MachInstLabelUse;
|
||||||
@@ -285,6 +269,35 @@ pub enum MachTerminator<'a> {
|
|||||||
Indirect(&'a [MachLabel]),
|
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.
|
/// A trait describing the ability to encode a MachInst into binary machine code.
|
||||||
pub trait MachInstEmit: MachInst {
|
pub trait MachInstEmit: MachInst {
|
||||||
/// Persistent state carried across `emit` invocations.
|
/// Persistent state carried across `emit` invocations.
|
||||||
@@ -330,6 +343,8 @@ pub struct MachCompileResult {
|
|||||||
pub disasm: Option<String>,
|
pub disasm: Option<String>,
|
||||||
/// Unwind info.
|
/// Unwind info.
|
||||||
pub unwind_info: Option<unwind_input::UnwindInfo<Reg>>,
|
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 {
|
impl MachCompileResult {
|
||||||
@@ -386,13 +401,17 @@ pub trait MachBackend {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Machine-specific condcode info needed by TargetIsa.
|
|
||||||
/// Creates a new System V Common Information Entry for the ISA.
|
/// Creates a new System V Common Information Entry for the ISA.
|
||||||
#[cfg(feature = "unwind")]
|
#[cfg(feature = "unwind")]
|
||||||
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
fn create_systemv_cie(&self) -> Option<gimli::write::CommonInformationEntry> {
|
||||||
// By default, an ISA cannot create a System V CIE
|
// By default, an ISA cannot create a System V CIE
|
||||||
None
|
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.
|
/// Expected unwind info type.
|
||||||
@@ -431,3 +450,15 @@ pub trait UnwindInfoGenerator<I: MachInstEmit> {
|
|||||||
context: UnwindInfoContext<I>,
|
context: UnwindInfoContext<I>,
|
||||||
) -> CodegenResult<Option<unwind_input::UnwindInfo<Reg>>>;
|
) -> 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::machinst::*;
|
||||||
use crate::settings;
|
use crate::settings;
|
||||||
use crate::timing;
|
use crate::timing;
|
||||||
|
|
||||||
use regalloc::Function as RegallocFunction;
|
use regalloc::Function as RegallocFunction;
|
||||||
use regalloc::Set as RegallocSet;
|
use regalloc::Set as RegallocSet;
|
||||||
use regalloc::{
|
use regalloc::{
|
||||||
@@ -110,11 +109,19 @@ pub struct VCode<I: VCodeInst> {
|
|||||||
/// Ranges for prologue and epilogue instructions.
|
/// Ranges for prologue and epilogue instructions.
|
||||||
prologue_epilogue_ranges: Option<(InsnRange, Box<[InsnRange]>)>,
|
prologue_epilogue_ranges: Option<(InsnRange, Box<[InsnRange]>)>,
|
||||||
|
|
||||||
/// Instruction end offsets
|
/// Do we generate debug info?
|
||||||
insts_layout: RefCell<(Vec<u32>, u32)>,
|
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.
|
||||||
constants: VCodeConstants,
|
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
|
/// A builder for a VCode function body. This builder is designed for the
|
||||||
@@ -157,7 +164,13 @@ impl<I: VCodeInst> VCodeBuilder<I> {
|
|||||||
constants: VCodeConstants,
|
constants: VCodeConstants,
|
||||||
) -> VCodeBuilder<I> {
|
) -> VCodeBuilder<I> {
|
||||||
let reftype_class = I::ref_type_regclass(abi.flags());
|
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 {
|
let stack_map_info = StackmapRequestInfo {
|
||||||
reftype_class,
|
reftype_class,
|
||||||
reftyped_vregs: vec![],
|
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.insts.push(insn);
|
||||||
self.vcode.srclocs.push(self.cur_srcloc);
|
self.vcode.srclocs.push(self.cur_srcloc);
|
||||||
if is_safepoint {
|
if is_safepoint {
|
||||||
@@ -296,6 +312,7 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
emit_info: I::Info,
|
emit_info: I::Info,
|
||||||
block_order: BlockLoweringOrder,
|
block_order: BlockLoweringOrder,
|
||||||
constants: VCodeConstants,
|
constants: VCodeConstants,
|
||||||
|
generate_debug_info: bool,
|
||||||
) -> VCode<I> {
|
) -> VCode<I> {
|
||||||
VCode {
|
VCode {
|
||||||
liveins: abi.liveins(),
|
liveins: abi.liveins(),
|
||||||
@@ -314,8 +331,10 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
safepoint_insns: vec![],
|
safepoint_insns: vec![],
|
||||||
safepoint_slots: vec![],
|
safepoint_slots: vec![],
|
||||||
prologue_epilogue_ranges: None,
|
prologue_epilogue_ranges: None,
|
||||||
insts_layout: RefCell::new((vec![], 0)),
|
generate_debug_info,
|
||||||
|
insts_layout: RefCell::new((vec![], vec![], 0)),
|
||||||
constants,
|
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_blocks(self.num_blocks() as BlockIndex);
|
||||||
buffer.reserve_labels_for_constants(&self.constants);
|
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 safepoint_idx = 0;
|
||||||
let mut cur_srcloc = None;
|
let mut cur_srcloc = None;
|
||||||
@@ -500,6 +520,7 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
|
|
||||||
let (start, end) = self.block_ranges[block as usize];
|
let (start, end) = self.block_ranges[block as usize];
|
||||||
buffer.bind_label(MachLabel::from_block(block));
|
buffer.bind_label(MachLabel::from_block(block));
|
||||||
|
label_insn_iix[block as usize] = start;
|
||||||
for iix in start..end {
|
for iix in start..end {
|
||||||
let srcloc = self.srclocs[iix as usize];
|
let srcloc = self.srclocs[iix as usize];
|
||||||
if cur_srcloc != Some(srcloc) {
|
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);
|
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() {
|
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());
|
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
|
buffer
|
||||||
}
|
}
|
||||||
@@ -567,13 +609,27 @@ impl<I: VCodeInst> VCode<I> {
|
|||||||
let context = UnwindInfoContext {
|
let context = UnwindInfoContext {
|
||||||
insts: &self.insts,
|
insts: &self.insts,
|
||||||
insts_layout: &layout.0,
|
insts_layout: &layout.0,
|
||||||
len: layout.1,
|
len: layout.2,
|
||||||
prologue: prologue.clone(),
|
prologue: prologue.clone(),
|
||||||
epilogues,
|
epilogues,
|
||||||
};
|
};
|
||||||
I::UnwindInfo::create_unwind_info(context)
|
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.
|
/// Get the IR block for a BlockIndex, if one exists.
|
||||||
pub fn bindex_to_bb(&self, block: BlockIndex) -> Option<ir::Block> {
|
pub fn bindex_to_bb(&self, block: BlockIndex) -> Option<ir::Block> {
|
||||||
self.block_order.lowered_order()[block as usize].orig_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::ir::{Function, SourceLoc, Value, ValueLabel, ValueLabelAssignments, ValueLoc};
|
||||||
use crate::isa::TargetIsa;
|
use crate::isa::TargetIsa;
|
||||||
|
use crate::machinst::MachCompileResult;
|
||||||
use crate::regalloc::{Context, RegDiversions};
|
use crate::regalloc::{Context, RegDiversions};
|
||||||
use crate::HashMap;
|
use crate::HashMap;
|
||||||
use alloc::collections::BTreeMap;
|
use alloc::collections::BTreeMap;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
|
use core::convert::From;
|
||||||
use core::iter::Iterator;
|
use core::iter::Iterator;
|
||||||
use core::ops::Bound::*;
|
use core::ops::Bound::*;
|
||||||
use core::ops::Deref;
|
use core::ops::Deref;
|
||||||
|
use regalloc::Reg;
|
||||||
|
|
||||||
#[cfg(feature = "enable-serde")]
|
#[cfg(feature = "enable-serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -17,13 +20,31 @@ use serde::{Deserialize, Serialize};
|
|||||||
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))]
|
||||||
pub struct ValueLocRange {
|
pub struct ValueLocRange {
|
||||||
/// The ValueLoc containing a ValueLabel during this range.
|
/// 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.
|
/// The start of the range. It is an offset in the generated code.
|
||||||
pub start: u32,
|
pub start: u32,
|
||||||
/// The end of the range. It is an offset in the generated code.
|
/// The end of the range. It is an offset in the generated code.
|
||||||
pub end: u32,
|
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.
|
/// Resulting map of Value labels and their ranges/locations.
|
||||||
pub type ValueLabelsRanges = HashMap<ValueLabel, Vec<ValueLocRange>>;
|
pub type ValueLabelsRanges = HashMap<ValueLabel, Vec<ValueLocRange>>;
|
||||||
|
|
||||||
@@ -86,14 +107,18 @@ where
|
|||||||
pub fn build_value_labels_ranges<T>(
|
pub fn build_value_labels_ranges<T>(
|
||||||
func: &Function,
|
func: &Function,
|
||||||
regalloc: &Context,
|
regalloc: &Context,
|
||||||
|
mach_compile_result: Option<&MachCompileResult>,
|
||||||
isa: &dyn TargetIsa,
|
isa: &dyn TargetIsa,
|
||||||
) -> ValueLabelsRanges
|
) -> ValueLabelsRanges
|
||||||
where
|
where
|
||||||
T: From<SourceLoc> + Deref<Target = SourceLoc> + Ord + Copy,
|
T: From<SourceLoc> + Deref<Target = SourceLoc> + Ord + Copy,
|
||||||
{
|
{
|
||||||
// FIXME(#1523): New-style backend does not yet have debug info.
|
if mach_compile_result.is_some() && mach_compile_result.unwrap().value_labels_ranges.is_some() {
|
||||||
if isa.get_mach_backend().is_some() {
|
return mach_compile_result
|
||||||
return HashMap::new();
|
.unwrap()
|
||||||
|
.value_labels_ranges
|
||||||
|
.clone()
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let values_labels = build_value_labels_index::<T>(func);
|
let values_labels = build_value_labels_index::<T>(func);
|
||||||
@@ -113,7 +138,7 @@ where
|
|||||||
.entry(label)
|
.entry(label)
|
||||||
.or_insert_with(Vec::new)
|
.or_insert_with(Vec::new)
|
||||||
.push(ValueLocRange {
|
.push(ValueLocRange {
|
||||||
loc,
|
loc: loc.into(),
|
||||||
start: range.0,
|
start: range.0,
|
||||||
end: range.1,
|
end: range.1,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ use crate::ir::{
|
|||||||
};
|
};
|
||||||
use crate::isa::{RegInfo, TargetIsa};
|
use crate::isa::{RegInfo, TargetIsa};
|
||||||
use crate::packed_option::ReservedValue;
|
use crate::packed_option::ReservedValue;
|
||||||
use crate::value_label::ValueLabelsRanges;
|
use crate::value_label::{LabelValueLoc, ValueLabelsRanges};
|
||||||
use crate::HashSet;
|
use crate::HashSet;
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
@@ -278,11 +278,13 @@ pub fn write_block_header(
|
|||||||
writeln!(w, "):")
|
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 {
|
match loc {
|
||||||
ValueLoc::Reg(r) => write!(w, "{}", regs.display_regunit(r)),
|
LabelValueLoc::ValueLoc(ValueLoc::Reg(r)) => write!(w, "{}", regs.display_regunit(r)),
|
||||||
ValueLoc::Stack(ss) => write!(w, "{}", ss),
|
LabelValueLoc::ValueLoc(ValueLoc::Stack(ss)) => write!(w, "{}", ss),
|
||||||
ValueLoc::Unassigned => write!(w, "?"),
|
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::hash::{Hash, Hasher};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use wasmtime_environ::entity::EntityRef;
|
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::isa::TargetIsa;
|
||||||
use wasmtime_environ::wasm::{get_vmctx_value_label, DefinedFuncIndex};
|
use wasmtime_environ::wasm::{get_vmctx_value_label, DefinedFuncIndex};
|
||||||
use wasmtime_environ::ModuleMemoryOffset;
|
use wasmtime_environ::ModuleMemoryOffset;
|
||||||
@@ -131,27 +131,24 @@ impl CompiledExpression {
|
|||||||
const X86_64_STACK_OFFSET: i64 = 16;
|
const X86_64_STACK_OFFSET: i64 = 16;
|
||||||
|
|
||||||
fn translate_loc(
|
fn translate_loc(
|
||||||
loc: ValueLoc,
|
loc: LabelValueLoc,
|
||||||
frame_info: Option<&FunctionFrameInfo>,
|
frame_info: Option<&FunctionFrameInfo>,
|
||||||
isa: &dyn TargetIsa,
|
isa: &dyn TargetIsa,
|
||||||
add_stack_value: bool,
|
add_stack_value: bool,
|
||||||
) -> Result<Option<Vec<u8>>> {
|
) -> Result<Option<Vec<u8>>> {
|
||||||
Ok(match loc {
|
Ok(match loc {
|
||||||
ValueLoc::Reg(reg) if add_stack_value => {
|
LabelValueLoc::ValueLoc(ValueLoc::Reg(reg)) => {
|
||||||
let machine_reg = isa.map_dwarf_register(reg)?;
|
let machine_reg = isa.map_dwarf_register(reg)?;
|
||||||
let mut writer = ExpressionWriter::new();
|
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())
|
Some(writer.into_vec())
|
||||||
}
|
}
|
||||||
ValueLoc::Reg(reg) => {
|
LabelValueLoc::ValueLoc(ValueLoc::Stack(ss)) => {
|
||||||
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) => {
|
|
||||||
if let Some(frame_info) = frame_info {
|
if let Some(frame_info) = frame_info {
|
||||||
if let Some(ss_offset) = frame_info.stack_slots[ss].offset {
|
if let Some(ss_offset) = frame_info.stack_slots[ss].offset {
|
||||||
let mut writer = ExpressionWriter::new();
|
let mut writer = ExpressionWriter::new();
|
||||||
@@ -165,6 +162,27 @@ fn translate_loc(
|
|||||||
}
|
}
|
||||||
None
|
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,
|
_ => None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -172,13 +190,13 @@ fn translate_loc(
|
|||||||
fn append_memory_deref(
|
fn append_memory_deref(
|
||||||
buf: &mut Vec<u8>,
|
buf: &mut Vec<u8>,
|
||||||
frame_info: &FunctionFrameInfo,
|
frame_info: &FunctionFrameInfo,
|
||||||
vmctx_loc: ValueLoc,
|
vmctx_loc: LabelValueLoc,
|
||||||
isa: &dyn TargetIsa,
|
isa: &dyn TargetIsa,
|
||||||
) -> Result<bool> {
|
) -> Result<bool> {
|
||||||
let mut writer = ExpressionWriter::new();
|
let mut writer = ExpressionWriter::new();
|
||||||
// FIXME for imported memory
|
// FIXME for imported memory
|
||||||
match vmctx_loc {
|
match vmctx_loc {
|
||||||
ValueLoc::Reg(vmctx_reg) => {
|
LabelValueLoc::ValueLoc(ValueLoc::Reg(vmctx_reg)) => {
|
||||||
let reg = isa.map_dwarf_register(vmctx_reg)? as u8;
|
let reg = isa.map_dwarf_register(vmctx_reg)? as u8;
|
||||||
writer.write_u8(gimli::constants::DW_OP_breg0.0 + reg)?;
|
writer.write_u8(gimli::constants::DW_OP_breg0.0 + reg)?;
|
||||||
let memory_offset = match frame_info.vmctx_memory_offset() {
|
let memory_offset = match frame_info.vmctx_memory_offset() {
|
||||||
@@ -189,7 +207,7 @@ fn append_memory_deref(
|
|||||||
};
|
};
|
||||||
writer.write_sleb128(memory_offset)?;
|
writer.write_sleb128(memory_offset)?;
|
||||||
}
|
}
|
||||||
ValueLoc::Stack(ss) => {
|
LabelValueLoc::ValueLoc(ValueLoc::Stack(ss)) => {
|
||||||
if let Some(ss_offset) = frame_info.stack_slots[ss].offset {
|
if let Some(ss_offset) = frame_info.stack_slots[ss].offset {
|
||||||
writer.write_op_breg(X86_64::RBP.0)?;
|
writer.write_op_breg(X86_64::RBP.0)?;
|
||||||
writer.write_sleb128(ss_offset as i64 + X86_64_STACK_OFFSET)?;
|
writer.write_sleb128(ss_offset as i64 + X86_64_STACK_OFFSET)?;
|
||||||
@@ -207,6 +225,31 @@ fn append_memory_deref(
|
|||||||
return Ok(false);
|
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);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
@@ -468,7 +511,7 @@ where
|
|||||||
let _ = code_chunk; // suppresses warning for final flush
|
let _ = code_chunk; // suppresses warning for final flush
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
// Find all landing pads by scanning bytes, do not care about
|
// Find all landing pads by scanning bytes, do not care about
|
||||||
// false location at this moment.
|
// false location at this moment.
|
||||||
// Looks hacky but it is fast; does not need to be really exact.
|
// Looks hacky but it is fast; does not need to be really exact.
|
||||||
@@ -653,7 +696,7 @@ struct CachedValueLabelRange {
|
|||||||
func_index: DefinedFuncIndex,
|
func_index: DefinedFuncIndex,
|
||||||
start: usize,
|
start: usize,
|
||||||
end: usize,
|
end: usize,
|
||||||
label_location: HashMap<ValueLabel, ValueLoc>,
|
label_location: HashMap<ValueLabel, LabelValueLoc>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ValueLabelRangesBuilder<'a, 'b> {
|
struct ValueLabelRangesBuilder<'a, 'b> {
|
||||||
@@ -1179,7 +1222,7 @@ mod tests {
|
|||||||
fn create_mock_value_ranges() -> (ValueLabelsRanges, (ValueLabel, ValueLabel, ValueLabel)) {
|
fn create_mock_value_ranges() -> (ValueLabelsRanges, (ValueLabel, ValueLabel, ValueLabel)) {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use wasmtime_environ::entity::EntityRef;
|
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 mut value_ranges = HashMap::new();
|
||||||
let value_0 = ValueLabel::new(0);
|
let value_0 = ValueLabel::new(0);
|
||||||
let value_1 = ValueLabel::new(1);
|
let value_1 = ValueLabel::new(1);
|
||||||
@@ -1187,7 +1230,7 @@ mod tests {
|
|||||||
value_ranges.insert(
|
value_ranges.insert(
|
||||||
value_0,
|
value_0,
|
||||||
vec![ValueLocRange {
|
vec![ValueLocRange {
|
||||||
loc: ValueLoc::Unassigned,
|
loc: LabelValueLoc::ValueLoc(ValueLoc::Unassigned),
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 25,
|
end: 25,
|
||||||
}],
|
}],
|
||||||
@@ -1195,7 +1238,7 @@ mod tests {
|
|||||||
value_ranges.insert(
|
value_ranges.insert(
|
||||||
value_1,
|
value_1,
|
||||||
vec![ValueLocRange {
|
vec![ValueLocRange {
|
||||||
loc: ValueLoc::Unassigned,
|
loc: LabelValueLoc::ValueLoc(ValueLoc::Unassigned),
|
||||||
start: 5,
|
start: 5,
|
||||||
end: 30,
|
end: 30,
|
||||||
}],
|
}],
|
||||||
@@ -1204,12 +1247,12 @@ mod tests {
|
|||||||
value_2,
|
value_2,
|
||||||
vec![
|
vec![
|
||||||
ValueLocRange {
|
ValueLocRange {
|
||||||
loc: ValueLoc::Unassigned,
|
loc: LabelValueLoc::ValueLoc(ValueLoc::Unassigned),
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 10,
|
end: 10,
|
||||||
},
|
},
|
||||||
ValueLocRange {
|
ValueLocRange {
|
||||||
loc: ValueLoc::Unassigned,
|
loc: LabelValueLoc::ValueLoc(ValueLoc::Unassigned),
|
||||||
start: 20,
|
start: 20,
|
||||||
end: 30,
|
end: 30,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
pub mod ir {
|
pub mod ir {
|
||||||
pub use cranelift_codegen::binemit::{Reloc, StackMap};
|
pub use cranelift_codegen::binemit::{Reloc, StackMap};
|
||||||
pub use cranelift_codegen::ir::{
|
pub use cranelift_codegen::ir::{
|
||||||
types, AbiParam, ArgumentPurpose, JumpTableOffsets, LibCall, Signature, SourceLoc,
|
types, AbiParam, ArgumentPurpose, JumpTableOffsets, LabelValueLoc, LibCall, Signature,
|
||||||
StackSlots, TrapCode, Type, ValueLabel, ValueLoc,
|
SourceLoc, StackSlots, TrapCode, Type, ValueLabel, ValueLoc,
|
||||||
};
|
};
|
||||||
pub use cranelift_codegen::{ValueLabelsRanges, ValueLocRange};
|
pub use cranelift_codegen::{ValueLabelsRanges, ValueLocRange};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,7 +137,11 @@ check: exited with status
|
|||||||
#[ignore]
|
#[ignore]
|
||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
any(target_os = "linux", target_os = "macos"),
|
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<()> {
|
pub fn test_debug_dwarf_ptr() -> Result<()> {
|
||||||
let output = lldb_with_script(
|
let output = lldb_with_script(
|
||||||
|
|||||||
@@ -114,7 +114,11 @@ check: DW_AT_decl_line (10)
|
|||||||
#[cfg(all(
|
#[cfg(all(
|
||||||
any(target_os = "linux", target_os = "macos"),
|
any(target_os = "linux", target_os = "macos"),
|
||||||
target_arch = "x86_64",
|
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<()> {
|
fn test_debug_dwarf5_translate_lines() -> Result<()> {
|
||||||
check_line_program(
|
check_line_program(
|
||||||
|
|||||||
Reference in New Issue
Block a user