Compare commits

..

No commits in common. "ee771e3ac169be1e47d4fe9d61846f9e0f459e39" and "8d77ee3a25c910ccabcbae58a92a3d60d349d2d2" have entirely different histories.

38 changed files with 244 additions and 1165 deletions

View File

@ -104,7 +104,7 @@ jobs:
- name: Compile coparun
run: |
mkdir -p build/runner
gcc -O3 -static -DENABLE_BASIC_LOGGING -o build/runner/coparun src/coparun/runmem.c src/coparun/coparun.c src/coparun/mem_man.c
gcc -O3 -DENABLE_BASIC_LOGGING -o build/runner/coparun src/coparun/runmem.c src/coparun/coparun.c src/coparun/mem_man.c
- name: Generate debug asm files
if: strategy.job-index == 0
@ -151,9 +151,9 @@ jobs:
- name: Use ARM64 container
run: |
docker run --rm -v $PWD:/app -w /app --platform linux/arm64 ghcr.io/nonannet/arm64_test:1 \
bash -lc "pip install .[mindev] && \
bash -lc "pip install . && \
mkdir -p build/runner && \
gcc -O3 -static -DENABLE_LOGGING -o build/runner/coparun src/coparun/runmem.c \
gcc -O3 -DENABLE_LOGGING -o build/runner/coparun src/coparun/runmem.c \
src/coparun/coparun.c src/coparun/mem_man.c && \
pytest && \
bash tools/create_asm.sh"
@ -180,10 +180,9 @@ jobs:
- name: Use ARMv6 container
run: |
docker run --rm -v $PWD:/app -w /app --platform linux/arm/v6 ghcr.io/nonannet/armv6_test:1 \
bash -lc "set -x && \
pip install .[mindev] && \
bash -lc "pip install . && \
mkdir -p build/runner && \
gcc -O3 -static -DENABLE_LOGGING -o build/runner/coparun src/coparun/runmem.c \
gcc -O3 -DENABLE_LOGGING -o build/runner/coparun src/coparun/runmem.c \
src/coparun/coparun.c src/coparun/mem_man.c && \
pytest && \
bash tools/create_asm.sh"
@ -210,14 +209,9 @@ jobs:
- name: Use ARMv7 container
run: |
docker run --rm -v $PWD:/app -w /app --platform linux/arm/v7 ghcr.io/nonannet/armv7_test:1 \
bash -lc "set -x && \
pip install .[mindev] && \
bash -lc "pip install . && \
mkdir -p build/runner && \
gcc -march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -marm -static \
-Wall -Wextra -Wconversion -Wsign-conversion \
-Wshadow -Wstrict-overflow -O3 \
-DENABLE_LOGGING \
-o build/runner/coparun src/coparun/runmem.c \
gcc -O3 -DENABLE_LOGGING -o build/runner/coparun src/coparun/runmem.c \
src/coparun/coparun.c src/coparun/mem_man.c && \
pytest && \
bash tools/create_asm.sh"
@ -227,76 +221,6 @@ jobs:
name: runner-linux-armv7
path: build/runner/*
build-armv7thumb:
needs: [build_stencils]
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: stencil-object-files
path: src/copapy/obj
- name: Set up QEMU for ARMv7
uses: docker/setup-qemu-action@v3
with:
platforms: linux/arm/v7
- name: Use ARMv7 container
run: |
docker run --rm -v $PWD:/app -w /app --platform linux/arm/v7 ghcr.io/nonannet/armv7_test:1 \
bash -lc "set -x && \
pip install .[mindev] && \
mkdir -p build/runner && \
gcc -march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -marm -static \
-Wall -Wextra -Wconversion -Wsign-conversion \
-Wshadow -Wstrict-overflow -O3 \
-DENABLE_LOGGING \
-o build/runner/coparun src/coparun/runmem.c \
src/coparun/coparun.c src/coparun/mem_man.c && \
export CP_TARGET_ARCH=armv7thumb && \
pytest && \
bash tools/create_asm.sh"
- uses: actions/upload-artifact@v4
with:
name: runner-linux-armv7thumb
path: build/runner/*
build-armv7mthumb:
needs: [build_stencils]
runs-on: ubuntu-latest
continue-on-error: true
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: stencil-object-files
path: src/copapy/obj
- name: Set up QEMU for ARMv7
uses: docker/setup-qemu-action@v3
with:
platforms: linux/arm/v7
- name: Use ARMv7 container
run: |
docker run --rm -v $PWD:/app -w /app --platform linux/arm/v7 ghcr.io/nonannet/armv7_test:1 \
bash -lc "set -x && \
pip install .[mindev] && \
mkdir -p build/runner && \
gcc -march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -marm -static \
-Wall -Wextra -Wconversion -Wsign-conversion \
-Wshadow -Wstrict-overflow -O3 \
-DENABLE_LOGGING \
-o build/runner/coparun src/coparun/runmem.c \
src/coparun/coparun.c src/coparun/mem_man.c && \
export CP_TARGET_ARCH=armv7mthumb && \
pytest && \
bash tools/create_asm.sh"
- uses: actions/upload-artifact@v4
with:
name: runner-linux-armv7mthumb
path: build/runner/*
build-windows:
needs: [build_stencils]
runs-on: windows-latest
@ -320,7 +244,7 @@ jobs:
python-version: ${{ matrix.python-version }}
- name: Install Python dependencies
run: python -m pip install .[mindev]
run: python -m pip install .[dev]
- name: Set up MSVC environment
uses: microsoft/setup-msbuild@v2
@ -346,7 +270,7 @@ jobs:
path: build/runner/*
release-stencils:
needs: [build_stencils, build-ubuntu, build-windows, build-arm64, build-armv6, build-armv7, build-armv7thumb, build-armv7mthumb]
needs: [build_stencils, build-ubuntu, build-windows, build-arm64, build-armv6, build-armv7]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
permissions:
@ -395,7 +319,7 @@ jobs:
fi
build-docs:
needs: [build_stencils, build-ubuntu, build-windows, build-arm64, build-armv6, build-armv7, build-armv7thumb, build-armv7mthumb]
needs: [build_stencils, build-ubuntu, build-windows, build-arm64, build-armv6, build-armv7]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

1
.gitignore vendored
View File

@ -30,4 +30,3 @@ core
*.log
docs/source/start.md
/src/copapy/_version.py
sketch*.py

View File

@ -13,7 +13,7 @@ The main features can be summarized as:
- Memory and type safety with a minimal set of runtime errors
- Deterministic execution
- Automatic differentiation for efficient realtime optimization (reverse-mode)
- Optimized machine code for x86_64, 32 Bit ARM (Cortex-A and Cortex-M) and AArch64
- Optimized machine code for x86_64, ARMv6, ARMv7 and AArch64
- Highly portable to new architectures
- Small Python package with minimal dependencies and no cross-compile toolchain required
@ -31,6 +31,7 @@ While hardware I/O is obviously a core aspect of the project, it is not yet avai
Currently in development:
- Array stencils for handling very large arrays and generating SIMD-optimized code - e.g., for machine vision and neural network applications
- Support for Thumb instructions required by ARM*-M targets (for MCUs)
- Constant regrouping for further symbolic optimization of the computation graph
Despite missing SIMD-optimization, benchmark performance shows promising numbers. The following chart plots the results in comparison to NumPy 2.3.5:
@ -252,4 +253,4 @@ This project is licensed under the MIT license - see the [LICENSE](LICENSE) file
[^2]: The compiler must support tail-call optimization (TCO). Currently, GCC is supported. Porting to a new architecture requires implementing a subset of relocation types used by that architecture.
[^3]: Supported architectures: x86_64, AArch64, ARMv6/7 (non-Thumb) and ARMv7 Thumb for Cortex-A and Cortex-M. Code for x86 32-bit exists but has unresolved issues and a low priority.
[^3]: Supported architectures: x86_64, AArch64, ARMv6 and 7 (non-Thumb). ARMv6/7-M (Thumb) support is in development. Code for x86 32-bit exists but has unresolved issues and a low priority.

View File

@ -2,7 +2,7 @@
name = "copapy"
dynamic = ["version"]
authors = [
{ name="Nicolas Kruse", email="nicolas.kruse@nonan.net" },
{ name="Nicolas Kruse", email="nicolas.kruse@nonan.net" },
]
description = "Copy-Patch Compiler"
readme = "README.md"
@ -45,18 +45,14 @@ dev = [
"ruff",
"mypy",
"pytest",
"pelfy>=1.0.8"
]
mindev = [
"pytest",
"pelfy>=1.0.8"
"pelfy>=1.0.7"
]
doc_build = [
"sphinx",
"pydata_sphinx_theme",
"sphinx-autodoc-typehints",
"myst-parser",
"pelfy>=1.0.8"
"sphinx",
"pydata_sphinx_theme",
"sphinx-autodoc-typehints",
"myst-parser",
"pelfy>=1.0.7"
]
[tool.mypy]

View File

@ -429,8 +429,8 @@ class Op(Node):
def __hash__(self) -> int:
return self.node_hash
# Interface for vector and tensor types
class ArrayType(Generic[TNum]):
"""Interface for vector and tensor types."""
def __init__(self, shape: tuple[int, ...]) -> None:
self.shape = shape
self.values: tuple[TNum | value[TNum], ...] = ()

View File

@ -6,14 +6,11 @@ ByteOrder = Literal['little', 'big']
Command = Enum('Command', [('ALLOCATE_DATA', 1), ('COPY_DATA', 2),
('ALLOCATE_CODE', 3), ('COPY_CODE', 4),
('PATCH_FUNC', 0x1000),
('PATCH_FUNC_ARM32_THM', 0x1005),
('PATCH_OBJECT', 0x2000),
('PATCH_FUNC', 0x1000), ('PATCH_OBJECT', 0x2000),
('PATCH_OBJECT_HI21', 0x2001),
('PATCH_OBJECT_ABS', 0x2002),
('PATCH_OBJECT_REL', 0x2003),
('PATCH_OBJECT_ARM32_ABS', 0x2004),
('PATCH_OBJECT_ARM32_ABS_THM', 0x2006),
('ENTRY_POINT', 7),
('RUN_PROG', 64), ('READ_DATA', 65),
('END_COM', 256), ('FREE_MEMORY', 257), ('DUMP_CODE', 258)])
@ -25,11 +22,6 @@ class data_writer():
self._data: list[tuple[str, bytes, int]] = []
self.byteorder: ByteOrder = byteorder
def copy(self) -> 'data_writer':
cp = data_writer(self.byteorder)
cp._data = self._data.copy()
return cp
def write_int(self, value: int, num_bytes: int = 4, signed: bool = False) -> None:
self._data.append((f"INT {value}", value.to_bytes(length=num_bytes, byteorder=self.byteorder, signed=signed), 0))

View File

@ -7,7 +7,6 @@ from ._basic_types import Net, Node, Store, CPConstant, Op, transl_type
def stable_toposort(edges: Iterable[tuple[Node, Node]]) -> list[Node]:
"""Perform a stable topological sort on a directed acyclic graph (DAG).
Arguments:
edges: Iterable of (u, v) pairs meaning u -> v
@ -134,7 +133,7 @@ def get_const_nets(nodes: list[Node]) -> list[Net]:
def add_load_ops(node_list: list[Node]) -> Generator[tuple[Net | None, Node], None, None]:
"""Add load/read node before each op where arguments are not already positioned
"""Add read node before each op where arguments are not already positioned
correctly in the registers
Arguments:
@ -172,7 +171,7 @@ def add_load_ops(node_list: list[Node]) -> Generator[tuple[Net | None, Node], No
def add_store_ops(net_node_list: list[tuple[Net | None, Node]], const_nets: list[Net]) -> Generator[tuple[Net | None, Node], None, None]:
"""Add store/write operation for each new defined net if a read operation is later followed
"""Add write operation for each new defined net if a read operation is later followed
Returns:
Yields tuples of a net and a node. The associated net is provided for read and write nodes.
@ -393,7 +392,6 @@ def compile_to_dag(node_list: Iterable[Node], sdb: stencil_database) -> tuple[bi
# assemble stencils to main program and patch stencils
data = sdb.get_function_code('entry_function_shell', 'start')
data_list.append(data)
#print(f"* entry_function_shell (0) " + ' '.join(f'{d:02X}' for d in data))
offset = aux_func_len + len(data)
for associated_net, node in extended_output_ops:
@ -452,8 +450,10 @@ def compile_to_dag(node_list: Iterable[Node], sdb: stencil_database) -> tuple[bi
#print('--> ', name, list(sdb.get_relocations(name)))
for reloc in sdb.get_relocations(name):
#assert reloc.target_symbol_info != 'STT_FUNC', "Not tested yet!"
if not reloc.target_section_index:
assert reloc.pelfy_reloc.type == 'R_ARM_V4BX', (reloc.pelfy_reloc.type, name, reloc.pelfy_reloc.symbol.name)
assert reloc.pelfy_reloc.type == 'R_ARM_V4BX'
elif reloc.target_symbol_info in {'STT_OBJECT', 'STT_NOTYPE', 'STT_SECTION'}:
# Patch constants/variable addresses on heap
@ -488,6 +488,6 @@ def compile_to_dag(node_list: Iterable[Node], sdb: stencil_database) -> tuple[bi
dw.write_int(patch.value, signed=True)
dw.write_com(binw.Command.ENTRY_POINT)
dw.write_int(aux_func_len + sdb.thumb_mode)
dw.write_int(aux_func_len)
return dw, variables

View File

@ -310,7 +310,7 @@ def get_42(x: value[Any]) -> value[float]: ...
def get_42(x: NumLike) -> value[float] | float:
"""Returns the value representing the constant 42"""
if isinstance(x, value):
return add_op('get_42', [x])
return add_op('get_42', [x, x])
return float((int(x) * 3.0 + 42.0) * 5.0 + 21.0)

View File

@ -2,7 +2,6 @@ from dataclasses import dataclass
from typing import Generator, Literal, Iterable, TYPE_CHECKING
import struct
import platform
import os
if TYPE_CHECKING:
import pelfy
@ -33,14 +32,11 @@ class relocation_entry:
@dataclass
class patch_entry:
"""
A dataclass for representing a patch entry
A dataclass for representing a relocation entry
Attributes:
mask (int): Bit-mask to apply to the patched value
address (int): Address where to patch
value (int): The value to write at the patch address
scale (int): The scale factor for the patch value
patch_type (int): The type of patch
addr (int): address of first byte to patch relative to the start of the symbol
type (RelocationType): relocation type
"""
mask: int
address: int
@ -50,14 +46,6 @@ class patch_entry:
def detect_process_arch() -> str:
"""For running the code locally in the python module
the architecture of the current process is detected
by this function to load the correct stencil database.
"""
cp_target_arch = os.environ.get("CP_TARGET_ARCH")
if cp_target_arch:
return cp_target_arch
bits = struct.calcsize("P") * 8
arch = platform.machine().lower()
@ -92,20 +80,11 @@ def get_return_function_type(symbol: pelfy.elf_symbol) -> str:
def get_stencil_position(func: pelfy.elf_symbol) -> tuple[int, int]:
start_index = 0 # There must be no prolog
# Find last relocation in function
last_instr = get_last_call_in_function(func)
assert func.section, f"No code section specified for symbol {func.name}"
# func.section.fields['sh_size'] is equivalent to func.fields['st_size']
# expect for ARM thumb, here nop padding at the end for 4-byte alignment
# is not included in st_size
function_size = func.section.fields['sh_size']
# Check if jump is the last instruction and can be striped
if last_instr + 5 >= function_size:
end_index = last_instr
function_size = func.fields['st_size']
if last_instr + 5 >= function_size: # Check if jump is last instruction
end_index = last_instr # Jump can be striped
else:
end_index = function_size
@ -119,12 +98,11 @@ def get_last_call_in_function(func: pelfy.elf_symbol) -> int:
if reloc.symbol.name.startswith('dummy_'):
return -0xFFFF # Last relocation is not a jump
else:
# Assume the jump/call instruction is 4 bytes long for relocations
# with less than 32 bit and 5 bytes otherwise
# Assume the call instruction is 4 bytes long for relocations with less than 32 bit and 5 bytes otherwise
instruction_lengths = 4 if reloc.bits < 32 else 5
address_field_length = 4
#print(f"-> {[r.fields['r_offset'] - func.fields['st_value'] for r in func.relocations]}")
return reloc.fields['r_offset'] - func.offset_in_section + address_field_length - instruction_lengths
return reloc.fields['r_offset'] - func.fields['st_value'] + address_field_length - instruction_lengths
def get_op_after_last_call_in_function(func: pelfy.elf_symbol) -> int:
@ -132,12 +110,7 @@ def get_op_after_last_call_in_function(func: pelfy.elf_symbol) -> int:
assert func.relocations, f'No call function in stencil function {func.name}.'
reloc = func.relocations[-1]
assert reloc.bits <= 32, "Relocation segment might be larger then 32 bit"
return reloc.fields['r_offset'] - func.offset_in_section + 4
def add_sign_int32(value: int) -> int:
"""Convert a 32-bit unsigned integer to a signed integer."""
return value - 0x100000000 if value > 0x7FFFFFFF else value
return reloc.fields['r_offset'] - func.fields['st_value'] + 4
class stencil_database():
@ -148,7 +121,6 @@ class stencil_database():
var_size (dict[str, int]): dictionary of object names and their sizes
byteorder (ByteOrder): byte order of the ELF file
elf (elf_file): the loaded ELF file
thumb_mode (bool): entry_function_shell in ARM thumb mode
"""
def __init__(self, obj_file: str | bytes):
@ -175,8 +147,6 @@ class stencil_database():
# if s.info == 'STT_OBJECT'}
self.byteorder: ByteOrder = self.elf.byteorder
self.thumb_mode = self.elf.symbols['entry_function_shell'].thumb_mode
#for name in self.function_definitions.keys():
# sym = self.elf.symbols[name]
# sym.relocations
@ -218,20 +188,19 @@ class stencil_database():
for reloc in symbol.relocations:
# address to fist byte to patch relative to the start of the symbol
patch_offset = reloc.fields['r_offset'] - symbol.offset_in_section - start_index
patch_offset = reloc.fields['r_offset'] - symbol.fields['st_value'] - start_index
if patch_offset < end_index - start_index: # Exclude the call to the result_* function
reloc_entry = relocation_entry(reloc.symbol.name,
reloc.symbol.info,
reloc.symbol.fields['st_value'], # LSB on ARM indicates thumb mode
reloc.symbol.fields['st_value'],
reloc.symbol.fields['st_shndx'],
symbol.offset_in_section,
symbol.fields['st_value'],
start_index,
reloc)
cache.append(reloc_entry)
yield reloc_entry
def get_patch(self, relocation: relocation_entry, symbol_address: int, function_offset: int, symbol_type: int) -> patch_entry:
"""Return patch positions for a provided symbol (function or object)
@ -257,14 +226,12 @@ class stencil_database():
if pr.type.endswith('64_PC32') or pr.type.endswith('64_PLT32'):
# S + A - P
addend = add_sign_int32(pr.fields['r_addend'])
patch_value = symbol_address + addend - patch_offset
patch_value = symbol_address + pr.fields['r_addend'] - patch_offset
#print(f" *> {pr.type} {patch_value=} {symbol_address=} {pr.fields['r_addend']=} {pr.bits=}, {function_offset=} {patch_offset=}")
elif pr.type == 'R_386_PC32':
# S + A - P
addend = add_sign_int32(pr.fields['r_addend'])
patch_value = symbol_address + addend - patch_offset
patch_value = symbol_address + pr.fields['r_addend'] - patch_offset
#print(f" *> {pr.type} {pr.symbol.name} {patch_value=} {symbol_address=} {pr.fields['r_addend']=} {bin(pr.fields['r_addend'])} {pr.bits=}, {function_offset=} {patch_offset=}")
elif pr.type == 'R_386_32':
@ -325,52 +292,28 @@ class stencil_database():
scale = 8
#print(f" *> {patch_value=} {symbol_address=} {pr.fields['r_addend']=}, {function_offset=}")
elif pr.type == 'R_ARM_MOVW_ABS_NC':
elif pr.type.endswith('_MOVW_ABS_NC'):
# R_ARM_MOVW_ABS_NC
# (S + A) & 0xFFFF
mask = 0xFFFF
patch_value = symbol_address + pr.fields['r_addend']
symbol_type = symbol_type + 0x04 # Absolut value
#print(f" *> {pr.type} {patch_value=} {symbol_address=}, {function_offset=}")
elif pr.type =='R_ARM_MOVT_ABS':
elif pr.type.endswith('_MOVT_ABS'):
# R_ARM_MOVT_ABS
# (S + A) & 0xFFFF0000
mask = 0xFFFF0000
patch_value = symbol_address + pr.fields['r_addend']
symbol_type = symbol_type + 0x04 # Absolut value
scale = 0x10000
#print(f" *> {pr.type} {patch_value=} {symbol_address=}, {function_offset=}, {pr.fields['r_addend']=}")
elif pr.type.endswith('_ABS32'):
# R_ARM_ABS32
# S + A (replaces full 32 bit)
assert not patch_offset % 4, 'R_ARM_ABS32 patched data like literals needs to be 4 Byte aligned'
# This might be caused by the call in entry_function_shell if not aligned
patch_value = symbol_address + pr.fields['r_addend']
symbol_type = symbol_type + 0x03 # Relative to data section
elif pr.type.endswith('_THM_JUMP24') or pr.type.endswith('_THM_CALL'):
# R_ARM_THM_JUMP24
# S + A - P
patch_value = symbol_address - patch_offset + pr.fields['r_addend']
symbol_type = symbol_type + 0x05 # PATCH_FUNC_ARM32_THM
#print(f" *> {pr.type} {patch_value=} {symbol_address=} {pr.fields['r_addend']=} {pr.bits=}, {function_offset=} {patch_offset=}")
elif pr.type == 'R_ARM_THM_MOVW_ABS_NC':
# (S + A) & 0xFFFF
mask = 0xFFFF
patch_value = symbol_address + pr.fields['r_addend']
symbol_type = symbol_type + 0x06 # PATCH_OBJECT_ARM32_ABS_THM
#print(f" *> {pr.type} {patch_value=} {symbol_address=}, {function_offset=}, {pr.fields['r_addend']=}")
elif pr.type == 'R_ARM_THM_MOVT_ABS':
# (S + A) & 0xFFFF0000
mask = 0xFFFF0000
patch_value = symbol_address + pr.fields['r_addend']
symbol_type = symbol_type + 0x06 # PATCH_OBJECT_ARM32_ABS_THM
scale = 0x10000
#print(f" *> {pr.type} {patch_value=} {symbol_address=}, {function_offset=}, {pr.fields['r_addend']=}")
else:
raise NotImplementedError(f"Relocation type {pr.type} in {relocation.pelfy_reloc.target_section.name} pointing to {relocation.pelfy_reloc.symbol.name} not implemented")
@ -391,8 +334,7 @@ class stencil_database():
func = self.elf.symbols[name]
start_stencil, end_stencil = get_stencil_position(func)
assert func.section
start_index = func.offset_in_file + start_stencil
start_index = func.section['sh_offset'] + func['st_value'] + start_stencil
lengths = end_stencil - start_stencil
self._stencil_cache[name] = (start_index, lengths)
@ -430,7 +372,7 @@ class stencil_database():
def get_symbol_offset(self, name: str) -> int:
"""Returns the offset of a specified symbol in the section."""
return self.elf.symbols[name].offset_in_section
return self.elf.symbols[name].fields['st_value']
def get_symbol_section_index(self, name: str) -> int:
"""Returns the section index for a specified symbol name."""

View File

@ -13,15 +13,7 @@ TRet = TypeVar("TRet", Iterable[int | float], int, float)
_jit_cache: dict[Any, tuple['Target', tuple[value[Any] | Iterable[value[Any]], ...], NumLike | Iterable[NumLike]]] = {}
def add_read_value_remote(dw: binw.data_writer, variables: dict[Net, tuple[int, int, str]], net: Net) -> None:
"""Adds a read memory to stdout command to the data_writer dw.
Arguments:
dw: data_writer to add the command to
variables: A dict for looking up variables by Net. The value is a tuple.
of relative address in memory, size in bytes and data type.
net: Variable specified by Net to read from memory and write to stdout.
"""
def add_read_command(dw: binw.data_writer, variables: dict[Net, tuple[int, int, str]], net: Net) -> None:
assert net in variables, f"Variable {net} not found in data writer variables"
addr, lengths, _ = variables[net]
dw.write_com(binw.Command.READ_DATA)
@ -196,5 +188,5 @@ class Target():
def read_value_remote(self, variable: value[Any]) -> None:
"""Reads the raw data of a value by the runner."""
dw = binw.data_writer(self.sdb.byteorder)
add_read_value_remote(dw, self._values, variable.net)
add_read_command(dw, self._values, variable.net)
assert coparun(self._context, dw.get_data()) > 0

View File

@ -3,14 +3,14 @@ Backend module for Copapy: contains internal data types
and give access to compiler internals and debugging tools.
"""
from ._target import add_read_value_remote
from ._target import add_read_command
from ._basic_types import Net, Op, Node, CPConstant, Store, stencil_db_from_package
from ._compiler import compile_to_dag, \
stable_toposort, get_const_nets, get_all_dag_edges, add_load_ops, get_all_dag_edges_between, \
add_store_ops, get_dag_stats
__all__ = [
"add_read_value_remote",
"add_read_command",
"Net",
"Op",
"Node",

View File

@ -1,15 +1,3 @@
/*
* file: coparun.c
* Description: This file alows to run copapy programs in the command line
* reading copapy data, code and patch instructions from a file and jump to the
* entry point to execute it or dump the patched code memory for debugging to a file.
*
* It's intended for testing and debugging purposes, to run copapy programs in a
* debugger or emulator like qemu.
*
* Usage: coparun <code_file> [memory_dump_file]
*/
#include <stdio.h>
#include <stdlib.h>
#include "runmem.h"

