Skip to content

Commit 10510b5

Browse files
committed
HIR passes for asm!
1 parent ec1ad61 commit 10510b5

File tree

9 files changed

+469
-13
lines changed

9 files changed

+469
-13
lines changed

src/librustc_middle/traits/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,8 @@ pub enum ObligationCauseCode<'tcx> {
180180
SizedReturnType,
181181
/// Yield type must be `Sized`.
182182
SizedYieldType,
183+
/// Inline asm operand type must be `Sized`.
184+
InlineAsmSized,
183185
/// `[T, ..n]` implies that `T` must be `Copy`.
184186
/// If `true`, suggest `const_in_array_repeat_expressions` feature flag.
185187
RepeatVec(bool),

src/librustc_middle/traits/structural_impls.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
152152
super::SizedArgumentType => Some(super::SizedArgumentType),
153153
super::SizedReturnType => Some(super::SizedReturnType),
154154
super::SizedYieldType => Some(super::SizedYieldType),
155+
super::InlineAsmSized => Some(super::InlineAsmSized),
155156
super::RepeatVec(suggest_flag) => Some(super::RepeatVec(suggest_flag)),
156157
super::FieldSized { adt_kind, last } => Some(super::FieldSized { adt_kind, last }),
157158
super::ConstSized => Some(super::ConstSized),

src/librustc_passes/intrinsicck.rs

Lines changed: 276 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use rustc_ast::ast::{FloatTy, IntTy, UintTy};
12
use rustc_errors::struct_span_err;
23
use rustc_hir as hir;
34
use rustc_hir::def::{DefKind, Res};
@@ -7,8 +8,10 @@ use rustc_index::vec::Idx;
78
use rustc_middle::ty::layout::{LayoutError, SizeSkeleton};
89
use rustc_middle::ty::query::Providers;
910
use rustc_middle::ty::{self, Ty, TyCtxt};
10-
use rustc_span::{sym, Span};
11+
use rustc_session::lint;
12+
use rustc_span::{sym, Span, Symbol, DUMMY_SP};
1113
use rustc_target::abi::{Pointer, VariantIdx};
14+
use rustc_target::asm::{InlineAsmRegOrRegClass, InlineAsmTemplatePiece, InlineAsmType};
1215
use rustc_target::spec::abi::Abi::RustIntrinsic;
1316

