Skip to content

Commit fa98120

Browse files
committed
Move alignment checks to codegen
1 parent 6f83750 commit fa98120

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
@@ -4,6 +4,7 @@ use cranelift_codegen::ir::UserFuncName;
44
use cranelift_codegen::CodegenError;
55
use cranelift_module::ModuleError;
66
use rustc_ast::InlineAsmOptions;
7+
use rustc_codegen_ssa::mir::pointers_to_check;
78
use rustc_index::IndexVec;
89
use rustc_middle::ty::adjustment::PointerCoercion;
910
use rustc_middle::ty::layout::FnAbiOf;
@@ -359,18 +360,6 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
359360
Some(source_info.span),
360361
);
361362
}
362-
AssertKind::MisalignedPointerDereference { ref required, ref found } => {
363-
let required = codegen_operand(fx, required).load_scalar(fx);
364-
let found = codegen_operand(fx, found).load_scalar(fx);
365-
let location = fx.get_caller_location(source_info).load_scalar(fx);
366-
367-
codegen_panic_inner(
368-
fx,
369-
rustc_hir::LangItem::PanicMisalignedPointerDereference,
370-
&[required, found, location],
371-
Some(source_info.span),
372-
);
373-
}
374363
_ => {
375364
let location = fx.get_caller_location(source_info).load_scalar(fx);
376365

@@ -513,6 +502,49 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
513502
}
514503
}
515504

505+
fn codegen_alignment_check<'tcx>(
506+
fx: &mut FunctionCx<'_, '_, 'tcx>,
507+
pointer: mir::Operand<'tcx>,
508+
required_alignment: u64,
509+
source_info: mir::SourceInfo,
510+
) {
511+
// Compute the alignment mask
512+
let required_alignment = required_alignment as i64;
513+
let mask = fx.bcx.ins().iconst(fx.pointer_type, required_alignment - 1);
514+
let required = fx.bcx.ins().iconst(fx.pointer_type, required_alignment);
515+
516+
// And the pointer with the mask
517+
let pointer = codegen_operand(fx, &pointer);
518+
let pointer = match pointer.layout().abi {
519+
Abi::Scalar(_) => pointer.load_scalar(fx),
520+
Abi::ScalarPair(..) => pointer.load_scalar_pair(fx).0,
521+
_ => unreachable!(),
522+
};
523+
let masked = fx.bcx.ins().band(pointer, mask);
524+
525+
// Branch on whether the masked value is zero
526+
let is_zero = fx.bcx.ins().icmp_imm(IntCC::Equal, masked, 0);
527+
528+
// Create destination blocks, branching on is_zero
529+
let panic = fx.bcx.create_block();
530+
let success = fx.bcx.create_block();
531+
fx.bcx.ins().brif(is_zero, success, &[], panic, &[]);
532+
533+
// Switch to the failure block and codegen a call to the panic intrinsic
534+
fx.bcx.switch_to_block(panic);
535+
let location = fx.get_caller_location(source_info).load_scalar(fx);
536+
codegen_panic_inner(
537+
fx,
538+
rustc_hir::LangItem::PanicMisalignedPointerDereference,
539+
&[required, pointer, location],
540+
Some(source_info.span),
541+
);
542+
543+
// Continue codegen in the success block
544+
fx.bcx.switch_to_block(success);
545+
fx.bcx.ins().nop();
546+
}
547+
516548
fn codegen_stmt<'tcx>(
517549
fx: &mut FunctionCx<'_, '_, 'tcx>,
518550
#[allow(unused_variables)] cur_block: Block,
@@ -534,6 +566,27 @@ fn codegen_stmt<'tcx>(
534566
}
535567
}
536568

569+
let required_align_of = |pointer| {
570+
let pointer_ty = fx.mir.local_decls[pointer].ty;
571+
let pointer_ty = fx.monomorphize(pointer_ty);
572+
if !pointer_ty.is_unsafe_ptr() {
573+
return None;
574+
}
575+
576+
let pointee_ty =
577+
pointer_ty.builtin_deref(true).expect("no builtin_deref for an unsafe pointer").ty;
578+
let pointee_layout = fx.layout_of(pointee_ty);
579+
580+
Some(pointee_layout.align.abi.bytes() as u64)
581+
};
582+
583+
if fx.tcx.may_insert_alignment_checks() {
584+
for (pointer, required_alignment) in pointers_to_check(stmt, required_align_of) {
585+
let pointer = mir::Operand::Copy(pointer.into());
586+
codegen_alignment_check(fx, pointer, required_alignment, stmt.source_info);
587+
}
588+
}
589+
537590
match &stmt.kind {
538591
StatementKind::SetDiscriminant { place, variant_index } => {
539592
let place = codegen_place(fx, **place);

compiler/rustc_codegen_gcc/example/mini_core.rs

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

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

compiler/rustc_codegen_ssa/src/mir/block.rs

+1-8
Original file line numberDiff line numberDiff line change
@@ -674,13 +674,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
674674
// and `#[track_caller]` adds an implicit third argument.
675675
(LangItem::PanicBoundsCheck, vec![index, len, location])
676676
}
677-
AssertKind::MisalignedPointerDereference { ref required, ref found } => {
678-
let required = self.codegen_operand(bx, required).immediate();
679-
let found = self.codegen_operand(bx, found).immediate();
680-
// It's `fn panic_misaligned_pointer_dereference(required: usize, found: usize)`,
681-
// and `#[track_caller]` adds an implicit third argument.
682-
(LangItem::PanicMisalignedPointerDereference, vec![required, found, location])
683-
}
684677
_ => {
685678
// It's `pub fn panic_...()` and `#[track_caller]` adds an implicit argument.
686679
(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
@@ -20,12 +20,14 @@ mod intrinsic;
2020
mod locals;
2121
pub mod operand;
2222
pub mod place;
23+
mod pointer_alignment_check;
2324
mod rvalue;
2425
mod statement;
2526

2627
use self::debuginfo::{FunctionDebugContext, PerLocalVarDebugInfo};
2728
use self::operand::{OperandRef, OperandValue};
2829
use self::place::PlaceRef;
30+
pub use self::pointer_alignment_check::pointers_to_check;
2931

3032
// Used for tracking the state of generated basic blocks.
3133
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;
22
use rustc_middle::mir::NonDivergingIntrinsic;
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").ty;
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)