Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler/rustc_codegen_gcc/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,8 @@ fn reg_class_to_gcc(reg_class: InlineAsmRegClass) -> &'static str {
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::Amdgpu(AmdgpuInlineAsmRegClass::vgpr) => "v",
InlineAsmRegClass::Amdgpu(AmdgpuInlineAsmRegClass::sgpr) => "Sg",
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => "r",
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
| InlineAsmRegClass::Arm(ArmInlineAsmRegClass::dreg_low16)
Expand Down Expand Up @@ -761,6 +763,7 @@ fn dummy_output_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, reg: InlineAsmRegCl
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::Amdgpu(_) => cx.type_i32(),
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => cx.type_i32(),
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
| InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => cx.type_f32(),
Expand Down Expand Up @@ -946,6 +949,7 @@ fn modifier_to_gcc(
InlineAsmRegClass::AArch64(AArch64InlineAsmRegClass::preg) => {
unreachable!("clobber-only")
}
InlineAsmRegClass::Amdgpu(_) => None,
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::reg) => None,
InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg)
| InlineAsmRegClass::Arm(ArmInlineAsmRegClass::sreg_low16) => None,
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_codegen_llvm/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ impl<'ll, 'tcx> AsmBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> {
InlineAsmArch::AArch64 | InlineAsmArch::Arm64EC | InlineAsmArch::Arm => {
constraints.push("~{cc}".to_string());
}
InlineAsmArch::Amdgpu => {}
InlineAsmArch::X86 | InlineAsmArch::X86_64 => {
constraints.extend_from_slice(&[
"~{dirflag}".to_string(),
Expand Down Expand Up @@ -645,6 +646,7 @@ fn reg_to_llvm(reg: InlineAsmRegOrRegClass, layout: Option<&TyAndLayout<'_>>) ->
| Arm(ArmInlineAsmRegClass::dreg_low8)
| Arm(ArmInlineAsmRegClass::qreg_low4) => "x",
Arm(ArmInlineAsmRegClass::dreg) | Arm(ArmInlineAsmRegClass::qreg) => "w",
Amdgpu(class) => class.prefix(),
Hexagon(HexagonInlineAsmRegClass::reg) => "r",
Hexagon(HexagonInlineAsmRegClass::preg) => unreachable!("clobber-only"),
LoongArch(LoongArchInlineAsmRegClass::reg) => "r",
Expand Down Expand Up @@ -745,6 +747,7 @@ fn modifier_to_llvm(
modifier
}
}
Amdgpu(_) => None,
Hexagon(_) => None,
LoongArch(_) => None,
Mips(_) => None,
Expand Down Expand Up @@ -825,6 +828,7 @@ fn dummy_output_type<'ll>(cx: &CodegenCx<'ll, '_>, reg: InlineAsmRegClass) -> &'
Arm(ArmInlineAsmRegClass::qreg)
| Arm(ArmInlineAsmRegClass::qreg_low8)
| Arm(ArmInlineAsmRegClass::qreg_low4) => cx.type_vector(cx.type_i64(), 2),
Amdgpu(_) => cx.type_i32(),
Hexagon(HexagonInlineAsmRegClass::reg) => cx.type_i32(),
Hexagon(HexagonInlineAsmRegClass::preg) => unreachable!("clobber-only"),
LoongArch(LoongArchInlineAsmRegClass::reg) => cx.type_i32(),
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2028,6 +2028,7 @@ symbols! {
self_struct_ctor,
semiopaque,
semitransparent,
sgpr,
sha2,
sha3,
sha512_sm_x86,
Expand Down Expand Up @@ -2448,6 +2449,7 @@ symbols! {
verbatim,
version,
vfp2,
vgpr,
vis,
visible_private_types,
volatile,
Expand Down
234 changes: 234 additions & 0 deletions compiler/rustc_target/src/asm/amdgpu.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
use std::fmt;

use rustc_span::Symbol;

use super::{InlineAsmArch, InlineAsmType, ModifierInfo};

def_reg_class! {
Amdgpu AmdgpuInlineAsmRegClass {
sgpr,
vgpr,
}
}

// See https://llvm.org/docs/AMDGPUOperandSyntax.html
impl AmdgpuInlineAsmRegClass {
pub fn valid_modifiers(self, _arch: InlineAsmArch) -> &'static [char] {
&[]
}

pub fn suggest_class(self, _arch: InlineAsmArch, _ty: InlineAsmType) -> Option<Self> {
None
}

pub fn suggest_modifier(
self,
_arch: InlineAsmArch,
_ty: InlineAsmType,
) -> Option<ModifierInfo> {
None
}

pub fn default_modifier(self, _arch: InlineAsmArch) -> Option<ModifierInfo> {
None
}

pub fn supported_types(
self,
_arch: InlineAsmArch,
) -> &'static [(InlineAsmType, Option<Symbol>)] {
types! { _: I16, F16, I32, F32, I64, F64, I128; }
}

/// The number of supported registers in this class.
/// The returned number is the length, so supported register
/// indices are 0 to max_num()-1.
fn max_num(self) -> u32 {
match self {
Self::sgpr => 106,
Self::vgpr => 256,
}
}

/// Prefix when printed and register constraint in LLVM.
pub fn prefix(self) -> &'static str {
match self {
Self::sgpr => "s",
Self::vgpr => "v",
}
}

/// Get register class from prefix.
fn parse_prefix(prefix: char) -> Result<Self, &'static str> {
match prefix {
's' => Ok(Self::sgpr),
'v' => Ok(Self::vgpr),
_ => Err("unknown register prefix"),
}
}
}

#[derive(
Copy,
Clone,
rustc_macros::Encodable,
rustc_macros::Decodable,
Debug,
Eq,
PartialEq,
PartialOrd,
Hash,
rustc_macros::HashStable_Generic
)]
enum AmdgpuRegRange {
/// Low 16-bit of a register
Low(u32),
/// High 16-bit of a register
High(u32),
/// One or more 32-bit registers, in the inclusive range
Range { start: u32, end: u32 },
}