1417
fn check_mod_intrinsics(tcx: TyCtxt<'_>, module_def_id: DefId) {
@@ -119,6 +122,262 @@ impl ExprVisitor<'tcx> {
119122
}
120123
err.emit()
121124
}
125+
126+
fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
127+
if ty.is_sized(self.tcx.at(DUMMY_SP), self.param_env) {
128+
return true;
129+
}
130+
if let ty::Foreign(..) = ty.kind {
131+
return true;
132+
}
133+
false
134+
}
135+
136+
fn check_asm_operand_type(
137+
&self,
138+
idx: usize,
139+
reg: InlineAsmRegOrRegClass,
140+
expr: &hir::Expr<'tcx>,
141+
template: &[InlineAsmTemplatePiece],
142+
tied_input: Option<(&hir::Expr<'tcx>, Option<InlineAsmType>)>,
143+
) -> Option<InlineAsmType> {
144+
// Check the type against the allowed types for inline asm.
145+
let ty = self.tables.expr_ty_adjusted(expr);
146+
let asm_ty_isize = match self.tcx.sess.target.ptr_width {
147+
16 => InlineAsmType::I16,
148+
32 => InlineAsmType::I32,
149+
64 => InlineAsmType::I64,
150+
_ => unreachable!(),
151+
};
152+
let asm_ty = match ty.kind {
153+
ty::Never | ty::Error => return None,
154+
ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8),
155+
ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16),
156+
ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32),
157+
ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64),
158+
ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128),
159+
ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize),
160+
ty::Float(FloatTy::F32) => Some(InlineAsmType::F32),
161+
ty::Float(FloatTy::F64) => Some(InlineAsmType::F64),
162+
ty::FnPtr(_) => Some(asm_ty_isize),
163+
ty::RawPtr(ty::TypeAndMut { ty, mutbl: _ }) if self.is_thin_ptr_ty(ty) => {
164+
Some(asm_ty_isize)
165+
}
166+
ty::Adt(adt, substs) if adt.repr.simd() => {
167+
let fields = &adt.non_enum_variant().fields;
168+
let elem_ty = fields[0].ty(self.tcx, substs);
169+
match elem_ty.kind {
170+
ty::Never | ty::Error => return None,
171+
ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => {
172+
Some(InlineAsmType::VecI8(fields.len() as u64))
173+
}
174+
ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => {
175+
Some(InlineAsmType::VecI16(fields.len() as u64))
176+
}
177+
ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => {
178+
Some(InlineAsmType::VecI32(fields.len() as u64))
179+
}
180+
ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => {
181+
Some(InlineAsmType::VecI64(fields.len() as u64))
182+
}
183+
ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => {
184+
Some(InlineAsmType::VecI128(fields.len() as u64))
185+
}
186+
ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => {
187+
Some(match self.tcx.sess.target.ptr_width {
188+
16 => InlineAsmType::VecI16(fields.len() as u64),
189+
32 => InlineAsmType::VecI32(fields.len() as u64),
190+
64 => InlineAsmType::VecI64(fields.len() as u64),
191+
_ => unreachable!(),
192+
})
193+
}
194+
ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(fields.len() as u64)),
195+
ty::Float(FloatTy::F64) => Some(InlineAsmType::VecF64(fields.len() as u64)),
196+
_ => None,
197+
}
198+
}
199+
_ => None,
200+
};
201+
let asm_ty = match asm_ty {
202+
Some(asm_ty) => asm_ty,
203+
None => {
204+
let msg = &format!("cannot use value of type `{}` for inline assembly", ty);
205+
let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
206+
err.note(
207+
"only integers, floats, SIMD vectors, pointers and function pointers \
208+
can be used as arguments for inline assembly",
209+
);
210+
err.emit();
211+
return None;
212+
}
213+
};
214+
215+
// Check that the type implements Copy. The only case where this can
216+
// possibly fail is for SIMD types which don't #[derive(Copy)].
217+
if !ty.is_copy_modulo_regions(self.tcx, self.param_env, DUMMY_SP) {
218+
let msg = "arguments for inline assembly must be copyable";
219+
let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
220+
err.note(&format!("`{}` does not implement the Copy trait", ty));
221+
err.emit();
222+
}
223+
224+
// Ideally we wouldn't need to do this, but LLVM's register allocator
225+
// really doesn't like it when tied operands have different types.
226+
//
227+
// This is purely an LLVM limitation, but we have to live with it since
228+
// there is no way to hide this with implicit conversions.
229+
//
230+
// For the purposes of this check we only look at the `InlineAsmType`,
231+
// which means that pointers and integers are treated as identical (modulo
232+
// size).
233+
if let Some((in_expr, Some(in_asm_ty))) = tied_input {
234+
if in_asm_ty != asm_ty {
235+
let msg = &format!("incompatible types for asm inout argument");
236+
let mut err = self.tcx.sess.struct_span_err(vec![in_expr.span, expr.span], msg);
237+
err.span_label(
238+
in_expr.span,
239+
&format!("type `{}`", self.tables.expr_ty_adjusted(in_expr)),
240+
);
241+
err.span_label(expr.span, &format!("type `{}`", ty));
242+
err.note("asm inout arguments must have the same type");
243+
err.note("unless they are both pointers or integers of the same size");
244+
err.emit();
245+
}
246+
247+
// All of the later checks have already been done on the input, so
248+
// let's not emit errors and warnings twice.
249+
return Some(asm_ty);
250+
}
251+
252+
// Check the type against the list of types supported by the selected
253+
// register class.
254+
let asm_arch = self.tcx.sess.asm_arch.unwrap();
255+
let reg_class = reg.reg_class();
256+
let supported_tys = reg_class.supported_types(asm_arch);
257+
let feature = match supported_tys.iter().find(|&&(t, _)| t == asm_ty) {
258+
Some((_, feature)) => feature,
259+
None => {
260+
let msg = &format!("type `{}` cannot be used with this register class", ty);
261+
let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
262+
let supported_tys: Vec<_> =
263+
supported_tys.iter().map(|(t, _)| t.to_string()).collect();
264+
err.note(&format!(
265+
"register class `{}` supports these types: {}",
266+
reg_class.name(),
267+
supported_tys.join(", "),
268+
));
269+
err.emit();
270+
return Some(asm_ty);
271+
}
272+
};
273+
274+
// Check whether the selected type requires a target feature.
275+
if let Some(feature) = feature {
276+
if !self.tcx.sess.target_features.contains(&Symbol::intern(feature)) {
277+
let msg = &format!("`{}` target feature is not enabled", feature);
278+
let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
279+
err.note(&format!(
280+
"this is required to use type `{}` with register class `{}`",
281+
ty,
282+
reg_class.name(),
283+
));
284+
err.emit();
285+
return Some(asm_ty);
286+
}
287+
}
288+
289+
// Check whether a modifier is suggested for using this type.
290+
if let Some((suggested_modifier, suggested_result, switch_reg_class)) =
291+
reg_class.suggest_modifier(asm_arch, asm_ty)
292+
{
293+
// Search for any use of this operand without a modifier and emit
294+
// the suggestion for them.
295+
let mut spans = vec![];
296+
for piece in template {
297+
if let &InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } = piece
298+
{
299+
if operand_idx == idx && modifier.is_none() {
300+
spans.push(span);
301+
}
302+
}
303+
}
304+
if !spans.is_empty() {
305+
let (default_modifier, default_result) =
306+
reg_class.default_modifier(asm_arch).unwrap();
307+
self.tcx.struct_span_lint_hir(
308+
lint::builtin::ASM_SUB_REGISTER,
309+
expr.hir_id,
310+
spans,
311+
|lint| {
312+
let msg = "formatting may not be suitable for sub-register argument";
313+
let mut err = lint.build(msg);
314+
err.span_label(expr.span, "for this argument");
315+
if let Some(switch_reg_class) = switch_reg_class {
316+
err.help(&format!(
317+
"use the `{}` modifier with the `{}` register class \
318+
to have the register formatted as `{}`",
319+
suggested_modifier, switch_reg_class, suggested_result,
320+
));
321+
} else {
322+
err.help(&format!(
323+
"use the `{}` modifier to have the register formatted as `{}`",
324+
suggested_modifier, suggested_result,
325+
));
326+
}
327+
err.help(&format!(
328+
"or use the `{}` modifier to keep the default formatting of `{}`",
329+
default_modifier, default_result,
330+
));
331+
err.emit();
332+
},
333+
);
334+
}
335+
}
336+
337+
Some(asm_ty)
338+
}
339+
340+
fn check_asm(&self, asm: &hir::InlineAsm<'tcx>) {
341+
for (idx, op) in asm.operands.iter().enumerate() {
342+
match *op {
343+
hir::InlineAsmOperand::In { reg, ref expr } => {
344+
self.check_asm_operand_type(idx, reg, expr, asm.template, None);
345+
}
346+
hir::InlineAsmOperand::Out { reg, late: _, ref expr } => {
347+
if let Some(expr) = expr {
348+
self.check_asm_operand_type(idx, reg, expr, asm.template, None);
349+
}
350+
}
351+
hir::InlineAsmOperand::InOut { reg, late: _, ref expr } => {
352+
self.check_asm_operand_type(idx, reg, expr, asm.template, None);
353+
}
354+
hir::InlineAsmOperand::SplitInOut { reg, late: _, ref in_expr, ref out_expr } => {
355+
let in_ty = self.check_asm_operand_type(idx, reg, in_expr, asm.template, None);
356+
if let Some(out_expr) = out_expr {
357+
self.check_asm_operand_type(
358+
idx,
359+
reg,
360+
out_expr,
361+
asm.template,
362+
Some((in_expr, in_ty)),
363+
);
364+
}
365+
}
366+
hir::InlineAsmOperand::Const { ref expr } => {
367+
let ty = self.tables.expr_ty_adjusted(expr);
368+
match ty.kind {
369+
ty::Int(_) | ty::Uint(_) | ty::Float(_) => {}
370+
_ => {
371+
let msg =
372+
"asm `const` arguments must be integer or floating-point values";
373+
self.tcx.sess.span_err(expr.span, msg);
374+
}
375+
}
376+
}
377+
hir::InlineAsmOperand::Sym { .. } => {}
378+
}
379+
}
380+
}
122381
}
123382

