diff --git a/compiler/rustc_codegen_gcc/src/asm.rs b/compiler/rustc_codegen_gcc/src/asm.rs index 5bb65365ad6ad..e4b75dc462ba1 100644 --- a/compiler/rustc_codegen_gcc/src/asm.rs +++ b/compiler/rustc_codegen_gcc/src/asm.rs @@ -751,6 +751,11 @@ fn reg_class_to_gcc(reg_class: InlineAsmRegClass) -> &'static str { | X86InlineAsmRegClass::mmx_reg | X86InlineAsmRegClass::tmm_reg, ) => unreachable!("clobber-only"), + InlineAsmRegClass::Xtensa(XtensaInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::Xtensa(XtensaInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::Xtensa( + XtensaInlineAsmRegClass::sreg | XtensaInlineAsmRegClass::breg, + ) => unreachable!("clobber-only"), InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("GCC backend does not support SPIR-V") } @@ -872,6 +877,11 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("GCC backend does not support SPIR-V") } + InlineAsmRegClass::Xtensa(XtensaInlineAsmRegClass::reg) => cx.type_i32(), + InlineAsmRegClass::Xtensa(XtensaInlineAsmRegClass::freg) => cx.type_f32(), + InlineAsmRegClass::Xtensa( + XtensaInlineAsmRegClass::sreg | XtensaInlineAsmRegClass::breg, + ) => unreachable!("clobber-only"), InlineAsmRegClass::Err => unreachable!(), } } @@ -1070,6 +1080,7 @@ fn modifier_to_gcc( InlineAsmRegClass::SpirV(SpirVInlineAsmRegClass::reg) => { bug!("LLVM backend does not support SPIR-V") } + InlineAsmRegClass::Xtensa(_) => None, InlineAsmRegClass::Err => unreachable!(), } } diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index c5ab9fc2336eb..824515ca85d7f 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -278,6 +278,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } InlineAsmArch::SpirV => {} InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => {} + InlineAsmArch::Xtensa => {} InlineAsmArch::Bpf => {} InlineAsmArch::Msp430 => { constraints.push("~{sr}".to_string()); @@ -740,6 +741,11 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) -> | X86InlineAsmRegClass::kreg0 | X86InlineAsmRegClass::tmm_reg, ) => unreachable!("clobber-only"), + Xtensa(XtensaInlineAsmRegClass::freg) => "f", + Xtensa(XtensaInlineAsmRegClass::reg) => "r", + Xtensa(XtensaInlineAsmRegClass::sreg | XtensaInlineAsmRegClass::breg) => { + unreachable!("clobber-only") + } Wasm(WasmInlineAsmRegClass::local) => "r", Bpf(BpfInlineAsmRegClass::reg) => "r", Bpf(BpfInlineAsmRegClass::wreg) => "w", @@ -845,6 +851,7 @@ fn modifier_to_llvm( | X86InlineAsmRegClass::kreg0 | X86InlineAsmRegClass::tmm_reg, ) => unreachable!("clobber-only"), + Xtensa(_) => None, Wasm(WasmInlineAsmRegClass::local) => None, Bpf(_) => None, Avr(AvrInlineAsmRegClass::reg_pair) @@ -939,6 +946,11 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &' | X86InlineAsmRegClass::kreg0 | X86InlineAsmRegClass::tmm_reg, ) => unreachable!("clobber-only"), + Xtensa(XtensaInlineAsmRegClass::reg) => cx.type_i32(), + Xtensa(XtensaInlineAsmRegClass::freg) => cx.type_f32(), + Xtensa(XtensaInlineAsmRegClass::sreg | XtensaInlineAsmRegClass::breg) => { + unreachable!("clobber-only") + } Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(), Bpf(BpfInlineAsmRegClass::reg) => cx.type_i64(), Bpf(BpfInlineAsmRegClass::wreg) => cx.type_i32(), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 72339efd0a132..413971f99eba4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -467,6 +467,8 @@ symbols! { async_iterator, async_iterator_poll_next, async_trait_bounds, + atomctl, + atomic, atomic_and, atomic_cxchg, atomic_cxchgweak, @@ -541,6 +543,7 @@ symbols! { braced_empty_structs, branch, breakpoint, + breg, bridge, bswap, built, @@ -705,6 +708,8 @@ symbols! { contracts_internals, contracts_requires, convert, + convert_identity, + coprocessor, copy, copy_closures, copy_nonoverlapping, @@ -882,6 +887,7 @@ symbols! { ermsb_target_feature, exact_div, except, + exception, exception_handling: "exception-handling", exclusive_range_pattern, exhaustive_integer_patterns, @@ -908,6 +914,7 @@ symbols! { expr_fragment_specifier_2024, extended_key_value_attributes, extended_varargs_abi_support, + extendedl32r, extern_absolute_paths, extern_crate_item_prelude, extern_crate_self, @@ -990,6 +997,9 @@ symbols! { format_argument, format_arguments, format_macro, + format_placeholder, + format_unsafe_arg, + fp, framework, freeze, freeze_impls, @@ -1050,6 +1060,8 @@ symbols! { hexagon_target_feature, hidden, hide, + highpriinterrupts, + hint, homogeneous_aggregate, html_favicon_url, html_logo_url, @@ -1110,6 +1122,7 @@ symbols! { internal, internal_eq_trait_method_impls, internal_features, + interrupt, into_async_iter_into_iter, into_future, into_iter, @@ -1205,6 +1218,7 @@ symbols! { lt, m68k, m68k_target_feature, + mac16, macho: "mach-o", macro_at_most_once_rep, macro_attr, @@ -1256,6 +1270,7 @@ symbols! { mem_variant_count, mem_zeroed, member_constraints, + memctl, memory, memtag, message, @@ -1317,6 +1332,8 @@ symbols! { mir_unwind_unreachable, mir_variant, miri, + misc, + miscsr, mmx_reg, modifiers, module, @@ -1563,6 +1580,10 @@ symbols! { prelude_import, preserves_flags, prfchw_target_feature, + prid, + primitive, + print_macro, + println_macro, proc_dash_macro: "proc-macro", proc_macro, proc_macro_attribute, @@ -1819,7 +1840,9 @@ symbols! { rustdoc_missing_doc_code_examples, rustfmt, rvalue_static_promotion, + rvector, rwpi, + s32c1i, s390x, s390x_target_feature, s390x_target_feature_vector, @@ -2049,9 +2072,13 @@ symbols! { test_unstable_lint, thread, thread_local, + thread_local_macro, + threadptr, three_way_compare, thumb2, thumb_mode: "thumb-mode", + time, + timerint, tmm_reg, to_owned_method, to_string, @@ -2270,6 +2297,7 @@ symbols! { while_let, whole_dash_archive: "whole-archive", width, + windowed, windows, windows_subsystem, with_negative_coherence, @@ -2295,9 +2323,11 @@ symbols! { x87_target_feature, xcoff, xer, + xloop, xmm_reg, xop_target_feature, xtensa, + xtensa_target_feature, yeet_desugar_details, yeet_expr, yes, diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 14fef2880ff68..cf4528429ed2a 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -194,6 +194,7 @@ mod sparc; mod spirv; mod wasm; mod x86; +mod xtensa; pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass}; pub use arm::{ArmInlineAsmReg, ArmInlineAsmRegClass}; @@ -213,6 +214,7 @@ pub use sparc::{SparcInlineAsmReg, SparcInlineAsmRegClass}; pub use spirv::{SpirVInlineAsmReg, SpirVInlineAsmRegClass}; pub use wasm::{WasmInlineAsmReg, WasmInlineAsmRegClass}; pub use x86::{X86InlineAsmReg, X86InlineAsmRegClass}; +pub use xtensa::{XtensaInlineAsmReg, XtensaInlineAsmRegClass}; #[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash)] pub enum InlineAsmArch { @@ -237,6 +239,7 @@ pub enum InlineAsmArch { SpirV, Wasm32, Wasm64, + Xtensa, Bpf, Avr, Msp430, @@ -273,7 +276,8 @@ impl InlineAsmArch { Arch::Msp430 => Some(Self::Msp430), Arch::M68k => Some(Self::M68k), Arch::CSky => Some(Self::CSKY), - Arch::AmdGpu | Arch::Xtensa | Arch::Other(_) => None, + Arch::Xtensa => Some(Self::Xtensa), + Arch::AmdGpu | Arch::Other(_) => None, } } } @@ -294,6 +298,7 @@ pub enum InlineAsmReg { Sparc(SparcInlineAsmReg), SpirV(SpirVInlineAsmReg), Wasm(WasmInlineAsmReg), + Xtensa(XtensaInlineAsmReg), Bpf(BpfInlineAsmReg), Avr(AvrInlineAsmReg), Msp430(Msp430InlineAsmReg), @@ -316,6 +321,7 @@ impl InlineAsmReg { Self::Mips(r) => r.name(), Self::S390x(r) => r.name(), Self::Sparc(r) => r.name(), + Self::Xtensa(r) => r.name(), Self::Bpf(r) => r.name(), Self::Avr(r) => r.name(), Self::Msp430(r) => r.name(), @@ -337,6 +343,7 @@ impl InlineAsmReg { Self::Mips(r) => InlineAsmRegClass::Mips(r.reg_class()), Self::S390x(r) => InlineAsmRegClass::S390x(r.reg_class()), Self::Sparc(r) => InlineAsmRegClass::Sparc(r.reg_class()), + Self::Xtensa(r) => InlineAsmRegClass::Xtensa(r.reg_class()), Self::Bpf(r) => InlineAsmRegClass::Bpf(r.reg_class()), Self::Avr(r) => InlineAsmRegClass::Avr(r.reg_class()), Self::Msp430(r) => InlineAsmRegClass::Msp430(r.reg_class()), @@ -370,6 +377,7 @@ impl InlineAsmReg { InlineAsmArch::Mips | InlineAsmArch::Mips64 => { Self::Mips(MipsInlineAsmReg::parse(name)?) } + InlineAsmArch::Xtensa => Self::Xtensa(XtensaInlineAsmReg::parse(name)?), InlineAsmArch::S390x => Self::S390x(S390xInlineAsmReg::parse(name)?), InlineAsmArch::Sparc | InlineAsmArch::Sparc64 => { Self::Sparc(SparcInlineAsmReg::parse(name)?) @@ -409,6 +417,7 @@ impl InlineAsmReg { Self::Sparc(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::Xtensa(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::M68k(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), Self::CSKY(r) => r.validate(arch, reloc_model, target_features, target, is_clobber), @@ -435,6 +444,7 @@ impl InlineAsmReg { Self::Mips(r) => r.emit(out, arch, modifier), Self::S390x(r) => r.emit(out, arch, modifier), Self::Sparc(r) => r.emit(out, arch, modifier), + Self::Xtensa(r) => r.emit(out, arch, modifier), Self::Bpf(r) => r.emit(out, arch, modifier), Self::Avr(r) => r.emit(out, arch, modifier), Self::Msp430(r) => r.emit(out, arch, modifier), @@ -456,6 +466,7 @@ impl InlineAsmReg { Self::Mips(_) => cb(self), Self::S390x(r) => r.overlapping_regs(|r| cb(Self::S390x(r))), Self::Sparc(_) => cb(self), + Self::Xtensa(_) => cb(self), Self::Bpf(r) => r.overlapping_regs(|r| cb(Self::Bpf(r))), Self::Avr(r) => r.overlapping_regs(|r| cb(Self::Avr(r))), Self::Msp430(_) => cb(self), @@ -482,6 +493,7 @@ pub enum InlineAsmRegClass { Sparc(SparcInlineAsmRegClass), SpirV(SpirVInlineAsmRegClass), Wasm(WasmInlineAsmRegClass), + Xtensa(XtensaInlineAsmRegClass), Bpf(BpfInlineAsmRegClass), Avr(AvrInlineAsmRegClass), Msp430(Msp430InlineAsmRegClass), @@ -507,6 +519,7 @@ impl InlineAsmRegClass { Self::Sparc(r) => r.name(), Self::SpirV(r) => r.name(), Self::Wasm(r) => r.name(), + Self::Xtensa(r) => r.name(), Self::Bpf(r) => r.name(), Self::Avr(r) => r.name(), Self::Msp430(r) => r.name(), @@ -534,6 +547,7 @@ impl InlineAsmRegClass { Self::Sparc(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Sparc), Self::SpirV(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::SpirV), Self::Wasm(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Wasm), + Self::Xtensa(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Xtensa), Self::Bpf(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Bpf), Self::Avr(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Avr), Self::Msp430(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Msp430), @@ -564,6 +578,7 @@ impl InlineAsmRegClass { Self::Sparc(r) => r.suggest_modifier(arch, ty), Self::SpirV(r) => r.suggest_modifier(arch, ty), Self::Wasm(r) => r.suggest_modifier(arch, ty), + Self::Xtensa(r) => r.suggest_modifier(arch, ty), Self::Bpf(r) => r.suggest_modifier(arch, ty), Self::Avr(r) => r.suggest_modifier(arch, ty), Self::Msp430(r) => r.suggest_modifier(arch, ty), @@ -594,6 +609,7 @@ impl InlineAsmRegClass { Self::Sparc(r) => r.default_modifier(arch), Self::SpirV(r) => r.default_modifier(arch), Self::Wasm(r) => r.default_modifier(arch), + Self::Xtensa(r) => r.default_modifier(arch), Self::Bpf(r) => r.default_modifier(arch), Self::Avr(r) => r.default_modifier(arch), Self::Msp430(r) => r.default_modifier(arch), @@ -627,6 +643,7 @@ impl InlineAsmRegClass { Self::Sparc(r) => r.supported_types(arch), Self::SpirV(r) => r.supported_types(arch), Self::Wasm(r) => r.supported_types(arch), + Self::Xtensa(r) => r.supported_types(arch), Self::Bpf(r) => r.supported_types(arch), Self::Avr(r) => r.supported_types(arch), Self::Msp430(r) => r.supported_types(arch), @@ -669,6 +686,7 @@ impl InlineAsmRegClass { } InlineAsmArch::Bpf => Self::Bpf(BpfInlineAsmRegClass::parse(name)?), InlineAsmArch::Avr => Self::Avr(AvrInlineAsmRegClass::parse(name)?), + InlineAsmArch::Xtensa => Self::Xtensa(XtensaInlineAsmRegClass::parse(name)?), InlineAsmArch::Msp430 => Self::Msp430(Msp430InlineAsmRegClass::parse(name)?), InlineAsmArch::M68k => Self::M68k(M68kInlineAsmRegClass::parse(name)?), InlineAsmArch::CSKY => Self::CSKY(CSKYInlineAsmRegClass::parse(name)?), @@ -692,6 +710,7 @@ impl InlineAsmRegClass { Self::Sparc(r) => r.valid_modifiers(arch), Self::SpirV(r) => r.valid_modifiers(arch), Self::Wasm(r) => r.valid_modifiers(arch), + Self::Xtensa(r) => r.valid_modifiers(arch), Self::Bpf(r) => r.valid_modifiers(arch), Self::Avr(r) => r.valid_modifiers(arch), Self::Msp430(r) => r.valid_modifiers(arch), @@ -893,6 +912,11 @@ pub fn allocatable_registers( wasm::fill_reg_map(arch, reloc_model, target_features, target, &mut map); map } + InlineAsmArch::Xtensa => { + let mut map = xtensa::regclass_map(); + xtensa::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, reloc_model, target_features, target, &mut map); @@ -940,6 +964,7 @@ pub enum InlineAsmClobberAbi { S390x, Bpf, Msp430, + Xtensa, } impl InlineAsmClobberAbi { @@ -1020,6 +1045,10 @@ impl InlineAsmClobberAbi { "C" | "system" => Ok(InlineAsmClobberAbi::Msp430), _ => Err(&["C", "system"]), }, + InlineAsmArch::Xtensa => match name { + "C" | "system" => Ok(InlineAsmClobberAbi::Xtensa), + _ => Err(&["C", "system"]), + }, _ => Err(&[]), } } @@ -1329,6 +1358,42 @@ impl InlineAsmClobberAbi { r11, r12, r13, r14, r15, } }, + InlineAsmClobberAbi::Xtensa => clobbered_regs! { + Xtensa XtensaInlineAsmReg { + // Refs: + // - Xtensa ISA Reference Manual, Section 8.1.4 & 8.1.6 + // - "Except for LITBASE, all non-privileged special registers are + // caller-saved." + + // Caller-saved general-purpose registers (a2-a11). + // a0 is the return address (reserved by LLVM). + // a1/sp is the stack pointer (reserved by LLVM). + // a12-a15 are callee-saved. + a2, a3, a4, a5, a6, a7, + a8, a9, a10, a11, + + // All floating-point registers are caller-saved. + f0, f1, f2, f3, f4, f5, f6, f7, + f8, f9, f10, f11, f12, f13, f14, f15, + + // SAR (Shift Amount Register) - caller-saved, always present. + sar, + + // SCOMPARE1 - caller-saved (s32c1i option). + scompare1, + + // Loop registers - caller-saved (loop option). + lbeg, lend, lcount, + + // MAC16 registers - caller-saved (mac16 option). + acclo, acchi, + m0, m1, m2, m3, + + // Boolean registers - caller-saved (boolean option). + b0, b1, b2, b3, b4, b5, b6, b7, + b8, b9, b10, b11, b12, b13, b14, b15, + } + }, } } } diff --git a/compiler/rustc_target/src/asm/xtensa.rs b/compiler/rustc_target/src/asm/xtensa.rs new file mode 100644 index 0000000000000..622750255178f --- /dev/null +++ b/compiler/rustc_target/src/asm/xtensa.rs @@ -0,0 +1,229 @@ +use std::fmt; + +use rustc_data_structures::fx::FxIndexSet; +use rustc_span::{Symbol, kw, sym}; + +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; +use crate::spec::{RelocModel, Target}; + +def_reg_class! { + Xtensa XtensaInlineAsmRegClass { + reg, + freg, + sreg, + breg, + } +} + +impl XtensaInlineAsmRegClass { + pub fn valid_modifiers(self, _arch: super::InlineAsmArch) -> &'static [char] { + &[] + } + + pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option { + None + } + + pub fn suggest_modifier( + self, + _arch: InlineAsmArch, + _ty: InlineAsmType, + ) -> Option { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option { + None + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option)] { + match self { + Self::reg => types! { _: I8, I16, I32; }, + Self::freg => types! { fp: F32; }, + Self::sreg | Self::breg => &[], + } + } +} + +fn has_fp( + _arch: InlineAsmArch, + _reloc_model: RelocModel, + target_features: &FxIndexSet, + _target: &Target, + _is_clobber: bool, +) -> Result<(), &'static str> { + if target_features.contains(&sym::fp) { + Ok(()) + } else { + Err("target does not support floating point registers") + } +} + +// The frame pointer is a7 under the windowed ABI and a15 otherwise. Always +// reject it as an inline asm operand: LLVM can force its use for variable- +// sized stack frames (e.g. via cross-language LTO with C). +fn frame_pointer_a7( + _arch: InlineAsmArch, + _reloc_model: RelocModel, + target_features: &FxIndexSet, + _target: &Target, + _is_clobber: bool, +) -> Result<(), &'static str> { + if target_features.contains(&sym::windowed) { + Err("the frame pointer (a7) cannot be used as an operand for inline asm") + } else { + Ok(()) + } +} + +fn frame_pointer_a15( + _arch: InlineAsmArch, + _reloc_model: RelocModel, + target_features: &FxIndexSet, + _target: &Target, + _is_clobber: bool, +) -> Result<(), &'static str> { + if !target_features.contains(&sym::windowed) { + Err("the frame pointer (a15) cannot be used as an operand for inline asm") + } else { + Ok(()) + } +} + +fn has_bool( + _arch: InlineAsmArch, + _reloc_model: RelocModel, + target_features: &FxIndexSet, + _target: &Target, + _is_clobber: bool, +) -> Result<(), &'static str> { + if target_features.contains(&sym::bool) { + Ok(()) + } else { + Err("target does not support boolean registers") + } +} + +fn has_loop( + _arch: InlineAsmArch, + _reloc_model: RelocModel, + target_features: &FxIndexSet, + _target: &Target, + _is_clobber: bool, +) -> Result<(), &'static str> { + if target_features.contains(&kw::Loop) { + Ok(()) + } else { + Err("target does not support loop registers") + } +} + +fn has_mac16( + _arch: InlineAsmArch, + _reloc_model: RelocModel, + target_features: &FxIndexSet, + _target: &Target, + _is_clobber: bool, +) -> Result<(), &'static str> { + if target_features.contains(&sym::mac16) { + Ok(()) + } else { + Err("target does not support MAC16 registers") + } +} + +fn has_s32c1i( + _arch: InlineAsmArch, + _reloc_model: RelocModel, + target_features: &FxIndexSet, + _target: &Target, + _is_clobber: bool, +) -> Result<(), &'static str> { + if target_features.contains(&sym::s32c1i) { + Ok(()) + } else { + Err("target does not support the s32c1i instruction") + } +} + +def_regs! { + Xtensa XtensaInlineAsmReg XtensaInlineAsmRegClass { + a2: reg = ["a2"], + a3: reg = ["a3"], + a4: reg = ["a4"], + a5: reg = ["a5"], + a6: reg = ["a6"], + a7: reg = ["a7"] % frame_pointer_a7, + a8: reg = ["a8"], + a9: reg = ["a9"], + a10: reg = ["a10"], + a11: reg = ["a11"], + a12: reg = ["a12"], + a13: reg = ["a13"], + a14: reg = ["a14"], + a15: reg = ["a15"] % frame_pointer_a15, + f0: freg = ["f0"] % has_fp, + f1: freg = ["f1"] % has_fp, + f2: freg = ["f2"] % has_fp, + f3: freg = ["f3"] % has_fp, + f4: freg = ["f4"] % has_fp, + f5: freg = ["f5"] % has_fp, + f6: freg = ["f6"] % has_fp, + f7: freg = ["f7"] % has_fp, + f8: freg = ["f8"] % has_fp, + f9: freg = ["f9"] % has_fp, + f10: freg = ["f10"] % has_fp, + f11: freg = ["f11"] % has_fp, + f12: freg = ["f12"] % has_fp, + f13: freg = ["f13"] % has_fp, + f14: freg = ["f14"] % has_fp, + f15: freg = ["f15"] % has_fp, + // Clobber-only special registers (caller-saved per ISA ยง8.1.7, except + // LITBASE); needed for `clobber_abi`. + sar: sreg = ["sar"], + scompare1: sreg = ["scompare1"] % has_s32c1i, + lbeg: sreg = ["lbeg"] % has_loop, + lend: sreg = ["lend"] % has_loop, + lcount: sreg = ["lcount"] % has_loop, + acclo: sreg = ["acclo"] % has_mac16, + acchi: sreg = ["acchi"] % has_mac16, + m0: sreg = ["m0"] % has_mac16, + m1: sreg = ["m1"] % has_mac16, + m2: sreg = ["m2"] % has_mac16, + m3: sreg = ["m3"] % has_mac16, + // Clobber-only boolean registers. + b0: breg = ["b0"] % has_bool, + b1: breg = ["b1"] % has_bool, + b2: breg = ["b2"] % has_bool, + b3: breg = ["b3"] % has_bool, + b4: breg = ["b4"] % has_bool, + b5: breg = ["b5"] % has_bool, + b6: breg = ["b6"] % has_bool, + b7: breg = ["b7"] % has_bool, + b8: breg = ["b8"] % has_bool, + b9: breg = ["b9"] % has_bool, + b10: breg = ["b10"] % has_bool, + b11: breg = ["b11"] % has_bool, + b12: breg = ["b12"] % has_bool, + b13: breg = ["b13"] % has_bool, + b14: breg = ["b14"] % has_bool, + b15: breg = ["b15"] % has_bool, + + #error = ["a0"] => "a0 is used internally by LLVM and cannot be used as an operand for inline asm", + #error = ["sp", "a1"] => "sp is used internally by LLVM and cannot be used as an operand for inline asm", + } +} + +impl XtensaInlineAsmReg { + pub fn emit( + self, + out: &mut dyn fmt::Write, + _arch: InlineAsmArch, + _modifier: Option, + ) -> fmt::Result { + out.write_str(self.name()) + } +} diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index e2bf1c48b7b47..f9aa58196841c 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -929,6 +929,34 @@ static AVR_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-end ]; +const XTENSA_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + ("bool", Unstable(sym::xtensa_target_feature), &[]), + ("fp", Unstable(sym::xtensa_target_feature), &["bool", "coprocessor"]), + ("coprocessor", Unstable(sym::xtensa_target_feature), &[]), + ("highpriinterrupts", Unstable(sym::xtensa_target_feature), &["interrupt"]), + ("interrupt", Unstable(sym::xtensa_target_feature), &["exception"]), + ("windowed", Unstable(sym::xtensa_target_feature), &["exception"]), + ("loop", Unstable(sym::xtensa_target_feature), &[]), + ("sext", Unstable(sym::xtensa_target_feature), &[]), + ("nsa", Unstable(sym::xtensa_target_feature), &[]), + ("mul32", Unstable(sym::xtensa_target_feature), &[]), + ("mul32high", Unstable(sym::xtensa_target_feature), &["mul32"]), + ("div32", Unstable(sym::xtensa_target_feature), &[]), + ("mac16", Unstable(sym::xtensa_target_feature), &[]), + ("s32c1i", Unstable(sym::xtensa_target_feature), &[]), + ("threadptr", Unstable(sym::xtensa_target_feature), &[]), + ("extendedl32r", Unstable(sym::xtensa_target_feature), &[]), + ("atomctl", Unstable(sym::xtensa_target_feature), &[]), + ("memctl", Unstable(sym::xtensa_target_feature), &[]), + ("debug", Unstable(sym::xtensa_target_feature), &["exception"]), + ("exception", Unstable(sym::xtensa_target_feature), &[]), + ("rvector", Unstable(sym::xtensa_target_feature), &["exception"]), + ("timerint", Unstable(sym::xtensa_target_feature), &["interrupt"]), + ("prid", Unstable(sym::xtensa_target_feature), &[]), + ("regprotect", Unstable(sym::xtensa_target_feature), &[]), + ("miscsr", Unstable(sym::xtensa_target_feature), &[]), +]; + /// When rustdoc is running, provide a list of all known features so that all their respective /// primitives may be documented. /// @@ -945,6 +973,7 @@ pub fn all_rust_features() -> impl Iterator { .chain(RISCV_FEATURES.iter()) .chain(WASM_FEATURES.iter()) .chain(BPF_FEATURES.iter()) + .chain(XTENSA_FEATURES.iter()) .chain(CSKY_FEATURES) .chain(LOONGARCH_FEATURES) .chain(IBMZ_FEATURES) @@ -1032,7 +1061,8 @@ impl Target { Arch::Sparc | Arch::Sparc64 => SPARC_FEATURES, Arch::M68k => M68K_FEATURES, Arch::Avr => AVR_FEATURES, - Arch::AmdGpu | Arch::Msp430 | Arch::SpirV | Arch::Xtensa | Arch::Other(_) => &[], + Arch::Xtensa => XTENSA_FEATURES, + Arch::AmdGpu | Arch::Msp430 | Arch::SpirV | Arch::Other(_) => &[], } } diff --git a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md index 23ac46b72ea70..d09da7c4758dc 100644 --- a/src/doc/unstable-book/src/language-features/asm-experimental-arch.md +++ b/src/doc/unstable-book/src/language-features/asm-experimental-arch.md @@ -18,6 +18,7 @@ This feature tracks `asm!` and `global_asm!` support for the following architect - M68k - CSKY - SPARC +- Xtensa ## Register classes @@ -46,6 +47,10 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | CSKY | `freg` | `f[0-31]` | `f` | | SPARC | `reg` | `r[2-29]` | `r` | | SPARC | `yreg` | `y` | Only clobbers | +| Xtensa | `reg` | `a[2-15]` | `r` | +| Xtensa | `freg` | `f[0-15]` | `f` | +| Xtensa | `sreg` | `sar`, `scompare1`, `lbeg`, `lend`, `lcount`, `acclo`, `acchi`, `m[0-3]` | Only clobbers | +| Xtensa | `breg` | `b[0-15]` | Only clobbers | > **Notes**: > - NVPTX doesn't have a fixed register set, so named registers are not supported. @@ -77,6 +82,10 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | CSKY | `freg` | None | `f32`, | | SPARC | `reg` | None | `i8`, `i16`, `i32`, `i64` (SPARC64 only) | | SPARC | `yreg` | N/A | Only clobbers | +| Xtensa | `reg` | None | `i8`, `i16`, `i32` | +| Xtensa | `freg` | `fp` | `f32` | +| Xtensa | `sreg` | N/A | Only clobbers | +| Xtensa | `breg` | `bool` | Only clobbers | ## Register aliases @@ -113,6 +122,7 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | SPARC | `r[8-15]` | `o[0-7]` | | SPARC | `r[16-23]` | `l[0-7]` | | SPARC | `r[24-31]` | `i[0-7]` | +| Xtensa | `a1` | `sp` | > **Notes**: > - TI does not mandate a frame pointer for MSP430, but toolchains are allowed @@ -145,6 +155,9 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | SPARC | `r5`/`g5` | Reserved for system. (SPARC32 only) | | SPARC | `r6`/`g6`, `r7`/`g7` | Reserved for system. | | SPARC | `r31`/`i7` | Return address cannot be used as inputs or outputs. | +| Xtensa | `a0` | This is the return address register and is used internally by LLVM. | +| Xtensa | `a1`/`sp` | This is the stack pointer and is used internally by LLVM. | +| Xtensa | `a7` (windowed ABI) / `a15` (call0 ABI) | The frame pointer cannot be used as an input or output. | ## Template modifiers @@ -160,6 +173,8 @@ This feature tracks `asm!` and `global_asm!` support for the following architect | SPARC | `reg` | None | `%o0` | None | | CSKY | `reg` | None | `r0` | None | | CSKY | `freg` | None | `f0` | None | +| Xtensa | `reg` | None | `a2` | None | +| Xtensa | `freg` | None | `f0` | None | # Flags covered by `preserves_flags` diff --git a/tests/assembly-llvm/asm/xtensa-types.rs b/tests/assembly-llvm/asm/xtensa-types.rs new file mode 100644 index 0000000000000..23c8d83e3ea30 --- /dev/null +++ b/tests/assembly-llvm/asm/xtensa-types.rs @@ -0,0 +1,232 @@ +//@ add-minicore +//@ assembly-output: emit-asm +//@ compile-flags: --target xtensa-esp32-none-elf +//@ needs-llvm-components: xtensa + +#![feature(no_core, lang_items, rustc_attrs, repr_simd, asm_experimental_arch)] +#![crate_type = "rlib"] +#![no_core] +#![allow(asm_sub_register, non_camel_case_types)] + +extern crate minicore; +use minicore::*; + +type ptr = *mut u8; + +extern "C" { + fn extern_func(); +} + +// Hack to avoid function merging +extern "Rust" { + fn dont_merge(s: &str); +} + +// CHECK-LABEL: sym_fn: +// CHECK: #APP +// CHECK: call4 extern_func +// CHECK: #NO_APP +#[no_mangle] +pub unsafe fn sym_fn() { + asm!("call4 {}", sym extern_func); +} + +macro_rules! check_general_reg { + ($func:ident $ty:ident $class:ident $mov:literal) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + dont_merge(stringify!($func)); + + let y; + asm!(concat!($mov, " {}, {}"), out($class) y, in($class) x); + y + } + }; +} + +// CHECK-LABEL: reg_i8: +// CHECK: #APP +// CHECK: or a{{[0-9]+}}, a{{[0-9]+}}, a{{[0-9]+}} +// CHECK: #NO_APP +check_general_reg!(reg_i8 i8 reg "mov"); + +// CHECK-LABEL: reg_i16: +// CHECK: #APP +// CHECK: or a{{[0-9]+}}, a{{[0-9]+}}, a{{[0-9]+}} +// CHECK: #NO_APP +check_general_reg!(reg_i16 i16 reg "mov"); + +// CHECK-LABEL: reg_i32: +// CHECK: #APP +// CHECK: or a{{[0-9]+}}, a{{[0-9]+}}, a{{[0-9]+}} +// CHECK: #NO_APP +check_general_reg!(reg_i32 i32 reg "mov"); + +// CHECK-LABEL: reg_ptr: +// CHECK: #APP +// CHECK: or a{{[0-9]+}}, a{{[0-9]+}}, a{{[0-9]+}} +// CHECK: #NO_APP +check_general_reg!(reg_ptr ptr reg "mov"); + +// CHECK-LABEL: freg_f32: +// CHECK: #APP +// CHECK: mov.s f{{[0-9]+}}, f{{[0-9]+}} +// CHECK: #NO_APP +check_general_reg!(freg_f32 f32 freg "mov.s"); + +macro_rules! check_explicit_reg { + ($func:ident $ty:ident $reg:tt $mov:literal) => { + #[no_mangle] + pub unsafe fn $func(x: $ty) -> $ty { + dont_merge(stringify!($func)); + + let y; + asm!(concat!($mov, " ", $reg, ", ", $reg), lateout($reg) y, in($reg) x); + y + } + }; +} + +// CHECK-LABEL: a2_i32: +// CHECK: #APP +// CHECK: or a2, a2, a2 +// CHECK: #NO_APP +check_explicit_reg!(a2_i32 i32 "a2" "mov"); + +// CHECK-LABEL: a5_i8: +// CHECK: #APP +// CHECK: or a5, a5, a5 +// CHECK: #NO_APP +check_explicit_reg!(a5_i8 i8 "a5" "mov"); + +// CHECK-LABEL: a5_i16: +// CHECK: #APP +// CHECK: or a5, a5, a5 +// CHECK: #NO_APP +check_explicit_reg!(a5_i16 i16 "a5" "mov"); + +// CHECK-LABEL: a5_i32: +// CHECK: #APP +// CHECK: or a5, a5, a5 +// CHECK: #NO_APP +check_explicit_reg!(a5_i32 i32 "a5" "mov"); + +// CHECK-LABEL: a5_ptr: +// CHECK: #APP +// CHECK: or a5, a5, a5 +// CHECK: #NO_APP +check_explicit_reg!(a5_ptr ptr "a5" "mov"); + +// CHECK-LABEL: a14_i32: +// CHECK: #APP +// CHECK: or a14, a14, a14 +// CHECK: #NO_APP +check_explicit_reg!(a14_i32 i32 "a14" "mov"); + +// a15 is the frame pointer under CALL0, but usable under the windowed ABI +// (this test target). +// CHECK-LABEL: a15_i32: +// CHECK: #APP +// CHECK: or a15, a15, a15 +// CHECK: #NO_APP +check_explicit_reg!(a15_i32 i32 "a15" "mov"); + +// CHECK-LABEL: f0_f32: +// CHECK: #APP +// CHECK: mov.s f0, f0 +// CHECK: #NO_APP +check_explicit_reg!(f0_f32 f32 "f0" "mov.s"); + +// Special/Boolean registers are clobber-only. +macro_rules! check_clobber { + ($func:ident $reg:tt $insn:literal) => { + #[no_mangle] + pub unsafe fn $func(x: i32) { + dont_merge(stringify!($func)); + asm!($insn, in(reg) x, out($reg) _); + } + }; +} + +// CHECK-LABEL: sar_clobber: +// CHECK: #APP +// CHECK: wsr a{{[0-9]+}}, sar +// CHECK: #NO_APP +check_clobber!(sar_clobber "sar" "wsr {0}, sar"); + +// CHECK-LABEL: scompare1_clobber: +// CHECK: #APP +// CHECK: wsr a{{[0-9]+}}, scompare1 +// CHECK: #NO_APP +check_clobber!(scompare1_clobber "scompare1" "wsr {0}, scompare1"); + +// CHECK-LABEL: lbeg_clobber: +// CHECK: #APP +// CHECK: wsr a{{[0-9]+}}, lbeg +// CHECK: #NO_APP +check_clobber!(lbeg_clobber "lbeg" "wsr {0}, lbeg"); + +// CHECK-LABEL: lend_clobber: +// CHECK: #APP +// CHECK: wsr a{{[0-9]+}}, lend +// CHECK: #NO_APP +check_clobber!(lend_clobber "lend" "wsr {0}, lend"); + +// CHECK-LABEL: lcount_clobber: +// CHECK: #APP +// CHECK: wsr a{{[0-9]+}}, lcount +// CHECK: #NO_APP +check_clobber!(lcount_clobber "lcount" "wsr {0}, lcount"); + +// CHECK-LABEL: acclo_clobber: +// CHECK: #APP +// CHECK: wsr a{{[0-9]+}}, acclo +// CHECK: #NO_APP +check_clobber!(acclo_clobber "acclo" "wsr {0}, acclo"); + +// CHECK-LABEL: acchi_clobber: +// CHECK: #APP +// CHECK: wsr a{{[0-9]+}}, acchi +// CHECK: #NO_APP +check_clobber!(acchi_clobber "acchi" "wsr {0}, acchi"); + +// CHECK-LABEL: m0_clobber: +// CHECK: #APP +// CHECK: wsr a{{[0-9]+}}, m0 +// CHECK: #NO_APP +check_clobber!(m0_clobber "m0" "wsr {0}, m0"); + +// CHECK-LABEL: m3_clobber: +// CHECK: #APP +// CHECK: wsr a{{[0-9]+}}, m3 +// CHECK: #NO_APP +check_clobber!(m3_clobber "m3" "wsr {0}, m3"); + +// Boolean registers are bits of BR; write them via `andb`. +macro_rules! check_breg_clobber { + ($func:ident $reg:tt) => { + #[no_mangle] + pub unsafe fn $func() { + dont_merge(stringify!($func)); + asm!(concat!("andb ", $reg, ", ", $reg, ", ", $reg), out($reg) _); + } + }; +} + +// CHECK-LABEL: b0_clobber: +// CHECK: #APP +// CHECK: andb b0, b0, b0 +// CHECK: #NO_APP +check_breg_clobber!(b0_clobber "b0"); + +// CHECK-LABEL: b7_clobber: +// CHECK: #APP +// CHECK: andb b7, b7, b7 +// CHECK: #NO_APP +check_breg_clobber!(b7_clobber "b7"); + +// CHECK-LABEL: b15_clobber: +// CHECK: #APP +// CHECK: andb b15, b15, b15 +// CHECK: #NO_APP +check_breg_clobber!(b15_clobber "b15"); diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index 981c173242408..57ae328a20ad6 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -30,6 +30,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `amx-tf32` `amx-tile` `apxf` +`atomctl` `atomics` `audio` `avx` @@ -67,11 +68,13 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `cache` `cmpxchg16b` `concurrent-functions` +`coprocessor` `crc` `crt-static` `cssc` `d` `d32` +`debug` `deflate-conversion` `dit` `div32` @@ -93,8 +96,10 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `elrw` `enhanced-sort` `ermsb` +`exception` `exception-handling` `extended-const` +`extendedl32r` `f` `f16c` `f32mm` @@ -112,6 +117,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `floate1` `fma` `fma4` +`fp` `fp-armv8` `fp16` `fp64` @@ -138,6 +144,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `hbc` `high-registers` `high-word` +`highpriinterrupts` `hvx` `hvx-ieee-fp` `hvx-length128b` @@ -157,6 +164,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `hwdiv` `i8mm` `ijmpcall` +`interrupt` `isa-68000` `isa-68010` `isa-68020` @@ -175,6 +183,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `lbt` `ld-seq-sa` `leoncasa` +`loop` `lor` `lowbytefirst` `lpm` @@ -187,7 +196,9 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `lvz` `lzcnt` `m` +`mac16` `mclass` +`memctl` `message-security-assist-extension12` `message-security-assist-extension3` `message-security-assist-extension4` @@ -197,6 +208,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `miscellaneous-extensions-2` `miscellaneous-extensions-3` `miscellaneous-extensions-4` +`miscsr` `mops` `movbe` `movrs` @@ -207,11 +219,14 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `msync` `mte` `mul` +`mul32` +`mul32high` `multivalue` `mutable-globals` `neon` `nnp-assist` `nontrapping-fptoint` +`nsa` `nvic` `outline-atomics` `paca` @@ -229,6 +244,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `power9-altivec` `power9-vector` `prfchw` +`prid` `ptx70` `ptx71` `ptx72` @@ -257,13 +273,17 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `rdrand` `rdseed` `reference-types` +`regprotect` `relax` `relaxed-simd` `rmw` `rtm` `rva23u64` +`rvector` +`s32c1i` `sb` `scq` +`sext` `sha` `sha2` `sha3` @@ -324,8 +344,10 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `sve2p1` `tail-call` `tbm` +`threadptr` `thumb-mode` `thumb2` +`timerint` `tinyencoding` `tme` `transactional-execution` @@ -392,6 +414,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `wfxt` `wide-arithmetic` `widekl` +`windowed` `x87` `xop` `xsave`