etnaviv: isa: Add parser module

This commit adds the actual parser, which makes use of the IsaParser
derive proc macro.

It provides two public functions:
 - asm_process_str(..)
   Parse the provided isa representation and return an etna_asm_result.
   This will be used by our unit tests.

 - asm_process_file(..)
   Parse a whole file full of isa and return an etna_asm_result. This
   will be used by our cli assembler.

Signed-off-by: Christian Gmeiner <cgmeiner@igalia.com>
Reviewed-by: @LingMan
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/28869>
This commit is contained in:
Christian Gmeiner
2024-06-17 13:21:15 +02:00
committed by Marge Bot
parent db5e733e1b
commit d9bcaa1478
4 changed files with 341 additions and 2 deletions

View File

@@ -1,4 +1,5 @@
// Copyright © 2024 Igalia S.L.
// SPDX-License-Identifier: MIT
mod parser;
mod util;

View File

@@ -147,6 +147,12 @@ if with_tools.contains('etnaviv')
required: true,
)
dep_pest = dependency('pest',
version: '>= 2.7.6',
fallback: ['pest', 'dep_pest'],
required: true,
)
_libetnaviv_isa_proc_rs = rust.proc_macro(
'etnaviv_isa_proc',
files('isa_proc.rs', 'isa.rs'),
@@ -163,8 +169,8 @@ if with_tools.contains('etnaviv')
files('lib.rs'),
gnu_symbol_visibility : 'hidden',
rust_abi : 'c',
dependencies: [dep_indexmap, dep_roxmltree],
link_with: [_libetnaviv_isa_bindings_gen],
dependencies: [dep_indexmap, dep_roxmltree, dep_pest],
link_with: [_libetnaviv_isa_bindings_gen, _libetnaviv_isa_proc_rs],
)
endif

289
src/etnaviv/isa/parser.rs Normal file
View File