124383
impl Visitor<'tcx> for ItemVisitor<'tcx> {
@@ -146,19 +405,23 @@ impl Visitor<'tcx> for ExprVisitor<'tcx> {
146405
}
147406

148407
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
149-
let res = if let hir::ExprKind::Path(ref qpath) = expr.kind {
150-
self.tables.qpath_res(qpath, expr.hir_id)
151-
} else {
152-
Res::Err
153-
};
154-
if let Res::Def(DefKind::Fn, did) = res {
155-
if self.def_id_is_transmute(did) {
156-
let typ = self.tables.node_type(expr.hir_id);
157-
let sig = typ.fn_sig(self.tcx);
158-
let from = sig.inputs().skip_binder()[0];
159-
let to = *sig.output().skip_binder();
160-
self.check_transmute(expr.span, from, to);
408+
match expr.kind {
409+
hir::ExprKind::Path(ref qpath) => {
410+
let res = self.tables.qpath_res(qpath, expr.hir_id);
411+
if let Res::Def(DefKind::Fn, did) = res {
412+
if self.def_id_is_transmute(did) {
413+
let typ = self.tables.node_type(expr.hir_id);
414+
let sig = typ.fn_sig(self.tcx);
415+
let from = sig.inputs().skip_binder()[0];
416+
let to = *sig.output().skip_binder();
417+
self.check_transmute(expr.span, from, to);
418+
}
419+
}
161420
}
421+
422+
hir::ExprKind::InlineAsm(asm) => self.check_asm(asm),
423+
424+
_ => {}
162425
}
163426

164427
intravisit::walk_expr(self, expr);

0 commit comments

Comments
 (0)