Skip to content

Commit 52f2d3f

Browse files
committed
Move alignment checks to codegen
1 parent 6e1d947 commit 52f2d3f

File tree

24 files changed

+322
-330
lines changed

24 files changed

+322
-330
lines changed

compiler/rustc_codegen_cranelift/example/mini_core.rs

+14
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,20 @@ fn panic_cannot_unwind() -> ! {
517517
}
518518
}
519519

520+
#[lang = "panic_misaligned_pointer_dereference"]
521+
#[track_caller]
522+
fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! {
523+
unsafe {
524+
libc::printf(
525+
"misaligned pointer dereference: address must be a multiple of %d but is %d\n\0"
526+
as *const str as *const i8,
527+
required,
528+
found,
529+
);
530+
intrinsics::abort();
531+
}
532+
}
533+
520534
#[lang = "eh_personality"]
521535
fn eh_personality() -> ! {
522536
loop {}

compiler/rustc_codegen_cranelift/src/base.rs

+65-12
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use cranelift_codegen::CodegenError;
55
use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext};
66
use cranelift_module::ModuleError;
77
use rustc_ast::InlineAsmOptions;
8+
use rustc_codegen_ssa::mir::pointers_to_check;
89
use rustc_index::IndexVec;
910
use rustc_middle::ty::adjustment::PointerCoercion;
1011
use rustc_middle::ty::layout::FnAbiOf;
@@ -370,18 +371,6 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
370371
Some(source_info.span),
371372
);
372373
}
373-
AssertKind::MisalignedPointerDereference { ref required, ref found } => {
374-
let required = codegen_operand(fx, required).load_scalar(fx);
375-
let found = codegen_operand(fx, found).load_scalar(fx);
376-
let location = fx.get_caller_location(source_info).load_scalar(fx);
377-
378-
codegen_panic_inner(
379-
fx,
380-
rustc_hir::LangItem::PanicMisalignedPointerDereference,
381-
&[required, found, location],
382-
Some(source_info.span),
383-
);
384-
}
385374
_ => {
386375
let location = fx.get_caller_location(source_info).load_scalar(fx);
387376

@@ -524,6 +513,49 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
524513
}
525514
}
526515

516+
fn codegen_alignment_check<'tcx>(
517+
fx: &mut FunctionCx<'_, '_, 'tcx>,
518+
pointer: mir::Operand<'tcx>,
519+
required_alignment: u64,
520+
source_info: mir::SourceInfo,
521+
) {
522+
// Compute the alignment mask
523+
let required_alignment = required_alignment as i64;
524+
let mask = fx.bcx.ins().iconst(fx.pointer_type, required_alignment - 1);
525+
let required = fx.bcx.ins().iconst(fx.pointer_type, required_alignment);
526+
527+
// And the pointer with the mask
528+
let pointer = codegen_operand(fx, &pointer);
529+
let pointer = match pointer.layout().abi {
530+
Abi::Scalar(_) => pointer.load_scalar(fx),
531+
Abi::ScalarPair(..) => pointer.load_scalar_pair(fx).0,
532+
_ => unreachable!(),
533+
};
534+
let masked = fx.bcx.ins().band(pointer, mask);
535+
536+
// Branch on whether the masked value is zero
537+
let is_zero = fx.bcx.ins().icmp_imm(IntCC::Equal, masked, 0);
538+
539+
// Create destination blocks, branching on is_zero
540+
let panic = fx.bcx.create_block();
541+
let success = fx.bcx.create_block();
542+
fx.bcx.ins().brif(is_zero, success, &[], panic, &[]);
543+
544+
// Switch to the failure block and codegen a call to the panic intrinsic
545+
fx.bcx.switch_to_block(panic);
546+
let location = fx.get_caller_location(source_info).load_scalar(fx);
547+
codegen_panic_inner(
548+
fx,
549+
rustc_hir::LangItem::PanicMisalignedPointerDereference,
550+
&[required, pointer, location],
551+
Some(source_info.span),
552+
);
553+
554+
// Continue codegen in the success block
555+
fx.bcx.switch_to_block(success);
556+
fx.bcx.ins().nop();
557+
}
558+
527559
fn codegen_stmt<'tcx>(
528560
fx: &mut FunctionCx<'_, '_, 'tcx>,
529561
#[allow(unused_variables)] cur_block: Block,
@@ -545,6 +577,27 @@ fn codegen_stmt<'tcx>(
545577
}
546578
}
547579