View File

@ -1,10 +1,3 @@
/*
* file: coparun_module.c
* Description: This file defines a Python C extension module that provides an
* interface to the core functions of the coparun runner, to run copapy programs
* directly local in Python.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "runmem.h"

View File

@ -1,11 +1,3 @@
/*
* file: mem_man.c
* Description: This file contains memory management functions for the coparun
* runner, including allocation and deallocation of executable and data memory.
* Depending of the target operating system or bare metal environment, it
* handles memory management accordingly.
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

View File

@ -1,10 +1,3 @@
/*
* file: runmem.c
* Description: This file contain the core functions of the runner
* to receive data, code and patch instruction, does the patching
* and jumps to the entry point of the copapy program
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -57,67 +50,6 @@ void patch_arm32_abs(uint8_t *patch_addr, uint32_t imm16)
*((uint32_t *)patch_addr) = instr;
}
void patch_arm_thm_abs(uint8_t *patch_addr, uint32_t imm16)
{
// Thumb MOVW (T3) / MOVT (T1) encoding
uint16_t *instr16 = (uint16_t *)patch_addr;
uint16_t first_half = instr16[0];
uint16_t second_half = instr16[1];
// Extract fields from imm16
uint32_t imm4 = (imm16 >> 12) & 0xF;
uint32_t i = (imm16 >> 11) & 0x1;
uint32_t imm3 = (imm16 >> 8) & 0x7;
uint32_t imm8 = imm16 & 0xFF;
// Clear bits
first_half &= (uint16_t)(~(0x000F | (1 << 10)));
second_half &= (uint16_t)(~(0x00FF | (0x7 << 12)));
// Set new fields
first_half |= (uint16_t)((imm4 << 0) | (i << 10));
second_half |= (uint16_t)(imm8 | (imm3 << 12));
instr16[0] = first_half;
instr16[1] = second_half;
}
void patch_arm_thm_jump24(uint8_t *patch_addr, int32_t imm24)
{
// Read the 32-bit instruction (two halfwords)
uint16_t *instr16 = (uint16_t *)patch_addr;
uint16_t first_half = instr16[0];
uint16_t second_half = instr16[1];
// Thumb branch instructions always have LSB = 0 (halfword aligned)
// The imm24 offset in Thumb is shifted right by 1 when encoded
int32_t offset = imm24 >> 1;
// Split into S, J1, J2, imm10, imm11
uint32_t S = (offset >> 23) & 0x1;
uint32_t I1 = (offset >> 22) & 0x1;
uint32_t I2 = (offset >> 21) & 0x1;
uint32_t imm10 = (offset >> 11) & 0x3FF;
uint32_t imm11 = offset & 0x7FF;
// Re-encode J1 and J2
uint32_t J1 = (~(I1 ^ S)) & 0x1;
uint32_t J2 = (~(I2 ^ S)) & 0x1;
// Clear old imm fields
first_half &= 0xF800; // Keep upper 5 bits
second_half &= 0xD000; // Keep upper 5 bits
// Set new imm fields
first_half |= (uint16_t)((S << 10) | imm10);
second_half |= (uint16_t)((J1 << 13) | (J2 << 11) | imm11);
// Write back
instr16[0] = first_half;
instr16[1] = second_half;
}
void free_memory(runmem_t *context) {
deallocate_memory(context->executable_memory, context->executable_memory_len);
deallocate_memory(context->data_memory, context->data_memory_len);
@ -248,26 +180,6 @@ int parse_commands(runmem_t *context, uint8_t *bytes) {
patch_arm32_abs(context->executable_memory + offs, (uint32_t)((uintptr_t)(context->data_memory + value) & patch_mask) / (uint32_t)patch_scale);
break;
case PATCH_FUNC_ARM32_THM:
offs = *(uint32_t*)bytes; bytes += 4;
patch_mask = *(uint32_t*)bytes; bytes += 4;
patch_scale = *(int32_t*)bytes; bytes += 4;
value = *(int32_t*)bytes; bytes += 4;
LOG("PATCH_FUNC_ARM32_THM patch_offs=%i patch_mask=%#08x scale=%i value=%i\n",
offs, patch_mask, patch_scale, value);
patch_arm_thm_jump24(context->executable_memory + offs, value);
break;
case PATCH_OBJECT_ARM32_ABS_THM:
offs = *(uint32_t*)bytes; bytes += 4;
patch_mask = *(uint32_t*)bytes; bytes += 4;
patch_scale = *(int32_t*)bytes; bytes += 4;
value = *(int32_t*)bytes; bytes += 4;
LOG("PATCH_OBJECT_ARM32_ABS_THM patch_offs=%i patch_mask=%#08x scale=%i value=%i imm16=%#04x\n",
offs, patch_mask, patch_scale, value, (uint32_t)((uintptr_t)(context->data_memory + value) & patch_mask) / (uint32_t)patch_scale);
patch_arm_thm_abs(context->executable_memory + offs, (uint32_t)((uintptr_t)(context->data_memory + value) & patch_mask) / (uint32_t)patch_scale);
break;
case ENTRY_POINT:
rel_entr_point = *(uint32_t*)bytes; bytes += 4;
context->entr_point = (entry_point_t)(context->executable_memory + rel_entr_point);

View File

@ -1,10 +1,3 @@
/**
* @file runmem.h
* @brief Header file for runmem.c, which contains core functions of
* the runner to receive data, code and patch instructions, perform
* patching, and jump to the entry point of the copapy program.
*/
#ifndef RUNMEM_H
#define RUNMEM_H
@ -27,13 +20,11 @@
#define ALLOCATE_CODE 3
#define COPY_CODE 4
#define PATCH_FUNC 0x1000
#define PATCH_FUNC_ARM32_THM 0x1005
#define PATCH_OBJECT 0x2000
#define PATCH_OBJECT_HI21 0x2001
#define PATCH_OBJECT_ABS 0x2002
#define PATCH_OBJECT_REL 0x2003
#define PATCH_OBJECT_ARM32_ABS 0x2004
#define PATCH_OBJECT_ARM32_ABS_THM 0x2006
#define ENTRY_POINT 7
#define RUN_PROG 64
#define READ_DATA 65