@@ -0,0 +1,289 @@
// Copyright © 2024 Igalia S.L.
// SPDX-License-Identifier: MIT
use crate::util::EtnaAsmResultExt;
use etnaviv_isa_proc::IsaParser;
use isa_bindings::*;
use pest::iterators::Pair;
use pest::Parser;
use std::fs;
use std::str::FromStr;
#[derive(IsaParser)]
#[isa = "etnaviv.xml"]
#[static_rules_file = "static_rules.pest"]
struct Isa;
fn get_child_rule(item: Pair<Rule>) -> Rule {
item.into_inner().next().unwrap().as_rule()
}
fn parse_pair<T: FromStr>(item: Pair<Rule>) -> T
where
T::Err: std::fmt::Debug,
{
item.as_str().parse::<T>().unwrap()
}
fn fill_swizzle(item: Pair<Rule>) -> u32 {
assert!(item.as_rule() == Rule::SrcSwizzle);
item.into_inner()
.map(|comp| match comp.as_rule() {
Rule::Swiz => match comp.as_str() {
"x" => 0,
"y" => 1,
"z" => 2,
"w" => 3,
_ => panic!("Unexpected swizzle {:?}", comp.as_str()),
},
_ => panic!("Unexpected rule {:?}", comp.as_rule()),
})
.enumerate()
.fold(0, |acc, (index, swiz_index)| {
acc | swiz_index << (2 * index)
})
}
fn fill_destination(pair: Pair<Rule>, dest: &mut etna_inst_dst) {
dest.set_use(1);
for item in pair.into_inner() {
match item.as_rule() {
Rule::RegAddressingMode => {
let rule = get_child_rule(item);
dest.set_amode(isa_reg_addressing_mode::from_rule(rule));
}
Rule::Register => {
dest.set_reg(parse_pair(item));
}
Rule::Wrmask => {
let rule = get_child_rule(item);
dest.set_write_mask(isa_wrmask::from_rule(rule));
}
_ => panic!("Unexpected rule {:?}", item.as_rule()),
}
}
}
fn fill_mem_destination(pair: Pair<Rule>, dest: &mut etna_inst_dst) {
dest.set_use(1);
for item in pair.into_inner() {
match item.as_rule() {
Rule::Wrmask => {
let rule = get_child_rule(item);
dest.set_write_mask(isa_wrmask::from_rule(rule));
}
_ => panic!("Unexpected rule {:?}", item.as_rule()),
}
}
}
fn fill_tex(pair: Pair<Rule>, tex: &mut etna_inst_tex) {
for item in pair.into_inner() {
match item.as_rule() {
Rule::Register => {
let r = parse_pair(item);
tex.set_id(r)
}
Rule::SrcSwizzle => {
let bytes = fill_swizzle(item);
tex.set_swiz(bytes);
}
_ => panic!("Unexpected rule {:?}", item.as_rule()),
}
}
}
fn fill_source(pair: Pair<Rule>, src: &mut etna_inst_src, dual_16_mode: bool) {
src.set_use(1);
for item in pair.into_inner() {
match item.as_rule() {
Rule::Absolute => unsafe {
src.__bindgen_anon_1.__bindgen_anon_1.set_abs(1);
},
Rule::Negate => unsafe {
src.__bindgen_anon_1.__bindgen_anon_1.set_neg(1);
},
Rule::RegGroup => {
let rule = get_child_rule(item);
src.set_rgroup(isa_reg_group::from_rule(rule));
}
Rule::RegAddressingMode => {
let rule = get_child_rule(item);
unsafe {
src.__bindgen_anon_1
.__bindgen_anon_1
.set_amode(isa_reg_addressing_mode::from_rule(rule));
}
}
Rule::Register => {
let r = parse_pair(item);
unsafe {
src.__bindgen_anon_1.__bindgen_anon_1.set_reg(r);
}
}
Rule::Immediate_Minus_Nan => {
src.set_rgroup(isa_reg_group::ISA_REG_GROUP_IMMED);
let imm_struct = unsafe { &mut src.__bindgen_anon_1.__bindgen_anon_2 };
imm_struct.set_imm_type(0);
imm_struct.set_imm_val(0xfffff);
}
Rule::Immediate_float => {
let value: f32 = parse_pair(item);
let bits = value.to_bits();
assert!((bits & 0xfff) == 0); /* 12 lsb cut off */
src.set_rgroup(isa_reg_group::ISA_REG_GROUP_IMMED);
let imm_type = if dual_16_mode { 3 } else { 0 };
let imm_struct = unsafe { &mut src.__bindgen_anon_1.__bindgen_anon_2 };
imm_struct.set_imm_type(imm_type);
imm_struct.set_imm_val(bits >> 12);
}
i_type @ (Rule::Immediate_int | Rule::Immediate_uint) => {
let value = if i_type == Rule::Immediate_int {
parse_pair::<i32>(item) as u32
} else {
parse_pair::<u32>(item)
};
src.set_rgroup(isa_reg_group::ISA_REG_GROUP_IMMED);
let imm_struct = unsafe { &mut src.__bindgen_anon_1.__bindgen_anon_2 };
imm_struct.set_imm_type(1);
imm_struct.set_imm_val(value);
}
Rule::SrcSwizzle => {
let bytes = fill_swizzle(item);
unsafe {
src.__bindgen_anon_1.__bindgen_anon_1.set_swiz(bytes);
}
}
_ => panic!("Unexpected rule {:?}", item.as_rule()),
}
}
}
fn process(input: Pair<Rule>, dual_16_mode: bool) -> Option<etna_inst> {
// The assembler and disassembler are both using the
// 'full' form of the ISA which contains void's and
// use the HW ordering of instruction src arguments.
if input.as_rule() == Rule::EOI {
return None;
}
// Create instruction with sane defaults.
let mut instr = etna_inst::default();
instr.dst.set_write_mask(isa_wrmask::ISA_WRMASK_XYZW);
instr.opcode = isa_opc::from_rule(input.as_rule());
let mut src_index = 0;
for p in input.into_inner() {
match p.as_rule() {
Rule::Dst_full => {
instr.set_dst_full(1);
}
Rule::Sat => {
instr.set_sat(1);
}
Rule::Cond => {
let rule = get_child_rule(p);
instr.set_cond(isa_cond::from_rule(rule));
}
Rule::Skphp => {
instr.set_skphp(1);
}
Rule::Pmode => {
instr.set_pmode(1);
}
Rule::Denorm => {
instr.set_denorm(1);
}
Rule::Local => {
instr.set_local(1);
}
Rule::Left_shift => {
let item = p.into_inner().next().unwrap();
let amount = parse_pair(item);
instr.set_left_shift(amount);
}
Rule::Type => {
let rule = get_child_rule(p);
instr.type_ = isa_type::from_rule(rule);
}
Rule::Thread => {
let rule = get_child_rule(p);
instr.set_thread(isa_thread::from_rule(rule));
}
Rule::Rounding => {
let rule = get_child_rule(p);
instr.rounding = isa_rounding::from_rule(rule);
}
Rule::DestVoid => {
// Nothing to do
}
Rule::DstRegister => {
fill_destination(p, &mut instr.dst);
}
Rule::DstMemAddr => {
fill_mem_destination(p, &mut instr.dst);
}
Rule::SrcVoid => {
// Nothing to do
}
Rule::SrcRegister => {
fill_source(p, &mut instr.src[src_index], dual_16_mode);
src_index += 1;
}
Rule::TexSrc => {
fill_tex(p, &mut instr.tex);
}
Rule::Target => {
let target = parse_pair(p);
instr.imm = target;
}
_ => panic!("Unexpected rule {:?}", p.as_rule()),
}
}
Some(instr)
}
fn parse(rule: Rule, content: &str, dual_16_mode: bool, asm_result: &mut etna_asm_result) {
let result = Isa::parse(rule, content);
match result {
Ok(program) => {
for line in program {
if let Some(result) = process(line, dual_16_mode) {
asm_result.append_instruction(result);
}
}
asm_result.success = true;
}
Err(e) => {
asm_result.set_error(&format!("{}", e));
asm_result.success = false;
}
}
}
pub fn asm_process_str(string: &str, dual_16_mode: bool, asm_result: &mut etna_asm_result) {
parse(Rule::instruction, string, dual_16_mode, asm_result)
}
pub fn asm_process_file(file: &str, dual_16_mode: bool, asm_result: &mut etna_asm_result) {
let content = fs::read_to_string(file).expect("cannot read file");
parse(Rule::instructions, &content, dual_16_mode, asm_result)
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright © 2024 Igalia S.L.
* SPDX-License-Identifier: MIT
*/
WHITESPACE = _{ " " | "\t" }
COMMENT = _{ ";" ~ (!NEWLINE ~ ANY)* ~ NEWLINE? }
Dst_full = { ".hp" }
Sat = { ".sat" }
Skphp = { ".skpHp" }
Pmode = { ".pack" }
Denorm = { ".denorm" }
Local = { ".local" }
Amount = { ASCII_DIGIT* }
Left_shift = { ".ls" ~ Amount }
SrcVoid = { "void" }
DestVoid = { "void" }
Negate = { "-" }
Absolute = { "|" }
Immediate_Minus_Nan = @{ Negate ~ "nan" }
Immediate_float = @{ Negate? ~ ASCII_DIGIT* ~ "." ~ ASCII_DIGIT* }
Immediate_int = @{ Negate ~ ASCII_DIGIT* }
Immediate_uint = @{ ASCII_DIGIT* }
Immediate = _{ Immediate_Minus_Nan | Immediate_float | Immediate_int | Immediate_uint }
Register = { ASCII_DIGIT* }
DstRegister = ${ "t" ~ Register ~ RegAddressingMode? ~ Wrmask? }
DstMemAddr = ${ "mem" ~ Wrmask? }
SrcSwizzle = ${ "." ~ Swiz ~ Swiz ~ Swiz ~ Swiz }
SrcRegister = ${
( Negate? ~ Absolute ~ RegGroup ~ Register ~ RegAddressingMode? ~ SrcSwizzle? ~ Absolute |
Negate? ~ RegGroup ~ Register ~ RegAddressingMode? ~ SrcSwizzle? |
Immediate )
}
Dest = _{ DstRegister }
Src = _{ SrcRegister }
TexSrc = ${ "tex" ~ Register ~ SrcSwizzle }
Target = { ASCII_DIGIT* }