diff --git a/compiler/rustc_codegen_llvm/src/asm.rs b/compiler/rustc_codegen_llvm/src/asm.rs index ecf62ed213df8..c2288b7cdbe0c 100644 --- a/compiler/rustc_codegen_llvm/src/asm.rs +++ b/compiler/rustc_codegen_llvm/src/asm.rs @@ -288,6 +288,7 @@ impl AsmBuilderMethods<'tcx> for Builder<'a, 'll, 'tcx> { InlineAsmArch::Mips | InlineAsmArch::Mips64 => {} InlineAsmArch::SpirV => {} InlineAsmArch::Wasm32 => {} + InlineAsmArch::Xtensa => {} InlineAsmArch::Bpf => {} } } @@ -594,6 +595,9 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'tcx>>) InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => "v", InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => "^Yk", 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::SpirV(SpirVInlineAsmRegClass::reg) => { @@ -637,6 +641,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::X86(X86InlineAsmRegClass::reg) @@ -712,6 +717,9 @@ fn dummy_output_type(cx: &CodegenCx<'ll, 'tcx>, reg: InlineAsmRegClass) -> &'ll | InlineAsmRegClass::X86(X86InlineAsmRegClass::zmm_reg) => cx.type_f32(), InlineAsmRegClass::X86(X86InlineAsmRegClass::kreg) => cx.type_i16(), 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::SpirV(SpirVInlineAsmRegClass::reg) => { diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index c89d42ecc58ac..a6c2ed076b6ec 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -210,6 +210,34 @@ const WASM_ALLOWED_FEATURES: &[(&str, Option)] = &[ ("nontrapping-fptoint", Some(sym::wasm_target_feature)), ]; +const XTENSA_ALLOWED_FEATURES: &[(&str, Option)] = &[ + ("fp", Some(sym::xtensa_target_feature)), + ("windowed", Some(sym::xtensa_target_feature)), + ("bool", Some(sym::xtensa_target_feature)), + ("loop", Some(sym::xtensa_target_feature)), + ("sext", Some(sym::xtensa_target_feature)), + ("nsa", Some(sym::xtensa_target_feature)), + ("mul32", Some(sym::xtensa_target_feature)), + ("mul32high", Some(sym::xtensa_target_feature)), + ("div32", Some(sym::xtensa_target_feature)), + ("mac16", Some(sym::xtensa_target_feature)), + ("dfpaccel", Some(sym::xtensa_target_feature)), + ("s32c1i", Some(sym::xtensa_target_feature)), + ("threadptr", Some(sym::xtensa_target_feature)), + ("extendedl32r", Some(sym::xtensa_target_feature)), + ("atomctl", Some(sym::xtensa_target_feature)), + ("memctl", Some(sym::xtensa_target_feature)), + ("debug", Some(sym::xtensa_target_feature)), + ("exception", Some(sym::xtensa_target_feature)), + ("coprocessor", Some(sym::xtensa_target_feature)), + ("interrupt", Some(sym::xtensa_target_feature)), + ("rvector", Some(sym::xtensa_target_feature)), + ("timerint", Some(sym::xtensa_target_feature)), + ("prid", Some(sym::xtensa_target_feature)), + ("regprotect", Some(sym::xtensa_target_feature)), + ("miscsr", Some(sym::xtensa_target_feature)), +]; + const BPF_ALLOWED_FEATURES: &[(&str, Option)] = &[("alu32", Some(sym::bpf_target_feature))]; /// When rustdoc is running, provide a list of all known features so that all their respective @@ -226,6 +254,7 @@ pub fn all_known_features() -> impl Iterator &'static [(&'static str, Opt "powerpc" | "powerpc64" => POWERPC_ALLOWED_FEATURES, "riscv32" | "riscv64" => RISCV_ALLOWED_FEATURES, "wasm32" | "wasm64" => WASM_ALLOWED_FEATURES, + "xtensa" => XTENSA_ALLOWED_FEATURES, "bpf" => BPF_ALLOWED_FEATURES, _ => &[], } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index a31ab5b350e4e..034fbfcfa1171 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -332,6 +332,7 @@ symbols! { braced_empty_structs, branch, breakpoint, + breg, bridge, bswap, c_str, @@ -1336,6 +1337,7 @@ symbols! { wreg, write_bytes, xmm_reg, + xtensa_target_feature, ymm_reg, zmm_reg, } diff --git a/compiler/rustc_target/src/asm/mod.rs b/compiler/rustc_target/src/asm/mod.rs index 305ea7d50e66e..66fba7e983770 100644 --- a/compiler/rustc_target/src/asm/mod.rs +++ b/compiler/rustc_target/src/asm/mod.rs @@ -157,6 +157,7 @@ mod riscv; mod spirv; mod wasm; mod x86; +mod xtensa; pub use aarch64::{AArch64InlineAsmReg, AArch64InlineAsmRegClass}; pub use arm::{ArmInlineAsmReg, ArmInlineAsmRegClass}; @@ -168,6 +169,7 @@ pub use powerpc::{PowerPCInlineAsmReg, PowerPCInlineAsmRegClass}; pub use riscv::{RiscVInlineAsmReg, RiscVInlineAsmRegClass}; 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)] @@ -186,6 +188,7 @@ pub enum InlineAsmArch { PowerPC64, SpirV, Wasm32, + Xtensa, Bpf, } @@ -208,6 +211,7 @@ impl FromStr for InlineAsmArch { "mips64" => Ok(Self::Mips64), "spirv" => Ok(Self::SpirV), "wasm32" => Ok(Self::Wasm32), + "xtensa" => Ok(Self::Xtensa), "bpf" => Ok(Self::Bpf), _ => Err(()), } @@ -237,6 +241,7 @@ pub enum InlineAsmReg { Mips(MipsInlineAsmReg), SpirV(SpirVInlineAsmReg), Wasm(WasmInlineAsmReg), + Xtensa(XtensaInlineAsmReg), Bpf(BpfInlineAsmReg), // Placeholder for invalid register constraints for the current target Err, @@ -252,6 +257,7 @@ impl InlineAsmReg { Self::PowerPC(r) => r.name(), Self::Hexagon(r) => r.name(), Self::Mips(r) => r.name(), + Self::Xtensa(r) => r.name(), Self::Bpf(r) => r.name(), Self::Err => "", } @@ -266,6 +272,7 @@ impl InlineAsmReg { Self::PowerPC(r) => InlineAsmRegClass::PowerPC(r.reg_class()), Self::Hexagon(r) => InlineAsmRegClass::Hexagon(r.reg_class()), Self::Mips(r) => InlineAsmRegClass::Mips(r.reg_class()), + Self::Xtensa(r) => InlineAsmRegClass::Xtensa(r.reg_class()), Self::Bpf(r) => InlineAsmRegClass::Bpf(r.reg_class()), Self::Err => InlineAsmRegClass::Err, } @@ -311,6 +318,9 @@ impl InlineAsmReg { InlineAsmArch::Wasm32 => { Self::Wasm(WasmInlineAsmReg::parse(arch, has_feature, target, &name)?) } + InlineAsmArch::Xtensa => { + Self::Xtensa(XtensaInlineAsmReg::parse(arch, has_feature, target, &name)?) + } InlineAsmArch::Bpf => { Self::Bpf(BpfInlineAsmReg::parse(arch, has_feature, target, &name)?) } @@ -333,6 +343,7 @@ impl InlineAsmReg { Self::PowerPC(r) => r.emit(out, arch, modifier), Self::Hexagon(r) => r.emit(out, arch, modifier), Self::Mips(r) => r.emit(out, arch, modifier), + Self::Xtensa(r) => r.emit(out, arch, modifier), Self::Bpf(r) => r.emit(out, arch, modifier), Self::Err => unreachable!("Use of InlineAsmReg::Err"), } @@ -347,6 +358,7 @@ impl InlineAsmReg { Self::PowerPC(_) => cb(self), Self::Hexagon(r) => r.overlapping_regs(|r| cb(Self::Hexagon(r))), Self::Mips(_) => cb(self), + Self::Xtensa(_) => cb(self), Self::Bpf(r) => r.overlapping_regs(|r| cb(Self::Bpf(r))), Self::Err => unreachable!("Use of InlineAsmReg::Err"), } @@ -376,6 +388,7 @@ pub enum InlineAsmRegClass { Mips(MipsInlineAsmRegClass), SpirV(SpirVInlineAsmRegClass), Wasm(WasmInlineAsmRegClass), + Xtensa(XtensaInlineAsmRegClass), Bpf(BpfInlineAsmRegClass), // Placeholder for invalid register constraints for the current target Err, @@ -394,6 +407,7 @@ impl InlineAsmRegClass { Self::Mips(r) => r.name(), Self::SpirV(r) => r.name(), Self::Wasm(r) => r.name(), + Self::Xtensa(r) => r.name(), Self::Bpf(r) => r.name(), Self::Err => rustc_span::symbol::sym::reg, } @@ -414,6 +428,7 @@ impl InlineAsmRegClass { Self::Mips(r) => 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::Xtensa(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Xtensa), Self::Bpf(r) => r.suggest_class(arch, ty).map(InlineAsmRegClass::Bpf), Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), } @@ -441,6 +456,7 @@ impl InlineAsmRegClass { 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::Xtensa(r) => r.suggest_modifier(arch, ty), Self::Bpf(r) => r.suggest_modifier(arch, ty), Self::Err => unreachable!("Use of InlineAsmRegClass::Err"), } @@ -464,6 +480,7 @@ impl InlineAsmRegClass { Self::Mips(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::Err => unreachable!("Use of InlineAsmRegClass::Err"), } @@ -486,6 +503,7 @@ impl InlineAsmRegClass { Self::Mips(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::Err => unreachable!("Use of InlineAsmRegClass::Err"), } @@ -511,6 +529,7 @@ impl InlineAsmRegClass { } InlineAsmArch::SpirV => Self::SpirV(SpirVInlineAsmRegClass::parse(arch, name)?), InlineAsmArch::Wasm32 => Self::Wasm(WasmInlineAsmRegClass::parse(arch, name)?), + InlineAsmArch::Xtensa => Self::Xtensa(XtensaInlineAsmRegClass::parse(arch, name)?), InlineAsmArch::Bpf => Self::Bpf(BpfInlineAsmRegClass::parse(arch, name)?), }) } @@ -529,6 +548,7 @@ impl InlineAsmRegClass { Self::Mips(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::Err => unreachable!("Use of InlineAsmRegClass::Err"), } @@ -573,6 +593,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, @@ -596,6 +617,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, @@ -617,6 +639,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"), @@ -699,6 +722,11 @@ pub fn allocatable_registers( wasm::fill_reg_map(arch, has_feature, target, &mut map); map } + InlineAsmArch::Xtensa => { + let mut map = xtensa::regclass_map(); + xtensa::fill_reg_map(arch, has_feature, target, &mut map); + map + } InlineAsmArch::Bpf => { let mut map = bpf::regclass_map(); bpf::fill_reg_map(arch, has_feature, 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..2d344df5a3680 --- /dev/null +++ b/compiler/rustc_target/src/asm/xtensa.rs @@ -0,0 +1,279 @@ +use super::{InlineAsmArch, InlineAsmType}; +use crate::spec::Target; +use rustc_macros::HashStable_Generic; +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<(char, &'static str)> { + None + } + + pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<(char, &'static str)> { + None + } + + pub fn supported_types( + self, + _arch: InlineAsmArch, + ) -> &'static [(InlineAsmType, Option<&'static str>)] { + 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, + mut has_feature: impl FnMut(&str) -> bool, + _target: &Target, + ) -> Result<(), &'static str> { + if has_feature($feature) { + Ok(()) + } else { + Err(concat!("target does not support ", $feature, " registers")) + } + } + }; +} + +feature!(has_fp, "fp"); +feature!(has_dfpaccel, "dfpaccel"); +feature!(has_bool, "bool"); +feature!(has_loop, "loop"); +feature!(has_extendedl32r, "extendedl32r"); +feature!(has_s32c1i, "s32c1i"); +feature!(has_mac16, "mac16"); +feature!(has_windowed, "windowed"); +feature!(has_debug, "debug"); +feature!(has_memctl, "memctl"); +feature!(has_atomctl, "atomctl"); +feature!(has_exception, "exception"); +feature!(has_coprocessor, "coprocessor"); +feature!(has_rvector, "rvector"); +feature!(has_timerint, "timerint"); +feature!(has_interrupt, "interrupt"); +feature!(has_prid, "prid"); +feature!(has_miscsr, "miscsr"); +feature!(has_threadptr, "threadptr"); + +fn has_expstate( + _arch: InlineAsmArch, + _has_feature: impl FnMut(&str) -> bool, + target: &Target, +) -> Result<(), &'static str> { + match target.cpu.as_str() { + "esp32" => Ok(()), + _ => Err("target does not support expstate registers") + } +} +fn has_gpio_out( + _arch: InlineAsmArch, + _has_feature: impl FnMut(&str) -> bool, + target: &Target, +) -> Result<(), &'static str> { + match target.cpu.as_str() { + "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, + mut has_feature: impl FnMut(&str) -> bool, + _target: &Target, +) -> bool { + has_feature("windowed") +} + +fn frame_pointer_a7( + arch: InlineAsmArch, + has_feature: impl FnMut(&str) -> bool, + target: &Target, +) -> Result<(), &'static str> { + if frame_pointer_is_a7(arch, has_feature, target) { + Err("the frame pointer (a7) cannot be used as an operand for inline asm") + } else { + Ok(()) + } +} + +fn frame_pointer_a15( + arch: InlineAsmArch, + has_feature: impl FnMut(&str) -> bool, + target: &Target, +) -> Result<(), &'static str> { + if !frame_pointer_is_a7(arch, has_feature, target) { + 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_loop, + lend: reg = ["lend"] % has_loop, + lcount: reg = ["lcount"] % has_loop, + 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_exception, + epc3: reg = ["epc3"] % has_exception, + epc4: reg = ["epc4"] % has_exception, + epc5: reg = ["epc5"] % has_exception, + epc6: reg = ["epc6"] % has_exception, + epc7: reg = ["epc7"] % has_exception, + depc: reg = ["depc"] % has_exception, + eps2: reg = ["eps2"] % has_exception, + eps3: reg = ["eps3"] % has_exception, + eps4: reg = ["eps4"] % has_exception, + eps5: reg = ["eps5"] % has_exception, + eps6: reg = ["eps6"] % has_exception, + eps7: reg = ["eps7"] % has_exception, + excsave1: reg = ["excsave1"] % has_exception, + excsave2: reg = ["excsave2"] % has_exception, + excsave3: reg = ["excsave3"] % has_exception, + excsave4: reg = ["excsave4"] % has_exception, + excsave5: reg = ["excsave5"] % has_exception, + excsave6: reg = ["excsave6"] % has_exception, + excsave7: reg = ["excsave7"] % has_exception, + 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/src/doc/unstable-book/src/library-features/asm.md b/src/doc/unstable-book/src/library-features/asm.md index 03dbf4fb617f4..785f54eaa3a7e 100644 --- a/src/doc/unstable-book/src/library-features/asm.md +++ b/src/doc/unstable-book/src/library-features/asm.md @@ -30,6 +30,7 @@ Inline assembly is currently supported on the following architectures: - Hexagon - MIPS32r2 and MIPS64r2 - wasm32 +- Xtensa - BPF ## Basic usage @@ -461,7 +462,7 @@ options := "options(" option *["," option] [","] ")" asm := "asm!(" format_string *("," format_string) *("," [ident "="] operand) ["," options] [","] ")" ``` -The macro will initially be supported only on ARM, AArch64, Hexagon, PowerPC, x86, x86-64 and RISC-V targets. Support for more targets may be added in the future. The compiler will emit an error if `asm!` is used on an unsupported target. +The macro will initially be supported only on ARM, AArch64, Hexagon, PowerPC, Xtensa, x86, x86-64 and RISC-V targets. Support for more targets may be added in the future. The compiler will emit an error if `asm!` is used on an unsupported target. [format-syntax]: https://doc.rust-lang.org/std/fmt/#syntax @@ -571,8 +572,25 @@ Here is the list of currently supported register classes: | PowerPC | `reg_nonzero` | | `r[1-31]` | `b` | | PowerPC | `freg` | `f[0-31]` | `f` | | wasm32 | `local` | None\* | `r` | +| Xtensa | `reg` | `a[0-15]` | `r` | +| Xtensa | `freg` | `f[0-15]` | `f` | +| Xtensa | `breg` | `b[0-15]` | `b` | | BPF | `reg` | `r[0-10]` | `r` | | BPF | `wreg` | `w[0-10]` | `w` | +| Xtensa | `reg` | `a[0-15]`, `lbeg`, `lend`, `lcount` | `r` | +| Xtensa | `reg` | `sar`, `br`, `litbase`, `scompare1` | `r` | +| Xtensa | `reg` | `acclo`, `acchi`, `m0`, `m1`, `m2`, `m3` | `r` | +| Xtensa | `reg` | `windowbase`, `windowstart`, `ibreakenable`, `memctl`, `atomctl`, `ddr` | `r` | +| Xtensa | `reg` | `ibreaka0`, `ibreaka1`, `dbreaka0`, `dbreaka1`, `dbreakc0`, `dbreakc1`, `configid[0-1]` | `r` | +| Xtensa | `reg` | `epc[1-7]`, `depc`, `eps[2-7]` | `r` | +| Xtensa | `reg` | `excsave[1-7]`, `cpenable`, `interrupt`, `intclear`, `intenable` | `r` | +| Xtensa | `reg` | `ps`, `vecbase`, `exccause`, `debugcause`, `ccount` | `r` | +| Xtensa | `reg` | `prid`, `icount`, `icountlevel`, `excvaddr`, `ccompare[0-2]` | `r` | +| Xtensa | `reg` | `excsave[1-7]`, `cpenable`, `interrupt`, `intclear`, `intenable` | `r` | +| Xtensa | `reg` | `misc[0-3]`, `gpio_out`, `expstate`, `threadptr` | `r` | +| Xtensa | `reg` | `fcr`, `fsr`, `f64r_lo`, `f64r_hi`, `f64s` | `r` | +| Xtensa | `breg` | `b[0-15]` | `b` | +| Xtensa | `freg` | `f[0-15]` | `f` | > **Note**: On x86 we treat `reg_byte` differently from `reg` because the compiler can allocate `al` and `ah` separately whereas `reg` reserves the whole register. > @@ -620,6 +638,9 @@ Each register class has constraints on which value types they can be used with. | wasm32 | `local` | None | `i8` `i16` `i32` `i64` `f32` `f64` | | BPF | `reg` | None | `i8` `i16` `i32` `i64` | | BPF | `wreg` | `alu32` | `i8` `i16` `i32` | +| Xtensa | `reg` | None | `i8`, `i16`, `i32` | +| Xtensa | `breg` | None | None | +| Xtensa | `freg` | None | `f32` | > **Note**: For the purposes of the above table pointers, function pointers and `isize`/`usize` are treated as the equivalent integer type (`i16`/`i32`/`i64` depending on the target). @@ -680,6 +701,7 @@ Some registers have multiple names. These are all treated by the compiler as ide | Hexagon | `r30` | `fr` | | Hexagon | `r31` | `lr` | | BPF | `r[0-10]` | `w[0-10]` | +| Xtensa | `a1` | `sp` | Some registers cannot be used for input or output operands: @@ -703,6 +725,7 @@ Some registers cannot be used for input or output operands: | MIPS | `$ra` | Return address cannot be used as inputs or outputs. | | RISC-V | `x0` | This is a constant zero register which can't be modified. | | RISC-V | `gp`, `tp` | These registers are reserved and cannot be used as inputs or outputs. | +| Xtensa | `a7` or `a15` | On Xtensa the frame pointer can be either `a7` or `a15` depending on whether the target supports the windowed ABI. The frame pointer cannot be used as an input or output. | | Hexagon | `lr` | This is the link register which cannot be used as an input or output. | In some cases LLVM will allocate a "reserved register" for `reg` operands even though this register cannot be explicitly specified. Assembly code making use of reserved registers should be careful since `reg` operands may alias with those registers. Reserved registers are the frame pointer and base pointer @@ -760,6 +783,9 @@ The supported modifiers are a subset of LLVM's (and GCC's) [asm template argumen | PowerPC | `reg` | None | `0` | None | | PowerPC | `reg_nonzero` | None | `3` | `b` | | PowerPC | `freg` | None | `0` | None | +| Xtensa | `reg` | None | `a0` | None | +| Xtensa | `breg` | None | `b0` | None | +| Xtensa | `freg` | None | `f0` | None | > Notes: > - on ARM `e` / `f`: this prints the low or high doubleword register name of a NEON quad (128-bit) register. diff --git a/src/test/assembly/asm/xtensa-types.rs b/src/test/assembly/asm/xtensa-types.rs new file mode 100644 index 0000000000000..8bdecc4f37b46 --- /dev/null +++ b/src/test/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");