diff --git a/compiler/rustc_ast_lowering/src/asm.rs b/compiler/rustc_ast_lowering/src/asm.rs index 0211d7b336736..171cc60dfd77c 100644 --- a/compiler/rustc_ast_lowering/src/asm.rs +++ b/compiler/rustc_ast_lowering/src/asm.rs @@ -64,12 +64,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let mut clobber_abis = FxHashMap::default(); if let Some(asm_arch) = asm_arch { for (abi_name, abi_span) in &asm.clobber_abis { - match asm::InlineAsmClobberAbi::parse( - asm_arch, - &self.sess.target_features, - &self.sess.target, - *abi_name, - ) { + match asm::InlineAsmClobberAbi::parse(asm_arch, &self.sess.target, *abi_name) { Ok(abi) => { // If the abi was already in the list, emit an error match clobber_abis.get(&abi) { @@ -129,17 +124,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { .operands .iter() .map(|(op, op_sp)| { - let lower_reg = |reg, is_clobber| match reg { + let lower_reg = |reg| match reg { InlineAsmRegOrRegClass::Reg(s) => { asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch { - asm::InlineAsmReg::parse( - asm_arch, - &sess.target_features, - &sess.target, - is_clobber, - s, - ) - .unwrap_or_else(|e| { + asm::InlineAsmReg::parse(asm_arch, s).unwrap_or_else(|e| { let msg = format!("invalid register `{}`: {}", s.as_str(), e); sess.struct_span_err(*op_sp, &msg).emit(); asm::InlineAsmReg::Err @@ -163,24 +151,24 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let op = match *op { InlineAsmOperand::In { reg, ref expr } => hir::InlineAsmOperand::In { - reg: lower_reg(reg, false), + reg: lower_reg(reg), expr: self.lower_expr_mut(expr), }, InlineAsmOperand::Out { reg, late, ref expr } => hir::InlineAsmOperand::Out { - reg: lower_reg(reg, expr.is_none()), + reg: lower_reg(reg), late, expr: expr.as_ref().map(|expr| self.lower_expr_mut(expr)), }, InlineAsmOperand::InOut { reg, late, ref expr } => { hir::InlineAsmOperand::InOut { - reg: lower_reg(reg, false), + reg: lower_reg(reg), late, expr: self.lower_expr_mut(expr), } } InlineAsmOperand::SplitInOut { reg, late, ref in_expr, ref out_expr } => { hir::InlineAsmOperand::SplitInOut { - reg: lower_reg(reg, false), + reg: lower_reg(reg), late, in_expr: self.lower_expr_mut(in_expr), out_expr: out_expr.as_ref().map(|expr| self.lower_expr_mut(expr)), diff --git a/compiler/rustc_codegen_cranelift/src/inline_asm.rs b/compiler/rustc_codegen_cranelift/src/inline_asm.rs index c242c75ed18ff..deac5dfd3ec1a 100644 --- a/compiler/rustc_codegen_cranelift/src/inline_asm.rs +++ b/compiler/rustc_codegen_cranelift/src/inline_asm.rs @@ -106,6 +106,7 @@ pub(crate) fn codegen_inline_asm<'tcx>( let mut asm_gen = InlineAssemblyGenerator { tcx: fx.tcx, arch: fx.tcx.sess.asm_arch.unwrap(), + enclosing_def_id: fx.instance.def_id(), template, operands, options, @@ -169,6 +170,7 @@ pub(crate) fn codegen_inline_asm<'tcx>( struct InlineAssemblyGenerator<'a, 'tcx> { tcx: TyCtxt<'tcx>, arch: InlineAsmArch, + enclosing_def_id: DefId, template: &'a [InlineAsmTemplatePiece], operands: &'a [InlineAsmOperand<'tcx>], options: InlineAsmOptions, @@ -182,7 +184,12 @@ struct InlineAssemblyGenerator<'a, 'tcx> { impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { fn allocate_registers(&mut self) { let sess = self.tcx.sess; - let map = allocatable_registers(self.arch, &sess.target_features, &sess.target); + let map = allocatable_registers( + self.arch, + sess.relocation_model(), + self.tcx.asm_target_features(self.enclosing_def_id), + &sess.target, + ); let mut allocated = FxHashMap::<_, (bool, bool)>::default(); let mut regs = vec![None; self.operands.len()]; @@ -313,14 +320,9 @@ impl<'tcx> InlineAssemblyGenerator<'_, 'tcx> { let mut new_slot = |x| new_slot_fn(&mut slot_size, x); // Allocate stack slots for saving clobbered registers - let abi_clobber = InlineAsmClobberAbi::parse( - self.arch, - &self.tcx.sess.target_features, - &self.tcx.sess.target, - sym::C, - ) - .unwrap() - .clobbered_regs(); + let abi_clobber = InlineAsmClobberAbi::parse(self.arch, &self.tcx.sess.target, sym::C) + .unwrap() + .clobbered_regs(); for (i, reg) in self.registers.iter().enumerate().filter_map(|(i, r)| r.map(|r| (i, r))) { let mut need_save = true; // If the register overlaps with a register clobbered by function call, then diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index f31b0ee592e9c..77166c89735e4 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -36,7 +36,6 @@ const ARM_ALLOWED_FEATURES: &[(&str, Option)] = &[ // #[target_feature]. ("thumb-mode", Some(sym::arm_target_feature)), ("thumb2", Some(sym::arm_target_feature)), - ("reserve-r9", Some(sym::arm_target_feature)), ]; const AARCH64_ALLOWED_FEATURES: &[(&str, Option)] = &[ diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index be86f1e92744e..069dac969c66e 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1046,6 +1046,10 @@ rustc_queries! { cache_on_disk_if { true } } + query asm_target_features(def_id: DefId) -> &'tcx FxHashSet { + desc { |tcx| "computing target features for inline asm of `{}`", tcx.def_path_str(def_id) } + } + query fn_arg_names(def_id: DefId) -> &'tcx [rustc_span::symbol::Ident] { desc { |tcx| "looking up function parameter names for `{}`", tcx.def_path_str(def_id) } separate_provide_extern diff --git a/compiler/rustc_passes/src/intrinsicck.rs b/compiler/rustc_passes/src/intrinsicck.rs index bd772d9975b3a..d7dde157864a4 100644 --- a/compiler/rustc_passes/src/intrinsicck.rs +++ b/compiler/rustc_passes/src/intrinsicck.rs @@ -1,4 +1,5 @@ use rustc_ast::InlineAsmTemplatePiece; +use rustc_data_structures::stable_set::FxHashSet; use rustc_errors::struct_span_err; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; @@ -138,7 +139,7 @@ impl<'tcx> ExprVisitor<'tcx> { template: &[InlineAsmTemplatePiece], is_input: bool, tied_input: Option<(&hir::Expr<'tcx>, Option)>, - target_features: &[Symbol], + target_features: &FxHashSet, ) -> Option { // Check the type against the allowed types for inline asm. let ty = self.typeck_results.expr_ty_adjusted(expr); @@ -285,9 +286,7 @@ impl<'tcx> ExprVisitor<'tcx> { // (!). In that case we still need the earlier check to verify that the // register class is usable at all. if let Some(feature) = feature { - if !self.tcx.sess.target_features.contains(&feature) - && !target_features.contains(&feature) - { + if !target_features.contains(&feature) { let msg = &format!("`{}` target feature is not enabled", feature); let mut err = self.tcx.sess.struct_span_err(expr.span, msg); err.note(&format!( @@ -347,7 +346,8 @@ impl<'tcx> ExprVisitor<'tcx> { let hir = self.tcx.hir(); let enclosing_id = hir.enclosing_body_owner(hir_id); let enclosing_def_id = hir.local_def_id(enclosing_id).to_def_id(); - let attrs = self.tcx.codegen_fn_attrs(enclosing_def_id); + let target_features = self.tcx.asm_target_features(enclosing_def_id); + let asm_arch = self.tcx.sess.asm_arch.unwrap(); for (idx, (op, op_sp)) in asm.operands.iter().enumerate() { // Validate register classes against currently enabled target // features. We check that at least one type is available for @@ -360,16 +360,29 @@ impl<'tcx> ExprVisitor<'tcx> { // Note that this is only possible for explicit register // operands, which cannot be used in the asm string. if let Some(reg) = op.reg() { + // Some explicit registers cannot be used depending on the + // target. Reject those here. + if let InlineAsmRegOrRegClass::Reg(reg) = reg { + if let Err(msg) = reg.validate( + asm_arch, + self.tcx.sess.relocation_model(), + &target_features, + &self.tcx.sess.target, + op.is_clobber(), + ) { + let msg = format!("cannot use register `{}`: {}", reg.name(), msg); + self.tcx.sess.struct_span_err(*op_sp, &msg).emit(); + continue; + } + } + if !op.is_clobber() { let mut missing_required_features = vec![]; let reg_class = reg.reg_class(); - for &(_, feature) in reg_class.supported_types(self.tcx.sess.asm_arch.unwrap()) - { + for &(_, feature) in reg_class.supported_types(asm_arch) { match feature { Some(feature) => { - if self.tcx.sess.target_features.contains(&feature) - || attrs.target_features.contains(&feature) - { + if target_features.contains(&feature) { missing_required_features.clear(); break; } else { @@ -425,7 +438,7 @@ impl<'tcx> ExprVisitor<'tcx> { asm.template, true, None, - &attrs.target_features, + &target_features, ); } hir::InlineAsmOperand::Out { reg, late: _, ref expr } => { @@ -437,7 +450,7 @@ impl<'tcx> ExprVisitor<'tcx> { asm.template, false, None, - &attrs.target_features, + &target_features, ); } } @@ -449,7 +462,7 @@ impl<'tcx> ExprVisitor<'tcx> { asm.template, false, None, - &attrs.target_features, + &target_features, ); } hir::InlineAsmOperand::SplitInOut { reg, late: _, ref in_expr, ref out_expr } => { @@ -460,7 +473,7 @@ impl<'tcx> ExprVisitor<'tcx> { asm.template, true, None, - &attrs.target_features, + &target_features, ); if let Some(out_expr) = out_expr { self.check_asm_operand_type( @@ -470,7 +483,7 @@ impl<'tcx> ExprVisitor<'tcx> { asm.template, false, Some((in_expr, in_ty)), - &attrs.target_features, + &target_features, ); } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c746255e95e18..6767593bbc51a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1122,7 +1122,6 @@ symbols! { repr_packed, repr_simd, repr_transparent, - reserved_r9: "reserved-r9", residual, result, rhs, diff --git a/compiler/rustc_target/src/asm/aarch64.rs b/compiler/rustc_target/src/asm/aarch64.rs index d184ad4e78ae5..7fb4dbdf2b181 100644 --- a/compiler/rustc_target/src/asm/aarch64.rs +++ b/compiler/rustc_target/src/asm/aarch64.rs @@ -1,5 +1,5 @@ use super::{InlineAsmArch, InlineAsmType}; -use crate::spec::Target; +use crate::spec::{RelocModel, Target}; use rustc_data_structures::stable_set::FxHashSet; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; @@ -73,17 +73,18 @@ impl AArch64InlineAsmRegClass { } } -pub fn reserved_x18( +pub fn target_reserves_x18(target: &Target) -> bool { + target.os == "android" || target.is_like_fuchsia || target.is_like_osx || target.is_like_windows +} + +fn reserved_x18( _arch: InlineAsmArch, + _reloc_model: RelocModel, _target_features: &FxHashSet, target: &Target, _is_clobber: bool, ) -> Result<(), &'static str> { - if target.os == "android" - || target.is_like_fuchsia - || target.is_like_osx - || target.is_like_windows - { + if target_reserves_x18(target) { Err("x18 is a reserved register on this target") } else { Ok(()) diff --git a/compiler/rustc_target/src/asm/arm.rs b/compiler/rustc_target/src/asm/arm.rs index b2d5bb3736afd..88f2d3f80d2c3 100644 --- a/compiler/rustc_target/src/asm/arm.rs +++ b/compiler/rustc_target/src/asm/arm.rs @@ -1,5 +1,5 @@ use super::{InlineAsmArch, InlineAsmType}; -use crate::spec::Target; +use crate::spec::{RelocModel, Target}; use rustc_data_structures::stable_set::FxHashSet; use rustc_macros::HashStable_Generic; use rustc_span::{sym, Symbol}; @@ -67,11 +67,12 @@ fn frame_pointer_is_r7(target_features: &FxHashSet, target: &Target) -> fn frame_pointer_r11( arch: InlineAsmArch, + reloc_model: RelocModel, target_features: &FxHashSet, target: &Target, is_clobber: bool, ) -> Result<(), &'static str> { - not_thumb1(arch, target_features, target, is_clobber)?; + not_thumb1(arch, reloc_model, target_features, target, is_clobber)?; if !frame_pointer_is_r7(target_features, target) { Err("the frame pointer (r11) cannot be used as an operand for inline asm") @@ -82,6 +83,7 @@ fn frame_pointer_r11( fn frame_pointer_r7( _arch: InlineAsmArch, + _reloc_model: RelocModel, target_features: &FxHashSet, target: &Target, _is_clobber: bool, @@ -95,6 +97,7 @@ fn frame_pointer_r7( fn not_thumb1( _arch: InlineAsmArch, + _reloc_model: RelocModel, target_features: &FxHashSet, _target: &Target, is_clobber: bool, @@ -111,18 +114,18 @@ fn not_thumb1( fn reserved_r9( arch: InlineAsmArch, + reloc_model: RelocModel, target_features: &FxHashSet, target: &Target, is_clobber: bool, ) -> Result<(), &'static str> { - not_thumb1(arch, target_features, target, is_clobber)?; + not_thumb1(arch, reloc_model, target_features, target, is_clobber)?; - // We detect this using the reserved-r9 feature instead of using the target - // because the relocation model can be changed with compiler options. - if target_features.contains(&sym::reserved_r9) { - Err("the RWPI static base register (r9) cannot be used as an operand for inline asm") - } else { - Ok(()) + match reloc_model { + RelocModel::Rwpi | RelocModel::RopiRwpi => { + Err("the RWPI static base register (r9) cannot be used as an operand for inline asm") + } + _ => Ok(()), } } diff --git a/compiler/rustc_target/src/asm/bpf.rs b/compiler/rustc_target/src/asm/bpf.rs index b4d982f3836be..3b03766a089b2 100644 --- a/compiler/rustc_target/src/asm/bpf.rs +++ b/compiler/rustc_target/src/asm/bpf.rs @@ -1,7 +1,6 @@ -use super::{InlineAsmArch, InlineAsmType, Target}; -use rustc_data_structures::stable_set::FxHashSet; +use super::{InlineAsmArch, InlineAsmType}; use rustc_macros::HashStable_Generic; -use rustc_span::{sym, Symbol}; +use rustc_span::Symbol; use std::fmt; def_reg_class! { @@ -43,19 +42,6 @@ impl BpfInlineAsmRegClass { } } -fn only_alu32( - _arch: InlineAsmArch, - target_features: &FxHashSet, - _target: &Target, - _is_clobber: bool, -) -> Result<(), &'static str> { - if !target_features.contains(&sym::alu32) { - Err("register can't be used without the `alu32` target feature") - } else { - Ok(()) - } -} - def_regs! { Bpf BpfInlineAsmReg BpfInlineAsmRegClass { r0: reg = ["r0"], @@ -68,16 +54,16 @@ def_regs! { r7: reg = ["r7"], r8: reg = ["r8"], r9: reg = ["r9"], - w0: wreg = ["w0"] % only_alu32, - w1: wreg = ["w1"] % only_alu32, - w2: wreg = ["w2"] % only_alu32, - w3: wreg = ["w3"] % only_alu32, - w4: wreg = ["w4"] % only_alu32, - w5: wreg = ["w5"] % only_alu32, - w6: wreg = ["w6"] % only_alu32, - w7: wreg = ["w7"] % only_alu32, - w8: wreg = ["w8"] % only_alu32, - w9: wreg = ["w9"] % only_alu32, + w0: wreg = ["w0"], + w1: wreg = ["w1"], + w2: wreg = ["w2"], + w3: wreg = ["w3"], + w4: wreg = ["w4"], + w5: wreg = ["w5"], + w6: wreg = ["w6"], + w7: wreg = ["w7"], + w8: wreg = ["w8"], + w9: wreg = ["w9"], #error = ["r10", "w10"] => "the stack pointer cannot be used as an operand for inline asm", diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index fd95b0338a6e1..5bc4b566daf67 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -1,5 +1,5 @@ -use crate::abi::Size; use crate::spec::Target; +use crate::{abi::Size, spec::RelocModel}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; @@ -25,7 +25,7 @@ macro_rules! def_reg_class { } } - pub fn parse(_arch: super::InlineAsmArch, name: rustc_span::Symbol) -> Result { + pub fn parse(name: rustc_span::Symbol) -> Result { match name { $( rustc_span::sym::$class => Ok(Self::$class), @@ -79,19 +79,10 @@ macro_rules! def_regs { } } - pub fn parse( - _arch: super::InlineAsmArch, - _target_features: &rustc_data_structures::fx::FxHashSet, - _target: &crate::spec::Target, - _is_clobber: bool, - name: &str, - ) -> Result { + pub fn parse(name: &str) -> Result { match name { $( - $($alias)|* | $reg_name => { - $($filter(_arch, _target_features, _target, _is_clobber)?;)? - Ok(Self::$reg) - } + $($alias)|* | $reg_name => Ok(Self::$reg), )* $( $($bad_reg)|* => Err($error), @@ -99,10 +90,34 @@ macro_rules! def_regs { _ => Err("unknown register"), } } + + pub fn validate(self, + _arch: super::InlineAsmArch, + _reloc_model: crate::spec::RelocModel, + _target_features: &rustc_data_structures::fx::FxHashSet, + _target: &crate::spec::Target, + _is_clobber: bool, + ) -> Result<(), &'static str> { + match self { + $( + Self::$reg => { + $($filter( + _arch, + _reloc_model, + _target_features, + _target, + _is_clobber + )?;)? + Ok(()) + } + )* + } + } } pub(super) fn fill_reg_map( _arch: super::InlineAsmArch, + _reloc_model: crate::spec::RelocModel, _target_features: &rustc_data_structures::fx::FxHashSet, _target: &crate::spec::Target, _map: &mut rustc_data_structures::fx::FxHashMap< @@ -113,7 +128,7 @@ macro_rules! def_regs { #[allow(unused_imports)] use super::{InlineAsmReg, InlineAsmRegClass}; $( - if $($filter(_arch, _target_features, _target, false).is_ok() &&)? true { + if $($filter(_arch, _reloc_model, _target_features, _target, false).is_ok() &&)? true { if let Some(set) = _map.get_mut(&InlineAsmRegClass::$arch($arch_regclass::$class)) { set.insert(InlineAsmReg::$arch($arch_reg::$reg)); } @@ -295,94 +310,60 @@ impl InlineAsmReg { } } - pub fn parse( - arch: InlineAsmArch, - target_features: &FxHashSet, - target: &Target, - is_clobber: bool, - name: Symbol, - ) -> Result { + pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result { // FIXME: use direct symbol comparison for register names // Use `Symbol::as_str` instead of `Symbol::with` here because `has_feature` may access `Symbol`. let name = name.as_str(); Ok(match arch { - InlineAsmArch::X86 | InlineAsmArch::X86_64 => { - Self::X86(X86InlineAsmReg::parse(arch, target_features, target, is_clobber, name)?) + InlineAsmArch::X86 | InlineAsmArch::X86_64 => Self::X86(X86InlineAsmReg::parse(name)?), + InlineAsmArch::Arm => Self::Arm(ArmInlineAsmReg::parse(name)?), + InlineAsmArch::AArch64 => Self::AArch64(AArch64InlineAsmReg::parse(name)?), + InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { + Self::RiscV(RiscVInlineAsmReg::parse(name)?) } - InlineAsmArch::Arm => { - Self::Arm(ArmInlineAsmReg::parse(arch, target_features, target, is_clobber, name)?) + InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmReg::parse(name)?), + InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => { + Self::PowerPC(PowerPCInlineAsmReg::parse(name)?) } - InlineAsmArch::AArch64 => Self::AArch64(AArch64InlineAsmReg::parse( - arch, - target_features, - target, - is_clobber, - name, - )?), - InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => Self::RiscV( - RiscVInlineAsmReg::parse(arch, target_features, target, is_clobber, name)?, - ), - InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmReg::parse( - arch, - target_features, - target, - is_clobber, - name, - )?), - InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => Self::PowerPC( - PowerPCInlineAsmReg::parse(arch, target_features, target, is_clobber, name)?, - ), - InlineAsmArch::Hexagon => Self::Hexagon(HexagonInlineAsmReg::parse( - arch, - target_features, - target, - is_clobber, - name, - )?), - InlineAsmArch::Mips | InlineAsmArch::Mips64 => Self::Mips(MipsInlineAsmReg::parse( - arch, - target_features, - target, - is_clobber, - name, - )?), - InlineAsmArch::S390x => Self::S390x(S390xInlineAsmReg::parse( - arch, - target_features, - target, - is_clobber, - name, - )?), - InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmReg::parse( - arch, - target_features, - target, - is_clobber, - name, - )?), - InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => Self::Wasm(WasmInlineAsmReg::parse( - arch, - target_features, - target, - is_clobber, - name, - )?), - InlineAsmArch::Bpf => { - Self::Bpf(BpfInlineAsmReg::parse(arch, target_features, target, is_clobber, name)?) + InlineAsmArch::Hexagon => Self::Hexagon(HexagonInlineAsmReg::parse(name)?), + InlineAsmArch::Mips | InlineAsmArch::Mips64 => { + Self::Mips(MipsInlineAsmReg::parse(name)?) } - InlineAsmArch::Avr => { - Self::Avr(AvrInlineAsmReg::parse(arch, target_features, target, is_clobber, name)?) + InlineAsmArch::S390x => Self::S390x(S390xInlineAsmReg::parse(name)?), + InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmReg::parse(name)?), + InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => { + Self::Wasm(WasmInlineAsmReg::parse(name)?) } - InlineAsmArch::Msp430 => Self::Msp430(Msp430InlineAsmReg::parse( - arch, - target_features, - target, - is_clobber, - name, - )?), + InlineAsmArch::Bpf => Self::Bpf(BpfInlineAsmReg::parse(name)?), + InlineAsmArch::Avr => Self::Avr(AvrInlineAsmReg::parse(name)?), + InlineAsmArch::Msp430 => Self::Msp430(Msp430InlineAsmReg::parse(name)?), }) } + pub fn validate( + self, + arch: InlineAsmArch, + reloc_model: RelocModel, + target_features: &FxHashSet, + target: &Target, + is_clobber: bool, + ) -> Result<(), &'static str> { + match self { + Self::X86(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::Arm(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::AArch64(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::RiscV(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::PowerPC(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::Hexagon(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::Mips(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::S390x(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::Bpf(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::Avr(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::Msp430(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), + Self::Err => unreachable!(), + } + } + // NOTE: This function isn't used at the moment, but is needed to support // falling back to an external assembler. pub fn emit( @@ -584,29 +565,29 @@ impl InlineAsmRegClass { pub fn parse(arch: InlineAsmArch, name: Symbol) -> Result { Ok(match arch { InlineAsmArch::X86 | InlineAsmArch::X86_64 => { - Self::X86(X86InlineAsmRegClass::parse(arch, name)?) + Self::X86(X86InlineAsmRegClass::parse(name)?) } - InlineAsmArch::Arm => Self::Arm(ArmInlineAsmRegClass::parse(arch, name)?), - InlineAsmArch::AArch64 => Self::AArch64(AArch64InlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Arm => Self::Arm(ArmInlineAsmRegClass::parse(name)?), + InlineAsmArch::AArch64 => Self::AArch64(AArch64InlineAsmRegClass::parse(name)?), InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { - Self::RiscV(RiscVInlineAsmRegClass::parse(arch, name)?) + Self::RiscV(RiscVInlineAsmRegClass::parse(name)?) } - InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Nvptx64 => Self::Nvptx(NvptxInlineAsmRegClass::parse(name)?), InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => { - Self::PowerPC(PowerPCInlineAsmRegClass::parse(arch, name)?) + Self::PowerPC(PowerPCInlineAsmRegClass::parse(name)?) } - InlineAsmArch::Hexagon => Self::Hexagon(HexagonInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Hexagon => Self::Hexagon(HexagonInlineAsmRegClass::parse(name)?), InlineAsmArch::Mips | InlineAsmArch::Mips64 => { - Self::Mips(MipsInlineAsmRegClass::parse(arch, name)?) + Self::Mips(MipsInlineAsmRegClass::parse(name)?) } - InlineAsmArch::S390x => Self::S390x(S390xInlineAsmRegClass::parse(arch, name)?), - InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::S390x => Self::S390x(S390xInlineAsmRegClass::parse(name)?), + InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmRegClass::parse(name)?), InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => { - Self::Wasm(WasmInlineAsmRegClass::parse(arch, name)?) + Self::Wasm(WasmInlineAsmRegClass::parse(name)?) } - InlineAsmArch::Bpf => Self::Bpf(BpfInlineAsmRegClass::parse(arch, name)?), - InlineAsmArch::Avr => Self::Avr(AvrInlineAsmRegClass::parse(arch, name)?), - InlineAsmArch::Msp430 => Self::Msp430(Msp430InlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Bpf => Self::Bpf(BpfInlineAsmRegClass::parse(name)?), + InlineAsmArch::Avr => Self::Avr(AvrInlineAsmRegClass::parse(name)?), + InlineAsmArch::Msp430 => Self::Msp430(Msp430InlineAsmRegClass::parse(name)?), }) } @@ -749,78 +730,79 @@ impl fmt::Display for InlineAsmType { // falling back to an external assembler. pub fn allocatable_registers( arch: InlineAsmArch, + reloc_model: RelocModel, target_features: &FxHashSet, target: &crate::spec::Target, ) -> FxHashMap> { match arch { InlineAsmArch::X86 | InlineAsmArch::X86_64 => { let mut map = x86::regclass_map(); - x86::fill_reg_map(arch, target_features, target, &mut map); + x86::fill_reg_map(arch, reloc_model, target_features, target, &mut map); map } InlineAsmArch::Arm => { let mut map = arm::regclass_map(); - arm::fill_reg_map(arch, target_features, target, &mut map); + arm::fill_reg_map(arch, reloc_model, target_features, target, &mut map); map } InlineAsmArch::AArch64 => { let mut map = aarch64::regclass_map(); - aarch64::fill_reg_map(arch, target_features, target, &mut map); + aarch64::fill_reg_map(arch, reloc_model, target_features, target, &mut map); map } InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => { let mut map = riscv::regclass_map(); - riscv::fill_reg_map(arch, target_features, target, &mut map); + riscv::fill_reg_map(arch, reloc_model, target_features, target, &mut map); map } InlineAsmArch::Nvptx64 => { let mut map = nvptx::regclass_map(); - nvptx::fill_reg_map(arch, target_features, target, &mut map); + nvptx::fill_reg_map(arch, reloc_model, target_features, target, &mut map); map } InlineAsmArch::PowerPC | InlineAsmArch::PowerPC64 => { let mut map = powerpc::regclass_map(); - powerpc::fill_reg_map(arch, target_features, target, &mut map); + powerpc::fill_reg_map(arch, reloc_model, target_features, target, &mut map); map } InlineAsmArch::Hexagon => { let mut map = hexagon::regclass_map(); - hexagon::fill_reg_map(arch, target_features, target, &mut map); + hexagon::fill_reg_map(arch, reloc_model, target_features, target, &mut map); map } InlineAsmArch::Mips | InlineAsmArch::Mips64 => { let mut map = mips::regclass_map(); - mips::fill_reg_map(arch, target_features, target, &mut map); + mips::fill_reg_map(arch, reloc_model, target_features, target, &mut map); map } InlineAsmArch::S390x => { let mut map = s390x::regclass_map(); - s390x::fill_reg_map(arch, target_features, target, &mut map); + s390x::fill_reg_map(arch, reloc_model, target_features, target, &mut map); map } InlineAsmArch::SpirV => { let mut map = spirv::regclass_map(); - spirv::fill_reg_map(arch, target_features, target, &mut map); + spirv::fill_reg_map(arch, reloc_model, target_features, target, &mut map); map } InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => { let mut map = wasm::regclass_map(); - wasm::fill_reg_map(arch, target_features, target, &mut map); + wasm::fill_reg_map(arch, reloc_model, target_features, target, &mut map); map } InlineAsmArch::Bpf => { let mut map = bpf::regclass_map(); - bpf::fill_reg_map(arch, target_features, target, &mut map); + bpf::fill_reg_map(arch, reloc_model, target_features, target, &mut map); map } InlineAsmArch::Avr => { let mut map = avr::regclass_map(); - avr::fill_reg_map(arch, target_features, target, &mut map); + avr::fill_reg_map(arch, reloc_model, target_features, target, &mut map); map } InlineAsmArch::Msp430 => { let mut map = msp430::regclass_map(); - msp430::fill_reg_map(arch, target_features, target, &mut map); + msp430::fill_reg_map(arch, reloc_model, target_features, target, &mut map); map } } @@ -853,7 +835,6 @@ impl InlineAsmClobberAbi { /// clobber ABIs for the target. pub fn parse( arch: InlineAsmArch, - target_features: &FxHashSet, target: &Target, name: Symbol, ) -> Result { @@ -877,13 +858,11 @@ impl InlineAsmClobberAbi { _ => Err(&["C", "system", "efiapi", "aapcs"]), }, InlineAsmArch::AArch64 => match name { - "C" | "system" | "efiapi" => { - Ok(if aarch64::reserved_x18(arch, target_features, target, true).is_err() { - InlineAsmClobberAbi::AArch64NoX18 - } else { - InlineAsmClobberAbi::AArch64 - }) - } + "C" | "system" | "efiapi" => Ok(if aarch64::target_reserves_x18(target) { + InlineAsmClobberAbi::AArch64NoX18 + } else { + InlineAsmClobberAbi::AArch64 + }), _ => Err(&["C", "system", "efiapi"]), }, InlineAsmArch::RiscV32 | InlineAsmArch::RiscV64 => match name { diff --git a/compiler/rustc_target/src/asm/riscv.rs b/compiler/rustc_target/src/asm/riscv.rs index e145ba8a16e64..987bf97052933 100644 --- a/compiler/rustc_target/src/asm/riscv.rs +++ b/compiler/rustc_target/src/asm/riscv.rs @@ -1,5 +1,5 @@ use super::{InlineAsmArch, InlineAsmType}; -use crate::spec::Target; +use crate::spec::{RelocModel, Target}; use rustc_data_structures::stable_set::FxHashSet; use rustc_macros::HashStable_Generic; use rustc_span::{sym, Symbol}; @@ -54,6 +54,7 @@ impl RiscVInlineAsmRegClass { fn not_e( _arch: InlineAsmArch, + _reloc_model: RelocModel, target_features: &FxHashSet, _target: &Target, _is_clobber: bool, diff --git a/compiler/rustc_target/src/asm/x86.rs b/compiler/rustc_target/src/asm/x86.rs index a8ee80ec4ea27..7c136a475486b 100644 --- a/compiler/rustc_target/src/asm/x86.rs +++ b/compiler/rustc_target/src/asm/x86.rs @@ -1,5 +1,5 @@ use super::{InlineAsmArch, InlineAsmType}; -use crate::spec::Target; +use crate::spec::{RelocModel, Target}; use rustc_data_structures::stable_set::FxHashSet; use rustc_macros::HashStable_Generic; use rustc_span::Symbol; @@ -139,6 +139,7 @@ impl X86InlineAsmRegClass { fn x86_64_only( arch: InlineAsmArch, + _reloc_model: RelocModel, _target_features: &FxHashSet, _target: &Target, _is_clobber: bool, @@ -152,6 +153,7 @@ fn x86_64_only( fn high_byte( arch: InlineAsmArch, + _reloc_model: RelocModel, _target_features: &FxHashSet, _target: &Target, _is_clobber: bool, @@ -164,6 +166,7 @@ fn high_byte( fn rbx_reserved( arch: InlineAsmArch, + _reloc_model: RelocModel, _target_features: &FxHashSet, _target: &Target, _is_clobber: bool, @@ -179,6 +182,7 @@ fn rbx_reserved( fn esi_reserved( arch: InlineAsmArch, + _reloc_model: RelocModel, _target_features: &FxHashSet, _target: &Target, _is_clobber: bool, diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 19b3d35566b24..392144ca7639c 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -1,3 +1,4 @@ +// ignore-tidy-filelength //! "Collection" is the process of determining the type and other external //! details of each item in Rust. Collection is specifically concerned //! with *inter-procedural* things -- for example, for a function @@ -87,6 +88,7 @@ pub fn provide(providers: &mut Providers) { static_mutability, generator_kind, codegen_fn_attrs, + asm_target_features, collect_mod_item_types, should_inherit_track_caller, ..*providers @@ -3255,6 +3257,24 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, id: DefId) -> CodegenFnAttrs { codegen_fn_attrs } +/// Computes the set of target features used in a function for the purposes of +/// inline assembly. +fn asm_target_features<'tcx>(tcx: TyCtxt<'tcx>, id: DefId) -> &'tcx FxHashSet { + let mut target_features = tcx.sess.target_features.clone(); + let attrs = tcx.codegen_fn_attrs(id); + target_features.extend(&attrs.target_features); + match attrs.instruction_set { + None => {} + Some(InstructionSetAttr::ArmA32) => { + target_features.remove(&sym::thumb_mode); + } + Some(InstructionSetAttr::ArmT32) => { + target_features.insert(sym::thumb_mode); + } + } + tcx.arena.alloc(target_features) +} + /// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller /// applied to the method prototype. fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool { diff --git a/src/test/ui/asm/issue-85247.rs b/src/test/ui/asm/issue-85247.rs new file mode 100644 index 0000000000000..e64f5e8af5238 --- /dev/null +++ b/src/test/ui/asm/issue-85247.rs @@ -0,0 +1,26 @@ +// revisions: ropi rwpi + +// [ropi] compile-flags: --target armv7-unknown-linux-gnueabihf -C relocation-model=ropi +// [rwpi] compile-flags: --target armv7-unknown-linux-gnueabihf -C relocation-model=rwpi +// [ropi] needs-llvm-components: arm +// [rwpi] needs-llvm-components: arm +// [ropi] build-pass + +#![feature(no_core, lang_items, rustc_attrs)] +#![no_core] +#![crate_type = "rlib"] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} +#[lang = "sized"] +trait Sized {} + +// R9 is reserved as the RWPI base register +fn main() { + unsafe { + asm!("", out("r9") _); + //[rwpi]~^ cannot use register `r9` + } +} diff --git a/src/test/ui/asm/issue-85247.rwpi.stderr b/src/test/ui/asm/issue-85247.rwpi.stderr new file mode 100644 index 0000000000000..996b0933a3412 --- /dev/null +++ b/src/test/ui/asm/issue-85247.rwpi.stderr @@ -0,0 +1,8 @@ +error: cannot use register `r9`: the RWPI static base register (r9) cannot be used as an operand for inline asm + --> $DIR/issue-85247.rs:23:18 + | +LL | asm!("", out("r9") _); + | ^^^^^^^^^^^ + +error: aborting due to previous error + diff --git a/src/test/ui/asm/issue-92378.rs b/src/test/ui/asm/issue-92378.rs new file mode 100644 index 0000000000000..d595e88ff80c5 --- /dev/null +++ b/src/test/ui/asm/issue-92378.rs @@ -0,0 +1,30 @@ +// compile-flags: --target armv5te-unknown-linux-gnueabi +// needs-llvm-components: arm +// build-pass + +#![feature(no_core, lang_items, rustc_attrs, isa_attribute)] +#![no_core] +#![crate_type = "rlib"] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} +#[lang = "sized"] +trait Sized {} + +// ARM uses R11 for the frame pointer, make sure R7 is usable. +#[instruction_set(arm::a32)] +pub fn arm() { + unsafe { + asm!("", out("r7") _); + } +} + +// Thumb uses R7 for the frame pointer, make sure R11 is usable. +#[instruction_set(arm::t32)] +pub fn thumb() { + unsafe { + asm!("", out("r11") _); + } +} diff --git a/src/test/ui/asm/x86_64/bad-reg.rs b/src/test/ui/asm/x86_64/bad-reg.rs index 257274b0bc318..4c4ce8b5e9e49 100644 --- a/src/test/ui/asm/x86_64/bad-reg.rs +++ b/src/test/ui/asm/x86_64/bad-reg.rs @@ -31,8 +31,6 @@ fn main() { //~^ ERROR invalid register `ip`: the instruction pointer cannot be used as an operand asm!("", in("k0") foo); //~^ ERROR invalid register `k0`: the k0 AVX mask register cannot be used as an operand - asm!("", in("ah") foo); - //~^ ERROR invalid register `ah`: high byte registers cannot be used as an operand asm!("", in("st(2)") foo); //~^ ERROR register class `x87_reg` can only be used as a clobber, not as an input or output diff --git a/src/test/ui/asm/x86_64/bad-reg.stderr b/src/test/ui/asm/x86_64/bad-reg.stderr index 3a89b2fdb74dd..f8b024e1acd62 100644 --- a/src/test/ui/asm/x86_64/bad-reg.stderr +++ b/src/test/ui/asm/x86_64/bad-reg.stderr @@ -70,50 +70,44 @@ error: invalid register `k0`: the k0 AVX mask register cannot be used as an oper LL | asm!("", in("k0") foo); | ^^^^^^^^^^^^ -error: invalid register `ah`: high byte registers cannot be used as an operand on x86_64 - --> $DIR/bad-reg.rs:34:18 - | -LL | asm!("", in("ah") foo); - | ^^^^^^^^^^^^ - error: register class `x87_reg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:37:18 + --> $DIR/bad-reg.rs:35:18 | LL | asm!("", in("st(2)") foo); | ^^^^^^^^^^^^^^^ error: register class `mmx_reg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:39:18 + --> $DIR/bad-reg.rs:37:18 | LL | asm!("", in("mm0") foo); | ^^^^^^^^^^^^^ error: register class `x87_reg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:43:20 + --> $DIR/bad-reg.rs:41:20 | LL | asm!("{}", in(x87_reg) foo); | ^^^^^^^^^^^^^^^ error: register class `mmx_reg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:45:20 + --> $DIR/bad-reg.rs:43:20 | LL | asm!("{}", in(mmx_reg) foo); | ^^^^^^^^^^^^^^^ error: register class `x87_reg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:47:20 + --> $DIR/bad-reg.rs:45:20 | LL | asm!("{}", out(x87_reg) _); | ^^^^^^^^^^^^^^ error: register class `mmx_reg` can only be used as a clobber, not as an input or output - --> $DIR/bad-reg.rs:49:20 + --> $DIR/bad-reg.rs:47:20 | LL | asm!("{}", out(mmx_reg) _); | ^^^^^^^^^^^^^^ error: register `al` conflicts with register `ax` - --> $DIR/bad-reg.rs:55:33 + --> $DIR/bad-reg.rs:53:33 | LL | asm!("", in("eax") foo, in("al") bar); | ------------- ^^^^^^^^^^^^ register `al` @@ -121,7 +115,7 @@ LL | asm!("", in("eax") foo, in("al") bar); | register `ax` error: register `ax` conflicts with register `ax` - --> $DIR/bad-reg.rs:57:33 + --> $DIR/bad-reg.rs:55:33 | LL | asm!("", in("rax") foo, out("rax") bar); | ------------- ^^^^^^^^^^^^^^ register `ax` @@ -129,13 +123,13 @@ LL | asm!("", in("rax") foo, out("rax") bar); | register `ax` | help: use `lateout` instead of `out` to avoid conflict - --> $DIR/bad-reg.rs:57:18 + --> $DIR/bad-reg.rs:55:18 | LL | asm!("", in("rax") foo, out("rax") bar); | ^^^^^^^^^^^^^ error: register `ymm0` conflicts with register `xmm0` - --> $DIR/bad-reg.rs:60:34 + --> $DIR/bad-reg.rs:58:34 | LL | asm!("", in("xmm0") foo, in("ymm0") bar); | -------------- ^^^^^^^^^^^^^^ register `ymm0` @@ -143,7 +137,7 @@ LL | asm!("", in("xmm0") foo, in("ymm0") bar); | register `xmm0` error: register `ymm0` conflicts with register `xmm0` - --> $DIR/bad-reg.rs:62:34 + --> $DIR/bad-reg.rs:60:34 | LL | asm!("", in("xmm0") foo, out("ymm0") bar); | -------------- ^^^^^^^^^^^^^^^ register `ymm0` @@ -151,10 +145,10 @@ LL | asm!("", in("xmm0") foo, out("ymm0") bar); | register `xmm0` | help: use `lateout` instead of `out` to avoid conflict - --> $DIR/bad-reg.rs:62:18 + --> $DIR/bad-reg.rs:60:18 | LL | asm!("", in("xmm0") foo, out("ymm0") bar); | ^^^^^^^^^^^^^^ -error: aborting due to 21 previous errors +error: aborting due to 20 previous errors