mirror of
https://github.com/RRZE-HPC/OSACA.git
synced 2026-01-06 19:20:07 +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"""
|
"""Open Source Architecture Code Analyzer"""
|
||||||
|
|
||||||
name = "osaca"
|
name = "osaca"
|
||||||
__version__ = "0.6.1"
|
__version__ = "0.6.2"
|
||||||
|
|
||||||
# To trigger travis deployment to pypi, do the following:
|
# To trigger travis deployment to pypi, do the following:
|
||||||
# 1. Increment __version___
|
# 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)
|
return _create_db_operand_riscv(operand)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unsupported ISA: {isa}")
|
raise ValueError(f"Unsupported ISA: {isa}")
|
||||||
|
|
||||||
|
|
||||||
def _create_db_operand_aarch64(operand):
|
def _create_db_operand_aarch64(operand):
|
||||||
"""Get DB operand for AArch64 by operand string."""
|
"""Get DB operand for AArch64 by operand string."""
|
||||||
@@ -308,8 +308,8 @@ def _create_db_operand_riscv(operand):
|
|||||||
}
|
}
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Parameter {operand} is not a valid operand code for RISC-V")
|
raise ValueError(f"Parameter {operand} is not a valid operand code for RISC-V")
|
||||||
|
|
||||||
|
|
||||||
########################
|
########################
|
||||||
# HELPERS SANITY CHECK #
|
# 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_x86 = ["vfm", "fm"]
|
||||||
suspicious_prefixes_arm = ["fml", "ldp", "stp", "str"]
|
suspicious_prefixes_arm = ["fml", "ldp", "stp", "str"]
|
||||||
suspicious_prefixes_riscv = [
|
suspicious_prefixes_riscv = [
|
||||||
"vfm", # Vector floating-point multiply
|
"vse", # Vector store (register is source, memory is destination)
|
||||||
"vle", # Vector load
|
"vfmacc", # Vector FMA with accumulation (first operand is both source and destination)
|
||||||
"vse", # Vector store
|
"vfmadd", # Vector FMA with addition (first operand is implicitly both source and destination)
|
||||||
"vset", # Vector configuration
|
"vset", # Vector configuration (complex operand pattern)
|
||||||
"vfmacc", # Vector FMA
|
"csrs", # CSR Set (first operand is both source and destination)
|
||||||
"vsetvl", # Vector length setting
|
"csrc", # CSR Clear (first operand is both source and destination)
|
||||||
"vfmv", # Vector floating-point move
|
"csrsi", # CSR Set Immediate (first operand is both source and destination)
|
||||||
"vadd", # Vector add
|
"csrci", # CSR Clear Immediate (first operand is both source and destination)
|
||||||
"vsub", # Vector subtract
|
"amo", # Atomic memory operations (read-modify-write to memory)
|
||||||
"vmul", # Vector multiply
|
"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
|
# Default to empty list if ISA not recognized
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
# TODO: Heuristics for detecting the RISCV ISA
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Parser superclass of specific parsers."""
|
"""Parser superclass of specific parsers."""
|
||||||
import operator
|
import operator
|
||||||
|
|||||||
@@ -423,6 +423,8 @@ class MachineModel(object):
|
|||||||
return self._is_AArch64_mem_type(i_mem, mem)
|
return self._is_AArch64_mem_type(i_mem, mem)
|
||||||
if self._data["isa"].lower() == "x86":
|
if self._data["isa"].lower() == "x86":
|
||||||
return self._is_x86_mem_type(i_mem, mem)
|
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):
|
def get_data_ports(self):
|
||||||
"""Return all data ports (i.e., ports with D-suffix) of current model."""
|
"""Return all data ports (i.e., ports with D-suffix) of current model."""
|
||||||
@@ -463,6 +465,8 @@ class MachineModel(object):
|
|||||||
"icl": "x86",
|
"icl": "x86",
|
||||||
"icx": "x86",
|
"icx": "x86",
|
||||||
"spr": "x86",
|
"spr": "x86",
|
||||||
|
"rv64": "riscv", # RISC-V 64-bit
|
||||||
|
"rv32": "riscv", # RISC-V 32-bit
|
||||||
}
|
}
|
||||||
arch = arch.lower()
|
arch = arch.lower()
|
||||||
if arch in arch_dict:
|
if arch in arch_dict:
|
||||||
@@ -706,6 +710,24 @@ class MachineModel(object):
|
|||||||
else:
|
else:
|
||||||
raise ValueError("Parameter {} is not a valid operand code".format(operand))
|
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):
|
def _check_for_duplicate(self, name, operands):
|
||||||
"""
|
"""
|
||||||
Check if instruction form exists at least twice in DB.
|
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)
|
return self._check_AArch64_operands(i_operand, operand)
|
||||||
if self._data["isa"].lower() == "x86":
|
if self._data["isa"].lower() == "x86":
|
||||||
return self._check_x86_operands(i_operand, operand)
|
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):
|
def _check_AArch64_operands(self, i_operand, operand):
|
||||||
"""Check if the types of operand ``i_operand`` and ``operand`` match."""
|
"""Check if the types of operand ``i_operand`` and ``operand`` match."""
|
||||||
@@ -831,6 +855,51 @@ class MachineModel(object):
|
|||||||
return isinstance(i_operand, IdentifierOperand)
|
return isinstance(i_operand, IdentifierOperand)
|
||||||
return self._compare_db_entries(i_operand, operand)
|
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):
|
def _compare_db_entries(self, operand_1, operand_2):
|
||||||
"""Check if operand types in DB format (i.e., not parsed) match."""
|
"""Check if operand types in DB format (i.e., not parsed) match."""
|
||||||
return True
|
return True
|
||||||
@@ -931,6 +1000,43 @@ class MachineModel(object):
|
|||||||
return True
|
return True
|
||||||
return False
|
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):
|
def _is_AArch64_mem_type(self, i_mem, mem):
|
||||||
"""Check if memory addressing type match."""
|
"""Check if memory addressing type match."""
|
||||||
if (
|
if (
|
||||||
@@ -1030,6 +1136,35 @@ class MachineModel(object):
|
|||||||
return True
|
return True
|
||||||
return False
|
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):
|
def _create_yaml_object(self):
|
||||||
"""Create YAML object for parsing and dumping DB"""
|
"""Create YAML object for parsing and dumping DB"""
|
||||||
yaml_obj = ruamel.yaml.YAML()
|
yaml_obj = ruamel.yaml.YAML()
|
||||||
@@ -1041,4 +1176,4 @@ class MachineModel(object):
|
|||||||
|
|
||||||
def __represent_none(self, yaml_obj, data):
|
def __represent_none(self, yaml_obj, data):
|
||||||
"""YAML representation for `None`"""
|
"""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
|
#!/usr/bin/env python3
|
||||||
from collections import OrderedDict
|
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.register import RegisterOperand
|
||||||
from osaca.parser.identifier import IdentifierOperand
|
from osaca.parser.identifier import IdentifierOperand
|
||||||
from osaca.parser.immediate import ImmediateOperand
|
from osaca.parser.immediate import ImmediateOperand
|
||||||
@@ -23,6 +23,8 @@ def reduce_to_section(kernel, isa):
|
|||||||
start, end = find_marked_kernel_x86ATT(kernel)
|
start, end = find_marked_kernel_x86ATT(kernel)
|
||||||
elif isa == "aarch64":
|
elif isa == "aarch64":
|
||||||
start, end = find_marked_kernel_AArch64(kernel)
|
start, end = find_marked_kernel_AArch64(kernel)
|
||||||
|
elif isa == "riscv":
|
||||||
|
start, end = find_marked_kernel_RISCV(kernel)
|
||||||
else:
|
else:
|
||||||
raise ValueError("ISA not supported.")
|
raise ValueError("ISA not supported.")
|
||||||
if start == -1:
|
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=""):
|
def get_marker(isa, comment=""):
|
||||||
"""Return tuple of start and end marker lines."""
|
"""Return tuple of start and end marker lines."""
|
||||||
isa = isa.lower()
|
isa = isa.lower()
|
||||||
@@ -101,6 +122,18 @@ def get_marker(isa, comment=""):
|
|||||||
"mov x1, #222 // OSACA END MARKER\n"
|
"mov x1, #222 // OSACA END MARKER\n"
|
||||||
".byte 213,3,32,31 // 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)
|
parser = get_parser(isa)
|
||||||
start_marker = parser.parse_file(start_marker_raw)
|
start_marker = parser.parse_file(start_marker_raw)
|
||||||
|
|||||||
@@ -38,52 +38,54 @@ class TestParserRISCV(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_label_parser(self):
|
def test_label_parser(self):
|
||||||
self.assertEqual(self._get_label(self.parser, "main:")[0].name, "main")
|
# Test common label patterns from kernel_riscv.s
|
||||||
self.assertEqual(self._get_label(self.parser, "loop_start:")[0].name, "loop_start")
|
self.assertEqual(self._get_label(self.parser, "saxpy_golden:")[0].name, "saxpy_golden")
|
||||||
self.assertEqual(self._get_label(self.parser, ".L1:\t\t\t# comment")[0].name, ".L1")
|
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(
|
self.assertEqual(
|
||||||
" ".join(self._get_label(self.parser, ".L1:\t\t\t# comment")[1]),
|
" ".join(self._get_label(self.parser, ".L25:\t\t\t# Return")[1]),
|
||||||
"comment",
|
"Return",
|
||||||
)
|
)
|
||||||
with self.assertRaises(ParseException):
|
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):
|
def test_directive_parser(self):
|
||||||
self.assertEqual(self._get_directive(self.parser, "\t.text")[0].name, "text")
|
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(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(
|
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.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.assertEqual(
|
||||||
self._get_directive(self.parser, " .byte 100,103,144 # IACA START")[
|
self._get_directive(self.parser, '.string "fail, %f=!%f\\n"')[0].parameters[0],
|
||||||
0
|
'"fail, %f=!%f\\n"'
|
||||||
].name,
|
)
|
||||||
"byte",
|
# Test set directive
|
||||||
|
self.assertEqual(
|
||||||
|
self._get_directive(self.parser, "\t.set\t.LANCHOR0,. + 0")[0].name, "set"
|
||||||
)
|
)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
self._get_directive(self.parser, " .byte 100,103,144 # IACA START")[
|
len(self._get_directive(self.parser, "\t.set\t.LANCHOR0,. + 0")[0].parameters), 2
|
||||||
0
|
|
||||||
].parameters[2],
|
|
||||||
"144",
|
|
||||||
)
|
|
||||||
self.assertEqual(
|
|
||||||
" ".join(
|
|
||||||
self._get_directive(self.parser, " .byte 100,103,144 # IACA START")[1]
|
|
||||||
),
|
|
||||||
"IACA START",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_parse_instruction(self):
|
def test_parse_instruction(self):
|
||||||
instr1 = "addi t0, zero, 1"
|
# Use generic RISC-V instructions for testing, not tied to a specific file
|
||||||
instr2 = "lw a0, 8(sp)"
|
instr1 = "beq a0,zero,.L12" # Branch instruction
|
||||||
instr3 = "beq t0, t1, loop_start"
|
instr2 = "vsetvli a5,zero,e32,m1,ta,ma" # Vector instruction
|
||||||
instr4 = "lui a0, %hi(data)"
|
instr3 = "vle32.v v1,0(a1)" # Vector load instruction
|
||||||
instr5 = "sw ra, -4(sp)"
|
instr4 = "fmadd.s fa5,fa0,fa5,fa4" # Floating-point instruction
|
||||||
instr6 = "jal ra, function"
|
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_1 = self.parser.parse_instruction(instr1)
|
||||||
parsed_2 = self.parser.parse_instruction(instr2)
|
parsed_2 = self.parser.parse_instruction(instr2)
|
||||||
@@ -91,128 +93,138 @@ class TestParserRISCV(unittest.TestCase):
|
|||||||
parsed_4 = self.parser.parse_instruction(instr4)
|
parsed_4 = self.parser.parse_instruction(instr4)
|
||||||
parsed_5 = self.parser.parse_instruction(instr5)
|
parsed_5 = self.parser.parse_instruction(instr5)
|
||||||
parsed_6 = self.parser.parse_instruction(instr6)
|
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
|
# Verify branch instruction
|
||||||
self.assertEqual(parsed_1.mnemonic, "addi")
|
self.assertEqual(parsed_1.mnemonic, "beq")
|
||||||
self.assertEqual(parsed_1.operands[0].name, "t0")
|
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[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
|
# Verify vector configuration instruction
|
||||||
self.assertEqual(parsed_2.mnemonic, "lw")
|
self.assertEqual(parsed_2.mnemonic, "vsetvli")
|
||||||
self.assertEqual(parsed_2.operands[0].name, "a0")
|
self.assertEqual(len(parsed_2.operands), 6) # Verify correct operand count
|
||||||
self.assertEqual(parsed_2.operands[1].offset.value, 8)
|
self.assertEqual(parsed_2.operands[0].name, "a5")
|
||||||
self.assertEqual(parsed_2.operands[1].base.name, "sp")
|
self.assertEqual(parsed_2.operands[1].name, "zero")
|
||||||
|
|
||||||
# Verify beq instruction
|
# Verify vector load instruction
|
||||||
self.assertEqual(parsed_3.mnemonic, "beq")
|
self.assertEqual(parsed_3.mnemonic, "vle32.v")
|
||||||
self.assertEqual(parsed_3.operands[0].name, "t0")
|
self.assertEqual(len(parsed_3.operands), 2)
|
||||||
self.assertEqual(parsed_3.operands[1].name, "t1")
|
self.assertEqual(parsed_3.operands[0].prefix, "v")
|
||||||
self.assertEqual(parsed_3.operands[2].name, "loop_start")
|
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
|
# Verify floating-point instruction
|
||||||
self.assertEqual(parsed_4.mnemonic, "lui")
|
self.assertEqual(parsed_4.mnemonic, "fmadd.s")
|
||||||
self.assertEqual(parsed_4.operands[0].name, "a0")
|
self.assertEqual(len(parsed_4.operands), 4)
|
||||||
self.assertEqual(parsed_4.operands[1].name, "data")
|
self.assertEqual(parsed_4.operands[0].prefix, "f")
|
||||||
|
|
||||||
# Verify sw instruction with negative offset
|
# Verify integer immediate instruction
|
||||||
self.assertEqual(parsed_5.mnemonic, "sw")
|
self.assertEqual(parsed_5.mnemonic, "addi")
|
||||||
self.assertEqual(parsed_5.operands[0].name, "ra")
|
self.assertEqual(len(parsed_5.operands), 3)
|
||||||
self.assertEqual(parsed_5.operands[1].offset.value, -4)
|
self.assertEqual(parsed_5.operands[0].name, "sp")
|
||||||
self.assertEqual(parsed_5.operands[1].base.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
|
# Verify CSR instruction
|
||||||
self.assertEqual(parsed_6.mnemonic, "jal")
|
self.assertEqual(parsed_6.mnemonic, "csrr")
|
||||||
self.assertEqual(parsed_6.operands[0].name, "ra")
|
self.assertEqual(len(parsed_6.operands), 2)
|
||||||
self.assertEqual(parsed_6.operands[1].name, "function")
|
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):
|
def test_parse_line(self):
|
||||||
line_comment = "# -- Begin main"
|
# Use generic RISC-V lines for testing
|
||||||
line_label = ".LBB0_1: # Loop Header"
|
line_label = "saxpy_golden:"
|
||||||
line_directive = ".cfi_def_cfa sp, 0"
|
line_branch = " beq a0,zero,.L12"
|
||||||
line_instruction = "addi sp, sp, -16 # allocate stack frame"
|
line_memory = " vle32.v v1,0(a1)"
|
||||||
|
line_directive = " .word 1113498583"
|
||||||
|
line_with_comment = " ret # Return from function"
|
||||||
|
|
||||||
instruction_form_1 = InstructionForm(
|
parsed_1 = self.parser.parse_line(line_label, 1)
|
||||||
mnemonic=None,
|
parsed_2 = self.parser.parse_line(line_branch, 2)
|
||||||
operands=[],
|
parsed_3 = self.parser.parse_line(line_memory, 3)
|
||||||
directive_id=None,
|
parsed_4 = self.parser.parse_line(line_directive, 4)
|
||||||
comment_id="-- Begin main",
|
parsed_5 = self.parser.parse_line(line_with_comment, 5)
|
||||||
label_id=None,
|
|
||||||
line="# -- Begin main",
|
|
||||||
line_number=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
instruction_form_2 = InstructionForm(
|
# Verify label parsing
|
||||||
mnemonic=None,
|
self.assertEqual(parsed_1.label, "saxpy_golden")
|
||||||
operands=[],
|
self.assertIsNone(parsed_1.mnemonic)
|
||||||
directive_id=None,
|
|
||||||
comment_id="Loop Header",
|
|
||||||
label_id=".LBB0_1",
|
|
||||||
line=".LBB0_1: # Loop Header",
|
|
||||||
line_number=2,
|
|
||||||
)
|
|
||||||
|
|
||||||
instruction_form_3 = InstructionForm(
|
# Verify branch instruction parsing
|
||||||
mnemonic=None,
|
self.assertEqual(parsed_2.mnemonic, "beq")
|
||||||
operands=[],
|
self.assertEqual(len(parsed_2.operands), 3)
|
||||||
directive_id=DirectiveOperand(name="cfi_def_cfa", parameters=["sp", "0"]),
|
self.assertEqual(parsed_2.operands[0].name, "a0")
|
||||||
comment_id=None,
|
self.assertEqual(parsed_2.operands[1].name, "zero")
|
||||||
label_id=None,
|
self.assertEqual(parsed_2.operands[2].name, ".L12")
|
||||||
line=".cfi_def_cfa sp, 0",
|
|
||||||
line_number=3,
|
|
||||||
)
|
|
||||||
|
|
||||||
instruction_form_4 = InstructionForm(
|
# Verify memory instruction parsing
|
||||||
mnemonic="addi",
|
self.assertEqual(parsed_3.mnemonic, "vle32.v")
|
||||||
operands=[
|
self.assertEqual(len(parsed_3.operands), 2)
|
||||||
RegisterOperand(prefix="x", name="sp"),
|
self.assertEqual(parsed_3.operands[0].prefix, "v")
|
||||||
RegisterOperand(prefix="x", name="sp"),
|
self.assertEqual(parsed_3.operands[0].name, "1")
|
||||||
ImmediateOperand(value=-16, imd_type="int"),
|
self.assertTrue(isinstance(parsed_3.operands[1], MemoryOperand))
|
||||||
],
|
|
||||||
directive_id=None,
|
# Verify directive parsing
|
||||||
comment_id="allocate stack frame",
|
self.assertIsNone(parsed_4.mnemonic)
|
||||||
label_id=None,
|
self.assertEqual(parsed_4.directive.name, "word")
|
||||||
line="addi sp, sp, -16 # allocate stack frame",
|
self.assertEqual(parsed_4.directive.parameters[0], "1113498583")
|
||||||
line_number=4,
|
|
||||||
)
|
# Verify comment parsing
|
||||||
|
self.assertEqual(parsed_5.mnemonic, "ret")
|
||||||
parsed_1 = self.parser.parse_line(line_comment, 1)
|
self.assertEqual(parsed_5.comment, "Return from function")
|
||||||
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)
|
|
||||||
|
|
||||||
def test_parse_file(self):
|
def test_parse_file(self):
|
||||||
parsed = self.parser.parse_file(self.riscv_code)
|
parsed = self.parser.parse_file(self.riscv_code)
|
||||||
self.assertEqual(parsed[0].line_number, 1)
|
self.assertGreater(len(parsed), 10) # There should be multiple lines
|
||||||
self.assertGreater(len(parsed), 80) # More than 80 lines should be parsed
|
|
||||||
|
|
||||||
# Test parsing specific parts of the file
|
# Find common elements that should exist in any RISC-V file
|
||||||
# Find saxpy_vec label (which is the vector routine in the updated file)
|
# without being tied to specific line numbers
|
||||||
vector_idx = next((i for i, instr in enumerate(parsed) if instr.label == "saxpy_vec"), None)
|
|
||||||
self.assertIsNotNone(vector_idx)
|
|
||||||
|
|
||||||
# Find floating-point instructions
|
# Verify that we can find at least one label
|
||||||
flw_idx = next((i for i, instr in enumerate(parsed) if instr.mnemonic == "flw"), None)
|
label_forms = [form for form in parsed if form.label is not None]
|
||||||
self.assertIsNotNone(flw_idx)
|
self.assertGreater(len(label_forms), 0, "No labels found in the file")
|
||||||
|
|
||||||
# Find vector instructions
|
# Verify that we can find at least one branch instruction
|
||||||
vle_idx = next((i for i, instr in enumerate(parsed) if instr.mnemonic and instr.mnemonic.startswith("vle")), None)
|
branch_forms = [form for form in parsed if form.mnemonic and form.mnemonic.startswith("b")]
|
||||||
self.assertIsNotNone(vle_idx)
|
self.assertGreater(len(branch_forms), 0, "No branch instructions found in the file")
|
||||||
|
|
||||||
# Find CSR instructions
|
# Verify that we can find at least one store/load instruction
|
||||||
csr_idx = next((i for i, instr in enumerate(parsed) if instr.mnemonic == "csrr"), None)
|
mem_forms = [form for form in parsed if form.mnemonic and (
|
||||||
self.assertIsNotNone(csr_idx)
|
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
|
# Test ABI name to register number mapping
|
||||||
reg_zero = RegisterOperand(name="zero")
|
reg_zero = RegisterOperand(name="zero")
|
||||||
reg_ra = RegisterOperand(name="ra")
|
reg_ra = RegisterOperand(name="ra")
|
||||||
@@ -224,6 +236,7 @@ class TestParserRISCV(unittest.TestCase):
|
|||||||
reg_x0 = RegisterOperand(prefix="x", name="0")
|
reg_x0 = RegisterOperand(prefix="x", name="0")
|
||||||
reg_x1 = RegisterOperand(prefix="x", name="1")
|
reg_x1 = RegisterOperand(prefix="x", name="1")
|
||||||
reg_x2 = RegisterOperand(prefix="x", name="2")
|
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_x10 = RegisterOperand(prefix="x", name="10")
|
||||||
reg_x6 = RegisterOperand(prefix="x", name="6")
|
reg_x6 = RegisterOperand(prefix="x", name="6")
|
||||||
reg_x18 = RegisterOperand(prefix="x", name="18")
|
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_zero, reg_x1))
|
||||||
self.assertFalse(self.parser.is_reg_dependend_of(reg_ra, reg_x2))
|
self.assertFalse(self.parser.is_reg_dependend_of(reg_ra, reg_x2))
|
||||||
self.assertFalse(self.parser.is_reg_dependend_of(reg_a0, reg_t1))
|
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):
|
def test_normalize_imd(self):
|
||||||
imd_decimal = ImmediateOperand(value="42")
|
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(imd_negative), -12)
|
||||||
self.assertEqual(self.parser.normalize_imd(identifier), identifier)
|
self.assertEqual(self.parser.normalize_imd(identifier), identifier)
|
||||||
|
|
||||||
def test_is_gpr(self):
|
def test_memory_operand_parsing(self):
|
||||||
# Test integer registers
|
# Test memory operand parsing with different offsets and base registers
|
||||||
reg_x5 = RegisterOperand(prefix="x", name="5")
|
|
||||||
reg_t0 = RegisterOperand(name="t0")
|
|
||||||
reg_sp = RegisterOperand(name="sp")
|
|
||||||
|
|
||||||
# Test floating point registers
|
# Parse memory operands from real instructions
|
||||||
reg_f10 = RegisterOperand(prefix="f", name="10")
|
instr1 = "vle32.v v1,0(a1)"
|
||||||
reg_fa0 = RegisterOperand(name="fa0")
|
instr2 = "lw a0,8(sp)"
|
||||||
|
instr3 = "sw ra,-4(sp)"
|
||||||
|
|
||||||
# Test vector registers
|
parsed1 = self.parser.parse_instruction(instr1)
|
||||||
reg_v3 = RegisterOperand(prefix="v", name="3")
|
parsed2 = self.parser.parse_instruction(instr2)
|
||||||
|
parsed3 = self.parser.parse_instruction(instr3)
|
||||||
|
|
||||||
self.assertTrue(self.parser.is_gpr(reg_x5))
|
# Verify memory operands
|
||||||
self.assertTrue(self.parser.is_gpr(reg_t0))
|
self.assertTrue(isinstance(parsed1.operands[1], MemoryOperand))
|
||||||
self.assertTrue(self.parser.is_gpr(reg_sp))
|
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.assertTrue(isinstance(parsed2.operands[1], MemoryOperand))
|
||||||
self.assertFalse(self.parser.is_gpr(reg_fa0))
|
self.assertEqual(parsed2.operands[1].base.name, "sp")
|
||||||
self.assertFalse(self.parser.is_gpr(reg_v3))
|
self.assertEqual(parsed2.operands[1].offset.value, 8)
|
||||||
|
|
||||||
def test_is_vector_register(self):
|
self.assertTrue(isinstance(parsed3.operands[1], MemoryOperand))
|
||||||
reg_v3 = RegisterOperand(prefix="v", name="3")
|
self.assertEqual(parsed3.operands[1].base.name, "sp")
|
||||||
reg_x5 = RegisterOperand(prefix="x", name="5")
|
self.assertEqual(parsed3.operands[1].offset.value, -4)
|
||||||
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))
|
|
||||||
|
|
||||||
##################
|
##################
|
||||||
# Helper functions
|
# Helper functions
|
||||||
|
|||||||
Reference in New Issue
Block a user