Skip to content

Commit

Permalink
core: Move backend related files to separate directories (#1104)
Browse files Browse the repository at this point in the history
This PR moves the current RISCV backend files to a directory under `xdsl/backend/riscv/` to improve module organization and does the same for related test files.
  • Loading branch information
compor committed Jun 12, 2023
1 parent 6d10b86 commit 41415aa
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 94 deletions.
3 changes: 3 additions & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ xdsl/transforms/*snitch* @nazavode
# xDSL frontend
xdsl/frontend/** @webmiche

# xDSL RISCV backend
xdsl/backend/riscv/** @AntonLydike @superlopuh @compor @nazavode

# xDSL documentation
docs/** @math-fehr @webmiche @superlopuh @compor

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from io import StringIO
from xdsl.builder import Builder
from xdsl.dialects import riscv
from xdsl.dialects.builtin import ModuleOp
Expand All @@ -14,12 +13,6 @@ def context() -> MLContext:
return ctx


def riscv_code(module: ModuleOp) -> str:
stream = StringIO()
riscv.print_assembly(module, stream)
return stream.getvalue()


# Handwritten riscv dialect code to test register allocation


Expand Down Expand Up @@ -117,4 +110,6 @@ def main_region() -> None:
def test_allocate_simple_linear():
RISCVRegisterAllocation("BlockNaive").apply(context(), simple_linear_riscv)

assert riscv_code(simple_linear_riscv) == riscv_code(simple_linear_riscv_allocated)
assert riscv.riscv_code(simple_linear_riscv) == riscv.riscv_code(
simple_linear_riscv_allocated
)
Empty file added xdsl/backend/__init__.py
Empty file.
Empty file added xdsl/backend/riscv/__init__.py
Empty file.
86 changes: 86 additions & 0 deletions xdsl/backend/riscv/register_allocation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
from abc import ABC
from xdsl.dialects.riscv import Register, RegisterType, RISCVOp
from xdsl.dialects.builtin import ModuleOp


class RegisterAllocator(ABC):
"""
Base class for register allocation strategies.
"""

def __init__(self) -> None:
pass

def allocate_registers(self, module: ModuleOp) -> None:
"""
Allocates unallocated registers in the module.
"""

raise NotImplementedError()


class RegisterAllocatorBlockNaive(RegisterAllocator):
idx: int

def __init__(self) -> None:
self.idx = 0

"""
Since we've got neither right now a handling of a consistent ABI nor of a calling convention,
let's just assume that we have all the registers available for our use except the one explicitly reserved by the default riscv ABI.
"""

self.available_registers = list(Register.ABI_INDEX_BY_NAME.keys())
reserved_registers = set(["zero", "sp", "gp", "tp", "fp", "s0"])
self.available_registers = [
reg for reg in self.available_registers if reg not in reserved_registers
]

def allocate_registers(self, module: ModuleOp) -> None:
"""
Sets unallocated registers for each block to a finite set of real available registers.
When it runs out of real registers for a block, it allocates j registers.
"""

for region in module.regions:
for block in region.blocks:
block_registers = self.available_registers.copy()

for op in block.walk():
if not isinstance(op, RISCVOp):
# Don't perform register allocations on non-RISCV-ops
continue

for result in op.results:
assert isinstance(result.typ, RegisterType)
if result.typ.data.name is None:
# If we run out of real registers, allocate a j register
if not block_registers:
result.typ = RegisterType(Register(f"j{self.idx}"))
self.idx += 1
else:
result.typ = RegisterType(
Register(block_registers.pop())
)


class RegisterAllocatorJRegs(RegisterAllocator):
idx: int

def __init__(self) -> None:
self.idx = 0

def allocate_registers(self, module: ModuleOp) -> None:
"""
Sets unallocated registers to an infinite set of `j` registers
"""
for op in module.walk():
if not isinstance(op, RISCVOp):
# Don't perform register allocations on non-RISCV-ops
continue

for result in op.results:
assert isinstance(result.typ, RegisterType)
if result.typ.data.name is None:
result.typ = RegisterType(Register(f"j{self.idx}"))
self.idx += 1
91 changes: 5 additions & 86 deletions xdsl/transforms/riscv_register_allocation.py
Original file line number Diff line number Diff line change
@@ -1,92 +1,11 @@
from abc import ABC
from dataclasses import dataclass
from xdsl.dialects.builtin import ModuleOp
from xdsl.dialects.riscv import Register, RegisterType, RISCVOp
from xdsl.ir import MLContext
from xdsl.passes import ModulePass


class AbstractRegisterAllocator(ABC):
"""
Base class for register allocation strategies.
"""

def __init__(self) -> None:
pass

def allocate_registers(self, module: ModuleOp) -> None:
"""
Allocates unallocated registers in the module.
"""

raise NotImplementedError()


class RegisterAllocatorBlockNaive(AbstractRegisterAllocator):
idx: int

def __init__(self) -> None:
self.idx = 0

"""
Since we've got neither right now a handling of a consistent ABI nor of a calling convention,
let's just assume that we have all the registers available for our use except the one explicitly reserved by the default riscv ABI.
"""

self.available_registers = list(Register.ABI_INDEX_BY_NAME.keys())
reserved_registers = set(["zero", "sp", "gp", "tp", "fp", "s0"])
self.available_registers = [
reg for reg in self.available_registers if reg not in reserved_registers
]

def allocate_registers(self, module: ModuleOp) -> None:
"""
Sets unallocated registers for each block to a finite set of real available registers.
When it runs out of real registers for a block, it allocates j registers.
"""

for region in module.regions:
for block in region.blocks:
block_registers = self.available_registers.copy()

for op in block.walk():
if not isinstance(op, RISCVOp):
# Don't perform register allocations on non-RISCV-ops
continue

for result in op.results:
assert isinstance(result.typ, RegisterType)
if result.typ.data.name is None:
# If we run out of real registers, allocate a j register
if not block_registers:
result.typ = RegisterType(Register(f"j{self.idx}"))
self.idx += 1
else:
result.typ = RegisterType(
Register(block_registers.pop())
)


class RegisterAllocatorJRegs(AbstractRegisterAllocator):
idx: int

def __init__(self) -> None:
self.idx = 0

def allocate_registers(self, module: ModuleOp) -> None:
"""
Sets unallocated registers to an infinite set of `j` registers
"""
for op in module.walk():
if not isinstance(op, RISCVOp):
# Don't perform register allocations on non-RISCV-ops
continue

for result in op.results:
assert isinstance(result.typ, RegisterType)
if result.typ.data.name is None:
result.typ = RegisterType(Register(f"j{self.idx}"))
self.idx += 1
from xdsl.dialects.builtin import ModuleOp
from xdsl.backend.riscv.register_allocation import (
RegisterAllocatorBlockNaive,
RegisterAllocatorJRegs,
)


@dataclass
Expand Down

0 comments on commit 41415aa

Please sign in to comment.