Skip to content

Commit b03ee9d

Browse files
committed
[WIP] Codegen tests
gherrit-pr-id: G5964d13c9ffb7a47ed4662892ef92c90ddc51e12
1 parent 4fe6388 commit b03ee9d

File tree

8 files changed

+280
-3
lines changed

8 files changed

+280
-3
lines changed

.github/workflows/ci.yml

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,8 @@ jobs:
379379
$FEATURES \
380380
--verbose \
381381
-- \
382-
--skip ui
382+
--skip ui \
383+
--skip codegen
383384
384385
# Only run tests when targetting Linux x86 (32- or 64-bit) - we're
385386
# executing on Linux x86_64, so we can't run tests for any non-x86 target.
@@ -388,6 +389,23 @@ jobs:
388389
# Run compile tests when building for other targets.
389390
if: contains(matrix.target, 'linux') && (contains(matrix.target, 'x86_64') || contains(matrix.target, 'i686'))
390391

392+
- name: Run codegen tests
393+
env:
394+
TOOLCHAIN: ${{ matrix.toolchain }}
395+
CRATE: ${{ matrix.crate }}
396+
TARGET: ${{ matrix.target }}
397+
FEATURES: ${{ matrix.features }}
398+
run: |
399+
sudo apt install llvm > /dev/null
400+
./cargo.sh +nightly install cargo-show-asm > /dev/null
401+
./cargo.sh +$TOOLCHAIN test \
402+
--package $CRATE \
403+
--target $TARGET \
404+
$FEATURES \
405+
--verbose \
406+
--test codegen
407+
if: matrix.crate == 'zerocopy' && matrix.toolchain == 'nightly' && matrix.target == 'x86_64-unknown-linux-gnu' && matrix.features == '--all-features'
408+
391409
- name: Run UI tests
392410
env:
393411
TOOLCHAIN: ${{ matrix.toolchain }}
@@ -631,7 +649,9 @@ jobs:
631649
--lcov \
632650
--output-path lcov.info \
633651
--verbose \
634-
-- --skip ui
652+
-- \
653+
--skip ui \
654+
--skip codegen
635655
- name: Upload coverage to Codecov
636656
uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2
637657
with:

benches/formats/coco.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
use zerocopy_derive::*;
2+
3+
// The only valid value of this type is the byte `0xC0`
4+
#[derive(TryFromBytes, KnownLayout, Immutable)]
5+
#[repr(u8)]
6+
enum C0 {
7+
_XC0 = 0xC0,
8+
}
9+
10+
// The only valid value of this type is the bytes `0xC0C0`.
11+
#[derive(TryFromBytes, KnownLayout, Immutable)]
12+
#[repr(C)]
13+
struct C0C0(C0, C0);
14+
15+
#[derive(TryFromBytes, KnownLayout, Immutable)]
16+
#[repr(C)]
17+
pub struct Packet {
18+
magic_number: C0C0,
19+
mug_size: u8,
20+
temperature: u8,
21+
marshmallows: [[u8; 2]],
22+
}

