Skip to content

Commit 134cfcb

Browse files
committed
Initial assembly test setup
1 parent a10fec8 commit 134cfcb

File tree

6 files changed

+149
-0
lines changed

6 files changed

+149
-0
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ members = [
77
"block2",
88
"block-sys",
99
"tests",
10+
"tests/assembly/*",
1011
]

tests/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ build = "build.rs"
1818
#
1919
# Also, they're slower than most tests.
2020
ui = ["trybuild"]
21+
assembly = ["cargo_metadata"]
2122

2223
[dependencies]
2324
block2 = { path = "../block2" }
@@ -29,9 +30,14 @@ objc2-foundation = { path = "../objc2-foundation" }
2930

3031
# Put here instead of dev-dependencies because we want to make it optional
3132
trybuild = { version = "1.0", optional = true }
33+
cargo_metadata = { version = "0.14", optional = true }
3234

3335
[build-dependencies]
3436
cc = "1.0"
3537

3638
[dev-dependencies]
3739
paste = "1.0"
40+
41+
[[bin]]
42+
name = "test_assembly"
43+
required-features = ["assembly"]
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[package]
2+
name = "test_msg_send_zero_cost"
3+
version = "0.1.0"
4+
edition = "2018"
5+
publish = false
6+
7+
[lib]
8+
path = "lib.rs"
9+
10+
[dependencies]
11+
objc2 = { path = "../../../objc2" }
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//! Test that the inner part of msg_send! is inlined into an objc_msgSend
2+
3+
use objc2::runtime::{Object, Sel};
4+
use objc2::MessageReceiver;
5+
6+
#[no_mangle]
7+
pub fn handle(obj: &Object, sel: Sel) -> *mut Object {
8+
unsafe { MessageReceiver::send_message(&obj, sel, ()).unwrap() }
9+
}

tests/build.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,8 @@ fn main() {
2222
builder.flag(flag);
2323
}
2424

25+
// For assembly tests
26+
println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap());
27+
2528
builder.compile("libencode_utils.a");
2629
}

tests/src/bin/test_assembly.rs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//! A helper script for testing the assembly output.
2+
//!
3+
//! Similar to `trybuild` and `compiletest`, except specialized to our setup!
4+
5+
use cargo_metadata::Message;
6+
use std::env;
7+
use std::env::args;
8+
use std::fmt::Write;
9+
use std::fs;
10+
use std::io;
11+
use std::path::Path;
12+
use std::process::{Command, Stdio};
13+
14+
fn strip_section(data: &str, section: &str) -> String {
15+
let mut res = String::with_capacity(data.len());
16+
let mut in_removed_section = false;
17+
for line in data.lines() {
18+
// This only works for the __LLVM sections we're interested in
19+
if line == "" {
20+
in_removed_section = false;
21+
}
22+
if line.trim().starts_with(".section") {
23+
in_removed_section = line.contains(section);
24+
}
25+
if !in_removed_section {
26+
res.push_str(line);
27+
} else {
28+
write!(res, "; Stripped {section} line").unwrap();
29+
}
30+
res.push('\n');
31+
}
32+
res
33+
}
34+
35+
fn read_assembly<P: AsRef<Path>>(path: P) -> io::Result<String> {
36+
let s = fs::read_to_string(path)?;
37+
let workspace_dir = Path::new(env!("CARGO_MANIFEST_DIR"))
38+
.parent()
39+
.unwrap()
40+
.as_os_str()
41+
.to_str()
42+
.unwrap();
43+
let s = s.replace(workspace_dir, "$WORKSPACE");
44+
// We remove the __LLVM,__bitcode and __LLVM,__cmdline sections because
45+
// they're uninteresting for out use-case.
46+
//
47+
// See https://github.com/rust-lang/rust/blob/1.59.0/compiler/rustc_codegen_llvm/src/back/write.rs#L978-L1074
48+
Ok(strip_section(&s, "__LLVM"))
49+
}
50+
51+
fn main() {
52+
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
53+
let should_overwrite = option_env!("TEST_OVERWRITE").is_some();
54+
let host = env!("TARGET");
55+
56+
for entry in manifest_dir.join("assembly").read_dir().unwrap() {
57+
let package_path = entry.unwrap().path();
58+
let package = package_path.file_name().unwrap().to_str().unwrap();
59+
60+
println!("Testing {package}.");
61+
62+
let result = Command::new(std::env::var("CARGO").unwrap_or("cargo".into()))
63+
// .arg("+nightly")
64+
// .arg("-Zbuild-std")
65+
// .arg("-vv")
66+
.arg("rustc")
67+
.arg(format!("--package={package}"))
68+
.args(args().skip(2))
69+
.arg("--release")
70+
.arg("--message-format=json-render-diagnostics")
71+
.arg("--")
72+
.arg("--emit=asm")
73+
.stdout(Stdio::piped())
74+
.stderr(Stdio::inherit())
75+
.output()
76+
.unwrap();
77+
78+
let artifact = Message::parse_stream(&*result.stdout)
79+
.find_map(|message| {
80+
if let Message::CompilerArtifact(artifact) = message.unwrap() {
81+
// Brittle!
82+
if artifact.target.name == package && artifact.filenames.len() == 2 {
83+
let path = artifact.filenames[1].clone();
84+
let stem = path.file_stem().unwrap().strip_prefix("lib").unwrap();
85+
return Some(path.with_file_name(format!("{stem}.s")));
86+
}
87+
}
88+
None
89+
})
90+
.unwrap_or_else(|| {
91+
panic!(
92+
"Could not find package data:\n{}",
93+
String::from_utf8_lossy(&result.stdout)
94+
)
95+
});
96+
97+
// Very brittle!
98+
let target = artifact
99+
.components()
100+
.map(|component| component.as_str())
101+
.skip_while(|&component| component != "target")
102+
.skip(1)
103+
.next()
104+
.unwrap_or(host);
105+
106+
println!("Target {target}.");
107+
108+
let expected_file = package_path.join("expected").join(format!("{target}.s"));
109+
110+
let actual = read_assembly(&artifact).unwrap();
111+
if should_overwrite {
112+
fs::write(expected_file, actual).unwrap();
113+
} else if let Ok(expected) = read_assembly(expected_file) {
114+
assert_eq!(expected, actual);
115+
} else {
116+
panic!("Missing assembly output for target {}:\n{}", target, actual);
117+
}
118+
}
119+
}

0 commit comments

Comments
 (0)