diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index 43b93d03ff6..58425793602 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -1331,84 +1331,83 @@ fn with_loop_condition_scope(&mut self, f: impl FnOnce(&mut Self) -> T) -> T } fn lower_expr_asm(&mut self, sp: Span, asm: &InlineAsm) -> hir::ExprKind<'hir> { - if self.sess.asm_arch.is_none() { + // Rustdoc needs to support asm! from foriegn architectures: don't try + // lowering the register contraints in this case. + let asm_arch = if self.sess.opts.actually_rustdoc { None } else { self.sess.asm_arch }; + if asm_arch.is_none() && !self.sess.opts.actually_rustdoc { struct_span_err!(self.sess, sp, E0472, "asm! is unsupported on this target").emit(); } if asm.options.contains(InlineAsmOptions::ATT_SYNTAX) - && !matches!( - self.sess.asm_arch, - Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64) - ) + && !matches!(asm_arch, Some(asm::InlineAsmArch::X86 | asm::InlineAsmArch::X86_64)) + && !self.sess.opts.actually_rustdoc { self.sess .struct_span_err(sp, "the `att_syntax` option is only supported on x86") .emit(); } - // Lower operands to HIR, filter_map skips any operands with invalid - // register classes. + // Lower operands to HIR. We use dummy register classes if an error + // occurs during lowering because we still need to be able to produce a + // valid HIR. let sess = self.sess; let operands: Vec<_> = asm .operands .iter() - .filter_map(|(op, op_sp)| { - let lower_reg = |reg| { - Some(match reg { - InlineAsmRegOrRegClass::Reg(s) => asm::InlineAsmRegOrRegClass::Reg( + .map(|(op, op_sp)| { + let lower_reg = |reg| match reg { + InlineAsmRegOrRegClass::Reg(s) => { + asm::InlineAsmRegOrRegClass::Reg(if let Some(asm_arch) = asm_arch { asm::InlineAsmReg::parse( - sess.asm_arch?, + asm_arch, |feature| sess.target_features.contains(&Symbol::intern(feature)), &sess.target, s, ) - .map_err(|e| { + .unwrap_or_else(|e| { let msg = format!("invalid register `{}`: {}", s.as_str(), e); sess.struct_span_err(*op_sp, &msg).emit(); + asm::InlineAsmReg::Err }) - .ok()?, - ), - InlineAsmRegOrRegClass::RegClass(s) => { - asm::InlineAsmRegOrRegClass::RegClass( - asm::InlineAsmRegClass::parse(sess.asm_arch?, s) - .map_err(|e| { - let msg = format!( - "invalid register class `{}`: {}", - s.as_str(), - e - ); - sess.struct_span_err(*op_sp, &msg).emit(); - }) - .ok()?, - ) - } - }) + } else { + asm::InlineAsmReg::Err + }) + } + InlineAsmRegOrRegClass::RegClass(s) => { + asm::InlineAsmRegOrRegClass::RegClass(if let Some(asm_arch) = asm_arch { + asm::InlineAsmRegClass::parse(asm_arch, s).unwrap_or_else(|e| { + let msg = format!("invalid register class `{}`: {}", s.as_str(), e); + sess.struct_span_err(*op_sp, &msg).emit(); + asm::InlineAsmRegClass::Err + }) + } else { + asm::InlineAsmRegClass::Err + }) + } }; - // lower_reg is executed last because we need to lower all - // sub-expressions even if we throw them away later. let op = match *op { InlineAsmOperand::In { reg, ref expr } => hir::InlineAsmOperand::In { + reg: lower_reg(reg), expr: self.lower_expr_mut(expr), - reg: lower_reg(reg)?, }, InlineAsmOperand::Out { reg, late, ref expr } => hir::InlineAsmOperand::Out { + reg: lower_reg(reg), late, expr: expr.as_ref().map(|expr| self.lower_expr_mut(expr)), - reg: lower_reg(reg)?, }, InlineAsmOperand::InOut { reg, late, ref expr } => { hir::InlineAsmOperand::InOut { + reg: lower_reg(reg), late, expr: self.lower_expr_mut(expr), - reg: lower_reg(reg)?, } } InlineAsmOperand::SplitInOut { reg, late, ref in_expr, ref out_expr } => { hir::InlineAsmOperand::SplitInOut { + 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)), - reg: lower_reg(reg)?, } } InlineAsmOperand::Const { ref expr } => { @@ -1418,17 +1417,11 @@ fn lower_expr_asm(&mut self, sp: Span, asm: &InlineAsm) -> hir::ExprKind<'hir> { hir::InlineAsmOperand::Sym { expr: self.lower_expr_mut(expr) } } }; - Some((op, *op_sp)) + (op, *op_sp) }) .collect(); - // Stop if there were any errors when lowering the register classes - if operands.len() != asm.operands.len() || sess.asm_arch.is_none() { - return hir::ExprKind::Err; - } - // Validate template modifiers against the register classes for the operands - let asm_arch = sess.asm_arch.unwrap(); for p in &asm.template { if let InlineAsmTemplatePiece::Placeholder { operand_idx, @@ -1443,7 +1436,10 @@ fn lower_expr_asm(&mut self, sp: Span, asm: &InlineAsm) -> hir::ExprKind<'hir> { | hir::InlineAsmOperand::InOut { reg, .. } | hir::InlineAsmOperand::SplitInOut { reg, .. } => { let class = reg.reg_class(); - let valid_modifiers = class.valid_modifiers(asm_arch); + if class == asm::InlineAsmRegClass::Err { + continue; + } + let valid_modifiers = class.valid_modifiers(asm_arch.unwrap()); if !valid_modifiers.contains(&modifier) { let mut err = sess.struct_span_err( placeholder_span, @@ -1506,7 +1502,10 @@ fn lower_expr_asm(&mut self, sp: Span, asm: &InlineAsm) -> hir::ExprKind<'hir> { // features. We check that at least one type is available for // the current target. let reg_class = reg.reg_class(); - for &(_, feature) in reg_class.supported_types(asm_arch) { + if reg_class == asm::InlineAsmRegClass::Err { + continue; + } + for &(_, feature) in reg_class.supported_types(asm_arch.unwrap()) { if let Some(feature) = feature { if self.sess.target_features.contains(&Symbol::intern(feature)) { required_features.clear(); diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 38c8ae711a4..e7d359c4f14 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -528,6 +528,7 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'tcx>>) InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("LLVM backend does not support SPIR-V") } + InlineAsmRegClass::Err => unreachable!(), } .to_string(), } @@ -594,6 +595,7 @@ fn modifier_to_llvm( InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("LLVM backend does not support SPIR-V") } + InlineAsmRegClass::Err => unreachable!(), } } @@ -637,6 +639,7 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("LLVM backend does not support SPIR-V") } + InlineAsmRegClass::Err => unreachable!(), } } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index dfc0989a9f8..1d0013bcd2c 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -229,6 +229,8 @@ pub enum InlineAsmReg { Mips(MipsInlineAsmReg), SpirV(SpirVInlineAsmReg), Wasm(WasmInlineAsmReg), + // Placeholder for invalid register constraints for the current target + Err, } impl InlineAsmReg { @@ -240,6 +242,7 @@ pub fn name(self) -> &'static str { Self::RiscV(r) => r.name(), Self::Hexagon(r) => r.name(), Self::Mips(r) => r.name(), + Self::Err => "", } } @@ -251,6 +254,7 @@ pub fn reg_class(self) -> InlineAsmRegClass { Self::RiscV(r) => InlineAsmRegClass::RiscV(r.reg_class()), Self::Hexagon(r) => InlineAsmRegClass::Hexagon(r.reg_class()), Self::Mips(r) => InlineAsmRegClass::Mips(r.reg_class()), + Self::Err => InlineAsmRegClass::Err, } } @@ -309,6 +313,7 @@ pub fn emit( Self::RiscV(r) => r.emit(out, arch, modifier), Self::Hexagon(r) => r.emit(out, arch, modifier), Self::Mips(r) => r.emit(out, arch, modifier), + Self::Err => unreachable!(), } } @@ -320,6 +325,7 @@ pub fn overlapping_regs(self, mut cb: impl FnMut(InlineAsmReg)) { Self::RiscV(_) => cb(self), Self::Hexagon(r) => r.overlapping_regs(|r| cb(Self::Hexagon(r))), Self::Mips(_) => cb(self), + Self::Err => unreachable!(), } } } @@ -346,6 +352,8 @@ pub enum InlineAsmRegClass { Mips(MipsInlineAsmRegClass), SpirV(SpirVInlineAsmRegClass), Wasm(WasmInlineAsmRegClass), + // Placeholder for invalid register constraints for the current target + Err, } impl InlineAsmRegClass { @@ -360,6 +368,7 @@ pub fn name(self) -> Symbol { Self::Mips(r) => r.name(), Self::SpirV(r) => r.name(), Self::Wasm(r) => r.name(), + Self::Err => rustc_span::symbol::sym::reg, } } @@ -377,6 +386,7 @@ pub fn suggest_class(self, arch: InlineAsmArch, ty: InlineAsmType) -> Option r.suggest_class(arch, ty).map(InlineAsmRegClass::Mips), Self::SpirV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::SpirV), Self::Wasm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Wasm), + Self::Err => unreachable!(), } } @@ -401,6 +411,7 @@ pub fn suggest_modifier( Self::Mips(r) => r.suggest_modifier(arch, ty), Self::SpirV(r) => r.suggest_modifier(arch, ty), Self::Wasm(r) => r.suggest_modifier(arch, ty), + Self::Err => unreachable!(), } } @@ -421,6 +432,7 @@ pub fn default_modifier(self, arch: InlineAsmArch) -> Option<(char, &'static str Self::Mips(r) => r.default_modifier(arch), Self::SpirV(r) => r.default_modifier(arch), Self::Wasm(r) => r.default_modifier(arch), + Self::Err => unreachable!(), } } @@ -440,6 +452,7 @@ pub fn supported_types( Self::Mips(r) => r.supported_types(arch), Self::SpirV(r) => r.supported_types(arch), Self::Wasm(r) => r.supported_types(arch), + Self::Err => unreachable!(), } } @@ -476,6 +489,7 @@ pub fn valid_modifiers(self, arch: InlineAsmArch) -> &'static [char] { Self::Mips(r) => r.valid_modifiers(arch), Self::SpirV(r) => r.valid_modifiers(arch), Self::Wasm(r) => r.valid_modifiers(arch), + Self::Err => unreachable!(), } } } diff --git a/src/test/rustdoc-ui/asm-foreign.rs b/src/test/rustdoc-ui/asm-foreign.rs new file mode 100644 index 00000000000..dfd73147417 --- /dev/null +++ b/src/test/rustdoc-ui/asm-foreign.rs @@ -0,0 +1,19 @@ +// check-pass +// Make sure rustdoc accepts asm! for a foreign architecture. + +#![feature(asm)] + +pub unsafe fn aarch64(a: f64, b: f64) { + let c; + asm!("add {:d}, {:d}, d0", out(vreg) c, in(vreg) a, in("d0") { + || {}; + b + }); + c +} + +pub unsafe fn x86(a: f64, b: f64) { + let c; + asm!("addsd {}, {}, xmm0", out(xmm_reg) c, in(xmm_reg) a, in("xmm0") b); + c +}