Skip to content

Commit

Permalink
Merge pull request #319 from pacak/merged
Browse files Browse the repository at this point in the history
Support merged functions
  • Loading branch information
pacak authored Oct 10, 2024
2 parents 88cc4b2 + 5c0f572 commit 757d1c7
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 31 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
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,8 @@ path = "src/main.rs"

[workspace.metadata.cauwugo]
bpaf = true

[profile.release-lto]
lto = true
codegen-units = 1
inherits = "release"
5 changes: 5 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Change Log

## [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)
thanks @zheland
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
}
62 changes: 60 additions & 2 deletions src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ pub fn find_items(lines: &[Statement]) -> BTreeMap<Item, Range<usize>> {
let mut names = BTreeMap::new();

for (ix, line) in lines.iter().enumerate() {
#[allow(clippy::if_same_then_else)]
if line.is_section_start() {
if item.is_none() {
sec_start = ix;
Expand Down Expand Up @@ -106,11 +105,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 +254,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
94 changes: 68 additions & 26 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,20 +139,34 @@ 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);
f.write_str(&format!(
"{} {dem}",
color!(".section", OwoColorize::bright_red),
))
write!(f, "{} {dem}", color!(".section", OwoColorize::bright_red))
}
Directive::SubsectionsViaSym => f.write_str(&format!(
Directive::SubsectionsViaSym => write!(
f,
".{}",
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 @@ -676,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 @@ -704,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 @@ -719,20 +757,24 @@ 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,
);

// use terminated on the subparsers so that if the subparser doesn't consume the whole line, it's discarded
// we assume that each label/instruction/directive will only take one line
alt((
terminated(label, newline),
terminated(dir, newline),
terminated(instr, newline),
terminated(nothing, newline),
terminated(dunno, newline),
))(input)
terminated(alt((label, dir, instr, nothing, dunno)), newline)(input)
}

fn good_for_label(c: char) -> bool {
Expand All @@ -751,7 +793,7 @@ impl Statement<'_> {
pub(crate) fn is_global(&self) -> bool {
match self {
Statement::Directive(Directive::Generic(GenericDirective(dir))) => {
dir.starts_with("globl\t")
dir.starts_with("globl\t") || dir.starts_with("global\t")
}
_ => false,
}
Expand Down

0 comments on commit 757d1c7

Please sign in to comment.