Apply selected improvements from 1ceac6e: enhanced RISC-V parser, ImmediateOperand enhancements, and rv6→rv64 file renames

- Enhanced ImmediateOperand with reloc_type and symbol attributes for better RISC-V support
- Updated RISC-V parser with relocation type support (%hi, %lo, %pcrel_hi, etc.)
- Renamed example files from rv6 to rv64 for consistency
- Updated related configuration and test files
- All 115 tests pass successfully
This commit is contained in:
Metehan Dundar
2025-07-11 18:15:51 +02:00
parent 61b52dbf28
commit ebf76caa18
16 changed files with 554 additions and 253 deletions

2
.gitignore vendored
View File

@@ -1,7 +1,5 @@
# OSACA specific files and folders
*.*.pickle
osaca_testfront_venv/
examples/riscy_asm_files/
# Byte-compiled / optimized / DLL files
__pycache__/

View File

@@ -661,7 +661,7 @@ instruction_forms:
latency: 3
throughput: 1
port_pressure: [[1, ["FP"]]]
- name: VFMADD.VV
operands:
- class: register

View File

@@ -10,6 +10,8 @@ class ImmediateOperand(Operand):
imd_type=None,
value=None,
shift=None,
reloc_type=None,
symbol=None,
source=False,
destination=False,
):
@@ -18,6 +20,8 @@ class ImmediateOperand(Operand):
self._imd_type = imd_type
self._value = value
self._shift = shift
self._reloc_type = reloc_type
self._symbol = symbol
@property
def identifier(self):
@@ -33,7 +37,15 @@ class ImmediateOperand(Operand):
@property
def shift(self):
return self._imd_type
return self._shift
@property
def reloc_type(self):
return self._reloc_type
@property
def symbol(self):
return self._symbol
@imd_type.setter
def imd_type(self, itype):
@@ -51,10 +63,19 @@ class ImmediateOperand(Operand):
def shift(self, shift):
self._shift = shift
@reloc_type.setter
def reloc_type(self, reloc_type):
self._reloc_type = reloc_type
@symbol.setter
def symbol(self, symbol):
self._symbol = symbol
def __str__(self):
return (
f"Immediate(identifier={self._identifier}, imd_type={self._imd_type}, "
f"value={self._value}, shift={self._shift}, source={self._source}, destination={self._destination})"
f"value={self._value}, shift={self._shift}, reloc_type={self._reloc_type}, "
f"symbol={self._symbol}, source={self._source}, destination={self._destination})"
)
def __repr__(self):
@@ -62,10 +83,18 @@ class ImmediateOperand(Operand):
def __eq__(self, other):
if isinstance(other, ImmediateOperand):
# Handle cases where old instances might not have the new attributes
self_reloc_type = getattr(self, "_reloc_type", None)
self_symbol = getattr(self, "_symbol", None)
other_reloc_type = getattr(other, "_reloc_type", None)
other_symbol = getattr(other, "_symbol", None)
return (
self._identifier == other._identifier
and self._imd_type == other._imd_type
and self._value == other._value
and self._shift == other._shift
and self_reloc_type == other_reloc_type
and self_symbol == other_symbol
)
return False

File diff suppressed because it is too large Load Diff

View File