View File

@ -57,8 +57,8 @@ def norm_indent(f: Callable[..., str]) -> Callable[..., str]:
def get_entry_function_shell() -> str:
return f"""
{entry_func_prefix}int entry_function_shell(){{
//volatile char stack_place_holder[{stack_size}];
//stack_place_holder[0] = 0;
volatile char stack_place_holder[{stack_size}];
stack_place_holder[0] = 0;
result_int(0);
return 1;
}}

View File

@ -1,8 +1,9 @@
from copapy import value
from copapy.backend import Store, compile_to_dag, add_read_value_remote
from copapy.backend import Store, compile_to_dag, add_read_command
import copapy as cp
import subprocess
from copapy import _binwrite
import copapy.backend
import pytest
@ -23,7 +24,7 @@ def test_compile():
out = [Store(r) for r in ret_test]
il, variables = compile_to_dag(out, cp.generic_sdb)
il, variables = compile_to_dag(out, copapy.generic_sdb)
# run program command
il.write_com(_binwrite.Command.RUN_PROG)
@ -31,7 +32,7 @@ def test_compile():
for v in ret_test:
assert isinstance(v, value)
add_read_value_remote(il, variables, v.net)
add_read_command(il, variables, v.net)
il.write_com(_binwrite.Command.END_COM)

View File

@ -1,9 +1,10 @@
from copapy import NumLike
from copapy.backend import Store, compile_to_dag, add_read_value_remote
from copapy.backend import Store, compile_to_dag, add_read_command
import copapy as cp
import subprocess
import struct
from copapy import _binwrite
import copapy.backend
import pytest
@ -59,14 +60,14 @@ def test_compile():
out = [Store(r) for r in ret]
il, variables = compile_to_dag(out, cp.generic_sdb)
il, variables = compile_to_dag(out, copapy.generic_sdb)
# run program command
il.write_com(_binwrite.Command.RUN_PROG)
for v in ret:
assert isinstance(v, cp.value)
add_read_value_remote(il, variables, v.net)
add_read_command(il, variables, v.net)
il.write_com(_binwrite.Command.END_COM)

View File

@ -1,5 +1,5 @@
from copapy import NumLike
from copapy.backend import Store, compile_to_dag, add_read_value_remote
from copapy.backend import Store, compile_to_dag, add_read_command
import subprocess
from copapy import _binwrite
import copapy.backend as backend
@ -62,7 +62,7 @@ def test_compile():
for v in ret:
assert isinstance(v, cp.value)
add_read_value_remote(il, variables, v.net)
add_read_command(il, variables, v.net)
il.write_com(_binwrite.Command.END_COM)

View File

@ -1,45 +1,32 @@
import os
from copapy import NumLike
from copapy.backend import Store, compile_to_dag, add_read_command
import subprocess
from copapy import _binwrite
import copapy.backend as backend
import copapy as cp
import os
import warnings
import pytest
import copapy as cp
import copapy.backend as backend
from copapy import NumLike, _binwrite
from copapy.backend import Store, add_read_value_remote, compile_to_dag
if os.name == "nt":
if os.name == 'nt':
# On Windows wsl and qemu-user is required:
# sudo apt install qemu-user
qemu_command = ["wsl", "qemu-arm"]
qemu_command = ['wsl', 'qemu-arm']
else:
qemu_command = ["qemu-arm"]
qemu_command = ['qemu-arm']
def run_command(command: list[str]) -> str:
result = subprocess.run(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf8",
check=False,
)
assert result.returncode != 11, (
f"SIGSEGV (segmentation fault)\n -Error occurred: {result.stderr}\n -Output: {result.stdout}"
)
assert result.returncode == 0, (
f"\n -Error occurred: {result.stderr}\n -Output: {result.stdout}"
)
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf8', check=False)
assert result.returncode != 11, f"SIGSEGV (segmentation fault)\n -Error occurred: {result.stderr}\n -Output: {result.stdout}"
assert result.returncode == 0, f"\n -Error occurred: {result.stderr}\n -Output: {result.stdout}"
return result.stdout
def check_for_qemu() -> bool:
command = qemu_command + ["--version"]
command = qemu_command + ['--version']
try:
result = subprocess.run(
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False
)
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)
except Exception:
return False
return result.returncode == 0
@ -67,42 +54,36 @@ def test_compile():
out = [Store(r) for r in ret]
sdb = backend.stencil_db_from_package("armv7")
sdb = backend.stencil_db_from_package('armv7')
il, variables = compile_to_dag(out, sdb)
# run program command
il.write_com(_binwrite.Command.RUN_PROG)
# il.write_com(_binwrite.Command.DUMP_CODE)
#il.write_com(_binwrite.Command.DUMP_CODE)
for v in ret:
assert isinstance(v, cp.value)
add_read_value_remote(il, variables, v.net)
add_read_command(il, variables, v.net)
il.write_com(_binwrite.Command.END_COM)
# print('* Data to runner:')
# il.print()
#print('* Data to runner:')
#il.print()
il.to_file("build/runner/test-armv7.copapy")
il.to_file('build/runner/test-armv7.copapy')
if not check_for_qemu():
warnings.warn("qemu-armv7 not found, test skipped!", UserWarning)
elif "wsl" in qemu_command:
warnings.warn("qemu-armv7 seams not work on wsl1, test skipped!", UserWarning)
elif not os.path.isfile("build/runner/coparun-armv7"):
warnings.warn("qemu-armv7 not found, aarch64 test skipped!", UserWarning)
elif not os.path.isfile('build/runner/coparun-armv7'):
warnings.warn("armv7 runner not found, aarch64 test skipped!", UserWarning)
else:
command = [
"build/runner/coparun-armv7",
"build/runner/test-armv7.copapy",
"build/runner/test-armv7.copapy.bin",
]
command = ['build/runner/coparun-armv7', 'build/runner/test-armv7.copapy', 'build/runner/test-armv7.copapy.bin']
result = run_command(qemu_command + command)
print("* Output from runner:\n--")
print('* Output from runner:\n--')
print(result)
print("--")
print('--')
assert "Return value: 1" in result
assert 'Return value: 1' in result
# Compare to x86_64 reference results
assert " size=4 data=24 00 00 00" in result
@ -111,5 +92,5 @@ def test_compile():
if __name__ == "__main__":
# test_example()
#test_example()
test_compile()

View File

@ -1,6 +1,6 @@
from copapy import value, NumLike
from copapy.backend import Store, compile_to_dag, add_read_value_remote
import copapy as cp
from copapy.backend import Store, compile_to_dag, add_read_command
import copapy
import subprocess
from copapy import _binwrite
import pytest
@ -28,14 +28,14 @@ def test_compile():
out = [Store(r) for r in ret]
il, vars = compile_to_dag(out, cp.generic_sdb)
il, vars = compile_to_dag(out, copapy.generic_sdb)
# run program command
il.write_com(_binwrite.Command.RUN_PROG)
for v in ret:
assert isinstance(v, value)
add_read_value_remote(il, vars, v.net)
add_read_command(il, vars, v.net)
il.write_com(_binwrite.Command.END_COM)

View File

@ -1,8 +1,9 @@
from copapy import value
from copapy.backend import Store, compile_to_dag, add_read_value_remote
from copapy.backend import Store, compile_to_dag, add_read_command
import copapy as cp
import subprocess
from copapy import _binwrite
import copapy.backend
import pytest
@ -22,14 +23,14 @@ def test_compile_sqrt():
out = [Store(r) for r in ret]
il, variables = compile_to_dag(out, cp.generic_sdb)
il, variables = compile_to_dag(out, copapy.generic_sdb)
# run program command
il.write_com(_binwrite.Command.RUN_PROG)
for v in ret:
assert isinstance(v, value)
add_read_value_remote(il, variables, v.net)
add_read_command(il, variables, v.net)
il.write_com(_binwrite.Command.END_COM)
@ -56,14 +57,14 @@ def test_compile_log():
out = [Store(r) for r in ret]
il, variables = compile_to_dag(out, cp.generic_sdb)
il, variables = compile_to_dag(out, copapy.generic_sdb)
# run program command
il.write_com(_binwrite.Command.RUN_PROG)
for v in ret:
assert isinstance(v, value)
add_read_value_remote(il, variables, v.net)
add_read_command(il, variables, v.net)
il.write_com(_binwrite.Command.END_COM)
@ -90,14 +91,14 @@ def test_compile_sin():
out = [Store(r) for r in ret]
il, variables = compile_to_dag(out, cp.generic_sdb)
il, variables = compile_to_dag(out, copapy.generic_sdb)
# run program command
il.write_com(_binwrite.Command.RUN_PROG)
for v in ret:
assert isinstance(v, cp.value)
add_read_value_remote(il, variables, v.net)
assert isinstance(v, copapy.value)
add_read_command(il, variables, v.net)
il.write_com(_binwrite.Command.END_COM)

View File

@ -1,5 +1,5 @@
from copapy import NumLike, iif, value
from copapy.backend import Store, compile_to_dag, add_read_value_remote
from copapy.backend import Store, compile_to_dag, add_read_command
import subprocess
from copapy import _binwrite
import copapy.backend as backend
@ -109,7 +109,7 @@ def test_compile():
for v in ret_test:
assert isinstance(v, value)
add_read_value_remote(dw, variables, v.net)
add_read_command(dw, variables, v.net)
#dw.write_com(_binwrite.Command.READ_DATA)
#dw.write_int(0)

View File

@ -1,22 +1,21 @@
from copapy import NumLike, iif, value
from copapy.backend import Store, compile_to_dag, add_read_command
import subprocess
from copapy import _binwrite
import copapy.backend as backend
import os
import warnings
import re
import struct
import subprocess
import warnings
import pytest
import copapy as cp
import copapy.backend as backend
from copapy import NumLike, _binwrite, iif, value
from copapy.backend import Store, add_read_value_remote, compile_to_dag
if os.name == "nt":
if os.name == 'nt':
# On Windows wsl and qemu-user is required:
# sudo apt install qemu-user
qemu_command = ["wsl", "qemu-arm"]
qemu_command = ['wsl', 'qemu-arm']
else:
qemu_command = ["qemu-arm"]
qemu_command = ['qemu-arm']
def parse_results(log_text: str) -> dict[int, bytes]:
@ -25,8 +24,8 @@ def parse_results(log_text: str) -> dict[int, bytes]:
var_dict: dict[int, bytes] = {}
for match in matches:
value_str: list[str] = match.group(3).strip().split(" ")
# print('--', value_str)
value_str: list[str] = match.group(3).strip().split(' ')
#print('--', value_str)
value = bytes(int(v, base=16) for v in value_str)
if len(value) <= 8:
var_dict[int(match.group(1))] = value
@ -35,49 +34,26 @@ def parse_results(log_text: str) -> dict[int, bytes]:
def run_command(command: list[str]) -> str:
result = subprocess.run(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf8",
check=False,
)
assert result.returncode != 11, (
f"SIGSEGV (segmentation fault)\n -Error occurred: {result.stderr}\n -Output: {result.stdout}"
)
assert result.returncode == 0, (
f"\n -Error occurred: {result.stderr}\n -Output: {result.stdout}"
)
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf8', check=False)
assert result.returncode != 11, f"SIGSEGV (segmentation fault)\n -Error occurred: {result.stderr}\n -Output: {result.stdout}"
assert result.returncode == 0, f"\n -Error occurred: {result.stderr}\n -Output: {result.stdout}"
return result.stdout
def check_for_qemu() -> bool:
command = qemu_command + ["--version"]
command = qemu_command + ['--version']
try:
result = subprocess.run(
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False
)
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)
except Exception:
return False
return result.returncode == 0
def function1(c1: NumLike) -> list[NumLike]:
return [
c1 / 4,
c1 / -4,
c1 // 4,
c1 // -4,
(c1 * -1) // 4,
c1 * 4,
c1 * -4,
c1 + 4,
c1 - 4,
c1 > 2,
c1 > 100,
c1 < 4,
c1 < 100,
]
return [c1 / 4, c1 / -4, c1 // 4, c1 // -4, (c1 * -1) // 4,
c1 * 4, c1 * -4,
c1 + 4, c1 - 4,
c1 > 2, c1 > 100, c1 < 4, c1 < 100]
def function2(c1: NumLike) -> list[NumLike]:
@ -101,13 +77,11 @@ def function6(c1: NumLike) -> list[NumLike]:
def iiftests(c1: NumLike) -> list[NumLike]:
return [
iif(c1 > 5, 8, 9),
iif(c1 < 5, 8.5, 9.5),
iif(1 > 5, 3.3, 8.8) + c1,
iif(1 < 5, c1 * 3.3, 8.8),
iif(c1 < 5, c1 * 3.3, 8.8),
]
return [iif(c1 > 5, 8, 9),
iif(c1 < 5, 8.5, 9.5),
iif(1 > 5, 3.3, 8.8) + c1,
iif(1 < 5, c1 * 3.3, 8.8),
iif(c1 < 5, c1 * 3.3, 8.8)]
@pytest.mark.runner
@ -116,90 +90,59 @@ def test_compile():
c_f = value(1.111)
c_b = value(True)
ret_test = (
function1(c_i)
+ function1(c_f)
+ function2(c_i)
+ function2(c_f)
+ function3(c_i)
+ function4(c_i)
+ function5(c_b)
+ [value(9) % 2]
+ iiftests(c_i)
+ iiftests(c_f)
+ [cp.asin(c_i / 10)]
)
ret_ref = (
function1(9)
+ function1(1.111)
+ function2(9)
+ function2(1.111)
+ function3(9)
+ function4(9)
+ function5(True)
+ [9 % 2]
+ iiftests(9)
+ iiftests(1.111)
+ [cp.asin(9 / 10)]
)
ret_test = function1(c_i) + function1(c_f) + function2(c_i) + function2(c_f) + function3(c_i) + function4(c_i) + function5(c_b) + [value(9) % 2] + iiftests(c_i) + iiftests(c_f) + [cp.asin(c_i/10)]
ret_ref = function1(9) + function1(1.111) + function2(9) + function2(1.111) + function3(9) + function4(9) + function5(True) + [9 % 2] + iiftests(9) + iiftests(1.111) + [cp.asin(9/10)]
# ret_test = (c_i * 100 // 5, c_f * 10 // 5)
# ret_ref = (9 * 100 // 5, 1.111 * 10 // 5)
#ret_test = (c_i * 100 // 5, c_f * 10 // 5)
#ret_ref = (9 * 100 // 5, 1.111 * 10 // 5)
out = [Store(r) for r in ret_test]
sdb = backend.stencil_db_from_package("armv6")
sdb = backend.stencil_db_from_package('armv6')
dw, variables = compile_to_dag(out, sdb)
# dw.write_com(_binwrite.Command.READ_DATA)
# dw.write_int(0)
# dw.write_int(28)
#dw.write_com(_binwrite.Command.READ_DATA)
#dw.write_int(0)
#dw.write_int(28)
# run program command
dw.write_com(_binwrite.Command.RUN_PROG)
# dw.write_com(_binwrite.Command.DUMP_CODE)
#dw.write_com(_binwrite.Command.DUMP_CODE)
for v in ret_test:
assert isinstance(v, value)
add_read_value_remote(dw, variables, v.net)
add_read_command(dw, variables, v.net)
# dw.write_com(_binwrite.Command.READ_DATA)
# dw.write_int(0)
# dw.write_int(28)
#dw.write_com(_binwrite.Command.READ_DATA)
#dw.write_int(0)
#dw.write_int(28)
dw.write_com(_binwrite.Command.END_COM)
# print('* Data to runner:')
# dw.print()
#print('* Data to runner:')
#dw.print()
dw.to_file("build/runner/test-armv6.copapy")
dw.to_file('build/runner/test-armv6.copapy')
if not check_for_qemu():
warnings.warn("qemu-armv6 not found, armv6 test skipped!", UserWarning)
return
if "wsl" in qemu_command:
warnings.warn("qemu-armv6 seams not work on wsl1, test skipped!", UserWarning)
return
if not os.path.isfile("build/runner/coparun-armv6"):
if not os.path.isfile('build/runner/coparun-armv6'):
warnings.warn("armv6 runner not found, armv6 test skipped!", UserWarning)
return
command = (
qemu_command
+ ["build/runner/coparun-armv6", "build/runner/test-armv6.copapy"]
+ ["build/runner/test-armv6.copapy.bin"]
)
# try:
command = qemu_command + ['build/runner/coparun-armv6', 'build/runner/test-armv6.copapy'] + ['build/runner/test-armv6.copapy.bin']
#try:
result = run_command(command)
# except FileNotFoundError:
#except FileNotFoundError:
# warnings.warn(f"Test skipped, executable not found.", UserWarning)
# return
# print('* Output from runner:\n--')
# print(result)
# print('--')
#print('* Output from runner:\n--')
#print(result)
#print('--')
assert "Return value: 1" in result
assert 'Return value: 1' in result
result_data = parse_results(result)
@ -207,25 +150,22 @@ def test_compile():
assert isinstance(test, value)
address = variables[test.net][0]
data = result_data[address]
if test.dtype == "int":
if test.dtype == 'int':
val = int.from_bytes(data, sdb.byteorder, signed=True)
elif test.dtype == "bool":
elif test.dtype == 'bool':
val = bool.from_bytes(data, sdb.byteorder)
elif test.dtype == "float":
en = {"little": "<", "big": ">"}[sdb.byteorder]
val = struct.unpack(en + "f", data)[0]
elif test.dtype == 'float':
en = {'little': '<', 'big': '>'}[sdb.byteorder]
val = struct.unpack(en + 'f', data)[0]
assert isinstance(val, float)
else:
raise Exception(f"Unknown type: {test.dtype}")
print("+", val, ref, test.dtype, f" addr={address}")
print('+', val, ref, test.dtype, f" addr={address}")
for t in (int, float, bool):
assert isinstance(val, t) == isinstance(ref, t), (
f"Result type does not match for {val} and {ref}"
)
assert val == pytest.approx(ref, 1e-5), (
f"Result does not match: {val} and reference: {ref}"
) # pyright: ignore[reportUnknownMemberType]
assert isinstance(val, t) == isinstance(ref, t), f"Result type does not match for {val} and {ref}"
assert val == pytest.approx(ref, 1e-5), f"Result does not match: {val} and reference: {ref}" # pyright: ignore[reportUnknownMemberType]
if __name__ == "__main__":
test_compile()
#test_compile()
test_slow_31bit_int_list_hash()

View File

@ -1,22 +1,21 @@
from copapy import NumLike, iif, value
from copapy.backend import Store, compile_to_dag, add_read_command
import subprocess
from copapy import _binwrite
import copapy.backend as backend
import os
import warnings
import re
import struct
import subprocess
import warnings
import pytest
import copapy as cp
import copapy.backend as backend
from copapy import NumLike, _binwrite, iif, value
from copapy.backend import Store, add_read_value_remote, compile_to_dag
if os.name == "nt":
if os.name == 'nt':
# On Windows wsl and qemu-user is required:
# sudo apt install qemu-user
qemu_command = ["wsl", "qemu-arm"]
qemu_command = ['wsl', 'qemu-arm']
else:
qemu_command = ["qemu-arm"]
qemu_command = ['qemu-arm']
def parse_results(log_text: str) -> dict[int, bytes]:
@ -25,8 +24,8 @@ def parse_results(log_text: str) -> dict[int, bytes]:
var_dict: dict[int, bytes] = {}
for match in matches:
value_str: list[str] = match.group(3).strip().split(" ")
# print('--', value_str)
value_str: list[str] = match.group(3).strip().split(' ')
#print('--', value_str)
value = bytes(int(v, base=16) for v in value_str)
if len(value) <= 8:
var_dict[int(match.group(1))] = value
@ -35,49 +34,26 @@ def parse_results(log_text: str) -> dict[int, bytes]:
def run_command(command: list[str]) -> str:
result = subprocess.run(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
encoding="utf8",
check=False,
)
assert result.returncode != 11, (
f"SIGSEGV (segmentation fault)\n -Error occurred: {result.stderr}\n -Output: {result.stdout}"
)
assert result.returncode == 0, (
f"\n -Error occurred: {result.stderr}\n -Output: {result.stdout}"
)
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf8', check=False)
assert result.returncode != 11, f"SIGSEGV (segmentation fault)\n -Error occurred: {result.stderr}\n -Output: {result.stdout}"
assert result.returncode == 0, f"\n -Error occurred: {result.stderr}\n -Output: {result.stdout}"
return result.stdout
def check_for_qemu() -> bool:
command = qemu_command + ["--version"]
command = qemu_command + ['--version']
try:
result = subprocess.run(
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False
)
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)
except Exception:
return False
return result.returncode == 0
def function1(c1: NumLike) -> list[NumLike]:
return [
c1 / 4,
c1 / -4,
c1 // 4,
c1 // -4,
(c1 * -1) // 4,
c1 * 4,
c1 * -4,
c1 + 4,
c1 - 4,
c1 > 2,
c1 > 100,
c1 < 4,
c1 < 100,
]
return [c1 / 4, c1 / -4, c1 // 4, c1 // -4, (c1 * -1) // 4,
c1 * 4, c1 * -4,
c1 + 4, c1 - 4,
c1 > 2, c1 > 100, c1 < 4, c1 < 100]
def function2(c1: NumLike) -> list[NumLike]:
@ -101,13 +77,11 @@ def function6(c1: NumLike) -> list[NumLike]:
def iiftests(c1: NumLike) -> list[NumLike]:
return [
iif(c1 > 5, 8, 9),
iif(c1 < 5, 8.5, 9.5),
iif(1 > 5, 3.3, 8.8) + c1,
iif(1 < 5, c1 * 3.3, 8.8),
iif(c1 < 5, c1 * 3.3, 8.8),
]
return [iif(c1 > 5, 8, 9),
iif(c1 < 5, 8.5, 9.5),
iif(1 > 5, 3.3, 8.8) + c1,
iif(1 < 5, c1 * 3.3, 8.8),
iif(c1 < 5, c1 * 3.3, 8.8)]
@pytest.mark.runner
@ -116,90 +90,59 @@ def test_compile():
c_f = value(1.111)
c_b = value(True)
ret_test = (
function1(c_i)
+ function1(c_f)
+ function2(c_i)
+ function2(c_f)
+ function3(c_i)
+ function4(c_i)
+ function5(c_b)
+ [value(9) % 2]
+ iiftests(c_i)
+ iiftests(c_f)
+ [cp.asin(c_i / 10)]
)
ret_ref = (
function1(9)
+ function1(1.111)
+ function2(9)
+ function2(1.111)
+ function3(9)
+ function4(9)
+ function5(True)
+ [9 % 2]
+ iiftests(9)
+ iiftests(1.111)
+ [cp.asin(9 / 10)]
)
ret_test = function1(c_i) + function1(c_f) + function2(c_i) + function2(c_f) + function3(c_i) + function4(c_i) + function5(c_b) + [value(9) % 2] + iiftests(c_i) + iiftests(c_f) + [cp.asin(c_i/10)]
ret_ref = function1(9) + function1(1.111) + function2(9) + function2(1.111) + function3(9) + function4(9) + function5(True) + [9 % 2] + iiftests(9) + iiftests(1.111) + [cp.asin(9/10)]
# ret_test = (c_i * 100 // 5, c_f * 10 // 5)
# ret_ref = (9 * 100 // 5, 1.111 * 10 // 5)
#ret_test = (c_i * 100 // 5, c_f * 10 // 5)
#ret_ref = (9 * 100 // 5, 1.111 * 10 // 5)
out = [Store(r) for r in ret_test]
sdb = backend.stencil_db_from_package("armv7")
sdb = backend.stencil_db_from_package('armv7')
dw, variables = compile_to_dag(out, sdb)
# dw.write_com(_binwrite.Command.READ_DATA)
# dw.write_int(0)
# dw.write_int(28)
#dw.write_com(_binwrite.Command.READ_DATA)
#dw.write_int(0)
#dw.write_int(28)
# run program command
dw.write_com(_binwrite.Command.RUN_PROG)
# dw.write_com(_binwrite.Command.DUMP_CODE)
#dw.write_com(_binwrite.Command.DUMP_CODE)
for v in ret_test:
assert isinstance(v, value)
add_read_value_remote(dw, variables, v.net)
add_read_command(dw, variables, v.net)
# dw.write_com(_binwrite.Command.READ_DATA)
# dw.write_int(0)
# dw.write_int(28)
#dw.write_com(_binwrite.Command.READ_DATA)
#dw.write_int(0)
#dw.write_int(28)
dw.write_com(_binwrite.Command.END_COM)
# print('* Data to runner:')
# dw.print()
#print('* Data to runner:')
#dw.print()
dw.to_file("build/runner/test-armv7.copapy")
dw.to_file('build/runner/test-armv7.copapy')
if not check_for_qemu():
warnings.warn("qemu-armv7 not found, armv7 test skipped!", UserWarning)
return
if "wsl" in qemu_command:
warnings.warn("qemu-armv7 seams not work on wsl1, test skipped!", UserWarning)
return
if not os.path.isfile("build/runner/coparun-armv7"):
if not os.path.isfile('build/runner/coparun-armv7'):
warnings.warn("armv7 runner not found, armv7 test skipped!", UserWarning)
return
command = (
qemu_command
+ ["build/runner/coparun-armv7", "build/runner/test-armv7.copapy"]
+ ["build/runner/test-armv7.copapy.bin"]
)
# try:
command = qemu_command + ['build/runner/coparun-armv7', 'build/runner/test-armv7.copapy'] + ['build/runner/test-armv7.copapy.bin']
#try:
result = run_command(command)
# except FileNotFoundError:
#except FileNotFoundError:
# warnings.warn(f"Test skipped, executable not found.", UserWarning)
# return
print("* Output from runner:\n--")
print('* Output from runner:\n--')
print(result)
print("--")
print('--')
assert "Return value: 1" in result
assert 'Return value: 1' in result
result_data = parse_results(result)
@ -207,25 +150,22 @@ def test_compile():
assert isinstance(test, value)
address = variables[test.net][0]
data = result_data[address]
if test.dtype == "int":
if test.dtype == 'int':
val = int.from_bytes(data, sdb.byteorder, signed=True)
elif test.dtype == "bool":
elif test.dtype == 'bool':
val = bool.from_bytes(data, sdb.byteorder)
elif test.dtype == "float":
en = {"little": "<", "big": ">"}[sdb.byteorder]
val = struct.unpack(en + "f", data)[0]
elif test.dtype == 'float':
en = {'little': '<', 'big': '>'}[sdb.byteorder]
val = struct.unpack(en + 'f', data)[0]
assert isinstance(val, float)
else:
raise Exception(f"Unknown type: {test.dtype}")
print("+", val, ref, test.dtype, f" addr={address}")
print('+', val, ref, test.dtype, f" addr={address}")
for t in (int, float, bool):
assert isinstance(val, t) == isinstance(ref, t), (
f"Result type does not match for {val} and {ref}"
)
assert val == pytest.approx(ref, 1e-5), (
f"Result does not match: {val} and reference: {ref}"
) # pyright: ignore[reportUnknownMemberType]
assert isinstance(val, t) == isinstance(ref, t), f"Result type does not match for {val} and {ref}"
assert val == pytest.approx(ref, 1e-5), f"Result does not match: {val} and reference: {ref}" # pyright: ignore[reportUnknownMemberType]
if __name__ == "__main__":
test_compile()
#test_example()
test_slow_31bit_int_list_hash()

View File

@ -1,174 +0,0 @@
from copapy import NumLike, iif, value
from copapy.backend import Store, compile_to_dag, add_read_value_remote
import subprocess
from copapy import _binwrite
import copapy.backend as backend
import os
import warnings
import re
import struct
import pytest
import copapy as cp
if os.name == 'nt':
# On Windows wsl and qemu-user is required:
# sudo apt install qemu-user
qemu_command = ['wsl', 'qemu-arm']
else:
qemu_command = ['qemu-arm']
def parse_results(log_text: str) -> dict[int, bytes]:
regex = r"^READ_DATA offs=(\d*) size=(\d*) data=(.*)$"
matches = re.finditer(regex, log_text, re.MULTILINE)
var_dict: dict[int, bytes] = {}
for match in matches:
value_str: list[str] = match.group(3).strip().split(' ')
#print('--', value_str)
value = bytes(int(v, base=16) for v in value_str)
if len(value) <= 8:
var_dict[int(match.group(1))] = value
return var_dict
def run_command(command: list[str]) -> str:
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf8', check=False)
assert result.returncode != 11, f"SIGSEGV (segmentation fault)\n -Error occurred: {result.stderr}\n -Output: {result.stdout}"
assert result.returncode == 0, f"\n -Error occurred: {result.stderr}\n -Output: {result.stdout}"
return result.stdout
def check_for_qemu() -> bool:
command = qemu_command + ['--version']
try:
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)
except Exception:
return False
return result.returncode == 0
def function1(c1: NumLike) -> list[NumLike]:
return [c1 / 4, c1 / -4, c1 // 4, c1 // -4, (c1 * -1) // 4,
c1 * 4, c1 * -4,
c1 + 4, c1 - 4,
c1 > 2, c1 > 100, c1 < 4, c1 < 100]
def function1ex(c1: NumLike) -> list[NumLike]:
return [c1 // 4]
def function2(c1: NumLike) -> list[NumLike]:
return [c1 * 4.44, c1 * -4.44]
def function3(c1: NumLike) -> list[NumLike]:
return [c1 / 4]
def function4(c1: NumLike) -> list[NumLike]:
return [c1 == 9, c1 == 4, c1 != 9, c1 != 4]
def function5(c1: NumLike) -> list[NumLike]:
return [c1 == True, c1 == False, c1 != True, c1 != False, c1 / 2, c1 + 2]
def function6(c1: NumLike) -> list[NumLike]:
return [c1 == True]
def iiftests(c1: NumLike) -> list[NumLike]:
return [iif(c1 > 5, 8, 9),
iif(c1 < 5, 8.5, 9.5),
iif(1 > 5, 3.3, 8.8) + c1,
iif(1 < 5, c1 * 3.3, 8.8),
iif(c1 < 5, c1 * 3.3, 8.8)]
@pytest.mark.runner
def test_compile():
c_i = value(9)
c_f = value(1.111)
c_b = value(True)
ret_test = function1(c_i) + function1(c_f) + function2(c_i) + function2(c_f) + function3(c_i) + function4(c_i) + function5(c_b) + [value(9) % 2] + iiftests(c_i) + iiftests(c_f) + [cp.asin(c_i/10)]
ret_ref = function1(9) + function1(1.111) + function2(9) + function2(1.111) + function3(9) + function4(9) + function5(True) + [9 % 2] + iiftests(9) + iiftests(1.111) + [cp.asin(9/10)]
out = [Store(r) for r in ret_test]
sdb = backend.stencil_db_from_package('armv7mthumb')
dw, variables = compile_to_dag(out, sdb)
#dw.write_com(_binwrite.Command.READ_DATA)
#dw.write_int(0)
#dw.write_int(28)
du = dw.copy()
dw.write_com(_binwrite.Command.RUN_PROG)
du.write_com(_binwrite.Command.DUMP_CODE)
for v in ret_test:
assert isinstance(v, value)
add_read_value_remote(dw, variables, v.net)
#dw.write_com(_binwrite.Command.READ_DATA)
#dw.write_int(0)
#dw.write_int(28)
dw.write_com(_binwrite.Command.END_COM)
du.write_com(_binwrite.Command.END_COM)
#print('* Data to runner:')
#dw.print()
dw.to_file('build/runner/test-armv7mthumb.copapy')
du.to_file('build/runner/test-armv7mthumb-dump.copapy')
if not check_for_qemu():
warnings.warn("qemu-armv7 not found, armv7 test skipped!", UserWarning)
return
if not os.path.isfile('build/runner/coparun-armv7'):
warnings.warn("armv7 runner not found, armv7 test skipped!", UserWarning)
return
print('----- Dump code...')
command = qemu_command + ['build/runner/coparun-armv7', 'build/runner/test-armv7mthumb-dump.copapy', 'build/runner/test.copapy-armv7mthumb.bin']
result = run_command(command)
print('----- Run code...')
command = qemu_command + ['build/runner/coparun-armv7', 'build/runner/test-armv7mthumb.copapy']
result = run_command(command)
print('* Output from runner:\n--')
print(result)
print('--')
assert 'Return value: 1' in result
result_data = parse_results(result)
for test, ref in zip(ret_test, ret_ref):
assert isinstance(test, value)
address = variables[test.net][0]
data = result_data[address]
if test.dtype == 'int':
val = int.from_bytes(data, sdb.byteorder, signed=True)
elif test.dtype == 'bool':
val = bool.from_bytes(data, sdb.byteorder)
elif test.dtype == 'float':
en = {'little': '<', 'big': '>'}[sdb.byteorder]
val = struct.unpack(en + 'f', data)[0]
assert isinstance(val, float)
else:
raise Exception(f"Unknown type: {test.dtype}")
print('+', val, ref, test.dtype, f" addr={address}")
for t in (int, float, bool):
assert isinstance(val, t) == isinstance(ref, t), f"Result type does not match for {val} and {ref}"
assert val == pytest.approx(ref, 1e-5), f"Result does not match: {val} and reference: {ref}" # pyright: ignore[reportUnknownMemberType]
if __name__ == "__main__":
test_compile()

View File

@ -1,184 +0,0 @@
from copapy import NumLike, iif, value
from copapy.backend import Store, compile_to_dag, add_read_value_remote
import subprocess
from copapy import _binwrite
import copapy.backend as backend
import os
import warnings
import re
import struct
import pytest
import copapy as cp
if os.name == 'nt':
# On Windows wsl and qemu-user is required:
# sudo apt install qemu-user
qemu_command = ['wsl', 'qemu-arm']
else:
qemu_command = ['qemu-arm']
def parse_results(log_text: str) -> dict[int, bytes]:
regex = r"^READ_DATA offs=(\d*) size=(\d*) data=(.*)$"
matches = re.finditer(regex, log_text, re.MULTILINE)
var_dict: dict[int, bytes] = {}
for match in matches:
value_str: list[str] = match.group(3).strip().split(' ')
#print('--', value_str)
value = bytes(int(v, base=16) for v in value_str)
if len(value) <= 8:
var_dict[int(match.group(1))] = value
return var_dict
def run_command(command: list[str]) -> str:
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf8', check=False)
assert result.returncode != 11, f"SIGSEGV (segmentation fault)\n -Error occurred: {result.stderr}\n -Output: {result.stdout}"
assert result.returncode == 0, f"\n -Error occurred: {result.stderr}\n -Output: {result.stdout}"
return result.stdout
def check_for_qemu() -> bool:
command = qemu_command + ['--version']
try:
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)
except Exception:
return False
return result.returncode == 0
def function1(c1: NumLike) -> list[NumLike]:
return [c1 / 4, c1 / -4, c1 // 4, c1 // -4, (c1 * -1) // 4,
c1 * 4, c1 * -4,
c1 + 4, c1 - 4,
c1 > 2, c1 > 100, c1 < 4, c1 < 100]
def function1ex(c1: NumLike) -> list[NumLike]:
return [c1 // 4]
def function2(c1: NumLike) -> list[NumLike]:
return [c1 * 4.44, c1 * -4.44]
def function3(c1: NumLike) -> list[NumLike]:
return [c1 / 4]
def function4(c1: NumLike) -> list[NumLike]:
return [c1 == 9, c1 == 4, c1 != 9, c1 != 4]
def function5(c1: NumLike) -> list[NumLike]:
return [c1 == True, c1 == False, c1 != True, c1 != False, c1 / 2, c1 + 2]
def function6(c1: NumLike) -> list[NumLike]:
return [c1 == True]
def iiftests(c1: NumLike) -> list[NumLike]:
return [iif(c1 > 5, 8, 9),
iif(c1 < 5, 8.5, 9.5),
iif(1 > 5, 3.3, 8.8) + c1,
iif(1 < 5, c1 * 3.3, 8.8),
iif(c1 < 5, c1 * 3.3, 8.8)]
@pytest.mark.runner
def test_compile():
c_i = value(9)
c_f = value(1.111)
c_b = value(True)
ret_test = function1(c_i) + function1(c_f) + function2(c_i) + function2(c_f) + function3(c_i) + function4(c_i) + function5(c_b) + [value(9) % 2] + iiftests(c_i) + iiftests(c_f) + [cp.asin(c_i/10)]
ret_ref = function1(9) + function1(1.111) + function2(9) + function2(1.111) + function3(9) + function4(9) + function5(True) + [9 % 2] + iiftests(9) + iiftests(1.111) + [cp.asin(9/10)]
out = [Store(r) for r in ret_test]
sdb = backend.stencil_db_from_package('armv7thumb')
dw, variables = compile_to_dag(out, sdb)
#dw.write_com(_binwrite.Command.READ_DATA)
#dw.write_int(0)
#dw.write_int(28)
du = dw.copy()
dw.write_com(_binwrite.Command.RUN_PROG)
du.write_com(_binwrite.Command.DUMP_CODE)
for v in ret_test:
assert isinstance(v, value)
add_read_value_remote(dw, variables, v.net)
#dw.write_com(_binwrite.Command.READ_DATA)
#dw.write_int(0)
#dw.write_int(28)
dw.write_com(_binwrite.Command.END_COM)
du.write_com(_binwrite.Command.END_COM)
#print('* Data to runner:')
#dw.print()
dw.to_file('build/runner/test-armv7thumb.copapy')
du.to_file('build/runner/test-armv7thumb-dump.copapy')
if not check_for_qemu():
warnings.warn("qemu-armv7 not found, armv7 test skipped!", UserWarning)
return
if not os.path.isfile('build/runner/coparun-armv7'):
warnings.warn("armv7 runner not found, armv7 test skipped!", UserWarning)
return
print('----- Dump code...')
command = qemu_command + ['build/runner/coparun-armv7', 'build/runner/test-armv7thumb-dump.copapy', 'build/runner/test.copapy-armv7thumb.bin']
result = run_command(command)
print('----- Run code...')
command = qemu_command + ['build/runner/coparun-armv7', 'build/runner/test-armv7thumb.copapy']
result = run_command(command)
print('* Output from runner:\n--')
print(result)
print('--')
assert 'Return value: 1' in result
result_data = parse_results(result)
for test, ref in zip(ret_test, ret_ref):
assert isinstance(test, value)
address = variables[test.net][0]
data = result_data[address]
if test.dtype == 'int':
val = int.from_bytes(data, sdb.byteorder, signed=True)
elif test.dtype == 'bool':
val = bool.from_bytes(data, sdb.byteorder)
elif test.dtype == 'float':
en = {'little': '<', 'big': '>'}[sdb.byteorder]
val = struct.unpack(en + 'f', data)[0]
assert isinstance(val, float)
else:
raise Exception(f"Unknown type: {test.dtype}")
print('+', val, ref, test.dtype, f" addr={address}")
for t in (int, float, bool):
assert isinstance(val, t) == isinstance(ref, t), f"Result type does not match for {val} and {ref}"
assert val == pytest.approx(ref, 1e-5), f"Result does not match: {val} and reference: {ref}" # pyright: ignore[reportUnknownMemberType]
if __name__ == "__main__":
test_compile()
"""
qemu-arm -d in_asm,exec,cpu_reset -D qemu.log build/runner/coparun-armv7thumb build/runner/test-armv7thumb.copapy build/runner/test.copapy-armv7thumb.bin
qemu-arm -d in_asm,exec -D qemu_trace.log \
-global driver=pl011.audiomaddr,property=addr,value=0xff7ec000 \
-global driver=pl011.audiomaddr,property=size,value=0x100000 \
your_binary
"""

View File

@ -1,5 +1,5 @@
from copapy import NumLike, iif, value
from copapy.backend import Store, compile_to_dag, add_read_value_remote
from copapy.backend import Store, compile_to_dag, add_read_command
import subprocess
from copapy import _binwrite
import copapy.backend as backend
@ -122,7 +122,7 @@ def test_compile():
for v in ret_test:
assert isinstance(v, value)
add_read_value_remote(dw, variables, v.net)
add_read_command(dw, variables, v.net)
#dw.write_com(_binwrite.Command.READ_DATA)
#dw.write_int(0)
@ -196,7 +196,7 @@ def test_vector_compile():
for v in ret:
assert isinstance(v, cp.value)
add_read_value_remote(il, variables, v.net)
add_read_command(il, variables, v.net)
il.write_com(_binwrite.Command.END_COM)
@ -258,7 +258,7 @@ def test_sinus():
for v in ret_test:
assert isinstance(v, value)
add_read_value_remote(dw, variables, v.net)
add_read_command(dw, variables, v.net)
#dw.write_com(_binwrite.Command.READ_DATA)
#dw.write_int(0)

View File

@ -19,18 +19,11 @@ def test_start_end_function():
if symbol.relocations and symbol.relocations[-1].symbol.info == 'STT_NOTYPE':
if symbol.section and symbol.section.name == '.text':
print('SKIP', sym_name, '(Aux function, not a stencil)')
continue
print('-', sym_name, get_stencil_position(symbol), len(symbol.data))
if symbol.section:
function_size = symbol.section.fields['sh_size'] # len(symbol.data) excludes nop after the function
start, end = get_stencil_position(symbol)
print('-', sym_name, get_stencil_position(symbol), function_size)
start, end = get_stencil_position(symbol)
assert (start >= 0 and end >= start and end <= function_size)
assert start >= 0 and end >= start and end <= len(symbol.data)
def test_aux_functions():

View File

@ -1,13 +1,13 @@
#!/bin/bash
set -eu
set -eux
ARCH=${1:-x86_64}
case "$ARCH" in
(x86_64|arm64|arm-v6|arm-v7|arm-v7-thumb|arm-v7m-thumb|all)
(x86_64|arm-v6|arm-v7|all)
;;
(*)
echo "Usage: $0 [x86_64|arm64|arm-v6|arm-v7|arm-v6-thumb|arm-v7m-thumb|all]"
echo "Usage: $0 [x86_64|arm-v6|arm-v7|all]"
exit 1
;;
esac
@ -42,44 +42,13 @@ if [[ "$ARCH" == "x86_64" || "$ARCH" == "all" ]]; then
-o build/runner/coparun
fi
#######################################
# ARM 64
#######################################
if [[ "$ARCH" == "arm64" || "$ARCH" == "all" ]]; then
echo "--------------arm64----------------"
LIBGCC=$(aarch64-linux-gnu-gcc -print-libgcc-file-name)
aarch64-linux-gnu-gcc -fno-pic -ffunction-sections \
-c $SRC -O3 -o build/stencils/stencils.o
aarch64-linux-gnu-ld -r \
build/stencils/stencils.o \
build/musl/musl_objects_arm64.o \
$LIBGCC \
-o $DEST/stencils_arm64_O3.o
aarch64-linux-gnu-objdump -d -x \
$DEST/stencils_arm64_O3.o \
> build/stencils/stencils_arm64_O3.asm
aarch64-linux-gnu-gcc \
-Wall -Wextra -Wconversion -Wsign-conversion -static \
-Wshadow -Wstrict-overflow -O3 \
-DENABLE_LOGGING \
src/coparun/runmem.c \
src/coparun/coparun.c \
src/coparun/mem_man.c \
-o build/runner/coparun-arm64
fi
#######################################
# ARM v6
#######################################
if [[ "$ARCH" == "arm-v6" || "$ARCH" == "all" ]]; then
echo "--------------arm-v6 32 bit----------------"
LIBGCC=$(arm-none-eabi-gcc -march=armv6 -mfpu=vfp -mfloat-abi=hard -marm -print-libgcc-file-name)
LIBGCC=$(arm-none-eabi-gcc -print-libgcc-file-name)
arm-none-eabi-gcc -fno-pic -ffunction-sections \
-march=armv6 -mfpu=vfp -mfloat-abi=hard -marm \
@ -112,7 +81,7 @@ fi
if [[ "$ARCH" == "arm-v7" || "$ARCH" == "all" ]]; then
echo "--------------arm-v7 32 bit----------------"
LIBGCC=$(arm-none-eabi-gcc -march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -marm -print-libgcc-file-name)
LIBGCC=$(arm-none-eabi-gcc -print-libgcc-file-name)
arm-none-eabi-gcc -fno-pic -ffunction-sections \
-march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -marm \
@ -128,7 +97,6 @@ if [[ "$ARCH" == "arm-v7" || "$ARCH" == "all" ]]; then
$DEST/stencils_armv7_O3.o \
> build/stencils/stencils_armv7_O3.asm
# The same runner for all ARM7
arm-linux-gnueabihf-gcc \
-march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -marm -static \
-Wall -Wextra -Wconversion -Wsign-conversion \
@ -139,71 +107,3 @@ if [[ "$ARCH" == "arm-v7" || "$ARCH" == "all" ]]; then
src/coparun/mem_man.c \
-o build/runner/coparun-armv7
fi
#######################################
# ARM v7 thumb Cortex-A
#######################################
if [[ "$ARCH" == "arm-v7-thumb" || "$ARCH" == "all" ]]; then
echo "--------------arm-v7a-thumb 32 bit----------------"
LIBGCC=$(arm-none-eabi-gcc -march=armv7 -mfpu=vfp3 -mthumb -print-libgcc-file-name)
arm-none-eabi-gcc -fno-pic -ffunction-sections \
-march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -mthumb \
-c $SRC -O3 -o build/stencils/stencils.o
arm-none-eabi-ld -r \
build/stencils/stencils.o \
build/musl/musl_objects_armv7thumb.o \
$LIBGCC \
-o $DEST/stencils_armv7thumb_O3.o
arm-none-eabi-objdump -d -x \
$DEST/stencils_armv7thumb_O3.o \
> build/stencils/stencils_armv7thumb_O3.asm
# The same runner for all ARM7
arm-linux-gnueabihf-gcc \
-march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -static \
-Wall -Wextra -Wconversion -Wsign-conversion \
-Wshadow -Wstrict-overflow -O3 \
-DENABLE_LOGGING \
src/coparun/runmem.c \
src/coparun/coparun.c \
src/coparun/mem_man.c \
-o build/runner/coparun-armv7thumb
fi
#######################################
# ARM v7 thumb Cortex-M
#######################################
if [[ "$ARCH" == "arm-v7m-thumb" || "$ARCH" == "all" ]]; then
echo "--------------arm-v7m-thumb 32 bit----------------"
LIBGCC=$(arm-none-eabi-gcc -march=armv7e-m -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -print-libgcc-file-name)
arm-none-eabi-gcc -fno-pic -ffunction-sections \
-march=armv7e-m -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb \
-c $SRC -O3 -o build/stencils/stencils.o
arm-none-eabi-ld -r \
build/stencils/stencils.o \
build/musl/musl_objects_armv7mthumb.o \
$LIBGCC \
-o $DEST/stencils_armv7mthumb_O3.o
arm-none-eabi-objdump -d -x \
$DEST/stencils_armv7mthumb_O3.o \
> build/stencils/stencils_armv7mthumb_O3.asm
# The same runner for all ARM7
arm-linux-gnueabihf-gcc \
-march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -static \
-Wall -Wextra -Wconversion -Wsign-conversion \
-Wshadow -Wstrict-overflow -O3 \
-DENABLE_LOGGING \
src/coparun/runmem.c \
src/coparun/coparun.c \
src/coparun/mem_man.c \
-o build/runner/coparun-armv7thumb
fi

View File

@ -10,6 +10,7 @@ cparch=$(python3 -c "import copapy; print(copapy._stencils.detect_process_arch()
# Disassemble stencil object file
objdump -d -x src/copapy/obj/stencils_${cparch}_O3.o > build/runner/stencils.asm
# Create example code disassembly
python3 tools/make_example.py
build/runner/coparun build/runner/test.copapy build/runner/test.copapy.bin
@ -27,10 +28,6 @@ fi
echo "Archtitecture: '$cparch'"
if [[ "$cparch" == *"thumb"* ]]; then
objdump -D -b binary -marm -M force-thumb --adjust-vma=0x10000 build/runner/test.copapy.bin > build/runner/example.asm
else
objdump -D -b binary -m $cparch --adjust-vma=0x10000 build/runner/test.copapy.bin > build/runner/example.asm
fi
objdump -D -b binary -m $cparch --adjust-vma=0x10000 build/runner/test.copapy.bin > build/runner/example.asm
rm build/runner/test.copapy.bin

View File

@ -26,11 +26,8 @@ sh ../packobjs.sh arm-none-eabi-gcc arm-none-eabi-ld /object_files/musl_objects_
# Armv7
sh ../packobjs.sh arm-none-eabi-gcc arm-none-eabi-ld /object_files/musl_objects_armv7.o "-march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -marm"
# Armv7 Thumb for Cortex-A
sh ../packobjs.sh arm-none-eabi-gcc arm-none-eabi-ld /object_files/musl_objects_armv7thumb.o "-march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -mthumb"
# Armv7 Thumb for Cortex-M3..7
sh ../packobjs.sh arm-none-eabi-gcc arm-none-eabi-ld /object_files/musl_objects_armv7mthumb.o "-march=armv7e-m -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb"
sh ../packobjs.sh arm-none-eabi-gcc arm-none-eabi-ld /object_files/musl_objects_armv7thumb.o "-march=armv7e-m -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb"
#sh ../packobjs.sh mips mips-linux-gnu-gcc-13 mips-linux-gnu-ld

View File

@ -25,16 +25,14 @@ ar x ../../musl/lib/libc.a sinf.o cosf.o tanf.o asinf.o acosf.o atanf.o atan2f.o
ar x ../../musl/lib/libc.a sqrtf.o logf.o expf.o sqrt.o
ar x ../../musl/lib/libc.a logf_data.o __tandf.o __cosdf.o __sindf.o
ar x ../../musl/lib/libc.a fabsf.o scalbn.o floor.o floorf.o exp2f_data.o powf.o powf_data.o
ar x ../../musl/lib/libc.a __rem_pio2f.o __math_invalid.o __math_invalidf.o __stack_chk_fail.o
ar x ../../musl/lib/libc.a __math_divzerof.o __math_oflowf.o __rem_pio2_large.o __math_uflowf.o __math_xflowf.o sqrt_data.o
ar x ../../musl/lib/libc.a __rem_pio2f.o __math_invalidf.o __stack_chk_fail.o __math_divzerof.o __math_oflowf.o __rem_pio2_large.o __math_uflowf.o __math_xflowf.o
# Check out .lo (PIC)
ar x ../../musl/lib/libc.a sinf.lo cosf.lo tanf.lo asinf.lo acosf.lo atanf.lo atan2f.lo
ar x ../../musl/lib/libc.a sqrtf.lo logf.lo expf.lo sqrt.lo
ar x ../../musl/lib/libc.a logf_data.lo __tandf.lo __cosdf.lo __sindf.lo
ar x ../../musl/lib/libc.a fabsf.lo scalbn.lo floor.lo floorf.o exp2f_data.lo powf.lo powf_data.lo
ar x ../../musl/lib/libc.a __rem_pio2f.lo __math_invalid.lo __math_invalidf.lo __stack_chk_fail.lo
ar x ../../musl/lib/libc.a __math_divzerof.lo __math_oflowf.lo __rem_pio2_large.lo __math_uflowf.lo __math_xflowf.lo sqrt_data.lo
ar x ../../musl/lib/libc.a __rem_pio2f.lo __math_invalidf.lo __stack_chk_fail.lo __math_divzerof.lo __math_oflowf.lo __rem_pio2_large.lo __math_uflowf.lo __math_xflowf.lo
cd ../../musl

View File

@ -36,20 +36,15 @@ arm-none-eabi-gcc -march=armv6 -mfpu=vfp -mfloat-abi=hard -marm $FLAGS -$OPT -c
LIBGCC=$(arm-none-eabi-gcc -print-libgcc-file-name)
arm-none-eabi-ld -r $STMP /object_files/musl_objects_armv6.o $LIBGCC -o $DEST/stencils_armv6_$OPT.o
# ARMv7 hardware fp for Cortex-A
# ARMv7 hardware fp
arm-none-eabi-gcc -march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -marm $FLAGS -$OPT -c $SRC -o $STMP
LIBGCC=$(arm-none-eabi-gcc -print-libgcc-file-name)
arm-none-eabi-ld -r $STMP /object_files/musl_objects_armv7.o $LIBGCC -o $DEST/stencils_armv7_$OPT.o
# ARMv7 Thumb for Cortex-A with hardware fp
arm-none-eabi-gcc -march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -mthumb $FLAGS -$OPT -c $SRC -o $STMP
LIBGCC=$(arm-none-eabi-gcc -march=armv7 -mfpu=vfp3 -mthumb -print-libgcc-file-name)
arm-none-eabi-ld -r $STMP /object_files/musl_objects_armv7thumb.o $LIBGCC -o $DEST/stencils_armv7thumb_$OPT.o
# Armv7 Thumb for Cortex-M3..7 hardware fp
arm-none-eabi-gcc -march=armv7e-m -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb $FLAGS -$OPT -c $SRC -o $STMP
LIBGCC=$(arm-none-eabi-gcc -march=armv7e-m -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mthumb -print-libgcc-file-name)
arm-none-eabi-ld -r $STMP /object_files/musl_objects_armv7mthumb.o $LIBGCC -o $DEST/stencils_armv7mthumb_$OPT.o
LIBGCC=$(arm-none-eabi-gcc -print-libgcc-file-name)
arm-none-eabi-ld -r $STMP /object_files/musl_objects_armv7thumb.o $LIBGCC -o $DEST/stencils_armv7thumb_$OPT.o
# PowerPC64LE
# powerpc64le-linux-gnu-gcc-13 $FLAGS -$OPT -c $SRC -o $DEST/stencils_ppc64le_$OPT.o

View File

@ -14,5 +14,3 @@ objdump -D -b binary -m i386:x86-64 --adjust-vma=0x1000 build/runner/test.copapy
build/runner/coparun-armv7 build/runner/test-armv7.copapy build/runner/test.copapy-armv7.bin
arm-none-eabi-objdump -D -b binary -marm --adjust-vma=0x50000 build/runner/test.copapy-armv7.bin > build/runner/test.copapy-armv7.asm
# arm-none-eabi-objdump -D -b binary -marm -M force-thumb --adjust-vma=0x50001 build/runner/test.copapy-armv7thumb.bin > build/runner/test.copapy-armv7thumb.asm

View File

@ -1,21 +0,0 @@
#!/bin/bash
# Build arm-v7 runner and stencils
bash tools/build.sh arm-v7
# Build arm-v7-thumb stencils
bash tools/build.sh arm-v7-thumb
# Build arm-v7-thumb example code
export CP_TARGET_ARCH=armv7thumb
python3 tools/make_example.py
build/runner/coparun-armv7 build/runner/test.copapy build/runner/test.copapy.bin
arm-none-eabi-objdump -D -b binary -marm -M force-thumb --adjust-vma=0x1000000 build/runner/test.copapy.bin > build/runner/test.copapy-example-armv7thumb.asm
# Build arm-v7-thumb example code
export CP_TARGET_ARCH=armv7
python3 tools/make_example.py
build/runner/coparun-armv7 build/runner/test.copapy build/runner/test.copapy.bin
arm-none-eabi-objdump -D -b binary -marm --adjust-vma=0x1000000 build/runner/test.copapy.bin > build/runner/test.copapy-example-armv7.asm

View File

@ -1,6 +0,0 @@
#!/bin/bash
bash tools/build.sh arm-v7-thumb
python tests/test_ops_armv7thumb.py
qemu-arm -d in_asm -D qemu.log build/runner/coparun-armv7thumb build/runner/test-armv7thumb.copapy build/runner/test.copapy-armv7thumb.bin
arm-none-eabi-objdump -D -b binary -marm -M force-thumb --adjust-vma=0xff7ed000 build/runner/test.copapy-armv7thumb.bin > build/runner/test.copapy-armv7thumb.asm