From ed232aa46d3e84156ce61be5e8dcbe7602a0b9c9 Mon Sep 17 00:00:00 2001 From: Justus Bergermann Date: Wed, 16 Oct 2024 15:56:30 +0200 Subject: [PATCH 01/55] feat: add risc-v example --- .gitignore | 1 + .idea/.gitignore | 3 + .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 7 + .idea/modules.xml | 8 + .idea/slothy.iml | 14 + .idea/vcs.xml | 6 + example.py | 28 +- examples/naive/riscv/riscv_simple0.s | 1 + slothy/targets/riscv/riscv.py | 3318 +++++++++++++++++ slothy/targets/riscv/xuantie_c908.py | 299 ++ todo.md | 9 + 12 files changed, 3699 insertions(+), 1 deletion(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/slothy.iml create mode 100644 .idea/vcs.xml create mode 100644 examples/naive/riscv/riscv_simple0.s create mode 100644 slothy/targets/riscv/riscv.py create mode 100644 slothy/targets/riscv/xuantie_c908.py create mode 100644 todo.md diff --git a/.gitignore b/.gitignore index fdbbfad2..383084fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ **/__pycache__ venv/ +.idea diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 00000000..26d33521 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000..105ce2da --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 00000000..2ca87cc6 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 00000000..5881bcf8 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/slothy.iml b/.idea/slothy.iml new file mode 100644 index 00000000..8e5446ac --- /dev/null +++ b/.idea/slothy.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 00000000..35eb1ddf --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/example.py b/example.py index fdc71340..30dbc100 100644 --- a/example.py +++ b/example.py @@ -41,12 +41,16 @@ import slothy.targets.aarch64.apple_m1_firestorm_experimental as Target_AppleM1_firestorm import slothy.targets.aarch64.apple_m1_icestorm_experimental as Target_AppleM1_icestorm +import slothy.targets.riscv.riscv as RISC_V +import slothy.targets.riscv.xuantie_c908 as Target_XiunTanC908 + target_label_dict = {Target_CortexA55: "a55", Target_CortexA72: "a72", Target_CortexM55r1: "m55", Target_CortexM85r1: "m85", Target_AppleM1_firestorm: "m1_firestorm", - Target_AppleM1_icestorm: "m1_icestorm"} + Target_AppleM1_icestorm: "m1_icestorm", + Target_XiunTanC908: "c908"} class ExampleException(Exception): @@ -76,6 +80,8 @@ def __init__(self, infile, name=None, funcname=None, suffix="opt", subfolder = "" if self.arch == AArch64_Neon: subfolder = "aarch64/" + elif self.arch == RISC_V: + subfolder = "riscv/" self.infile_full = f"examples/naive/{subfolder}{self.infile}.s" self.outfile_full = f"examples/opt/{subfolder}{self.outfile}.s" self.name = name @@ -1355,6 +1361,23 @@ def core(self, slothy): slothy.config.sw_pipelining.optimize_postamble = False slothy.optimize_loop("flt_radix4_fft_loop_start") +class RISC_VExample0(Example): + def __init__(self, var="", arch=RISC_V, target=Target_XiunTanC908): + name = "riscv_simple0" + infile = name + + if var != "": + name += f"_{var}" + infile += f"_{var}" + name += f"_{target_label_dict[target]}" + + super().__init__(infile, name, rename=True, arch=arch, target=target) + + def core(self,slothy): + slothy.config.variable_size=True + slothy.config.constraints.stalls_first_attempt=32 + slothy.optimize() + ############################################################################################# @@ -1497,6 +1520,9 @@ def main(): fft_floatingpoint_radix4(), # Fixed point fft_fixedpoint_radix4(), + + # RISC-V + RISC_VExample0(target=Target_XiunTanC908) ] all_example_names = [e.name for e in examples] diff --git a/examples/naive/riscv/riscv_simple0.s b/examples/naive/riscv/riscv_simple0.s new file mode 100644 index 00000000..b2e52f79 --- /dev/null +++ b/examples/naive/riscv/riscv_simple0.s @@ -0,0 +1 @@ +addi x0, x0, 0 \ No newline at end of file diff --git a/slothy/targets/riscv/riscv.py b/slothy/targets/riscv/riscv.py new file mode 100644 index 00000000..90aa8dbc --- /dev/null +++ b/slothy/targets/riscv/riscv.py @@ -0,0 +1,3318 @@ +# +# Copyright (c) 2022 Arm Limited +# Copyright (c) 2022 Hanno Becker +# Copyright (c) 2023 Amin Abdulrahman, Matthias Kannwischer +# SPDX-License-Identifier: MIT +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# Author: Hanno Becker +# + +""" +Partial SLOTHY architecture model for AArch64 + +Various arithmetic and LSU scalar and Neon instructions are included, +but many are still missing. The model is lazily growing with the workloads +that SLOTHY is being used for. + +Adding new instructions is simple thanks to the generic AArch64Instruction +class which generates instruction parsers and writers from instruction templates +similar to those used in the Arm ARM. +""" + +import logging +import inspect +import re +import math +from enum import Enum +from functools import cache + +from sympy import simplify + +llvm_mca_arch = "aarch64" + +class RegisterType(Enum): + GPR = 1 + NEON = 2 + STACK_NEON = 3 + STACK_GPR = 4 + FLAGS = 5 + HINT = 6 + + def __str__(self): + return self.name + def __repr__(self): + return self.name + + @cache + @staticmethod + def spillable(reg_type): + return reg_type in [RegisterType.GPR, RegisterType.NEON] + + @cache + @staticmethod + def list_registers(reg_type, only_extra=False, only_normal=False, with_variants=False): + """Return the list of all registers of a given type""" + + qstack_locations = [ f"QSTACK{i}" for i in range(8) ] + stack_locations = [ f"STACK{i}" for i in range(8) ] + + gprs_normal = [ f"x{i}" for i in range(31) ] + ["sp"] + vregs_normal = [ f"v{i}" for i in range(32) ] + + gprs_extra = [] + vregs_extra = [] + + gprs_variants = [ f"w{i}" for i in range(31) ] + vregs_variants = [ f"q{i}" for i in range(32) ] + + gprs = [] + vregs = [] + hints = [ f"t{i}" for i in range(100) ] + \ + [ f"t{i}{j}" for i in range(8) for j in range(8) ] + \ + [ f"t{i}_{j}" for i in range(16) for j in range(16) ] + + flags = ["flags"] + if not only_extra: + gprs += gprs_normal + vregs += vregs_normal + if not only_normal: + gprs += gprs_extra + vregs += vregs_extra + if with_variants: + gprs += gprs_variants + vregs += vregs_variants + + return { RegisterType.GPR : gprs, + RegisterType.STACK_GPR : stack_locations, + RegisterType.STACK_NEON : qstack_locations, + RegisterType.NEON : vregs, + RegisterType.HINT : hints, + RegisterType.FLAGS : flags}[reg_type] + + @staticmethod + def find_type(r): + """Find type of architectural register""" + + if r.startswith("hint_"): + return RegisterType.HINT + + for ty in RegisterType: + if r in RegisterType.list_registers(ty): + return ty + + return None + + @staticmethod + def is_renamed(ty): + """Indicate if register type should be subject to renaming""" + if ty == RegisterType.HINT: + return False + return True + + @staticmethod + def from_string(string): + """Find registe type from string""" + string = string.lower() + return { "qstack" : RegisterType.STACK_NEON, + "stack" : RegisterType.STACK_GPR, + "neon" : RegisterType.NEON, + "gpr" : RegisterType.GPR, + "hint" : RegisterType.HINT, + "flags" : RegisterType.FLAGS}.get(string,None) + + @staticmethod + def default_reserved(): + """Return the list of registers that should be reserved by default""" + return set(["flags", "sp"] + RegisterType.list_registers(RegisterType.HINT)) + + @staticmethod + def default_aliases(): + "Register aliases used by the architecture" + return {} + +class Branch: + """Helper for emitting branches""" + + @staticmethod + def if_equal(cnt, val, lbl): + """Emit assembly for a branch-if-equal sequence""" + yield f"cmp {cnt}, #{val}" + yield f"b.eq {lbl}" + + @staticmethod + def if_greater_equal(cnt, val, lbl): + """Emit assembly for a branch-if-greater-equal sequence""" + yield f"cmp {cnt}, #{val}" + yield f"b.ge {lbl}" + + @staticmethod + def unconditional(lbl): + """Emit unconditional branch""" + yield f"b {lbl}" + +class Loop: + """Helper functions for parsing and writing simple loops in AArch64 + + TODO: Generalize; current implementation too specific about shape of loop""" + + def __init__(self, lbl_start="1", lbl_end="2", loop_init="lr"): + self.lbl_start = lbl_start + self.lbl_end = lbl_end + self.loop_init = loop_init + + def start(self, loop_cnt, indentation=0, fixup=0, unroll=1, jump_if_empty=None): + """Emit starting instruction(s) and jump label for loop""" + indent = ' ' * indentation + if unroll > 1: + assert unroll in [1,2,4,8,16,32] + yield f"{indent}lsr {loop_cnt}, {loop_cnt}, #{int(math.log2(unroll))}" + if fixup != 0: + yield f"{indent}sub {loop_cnt}, {loop_cnt}, #{fixup}" + if jump_if_empty is not None: + yield f"cbz {loop_cnt}, {jump_if_empty}" + yield f"{self.lbl_start}:" + + def end(self, other, indentation=0): + """Emit compare-and-branch at the end of the loop""" + (reg0, reg1, imm) = other + indent = ' ' * indentation + lbl_start = self.lbl_start + if lbl_start.isdigit(): + lbl_start += "b" + + yield f"{indent}sub {reg0}, {reg1}, {imm}" + yield f"{indent}cbnz {reg0}, {lbl_start}" + + @staticmethod + def extract(source, lbl): + """Locate a loop with start label `lbl` in `source`. + + We currently only support the following loop forms: + + ``` + loop_lbl: + {code} + sub[s] , , #1 + (cbnz|bnz|bne) , loop_lbl + ``` + + """ + assert isinstance(source, list) + + pre = [] + body = [] + post = [] + loop_lbl_regexp_txt = r"^\s*(?P