diff --git a/.github/actions/setup-ubuntu/action.yml b/.github/actions/setup-ubuntu/action.yml new file mode 100644 index 00000000..87f0b43d --- /dev/null +++ b/.github/actions/setup-ubuntu/action.yml @@ -0,0 +1,33 @@ +name: Setup ubuntu +description: Setup ubuntu + +inputs: + packages: + description: Space-separated list of additional packages to install + required: false + default: 'llvm llvm-runtime' + +runs: + using: composite + steps: + - name: Update package repository + shell: bash + run: | + sudo apt-get update + - name: Install base packages + shell: bash + run: | + sudo apt-get install python3-venv python3-pip make -y + - name: Install additional packages + if: ${{ inputs.packages != ''}} + shell: bash + run: | + sudo apt-get install ${{ inputs.packages }} -y + - name: Setup Python venv + shell: bash + run: | + python3 -m venv venv + source venv/bin/activate + python3 -m pip install -r requirements.txt + deactivate + echo "$(pwd)/venv/bin/" >> "$GITHUB_PATH" diff --git a/.github/workflows/test_basic.yaml b/.github/workflows/test_basic.yaml index 398cb039..4a98e09d 100644 --- a/.github/workflows/test_basic.yaml +++ b/.github/workflows/test_basic.yaml @@ -12,16 +12,12 @@ jobs: github.event.pull_request.user.login == 'mkannwischer' }} runs-on: ubuntu-latest - strategy: - matrix: + strategy: + matrix: target: [slothy.targets.arm_v7m.cortex_m7,slothy.targets.arm_v81m.cortex_m55r1, slothy.targets.arm_v81m.cortex_m85r1, slothy.targets.aarch64.cortex_a55, slothy.targets.aarch64.cortex_a72_frontend, slothy.targets.aarch64.apple_m1_firestorm_experimental, slothy.targets.aarch64.apple_m1_icestorm_experimental] steps: - uses: actions/checkout@v3 - - name: Install python dependencies - run: | - python3 -m venv venv - ./venv/bin/python3 -m pip install -r requirements.txt - echo BASH_ENV="./venv/bin/activate" >> $GITHUB_ENV + - uses: ./.github/actions/setup-ubuntu - name: Run examples run: | python3 example.py --dry-run --only-target=${{ matrix.target }} @@ -34,11 +30,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Install python dependencies - run: | - python3 -m venv venv - ./venv/bin/python3 -m pip install -r requirements.txt - echo BASH_ENV="./venv/bin/activate" >> $GITHUB_ENV + - uses: ./.github/actions/setup-ubuntu - name: Run tutorial run: | (cd tutorial && ./tutorial_all.sh) @@ -51,11 +43,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Install python dependencies - run: | - python3 -m venv venv - ./venv/bin/python3 -m pip install -r requirements.txt - echo BASH_ENV="./venv/bin/activate" >> $GITHUB_ENV + - uses: ./.github/actions/setup-ubuntu - name: Run examples run: | python3 example.py --examples simple0,simple1,simple0_loop,simple1_loop @@ -68,11 +56,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Install python dependencies - run: | - python3 -m venv venv - ./venv/bin/python3 -m pip install -r requirements.txt - echo BASH_ENV="./venv/bin/activate" >> $GITHUB_ENV + - uses: ./.github/actions/setup-ubuntu - name: Run examples run: | python3 example.py --examples ntt_kyber_1_23_45_67_m55,ntt_dilithium_12_34_56_78_m55 --timeout=300 @@ -85,11 +69,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Install python dependencies - run: | - python3 -m venv venv - ./venv/bin/python3 -m pip install -r requirements.txt - echo BASH_ENV="./venv/bin/activate" >> $GITHUB_ENV + - uses: ./.github/actions/setup-ubuntu - name: Run examples run: | python3 example.py --examples ntt_kyber_123_4567_a55,ntt_dilithium_123_45678_a55 --timeout=300 @@ -102,11 +82,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Install python dependencies - run: | - python3 -m venv venv - ./venv/bin/python3 -m pip install -r requirements.txt - echo BASH_ENV="./venv/bin/activate" >> $GITHUB_ENV + - uses: ./.github/actions/setup-ubuntu - name: Run examples run: | (cd paper/scripts && NO_LOG=Y ./slothy_sqmag.sh) @@ -119,11 +95,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - - name: Install python dependencies - run: | - python3 -m venv venv - ./venv/bin/python3 -m pip install -r requirements.txt - echo BASH_ENV="./venv/bin/activate" >> $GITHUB_ENV + - uses: ./.github/actions/setup-ubuntu - name: Run examples run: | (cd paper/scripts && NO_LOG=Y ./slothy_fft.sh) diff --git a/requirements.txt b/requirements.txt index b1f405e9..8191eb9b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ numpy==1.26.4 ortools==9.7.2996 pandas==2.1.1 sympy==1.12 +unicorn=2.1.1 diff --git a/slothy/core/config.py b/slothy/core/config.py index 77d39669..1140a2bb 100644 --- a/slothy/core/config.py +++ b/slothy/core/config.py @@ -100,6 +100,52 @@ def reserved_regs_are_locked(self): to eliminate uses of particular registers from some piece of assembly.""" return self._reserved_regs_are_locked + @property + def selftest(self): + """Indicates whether SLOTHY performs an empirical equivalence-test on the + optimization results. + + When this is set, and if the target architecture and host platform support it, + this will run an empirical equivalence checker trying to confirm that the + input and output of SLOTHY are likely functionally equivalent. + + The primary purpose of this checker is to detect issue that would presently + be overlooked by the selfcheck: + - The selfcheck is currently blind to address offset fixup. If something goes + wrong, the input and output will not be functionally equivalent, but we would + only notice once we actually compile and run the code. The selftest will + likely catch issues. + - When using software pipelining, the selfcheck reduces to a straightline check + for a bounded unrolling of the loop. An unbounded selfcheck is currently not + implemented. + With the selftest, you still need to fix a loop bound, but at least you can + equivalence-check the loop-form (including the compare+branch instructions + at the loop boundary) rather than the unrolled code. + + DEPENDENCY: To run this, you need `llvm-mc` the binary in your path or configured + as via `llvm_mc_binary`, and `unicorn-engine` Python bindings setup. + + NOTE: This is so far implemented as a repeated randomized test -- nothing clever. + """ + return self._selftest + + @property + def selftest_iterations(self): + """If selftest is set, indicates the number of random selftest to conduct""" + return self._selftest_iterations + + @property + def selftest_address_gprs(self): + """Dictionary of (reg, sz) items indicating which registers are assumed to be + pointers to memory, and if so, of what size.""" + return self._selftest_address_gprs + + @property + def selftest_default_memory_size(self): + """Default buffer size to use for registers which are automatically inferred to be + used as pointers and for which no memory size has been configured via `address_gprs`.""" + return self._selftest_default_memory_size + @property def selfcheck(self): """Indicates whether SLOTHY performs a self-check on the optimization result. @@ -431,6 +477,13 @@ def llvm_mca_binary(self): is set.""" return self._llvm_mca_binary + @property + def llvm_mc_binary(self): + """The llvm-mc binary to be used for assembling output data + + This is only relevant if `selftest` is set.""" + return self._llvm_mc_binary + @property def timeout(self): """The timeout in seconds after which the underlying constraint solver stops @@ -1143,6 +1196,10 @@ def __init__(self, Arch, Target): self._reserved_regs = None self._reserved_regs_are_locked = True + self._selftest = True + self._selftest_iterations = 10 + self._selftest_address_gprs = None + self._selftest_default_memory_size = 1024 self._selfcheck = True self._selfcheck_failure_logfile = None self._allow_useless_instructions = False @@ -1172,6 +1229,7 @@ def __init__(self, Arch, Target): self._compiler_binary = "gcc" self._compiler_include_paths = None self._llvm_mca_binary = "llvm-mca" + self._llvm_mc_binary = "llvm-mc" self.keep_tags = True self.inherit_macro_comments = False @@ -1260,6 +1318,20 @@ def reserved_regs_are_locked(self,val): @variable_size.setter def variable_size(self,val): self._variable_size = val + @selftest.setter + def selftest(self,val): + if hasattr(self.arch, "Checker") is False: + raise InvalidConfig("Trying to enable checker, but architecture model does not seem to support it") + self._selftest = val + @selftest_iterations.setter + def selftest_iterations(self,val): + self._selftest_iterations = val + @selftest_address_gprs.setter + def selftest_address_gprs(self,val): + self._selftest_address_gprs = val + @selftest_default_memory_size.setter + def selftest_default_memory_size(self,val): + self._selftest_default_memory_size = val @selfcheck.setter def selfcheck(self,val): self._selfcheck = val @@ -1308,6 +1380,9 @@ def compiler_include_paths(self, val): @llvm_mca_binary.setter def llvm_mca_binary(self, val): self._llvm_mca_binary = val + @llvm_mc_binary.setter + def llvm_mc_binary(self, val): + self._llvm_mc_binary = val @timeout.setter def timeout(self, val): self._timeout = val diff --git a/slothy/core/core.py b/slothy/core/core.py index bfbaeab4..8f4bcff1 100644 --- a/slothy/core/core.py +++ b/slothy/core/core.py @@ -27,6 +27,7 @@ import logging import math +import os import time from types import SimpleNamespace @@ -38,7 +39,12 @@ from ortools.sat.python import cp_model from slothy.core.config import Config -from slothy.helper import LockAttributes, Permutation, DeferHandler, SourceLine +from slothy.helper import LockAttributes, Permutation, DeferHandler, SourceLine, LLVM_Mc + +try: + from unicorn import Uc +except ImportError: + Uc = None from slothy.core.dataflow import DataFlowGraph as DFG from slothy.core.dataflow import Config as DFGConfig @@ -818,6 +824,121 @@ def selfcheck(self, log): raise SlothySelfCheckException("Isomorphism between computation flow graphs: FAIL!") return res + def selftest(self, log): + """Run empirical self test, if it exists for the target architecture""" + if self._config.selftest is False: + return + + if Uc is None: + raise SlothySelfTestException("Cannot run selftest -- unicorn-engine is not available.") + + if self._config.arch.unicorn_arch is None or \ + self._config.arch.llvm_mc_arch is None: + log.warning("Selftest not supported on target architecture") + return + + log.info(f"Running selftest ({self._config.selftest_iterations} iterations)...") + + address_gprs = self._config.selftest_address_gprs + if address_gprs is None: + # Try to infer which registes need to be pointers + log_addresses = log.getChild("infer_address_gprs") + tree = DFG(self._orig_code, log_addresses, DFGConfig(self.config, outputs=self.outputs)) + # Look for load/store instructions and remember addresses + addresses = set() + for t in tree.nodes: + addr = getattr(t.inst, "addr", None) + if addr is None: + continue + addresses.add(addr) + + # For now, we don't look into increments and immedate offsets + # to gauge the amount of memory we actually need. Instaed, we + # just allocate a buffer of a configurable default size. + log.info(f"Inferred that the following registers seem to act as pointers: {addresses}") + log.info(f"Using default buffer size of {self._config.selftest_default_memory_size} bytes. " + "If you want different buffer sizes, set selftest_address_gprs manually.") + address_gprs = { a: self._config.selftest_default_memory_size for a in addresses } + + # This produces _unrolled_ code, the same that is checked in the selfcheck. + # The selftest should instead use the rolled form of the loop. + iterations = 7 + if self.config.sw_pipelining.enabled is True: + old_source, new_source = self.get_fully_unrolled_loop(iterations) + else: + old_source, new_source = self.orig_code, self.code + + CODE_BASE = 0x010000 + CODE_SZ = 0x010000 + RAM_BASE = 0x020000 + RAM_SZ = 0x010000 + + regs = [r for ty in self._config.arch.RegisterType for r in \ + self._config.arch.RegisterType.list_registers(ty)] + + def run_code(code, txt=None): + objcode = LLVM_Mc.assemble(code, self._config.llvm_mc_binary, + self._config.arch.llvm_mc_arch, + self._config.arch.llvm_mc_attr, + log) + + # Setup emulator + mu = Uc(self.config.arch.unicorn_arch, self.config.arch.unicorn_mode) + # Copy initial register contents into emulator + for r,v in initial_register_contents.items(): + ur = self._config.arch.RegisterType.unicorn_reg_by_name(r) + if ur is None: + continue + mu.reg_write(ur, v) + # Copy code into emulator + mu.mem_map(CODE_BASE, CODE_SZ) + mu.mem_write(CODE_BASE, objcode) + # Copy initial memory contents into emulator + mu.mem_map(RAM_BASE, RAM_SZ) + mu.mem_write(RAM_BASE, initial_memory) + # Run emulator + mu.emu_start(CODE_BASE, CODE_BASE + len(objcode)) + + final_register_contents = {} + for r in regs: + ur = self._config.arch.RegisterType.unicorn_reg_by_name(r) + if ur is None: + continue + final_register_contents[r] = mu.reg_read(ur) + final_memory_contents = mu.mem_read(RAM_BASE, RAM_SZ) + + return final_register_contents, final_memory_contents + + for _ in range(self._config.selftest_iterations): + initial_memory = os.urandom(RAM_SZ) + cur_ram = RAM_BASE + # Set initial register contents arbitrarily, except for registers + # which must hold valid memory addresses. + initial_register_contents = {} + for r in regs: + initial_register_contents[r] = int.from_bytes(os.urandom(16)) + for (reg, sz) in address_gprs.items(): + initial_register_contents[reg] = cur_ram + cur_ram += sz + + final_regs_old, final_mem_old = run_code(old_source, txt="old") + final_regs_new, final_mem_new = run_code(new_source, txt="new") + + # Check if memory contents are the same + if final_mem_old != final_mem_new: + raise SlothySelfTestException(f"Selftest failed: Memory mismatch") + + # Check if register contents are the same + regs_expected = set(self.config.outputs).union(self.config.reserved_regs) + # Ignore hint registers, flags and sp for now + regs_expected = set(filter(lambda t: t.startswith("t") is False and + t != "sp" and t != "flags", regs_expected)) + for r in regs_expected: + if final_regs_old[r] != final_regs_new[r]: + raise SlothySelfTestException(f"Selftest failed: Register mismatch for {r}: {hex(final_regs_old[r])} != {hex(final_regs_new[r])}") + + log.info("Selftest: OK") + def selfcheck_with_fixup(self, log): """Do selfcheck, and consider preamble/postamble fixup in case of SW pipelining @@ -1315,6 +1436,9 @@ def __init__(self, config): class SlothySelfCheckException(Exception): """Exception thrown upon selfcheck failures""" +class SlothySelfTestException(Exception): + """Exception thrown upon selftest failures""" + class SlothyBase(LockAttributes): """Stateless core of SLOTHY. @@ -1875,6 +1999,8 @@ def _extract_result(self): self._result.selfcheck_with_fixup(self.logger.getChild("selfcheck")) self._result.offset_fixup(self.logger.getChild("fixup")) + self._result.selftest(self.logger.getChild("selftest")) + def _extract_positions(self, get_value): if self.config.variable_size: @@ -2952,7 +3078,7 @@ def _add_constraints_latencies(self): if isinstance(latency, int): self.logger.debug("General latency constraint: [%s] >= [%s] + %d", t, i.src, latency) - # Some microarchitectures have instructions with 0-cycle latency, i.e., the can + # Some microarchitectures have instructions with 0-cycle latency, i.e., the can # forward the result to an instruction in the same cycle (e.g., X+str on Cortex-M7) # If that is the case we need to make sure that the consumer is after the producer # in the output. diff --git a/slothy/helper.py b/slothy/helper.py index 86f118d7..020ea66d 100644 --- a/slothy/helper.py +++ b/slothy/helper.py @@ -32,7 +32,6 @@ from sympy import simplify from slothy.targets.common import * - class SourceLine: """Representation of a single line of source code""" @@ -1079,6 +1078,49 @@ def unfold(header, body, post, gcc, include=None): return [SourceLine(r) for r in unfolded_code] +class LLVM_Mc_Error(Exception): + """Exception thrown if llvm-mc subprocess fails""" + +class LLVM_Mc(): + """Helper class for the application of the LLVM MC tool""" + + @staticmethod + def assemble(source, mc_binary, arch, attr, log): + """Runs LLVM-MC tool to assemble `source`, returning byte code""" + + LLVM_MCA_BEGIN = SourceLine("").add_comment("LLVM-MCA-BEGIN") + LLVM_MCA_END = SourceLine("").add_comment("LLVM-MCA-END") + + # Unfortunately, there is no option to directly extract byte code + # from LLVM-MC: One either gets a textual description, or an object file. + # To not introduce another binary dependency, we just extract the byte + # code directly from the textual output, which for every assembly line + # has a "encoding: [byte0, byte1, ...]" comment at the end. + + code = SourceLine.write_multiline(source) + log.debug(f"Calling LLVM MC assmelber on the following code") + log.debug(code) + args = [f"--arch={arch}", "--assemble", "--show-encoding"] + if attr is not None: + args.append(f"--mattr={attr}") + try: + r = subprocess.run([mc_binary] + args, + input=code, text=True, capture_output=True, check=True) + except subprocess.CalledProcessError as exc: + raise LLVM_Mc_Error from exc + + res = r.stdout.split('\n') + res = filter(lambda s: "encoding:" in s, res) + res = list(map(lambda s: s.split("encoding:")[1].strip(), res)) + + # Every line has the form "[byte, byte, byte,...]" now -- interpret as byte array + # Bit hacky, but nevermind... + def string_as_byte_array(s): + return s.replace("[", "").replace("]", "").split(",") + res = list(map(string_as_byte_array, res)) + res = [int(b, base=16) for l in res for b in l] # Flatten + return bytes(res) + class LLVM_Mca_Error(Exception): """Exception thrown if llvm-mca subprocess fails""" @@ -1265,7 +1307,7 @@ def _extract(self, source, lbl): elif loop_end_ctr > 0 and l_str != "": # Case: The sequence of loop end candidates was interrupted # i.e., we found a false-positive or this is not a proper loop - + # The loop end candidates are not part of the loop, meaning # they belonged to the body body += loop_end_candidates @@ -1295,4 +1337,4 @@ def extract(source, lbl): logging.debug("Parsing loop type '%s'failed", loop_type) pass - raise FatalParsingException(f"Couldn't identify loop {lbl}") \ No newline at end of file + raise FatalParsingException(f"Couldn't identify loop {lbl}") diff --git a/slothy/targets/aarch64/aarch64_neon.py b/slothy/targets/aarch64/aarch64_neon.py index 68fdf3c0..9547e4ac 100644 --- a/slothy/targets/aarch64/aarch64_neon.py +++ b/slothy/targets/aarch64/aarch64_neon.py @@ -40,17 +40,28 @@ class which generates instruction parsers and writers from instruction templates import logging import inspect import re +import os import math +import platform +import subprocess from enum import Enum from functools import cache - from sympy import simplify +from unicorn import * +from unicorn.arm64_const import * + from slothy.targets.common import * -from slothy.helper import Loop +from slothy.helper import Loop, LLVM_Mc arch_name = "Arm_AArch64" + llvm_mca_arch = "aarch64" +llvm_mc_arch = "aarch64" +llvm_mc_attr = None + +unicorn_arch = UC_ARCH_ARM64 +unicorn_mode = UC_MODE_ARM class RegisterType(Enum): GPR = 1 @@ -70,6 +81,79 @@ def __repr__(self): def spillable(reg_type): return reg_type in [RegisterType.GPR, RegisterType.NEON] + @cache + @staticmethod + def unicorn_reg_by_name(reg): + """Converts string name of register into numerical identifiers used + within the unicorn engine""" + + d = { + "x0": UC_ARM64_REG_X0, + "x1": UC_ARM64_REG_X1, + "x2": UC_ARM64_REG_X2, + "x3": UC_ARM64_REG_X3, + "x4": UC_ARM64_REG_X4, + "x5": UC_ARM64_REG_X5, + "x6": UC_ARM64_REG_X6, + "x7": UC_ARM64_REG_X7, + "x8": UC_ARM64_REG_X8, + "x9": UC_ARM64_REG_X9, + "x10": UC_ARM64_REG_X10, + "x11": UC_ARM64_REG_X11, + "x12": UC_ARM64_REG_X12, + "x13": UC_ARM64_REG_X13, + "x14": UC_ARM64_REG_X14, + "x15": UC_ARM64_REG_X15, + "x16": UC_ARM64_REG_X16, + "x17": UC_ARM64_REG_X17, + "x18": UC_ARM64_REG_X18, + "x19": UC_ARM64_REG_X19, + "x20": UC_ARM64_REG_X20, + "x21": UC_ARM64_REG_X21, + "x22": UC_ARM64_REG_X22, + "x23": UC_ARM64_REG_X23, + "x24": UC_ARM64_REG_X24, + "x25": UC_ARM64_REG_X25, + "x26": UC_ARM64_REG_X26, + "x27": UC_ARM64_REG_X27, + "x28": UC_ARM64_REG_X28, + "x29": UC_ARM64_REG_X29, + "x30": UC_ARM64_REG_X30, + "v0": UC_ARM64_REG_V0, + "v1": UC_ARM64_REG_V1, + "v2": UC_ARM64_REG_V2, + "v3": UC_ARM64_REG_V3, + "v4": UC_ARM64_REG_V4, + "v5": UC_ARM64_REG_V5, + "v6": UC_ARM64_REG_V6, + "v7": UC_ARM64_REG_V7, + "v8": UC_ARM64_REG_V8, + "v9": UC_ARM64_REG_V9, + "v10": UC_ARM64_REG_V10, + "v11": UC_ARM64_REG_V11, + "v12": UC_ARM64_REG_V12, + "v13": UC_ARM64_REG_V13, + "v14": UC_ARM64_REG_V14, + "v15": UC_ARM64_REG_V15, + "v16": UC_ARM64_REG_V16, + "v17": UC_ARM64_REG_V17, + "v18": UC_ARM64_REG_V18, + "v19": UC_ARM64_REG_V19, + "v20": UC_ARM64_REG_V20, + "v21": UC_ARM64_REG_V21, + "v22": UC_ARM64_REG_V22, + "v23": UC_ARM64_REG_V23, + "v24": UC_ARM64_REG_V24, + "v25": UC_ARM64_REG_V25, + "v26": UC_ARM64_REG_V26, + "v27": UC_ARM64_REG_V27, + "v28": UC_ARM64_REG_V28, + "v29": UC_ARM64_REG_V29, + "v30": UC_ARM64_REG_V30, + "v31": UC_ARM64_REG_V31, + } + return d.get(reg, None) + @cache @staticmethod def list_registers(reg_type, only_extra=False, only_normal=False, with_variants=False): diff --git a/slothy/targets/arm_v7m/arch_v7m.py b/slothy/targets/arm_v7m/arch_v7m.py index 6da66177..aa9bf276 100644 --- a/slothy/targets/arm_v7m/arch_v7m.py +++ b/slothy/targets/arm_v7m/arch_v7m.py @@ -1,15 +1,23 @@ import logging import inspect +import os import re import math from enum import Enum from functools import cache -from slothy.helper import SourceLine, Loop +from unicorn import * +from unicorn.arm_const import * + +from slothy.helper import SourceLine, Loop, LLVM_Mc from sympy import simplify -llvm_mca_arch = "arm" # TODO +llvm_mca_arch = "arm" +llvm_mc_arch = "arm" +llvm_mc_attr = "armv5te" +unicorn_arch = UC_ARCH_ARM +unicorn_mode = UC_MODE_ARM class RegisterType(Enum): GPR = 1 @@ -27,6 +35,63 @@ def __repr__(self): def spillable(reg_type): return reg_type in [RegisterType.GPR] + @cache + @staticmethod + def unicorn_reg_by_name(reg): + """Converts string name of register into numerical identifiers used + within the unicorn engine""" + + d = { + "r0": UC_ARM_REG_R0, + "r1": UC_ARM_REG_R1, + "r2": UC_ARM_REG_R2, + "r3": UC_ARM_REG_R3, + "r4": UC_ARM_REG_R4, + "r5": UC_ARM_REG_R5, + "r6": UC_ARM_REG_R6, + "r7": UC_ARM_REG_R7, + "r8": UC_ARM_REG_R8, + "r9": UC_ARM_REG_R9, + "r10": UC_ARM_REG_R10, + "r11": UC_ARM_REG_R11, + "r12": UC_ARM_REG_R12, + "r13": UC_ARM_REG_SP, + "r14": UC_ARM_REG_LR, + "s0": UC_ARM_REG_S0, + "s1": UC_ARM_REG_S1, + "s2": UC_ARM_REG_S2, + "s3": UC_ARM_REG_S3, + "s4": UC_ARM_REG_S4, + "s5": UC_ARM_REG_S5, + "s6": UC_ARM_REG_S6, + "s7": UC_ARM_REG_S7, + "s8": UC_ARM_REG_S8, + "s9": UC_ARM_REG_S9, + "s10": UC_ARM_REG_S10, + "s11": UC_ARM_REG_S11, + "s12": UC_ARM_REG_S12, + "s13": UC_ARM_REG_S13, + "s14": UC_ARM_REG_S14, + "s15": UC_ARM_REG_S15, + "s16": UC_ARM_REG_S16, + "s17": UC_ARM_REG_S17, + "s18": UC_ARM_REG_S18, + "s19": UC_ARM_REG_S19, + "s20": UC_ARM_REG_S20, + "s21": UC_ARM_REG_S21, + "s22": UC_ARM_REG_S22, + "s23": UC_ARM_REG_S23, + "s24": UC_ARM_REG_S24, + "s25": UC_ARM_REG_S25, + "s26": UC_ARM_REG_S26, + "s27": UC_ARM_REG_S27, + "s28": UC_ARM_REG_S28, + "s29": UC_ARM_REG_S29, + "s30": UC_ARM_REG_S30, + "s31": UC_ARM_REG_S31, + } + return d.get(reg, None) + @cache @staticmethod def list_registers(reg_type, only_extra=False, only_normal=False, with_variants=False): @@ -125,12 +190,12 @@ def unconditional(lbl): class VmovCmpLoop(Loop): """ Loop ending in a vmov, a compare, and a branch. - + The modification to the value we compare against happens inside the loop body. The value that is being compared to is stashed to a floating point register before the loop starts and therefore needs to be recovered before - the comparison. - + the comparison. + WARNING: This type of loop is experimental as slothy has no knowledge about what happens inside the loop boundary! Especially, a register is written inside the boundary which may be used for renaming by slothy. Use with @@ -218,7 +283,7 @@ class CmpLoop(Loop): """ Loop ending in a compare and a branch. The modification to the value we compare against happens inside the loop body. - WARNING: This type of loop is experimental as slothy has no knowledge about + WARNING: This type of loop is experimental as slothy has no knowledge about what happens inside the loop boundary! Use with caution. Example: diff --git a/slothy/targets/arm_v81m/arch_v81m.py b/slothy/targets/arm_v81m/arch_v81m.py index f14e47c8..f2c64778 100644 --- a/slothy/targets/arm_v81m/arch_v81m.py +++ b/slothy/targets/arm_v81m/arch_v81m.py @@ -46,6 +46,11 @@ arch_name = "Arm_v81M" llvm_mca_arch = "arm" +llvm_mc_arch = None +llvm_mc_attr = None +unicorn_arch = None +unicorn_mode = None + class RegisterType(Enum): GPR = 1, MVE = 2, @@ -115,14 +120,14 @@ def default_reserved(): class LeLoop(Loop): """ Loop ending in a le instruction. - + Example: ``` loop_lbl: {code} le , loop_lbl ``` - + where cnt is the loop counter in lr. """ def __init__(self, lbl_start="1", lbl_end="2"):