#![allow(dead_code)]
use std::process::Command;
pub fn nested_cargo() -> Command {
let mut cargo = Command::new(env!("CARGO"));
cargo
.env("RUSTFLAGS", "")
.env("RUSTDOCFLAGS", "")
.env_remove("CARGO_ENCODED_RUSTFLAGS")
.env_remove("CARGO_ENCODED_RUSTDOCFLAGS")
.env_remove("CARGO_BUILD_RUSTFLAGS")
.env_remove("CARGO_BUILD_RUSTDOCFLAGS")
.env_remove("RUSTC_WRAPPER")
.env_remove("RUSTC_WORKSPACE_WRAPPER")
.env_remove("LLVM_PROFILE_FILE");
for (key, _) in std::env::vars() {
if key.starts_with("CARGO_TARGET_") && key.ends_with("_RUSTFLAGS") {
cargo.env_remove(key);
}
}
cargo
}
pub fn flag_environment() -> String {
let mut found: Vec<String> = std::env::vars()
.filter(|(k, _)| {
k.contains("RUSTFLAGS")
|| k.contains("RUSTDOCFLAGS")
|| k.contains("PROFILE")
|| k.contains("COV")
|| k == "RUSTC"
|| k.contains("WRAPPER")
})
.map(|(k, v)| format!(" {}={}", k, v))
.collect();
found.sort();
if found.is_empty() {
" (nothing set)".to_owned()
} else {
found.join("\n")
}
}
pub fn is_instrumented(ir: &str) -> bool {
ir.contains("__profc")
}
pub fn call_target(line: &str) -> Option<&str> {
let head = &line[..line.find('(')?];
let mut tokens = head.split_whitespace();
tokens
.any(|token| token == "call" || token == "invoke")
.then(|| tokens.last())?
}
pub fn is_indirect_call(line: &str) -> bool {
call_target(line).is_some_and(|callee| callee.starts_with('%'))
}
pub fn body_of<'a>(ir: &'a str, name: &str) -> Option<Vec<&'a str>> {
let header = format!("@{}(", name);
let mut lines = ir
.lines()
.skip_while(|l| !(l.starts_with("define") && l.contains(&header)))
.skip(1);
let mut body = Vec::new();
for line in &mut lines {
let line = line.trim();
if line == "}" {
return Some(body);
}
if line.is_empty() || line.starts_with(';') || is_label(line) || is_continuation(line) {
continue;
}
body.push(line);
}
None
}
fn is_continuation(line: &str) -> bool {
line == "]"
|| (line.contains(", label %") && !line.contains(" br ") && !line.starts_with("br "))
}
fn is_label(line: &str) -> bool {
match line.split_once(':') {
Some((name, rest)) => {
!name.is_empty()
&& !name.contains(char::is_whitespace)
&& (rest.is_empty() || rest.trim_start().starts_with(';'))
}
None => false,
}
}
pub fn fast_path_of<'a>(ir: &'a str, name: &str) -> Option<Vec<&'a str>> {
let mut body: Vec<&str> = body_of(ir, name)?
.into_iter()
.filter(|line| !line.contains("@llvm.assume"))
.collect();
loop {
let mut read: Vec<&str> = Vec::new();
for line in &body {
let rhs = match line.split_once(" = ") {
Some((_, rhs)) => rhs,
None => line,
};
read.extend(registers(rhs));
}
let live: Vec<&str> = body
.iter()
.copied()
.filter(|line| match defined_register(line) {
Some(reg) => read.contains(®),
None => true,
})
.collect();
if live.len() == body.len() {
return Some(live);
}
body = live;
}
}
fn defined_register(line: &str) -> Option<&str> {
let (lhs, _) = line.split_once(" = ")?;
lhs.starts_with('%').then_some(lhs)
}
fn registers(fragment: &str) -> impl Iterator<Item = &str> {
fragment
.split(|c: char| !(c.is_alphanumeric() || c == '_' || c == '.' || c == '%'))
.filter(|token| token.starts_with('%') && token.len() > 1)
}
pub fn opcodes<'a>(body: &[&'a str]) -> Vec<&'a str> {
body.iter()
.map(|line| {
let instruction = line.split_once(" = ").map_or(*line, |(_, rhs)| rhs);
let opcode = instruction.split_whitespace().next().unwrap_or("");
match opcode {
"tail" | "musttail" | "notail" => "call",
other => other,
}
})
.collect()
}
pub fn called_symbols<'a>(body: &[&'a str]) -> Vec<&'a str> {
let mut names: Vec<&str> = body
.iter()
.filter_map(|line| call_target(line))
.filter_map(|callee| callee.strip_prefix('@'))
.map(|name| name.trim_matches('"'))
.filter(|name| !name.starts_with("llvm.") && !name.starts_with("anon."))
.collect();
names.sort_unstable();
names.dedup();
names
}