Skip to content

Commit 129c889

Browse files
authored
Fix unaligned edge access (#232)
We load/store edges using ptr::{read,write}_unaligned on x86 and x86_64 to workaround unaligned edges in code roots.
1 parent 91e163a commit 129c889

File tree

6 files changed

+120
-21
lines changed

6 files changed

+120
-21
lines changed

mmtk/src/api.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ pub extern "C" fn mmtk_object_reference_write_pre(
343343
) {
344344
mutator
345345
.barrier()
346-
.object_reference_write_pre(src, slot, target);
346+
.object_reference_write_pre(src, slot.into(), target);
347347
}
348348

349349
/// Full post barrier
@@ -356,7 +356,7 @@ pub extern "C" fn mmtk_object_reference_write_post(
356356
) {
357357
mutator
358358
.barrier()
359-
.object_reference_write_post(src, slot, target);
359+
.object_reference_write_post(src, slot.into(), target);
360360
}
361361

362362
/// Barrier slow-path call
@@ -369,7 +369,7 @@ pub extern "C" fn mmtk_object_reference_write_slow(
369369
) {
370370
mutator
371371
.barrier()
372-
.object_reference_write_slow(src, slot, target);
372+
.object_reference_write_slow(src, slot.into(), target);
373373
}
374374

375375
/// Array-copy pre-barrier
@@ -383,7 +383,7 @@ pub extern "C" fn mmtk_array_copy_pre(
383383
let bytes = count << LOG_BYTES_IN_ADDRESS;
384384
mutator
385385
.barrier()
386-
.memory_region_copy_pre(src..src + bytes, dst..dst + bytes);
386+
.memory_region_copy_pre((src..src + bytes).into(), (dst..dst + bytes).into());
387387
}
388388

389389
/// Array-copy post-barrier
@@ -397,7 +397,7 @@ pub extern "C" fn mmtk_array_copy_post(
397397
let bytes = count << LOG_BYTES_IN_ADDRESS;
398398
mutator
399399
.barrier()
400-
.memory_region_copy_post(src..src + bytes, dst..dst + bytes);
400+
.memory_region_copy_post((src..src + bytes).into(), (dst..dst + bytes).into());
401401
}
402402

403403
/// C2 Slowpath allocation barrier

mmtk/src/edges.rs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
use std::ops::Range;
2+
3+
use mmtk::{
4+
util::{Address, ObjectReference},
5+
vm::edge_shape::{AddressRangeIterator, Edge, MemorySlice},
6+
};
7+
8+
/// The type of edges in OpenJDK.
9+
/// Currently it has the same layout as `Address`, but we override its load and store methods.
10+
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
11+
#[repr(transparent)]
12+
pub struct OpenJDKEdge {
13+
pub addr: Address,
14+
}
15+
16+
impl From<Address> for OpenJDKEdge {
17+
fn from(value: Address) -> Self {
18+
Self { addr: value }
19+
}
20+
}
21+
22+
impl Edge for OpenJDKEdge {
23+
fn load(&self) -> ObjectReference {
24+
if cfg!(any(target_arch = "x86", target_arch = "x86_64")) {
25+
// Workaround: On x86 (including x86_64), machine instructions may contain pointers as
26+
// immediates, and they may be unaligned. It is an undefined behavior in Rust to
27+
// dereference unaligned pointers. We have to explicitly use unaligned memory access
28+
// methods. On x86, ordinary MOV instructions can load and store memory at unaligned
29+
// addresses, so we expect `ptr.read_unaligned()` to have no performance penalty over
30+
// `ptr.read()` if `ptr` is actually aligned.
31+
unsafe {
32+
let ptr = self.addr.to_ptr::<ObjectReference>();
33+
ptr.read_unaligned()
34+
}
35+
} else {
36+
unsafe { self.addr.load() }
37+
}
38+
}
39+
40+
fn store(&self, object: ObjectReference) {
41+
if cfg!(any(target_arch = "x86", target_arch = "x86_64")) {
42+
unsafe {
43+
let ptr = self.addr.to_mut_ptr::<ObjectReference>();
44+
ptr.write_unaligned(object)
45+
}
46+
} else {
47+
unsafe { self.addr.store(object) }
48+
}
49+
}
50+
}
51+
52+
/// A range of OpenJDKEdge, usually used for arrays.
53+
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
54+
pub struct OpenJDKEdgeRange {
55+
range: Range<Address>,
56+
}
57+
58+
impl From<Range<Address>> for OpenJDKEdgeRange {
59+
fn from(value: Range<Address>) -> Self {
60+
Self { range: value }
61+
}
62+
}
63+
64+
pub struct OpenJDKEdgeRangeIterator {
65+
inner: AddressRangeIterator,
66+
}
67+
68+
impl Iterator for OpenJDKEdgeRangeIterator {
69+
type Item = OpenJDKEdge;
70+
71+
fn next(&mut self) -> Option<Self::Item> {
72+
self.inner.next().map(|a| a.into())
73+
}
74+
}
75+
76+
// Note that we cannot implement MemorySlice for `Range<OpenJDKEdgeRange>` because neither
77+
// `MemorySlice` nor `Range<T>` are defined in the `mmtk-openjdk` crate. ("orphan rule")
78+
impl MemorySlice for OpenJDKEdgeRange {
79+
type Edge = OpenJDKEdge;
80+
type EdgeIterator = OpenJDKEdgeRangeIterator;
81+
82+
fn iter_edges(&self) -> Self::EdgeIterator {
83+
OpenJDKEdgeRangeIterator {
84+
inner: self.range.iter_edges(),
85+
}
86+
}
87+
88+
fn object(&self) -> Option<ObjectReference> {
89+
self.range.object()
90+
}
91+
92+
fn start(&self) -> Address {
93+
self.range.start()
94+
}
95+
96+
fn bytes(&self) -> usize {
97+
self.range.bytes()
98+
}
99+
100+
fn copy(src: &Self, tgt: &Self) {
101+
MemorySlice::copy(&src.range, &tgt.range)
102+
}
103+
}

mmtk/src/gc_work.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ impl<F: RootsWorkFactory<OpenJDKEdge>> GCWork<OpenJDK> for ScanCodeCacheRoots<F>
5959
let mut edges = Vec::with_capacity(crate::CODE_CACHE_ROOTS_SIZE.load(Ordering::Relaxed));
6060
for roots in (*crate::CODE_CACHE_ROOTS.lock().unwrap()).values() {
6161
for r in roots {
62-
edges.push(*r)
62+
edges.push((*r).into())
6363
}
6464
}
6565
// Create work packet

mmtk/src/lib.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
extern crate lazy_static;
33

44
use std::collections::HashMap;
5-
use std::ops::Range;
65
use std::ptr::null_mut;
76
use std::sync::atomic::AtomicUsize;
87
use std::sync::Mutex;
98

9+
use edges::{OpenJDKEdge, OpenJDKEdgeRange};
1010
use libc::{c_char, c_void, uintptr_t};
1111
use mmtk::util::alloc::AllocationError;
1212
use mmtk::util::opaque_pointer::*;
@@ -19,6 +19,7 @@ pub mod active_plan;
1919
pub mod api;
2020
mod build_info;
2121
pub mod collection;
22+
mod edges;
2223
mod gc_work;
2324
pub mod object_model;
2425
mod object_scanning;
@@ -135,13 +136,6 @@ pub static FREE_LIST_ALLOCATOR_SIZE: uintptr_t =
135136
#[derive(Default)]
136137
pub struct OpenJDK;
137138

138-
/// The type of edges in OpenJDK.
139-
///
140-
/// TODO: We currently make it an alias to Address to make the change minimal.
141-
/// If we support CompressedOOPs, we should define an enum type to support both
142-
/// compressed and uncompressed OOPs.
143-
pub type OpenJDKEdge = Address;
144-
145139
impl VMBinding for OpenJDK {
146140
type VMObjectModel = object_model::VMObjectModel;
147141
type VMScanning = scanning::VMScanning;
@@ -150,7 +144,7 @@ impl VMBinding for OpenJDK {
150144
type VMReferenceGlue = reference_glue::VMReferenceGlue;
151145

152146
type VMEdge = OpenJDKEdge;
153-
type VMMemorySlice = Range<Address>;
147+
type VMMemorySlice = OpenJDKEdgeRange;
154148

155149
const MIN_ALIGNMENT: usize = 8;
156150
const MAX_ALIGNMENT: usize = 8;

mmtk/src/object_scanning.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ impl OopIterate for OopMapBlock {
1616
fn oop_iterate(&self, oop: Oop, closure: &mut impl EdgeVisitor<OpenJDKEdge>) {
1717
let start = oop.get_field_address(self.offset);
1818
for i in 0..self.count as usize {
19-
let edge = start + (i << LOG_BYTES_IN_ADDRESS);
19+
let edge = (start + (i << LOG_BYTES_IN_ADDRESS)).into();
2020
closure.visit_edge(edge);
2121
}
2222
}
@@ -66,7 +66,7 @@ impl OopIterate for InstanceMirrorKlass {
6666
let len = Self::static_oop_field_count(oop);
6767
let slice = unsafe { slice::from_raw_parts(start, len as _) };
6868
for oop in slice {
69-
closure.visit_edge(Address::from_ref(oop as &Oop));
69+
closure.visit_edge(Address::from_ref(oop as &Oop).into());
7070
}
7171
}
7272
}
@@ -88,7 +88,7 @@ impl OopIterate for ObjArrayKlass {
8888
fn oop_iterate(&self, oop: Oop, closure: &mut impl EdgeVisitor<OpenJDKEdge>) {
8989
let array = unsafe { oop.as_array_oop() };
9090
for oop in unsafe { array.data::<Oop>(BasicType::T_OBJECT) } {
91-
closure.visit_edge(Address::from_ref(oop as &Oop));
91+
closure.visit_edge(Address::from_ref(oop as &Oop).into());
9292
}
9393
}
9494
}
@@ -133,9 +133,9 @@ impl InstanceRefKlass {
133133
}
134134
fn process_ref_as_strong(oop: Oop, closure: &mut impl EdgeVisitor<OpenJDKEdge>) {
135135
let referent_addr = Self::referent_address(oop);
136-
closure.visit_edge(referent_addr);
136+
closure.visit_edge(referent_addr.into());
137137
let discovered_addr = Self::discovered_address(oop);
138-
closure.visit_edge(discovered_addr);
138+
closure.visit_edge(discovered_addr.into());
139139
}
140140
}
141141

mmtk/src/scanning.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ extern "C" fn report_edges_and_renew_buffer<F: RootsWorkFactory<OpenJDKEdge>>(
2020
factory_ptr: *mut libc::c_void,
2121
) -> NewBuffer {
2222
if !ptr.is_null() {
23-
let buf = unsafe { Vec::<Address>::from_raw_parts(ptr, length, capacity) };
23+
// Note: Currently OpenJDKEdge has the same layout as Address. If the layout changes, we
24+
// should fix the Rust-to-C interface.
25+
let buf = unsafe { Vec::<OpenJDKEdge>::from_raw_parts(ptr as _, length, capacity) };
2426
let factory: &mut F = unsafe { &mut *(factory_ptr as *mut F) };
2527
factory.create_process_edge_roots_work(buf);
2628
}

0 commit comments

Comments
 (0)