Compare commits

...

32 Commits

Author SHA1 Message Date
Nicolas Kruse ee771e3ac1
Merge pull request #32 from Nonannet/dev
CI: Updated dependency for build-docs updated to insure arm thumb is included in the docs
2026-03-04 16:25:56 +01:00
Nicolas 548ca6fab8 CI: Updated dependency for build-docs updated to insure arm thumb is included in the docs 2026-03-04 16:03:31 +01:00
Nicolas Kruse 58f796513c
Merge pull request #31 from Nonannet/dev
- ARM Thumb suport for Cortex-A and Cortex-M
2026-03-04 15:29:05 +01:00
Nicolas Kruse 5ca947ea5b
Merge pull request #30 from Nonannet/feature_arm_thumb
Feature: Adding support for ARM thumb
2026-03-04 15:17:50 +01:00
Nicolas 01f02cc9ba Readme updated 2026-03-04 15:16:51 +01:00
Nicolas 031249241e Removed un-required bulk stack allocation and added 4 Byte alignment check. CI updated. 2026-03-04 14:58:52 +01:00
Nicolas e48fc7c485 test for arm thumb variants split into two tests 2026-03-03 16:09:06 +01:00
Nicolas c6fd69d61b CI: different armv7 variates separated 2026-03-03 15:50:48 +01:00
Nicolas b7d5f3a129 exclude non-stencil functions in the test "test_start_end_function" 2026-03-03 15:05:45 +01:00
Nicolas 0212fa77a3 type annotation for data_writer.copy() fixed 2026-03-03 14:25:50 +01:00
Nicolas c31601853b CI: LIBGCC version fixed for thumb builds 2026-03-03 13:31:22 +01:00
Nicolas 04cdf50a04 Updated pelfy dependency 2026-03-03 13:24:46 +01:00
Nicolas 7a3088ec48 added add_sign_int32 function, since pelfy returns addend-values for 32 bit x86 as unsigned int32 2026-03-03 13:05:47 +01:00
Nicolas Kruse a924d42e6a
Merge branch 'dev' into feature_arm_thumb 2026-03-03 08:59:22 +01:00
Nicolas 7f963d7e43 CI: Testing for ARMv7 extended to armv7thumb and armv7mthumb 2026-03-02 21:33:24 +01:00
Nicolas dd7fb12c64 Helper bash script added for debugging ARM thumb stencils 2026-03-02 21:32:45 +01:00
Nicolas d2069d5d07 build script for local stencil builds updated for ARM64, ARM-Thumb and ARM-CortexM-Thumb 2026-03-02 21:32:12 +01:00
Nicolas accb03f042 Fix in test function "get_42" 2026-03-02 21:31:07 +01:00
Nicolas 2eb49cc2e5 test for ARM thumb updated 2026-03-02 21:29:46 +01:00
Nicolas c7c8db6332 R_ARM_THM_MOV* support added 2026-03-02 21:28:46 +01:00
Nicolas 8fcf0dedac patch type added: PATCH_OBJECT_ARM32_ABS_THM (for R_ARM_THM_MOVW_ABS_NC and R_ARM_THM_MOVT_ABS) 2026-03-02 21:28:05 +01:00
Nicolas afc442ada6 stencil build script updated 2026-02-28 22:09:12 +01:00
Nicolas 436a09c1ea copy method to data_writer added 2026-02-28 22:08:22 +01:00
Nicolas cabfda4ec6 CI: stencil build script updated with Cortex-A Thumb version 2026-02-28 22:07:27 +01:00
Nicolas 83ce6ce0e7 musl functions for math on ARM thumb added to stencil build pipeline 2026-02-13 01:13:01 +01:00
Nicolas a81236a3fc 4-Byte-Alignment error on ARM thumb fixed by using section size instead of function size to include nop padding 2026-02-13 00:51:55 +01:00
Nicolas bc0ccd90b7 Updated pelfy and using offset_in_section instead of fields['st_value'] 2026-02-12 23:54:20 +01:00
Nicolas Kruse e52cbe9e1b docstrings added and descriptions for c files added 2026-02-05 14:26:07 +01:00
Nicolas 58120f292c partial arm thumb implementation added 2026-01-26 23:57:15 +01:00
Nicolas 7131483a22 CI: Added -static for building the runner 2026-01-25 17:16:33 +01:00
Nicolas 3ec0ba10c3 Skiping qemo based ARM32 bit tests on windows since they are not
compatible to wsl1
2026-01-25 17:13:01 +01:00
Nicolas Kruse d394b2d249 add_read_value_remote backend function renamed and docstring updated 2026-01-12 16:57:54 +01:00
38 changed files with 1169 additions and 248 deletions

