mirror of
https://github.com/RRZE-HPC/OSACA.git
synced 2025-12-16 09:00:05 +01:00
Add RISC-V support and update version to 0.6.2
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"""Open Source Architecture Code Analyzer"""
|
||||
|
||||
name = "osaca"
|
||||
__version__ = "0.6.1"
|
||||
__version__ = "0.6.2"
|
||||
|
||||
# To trigger travis deployment to pypi, do the following:
|
||||
# 1. Increment __version___
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +1,656 @@
|
||||
#TODO
|
||||
---
|
||||
osaca_version: 0.6.1
|
||||
micro_architecture: rv64
|
||||
arch_code: rv64
|
||||
isa: riscv
|
||||
|
||||
# RV64 core parameters
|
||||
ROB_size: 96
|
||||
retired_uOps_per_cycle: 3
|
||||
scheduler_size: 84
|
||||
hidden_loads: false
|
||||
|
||||
# Pipeline ports (simplified model)
|
||||
ports:
|
||||
- ALU
|
||||
- MEM
|
||||
- DIV
|
||||
- FP
|
||||
|
||||
port_model_scheme: |
|
||||
ALU: Integer arithmetic and logic operations
|
||||
MEM: Load/store and address generation
|
||||
DIV: Integer division operations
|
||||
FP: Floating-point operations and vector instructions
|
||||
|
||||
# Load latency in cycles by register type
|
||||
load_latency:
|
||||
gpr: 3
|
||||
fpr: 3
|
||||
vr: 4
|
||||
|
||||
# Simplified load throughput model
|
||||
load_throughput_default: [[1, ["MEM"]]]
|
||||
store_throughput_default: [[1, ["MEM"]]]
|
||||
store_to_load_forward_latency: 4
|
||||
|
||||
# Instruction forms - listed by category
|
||||
instruction_forms:
|
||||
# Basic integer arithmetic (latency 1, throughput 1)
|
||||
- name: ADD
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: ADDI
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: immediate
|
||||
imd: int
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: SUB
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: LUI
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: immediate
|
||||
imd: int
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: AUIPC
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: immediate
|
||||
imd: int
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
# Logical operations
|
||||
- name: AND
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: ANDI
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: immediate
|
||||
imd: int
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: OR
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: ORI
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: immediate
|
||||
imd: int
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: XOR
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: XORI
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: immediate
|
||||
imd: int
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
# Shifts
|
||||
- name: SLL
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: SLLI
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: immediate
|
||||
imd: int
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: SRL
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: SRLI
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: immediate
|
||||
imd: int
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: SRA
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: SRAI
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: immediate
|
||||
imd: int
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
# Multiplication
|
||||
- name: MUL
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
latency: 3
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: MULH
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
latency: 3
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
# Division (higher latency)
|
||||
- name: DIV
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
latency: 10
|
||||
throughput: 10
|
||||
port_pressure: [[1, ["DIV"]]]
|
||||
|
||||
- name: DIVU
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
latency: 10
|
||||
throughput: 10
|
||||
port_pressure: [[1, ["DIV"]]]
|
||||
|
||||
- name: REM
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
latency: 10
|
||||
throughput: 10
|
||||
port_pressure: [[1, ["DIV"]]]
|
||||
|
||||
- name: REMU
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
latency: 10
|
||||
throughput: 10
|
||||
port_pressure: [[1, ["DIV"]]]
|
||||
|
||||
# Memory operations
|
||||
- name: LW
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: memory
|
||||
base: gpr
|
||||
offset: imd
|
||||
index: null
|
||||
scale: 1
|
||||
latency: 3
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["MEM"]]]
|
||||
|
||||
- name: LD
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: memory
|
||||
base: gpr
|
||||
offset: imd
|
||||
index: null
|
||||
scale: 1
|
||||
latency: 3
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["MEM"]]]
|
||||
|
||||
- name: SW
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: memory
|
||||
base: gpr
|
||||
offset: imd
|
||||
index: null
|
||||
scale: 1
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["MEM"]]]
|
||||
|
||||
- name: SD
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: memory
|
||||
base: gpr
|
||||
offset: imd
|
||||
index: null
|
||||
scale: 1
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["MEM"]]]
|
||||
|
||||
# Control flow
|
||||
- name: BEQ
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: identifier
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: BNE
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: identifier
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: BLT
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: identifier
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: BGE
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: identifier
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: JAL
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: identifier
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: JALR
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: immediate
|
||||
imd: int
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
# Floating point operations
|
||||
- name: FLW
|
||||
operands:
|
||||
- class: register
|
||||
prefix: f
|
||||
- class: memory
|
||||
base: gpr
|
||||
offset: imd
|
||||
index: null
|
||||
scale: 1
|
||||
latency: 3
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["MEM"]]]
|
||||
|
||||
- name: FLD
|
||||
operands:
|
||||
- class: register
|
||||
prefix: f
|
||||
- class: memory
|
||||
base: gpr
|
||||
offset: imd
|
||||
index: null
|
||||
scale: 1
|
||||
latency: 3
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["MEM"]]]
|
||||
|
||||
- name: FSW
|
||||
operands:
|
||||
- class: register
|
||||
prefix: f
|
||||
- class: memory
|
||||
base: gpr
|
||||
offset: imd
|
||||
index: null
|
||||
scale: 1
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["MEM"]]]
|
||||
|
||||
- name: FSD
|
||||
operands:
|
||||
- class: register
|
||||
prefix: f
|
||||
- class: memory
|
||||
base: gpr
|
||||
offset: imd
|
||||
index: null
|
||||
scale: 1
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["MEM"]]]
|
||||
|
||||
- name: FADD.S
|
||||
operands:
|
||||
- class: register
|
||||
prefix: f
|
||||
- class: register
|
||||
prefix: f
|
||||
- class: register
|
||||
prefix: f
|
||||
latency: 3
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["FP"]]]
|
||||
|
||||
- name: FSUB.S
|
||||
operands:
|
||||
- class: register
|
||||
prefix: f
|
||||
- class: register
|
||||
prefix: f
|
||||
- class: register
|
||||
prefix: f
|
||||
latency: 3
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["FP"]]]
|
||||
|
||||
- name: FMUL.S
|
||||
operands:
|
||||
- class: register
|
||||
prefix: f
|
||||
- class: register
|
||||
prefix: f
|
||||
- class: register
|
||||
prefix: f
|
||||
latency: 3
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["FP"]]]
|
||||
|
||||
- name: FDIV.S
|
||||
operands:
|
||||
- class: register
|
||||
prefix: f
|
||||
- class: register
|
||||
prefix: f
|
||||
- class: register
|
||||
prefix: f
|
||||
latency: 10
|
||||
throughput: 10
|
||||
port_pressure: [[1, ["FP"]]]
|
||||
|
||||
- name: FMADD.S
|
||||
operands:
|
||||
- class: register
|
||||
prefix: f
|
||||
- class: register
|
||||
prefix: f
|
||||
- class: register
|
||||
prefix: f
|
||||
- class: register
|
||||
prefix: f
|
||||
latency: 4
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["FP"]]]
|
||||
|
||||
# Vector instructions
|
||||
- name: VSETVLI
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: identifier
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: VLE32.V
|
||||
operands:
|
||||
- class: register
|
||||
prefix: v
|
||||
- class: memory
|
||||
base: gpr
|
||||
offset: null
|
||||
index: null
|
||||
scale: 1
|
||||
latency: 4
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["MEM"]]]
|
||||
|
||||
- name: VSE32.V
|
||||
operands:
|
||||
- class: register
|
||||
prefix: v
|
||||
- class: memory
|
||||
base: gpr
|
||||
offset: null
|
||||
index: null
|
||||
scale: 1
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["MEM"]]]
|
||||
|
||||
- name: VFMACC.VF
|
||||
operands:
|
||||
- class: register
|
||||
prefix: v
|
||||
- class: register
|
||||
prefix: f
|
||||
- class: register
|
||||
prefix: v
|
||||
latency: 4
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["FP"]]]
|
||||
|
||||
- name: VFMADD.VV
|
||||
operands:
|
||||
- class: register
|
||||
prefix: v
|
||||
- class: register
|
||||
prefix: v
|
||||
- class: register
|
||||
prefix: v
|
||||
latency: 4
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["FP"]]]
|
||||
|
||||
# CSR instructions
|
||||
- name: CSRR
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: identifier
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: CSRW
|
||||
operands:
|
||||
- class: identifier
|
||||
- class: register
|
||||
name: gpr
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
# Pseudo-instructions
|
||||
- name: MV
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: register
|
||||
name: gpr
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: LI
|
||||
operands:
|
||||
- class: register
|
||||
name: gpr
|
||||
- class: immediate
|
||||
imd: int
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
- name: RET
|
||||
operands: []
|
||||
latency: 1
|
||||
throughput: 1
|
||||
port_pressure: [[1, ["ALU"]]]
|
||||
|
||||
@@ -238,7 +238,7 @@ def _create_db_operand(operand, isa):
|
||||
return _create_db_operand_riscv(operand)
|
||||
else:
|
||||
raise ValueError(f"Unsupported ISA: {isa}")
|
||||
|
||||
|
||||
|
||||
def _create_db_operand_aarch64(operand):
|
||||
"""Get DB operand for AArch64 by operand string."""
|
||||
@@ -308,8 +308,8 @@ def _create_db_operand_riscv(operand):
|
||||
}
|
||||
else:
|
||||
raise ValueError(f"Parameter {operand} is not a valid operand code for RISC-V")
|
||||
|
||||
|
||||
|
||||
|
||||
########################
|
||||
# HELPERS SANITY CHECK #
|
||||
########################
|
||||
@@ -412,16 +412,18 @@ def _check_sanity_arch_db(arch_mm, isa_mm, internet_check=True):
|
||||
suspicious_prefixes_x86 = ["vfm", "fm"]
|
||||
suspicious_prefixes_arm = ["fml", "ldp", "stp", "str"]
|
||||
suspicious_prefixes_riscv = [
|
||||
"vfm", # Vector floating-point multiply
|
||||
"vle", # Vector load
|
||||
"vse", # Vector store
|
||||
"vset", # Vector configuration
|
||||
"vfmacc", # Vector FMA
|
||||
"vsetvl", # Vector length setting
|
||||
"vfmv", # Vector floating-point move
|
||||
"vadd", # Vector add
|
||||
"vsub", # Vector subtract
|
||||
"vmul", # Vector multiply
|
||||
"vse", # Vector store (register is source, memory is destination)
|
||||
"vfmacc", # Vector FMA with accumulation (first operand is both source and destination)
|
||||
"vfmadd", # Vector FMA with addition (first operand is implicitly both source and destination)
|
||||
"vset", # Vector configuration (complex operand pattern)
|
||||
"csrs", # CSR Set (first operand is both source and destination)
|
||||
"csrc", # CSR Clear (first operand is both source and destination)
|
||||
"csrsi", # CSR Set Immediate (first operand is both source and destination)
|
||||
"csrci", # CSR Clear Immediate (first operand is both source and destination)
|
||||
"amo", # Atomic memory operations (read-modify-write to memory)
|
||||
"lr", # Load-Reserved (part of atomic operations)
|
||||
"sc", # Store-Conditional (part of atomic operations)
|
||||
"czero", # Conditional zero instructions (Zicond extension)
|
||||
]
|
||||
|
||||
# Default to empty list if ISA not recognized
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# TODO: Heuristics for detecting the RISCV ISA
|
||||
#!/usr/bin/env python3
|
||||
"""Parser superclass of specific parsers."""
|
||||
import operator
|
||||
|
||||
@@ -423,6 +423,8 @@ class MachineModel(object):
|
||||
return self._is_AArch64_mem_type(i_mem, mem)
|
||||
if self._data["isa"].lower() == "x86":
|
||||
return self._is_x86_mem_type(i_mem, mem)
|
||||
if self._data["isa"].lower() == "riscv":
|
||||
return self._is_RISCV_mem_type(i_mem, mem)
|
||||
|
||||
def get_data_ports(self):
|
||||
"""Return all data ports (i.e., ports with D-suffix) of current model."""
|
||||
@@ -463,6 +465,8 @@ class MachineModel(object):
|
||||
"icl": "x86",
|
||||
"icx": "x86",
|
||||
"spr": "x86",
|
||||
"rv64": "riscv", # RISC-V 64-bit
|
||||
"rv32": "riscv", # RISC-V 32-bit
|
||||
}
|
||||
arch = arch.lower()
|
||||
if arch in arch_dict:
|
||||
@@ -706,6 +710,24 @@ class MachineModel(object):
|
||||
else:
|
||||
raise ValueError("Parameter {} is not a valid operand code".format(operand))
|
||||
|
||||
def _create_db_operand_riscv(self, operand):
|
||||
"""Create instruction form operand for DB out of operand string."""
|
||||
if operand == "i":
|
||||
return ImmediateOperand(imd_type="int")
|
||||
elif operand in ["x", "a", "s", "t", "f", "v"]:
|
||||
return RegisterOperand(prefix=operand)
|
||||
elif operand.startswith("m"):
|
||||
return MemoryOperand(
|
||||
base="x" if "b" in operand else None,
|
||||
offset="imd" if "o" in operand else None,
|
||||
index="gpr" if "i" in operand else None,
|
||||
scale=1, # RISC-V doesn't use scaling
|
||||
pre_indexed=False, # RISC-V doesn't use pre/post indexing
|
||||
post_indexed=False,
|
||||
)
|
||||
else:
|
||||
raise ValueError("Parameter {} is not a valid operand code".format(operand))
|
||||
|
||||
def _check_for_duplicate(self, name, operands):
|
||||
"""
|
||||
Check if instruction form exists at least twice in DB.
|
||||
@@ -750,6 +772,8 @@ class MachineModel(object):
|
||||
return self._check_AArch64_operands(i_operand, operand)
|
||||
if self._data["isa"].lower() == "x86":
|
||||
return self._check_x86_operands(i_operand, operand)
|
||||
if self._data["isa"].lower() == "riscv":
|
||||
return self._check_RISCV_operands(i_operand, operand)
|
||||
|
||||
def _check_AArch64_operands(self, i_operand, operand):
|
||||
"""Check if the types of operand ``i_operand`` and ``operand`` match."""
|
||||
@@ -831,6 +855,51 @@ class MachineModel(object):
|
||||
return isinstance(i_operand, IdentifierOperand)
|
||||
return self._compare_db_entries(i_operand, operand)
|
||||
|
||||
def _check_RISCV_operands(self, i_operand, operand):
|
||||
"""Check if the types of operand ``i_operand`` and ``operand`` match."""
|
||||
# register
|
||||
if isinstance(operand, RegisterOperand):
|
||||
if not isinstance(i_operand, RegisterOperand):
|
||||
return False
|
||||
return self._is_RISCV_reg_type(i_operand, operand)
|
||||
# memory
|
||||
if isinstance(operand, MemoryOperand):
|
||||
if not isinstance(i_operand, MemoryOperand):
|
||||
return False
|
||||
return self._is_RISCV_mem_type(i_operand, operand)
|
||||
# immediate
|
||||
if isinstance(i_operand, ImmediateOperand) and i_operand.imd_type == self.WILDCARD:
|
||||
return isinstance(operand, ImmediateOperand) and (operand.value is not None)
|
||||
|
||||
if isinstance(i_operand, ImmediateOperand) and i_operand.imd_type == "int":
|
||||
return (
|
||||
isinstance(operand, ImmediateOperand)
|
||||
and operand.imd_type == "int"
|
||||
and operand.value is not None
|
||||
)
|
||||
|
||||
if isinstance(i_operand, ImmediateOperand) and i_operand.imd_type == "float":
|
||||
return (
|
||||
isinstance(operand, ImmediateOperand)
|
||||
and operand.imd_type == "float"
|
||||
and operand.value is not None
|
||||
)
|
||||
|
||||
if isinstance(i_operand, ImmediateOperand) and i_operand.imd_type == "double":
|
||||
return (
|
||||
isinstance(operand, ImmediateOperand)
|
||||
and operand.imd_type == "double"
|
||||
and operand.value is not None
|
||||
)
|
||||
|
||||
# identifier
|
||||
if isinstance(operand, IdentifierOperand) or (
|
||||
isinstance(operand, ImmediateOperand) and operand.identifier is not None
|
||||
):
|
||||
return isinstance(i_operand, IdentifierOperand)
|
||||
# no match
|
||||
return False
|
||||
|
||||
def _compare_db_entries(self, operand_1, operand_2):
|
||||
"""Check if operand types in DB format (i.e., not parsed) match."""
|
||||
return True
|
||||
@@ -931,6 +1000,43 @@ class MachineModel(object):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _is_RISCV_reg_type(self, i_reg, reg):
|
||||
"""Check if register type match for RISC-V."""
|
||||
# check for wildcards
|
||||
if reg.prefix == self.WILDCARD or i_reg.prefix == self.WILDCARD:
|
||||
return True
|
||||
|
||||
# First handle potentially None values to avoid AttributeError
|
||||
if reg.name is None or i_reg.name is None:
|
||||
# If both have same prefix, they might still match
|
||||
if reg.prefix == i_reg.prefix:
|
||||
return True
|
||||
# If we can't determine canonical names, be conservative and return False
|
||||
return False
|
||||
|
||||
# Check for ABI name (a0, t0, etc.) vs x-prefix registers (x10, x5, etc.)
|
||||
if (reg.prefix is None and i_reg.prefix == "x") or (reg.prefix == "x" and i_reg.prefix is None):
|
||||
try:
|
||||
# Need to check if they refer to the same register
|
||||
from osaca.parser import ParserRISCV
|
||||
parser = ParserRISCV()
|
||||
reg_canonical = parser._get_canonical_reg_name(reg)
|
||||
i_reg_canonical = parser._get_canonical_reg_name(i_reg)
|
||||
if reg_canonical == i_reg_canonical:
|
||||
return True
|
||||
except (AttributeError, KeyError):
|
||||
# If we can't determine canonical names, be conservative
|
||||
return False
|
||||
|
||||
# Check for direct prefix matches
|
||||
if reg.prefix == i_reg.prefix:
|
||||
# For vector registers, check lanes if present
|
||||
if reg.prefix == "v" and reg.lanes is not None and i_reg.lanes is not None:
|
||||
return reg.lanes == i_reg.lanes or self.WILDCARD in (reg.lanes + i_reg.lanes)
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def _is_AArch64_mem_type(self, i_mem, mem):
|
||||
"""Check if memory addressing type match."""
|
||||
if (
|
||||
@@ -1030,6 +1136,35 @@ class MachineModel(object):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _is_RISCV_mem_type(self, i_mem, mem):
|
||||
"""Check if memory addressing type match for RISC-V."""
|
||||
if (
|
||||
# check base
|
||||
(
|
||||
(mem.base is None and i_mem.base is None)
|
||||
or i_mem.base == self.WILDCARD
|
||||
or (isinstance(mem.base, RegisterOperand) and
|
||||
(mem.base.prefix == i_mem.base or
|
||||
(mem.base.name is not None and i_mem.base is not None)))
|
||||
)
|
||||
# check offset
|
||||
and (
|
||||
mem.offset == i_mem.offset
|
||||
or i_mem.offset == self.WILDCARD
|
||||
or (
|
||||
mem.offset is not None
|
||||
and isinstance(mem.offset, ImmediateOperand)
|
||||
and i_mem.offset == "imd"
|
||||
)
|
||||
)
|
||||
# RISC-V doesn't use index registers in its memory addressing
|
||||
and (mem.index is None and i_mem.index is None)
|
||||
# RISC-V doesn't use scaling in its memory addressing
|
||||
and (mem.scale == 1 and i_mem.scale == 1)
|
||||
):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _create_yaml_object(self):
|
||||
"""Create YAML object for parsing and dumping DB"""
|
||||
yaml_obj = ruamel.yaml.YAML()
|
||||
@@ -1041,4 +1176,4 @@ class MachineModel(object):
|
||||
|
||||
def __represent_none(self, yaml_obj, data):
|
||||
"""YAML representation for `None`"""
|
||||
return yaml_obj.represent_scalar("tag:yaml.org,2002:null", "~")
|
||||
return yaml_obj.represent_scalar("tag:yaml.org,2002:null", "~")
|
||||
@@ -2,7 +2,7 @@
|
||||
#!/usr/bin/env python3
|
||||
from collections import OrderedDict
|
||||
|
||||
from osaca.parser import ParserAArch64, ParserX86ATT, get_parser
|
||||
from osaca.parser import ParserAArch64, ParserX86ATT, ParserRISCV, get_parser
|
||||
from osaca.parser.register import RegisterOperand
|
||||
from osaca.parser.identifier import IdentifierOperand
|
||||
from osaca.parser.immediate import ImmediateOperand
|
||||
@@ -23,6 +23,8 @@ def reduce_to_section(kernel, isa):
|
||||
start, end = find_marked_kernel_x86ATT(kernel)
|
||||
elif isa == "aarch64":
|
||||
start, end = find_marked_kernel_AArch64(kernel)
|
||||
elif isa == "riscv":
|
||||
start, end = find_marked_kernel_RISCV(kernel)
|
||||
else:
|
||||
raise ValueError("ISA not supported.")
|
||||
if start == -1:
|
||||
@@ -71,6 +73,25 @@ def find_marked_kernel_x86ATT(lines):
|
||||
)
|
||||
|
||||
|
||||
def find_marked_kernel_RISCV(lines):
|
||||
"""
|
||||
Find marked section for RISC-V
|
||||
|
||||
:param list lines: kernel
|
||||
:returns: `tuple of int` -- start and end line of marked section
|
||||
"""
|
||||
nop_bytes = [19, 0, 0, 0] # RISC-V NOP (addi x0, x0, 0)
|
||||
return find_marked_section(
|
||||
lines,
|
||||
ParserRISCV(),
|
||||
["li"],
|
||||
"a1",
|
||||
[111, 222],
|
||||
nop_bytes,
|
||||
comments=COMMENT_MARKER,
|
||||
)
|
||||
|
||||
|
||||
def get_marker(isa, comment=""):
|
||||
"""Return tuple of start and end marker lines."""
|
||||
isa = isa.lower()
|
||||
@@ -101,6 +122,18 @@ def get_marker(isa, comment=""):
|
||||
"mov x1, #222 // OSACA END MARKER\n"
|
||||
".byte 213,3,32,31 // OSACA END MARKER\n"
|
||||
)
|
||||
elif isa == "riscv":
|
||||
start_marker_raw = (
|
||||
"li a1, 111 # OSACA START MARKER\n"
|
||||
".byte 19,0,0,0 # OSACA START MARKER\n"
|
||||
)
|
||||
if comment:
|
||||
start_marker_raw += "# {}\n".format(comment)
|
||||
# After loop
|
||||
end_marker_raw = (
|
||||
"li a1, 222 # OSACA END MARKER\n"
|
||||
".byte 19,0,0,0 # OSACA END MARKER\n"
|
||||
)
|
||||
|
||||
parser = get_parser(isa)
|
||||
start_marker = parser.parse_file(start_marker_raw)
|
||||
|
||||
@@ -38,52 +38,54 @@ class TestParserRISCV(unittest.TestCase):
|
||||
)
|
||||
|
||||
def test_label_parser(self):
|
||||
self.assertEqual(self._get_label(self.parser, "main:")[0].name, "main")
|
||||
self.assertEqual(self._get_label(self.parser, "loop_start:")[0].name, "loop_start")
|
||||
self.assertEqual(self._get_label(self.parser, ".L1:\t\t\t# comment")[0].name, ".L1")
|
||||
# Test common label patterns from kernel_riscv.s
|
||||
self.assertEqual(self._get_label(self.parser, "saxpy_golden:")[0].name, "saxpy_golden")
|
||||
self.assertEqual(self._get_label(self.parser, ".L4:")[0].name, ".L4")
|
||||
self.assertEqual(self._get_label(self.parser, ".L25:\t\t\t# Return")[0].name, ".L25")
|
||||
self.assertEqual(
|
||||
" ".join(self._get_label(self.parser, ".L1:\t\t\t# comment")[1]),
|
||||
"comment",
|
||||
" ".join(self._get_label(self.parser, ".L25:\t\t\t# Return")[1]),
|
||||
"Return",
|
||||
)
|
||||
with self.assertRaises(ParseException):
|
||||
self._get_label(self.parser, "\t.cfi_startproc")
|
||||
self._get_label(self.parser, "\t.word 1113498583")
|
||||
|
||||
def test_directive_parser(self):
|
||||
self.assertEqual(self._get_directive(self.parser, "\t.text")[0].name, "text")
|
||||
self.assertEqual(len(self._get_directive(self.parser, "\t.text")[0].parameters), 0)
|
||||
self.assertEqual(self._get_directive(self.parser, "\t.align\t4")[0].name, "align")
|
||||
self.assertEqual(self._get_directive(self.parser, "\t.word\t1113498583")[0].name, "word")
|
||||
self.assertEqual(
|
||||
len(self._get_directive(self.parser, "\t.align\t4")[0].parameters), 1
|
||||
len(self._get_directive(self.parser, "\t.word\t1113498583")[0].parameters), 1
|
||||
)
|
||||
self.assertEqual(
|
||||
self._get_directive(self.parser, "\t.align\t4")[0].parameters[0], "4"
|
||||
self._get_directive(self.parser, "\t.word\t1113498583")[0].parameters[0], "1113498583"
|
||||
)
|
||||
# Test string directive
|
||||
self.assertEqual(
|
||||
self._get_directive(self.parser, '.string "fail, %f=!%f\\n"')[0].name, "string"
|
||||
)
|
||||
self.assertEqual(
|
||||
self._get_directive(self.parser, " .byte 100,103,144 # IACA START")[
|
||||
0
|
||||
].name,
|
||||
"byte",
|
||||
self._get_directive(self.parser, '.string "fail, %f=!%f\\n"')[0].parameters[0],
|
||||
'"fail, %f=!%f\\n"'
|
||||
)
|
||||
# Test set directive
|
||||
self.assertEqual(
|
||||
self._get_directive(self.parser, "\t.set\t.LANCHOR0,. + 0")[0].name, "set"
|
||||
)
|
||||
self.assertEqual(
|
||||
self._get_directive(self.parser, " .byte 100,103,144 # IACA START")[
|
||||
0
|
||||
].parameters[2],
|
||||
"144",
|
||||
)
|
||||
self.assertEqual(
|
||||
" ".join(
|
||||
self._get_directive(self.parser, " .byte 100,103,144 # IACA START")[1]
|
||||
),
|
||||
"IACA START",
|
||||
len(self._get_directive(self.parser, "\t.set\t.LANCHOR0,. + 0")[0].parameters), 2
|
||||
)
|
||||
|
||||
def test_parse_instruction(self):
|
||||
instr1 = "addi t0, zero, 1"
|
||||
instr2 = "lw a0, 8(sp)"
|
||||
instr3 = "beq t0, t1, loop_start"
|
||||
instr4 = "lui a0, %hi(data)"
|
||||
instr5 = "sw ra, -4(sp)"
|
||||
instr6 = "jal ra, function"
|
||||
# Use generic RISC-V instructions for testing, not tied to a specific file
|
||||
instr1 = "beq a0,zero,.L12" # Branch instruction
|
||||
instr2 = "vsetvli a5,zero,e32,m1,ta,ma" # Vector instruction
|
||||
instr3 = "vle32.v v1,0(a1)" # Vector load instruction
|
||||
instr4 = "fmadd.s fa5,fa0,fa5,fa4" # Floating-point instruction
|
||||
instr5 = "addi sp,sp,-64" # Integer immediate instruction
|
||||
instr6 = "csrr a4,vlenb" # CSR instruction
|
||||
instr7 = "ret" # Return instruction
|
||||
instr8 = "lui a0,%hi(data)" # Load upper immediate with relocation
|
||||
instr9 = "sw ra,-4(sp)" # Store with negative offset
|
||||
|
||||
parsed_1 = self.parser.parse_instruction(instr1)
|
||||
parsed_2 = self.parser.parse_instruction(instr2)
|
||||
@@ -91,128 +93,138 @@ class TestParserRISCV(unittest.TestCase):
|
||||
parsed_4 = self.parser.parse_instruction(instr4)
|
||||
parsed_5 = self.parser.parse_instruction(instr5)
|
||||
parsed_6 = self.parser.parse_instruction(instr6)
|
||||
parsed_7 = self.parser.parse_instruction(instr7)
|
||||
parsed_8 = self.parser.parse_instruction(instr8)
|
||||
parsed_9 = self.parser.parse_instruction(instr9)
|
||||
|
||||
# Verify addi instruction
|
||||
self.assertEqual(parsed_1.mnemonic, "addi")
|
||||
self.assertEqual(parsed_1.operands[0].name, "t0")
|
||||
# Verify branch instruction
|
||||
self.assertEqual(parsed_1.mnemonic, "beq")
|
||||
self.assertEqual(len(parsed_1.operands), 3)
|
||||
self.assertTrue(isinstance(parsed_1.operands[0], RegisterOperand))
|
||||
self.assertEqual(parsed_1.operands[0].name, "a0")
|
||||
self.assertTrue(isinstance(parsed_1.operands[1], RegisterOperand))
|
||||
self.assertEqual(parsed_1.operands[1].name, "zero")
|
||||
self.assertEqual(parsed_1.operands[2].value, 1)
|
||||
self.assertTrue(isinstance(parsed_1.operands[2], IdentifierOperand))
|
||||
self.assertEqual(parsed_1.operands[2].name, ".L12")
|
||||
|
||||
# Verify lw instruction
|
||||
self.assertEqual(parsed_2.mnemonic, "lw")
|
||||
self.assertEqual(parsed_2.operands[0].name, "a0")
|
||||
self.assertEqual(parsed_2.operands[1].offset.value, 8)
|
||||
self.assertEqual(parsed_2.operands[1].base.name, "sp")
|
||||
# Verify vector configuration instruction
|
||||
self.assertEqual(parsed_2.mnemonic, "vsetvli")
|
||||
self.assertEqual(len(parsed_2.operands), 6) # Verify correct operand count
|
||||
self.assertEqual(parsed_2.operands[0].name, "a5")
|
||||
self.assertEqual(parsed_2.operands[1].name, "zero")
|
||||
|
||||
# Verify beq instruction
|
||||
self.assertEqual(parsed_3.mnemonic, "beq")
|
||||
self.assertEqual(parsed_3.operands[0].name, "t0")
|
||||
self.assertEqual(parsed_3.operands[1].name, "t1")
|
||||
self.assertEqual(parsed_3.operands[2].name, "loop_start")
|
||||
# Verify vector load instruction
|
||||
self.assertEqual(parsed_3.mnemonic, "vle32.v")
|
||||
self.assertEqual(len(parsed_3.operands), 2)
|
||||
self.assertEqual(parsed_3.operands[0].prefix, "v")
|
||||
self.assertEqual(parsed_3.operands[0].name, "1")
|
||||
self.assertTrue(isinstance(parsed_3.operands[1], MemoryOperand))
|
||||
self.assertEqual(parsed_3.operands[1].base.name, "a1")
|
||||
|
||||
# Verify lui instruction with high bits relocation
|
||||
self.assertEqual(parsed_4.mnemonic, "lui")
|
||||
self.assertEqual(parsed_4.operands[0].name, "a0")
|
||||
self.assertEqual(parsed_4.operands[1].name, "data")
|
||||
# Verify floating-point instruction
|
||||
self.assertEqual(parsed_4.mnemonic, "fmadd.s")
|
||||
self.assertEqual(len(parsed_4.operands), 4)
|
||||
self.assertEqual(parsed_4.operands[0].prefix, "f")
|
||||
|
||||
# Verify sw instruction with negative offset
|
||||
self.assertEqual(parsed_5.mnemonic, "sw")
|
||||
self.assertEqual(parsed_5.operands[0].name, "ra")
|
||||
self.assertEqual(parsed_5.operands[1].offset.value, -4)
|
||||
self.assertEqual(parsed_5.operands[1].base.name, "sp")
|
||||
# Verify integer immediate instruction
|
||||
self.assertEqual(parsed_5.mnemonic, "addi")
|
||||
self.assertEqual(len(parsed_5.operands), 3)
|
||||
self.assertEqual(parsed_5.operands[0].name, "sp")
|
||||
self.assertEqual(parsed_5.operands[1].name, "sp")
|
||||
self.assertTrue(isinstance(parsed_5.operands[2], ImmediateOperand))
|
||||
self.assertEqual(parsed_5.operands[2].value, -64)
|
||||
|
||||
# Verify jal instruction
|
||||
self.assertEqual(parsed_6.mnemonic, "jal")
|
||||
self.assertEqual(parsed_6.operands[0].name, "ra")
|
||||
self.assertEqual(parsed_6.operands[1].name, "function")
|
||||
# Verify CSR instruction
|
||||
self.assertEqual(parsed_6.mnemonic, "csrr")
|
||||
self.assertEqual(len(parsed_6.operands), 2)
|
||||
self.assertEqual(parsed_6.operands[0].name, "a4")
|
||||
self.assertEqual(parsed_6.operands[1].name, "vlenb")
|
||||
|
||||
# Verify return instruction
|
||||
self.assertEqual(parsed_7.mnemonic, "ret")
|
||||
self.assertEqual(len(parsed_7.operands), 0)
|
||||
|
||||
# Verify load upper immediate with relocation
|
||||
self.assertEqual(parsed_8.mnemonic, "lui")
|
||||
self.assertEqual(len(parsed_8.operands), 2)
|
||||
self.assertEqual(parsed_8.operands[0].name, "a0")
|
||||
self.assertEqual(parsed_8.operands[1].name, "data")
|
||||
|
||||
# Verify store with negative offset
|
||||
self.assertEqual(parsed_9.mnemonic, "sw")
|
||||
self.assertEqual(len(parsed_9.operands), 2)
|
||||
self.assertEqual(parsed_9.operands[0].name, "ra")
|
||||
self.assertTrue(isinstance(parsed_9.operands[1], MemoryOperand))
|
||||
self.assertEqual(parsed_9.operands[1].base.name, "sp")
|
||||
self.assertEqual(parsed_9.operands[1].offset.value, -4)
|
||||
|
||||
def test_parse_line(self):
|
||||
line_comment = "# -- Begin main"
|
||||
line_label = ".LBB0_1: # Loop Header"
|
||||
line_directive = ".cfi_def_cfa sp, 0"
|
||||
line_instruction = "addi sp, sp, -16 # allocate stack frame"
|
||||
# Use generic RISC-V lines for testing
|
||||
line_label = "saxpy_golden:"
|
||||
line_branch = " beq a0,zero,.L12"
|
||||
line_memory = " vle32.v v1,0(a1)"
|
||||
line_directive = " .word 1113498583"
|
||||
line_with_comment = " ret # Return from function"
|
||||
|
||||
instruction_form_1 = InstructionForm(
|
||||
mnemonic=None,
|
||||
operands=[],
|
||||
directive_id=None,
|
||||
comment_id="-- Begin main",
|
||||
label_id=None,
|
||||
line="# -- Begin main",
|
||||
line_number=1,
|
||||
)
|
||||
parsed_1 = self.parser.parse_line(line_label, 1)
|
||||
parsed_2 = self.parser.parse_line(line_branch, 2)
|
||||
parsed_3 = self.parser.parse_line(line_memory, 3)
|
||||
parsed_4 = self.parser.parse_line(line_directive, 4)
|
||||
parsed_5 = self.parser.parse_line(line_with_comment, 5)
|
||||
|
||||
instruction_form_2 = InstructionForm(
|
||||
mnemonic=None,
|
||||
operands=[],
|
||||
directive_id=None,
|
||||
comment_id="Loop Header",
|
||||
label_id=".LBB0_1",
|
||||
line=".LBB0_1: # Loop Header",
|
||||
line_number=2,
|
||||
)
|
||||
# Verify label parsing
|
||||
self.assertEqual(parsed_1.label, "saxpy_golden")
|
||||
self.assertIsNone(parsed_1.mnemonic)
|
||||
|
||||
instruction_form_3 = InstructionForm(
|
||||
mnemonic=None,
|
||||
operands=[],
|
||||
directive_id=DirectiveOperand(name="cfi_def_cfa", parameters=["sp", "0"]),
|
||||
comment_id=None,
|
||||
label_id=None,
|
||||
line=".cfi_def_cfa sp, 0",
|
||||
line_number=3,
|
||||
)
|
||||
# Verify branch instruction parsing
|
||||
self.assertEqual(parsed_2.mnemonic, "beq")
|
||||
self.assertEqual(len(parsed_2.operands), 3)
|
||||
self.assertEqual(parsed_2.operands[0].name, "a0")
|
||||
self.assertEqual(parsed_2.operands[1].name, "zero")
|
||||
self.assertEqual(parsed_2.operands[2].name, ".L12")
|
||||
|
||||
instruction_form_4 = InstructionForm(
|
||||
mnemonic="addi",
|
||||
operands=[
|
||||
RegisterOperand(prefix="x", name="sp"),
|
||||
RegisterOperand(prefix="x", name="sp"),
|
||||
ImmediateOperand(value=-16, imd_type="int"),
|
||||
],
|
||||
directive_id=None,
|
||||
comment_id="allocate stack frame",
|
||||
label_id=None,
|
||||
line="addi sp, sp, -16 # allocate stack frame",
|
||||
line_number=4,
|
||||
)
|
||||
|
||||
parsed_1 = self.parser.parse_line(line_comment, 1)
|
||||
parsed_2 = self.parser.parse_line(line_label, 2)
|
||||
parsed_3 = self.parser.parse_line(line_directive, 3)
|
||||
parsed_4 = self.parser.parse_line(line_instruction, 4)
|
||||
|
||||
self.assertEqual(parsed_1.comment, instruction_form_1.comment)
|
||||
self.assertEqual(parsed_2.label, instruction_form_2.label)
|
||||
self.assertEqual(parsed_3.directive.name, instruction_form_3.directive.name)
|
||||
self.assertEqual(parsed_3.directive.parameters, instruction_form_3.directive.parameters)
|
||||
self.assertEqual(parsed_4.mnemonic, instruction_form_4.mnemonic)
|
||||
self.assertEqual(parsed_4.operands[0].name, instruction_form_4.operands[0].name)
|
||||
self.assertEqual(parsed_4.operands[2].value, instruction_form_4.operands[2].value)
|
||||
self.assertEqual(parsed_4.comment, instruction_form_4.comment)
|
||||
# Verify memory instruction parsing
|
||||
self.assertEqual(parsed_3.mnemonic, "vle32.v")
|
||||
self.assertEqual(len(parsed_3.operands), 2)
|
||||
self.assertEqual(parsed_3.operands[0].prefix, "v")
|
||||
self.assertEqual(parsed_3.operands[0].name, "1")
|
||||
self.assertTrue(isinstance(parsed_3.operands[1], MemoryOperand))
|
||||
|
||||
# Verify directive parsing
|
||||
self.assertIsNone(parsed_4.mnemonic)
|
||||
self.assertEqual(parsed_4.directive.name, "word")
|
||||
self.assertEqual(parsed_4.directive.parameters[0], "1113498583")
|
||||
|
||||
# Verify comment parsing
|
||||
self.assertEqual(parsed_5.mnemonic, "ret")
|
||||
self.assertEqual(parsed_5.comment, "Return from function")
|
||||
|
||||
def test_parse_file(self):
|
||||
parsed = self.parser.parse_file(self.riscv_code)
|
||||
self.assertEqual(parsed[0].line_number, 1)
|
||||
self.assertGreater(len(parsed), 80) # More than 80 lines should be parsed
|
||||
self.assertGreater(len(parsed), 10) # There should be multiple lines
|
||||
|
||||
# Test parsing specific parts of the file
|
||||
# Find saxpy_vec label (which is the vector routine in the updated file)
|
||||
vector_idx = next((i for i, instr in enumerate(parsed) if instr.label == "saxpy_vec"), None)
|
||||
self.assertIsNotNone(vector_idx)
|
||||
# Find common elements that should exist in any RISC-V file
|
||||
# without being tied to specific line numbers
|
||||
|
||||
# Find floating-point instructions
|
||||
flw_idx = next((i for i, instr in enumerate(parsed) if instr.mnemonic == "flw"), None)
|
||||
self.assertIsNotNone(flw_idx)
|
||||
# Verify that we can find at least one label
|
||||
label_forms = [form for form in parsed if form.label is not None]
|
||||
self.assertGreater(len(label_forms), 0, "No labels found in the file")
|
||||
|
||||
# Find vector instructions
|
||||
vle_idx = next((i for i, instr in enumerate(parsed) if instr.mnemonic and instr.mnemonic.startswith("vle")), None)
|
||||
self.assertIsNotNone(vle_idx)
|
||||
# Verify that we can find at least one branch instruction
|
||||
branch_forms = [form for form in parsed if form.mnemonic and form.mnemonic.startswith("b")]
|
||||
self.assertGreater(len(branch_forms), 0, "No branch instructions found in the file")
|
||||
|
||||
# Find CSR instructions
|
||||
csr_idx = next((i for i, instr in enumerate(parsed) if instr.mnemonic == "csrr"), None)
|
||||
self.assertIsNotNone(csr_idx)
|
||||
# Verify that we can find at least one store/load instruction
|
||||
mem_forms = [form for form in parsed if form.mnemonic and (
|
||||
form.mnemonic.startswith("s") or
|
||||
form.mnemonic.startswith("l"))]
|
||||
self.assertGreater(len(mem_forms), 0, "No memory instructions found in the file")
|
||||
|
||||
# Verify that we can find at least one directive
|
||||
directive_forms = [form for form in parsed if form.directive is not None]
|
||||
self.assertGreater(len(directive_forms), 0, "No directives found in the file")
|
||||
|
||||
def test_register_mapping(self):
|
||||
def test_register_dependency(self):
|
||||
# Test ABI name to register number mapping
|
||||
reg_zero = RegisterOperand(name="zero")
|
||||
reg_ra = RegisterOperand(name="ra")
|
||||
@@ -224,6 +236,7 @@ class TestParserRISCV(unittest.TestCase):
|
||||
reg_x0 = RegisterOperand(prefix="x", name="0")
|
||||
reg_x1 = RegisterOperand(prefix="x", name="1")
|
||||
reg_x2 = RegisterOperand(prefix="x", name="2")
|
||||
reg_x5 = RegisterOperand(prefix="x", name="5") # Define reg_x5 for use in tests below
|
||||
reg_x10 = RegisterOperand(prefix="x", name="10")
|
||||
reg_x6 = RegisterOperand(prefix="x", name="6")
|
||||
reg_x18 = RegisterOperand(prefix="x", name="18")
|
||||
@@ -248,6 +261,27 @@ class TestParserRISCV(unittest.TestCase):
|
||||
self.assertFalse(self.parser.is_reg_dependend_of(reg_zero, reg_x1))
|
||||
self.assertFalse(self.parser.is_reg_dependend_of(reg_ra, reg_x2))
|
||||
self.assertFalse(self.parser.is_reg_dependend_of(reg_a0, reg_t1))
|
||||
|
||||
# Test floating-point registers
|
||||
reg_fa0 = RegisterOperand(prefix="f", name="a0")
|
||||
reg_fa1 = RegisterOperand(prefix="f", name="a1")
|
||||
reg_f10 = RegisterOperand(prefix="f", name="10")
|
||||
|
||||
# Test vector registers
|
||||
reg_v1 = RegisterOperand(prefix="v", name="1")
|
||||
reg_v2 = RegisterOperand(prefix="v", name="2")
|
||||
|
||||
# Test register type detection
|
||||
self.assertTrue(self.parser.is_gpr(reg_a0))
|
||||
self.assertTrue(self.parser.is_gpr(reg_x5))
|
||||
self.assertTrue(self.parser.is_gpr(reg_sp))
|
||||
|
||||
self.assertFalse(self.parser.is_gpr(reg_fa0))
|
||||
self.assertFalse(self.parser.is_gpr(reg_f10))
|
||||
|
||||
self.assertTrue(self.parser.is_vector_register(reg_v1))
|
||||
self.assertFalse(self.parser.is_vector_register(reg_x10))
|
||||
self.assertFalse(self.parser.is_vector_register(reg_fa0))
|
||||
|
||||
def test_normalize_imd(self):
|
||||
imd_decimal = ImmediateOperand(value="42")
|
||||
@@ -260,35 +294,30 @@ class TestParserRISCV(unittest.TestCase):
|
||||
self.assertEqual(self.parser.normalize_imd(imd_negative), -12)
|
||||
self.assertEqual(self.parser.normalize_imd(identifier), identifier)
|
||||
|
||||
def test_is_gpr(self):
|
||||
# Test integer registers
|
||||
reg_x5 = RegisterOperand(prefix="x", name="5")
|
||||
reg_t0 = RegisterOperand(name="t0")
|
||||
reg_sp = RegisterOperand(name="sp")
|
||||
def test_memory_operand_parsing(self):
|
||||
# Test memory operand parsing with different offsets and base registers
|
||||
|
||||
# Test floating point registers
|
||||
reg_f10 = RegisterOperand(prefix="f", name="10")
|
||||
reg_fa0 = RegisterOperand(name="fa0")
|
||||
# Parse memory operands from real instructions
|
||||
instr1 = "vle32.v v1,0(a1)"
|
||||
instr2 = "lw a0,8(sp)"
|
||||
instr3 = "sw ra,-4(sp)"
|
||||
|
||||
# Test vector registers
|
||||
reg_v3 = RegisterOperand(prefix="v", name="3")
|
||||
parsed1 = self.parser.parse_instruction(instr1)
|
||||
parsed2 = self.parser.parse_instruction(instr2)
|
||||
parsed3 = self.parser.parse_instruction(instr3)
|
||||
|
||||
self.assertTrue(self.parser.is_gpr(reg_x5))
|
||||
self.assertTrue(self.parser.is_gpr(reg_t0))
|
||||
self.assertTrue(self.parser.is_gpr(reg_sp))
|
||||
# Verify memory operands
|
||||
self.assertTrue(isinstance(parsed1.operands[1], MemoryOperand))
|
||||
self.assertEqual(parsed1.operands[1].base.name, "a1")
|
||||
self.assertEqual(parsed1.operands[1].offset.value, 0)
|
||||
|
||||
self.assertFalse(self.parser.is_gpr(reg_f10))
|
||||
self.assertFalse(self.parser.is_gpr(reg_fa0))
|
||||
self.assertFalse(self.parser.is_gpr(reg_v3))
|
||||
self.assertTrue(isinstance(parsed2.operands[1], MemoryOperand))
|
||||
self.assertEqual(parsed2.operands[1].base.name, "sp")
|
||||
self.assertEqual(parsed2.operands[1].offset.value, 8)
|
||||
|
||||
def test_is_vector_register(self):
|
||||
reg_v3 = RegisterOperand(prefix="v", name="3")
|
||||
reg_x5 = RegisterOperand(prefix="x", name="5")
|
||||
reg_f10 = RegisterOperand(prefix="f", name="10")
|
||||
|
||||
self.assertTrue(self.parser.is_vector_register(reg_v3))
|
||||
self.assertFalse(self.parser.is_vector_register(reg_x5))
|
||||
self.assertFalse(self.parser.is_vector_register(reg_f10))
|
||||
self.assertTrue(isinstance(parsed3.operands[1], MemoryOperand))
|
||||
self.assertEqual(parsed3.operands[1].base.name, "sp")
|
||||
self.assertEqual(parsed3.operands[1].offset.value, -4)
|
||||
|
||||
##################
|
||||
# Helper functions
|
||||
|
||||
Reference in New Issue
Block a user