benches/try_ref_from_bytes.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use zerocopy::TryFromBytes;
2+
3+
#[path = "formats/coco.rs"]
4+
mod coco;
5+
6+
#[unsafe(no_mangle)]
7+
fn codegen_test(source: &[u8]) -> Option<&coco::Packet> {
8+
TryFromBytes::try_ref_from_bytes(source).ok()
9+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
Iterations: 100
2+
Instructions: 1900
3+
Total Cycles: 608
4+
Total uOps: 2100
5+
6+
Dispatch Width: 4
7+
uOps Per Cycle: 3.45
8+
IPC: 3.13
9+
Block RThroughput: 6.0
10+
11+
12+
Instruction Info:
13+
[1]: #uOps
14+
[2]: Latency
15+
[3]: RThroughput
16+
[4]: MayLoad
17+
[5]: MayStore
18+
[6]: HasSideEffects (U)
19+
20+
[1] [2] [3] [4] [5] [6] Instructions:
21+
1 1 0.33 mov rdx, rsi
22+
1 1 0.33 cmp rsi, 4
23+
1 1 1.00 jb .LBB5_5
24+
1 1 0.50 lea rcx, [rdx - 4]
25+
1 1 0.33 mov rsi, rcx
26+
1 1 0.33 and rsi, -2
27+
1 1 0.33 add rsi, 4
28+
1 1 0.33 cmp rdx, rsi
29+
1 1 1.00 jne .LBB5_5
30+
1 1 0.33 mov rax, rdi
31+
2 6 0.50 * cmp byte ptr [rdi], -64
32+
1 1 1.00 jne .LBB5_5
33+
2 6 0.50 * cmp byte ptr [rax + 1], -64
34+
1 1 1.00 jne .LBB5_5
35+
1 1 0.50 shr rcx
36+
1 1 0.33 mov rdx, rcx
37+
1 1 1.00 U ret
38+
1 0 0.25 xor eax, eax
39+
1 1 1.00 U ret
40+
41+
42+
Resources:
43+
[0] - SBDivider
44+
[1] - SBFPDivider
45+
[2] - SBPort0
46+
[3] - SBPort1
47+
[4] - SBPort4
48+
[5] - SBPort5
49+
[6.0] - SBPort23
50+
[6.1] - SBPort23
51+
52+
53+
Resource pressure per iteration:
54+
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
55+
- - 5.98 5.98 - 6.04 1.00 1.00
56+
57+
Resource pressure by instruction:
58+
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
59+
- - 0.99 - - 0.01 - - mov rdx, rsi
60+
- - - 1.00 - - - - cmp rsi, 4
61+
- - - - - 1.00 - - jb .LBB5_5
62+
- - 1.00 - - - - - lea rcx, [rdx - 4]
63+
- - - 1.00 - - - - mov rsi, rcx
64+
- - - 1.00 - - - - and rsi, -2
65+
- - - 0.99 - 0.01 - - add rsi, 4
66+
- - - 1.00 - - - - cmp rdx, rsi
67+
- - - - - 1.00 - - jne .LBB5_5
68+
- - 1.00 - - - - - mov rax, rdi
69+
- - 0.99 - - 0.01 - 1.00 cmp byte ptr [rdi], -64
70+
- - - - - 1.00 - - jne .LBB5_5
71+
- - 1.00 - - - 1.00 - cmp byte ptr [rax + 1], -64
72+
- - - - - 1.00 - - jne .LBB5_5
73+
- - 1.00 - - - - - shr rcx
74+
- - - 0.99 - 0.01 - - mov rdx, rcx
75+
- - - - - 1.00 - - ret
76+
- - - - - - - - xor eax, eax
77+
- - - - - 1.00 - - ret

benches/try_transmute_ref.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
use zerocopy::try_transmute_ref;
2+
use zerocopy_derive::*;
3+
4+
#[path = "formats/coco.rs"]
5+
mod coco;
6+
7+
#[derive(IntoBytes, Unaligned, KnownLayout, Immutable)]
8+
#[repr(C)]
9+
struct MinimalViableSource {
10+
header: [u8; 4],
11+
trailer: [[u8; 2]],
12+
}
13+
14+
#[unsafe(no_mangle)]
15+
fn codegen_test(source: &MinimalViableSource) -> Option<&coco::Packet> {
16+
try_transmute_ref!(source).ok()
17+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
Iterations: 100
2+
Instructions: 900
3+
Total Cycles: 261
4+
Total uOps: 1000
5+
6+
Dispatch Width: 4
7+
uOps Per Cycle: 3.83
8+
IPC: 3.45
9+
Block RThroughput: 2.5
10+
11+
12+
Instruction Info:
13+
[1]: #uOps
14+
[2]: Latency
15+
[3]: RThroughput
16+
[4]: MayLoad
17+
[5]: MayStore
18+
[6]: HasSideEffects (U)
19+
20+
[1] [2] [3] [4] [5] [6] Instructions:
21+
1 1 0.33 mov rdx, rsi
22+
1 5 0.50 * movzx ecx, byte ptr [rdi]
23+
1 5 0.50 * movzx esi, byte ptr [rdi + 1]
24+
1 1 0.33 xor cl, -64
25+
1 1 0.33 xor sil, -64
26+
1 0 0.25 xor eax, eax
27+
1 1 0.33 or sil, cl
28+
2 2 0.67 cmove rax, rdi
29+
1 1 1.00 U ret
30+
31+
32+
Resources:
33+
[0] - SBDivider
34+
[1] - SBFPDivider
35+
[2] - SBPort0
36+
[3] - SBPort1
37+
[4] - SBPort4
38+
[5] - SBPort5
39+
[6.0] - SBPort23
40+
[6.1] - SBPort23
41+
42+
43+
Resource pressure per iteration:
44+
[0] [1] [2] [3] [4] [5] [6.0] [6.1]
45+
- - 2.00 2.50 - 2.50 1.00 1.00
46+
47+
Resource pressure by instruction:
48+
[0] [1] [2] [3] [4] [5] [6.0] [6.1] Instructions:
49+
- - - 0.01 - 0.99 - - mov rdx, rsi
50+
- - - - - - - 1.00 movzx ecx, byte ptr [rdi]
51+
- - - - - - 1.00 - movzx esi, byte ptr [rdi + 1]
52+
- - 0.49 0.51 - - - - xor cl, -64
53+
- - 0.51 - - 0.49 - - xor sil, -64
54+
- - - - - - - - xor eax, eax
55+
- - - 0.99 - 0.01 - - or sil, cl
56+
- - 1.00 0.99 - 0.01 - - cmove rax, rdi
57+
- - - - - 1.00 - - ret

tests/codegen.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// Copyright 2026 The Fuchsia Authors
2+
//
3+
// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
4+
// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
5+
// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
6+
// This file may not be copied, modified, or distributed except according to
7+
// those terms.
8+
9+
#![cfg(__ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS)]
10+
11+
use std::{path::PathBuf, process::Command};
12+
13+
fn run_codegen_test(bench_name: &str, target_cpu: &str, bless: bool) {
14+
let manifest_path = env!("CARGO_MANIFEST_PATH");
15+
let target_dir = env!("CARGO_TARGET_DIR");
16+
17+
let output = Command::new("cargo")
18+
.args([
19+
"asm",
20+
"-p",
21+
"zerocopy",
22+
"--manifest-path",
23+
manifest_path,
24+
"--target-dir",
25+
target_dir,
26+
"--bench",
27+
bench_name,
28+
"--target-cpu",
29+
target_cpu,
30+
"--mca",
31+
"codegen_test",
32+
])
33+
.output()
34+
.expect("failed to execute process");
35+
36+
let actual_result = output.stdout;
37+
38+
if !(output.status.success()) {
39+
panic!("{}", String::from_utf8_lossy(&output.stderr));
40+
}
41+
42+
let expected_file_path = {
43+
let mut path: PathBuf = env!("CARGO_MANIFEST_DIR").into();
44+
path.push("benches");
45+
let file_name = format!("{bench_name}.{target_cpu}.mca");
46+
path.push(file_name);
47+
path
48+
};
49+
50+
if bless {
51+
std::fs::write(expected_file_path, &actual_result).unwrap();
52+
} else {
53+
let expected_result = std::fs::read(expected_file_path).unwrap_or_default();
54+
if actual_result != expected_result {
55+
let expected = String::from_utf8_lossy(&expected_result[..]);
56+
panic!("Bless codegen tests with BLESS=1\nGot unexpected output:\n{}", expected);
57+
}
58+
}
59+
}
60+
61+
#[test]
62+
#[cfg_attr(miri, ignore)]
63+
fn codegen() {
64+
let bless = std::env::var("BLESS").ok().filter(|s| s == "1").is_some();
65+
let paths = std::fs::read_dir("benches").unwrap();
66+
for path in paths {
67+
let path = path.unwrap().path();
68+
if !path.extension().map(|s| s == "rs").unwrap_or(false) {
69+
continue;
70+
}
71+
let path = path.file_stem().unwrap().to_str().unwrap();
72+
run_codegen_test(path, "x86-64", bless);
73+
}
74+
}

tools/update-expected-test-output.sh

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010

1111
set -eo pipefail
1212

13-
# Update the `.stderr` reference files used to validate our UI tests.
13+
# Update the `.stderr` reference files used to validate our UI and codegen tests.
14+
BLESS=1 ./cargo.sh +nightly test --test codegen -p zerocopy --all-features
1415
BLESS=1 ./cargo.sh +nightly test --test ui -p zerocopy --all-features
1516
BLESS=1 ./cargo.sh +stable test --test ui -p zerocopy --features=__internal_use_only_features_that_work_on_stable
1617
BLESS=1 ./cargo.sh +msrv test --test ui -p zerocopy --features=__internal_use_only_features_that_work_on_stable

0 commit comments

Comments
 (0)