580+
let required_align_of = |pointer| {
581+
let pointer_ty = fx.mir.local_decls[pointer].ty;
582+
let pointer_ty = fx.monomorphize(pointer_ty);
583+
if !pointer_ty.is_unsafe_ptr() {
584+
return None;
585+
}
586+
587+
let pointee_ty =
588+
pointer_ty.builtin_deref(true).expect("no builtin_deref for an unsafe pointer").ty;
589+
let pointee_layout = fx.layout_of(pointee_ty);
590+
591+
Some(pointee_layout.align.abi.bytes() as u64)
592+
};
593+
594+
if fx.tcx.may_insert_alignment_checks() {
595+
for (pointer, required_alignment) in pointers_to_check(stmt, required_align_of) {
596+
let pointer = mir::Operand::Copy(pointer.into());
597+
codegen_alignment_check(fx, pointer, required_alignment, stmt.source_info);
598+
}
599+
}
600+
548601
match &stmt.kind {
549602
StatementKind::SetDiscriminant { place, variant_index } => {
550603
let place = codegen_place(fx, **place);

compiler/rustc_codegen_gcc/example/mini_core.rs

+14
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,20 @@ fn panic_bounds_check(index: usize, len: usize) -> ! {
473473
}
474474
}
475475

476+
#[lang = "panic_misaligned_pointer_dereference"]
477+
#[track_caller]
478+
fn panic_misaligned_pointer_dereference(required: usize, found: usize) -> ! {
479+
unsafe {
480+
libc::printf(
481+
"misaligned pointer dereference: address must be a multiple of %d but is %d\n\0"
482+
as *const str as *const i8,
483+
required,
484+
found,
485+
);
486+
intrinsics::abort();
487+
}
488+
}
489+
476490
#[lang = "eh_personality"]
477491
fn eh_personality() -> ! {
478492
loop {}

compiler/rustc_codegen_ssa/src/mir/block.rs

+1-8
Original file line numberDiff line numberDiff line change
@@ -677,13 +677,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
677677
// and `#[track_caller]` adds an implicit third argument.
678678
(LangItem::PanicBoundsCheck, vec![index, len, location])
679679
}
680-
AssertKind::MisalignedPointerDereference { ref required, ref found } => {
681-
let required = self.codegen_operand(bx, required).immediate();
682-
let found = self.codegen_operand(bx, found).immediate();
683-
// It's `fn panic_misaligned_pointer_dereference(required: usize, found: usize)`,
684-
// and `#[track_caller]` adds an implicit third argument.
685-
(LangItem::PanicMisalignedPointerDereference, vec![required, found, location])
686-
}
687680
_ => {
688681
// It's `pub fn panic_...()` and `#[track_caller]` adds an implicit argument.
689682
(msg.panic_function(), vec![location])
@@ -1583,7 +1576,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
15831576
tuple.layout.fields.count()
15841577
}
15851578

1586-
fn get_caller_location(
1579+
pub fn get_caller_location(
15871580
&mut self,
15881581
bx: &mut Bx,
15891582
source_info: mir::SourceInfo,

compiler/rustc_codegen_ssa/src/mir/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@ mod intrinsic;
2222
mod locals;
2323
pub mod operand;
2424
pub mod place;
25+
mod pointer_alignment_check;
2526
mod rvalue;
2627
mod statement;
2728

2829
use self::debuginfo::{FunctionDebugContext, PerLocalVarDebugInfo};
2930
use self::operand::{OperandRef, OperandValue};
3031
use self::place::PlaceRef;
32+
pub use self::pointer_alignment_check::pointers_to_check;
3133

3234
// Used for tracking the state of generated basic blocks.
3335
enum CachedLlbb<T> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
use rustc_hir::LangItem;
2+
use rustc_middle::mir;
3+
use rustc_middle::mir::visit::Visitor;
4+
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext};
5+
use rustc_span::Span;
6+
7+
use super::FunctionCx;
8+
use crate::base;
9+
use crate::common;
10+
use crate::mir::OperandValue;
11+
use crate::traits::*;
12+
13+
pub fn pointers_to_check<F>(
14+
statement: &mir::Statement<'_>,
15+
required_align_of: F,
16+
) -> Vec<(mir::Local, u64)>
17+
where
18+
F: Fn(mir::Local) -> Option<u64>,
19+
{
20+
let mut finder = PointerFinder { required_align_of, pointers: Vec::new() };
21+
finder.visit_statement(statement, rustc_middle::mir::Location::START);
22+
finder.pointers
23+
}
24+
25+
struct PointerFinder<F> {
26+
pointers: Vec<(mir::Local, u64)>,
27+
required_align_of: F,
28+
}
29+
30+
impl<'tcx, F> Visitor<'tcx> for PointerFinder<F>
31+
where
32+
F: Fn(mir::Local) -> Option<u64>,
33+
{
34+
fn visit_place(
35+
&mut self,
36+
place: &mir::Place<'tcx>,
37+
context: PlaceContext,
38+
location: mir::Location,
39+
) {
40+
// We want to only check reads and writes to Places, so we specifically exclude
41+
// Borrows and AddressOf.
42+
match context {
43+
PlaceContext::MutatingUse(
44+
MutatingUseContext::Store
45+
| MutatingUseContext::AsmOutput
46+
| MutatingUseContext::Call
47+
| MutatingUseContext::Yield
48+
| MutatingUseContext::Drop,
49+
) => {}
50+
PlaceContext::NonMutatingUse(
51+
NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
52+
) => {}
53+
_ => {
54+
return;
55+
}
56+
}
57+
58+
if !place.is_indirect() {
59+
return;
60+
}
61+
62+
let pointer = place.local;
63+
let Some(required_alignment) = (self.required_align_of)(pointer) else {
64+
return;
65+
};
66+
67+
if required_alignment == 1 {
68+
return;
69+
}
70+
71+
// Ensure that this place is based on an aligned pointer.
72+
self.pointers.push((pointer, required_alignment));
73+
74+
self.super_place(place, context, location);
75+
}
76+
}
77+
78+
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
79+
#[instrument(level = "debug", skip(self, bx))]
80+
pub fn codegen_alignment_check(
81+
&mut self,
82+
bx: &mut Bx,
83+
pointer: mir::Operand<'tcx>,
84+
required_alignment: u64,
85+
source_info: mir::SourceInfo,
86+
) {
87+
// Compute the alignment mask
88+
let mask = bx.const_usize(required_alignment - 1);
89+
let zero = bx.const_usize(0);
90+
let required_alignment = bx.const_usize(required_alignment);
91+
92+
// And the pointer with the mask
93+
let pointer = match self.codegen_operand(bx, &pointer).val {
94+
OperandValue::Immediate(imm) => imm,
95+
OperandValue::Pair(ptr, _) => ptr,
96+
_ => {
97+
unreachable!("{pointer:?}");
98+
}
99+
};
100+
let addr = bx.ptrtoint(pointer, bx.cx().type_isize());
101+
let masked = bx.and(addr, mask);
102+
103+
// Branch on whether the masked value is zero
104+
let is_zero = bx.icmp(
105+
base::bin_op_to_icmp_predicate(mir::BinOp::Eq.to_hir_binop(), false),
106+
masked,
107+
zero,
108+
);
109+
110+
// Create destination blocks, branching on is_zero
111+
let panic = bx.append_sibling_block("panic");
112+
let success = bx.append_sibling_block("success");
113+
bx.cond_br(is_zero, success, panic);
114+
115+
// Switch to the failure block and codegen a call to the panic intrinsic
116+
bx.switch_to_block(panic);
117+
self.set_debug_loc(bx, source_info);
118+
let location = self.get_caller_location(bx, source_info).immediate();
119+
self.codegen_nounwind_panic(
120+
bx,
121+
LangItem::PanicMisalignedPointerDereference,
122+
&[required_alignment, addr, location],
123+
source_info.span,
124+
);
125+
126+
// Continue codegen in the success block.
127+
bx.switch_to_block(success);
128+
self.set_debug_loc(bx, source_info);
129+
}
130+
131+
/// Emit a call to a diverging and `rustc_nounwind` panic helper.
132+
#[instrument(level = "debug", skip(self, bx))]
133+
fn codegen_nounwind_panic(
134+
&mut self,
135+
bx: &mut Bx,
136+
lang_item: LangItem,
137+
args: &[Bx::Value],
138+
span: Span,
139+
) {
140+
let (fn_abi, fn_ptr, instance) = common::build_langcall(bx, Some(span), lang_item);
141+
let fn_ty = bx.fn_decl_backend_type(&fn_abi);
142+
let fn_attrs = if bx.tcx().def_kind(self.instance.def_id()).has_codegen_attrs() {
143+
Some(bx.tcx().codegen_fn_attrs(self.instance.def_id()))
144+
} else {
145+
None
146+
};
147+
148+
// bx.call requires that the call not unwind. Double-check that this LangItem can't unwind.
149+
assert!(!fn_abi.can_unwind);
150+
151+
bx.call(
152+
fn_ty,
153+
fn_attrs,
154+
Some(&fn_abi),
155+
fn_ptr,
156+
args,
157+
None, /* funclet */
158+
Some(instance),
159+
);
160+
bx.unreachable();
161+
}
162+
}

compiler/rustc_codegen_ssa/src/mir/statement.rs

+31
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use rustc_middle::mir::{self, NonDivergingIntrinsic};
22
use rustc_middle::span_bug;
33
use rustc_session::config::OptLevel;
44

5+
use super::pointers_to_check;
56
use super::FunctionCx;
67
use super::LocalRef;
78
use crate::traits::*;
@@ -10,6 +11,36 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
1011
#[instrument(level = "debug", skip(self, bx))]
1112
pub fn codegen_statement(&mut self, bx: &mut Bx, statement: &mir::Statement<'tcx>) {
1213
self.set_debug_loc(bx, statement.source_info);
14+
15+
let required_align_of = |local| {
16+
// Since Deref projections must come first and only once, the pointer for an indirect place
17+
// is the Local that the Place is based on.
18+
let pointer_ty = self.mir.local_decls[local].ty;
19+
let pointer_ty = self.monomorphize(pointer_ty);
20+
21+
// We only want to check places based on unsafe pointers
22+
if !pointer_ty.is_unsafe_ptr() {
23+
return None;
24+
}
25+
26+
let pointee_ty =
27+
pointer_ty.builtin_deref(true).expect("no builtin_deref for an unsafe pointer");
28+
let pointee_layout = bx.layout_of(pointee_ty);
29+
30+
Some(pointee_layout.layout.align.abi.bytes())
31+
};
32+
33+
if bx.tcx().may_insert_alignment_checks() {
34+
for (pointer, required_alignment) in pointers_to_check(statement, required_align_of) {
35+
let pointer = mir::Operand::Copy(pointer.into());
36+
self.codegen_alignment_check(
37+
bx,
38+
pointer,
39+
required_alignment,
40+
statement.source_info,
41+
);
42+
}
43+
}
1344
match statement.kind {
1445
mir::StatementKind::Assign(box (ref place, ref rvalue)) => {
1546
if let Some(index) = place.as_local() {

0 commit comments

Comments
 (0)