Skip to content

Commit

Permalink
Detect and render merged functions
Browse files Browse the repository at this point in the history
  • Loading branch information
pacak committed Oct 10, 2024
1 parent ce2e198 commit 9105a1d
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 16 deletions.
25 changes: 25 additions & 0 deletions .github/workflows/check-and-lint.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,15 @@ jobs:
- name: cdylib project, underscore prefix
run: cargo run -- --manifest-path sample_cdylib/Cargo.toml _mul

- name: merged functions
run: |
cargo run -- --manifest-path sample_merged/Cargo.toml sample_merged::merged_0
cargo run -- --manifest-path sample_merged/Cargo.toml sample_merged::merged_1
cargo run -- --manifest-path sample_merged/Cargo.toml sample_merged::one_num
cargo run -- --manifest-path sample_merged/Cargo.toml sample_merged::one_plus_one
cargo run -- --manifest-path sample_merged/Cargo.toml sample_merged::two_num
cargo run -- --manifest-path sample_merged/Cargo.toml sample_merged::two_minus_one
windows:
runs-on: windows-latest
name: Tests on windows
Expand Down Expand Up @@ -147,6 +156,15 @@ jobs:
- name: cdylib project, underscore prefix
run: cargo run -- --manifest-path sample_cdylib/Cargo.toml _mul

- name: merged functions
run: |
cargo run -- --manifest-path sample_merged/Cargo.toml sample_merged::merged_0
cargo run -- --manifest-path sample_merged/Cargo.toml sample_merged::merged_1
cargo run -- --manifest-path sample_merged/Cargo.toml sample_merged::one_num
cargo run -- --manifest-path sample_merged/Cargo.toml sample_merged::one_plus_one
cargo run -- --manifest-path sample_merged/Cargo.toml sample_merged::two_num
cargo run -- --manifest-path sample_merged/Cargo.toml sample_merged::two_minus_one
macos:
runs-on: macos-latest
name: Tests on MacOS
Expand Down Expand Up @@ -211,3 +229,10 @@ jobs:

- name: cdylib project, underscore prefix
run: cargo run -- --manifest-path sample_cdylib/Cargo.toml _mul

