Skip to content

Commit 2f1ced7

Browse files
committed
Added tests to validate functions are hotpatchable
1 parent dfe6580 commit 2f1ced7

File tree

3 files changed

+115
-0
lines changed

3 files changed

+115
-0
lines changed

src/tools/run-make-support/src/external_deps/llvm.rs

+7
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,13 @@ impl LlvmFilecheck {
254254
self
255255
}
256256

257+
/// Specify the prefix (without :) for patterns to match. By default, these patterns are prefixed with "CHECK:".
258+
pub fn check_prefix(&mut self, prefix: &str) -> &mut Self {
259+
self.cmd.arg("--check-prefix");
260+
self.cmd.arg(prefix);
261+
self
262+
}
263+
257264
/// `--input-file` option.
258265
pub fn input_file<P: AsRef<Path>>(&mut self, input_file: P) -> &mut Self {
259266
self.cmd.arg("--input-file");

tests/run-make/hotpatch/lib.rs

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// hotpatch has two requirements:
2+
// 1. the first instruction of a functin must be at least two bytes long
3+
// 2. there must not be a jump to the first instruction
4+
5+
// the hotpatch flag should insert nops as needed to fullfil the requirements,
6+
// but only if the the function does not already fulfill them.
7+
// Over 99% of function in regular codebases already fulfill the conditions,
8+
// so its important to check that those are
9+
// unneccessarily affected
10+
11+
// ------------------------------------------------------------------------------------------------
12+
13+
// regularly this tailcall would jump to the first instruction the function
14+
// CHECK-LABEL: <tailcall_fn>:
15+
// CHECK: jne 0x0 <tailcall_fn>
16+
17+
// hotpatch insert nops so that the tailcall will not jump to the first instruction of the function
18+
// HOTPATCH-LABEL: <tailcall_fn>:
19+
// HOTPATCH-NOT: jne 0x0 <tailcall_fn>
20+
21+
#[no_mangle]
22+
pub fn tailcall_fn() {
23+
use std::sync::atomic::{AtomicUsize, Ordering};
24+
static COUNT: AtomicUsize = AtomicUsize::new(0);
25+
if COUNT.fetch_sub(1, Ordering::Relaxed) != 0 {
26+
tailcall_fn()
27+
}
28+
}
29+
30+
// ------------------------------------------------------------------------------------------------
31+
32+
// empty_fn just returns. Note that 'ret' is a single byte instruction, but hotpatch requires a two
33+
// or more byte instructions to be at the start of the functions.
34+
// Preferably we would also tests a different single byte instruction,
35+
// but I was not able to make rustc emit anything but 'ret'.
36+
37+
// CHECK-LABEL: <empty_fn>:
38+
// CHECK-NEXT: ret
39+
40+
// HOTPATCH-LABEL: <empty_fn>:
41+
// HOTPATCH-NOT: ret
42+
// HOTPATCH: ret
43+
44+
#[no_mangle]
45+
#[inline(never)]
46+
pub fn empty_fn() {}
47+
48+
// ------------------------------------------------------------------------------------------------
49+
50+
// return_42 should not be affected by hotpatch
51+
52+
// CHECK-LABEL: <return_42>:
53+
// CHECK-NEXT: 0:
54+
// CHECK-NEXT: ret
55+
56+
// HOTPATCH-LABEL: <return_42>:
57+
// HOTPATCH-NEXT: 0:
58+
// HOTPATCH-NEXT: ret
59+
60+
#[no_mangle]
61+
#[inline(never)]
62+
pub fn return_42() -> i32 {
63+
42
64+
}

tests/run-make/hotpatch/rmake.rs

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Check if hotpatch only makes the functions hotpachable that were not,
2+
// but leaving the other functions untouched
3+
// More details in lib.rs
4+
5+
//@ revisions: x32 x64
6+
//@[x32] only-x86
7+
//@[x64] only-x86_64
8+
// Reason: hotpatch is only implemented for X86
9+
10+
use run_make_support::{llvm, rfs, rustc};
11+
12+
fn main() {
13+
{
14+
rustc().input("lib.rs").crate_name("regular").crate_type("lib").opt_level("3").run();
15+
16+
let regular_dump = llvm::llvm_objdump()
17+
.arg("--disassemble-symbols=tailcall_fn,empty_fn,return_42")
18+
.input("libregular.rlib")
19+
.run();
20+
21+
llvm::llvm_filecheck().patterns("lib.rs").stdin_buf(regular_dump.stdout_utf8()).run();
22+
}
23+
24+
{
25+
rustc()
26+
.input("lib.rs")
27+
.crate_name("hotpatch")
28+
.crate_type("lib")
29+
.opt_level("3")
30+
.arg("-Zhotpatch")
31+
.run();
32+
33+
let hotpatch_dump = llvm::llvm_objdump()
34+
.arg("--disassemble-symbols=tailcall_fn,empty_fn,return_42")
35+
.input("libhotpatch.rlib")
36+
.run();
37+
38+
llvm::llvm_filecheck()
39+
.patterns("lib.rs")
40+
.check_prefix("HOTPATCH")
41+
.stdin_buf(hotpatch_dump.stdout_utf8())
42+
.run();
43+
}
44+
}

0 commit comments

Comments
 (0)