View File

@ -104,7 +104,7 @@ jobs:
- name: Compile coparun - name: Compile coparun
run: | run: |
mkdir -p build/runner mkdir -p build/runner
gcc -O3 -DENABLE_BASIC_LOGGING -o build/runner/coparun src/coparun/runmem.c src/coparun/coparun.c src/coparun/mem_man.c gcc -O3 -static -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 - name: Generate debug asm files
if: strategy.job-index == 0 if: strategy.job-index == 0
@ -151,9 +151,9 @@ jobs:
- name: Use ARM64 container - name: Use ARM64 container
run: | run: |
docker run --rm -v $PWD:/app -w /app --platform linux/arm64 ghcr.io/nonannet/arm64_test:1 \ docker run --rm -v $PWD:/app -w /app --platform linux/arm64 ghcr.io/nonannet/arm64_test:1 \
bash -lc "pip install . && \ bash -lc "pip install .[mindev] && \
mkdir -p build/runner && \ mkdir -p build/runner && \
gcc -O3 -DENABLE_LOGGING -o build/runner/coparun src/coparun/runmem.c \ gcc -O3 -static -DENABLE_LOGGING -o build/runner/coparun src/coparun/runmem.c \
src/coparun/coparun.c src/coparun/mem_man.c && \ src/coparun/coparun.c src/coparun/mem_man.c && \
pytest && \ pytest && \
bash tools/create_asm.sh" bash tools/create_asm.sh"
@ -180,9 +180,10 @@ jobs:
- name: Use ARMv6 container - name: Use ARMv6 container
run: | run: |
docker run --rm -v $PWD:/app -w /app --platform linux/arm/v6 ghcr.io/nonannet/armv6_test:1 \ docker run --rm -v $PWD:/app -w /app --platform linux/arm/v6 ghcr.io/nonannet/armv6_test:1 \
bash -lc "pip install . && \ bash -lc "set -x && \
pip install .[mindev] && \
mkdir -p build/runner && \ mkdir -p build/runner && \
gcc -O3 -DENABLE_LOGGING -o build/runner/coparun src/coparun/runmem.c \ gcc -O3 -static -DENABLE_LOGGING -o build/runner/coparun src/coparun/runmem.c \
src/coparun/coparun.c src/coparun/mem_man.c && \ src/coparun/coparun.c src/coparun/mem_man.c && \
pytest && \ pytest && \
bash tools/create_asm.sh" bash tools/create_asm.sh"
@ -209,9 +210,14 @@ jobs:
- name: Use ARMv7 container - name: Use ARMv7 container
run: | run: |
docker run --rm -v $PWD:/app -w /app --platform linux/arm/v7 ghcr.io/nonannet/armv7_test:1 \ docker run --rm -v $PWD:/app -w /app --platform linux/arm/v7 ghcr.io/nonannet/armv7_test:1 \
bash -lc "pip install . && \ bash -lc "set -x && \
pip install .[mindev] && \
mkdir -p build/runner && \ mkdir -p build/runner && \
gcc -O3 -DENABLE_LOGGING -o build/runner/coparun src/coparun/runmem.c \ 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 && \ src/coparun/coparun.c src/coparun/mem_man.c && \
pytest && \ pytest && \
bash tools/create_asm.sh" bash tools/create_asm.sh"
@ -221,6 +227,76 @@ jobs:
name: runner-linux-armv7 name: runner-linux-armv7
path: build/runner/* 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: build-windows:
needs: [build_stencils] needs: [build_stencils]
runs-on: windows-latest runs-on: windows-latest
@ -244,7 +320,7 @@ jobs:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Install Python dependencies - name: Install Python dependencies
run: python -m pip install .[dev] run: python -m pip install .[mindev]
- name: Set up MSVC environment - name: Set up MSVC environment
uses: microsoft/setup-msbuild@v2 uses: microsoft/setup-msbuild@v2
@ -270,7 +346,7 @@ jobs:
path: build/runner/* path: build/runner/*
release-stencils: release-stencils:
needs: [build_stencils, build-ubuntu, build-windows, build-arm64, build-armv6, build-armv7] needs: [build_stencils, build-ubuntu, build-windows, build-arm64, build-armv6, build-armv7, build-armv7thumb, build-armv7mthumb]
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push' if: github.ref == 'refs/heads/main' && github.event_name == 'push'
permissions: permissions:
@ -319,7 +395,7 @@ jobs:
fi fi
build-docs: build-docs:
needs: [build_stencils, build-ubuntu, build-windows, build-arm64, build-armv6, build-armv7] needs: [build_stencils, build-ubuntu, build-windows, build-arm64, build-armv6, build-armv7, build-armv7thumb, build-armv7mthumb]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4

1
.gitignore vendored
View File

@ -30,3 +30,4 @@ core
*.log *.log
docs/source/start.md docs/source/start.md
/src/copapy/_version.py /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 - Memory and type safety with a minimal set of runtime errors
- Deterministic execution - Deterministic execution
- Automatic differentiation for efficient realtime optimization (reverse-mode) - Automatic differentiation for efficient realtime optimization (reverse-mode)
- Optimized machine code for x86_64, ARMv6, ARMv7 and AArch64 - Optimized machine code for x86_64, 32 Bit ARM (Cortex-A and Cortex-M) and AArch64
- Highly portable to new architectures - Highly portable to new architectures
- Small Python package with minimal dependencies and no cross-compile toolchain required - Small Python package with minimal dependencies and no cross-compile toolchain required
@ -31,7 +31,6 @@ While hardware I/O is obviously a core aspect of the project, it is not yet avai
Currently in development: Currently in development:
- Array stencils for handling very large arrays and generating SIMD-optimized code - e.g., for machine vision and neural network applications - 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 - 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: Despite missing SIMD-optimization, benchmark performance shows promising numbers. The following chart plots the results in comparison to NumPy 2.3.5:
@ -253,4 +252,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. [^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 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. [^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.

View File

@ -45,14 +45,18 @@ dev = [
"ruff", "ruff",
"mypy", "mypy",
"pytest", "pytest",
"pelfy>=1.0.7" "pelfy>=1.0.8"
]
mindev = [
"pytest",
"pelfy>=1.0.8"
] ]
doc_build = [ doc_build = [
"sphinx", "sphinx",
"pydata_sphinx_theme", "pydata_sphinx_theme",
"sphinx-autodoc-typehints", "sphinx-autodoc-typehints",
"myst-parser", "myst-parser",
"pelfy>=1.0.7" "pelfy>=1.0.8"
] ]
[tool.mypy] [tool.mypy]

View File

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

View File

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

View File

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

View File

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

View File

@ -13,7 +13,15 @@ TRet = TypeVar("TRet", Iterable[int | float], int, float)
_jit_cache: dict[Any, tuple['Target', tuple[value[Any] | Iterable[value[Any]], ...], NumLike | Iterable[NumLike]]] = {} _jit_cache: dict[Any, tuple['Target', tuple[value[Any] | Iterable[value[Any]], ...], NumLike | Iterable[NumLike]]] = {}
def add_read_command(dw: binw.data_writer, variables: dict[Net, tuple[int, int, str]], net: Net) -> None: 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.
"""
assert net in variables, f"Variable {net} not found in data writer variables" assert net in variables, f"Variable {net} not found in data writer variables"
addr, lengths, _ = variables[net] addr, lengths, _ = variables[net]
dw.write_com(binw.Command.READ_DATA) dw.write_com(binw.Command.READ_DATA)
@ -188,5 +196,5 @@ class Target():
def read_value_remote(self, variable: value[Any]) -> None: def read_value_remote(self, variable: value[Any]) -> None:
"""Reads the raw data of a value by the runner.""" """Reads the raw data of a value by the runner."""
dw = binw.data_writer(self.sdb.byteorder) dw = binw.data_writer(self.sdb.byteorder)
add_read_command(dw, self._values, variable.net) add_read_value_remote(dw, self._values, variable.net)
assert coparun(self._context, dw.get_data()) > 0 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. and give access to compiler internals and debugging tools.
""" """
from ._target import add_read_command from ._target import add_read_value_remote
from ._basic_types import Net, Op, Node, CPConstant, Store, stencil_db_from_package from ._basic_types import Net, Op, Node, CPConstant, Store, stencil_db_from_package
from ._compiler import compile_to_dag, \ from ._compiler import compile_to_dag, \
stable_toposort, get_const_nets, get_all_dag_edges, add_load_ops, get_all_dag_edges_between, \ stable_toposort, get_const_nets, get_all_dag_edges, add_load_ops, get_all_dag_edges_between, \
add_store_ops, get_dag_stats add_store_ops, get_dag_stats
__all__ = [ __all__ = [
"add_read_command", "add_read_value_remote",
"Net", "Net",
"Op", "Op",
"Node", "Node",

View File

@ -1,3 +1,15 @@
/*
* 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 <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "runmem.h" #include "runmem.h"

View File

@ -1,3 +1,10 @@
/*
* 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 #define PY_SSIZE_T_CLEAN
#include <Python.h> #include <Python.h>
#include "runmem.h" #include "runmem.h"

View File

@ -1,3 +1,11 @@
/*
* 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 <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>

View File

@ -1,3 +1,10 @@
/*
* 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 <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -50,6 +57,67 @@ void patch_arm32_abs(uint8_t *patch_addr, uint32_t imm16)
*((uint32_t *)patch_addr) = instr; *((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) { void free_memory(runmem_t *context) {
deallocate_memory(context->executable_memory, context->executable_memory_len); deallocate_memory(context->executable_memory, context->executable_memory_len);
deallocate_memory(context->data_memory, context->data_memory_len); deallocate_memory(context->data_memory, context->data_memory_len);
@ -180,6 +248,26 @@ 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); patch_arm32_abs(context->executable_memory + offs, (uint32_t)((uintptr_t)(context->data_memory + value) & patch_mask) / (uint32_t)patch_scale);
break; 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: case ENTRY_POINT:
rel_entr_point = *(uint32_t*)bytes; bytes += 4; rel_entr_point = *(uint32_t*)bytes; bytes += 4;
context->entr_point = (entry_point_t)(context->executable_memory + rel_entr_point); context->entr_point = (entry_point_t)(context->executable_memory + rel_entr_point);

View File

@ -1,3 +1,10 @@
/**
* @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 #ifndef RUNMEM_H
#define RUNMEM_H #define RUNMEM_H
@ -20,11 +27,13 @@
#define ALLOCATE_CODE 3 #define ALLOCATE_CODE 3
#define COPY_CODE 4 #define COPY_CODE 4
#define PATCH_FUNC 0x1000 #define PATCH_FUNC 0x1000
#define PATCH_FUNC_ARM32_THM 0x1005
#define PATCH_OBJECT 0x2000 #define PATCH_OBJECT 0x2000
#define PATCH_OBJECT_HI21 0x2001 #define PATCH_OBJECT_HI21 0x2001
#define PATCH_OBJECT_ABS 0x2002 #define PATCH_OBJECT_ABS 0x2002
#define PATCH_OBJECT_REL 0x2003 #define PATCH_OBJECT_REL 0x2003
#define PATCH_OBJECT_ARM32_ABS 0x2004 #define PATCH_OBJECT_ARM32_ABS 0x2004
#define PATCH_OBJECT_ARM32_ABS_THM 0x2006
#define ENTRY_POINT 7 #define ENTRY_POINT 7
#define RUN_PROG 64 #define RUN_PROG 64
#define READ_DATA 65 #define READ_DATA 65

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,174 @@
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

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

View File

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

View File

@ -1,13 +1,13 @@
#!/bin/bash #!/bin/bash
set -eux set -eu
ARCH=${1:-x86_64} ARCH=${1:-x86_64}
case "$ARCH" in case "$ARCH" in
(x86_64|arm-v6|arm-v7|all) (x86_64|arm64|arm-v6|arm-v7|arm-v7-thumb|arm-v7m-thumb|all)
;; ;;
(*) (*)
echo "Usage: $0 [x86_64|arm-v6|arm-v7|all]" echo "Usage: $0 [x86_64|arm64|arm-v6|arm-v7|arm-v6-thumb|arm-v7m-thumb|all]"
exit 1 exit 1
;; ;;
esac esac
@ -42,13 +42,44 @@ if [[ "$ARCH" == "x86_64" || "$ARCH" == "all" ]]; then
-o build/runner/coparun -o build/runner/coparun
fi 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 # ARM v6
####################################### #######################################
if [[ "$ARCH" == "arm-v6" || "$ARCH" == "all" ]]; then if [[ "$ARCH" == "arm-v6" || "$ARCH" == "all" ]]; then
echo "--------------arm-v6 32 bit----------------" echo "--------------arm-v6 32 bit----------------"
LIBGCC=$(arm-none-eabi-gcc -print-libgcc-file-name) LIBGCC=$(arm-none-eabi-gcc -march=armv6 -mfpu=vfp -mfloat-abi=hard -marm -print-libgcc-file-name)
arm-none-eabi-gcc -fno-pic -ffunction-sections \ arm-none-eabi-gcc -fno-pic -ffunction-sections \
-march=armv6 -mfpu=vfp -mfloat-abi=hard -marm \ -march=armv6 -mfpu=vfp -mfloat-abi=hard -marm \
@ -81,7 +112,7 @@ fi
if [[ "$ARCH" == "arm-v7" || "$ARCH" == "all" ]]; then if [[ "$ARCH" == "arm-v7" || "$ARCH" == "all" ]]; then
echo "--------------arm-v7 32 bit----------------" echo "--------------arm-v7 32 bit----------------"
LIBGCC=$(arm-none-eabi-gcc -print-libgcc-file-name) LIBGCC=$(arm-none-eabi-gcc -march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -marm -print-libgcc-file-name)
arm-none-eabi-gcc -fno-pic -ffunction-sections \ arm-none-eabi-gcc -fno-pic -ffunction-sections \
-march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -marm \ -march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -marm \
@ -97,6 +128,7 @@ if [[ "$ARCH" == "arm-v7" || "$ARCH" == "all" ]]; then
$DEST/stencils_armv7_O3.o \ $DEST/stencils_armv7_O3.o \
> build/stencils/stencils_armv7_O3.asm > build/stencils/stencils_armv7_O3.asm
# The same runner for all ARM7
arm-linux-gnueabihf-gcc \ arm-linux-gnueabihf-gcc \
-march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -marm -static \ -march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -marm -static \
-Wall -Wextra -Wconversion -Wsign-conversion \ -Wall -Wextra -Wconversion -Wsign-conversion \
@ -107,3 +139,71 @@ if [[ "$ARCH" == "arm-v7" || "$ARCH" == "all" ]]; then
src/coparun/mem_man.c \ src/coparun/mem_man.c \
-o build/runner/coparun-armv7 -o build/runner/coparun-armv7
fi 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,7 +10,6 @@ cparch=$(python3 -c "import copapy; print(copapy._stencils.detect_process_arch()
# Disassemble stencil object file # Disassemble stencil object file
objdump -d -x src/copapy/obj/stencils_${cparch}_O3.o > build/runner/stencils.asm objdump -d -x src/copapy/obj/stencils_${cparch}_O3.o > build/runner/stencils.asm
# Create example code disassembly
python3 tools/make_example.py python3 tools/make_example.py
build/runner/coparun build/runner/test.copapy build/runner/test.copapy.bin build/runner/coparun build/runner/test.copapy build/runner/test.copapy.bin
@ -28,6 +27,10 @@ fi
echo "Archtitecture: '$cparch'" echo "Archtitecture: '$cparch'"
objdump -D -b binary -m $cparch --adjust-vma=0x10000 build/runner/test.copapy.bin > build/runner/example.asm 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
rm build/runner/test.copapy.bin rm build/runner/test.copapy.bin

View File

@ -26,8 +26,11 @@ sh ../packobjs.sh arm-none-eabi-gcc arm-none-eabi-ld /object_files/musl_objects_
# Armv7 # 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" 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 # Armv7 Thumb for Cortex-M3..7
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 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 mips mips-linux-gnu-gcc-13 mips-linux-gnu-ld #sh ../packobjs.sh mips mips-linux-gnu-gcc-13 mips-linux-gnu-ld

View File

@ -25,14 +25,16 @@ 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 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 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 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_invalidf.o __stack_chk_fail.o __math_divzerof.o __math_oflowf.o __rem_pio2_large.o __math_uflowf.o __math_xflowf.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
# Check out .lo (PIC) # 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 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 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 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 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_invalidf.lo __stack_chk_fail.lo __math_divzerof.lo __math_oflowf.lo __rem_pio2_large.lo __math_uflowf.lo __math_xflowf.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
cd ../../musl cd ../../musl

View File

@ -36,15 +36,20 @@ arm-none-eabi-gcc -march=armv6 -mfpu=vfp -mfloat-abi=hard -marm $FLAGS -$OPT -c
LIBGCC=$(arm-none-eabi-gcc -print-libgcc-file-name) 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 arm-none-eabi-ld -r $STMP /object_files/musl_objects_armv6.o $LIBGCC -o $DEST/stencils_armv6_$OPT.o
# ARMv7 hardware fp # ARMv7 hardware fp for Cortex-A
arm-none-eabi-gcc -march=armv7-a -mfpu=neon-vfpv3 -mfloat-abi=hard -marm $FLAGS -$OPT -c $SRC -o $STMP 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) 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 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 # 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 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 -print-libgcc-file-name) 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_armv7thumb.o $LIBGCC -o $DEST/stencils_armv7thumb_$OPT.o arm-none-eabi-ld -r $STMP /object_files/musl_objects_armv7mthumb.o $LIBGCC -o $DEST/stencils_armv7mthumb_$OPT.o
# PowerPC64LE # PowerPC64LE
# powerpc64le-linux-gnu-gcc-13 $FLAGS -$OPT -c $SRC -o $DEST/stencils_ppc64le_$OPT.o # powerpc64le-linux-gnu-gcc-13 $FLAGS -$OPT -c $SRC -o $DEST/stencils_ppc64le_$OPT.o

View File

@ -14,3 +14,5 @@ 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 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 --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

21
tools/test_example_code.sh Executable file
View File

@ -0,0 +1,21 @@
#!/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

6
tools/test_thumb_stancils.sh Executable file
View File

@ -0,0 +1,6 @@
#!/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