- name: merged functions
run: |
cargo run -- --manifest-path sample_merged/Cargo.toml sample_merged::one_num
cargo run -- --manifest-path sample_merged/Cargo.toml sample_merged::one_plus_one
cargo run -- --manifest-path sample_merged/Cargo.toml sample_merged::two_num
cargo run -- --manifest-path sample_merged/Cargo.toml sample_merged::two_minus_one
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
/target
/sample/target
/sample_rlib/target
/sample_cdylib/target
/*/target
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## [0.2.41] - Unreleased
- add release-lto profile for slightly smaller/faster version
thanks @zamazan4ik for the suggestion
- detect and render merged functions (#310)

## [0.2.40] - 2024-10-01
- more consistend behavior when only one item is detected (#312)
Expand Down
7 changes: 7 additions & 0 deletions sample_merged/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions sample_merged/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "sample_merged"
version = "0.1.0"
edition = "2021"

[dependencies]

[workspace]
37 changes: 37 additions & 0 deletions sample_merged/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#[inline(never)]
#[cfg(target_arch = "x86_64")]
pub fn merged_0() {
let simd_reg = unsafe {
std::arch::x86_64::_mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
};
std::hint::black_box(simd_reg);
}

#[inline(never)]
#[cfg(target_arch = "x86_64")]
pub fn merged_1() {
let simd_reg = unsafe {
std::arch::x86_64::_mm_set_epi8(15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
};
std::hint::black_box(simd_reg);
}

#[inline(never)]
pub fn two_num() -> u32 {
2
}

#[inline(never)]
pub fn one_num() -> u32 {
1
}

#[inline(never)]
pub fn one_plus_one() -> u32 {
1 + 1
}

#[inline(never)]
pub fn two_minus_one() -> u32 {
2 - 1
}
61 changes: 60 additions & 1 deletion src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,69 @@ pub fn find_items(lines: &[Statement]) -> BTreeMap<Item, Range<usize>> {
*name_entry += 1;
}
}
} else if let Some((name, range)) = merged_function(lines, ix) {
assert_eq!(item, None);

let sym = name;
if let Some(dem) = demangle::demangled(sym) {
let hashed = format!("{dem:?}");
let name = format!("{dem:#?}");
let name_entry = names.entry(name.clone()).or_insert(0);
res.insert(
Item {
mangled_name: sym.to_string(),
name,
hashed,
index: *name_entry,
len: range.len(),
non_blank_len: range.len(),
},
range,
);
*name_entry += 1;
}
}
}
res
}

/// CHeck if ix is a merged function.
///
/// merged function are defined (at least on Linux) as a sequence of 3
/// commands:
/// .globl _ZN13sample_merged3two17h0afab563317f9d7bE
/// .type _ZN13sample_merged3two17h0afab563317f9d7bE,@function
/// .set _ZN13sample_merged3two17h0afab563317f9d7bE, _ZN13sample_merged12one_plus_one17h408b56cb936d6f10E
///
/// check above looks for `.type..@function` followed by `.set ...`
///
/// except if we are on mac, where .type is absent:
/// .globl _ZN13sample_merged3two17h0afab563317f9d7bE
/// .set _ZN13sample_merged3two17h0afab563317f9d7bE, _ZN13sample_merged12one_plus_one17h408b56cb936d6f10E
///
/// So... We have to check both
fn merged_function<'a>(lines: &'a [Statement<'a>], ix: usize) -> Option<(&'a str, Range<usize>)> {
let Statement::Directive(Directive::SetValue(name, _val)) = lines.get(ix)? else {
return None;
};

if let Some(Statement::Directive(Directive::SymIsFun(sym))) = lines.get(ix.checked_sub(1)?) {
assert_eq!(sym, name);
Some((name, ix - 2..ix + 1))
} else if lines
.get(ix.checked_sub(1)?)
.map_or(false, |l| l.is_global())
{
Some((name, ix - 1..ix + 1))
} else {
None
}

//if let Some(Statement::Directive(Directive::SymIsFun(sym))),
// Statement::Directive(Directive::SetValue(name, _val)),
// ) = (ix.checked_sub(1).and_then(|prev| lines.get(prev)), line)
}

/// Handles the non-mangled labels found in the given lines of ASM statements.
///
/// Returns item if the label is a valid function item, otherwise returns None.
Expand Down Expand Up @@ -197,7 +255,8 @@ fn used_labels<'a>(stmts: &'_ [Statement<'a>]) -> BTreeSet<&'a str> {
Directive::File(_)
| Directive::Loc(_)
| Directive::SubsectionsViaSym
| Directive::Set(_) => None,
| Directive::SymIsFun(_) => None,
Directive::SetValue(_, val) => Some(*val),
Directive::Generic(g) => Some(g.0),
Directive::SectionStart(ss) => Some(*ss),
},
Expand Down
74 changes: 62 additions & 12 deletions src/asm/statements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use std::sync::OnceLock;

use nom::branch::alt;
use nom::bytes::complete::{escaped_transform, tag, take_while1, take_while_m_n};
use nom::character::complete;
use nom::character::complete::{newline, none_of, not_line_ending, one_of, space1};
use nom::character::complete::{self, newline, none_of, not_line_ending, one_of, space0, space1};
use nom::combinator::{map, opt, recognize, value, verify};
use nom::multi::count;
use nom::sequence::{delimited, pair, preceded, terminated, tuple};
Expand All @@ -17,7 +16,7 @@ use crate::demangle::LabelKind;
use crate::opts::NameDisplay;
use crate::{color, demangle};

#[derive(Clone, Debug)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Statement<'a> {
Label(Label<'a>),
Directive(Directive<'a>),
Expand All @@ -26,7 +25,7 @@ pub enum Statement<'a> {
Dunno(&'a str),
}

#[derive(Clone, Debug)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Instruction<'a> {
pub op: &'a str,
pub args: Option<&'a str>,
Expand Down Expand Up @@ -77,6 +76,10 @@ impl<'a> Statement<'a> {
});
return !reg.is_match(x);
}

if let Statement::Directive(Directive::SetValue(_, _)) = self {
return false;
}
if let Statement::Directive(Directive::SectionStart(name)) = self {
if name.starts_with(".data") || name.starts_with(".rodata") {
return false;
Expand Down Expand Up @@ -136,8 +139,16 @@ impl std::fmt::Display for Directive<'_> {
Directive::File(ff) => ff.fmt(f),
Directive::Loc(l) => l.fmt(f),
Directive::Generic(g) => g.fmt(f),
Directive::Set(g) => {
f.write_str(&format!(".set {}", color!(g, OwoColorize::bright_cyan)))
Directive::SetValue(key, val) => {
let key = demangle::contents(key, display);
let val = demangle::contents(val, display);
write!(
f,
".{} {}, {}",
color!("set", OwoColorize::bright_magenta),
color!(key, OwoColorize::bright_cyan),
color!(val, OwoColorize::bright_cyan)
)
}
Directive::SectionStart(s) => {
let dem = demangle::contents(s, display);
Expand All @@ -148,6 +159,14 @@ impl std::fmt::Display for Directive<'_> {
".{}",
color!("subsections_via_symbols", OwoColorize::bright_red)
),
Directive::SymIsFun(s) => {
let dem = demangle::contents(s, display);
write!(
f,
".{} {dem},@function",
color!("type", OwoColorize::bright_magenta)
)
}
}
}
}
Expand Down Expand Up @@ -674,17 +693,31 @@ fn test_parse_file() {
);
}

#[derive(Clone, Debug)]
#[test]
fn parse_function_alias() {
assert_eq!(
parse_statement("\t.type\ttwo,@function\n").unwrap().1,
Statement::Directive(Directive::SymIsFun("two"))
);

assert_eq!(
parse_statement(".set\ttwo,\tone_plus_one\n").unwrap().1,
Statement::Directive(Directive::SetValue("two", "one_plus_one"))
)
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Directive<'a> {
File(File<'a>),
Loc(Loc<'a>),
Generic(GenericDirective<'a>),
Set(&'a str),
SymIsFun(&'a str),
SetValue(&'a str, &'a str),
SubsectionsViaSym,
SectionStart(&'a str),
}

#[derive(Clone, Debug)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct GenericDirective<'a>(pub &'a str);

pub fn parse_statement(input: &str) -> IResult<&str, Statement> {
Expand All @@ -702,8 +735,15 @@ pub fn parse_statement(input: &str) -> IResult<&str, Statement> {
Directive::Generic(GenericDirective(s))
});
let set = map(
preceded(tag(".set"), take_while1(|c| c != '\n')),
Directive::Set,
tuple((
tag(".set"),
space1,
take_while1(good_for_label),
tag(","),
space0,
take_while1(|c| c != '\n'),
)),
|(_, _, name, _, _, val)| Directive::SetValue(name, val),
);
let ssvs = map(tag(".subsections_via_symbols"), |_| {
Directive::SubsectionsViaSym
Expand All @@ -717,8 +757,18 @@ pub fn parse_statement(input: &str) -> IResult<&str, Statement> {
Statement::Nothing
});

let typ = map(
tuple((
tag("\t.type"),
space1,
take_while1(good_for_label),
tag(",@function"),
)),
|(_, _, id, _)| Directive::SymIsFun(id),
);

let dir = map(
alt((file, loc, set, ssvs, section, generic)),
alt((file, loc, set, ssvs, section, typ, generic)),
Statement::Directive,
);

Expand Down

0 comments on commit 9105a1d

Please sign in to comment.