#[derive(
Copy,
Clone,
rustc_macros::Encodable,
rustc_macros::Decodable,
Debug,
Eq,
PartialEq,
PartialOrd,
Hash,
rustc_macros::HashStable_Generic
)]
#[allow(non_camel_case_types)]
pub struct AmdgpuInlineAsmReg {
class: AmdgpuInlineAsmRegClass,
range: AmdgpuRegRange,
}

impl AmdgpuInlineAsmReg {
pub fn name(self) -> String {
let c = self.class.prefix();
match self.range {
AmdgpuRegRange::Low(n) => format!("{c}{n}.l"),
AmdgpuRegRange::High(n) => format!("{c}{n}.h"),
AmdgpuRegRange::Range { start, end } if start == end => format!("{c}{start}"),
AmdgpuRegRange::Range { start, end } => format!("{c}[{start}:{end}]"),
}
}

pub fn reg_class(self) -> AmdgpuInlineAsmRegClass {
self.class
}

pub fn parse(name: &str) -> Result<Self, &'static str> {
if name.is_empty() {
return Err("invalid empty register");
}
let class = AmdgpuInlineAsmRegClass::parse_prefix(name.chars().next().unwrap())?;
// Form with range, e.g. s[2:3]
let res;
if name[1..].starts_with('[') {
if !name.ends_with(']') {
return Err("invalid register, missing closing bracket");
}
if let Some((start, end)) = name[2..name.len() - 1].split_once(':') {
let Ok(start) = start.parse() else {
return Err("invalid register range start");
};
let Ok(end) = end.parse() else {
return Err("invalid register range end");
};

// Check range
if start > end {
return Err("invalid reversed register range");
}

if end >= class.max_num() {
return Err("too large register for this class");
}
res = Self { class, range: AmdgpuRegRange::Range { start, end } };
} else {
return Err("invalid register range");
}
} else {
let parse_num = |core: &str| {
let Ok(start) = core.parse() else {
return Err("invalid register number");
};

if start >= class.max_num() {
return Err("too large register for this class");
}

Ok(start)
};

let name = &name[1..];
let range = if let Some(name) = name.strip_suffix(".l") {
AmdgpuRegRange::Low(parse_num(name)?)
} else if let Some(name) = name.strip_suffix(".h") {
AmdgpuRegRange::High(parse_num(name)?)
} else {
let start = parse_num(name)?;
AmdgpuRegRange::Range { start, end: start }
};
res = Self { class, range };
}
Ok(res)
}

pub fn validate(
self,
_arch: super::InlineAsmArch,
_reloc_model: crate::spec::RelocModel,
_target_features: &rustc_data_structures::fx::FxIndexSet<Symbol>,
_target: &crate::spec::Target,
_is_clobber: bool,
) -> Result<(), &'static str> {
Ok(())
}
}

pub(super) fn fill_reg_map(
_arch: super::InlineAsmArch,
_reloc_model: crate::spec::RelocModel,
_target_features: &rustc_data_structures::fx::FxIndexSet<Symbol>,
_target: &crate::spec::Target,
map: &mut rustc_data_structures::fx::FxHashMap<
super::InlineAsmRegClass,
rustc_data_structures::fx::FxIndexSet<super::InlineAsmReg>,
>,
) {
use super::{InlineAsmReg, InlineAsmRegClass};

// Add single registers of each class (no register ranges)
#[allow(rustc::potential_query_instability)]
for class in regclass_map().keys() {
let InlineAsmRegClass::Amdgpu(class) = *class else { unreachable!("Must be amdgpu class") };
if let Some(set) = map.get_mut(&InlineAsmRegClass::Amdgpu(class)) {
for i in 0..class.max_num() {
set.insert(InlineAsmReg::Amdgpu(AmdgpuInlineAsmReg {
class,
range: AmdgpuRegRange::Range { start: i, end: i },
}));
}
}
}
}

impl AmdgpuInlineAsmReg {
pub fn emit(
self,
out: &mut dyn fmt::Write,
_arch: InlineAsmArch,
_modifier: Option<char>,
) -> fmt::Result {
out.write_str(&self.name())
}

// There are too many conflicts to list
pub fn overlapping_regs(self, mut _cb: impl FnMut(AmdgpuInlineAsmReg)) {}
}
Loading
Loading