@@ -873,13 +873,13 @@ class MachineModel(object):
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(operand, (ImmediateOperand, int)):
if not isinstance(i_operand, ImmediateOperand):
@@ -895,7 +895,7 @@ class MachineModel(object):
if i_operand.imd_type == self.WILDCARD:
return True
return False
# identifier
if isinstance(operand, IdentifierOperand) or (
isinstance(operand, ImmediateOperand) and operand.identifier is not None
@@ -1011,7 +1011,7 @@ class MachineModel(object):
# 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
@@ -1019,12 +1019,13 @@ class MachineModel(object):
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)
@@ -1032,16 +1033,16 @@ class MachineModel(object):
return True
except (AttributeError, KeyError):
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 (
@@ -1181,4 +1182,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", "~")

View File

@@ -307,11 +307,13 @@ class TestCLI(unittest.TestCase):
@staticmethod
def _find_file(kernel, arch, comp):
testdir = os.path.dirname(__file__)
# Handle special case for rv64 architecture
arch_prefix = arch.lower() if arch.lower() == "rv64" else arch[:3].lower()
name = os.path.join(
testdir,
"../examples",
kernel,
kernel + ".s." + arch[:3].lower() + "." + comp.lower() + ".s",
kernel + ".s." + arch_prefix + "." + comp.lower() + ".s",
)
if kernel == "j2d" and arch.lower() == "csx":
name = name[:-1] + "AVX.s"

View File

@@ -8,9 +8,7 @@ import unittest
from pyparsing import ParseException
from osaca.parser import ParserRISCV, InstructionForm
from osaca.parser.directive import DirectiveOperand
from osaca.parser.memory import MemoryOperand
from osaca.parser import ParserRISCV
from osaca.parser.register import RegisterOperand
from osaca.parser.immediate import ImmediateOperand
from osaca.parser.identifier import IdentifierOperand
@@ -105,7 +103,7 @@ class TestParserRISCV(unittest.TestCase):
# Test 1: Line with label and instruction
parsed_1 = self.parser.parse_line(".L2:")
self.assertEqual(parsed_1.label, ".L2")
# Test 2: Line with instruction and comment
parsed_2 = self.parser.parse_line("addi x10, x10, 1 # increment")
self.assertEqual(parsed_2.mnemonic, "addi")
@@ -118,14 +116,14 @@ class TestParserRISCV(unittest.TestCase):
def test_parse_file(self):
parsed = self.parser.parse_file(self.riscv_code)
self.assertGreater(len(parsed), 10) # There should be multiple lines
# Find common elements that should exist in any RISC-V file
# without being tied to specific line numbers
# 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")
# 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")
@@ -148,7 +146,7 @@ class TestParserRISCV(unittest.TestCase):
reg_a0 = RegisterOperand(name="a0")
reg_t1 = RegisterOperand(name="t1")
reg_s2 = RegisterOperand(name="s2")
reg_x0 = RegisterOperand(prefix="x", name="0")
reg_x1 = RegisterOperand(prefix="x", name="1")
reg_x2 = RegisterOperand(prefix="x", name="2")
@@ -156,7 +154,7 @@ class TestParserRISCV(unittest.TestCase):
reg_x10 = RegisterOperand(prefix="x", name="10")
reg_x6 = RegisterOperand(prefix="x", name="6")
reg_x18 = RegisterOperand(prefix="x", name="18")
# Test canonical name conversion
self.assertEqual(self.parser._get_canonical_reg_name(reg_zero), "x0")
self.assertEqual(self.parser._get_canonical_reg_name(reg_ra), "x1")
@@ -164,7 +162,7 @@ class TestParserRISCV(unittest.TestCase):
self.assertEqual(self.parser._get_canonical_reg_name(reg_a0), "x10")
self.assertEqual(self.parser._get_canonical_reg_name(reg_t1), "x6")
self.assertEqual(self.parser._get_canonical_reg_name(reg_s2), "x18")
# Test register dependency
self.assertTrue(self.parser.is_reg_dependend_of(reg_zero, reg_x0))
self.assertTrue(self.parser.is_reg_dependend_of(reg_ra, reg_x1))
@@ -172,29 +170,27 @@ class TestParserRISCV(unittest.TestCase):
self.assertTrue(self.parser.is_reg_dependend_of(reg_a0, reg_x10))
self.assertTrue(self.parser.is_reg_dependend_of(reg_t1, reg_x6))
self.assertTrue(self.parser.is_reg_dependend_of(reg_s2, reg_x18))
# Test non-dependent registers
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))