Parsing .insn assembly directive.

This commit is contained in:
Lucas Clemente Vella
2023-09-19 20:46:19 +01:00
parent 04880f0e4a
commit 209529e182
3 changed files with 84 additions and 2 deletions

View File

@@ -12,7 +12,10 @@ fn main() {
}
fn build_lalrpop() {
lalrpop::process_root().unwrap();
lalrpop::Configuration::new()
.emit_rerun_directives(true)
.process_current_dir()
.unwrap();
}
fn build_instruction_tests() {

View File

@@ -329,3 +329,74 @@ fn output_files_from_cargo_build_plan(
assemblies
}
/// Maps an instruction in .insn syntax to Statement::Instruction() in the expected format.
///
/// See https://www.rowleydownload.co.uk/arm/documentation/gnu/as/RISC_002dV_002dFormats.html
pub fn map_insn_i(
opcode6: Expression,
func3: Expression,
rd: Register,
rs1: Register,
simm12: Expression,
) -> Statement {
let (Expression::Number(opcode6), Expression::Number(func3)) = (opcode6, func3) else {
panic!("Only literal opcode and function are supported in .insn syntax");
};
// These are almost all instructions in RISC-V Instruction Set Manual that
// we are supposed to implement and roughly fits the pattern of the I-type
// instruction. Only "csr*i" instructions are missing.
// First we try to match the instructions that uses the I-type encoding
// ordinarily, i.e. where all fields are what they are supposed to be:
let name = match (opcode6, func3) {
(0b1100111, 0b000) => "jalr",
(0b0000011, 0b000) => "lb",
(0b0000011, 0b001) => "lh",
(0b0000011, 0b010) => "lw",
(0b0000011, 0b100) => "lbu",
(0b0000011, 0b101) => "lhu",
(0b0010011, 0b000) => "addi",
(0b0010011, 0b010) => "slti",
(0b0010011, 0b011) => "sltiu",
(0b0010011, 0b100) => "xori",
(0b0010011, 0b110) => "ori",
(0b0010011, 0b111) => "andi",
(0b1110011, 0b001) => "csrrw",
(0b1110011, 0b010) => "csrrs",
(0b1110011, 0b011) => "csrrc",
// won't interpret "csr*i" instructions because it is too weird to
// encode an immediate as a register
opfunc => {
// We now try the instructions that take certain liberties with the
// I-type encoding, and don't use the standard arguments for it.
let name = match opfunc {
(0b0001111, 0b000) => "fence",
(0b0001111, 0b001) => "fence.i",
(0b1110011, 0b000) => {
let Expression::Number(simm12) = simm12 else {
panic!(
"Only literal simm12 is supported for ecall and ebreak instructions"
);
};
match simm12 {
0 => "ecall",
1 => "ebreak",
_ => panic!("unknown instruction"),
}
}
_ => panic!("unsupported .insn instruction"),
};
return Statement::Instruction(name.to_string(), Vec::new());
}
};
let args = vec![
Argument::Register(rd),
Argument::Register(rs1),
Argument::Expression(simm12),
];
Statement::Instruction(name.to_string(), args)
}

View File

@@ -13,7 +13,7 @@
use std::str::FromStr;
use asm_utils::ast::{unescape_string, BinaryOpKind as BOp, UnaryOpKind as UOp,
new_binary_op as bin_op, new_unary_op as un_op, new_function_op as fn_op};
use crate::{Argument, Register, Statement, FunctionKind as FOp, Expression};
use crate::{Argument, Register, Statement, FunctionKind as FOp, Expression, map_insn_i};
grammar;
@@ -67,9 +67,17 @@ Directive: Statement = {
}
Instruction: Statement = {
<InsnDirective>,
<DotlessSymbol> <Arguments> => Statement::Instruction(<>)
}
InsnDirective: Statement = {
".insn" "i" <opcode6:Expression> "," <func3:Expression> "," <rd:Register> "," <rs1:Register> "," <simm12:Expression> => map_insn_i(opcode6, func3, rd, rs1, simm12),
".insn" "i" <opcode6:Expression> "," <func3:Expression> "," <rd:Register> "," <simm12:Expression> "(" <rs1:Register> ")" => map_insn_i(opcode6, func3, rd, rs1, simm12)
// TODO: implement the other kinds of .insn instructions.
// See https://www.rowleydownload.co.uk/arm/documentation/gnu/as/RISC_002dV_002dFormats.html
}
Arguments: Vec<Argument> = {
=> vec![],
<mut list:( <Argument> "," )*> <end:Argument> => { list.push(end); list }