From d782f06e84c5eed7e9e9743ca8c22b114a611b28 Mon Sep 17 00:00:00 2001 From: Metehan Dundar Date: Fri, 21 Mar 2025 17:16:39 +0100 Subject: [PATCH] Add RISC-V support and update version to 0.6.2 --- osaca/__init__.py | 2 +- osaca/data/isa/riscv.yml | 1820 +++++++++++++++---------------- osaca/data/rv64.yml | 657 ++++++++++- osaca/db_interface.py | 28 +- osaca/parser/base_parser.py | 1 + osaca/semantics/hw_model.py | 137 ++- osaca/semantics/marker_utils.py | 35 +- tests/test_parser_RISCV.py | 337 +++--- 8 files changed, 1908 insertions(+), 1109 deletions(-) diff --git a/osaca/__init__.py b/osaca/__init__.py index 27c3cbf..a3b56b3 100644 --- a/osaca/__init__.py +++ b/osaca/__init__.py @@ -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___ diff --git a/osaca/data/isa/riscv.yml b/osaca/data/isa/riscv.yml index cbaf0bb..803575a 100644 --- a/osaca/data/isa/riscv.yml +++ b/osaca/data/isa/riscv.yml @@ -8,943 +8,887 @@ isa: riscv # For vector instructions with suffixes (.v, .vv, .vf), the operand behavior follows # the base instruction pattern. instruction_forms: - - name: addi - operands: - - class: register - prefix: x - source: false - destination: true - - class: register - prefix: x - source: true - destination: false - - class: immediate - imd: 'int' - source: true - destination: false - operation: "op1['value'] = op2['value'] + op3['value']; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "Z" - source: false - destination: true - - class: flag - name: "N" - source: false - destination: true - - name: add - operands: - - class: register - prefix: x - source: false - destination: true - - class: register - prefix: x - source: true - destination: false - - class: register - prefix: x - source: true - destination: false - operation: "op1['value'] = op2['value'] + op3['value']; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "Z" - source: false - destination: true - - class: flag - name: "N" - source: false - destination: true - - name: sub - operands: - - class: register - prefix: x - source: false - destination: true - - class: register - prefix: x - source: true - destination: false - - class: register - prefix: x - source: true - destination: false - operation: "op1['value'] = op2['value'] - op3['value']; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "Z" - source: false - destination: true - - class: flag - name: "N" - source: false - destination: true - - name: mul - operands: - - class: register - prefix: x - source: false - destination: true - - class: register - prefix: x - source: true - destination: false - - class: register - prefix: x - source: true - destination: false - operation: "op1['value'] = op2['value'] * op3['value']; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "Z" - source: false - destination: true - - class: flag - name: "N" - source: false - destination: true - - name: div - operands: - - class: register - prefix: x - source: false - destination: true - - class: register - prefix: x - source: true - destination: false - - class: register - prefix: x - source: true - destination: false - operation: "op1['value'] = op2['value'] / op3['value']; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "Z" - source: false - destination: true - - class: flag - name: "N" - source: false - destination: true - - name: and - operands: - - class: register - prefix: x - source: false - destination: true - - class: register - prefix: x - source: true - destination: false - - class: register - prefix: x - source: true - destination: false - operation: "op1['value'] = op2['value'] & op3['value']; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "Z" - source: false - destination: true - - class: flag - name: "N" - source: false - destination: true - - name: or - operands: - - class: register - prefix: x - source: false - destination: true - - class: register - prefix: x - source: true - destination: false - - class: register - prefix: x - source: true - destination: false - operation: "op1['value'] = op2['value'] | op3['value']; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "Z" - source: false - destination: true - - class: flag - name: "N" - source: false - destination: true - - name: xor - operands: - - class: register - prefix: x - source: false - destination: true - - class: register - prefix: x - source: true - destination: false - - class: register - prefix: x - source: true - destination: false - operation: "op1['value'] = op2['value'] ^ op3['value']; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "Z" - source: false - destination: true - - class: flag - name: "N" - source: false - destination: true - - name: sll - operands: - - class: register - prefix: x - source: false - destination: true - - class: register - prefix: x - source: true - destination: false - - class: register - prefix: x - source: true - destination: false - operation: "op1['value'] = op2['value'] << op3['value']; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "Z" - source: false - destination: true - - class: flag - name: "N" - source: false - destination: true - - name: srl - operands: - - class: register - prefix: x - source: false - destination: true - - class: register - prefix: x - source: true - destination: false - - class: register - prefix: x - source: true - destination: false - operation: "op1['value'] = op2['value'] >> op3['value']; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "Z" - source: false - destination: true - - class: flag - name: "N" - source: false - destination: true - - name: sra - operands: - - class: register - prefix: x - source: false - destination: true - - class: register - prefix: x - source: true - destination: false - - class: register - prefix: x - source: true - destination: false - operation: "op1['value'] = op2['value'] >> op3['value']; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "Z" - source: false - destination: true - - class: flag - name: "N" - source: false - destination: true - - name: lw - operands: - - class: register - prefix: x - source: false - destination: true - - class: memory - base: "*" - offset: "*" - index: "*" - scale: "*" - pre_indexed: "*" - post_indexed: "*" - source: true - destination: false - operation: "op1['value'] = memory[op2['value']]; op1['name'] = op2['name']" - - name: sw - operands: - - class: register - prefix: x - source: true - destination: false - - class: memory - base: "*" - offset: "*" - index: "*" - scale: "*" - pre_indexed: "*" - post_indexed: "*" - source: false - destination: true - operation: "memory[op2['value']] = op1['value']" - - name: lb - operands: - - class: register - prefix: x - source: false - destination: true - - class: memory - base: "*" - offset: "*" - index: "*" - scale: "*" - pre_indexed: "*" - post_indexed: "*" - source: true - destination: false - operation: "op1['value'] = memory[op2['value']]; op1['name'] = op2['name']" - - name: sb - operands: - - class: register - prefix: x - source: true - destination: false - - class: memory - base: "*" - offset: "*" - index: "*" - scale: "*" - pre_indexed: "*" - post_indexed: "*" - source: false - destination: true - operation: "memory[op2['value']] = op1['value']" - - name: lh - operands: - - class: register - prefix: x - source: false - destination: true - - class: memory - base: "*" - offset: "*" - index: "*" - scale: "*" - pre_indexed: "*" - post_indexed: "*" - source: true - destination: false - operation: "op1['value'] = memory[op2['value']]; op1['name'] = op2['name']" - - name: sh - operands: - - class: register - prefix: x - source: true - destination: false - - class: memory - base: "*" - offset: "*" - index: "*" - scale: "*" - pre_indexed: "*" - post_indexed: "*" - source: false - destination: true - operation: "memory[op2['value']] = op1['value']" - - name: beq - operands: - - class: register - prefix: x - source: true - destination: false - - class: register - prefix: x - source: true - destination: false - - class: identifier - source: true - destination: false - hidden_operands: - - class: flag - name: "Z" - source: true - destination: false - - name: bne - operands: - - class: register - prefix: x - source: true - destination: false - - class: register - prefix: x - source: true - destination: false - - class: identifier - source: true - destination: false - hidden_operands: - - class: flag - name: "Z" - source: true - destination: false - - name: blt - operands: - - class: register - prefix: x - source: true - destination: false - - class: register - prefix: x - source: true - destination: false - - class: identifier - source: true - destination: false - hidden_operands: - - class: flag - name: "N" - source: true - destination: false - - name: bge - operands: - - class: register - prefix: x - source: true - destination: false - - class: register - prefix: x - source: true - destination: false - - class: identifier - source: true - destination: false - hidden_operands: - - class: flag - name: "N" - source: true - destination: false - - name: jal - operands: - - class: register - prefix: x - source: false - destination: true - - class: identifier - source: true - destination: false - operation: "op1['value'] = pc + 4; pc = op2['value']" - - name: jalr - operands: - - class: register - prefix: x - source: false - destination: true - - class: register - prefix: x - source: true - destination: false - - class: immediate - imd: 'int' - source: true - destination: false - operation: "op1['value'] = pc + 4; pc = op2['value'] + op3['value']" - - name: lui - operands: - - class: register - prefix: x - source: false - destination: true - - class: immediate - imd: 'int' - source: true - destination: false - operation: "op1['value'] = op2['value'] << 12" - - name: auipc - operands: - - class: register - prefix: x - source: false - destination: true - - class: immediate - imd: 'int' - source: true - destination: false - operation: "op1['value'] = pc + (op2['value'] << 12)" - - name: li - operands: - - class: register - prefix: x - source: false - destination: true - - class: immediate - imd: 'int' - source: true - destination: false - operation: "op1['value'] = op2['value']" - - name: mv - operands: - - class: register - prefix: x - source: false - destination: true - - class: register - prefix: x - source: true - destination: false - operation: "op1['value'] = op2['value']" - - name: ret - operands: - - class: register - prefix: x - name: "1" # x1 is the return address register (ra) - source: true - destination: false - operation: "pc = op1['value']" - - name: j - operands: - - class: identifier - source: true - destination: false - operation: "pc = op1['value']" - - name: jr - operands: - - class: register - prefix: x - source: true - destination: false - operation: "pc = op1['value']" - # Floating-point instructions - - name: flw - operands: - - class: register - prefix: f - source: false - destination: true - - class: memory - base: "*" - offset: "*" - index: "*" - scale: "*" - pre_indexed: "*" - post_indexed: "*" - source: true - destination: false - operation: "op1['value'] = memory[op2['value']]; op1['name'] = op2['name']" - - name: fsw - operands: - - class: register - prefix: f - source: true - destination: false - - class: memory - base: "*" - offset: "*" - index: "*" - scale: "*" - pre_indexed: "*" - post_indexed: "*" - source: false - destination: true - operation: "memory[op2['value']] = op1['value']" - - name: fadd - operands: - - class: register - prefix: f - source: false - destination: true - - class: register - prefix: f - source: true - destination: false - - class: register - prefix: f - source: true - destination: false - operation: "op1['value'] = op2['value'] + op3['value']; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "FSR" - source: true - destination: true - - name: fsub - operands: - - class: register - prefix: f - source: false - destination: true - - class: register - prefix: f - source: true - destination: false - - class: register - prefix: f - source: true - destination: false - operation: "op1['value'] = op2['value'] - op3['value']; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "FSR" - source: true - destination: true - - name: fmul - operands: - - class: register - prefix: f - source: false - destination: true - - class: register - prefix: f - source: true - destination: false - - class: register - prefix: f - source: true - destination: false - operation: "op1['value'] = op2['value'] * op3['value']; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "FSR" - source: true - destination: true - - name: fdiv - operands: - - class: register - prefix: f - source: false - destination: true - - class: register - prefix: f - source: true - destination: false - - class: register - prefix: f - source: true - destination: false - operation: "op1['value'] = op2['value'] / op3['value']; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "FSR" - source: true - destination: true - - name: fmv.x.w - operands: - - class: register - prefix: x - source: false - destination: true - - class: register - prefix: f - source: true - destination: false - operation: "op1['value'] = op2['value']" - - name: fmv.w.x - operands: - - class: register - prefix: f - source: false - destination: true - - class: register - prefix: x - source: true - destination: false - operation: "op1['value'] = op2['value']" - # Vector instructions - - name: vsetvli - operands: - - class: register - prefix: x - source: false - destination: true - - class: register - prefix: x - source: true - destination: false - - class: identifier - source: true - destination: false - - class: identifier - source: true - destination: false - - class: identifier - source: true - destination: false - - class: identifier - source: true - destination: false - operation: "op1['value'] = min(op2['value'], op3['value']); vtype = op4['value']; vsew = op5['value']; vlmul = op6['value']" - hidden_operands: - - class: flag - name: "VSR" - source: true - destination: true - - name: vsetivli - operands: - - class: register - prefix: x - source: false - destination: true - - class: immediate - imd: 'int' - source: true - destination: false - - class: identifier - source: true - destination: false - - class: identifier - source: true - destination: false - - class: identifier - source: true - destination: false - - class: identifier - source: true - destination: false - operation: "op1['value'] = min(op2['value'], op3['value']); vtype = op4['value']; vsew = op5['value']; vlmul = op6['value']" - hidden_operands: - - class: flag - name: "VSR" - source: true - destination: true - - name: vle32.v - operands: - - class: register - prefix: v - source: false - destination: true - - class: memory - base: "*" - offset: "*" - index: "*" - scale: "*" - pre_indexed: "*" - post_indexed: "*" - source: true - destination: false - operation: "op1['value'] = memory[op2['value']]; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "VSR" - source: true - destination: true - - name: vse32.v - operands: - - class: register - prefix: v - source: true - destination: false - - class: memory - base: "*" - offset: "*" - index: "*" - scale: "*" - pre_indexed: "*" - post_indexed: "*" - source: false - destination: true - operation: "memory[op2['value']] = op1['value']" - hidden_operands: - - class: flag - name: "VSR" - source: true - destination: true - - name: vadd.vv - operands: - - class: register - prefix: v - source: false - destination: true - - class: register - prefix: v - source: true - destination: false - - class: register - prefix: v - source: true - destination: false - operation: "op1['value'] = op2['value'] + op3['value']; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "VSR" - source: true - destination: true - - name: vfmv.v.f - operands: - - class: register - prefix: v - source: false - destination: true - - class: register - prefix: f - source: true - destination: false - operation: "op1['value'] = op2['value']" - hidden_operands: - - class: flag - name: "VSR" - source: true - destination: true - - name: vfmadd.vv - operands: - - class: register - prefix: v - source: false - destination: true - - class: register - prefix: v - source: true - destination: false - - class: register - prefix: v - source: true - destination: false - operation: "op1['value'] = op2['value'] * op3['value'] + op1['value']; op1['name'] = op2['name']" - hidden_operands: - - class: flag - name: "VSR" - source: true - destination: true - - name: vfmacc.vf - operands: - - class: register - prefix: v - source: true - destination: true - - class: register - prefix: f - source: true - destination: false - - class: register - prefix: v - source: true - destination: false - operation: "op1['value'] = op2['value'] * op3['value'] + op1['value']" - hidden_operands: - - class: flag - name: "VSR" - source: true - destination: true +- name: addi + operands: + - class: register + prefix: x + source: false + destination: true + - class: register + prefix: x + source: true + destination: false + - class: immediate + imd: int + source: true + destination: false + hidden_operands: + - class: flag + name: Z + source: false + destination: true + - class: flag + name: N + source: false + destination: true +- name: add + operands: + - class: register + prefix: x + source: false + destination: true + - class: register + prefix: x + source: true + destination: false + - class: register + prefix: x + source: true + destination: false + hidden_operands: + - class: flag + name: Z + source: false + destination: true + - class: flag + name: N + source: false + destination: true +- name: sub + operands: + - class: register + prefix: x + source: false + destination: true + - class: register + prefix: x + source: true + destination: false + - class: register + prefix: x + source: true + destination: false + hidden_operands: + - class: flag + name: Z + source: false + destination: true + - class: flag + name: N + source: false + destination: true +- name: mul + operands: + - class: register + prefix: x + source: false + destination: true + - class: register + prefix: x + source: true + destination: false + - class: register + prefix: x + source: true + destination: false + hidden_operands: + - class: flag + name: Z + source: false + destination: true + - class: flag + name: N + source: false + destination: true +- name: div + operands: + - class: register + prefix: x + source: false + destination: true + - class: register + prefix: x + source: true + destination: false + - class: register + prefix: x + source: true + destination: false + hidden_operands: + - class: flag + name: Z + source: false + destination: true + - class: flag + name: N + source: false + destination: true +- name: and + operands: + - class: register + prefix: x + source: false + destination: true + - class: register + prefix: x + source: true + destination: false + - class: register + prefix: x + source: true + destination: false + hidden_operands: + - class: flag + name: Z + source: false + destination: true + - class: flag + name: N + source: false + destination: true +- name: or + operands: + - class: register + prefix: x + source: false + destination: true + - class: register + prefix: x + source: true + destination: false + - class: register + prefix: x + source: true + destination: false + hidden_operands: + - class: flag + name: Z + source: false + destination: true + - class: flag + name: N + source: false + destination: true +- name: xor + operands: + - class: register + prefix: x + source: false + destination: true + - class: register + prefix: x + source: true + destination: false + - class: register + prefix: x + source: true + destination: false + hidden_operands: + - class: flag + name: Z + source: false + destination: true + - class: flag + name: N + source: false + destination: true +- name: sll + operands: + - class: register + prefix: x + source: false + destination: true + - class: register + prefix: x + source: true + destination: false + - class: register + prefix: x + source: true + destination: false + hidden_operands: + - class: flag + name: Z + source: false + destination: true + - class: flag + name: N + source: false + destination: true +- name: srl + operands: + - class: register + prefix: x + source: false + destination: true + - class: register + prefix: x + source: true + destination: false + - class: register + prefix: x + source: true + destination: false + hidden_operands: + - class: flag + name: Z + source: false + destination: true + - class: flag + name: N + source: false + destination: true +- name: sra + operands: + - class: register + prefix: x + source: false + destination: true + - class: register + prefix: x + source: true + destination: false + - class: register + prefix: x + source: true + destination: false + hidden_operands: + - class: flag + name: Z + source: false + destination: true + - class: flag + name: N + source: false + destination: true +- name: lw + operands: + - class: register + prefix: x + source: false + destination: true + - class: memory + base: '*' + offset: '*' + index: '*' + scale: '*' + pre_indexed: '*' + post_indexed: '*' + source: true + destination: false +- name: sw + operands: + - class: register + prefix: x + source: true + destination: false + - class: memory + base: '*' + offset: '*' + index: '*' + scale: '*' + pre_indexed: '*' + post_indexed: '*' + source: false + destination: true +- name: lb + operands: + - class: register + prefix: x + source: false + destination: true + - class: memory + base: '*' + offset: '*' + index: '*' + scale: '*' + pre_indexed: '*' + post_indexed: '*' + source: true + destination: false +- name: sb + operands: + - class: register + prefix: x + source: true + destination: false + - class: memory + base: '*' + offset: '*' + index: '*' + scale: '*' + pre_indexed: '*' + post_indexed: '*' + source: false + destination: true +- name: lh + operands: + - class: register + prefix: x + source: false + destination: true + - class: memory + base: '*' + offset: '*' + index: '*' + scale: '*' + pre_indexed: '*' + post_indexed: '*' + source: true + destination: false +- name: sh + operands: + - class: register + prefix: x + source: true + destination: false + - class: memory + base: '*' + offset: '*' + index: '*' + scale: '*' + pre_indexed: '*' + post_indexed: '*' + source: false + destination: true +- name: beq + operands: + - class: register + prefix: x + source: true + destination: false + - class: register + prefix: x + source: true + destination: false + - class: identifier + source: true + destination: false + hidden_operands: + - class: flag + name: Z + source: true + destination: false +- name: bne + operands: + - class: register + prefix: x + source: true + destination: false + - class: register + prefix: x + source: true + destination: false + - class: identifier + source: true + destination: false + hidden_operands: + - class: flag + name: Z + source: true + destination: false +- name: blt + operands: + - class: register + prefix: x + source: true + destination: false + - class: register + prefix: x + source: true + destination: false + - class: identifier + source: true + destination: false + hidden_operands: + - class: flag + name: N + source: true + destination: false +- name: bge + operands: + - class: register + prefix: x + source: true + destination: false + - class: register + prefix: x + source: true + destination: false + - class: identifier + source: true + destination: false + hidden_operands: + - class: flag + name: N + source: true + destination: false +- name: jal + operands: + - class: register + prefix: x + source: false + destination: true + - class: identifier + source: true + destination: false +- name: jalr + operands: + - class: register + prefix: x + source: false + destination: true + - class: register + prefix: x + source: true + destination: false + - class: immediate + imd: int + source: true + destination: false +- name: lui + operands: + - class: register + prefix: x + source: false + destination: true + - class: immediate + imd: int + source: true + destination: false +- name: auipc + operands: + - class: register + prefix: x + source: false + destination: true + - class: immediate + imd: int + source: true + destination: false +- name: li + operands: + - class: register + prefix: x + source: false + destination: true + - class: immediate + imd: int + source: true + destination: false +- name: mv + operands: + - class: register + prefix: x + source: false + destination: true + - class: register + prefix: x + source: true + destination: false +- name: ret + operands: + - class: register + prefix: x + name: '1' # x1 is the return address register (ra) + source: true + destination: false +- name: j + operands: + - class: identifier + source: true + destination: false +- name: jr + operands: + - class: register + prefix: x + source: true + destination: false +- name: flw + operands: + - class: register + prefix: f + source: false + destination: true + - class: memory + base: '*' + offset: '*' + index: '*' + scale: '*' + pre_indexed: '*' + post_indexed: '*' + source: true + destination: false +- name: fsw + operands: + - class: register + prefix: f + source: true + destination: false + - class: memory + base: '*' + offset: '*' + index: '*' + scale: '*' + pre_indexed: '*' + post_indexed: '*' + source: false + destination: true +- name: fadd + operands: + - class: register + prefix: f + source: false + destination: true + - class: register + prefix: f + source: true + destination: false + - class: register + prefix: f + source: true + destination: false + hidden_operands: + - class: flag + name: FSR + source: true + destination: true +- name: fsub + operands: + - class: register + prefix: f + source: false + destination: true + - class: register + prefix: f + source: true + destination: false + - class: register + prefix: f + source: true + destination: false + hidden_operands: + - class: flag + name: FSR + source: true + destination: true +- name: fmul + operands: + - class: register + prefix: f + source: false + destination: true + - class: register + prefix: f + source: true + destination: false + - class: register + prefix: f + source: true + destination: false + hidden_operands: + - class: flag + name: FSR + source: true + destination: true +- name: fdiv + operands: + - class: register + prefix: f + source: false + destination: true + - class: register + prefix: f + source: true + destination: false + - class: register + prefix: f + source: true + destination: false + hidden_operands: + - class: flag + name: FSR + source: true + destination: true +- name: fmv.x.w + operands: + - class: register + prefix: x + source: false + destination: true + - class: register + prefix: f + source: true + destination: false +- name: fmv.w.x + operands: + - class: register + prefix: f + source: false + destination: true + - class: register + prefix: x + source: true + destination: false +- name: vsetvli + operands: + - class: register + prefix: x + source: false + destination: true + - class: register + prefix: x + source: true + destination: false + - class: identifier + source: true + destination: false + - class: identifier + source: true + destination: false + - class: identifier + source: true + destination: false + - class: identifier + source: true + destination: false + hidden_operands: + - class: flag + name: VSR + source: false + destination: true +- name: vsetivli + operands: + - class: register + prefix: x + source: false + destination: true + - class: immediate + imd: int + source: true + destination: false + - class: identifier + source: true + destination: false + - class: identifier + source: true + destination: false + hidden_operands: + - class: flag + name: VSR + source: false + destination: true +- name: vle32.v + operands: + - class: register + prefix: v + source: false + destination: true + - class: memory + base: '*' + offset: '*' + index: '*' + scale: '*' + pre_indexed: '*' + post_indexed: '*' + source: true + destination: false + hidden_operands: + - class: flag + name: VSR + source: true + destination: true +- name: vse32.v + operands: + - class: register + prefix: v + source: true + destination: false + - class: memory + base: '*' + offset: '*' + index: '*' + scale: '*' + pre_indexed: '*' + post_indexed: '*' + source: false + destination: true + hidden_operands: + - class: flag + name: VSR + source: true + destination: true +- name: vadd.vv + operands: + - class: register + prefix: v + source: false + destination: true + - class: register + prefix: v + source: true + destination: false + - class: register + prefix: v + source: true + destination: false + hidden_operands: + - class: flag + name: VSR + source: true + destination: true +- name: vfmv.v.f + operands: + - class: register + prefix: v + source: false + destination: true + - class: register + prefix: f + source: true + destination: false + hidden_operands: + - class: flag + name: VSR + source: true + destination: true +- name: vfmadd.vv + operands: + - class: register + prefix: v + source: false + destination: true + - class: register + prefix: v + source: true + destination: false + - class: register + prefix: v + source: true + destination: false + hidden_operands: + - class: flag + name: VSR + source: true + destination: true +- name: vfmacc.vf + operands: + - class: register + prefix: v + source: true + destination: true + - class: register + prefix: f + source: true + destination: false + - class: register + prefix: v + source: true + destination: false + hidden_operands: + - class: flag + name: VSR + source: true + destination: true # CSR instructions - - name: csrr - operands: - - class: register - prefix: x - source: false - destination: true - - class: identifier - source: true - destination: false - operation: "op1['value'] = csr[op2['value']]" - hidden_operands: - - class: flag - name: "CSR" - source: true - destination: true - - name: csrw - operands: - - class: identifier - source: false - destination: true - - class: register - prefix: x - source: true - destination: false - operation: "csr[op1['value']] = op2['value']" - hidden_operands: - - class: flag - name: "CSR" - source: true - destination: true - - name: csrs - operands: - - class: identifier - source: true - destination: true - - class: register - prefix: x - source: true - destination: false - operation: "csr[op1['value']] |= op2['value']" - hidden_operands: - - class: flag - name: "CSR" - source: true - destination: true - - name: csrc - operands: - - class: identifier - source: true - destination: true - - class: register - prefix: x - source: true - destination: false - operation: "csr[op1['value']] &= ~op2['value']" - hidden_operands: - - class: flag - name: "CSR" - source: true - destination: true +- name: csrr + operands: + - class: register + prefix: x + source: false + destination: true + - class: identifier + source: true + destination: false + hidden_operands: + - class: flag + name: CSR + source: true + destination: false +- name: csrw + operands: + - class: identifier + source: false + destination: true + - class: register + prefix: x + source: true + destination: false + hidden_operands: + - class: flag + name: CSR + source: false + destination: true +- name: csrs + operands: + - class: identifier + source: true + destination: true + - class: register + prefix: x + source: true + destination: false + hidden_operands: + - class: flag + name: CSR + source: true + destination: true +- name: csrc + operands: + - class: identifier + source: true + destination: true + - class: register + prefix: x + source: true + destination: false + hidden_operands: + - class: flag + name: CSR + source: true + destination: true # Atomic instructions - - name: lr.w - operands: - - class: register - prefix: x - source: false - destination: true - - class: memory - base: "*" - offset: "*" - index: "*" - scale: "*" - pre_indexed: "*" - post_indexed: "*" - source: true - destination: false - operation: "op1['value'] = memory[op2['value']]; memory[op2['value']] = reserved" - hidden_operands: - - class: flag - name: "CSR" - source: true - destination: true - - name: sc.w - operands: - - class: register - prefix: x - source: false - destination: true - - class: register - prefix: x - source: true - destination: false - - class: memory - base: "*" - offset: "*" - index: "*" - scale: "*" - pre_indexed: "*" - post_indexed: "*" - source: true - destination: true - operation: "if (memory[op3['value']] == reserved) { memory[op3['value']] = op2['value']; op1['value'] = 0; } else { op1['value'] = 1; }" - hidden_operands: - - class: flag - name: "CSR" - source: true - destination: true \ No newline at end of file +- name: lr.w + operands: + - class: register + prefix: x + source: false + destination: true + - class: memory + base: '*' + offset: '*' + index: '*' + scale: '*' + pre_indexed: '*' + post_indexed: '*' + source: true + destination: false + hidden_operands: + - class: flag + name: CSR + source: true + destination: true +- name: sc.w + operands: + - class: register + prefix: x + source: false + destination: true + - class: register + prefix: x + source: true + destination: false + - class: memory + base: '*' + offset: '*' + index: '*' + scale: '*' + pre_indexed: '*' + post_indexed: '*' + source: true + destination: true + hidden_operands: + - class: flag + name: CSR + source: true + destination: true diff --git a/osaca/data/rv64.yml b/osaca/data/rv64.yml index 503fa1d..cb0bd12 100644 --- a/osaca/data/rv64.yml +++ b/osaca/data/rv64.yml @@ -1 +1,656 @@ -#TODO \ No newline at end of file +--- +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"]]] diff --git a/osaca/db_interface.py b/osaca/db_interface.py index b9b4a6c..ce02418 100644 --- a/osaca/db_interface.py +++ b/osaca/db_interface.py @@ -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 diff --git a/osaca/parser/base_parser.py b/osaca/parser/base_parser.py index 3ac2124..6fa0388 100644 --- a/osaca/parser/base_parser.py +++ b/osaca/parser/base_parser.py @@ -1,3 +1,4 @@ +# TODO: Heuristics for detecting the RISCV ISA #!/usr/bin/env python3 """Parser superclass of specific parsers.""" import operator diff --git a/osaca/semantics/hw_model.py b/osaca/semantics/hw_model.py index cd9d3c5..681de9b 100644 --- a/osaca/semantics/hw_model.py +++ b/osaca/semantics/hw_model.py @@ -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", "~") \ No newline at end of file diff --git a/osaca/semantics/marker_utils.py b/osaca/semantics/marker_utils.py index dc7c784..91a9140 100644 --- a/osaca/semantics/marker_utils.py +++ b/osaca/semantics/marker_utils.py @@ -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) diff --git a/tests/test_parser_RISCV.py b/tests/test_parser_RISCV.py index 32d0ed4..8f50a96 100644 --- a/tests/test_parser_RISCV.py +++ b/tests/test_parser_RISCV.py @@ -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