From f29def76a9b23d8a2512c7bf1999dec05fb4f675 Mon Sep 17 00:00:00 2001 From: Scott Mabin Date: Thu, 12 Aug 2021 12:05:56 +0100 Subject: [PATCH] asm! support for the Xtensa architecture (#68) --- compiler/rustc_codegen_llvm/src/asm.rs | 8 + compiler/rustc_span/src/symbol.rs | 24 ++ compiler/rustc_target/src/asm/mod.rs | 29 ++ compiler/rustc_target/src/asm/xtensa.rs | 293 +++++++++++++++++++ compiler/rustc_target/src/target_features.rs | 30 ++ tests/assembly/asm/xtensa-types.rs | 140 +++++++++ 6 files changed, 524 insertions(+) create mode 100644 compiler/rustc_target/src/asm/xtensa.rs create mode 100644 tests/assembly/asm/xtensa-types.rs diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index 1d91c3fb17ddc..ad7a438dd7d2d 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -270,6 +270,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()); @@ -701,6 +702,9 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) -> | X86InlineAsmRegClass::tmm_reg, ) => unreachable!("clobber-only"), InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => "r", + InlineAsmRegClass::Xtensa(XtensaInlineAsmRegClass::reg) => "r", + InlineAsmRegClass::Xtensa(XtensaInlineAsmRegClass::freg) => "f", + InlineAsmRegClass::Xtensa(XtensaInlineAsmRegClass::breg) => "b", InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => "r", InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => "w", InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg) => "r", @@ -763,6 +767,7 @@ fn modifier_to_llvm( InlineAsmRegClass::Mips(_) => None, InlineAsmRegClass::Nvptx(_) => None, InlineAsmRegClass::PowerPC(_) => None, + InlineAsmRegClass::Xtensa(_) => None, InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::reg) | InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::freg) => None, InlineAsmRegClass::RiscV(RiscVInlineAsmRegClass::vreg) => { @@ -880,6 +885,9 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &' unreachable!("clobber-only") } InlineAsmRegClass::Wasm(WasmInlineAsmRegClass::local) => cx.type_i32(), + InlineAsmRegClass::Xtensa(XtensaInlineAsmRegClass::reg) => cx.type_i32(), + InlineAsmRegClass::Xtensa(XtensaInlineAsmRegClass::freg) => cx.type_f32(), + InlineAsmRegClass::Xtensa(XtensaInlineAsmRegClass::breg) => cx.type_i1(), InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::reg) => cx.type_i64(), InlineAsmRegClass::Bpf(BpfInlineAsmRegClass::wreg) => cx.type_i32(), InlineAsmRegClass::Avr(AvrInlineAsmRegClass::reg) => cx.type_i8(), diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 16f5e55835ea7..c473e26ad1e40 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -466,6 +466,7 @@ symbols! { async_for_loop, async_iterator, async_iterator_poll_next, + atomctl, atomic, atomic_mod, atomics, @@ -507,6 +508,7 @@ symbols! { braced_empty_structs, branch, breakpoint, + breg, bridge, bswap, builtin_syntax, @@ -641,6 +643,7 @@ symbols! { constant, constructor, convert_identity, + coprocessor, copy, copy_closures, copy_nonoverlapping, @@ -731,6 +734,7 @@ symbols! { derive_smart_pointer, destruct, destructuring_assignment, + dfpaccel, diagnostic, diagnostic_namespace, direct, @@ -794,6 +798,7 @@ symbols! { ermsb_target_feature, exact_div, except, + exception, exchange_malloc, exclusive_range_pattern, exhaustive_integer_patterns, @@ -817,6 +822,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, @@ -921,6 +927,7 @@ symbols! { format_macro, format_placeholder, format_unsafe_arg, + fp, freeze, freeze_impls, freg, @@ -972,6 +979,7 @@ symbols! { hash, hexagon_target_feature, hidden, + highpriinterrupts, homogeneous_aggregate, host, html_favicon_url, @@ -1050,6 +1058,8 @@ symbols! { instruction_set, integer_: "integer", // underscore to avoid clashing with the function `sym::integer` below integral, + intel, + interrupt, into_async_iter_into_iter, into_future, into_iter, @@ -1137,6 +1147,7 @@ symbols! { loongarch_target_feature, loop_break_value, lt, + mac16, macro_at_most_once_rep, macro_attributes_in_derive_output, macro_escape, @@ -1180,6 +1191,7 @@ symbols! { mem_variant_count, mem_zeroed, member_constraints, + memctl, memory, memtag, message, @@ -1234,6 +1246,8 @@ symbols! { mir_unwind_unreachable, mir_variant, miri, + misc, + miscsr, mmx_reg, modifiers, module, @@ -1450,6 +1464,8 @@ symbols! { prelude_import, preserves_flags, prfchw_target_feature, + prid, + primitive, print_macro, println_macro, proc_dash_macro: "proc-macro", @@ -1702,8 +1718,10 @@ symbols! { rustdoc_missing_doc_code_examples, rustfmt, rvalue_static_promotion, + rvector, rwpi, s, + s32c1i, s390x_target_feature, safety, sanitize, @@ -1900,10 +1918,12 @@ symbols! { thread, thread_local, thread_local_macro, + threadptr, three_way_compare, thumb2, thumb_mode: "thumb-mode", time, + timerint, tmm_reg, to_owned_method, to_string, @@ -2081,6 +2101,8 @@ symbols! { wasm_import_module, wasm_target_feature, while_let, + width, + windowed, windows, windows_subsystem, with_negative_coherence, @@ -2099,8 +2121,10 @@ symbols! { x86_amx_intrinsics, x87_reg, xer, + xloop, xmm_reg, xop_target_feature, + 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 4d8c5cea8a830..585d86c67f323 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -194,6 +194,7 @@ mod s390x; mod spirv; mod wasm; mod x86; +mod xtensa; pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass}; pub use arm::{ArmInlineAsmReg, ArmInlineAsmRegClass}; @@ -211,6 +212,7 @@ pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass}; pub use s390x::{S390xInlineAsmReg, S390xInlineAsmRegClass}; pub use spirv::{SpirVInlineAsmReg, SpirVInlineAsmRegClass}; pub use wasm::{WasmInlineAsmReg, WasmInlineAsmRegClass}; +pub use xtensa::{XtensaInlineAsmReg, XtensaInlineAsmRegClass}; pub use x86::{X86InlineAsmReg, X86InlineAsmRegClass}; #[derive(Copy, Clone, Encodable, Decodable, Debug, Eq, PartialEq, Hash)] @@ -233,6 +235,7 @@ pub enum InlineAsmArch { SpirV, Wasm32, Wasm64, + Xtensa, Bpf, Avr, Msp430, @@ -263,6 +266,7 @@ impl FromStr for InlineAsmArch { "spirv" => Ok(Self::SpirV), "wasm32" => Ok(Self::Wasm32), "wasm64" => Ok(Self::Wasm64), + "xtensa" => Ok(Self::Xtensa), "bpf" => Ok(Self::Bpf), "avr" => Ok(Self::Avr), "msp430" => Ok(Self::Msp430), @@ -288,6 +292,7 @@ pub enum InlineAsmReg { S390x(S390xInlineAsmReg), SpirV(SpirVInlineAsmReg), Wasm(WasmInlineAsmReg), + Xtensa(XtensaInlineAsmReg), Bpf(BpfInlineAsmReg), Avr(AvrInlineAsmReg), Msp430(Msp430InlineAsmReg), @@ -309,6 +314,7 @@ impl InlineAsmReg { Self::LoongArch(r) => r.name(), Self::Mips(r) => r.name(), Self::S390x(r) => r.name(), + Self::Xtensa(r) => r.name(), Self::Bpf(r) => r.name(), Self::Avr(r) => r.name(), Self::Msp430(r) => r.name(), @@ -329,6 +335,7 @@ impl InlineAsmReg { Self::LoongArch(r) => InlineAsmRegClass::LoongArch(r.reg_class()), Self::Mips(r) => InlineAsmRegClass::Mips(r.reg_class()), Self::S390x(r) => InlineAsmRegClass::S390x(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()), @@ -360,6 +367,9 @@ 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::SpirV => Self::SpirV(SpirVInlineAsmReg::parse(name)?), InlineAsmArch::Wasm32 | InlineAsmArch::Wasm64 => { @@ -395,6 +405,7 @@ impl InlineAsmReg { 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::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), @@ -420,6 +431,7 @@ impl InlineAsmReg { Self::LoongArch(r) => r.emit(out, arch, modifier), Self::Mips(r) => r.emit(out, arch, modifier), Self::S390x(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), @@ -440,6 +452,7 @@ impl InlineAsmReg { Self::LoongArch(_) => cb(self), Self::Mips(_) => cb(self), Self::S390x(_) => 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), @@ -465,6 +478,7 @@ pub enum InlineAsmRegClass { S390x(S390xInlineAsmRegClass), SpirV(SpirVInlineAsmRegClass), Wasm(WasmInlineAsmRegClass), + Xtensa(XtensaInlineAsmRegClass), Bpf(BpfInlineAsmRegClass), Avr(AvrInlineAsmRegClass), Msp430(Msp430InlineAsmRegClass), @@ -489,6 +503,7 @@ impl InlineAsmRegClass { Self::S390x(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(), @@ -515,6 +530,7 @@ impl InlineAsmRegClass { Self::S390x(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::S390x), 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), @@ -544,6 +560,7 @@ impl InlineAsmRegClass { Self::S390x(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), @@ -573,6 +590,7 @@ impl InlineAsmRegClass { Self::S390x(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), @@ -601,6 +619,7 @@ impl InlineAsmRegClass { Self::S390x(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), @@ -638,6 +657,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)?), @@ -660,6 +680,7 @@ impl InlineAsmRegClass { Self::S390x(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), @@ -704,6 +725,7 @@ impl fmt::Display for InlineAsmRegOrRegClass { /// Set of types which can be used with a particular register class. #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum InlineAsmType { + I1, I8, I16, I32, @@ -731,6 +753,7 @@ impl InlineAsmType { pub fn size(self) -> Size { Size::from_bytes(match self { + Self::I1 => return Size::from_bits(1), Self::I8 => 1, Self::I16 => 2, Self::I32 => 4, @@ -756,6 +779,7 @@ impl InlineAsmType { impl fmt::Display for InlineAsmType { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { + Self::I1 => f.write_str("i1"), Self::I8 => f.write_str("i8"), Self::I16 => f.write_str("i16"), Self::I32 => f.write_str("i32"), @@ -853,6 +877,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); diff --git a/compiler/rustc_target/src/asm/xtensa.rs b/compiler/rustc_target/src/asm/xtensa.rs new file mode 100644 index 0000000000000..6fd9aed0e09b4 --- /dev/null +++ b/compiler/rustc_target/src/asm/xtensa.rs @@ -0,0 +1,293 @@ +use super::{InlineAsmArch, InlineAsmType, ModifierInfo}; +use crate::spec::{Target, RelocModel}; +use rustc_data_structures::fx::FxIndexSet; +use rustc_span::{sym, Symbol}; +use std::fmt; + +def_reg_class! { + Xtensa XtensaInlineAsmRegClass { + reg, + freg, + 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::breg => types! { bool: I1; }, + Self::freg => types! { fp: F32; dfpaccel: F64; }, + } + } +} + +// Xtensa has lots of features - macro to reduce boiler plate +macro_rules! feature { + ($fnname:ident, $feature:expr) => { + fn $fnname( + _arch: InlineAsmArch, + _reloc_model: RelocModel, + target_features: &FxIndexSet, + _target: &Target, + _is_clobber: bool, + ) -> Result<(), &'static str> { + if target_features.contains(&$feature) { + Ok(()) + } else { + Err(concat!("target does not support ", stringify!($feature), " registers")) + } + } + }; +} + +feature!(has_fp, sym::fp); +feature!(has_dfpaccel, sym::dfpaccel); +feature!(has_bool, sym::bool); +feature!(has_xloop, sym::xloop); +feature!(has_extendedl32r, sym::extendedl32r); +feature!(has_s32c1i, sym::s32c1i); +feature!(has_mac16, sym::mac16); +feature!(has_windowed, sym::windowed); +feature!(has_debug, sym::debug); +feature!(has_memctl, sym::memctl); +feature!(has_atomctl, sym::atomctl); +feature!(has_exception, sym::exception); +feature!(has_highpriinterrupts, sym::highpriinterrupts); +feature!(has_coprocessor, sym::coprocessor); +feature!(has_rvector, sym::rvector); +feature!(has_timerint, sym::timerint); +feature!(has_interrupt, sym::interrupt); +feature!(has_prid, sym::prid); +feature!(has_miscsr, sym::miscsr); +feature!(has_threadptr, sym::threadptr); + +fn has_expstate( + _arch: InlineAsmArch, + _reloc_model: RelocModel, + _target_features: &FxIndexSet, + target: &Target, + _is_clobber: bool, +) -> Result<(), &'static str> { + match target.cpu.as_ref() { + "esp32" => Ok(()), + _ => Err("target does not support expstate registers") + } +} +fn has_gpio_out( + _arch: InlineAsmArch, + _reloc_model: RelocModel, + _target_features: &FxIndexSet, + target: &Target, + _is_clobber: bool, +) -> Result<(), &'static str> { + match target.cpu.as_ref() { + "esp32-s2" => Ok(()), + _ => Err("target does not support gpio_out registers") + } +} + +// FIXME sometimes there isn't a frame pointer at all? +fn frame_pointer_is_a7( + _arch: InlineAsmArch, + _reloc_model: RelocModel, + target_features: &FxIndexSet, + _target: &Target, + _is_clobber: bool, +) -> bool { + target_features.contains(&sym::windowed) +} + +fn frame_pointer_a7( + arch: InlineAsmArch, + reloc_model: RelocModel, + target_features: &FxIndexSet, + target: &Target, + is_clobber: bool, +) -> Result<(), &'static str> { + if frame_pointer_is_a7(arch, reloc_model, target_features, target, is_clobber) { + 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 !frame_pointer_is_a7(arch, reloc_model, target_features, target, is_clobber) { + Err("the frame pointer (a15) cannot be used as an operand for inline asm") + } else { + Ok(()) + } +} + +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, + sar: reg = ["sar"], + configid0: reg = ["configid0"], + configid1: reg = ["configid1"], + lbeg: reg = ["lbeg"] % has_xloop, + lend: reg = ["lend"] % has_xloop, + lcount: reg = ["lcount"] % has_xloop, + litbase: reg = ["litbase"] % has_extendedl32r, + scompare1: reg = ["scompare1"] % has_s32c1i, + acclo: reg = ["acclo"] % has_mac16, + acchi: reg = ["acchi"] % has_mac16, + m0: reg = ["m0"] % has_mac16, + m1: reg = ["m1"] % has_mac16, + m2: reg = ["m2"] % has_mac16, + m3: reg = ["m3"] % has_mac16, + windowbase: reg = ["windowbase"] % has_windowed, + windowstart: reg = ["windowstart"] % has_windowed, + ddr: reg = ["ddr"] % has_debug, + ibreakenable: reg = ["ibreakenable"] % has_debug, + ibreaka0: reg = ["ibreaka0"] % has_debug, + ibreaka1: reg = ["ibreaka1"] % has_debug, + dbreaka0: reg = ["dbreaka0"] % has_debug, + dbreaka1: reg = ["dbreaka1"] % has_debug, + dbreakc0: reg = ["dbreakc0"] % has_debug, + dbreakc1: reg = ["dbreakc1"] % has_debug, + icount: reg = ["icount"] % has_debug, + icountlevel: reg = ["icountlevel"] % has_debug, + debugcause: reg = ["debugcause"] % has_debug, + memctl: reg = ["memctl"] % has_memctl, + atomctl: reg = ["atomctl"] % has_atomctl, + ps: reg = ["ps"] % has_exception, + epc1: reg = ["epc1"] % has_exception, + epc2: reg = ["epc2"] % has_highpriinterrupts, + epc3: reg = ["epc3"] % has_highpriinterrupts, + epc4: reg = ["epc4"] % has_highpriinterrupts, + epc5: reg = ["epc5"] % has_highpriinterrupts, + epc6: reg = ["epc6"] % has_highpriinterrupts, + epc7: reg = ["epc7"] % has_highpriinterrupts, + depc: reg = ["depc"] % has_exception, + eps2: reg = ["eps2"] % has_highpriinterrupts, + eps3: reg = ["eps3"] % has_highpriinterrupts, + eps4: reg = ["eps4"] % has_highpriinterrupts, + eps5: reg = ["eps5"] % has_highpriinterrupts, + eps6: reg = ["eps6"] % has_highpriinterrupts, + eps7: reg = ["eps7"] % has_highpriinterrupts, + excsave1: reg = ["excsave1"] % has_exception, + excsave2: reg = ["excsave2"] % has_highpriinterrupts, + excsave3: reg = ["excsave3"] % has_highpriinterrupts, + excsave4: reg = ["excsave4"] % has_highpriinterrupts, + excsave5: reg = ["excsave5"] % has_highpriinterrupts, + excsave6: reg = ["excsave6"] % has_highpriinterrupts, + excsave7: reg = ["excsave7"] % has_highpriinterrupts, + exccause: reg = ["exccause"] % has_exception, + excvaddr: reg = ["excvaddr"] % has_exception, + cpenable: reg = ["cpenable"] % has_coprocessor, + vecbase: reg = ["vecbase"] % has_rvector, + interrupt: reg = ["interrupt"] % has_interrupt, + intclear: reg = ["intclear"] % has_interrupt, + intenable: reg = ["intenable"] % has_interrupt, + prid: reg = ["prid"] % has_prid, + ccount: reg = ["ccount"] % has_timerint, + ccompare0: reg = ["ccompare0"] % has_timerint, + ccompare1: reg = ["ccompare1"] % has_timerint, + ccompare2: reg = ["ccompare2"] % has_timerint, + misc0: reg = ["misc0"] % has_miscsr, + misc1: reg = ["misc1"] % has_miscsr, + misc2: reg = ["misc2"] % has_miscsr, + misc3: reg = ["misc3"] % has_miscsr, + threadptr: reg = ["threadptr"] % has_threadptr, + fcr: reg = ["fcr"] % has_dfpaccel, + fsr: reg = ["fsr"] % has_dfpaccel, + f64r_lo: reg = ["f64r_lo"] % has_dfpaccel, + f64r_hi: reg = ["f64r_hi"] % has_dfpaccel, + f64s: reg = ["f64s"] % has_dfpaccel, + 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, + br: reg = ["br"] % has_bool, + 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, + + // Custom TIE extensions - https://en.wikipedia.org/wiki/Tensilica_Instruction_Extension + gpio_out: reg = ["gpio_out"] % has_gpio_out, + expstate: reg = ["expstate"] % has_expstate, + + #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 8319cb880cc79..86dab5f1399c5 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -497,6 +497,35 @@ const IBMZ_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // tidy-alphabetical-end ]; +const XTENSA_ALLOWED_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ + ("fp", Unstable(sym::xtensa_target_feature), &[]), + ("windowed", Unstable(sym::xtensa_target_feature), &[]), + ("bool", Unstable(sym::xtensa_target_feature), &[]), + ("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), &[]), + ("div32", Unstable(sym::xtensa_target_feature), &[]), + ("mac16", Unstable(sym::xtensa_target_feature), &[]), + ("dfpaccel", 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", Unstable(sym::xtensa_target_feature), &[]), + ("highpriinterrupts", Unstable(sym::xtensa_target_feature), &[]), + ("coprocessor", Unstable(sym::xtensa_target_feature), &[]), + ("interrupt", Unstable(sym::xtensa_target_feature), &[]), + ("rvector", Unstable(sym::xtensa_target_feature), &[]), + ("timerint", Unstable(sym::xtensa_target_feature), &[]), + ("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. /// @@ -512,6 +541,7 @@ pub fn all_known_features() -> impl Iterator { .chain(RISCV_ALLOWED_FEATURES.iter()) .chain(WASM_ALLOWED_FEATURES.iter()) .chain(BPF_ALLOWED_FEATURES.iter()) + .chain(XTENSA_ALLOWED_FEATURES.iter()) .chain(CSKY_ALLOWED_FEATURES) .chain(LOONGARCH_ALLOWED_FEATURES) .chain(IBMZ_ALLOWED_FEATURES) diff --git a/tests/assembly/asm/xtensa-types.rs b/tests/assembly/asm/xtensa-types.rs new file mode 100644 index 0000000000000..8bdecc4f37b46 --- /dev/null +++ b/tests/assembly/asm/xtensa-types.rs @@ -0,0 +1,140 @@ +// min-llvm-version: 10.0.1 +// assembly-output: emit-asm +// compile-flags: --target xtensa-esp32-none-elf +// needs-llvm-components: xtensa + +#![feature(no_core, lang_items, rustc_attrs, repr_simd)] +#![crate_type = "rlib"] +#![no_core] +#![allow(asm_sub_register, non_camel_case_types)] + +#[rustc_builtin_macro] +macro_rules! asm { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! concat { + () => {}; +} +#[rustc_builtin_macro] +macro_rules! stringify { + () => {}; +} + +#[lang = "sized"] +trait Sized {} +#[lang = "copy"] +trait Copy {} + +type ptr = *const i32; + +impl Copy for i8 {} +impl Copy for i16 {} +impl Copy for i32 {} +impl Copy for f32 {} +impl Copy for f64 {} +impl Copy for ptr {} + +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: mov a{{[0-9]+}}, a{{[0-9]+}} +// CHECK: #NO_APP +check_general_reg!(reg_i8 i8 reg "mov"); + +// CHECK-LABEL: reg_i16: +// CHECK: #APP +// CHECK: mov a{{[0-9]+}}, a{{[0-9]+}} +// CHECK: #NO_APP +check_general_reg!(reg_i16 i16 reg "mov"); + +// CHECK-LABEL: reg_i32: +// CHECK: #APP +// CHECK: mov a{{[0-9]+}}, a{{[0-9]+}} +// CHECK: #NO_APP +check_general_reg!(reg_i32 i32 reg "mov"); + +// CHECK-LABEL: reg_ptr: +// CHECK: #APP +// CHECK: mov 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: a5_i8: +// CHECK: #APP +// CHECK: mov a5, a5 +// CHECK: #NO_APP +check_explicit_reg!(a5_i8 i8 "a5" "mov"); + +// CHECK-LABEL: a5_i16: +// CHECK: #APP +// CHECK: mov a5, a5 +// CHECK: #NO_APP +check_explicit_reg!(a5_i16 i16 "a5" "mov"); + +// CHECK-LABEL: a0_i32: +// CHECK: #APP +// CHECK: mov a5, a5 +// CHECK: #NO_APP +check_explicit_reg!(a5_i32 i32 "a5" "mov"); + +// CHECK-LABEL: a5_ptr: +// CHECK: #APP +// CHECK: mov a5, a5 +// CHECK: #NO_APP +check_explicit_reg!(a5_ptr ptr "a5" "mov"); + +// CHECK-LABEL: f0_f32: +// CHECK: #APP +// CHECK: mov.s f0, f0 +// CHECK: #NO_APP +check_explicit_reg!(f0_f32 f32 "f0" "mov.s");