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
1,110 changes: 1,110 additions & 0 deletions AMD_ISA_SUPPORT.md

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion asm-lsp/config_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use dirs::config_dir;

use crate::types::{Arch, Assembler, Config, ConfigOptions, ProjectConfig, RootConfig};

const ARCH_LIST: [Arch; 11] = [
const ARCH_LIST: [Arch; 15] = [
Arch::X86,
Arch::X86_64,
Arch::X86_AND_X86_64,
Expand All @@ -21,6 +21,10 @@ const ARCH_LIST: [Arch; 11] = [
Arch::PowerISA,
Arch::Avr,
Arch::Mips,
Arch::AmdgpuGfx11,
Arch::AmdgpuGfx950,
Arch::AmdgpuGfx12,
Arch::AmdgpuGfx1250,
];

const ASSEMBLER_LIST: [Assembler; 8] = [
Expand Down
32 changes: 30 additions & 2 deletions asm-lsp/lsp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -925,6 +925,7 @@ pub fn get_hover_resp(
|| config.is_assembler_enabled(Assembler::Avr)
|| config.is_assembler_enabled(Assembler::Fasm)
|| config.is_assembler_enabled(Assembler::Mars)
|| config.instruction_set.is_amdgpu()
{
// all gas, AVR, and Mars directives have a '.' prefix, some masm directives do
let directive_lookup =
Expand Down Expand Up @@ -1024,17 +1025,43 @@ fn search_for_dir_by_assembler<'a>(
dir_map: &'a HashMap<(Assembler, String), Directive>,
config: &Config,
) -> Option<&'a Directive> {
if config.instruction_set.is_amdgpu()
&& let Some(dir) = dir_map.get(&(Assembler::Amdgpu, word.to_string()))
{
return Some(dir);
}
dir_map.get(&(config.assembler, word.to_string()))
}

/// AMDGPU encoding suffixes that can be stripped to find the base mnemonic.
/// Order matters: longer suffixes must come first to avoid partial matches.
const AMDGPU_ENCODING_SUFFIXES: &[&str] = &["_dpp16", "_dpp8", "_sdwa", "_dpp", "_e64", "_e32"];

/// If `word` ends with an AMDGPU encoding suffix, return the base mnemonic.
fn strip_amdgpu_encoding_suffix(word: &str) -> Option<&str> {
AMDGPU_ENCODING_SUFFIXES
.iter()
.find_map(|suffix| word.strip_suffix(suffix))
}

fn get_hover_resp_by_arch<T: Hoverable>(
word: &str,
map: &HashMap<(Arch, String), T>,
config: &Config,
) -> Option<Hover> {
// ensure hovered text is always lowercase
let hovered_word = word.to_ascii_lowercase();
let instr_resp = search_for_hoverable_by_arch(&hovered_word, map, config);
let mut instr_resp = search_for_hoverable_by_arch(&hovered_word, map, config);

// For AMDGPU: if the exact mnemonic wasn't found, strip encoding suffixes
// (_e32, _e64, _dpp, _sdwa, ...) and retry with the base mnemonic.
if matches!(instr_resp, (None, None))
&& config.instruction_set.is_amdgpu()
&& let Some(base) = strip_amdgpu_encoding_suffix(&hovered_word)
{
instr_resp = search_for_hoverable_by_arch(base, map, config);
}

let value = match instr_resp {
(Some(resp1), Some(resp2)) => {
format!("{resp1}\n\n{resp2}")
Expand Down Expand Up @@ -1316,14 +1343,15 @@ pub fn get_comp_resp(
});
}
}
// prepend all GAS, all Ca65, all AVR, all Mars, some MASM, some NASM directives with "."
// prepend all GAS, all Ca65, all AVR, all Mars, some MASM, some NASM, all AMD GPU directives with "."
Some(".") => {
if config.is_assembler_enabled(Assembler::Gas)
|| config.is_assembler_enabled(Assembler::Masm)
|| config.is_assembler_enabled(Assembler::Nasm)
|| config.is_assembler_enabled(Assembler::Ca65)
|| config.is_assembler_enabled(Assembler::Avr)
|| config.is_assembler_enabled(Assembler::Mars)
|| config.is_assembler_enabled(Assembler::Amdgpu)
{
return Some(CompletionList {
is_incomplete: true,
Expand Down
37 changes: 37 additions & 0 deletions asm-lsp/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,43 @@ pub fn populate_mips_instructions(json_contents: &str) -> Result<Vec<Instruction
Ok(instructions)
}

/// Parse the provided JSON contents and return a vector of AMD GPU instructions.
///
/// The JSON format matches the simple array format used for MIPS:
/// `[{"name": "v_add_f32", "summary": "...", "asm_templates": ["V_ADD_F32 VDST, SRC0, SRC1"]}, ...]`
///
/// The `arch` parameter specifies which AMD GPU generation these instructions belong to
/// (e.g. `Arch::AmdgpuGfx11`, `Arch::AmdgpuGfx12`, etc.).
///
/// # Errors
///
/// Returns an error if the JSON is malformed or cannot be deserialized.
pub fn populate_amdgpu_instructions(arch: Arch, json_contents: &str) -> Result<Vec<Instruction>> {
#[derive(Deserialize, Debug)]
struct AmdgpuInstruction {
pub name: String,
pub summary: String,
pub asm_templates: Vec<String>,
}

let raw_instrs: Vec<AmdgpuInstruction> =
serde_json::from_str(json_contents).map_err(|e| anyhow!("Failed to parse JSON: {e}"))?;
let instructions: Vec<Instruction> = raw_instrs
.into_iter()
.map(|instr| Instruction {
name: instr.name.to_ascii_lowercase(),
summary: instr.summary,
asm_templates: instr.asm_templates,
arch,
forms: Vec::new(),
aliases: Vec::new(),
url: None,
})
.collect();

Ok(instructions)
}

/// Parse the provided HTML contents and return a vector of all the instructions based on that.
/// <https://www.masswerk.at/6502/6502_instruction_set.html>
///
Expand Down
Binary file added asm-lsp/serialized/directives/amdgpu
Binary file not shown.
Binary file added asm-lsp/serialized/opcodes/amdgpu-gfx11
Binary file not shown.
Binary file added asm-lsp/serialized/opcodes/amdgpu-gfx12
Binary file not shown.
Binary file added asm-lsp/serialized/opcodes/amdgpu-gfx1250
Binary file not shown.
Binary file added asm-lsp/serialized/opcodes/amdgpu-gfx950
Binary file not shown.
Binary file added asm-lsp/serialized/registers/amdgpu
Binary file not shown.
45 changes: 41 additions & 4 deletions asm-lsp/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ mod tests {
Register, ServerStore, TreeEntry, get_comp_resp, get_completes, get_hover_resp,
get_word_from_pos_params, instr_filter_targets,
parser::{
populate_6502_instructions, populate_arm_instructions, populate_avr_directives,
populate_avr_instructions, populate_ca65_directives, populate_mars_pseudo_instructions,
populate_masm_nasm_fasm_mars_directives, populate_mips_instructions,
populate_power_isa_instructions, populate_riscv_instructions, populate_riscv_registers,
populate_6502_instructions, populate_amdgpu_instructions, populate_arm_instructions,
populate_avr_directives, populate_avr_instructions, populate_ca65_directives,
populate_mars_pseudo_instructions, populate_masm_nasm_fasm_mars_directives,
populate_mips_instructions, populate_power_isa_instructions,
populate_riscv_instructions, populate_riscv_registers,
},
populate_gas_directives, populate_instructions, populate_name_to_directive_map,
populate_name_to_instruction_map, populate_name_to_register_map, populate_registers,
Expand Down Expand Up @@ -3295,4 +3296,40 @@ Width: 8 bits",
populate_masm_nasm_fasm_mars_directives
);
}

/**************************************************************************
* AMDGPU serialization freshness tests
*************************************************************************/
#[test]
fn serialized_amdgpu_gfx11_instructions_are_up_to_date() {
serialized_instructions_test!(
"serialized/opcodes/amdgpu-gfx11",
"../docs_store/opcodes/amdgpu-gfx11.json",
|s| populate_amdgpu_instructions(Arch::AmdgpuGfx11, s)
);
}
#[test]
fn serialized_amdgpu_gfx950_instructions_are_up_to_date() {
serialized_instructions_test!(
"serialized/opcodes/amdgpu-gfx950",
"../docs_store/opcodes/amdgpu-gfx950.json",
|s| populate_amdgpu_instructions(Arch::AmdgpuGfx950, s)
);
}
#[test]
fn serialized_amdgpu_gfx12_instructions_are_up_to_date() {
serialized_instructions_test!(
"serialized/opcodes/amdgpu-gfx12",
"../docs_store/opcodes/amdgpu-gfx12.json",
|s| populate_amdgpu_instructions(Arch::AmdgpuGfx12, s)
);
}
#[test]
fn serialized_amdgpu_gfx1250_instructions_are_up_to_date() {
serialized_instructions_test!(
"serialized/opcodes/amdgpu-gfx1250",
"../docs_store/opcodes/amdgpu-gfx1250.json",
|s| populate_amdgpu_instructions(Arch::AmdgpuGfx1250, s)
);
}
}
Loading