Skip to content

Commit ab360ea

Browse files
authored
Merge pull request #572 from rust-lang/fix/volatile
Fix volatile loads and stores
2 parents 764770d + b9cb8eb commit ab360ea

File tree

3 files changed

+126
-7
lines changed

3 files changed

+126
-7
lines changed

Diff for: libgccjit.version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
29901846ff610daab8a80436cfe36e93b4b5aa1e
1+
50d1270fd6409407f38b982e606df1dba4bf58ed

Diff for: src/builder.rs

+12-6
Original file line numberDiff line numberDiff line change
@@ -1106,18 +1106,24 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
11061106
val: RValue<'gcc>,
11071107
ptr: RValue<'gcc>,
11081108
align: Align,
1109-
_flags: MemFlags,
1109+
flags: MemFlags,
11101110
) -> RValue<'gcc> {
11111111
let ptr = self.check_store(val, ptr);
11121112
let destination = ptr.dereference(self.location);
11131113
// NOTE: libgccjit does not support specifying the alignment on the assignment, so we cast
11141114
// to type so it gets the proper alignment.
11151115
let destination_type = destination.to_rvalue().get_type().unqualified();
1116-
let aligned_type = destination_type.get_aligned(align.bytes()).make_pointer();
1117-
let aligned_destination = self.cx.context.new_bitcast(self.location, ptr, aligned_type);
1118-
let aligned_destination = aligned_destination.dereference(self.location);
1119-
self.llbb().add_assignment(self.location, aligned_destination, val);
1120-
// TODO(antoyo): handle align and flags.
1116+
let align = if flags.contains(MemFlags::UNALIGNED) { 1 } else { align.bytes() };
1117+
let mut modified_destination_type = destination_type.get_aligned(align);
1118+
if flags.contains(MemFlags::VOLATILE) {
1119+
modified_destination_type = modified_destination_type.make_volatile();
1120+
}
1121+
1122+
let modified_ptr =
1123+
self.cx.context.new_cast(self.location, ptr, modified_destination_type.make_pointer());
1124+
let modified_destination = modified_ptr.dereference(self.location);
1125+
self.llbb().add_assignment(self.location, modified_destination, val);
1126+
// TODO(antoyo): handle `MemFlags::NONTEMPORAL`.
11211127
// NOTE: dummy value here since it's never used. FIXME(antoyo): API should not return a value here?
11221128
self.cx.context.new_rvalue_zero(self.type_i32())
11231129
}

Diff for: tests/run/volatile2.rs

+113
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Compiler:
2+
//
3+
// Run-time:
4+
// status: 0
5+
6+
mod libc {
7+
#[link(name = "c")]
8+
extern "C" {
9+
pub fn puts(s: *const u8) -> i32;
10+
11+
pub fn sigaction(signum: i32, act: *const sigaction, oldact: *mut sigaction) -> i32;
12+
pub fn mmap(addr: *mut (), len: usize, prot: i32, flags: i32, fd: i32, offset: i64) -> *mut ();
13+
pub fn mprotect(addr: *mut (), len: usize, prot: i32) -> i32;
14+
}
15+
16+
pub const PROT_READ: i32 = 1;
17+
pub const PROT_WRITE: i32 = 2;
18+
pub const MAP_PRIVATE: i32 = 0x0002;
19+
pub const MAP_ANONYMOUS: i32 = 0x0020;
20+
pub const MAP_FAILED: *mut u8 = !0 as *mut u8;
21+
22+
/// glibc sigaction
23+
#[repr(C)]
24+
pub struct sigaction {
25+
pub sa_sigaction: Option<unsafe extern "C" fn(i32, *mut (), *mut ())>,
26+
pub sa_mask: [u32; 32],
27+
pub sa_flags: i32,
28+
pub sa_restorer: Option<unsafe extern "C" fn()>,
29+
}
30+
31+
pub const SA_SIGINFO: i32 = 0x00000004;
32+
pub const SIGSEGV: i32 = 11;
33+
}
34+
35+
static mut COUNT: u32 = 0;
36+
static mut STORAGE: *mut u8 = core::ptr::null_mut();
37+
const PAGE_SIZE: usize = 1 << 15;
38+
39+
fn main() {
40+
unsafe {
41+
// Register a segfault handler
42+
libc::sigaction(
43+
libc::SIGSEGV,
44+
&libc::sigaction {
45+
sa_sigaction: Some(segv_handler),
46+
sa_flags: libc::SA_SIGINFO,
47+
..core::mem::zeroed()
48+
},
49+
core::ptr::null_mut(),
50+
);
51+
52+
STORAGE = libc::mmap(
53+
core::ptr::null_mut(),
54+
PAGE_SIZE * 2,
55+
0,
56+
libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
57+
-1,
58+
0,
59+
).cast();
60+
if STORAGE == libc::MAP_FAILED {
61+
panic!("error: mmap failed");
62+
}
63+
64+
let p_count = (&mut COUNT) as *mut u32;
65+
p_count.write_volatile(0);
66+
67+
// Trigger segfaults
68+
STORAGE.add(0).write_volatile(1);
69+
STORAGE.add(PAGE_SIZE).write_volatile(1);
70+
STORAGE.add(0).write_volatile(1);
71+
STORAGE.add(PAGE_SIZE).write_volatile(1);
72+
STORAGE.add(0).write_volatile(1);
73+
STORAGE.add(PAGE_SIZE).write_volatile(1);
74+
STORAGE.add(0).read_volatile();
75+
STORAGE.add(PAGE_SIZE).read_volatile();
76+
STORAGE.add(0).read_volatile();
77+
STORAGE.add(PAGE_SIZE).read_volatile();
78+
STORAGE.add(0).read_volatile();
79+
STORAGE.add(PAGE_SIZE).read_volatile();
80+
STORAGE.add(0).write_volatile(1);
81+
STORAGE.add(PAGE_SIZE).write_volatile(1);
82+
83+
// The segfault handler should have been called for every `write_volatile` and
84+
// `read_volatile` in `STORAGE`. If the compiler ignores volatility, some of these writes
85+
// will be combined, causing a different number of segfaults.
86+
//
87+
// This `p_count` read is done by a volatile read. If the compiler
88+
// ignores volatility, the compiler will speculate that `*p_count` is
89+
// unchanged and remove this check, failing the test.
90+
if p_count.read_volatile() != 14 {
91+
panic!("error: segfault count mismatch: {}", p_count.read_volatile());
92+
}
93+
}
94+
}
95+
96+
unsafe extern "C" fn segv_handler(_: i32, _: *mut (), _: *mut ()) {
97+
let p_count = (&mut COUNT) as *mut u32;
98+
p_count.write_volatile(p_count.read_volatile() + 1);
99+
let count = p_count.read_volatile();
100+
101+
// Toggle the protected page so that the handler will be called for
102+
// each `write_volatile`
103+
libc::mprotect(
104+
STORAGE.cast(),
105+
PAGE_SIZE,
106+
if count % 2 == 1 { libc::PROT_READ | libc::PROT_WRITE } else { 0 },
107+
);
108+
libc::mprotect(
109+
STORAGE.add(PAGE_SIZE).cast(),
110+
PAGE_SIZE,
111+
if count % 2 == 0 { libc::PROT_READ | libc::PROT_WRITE } else { 0 },
112+
);
113+
}

0 commit comments

Comments
 (0)