forked from M-Labs/nac3
Compare commits
202 Commits
099971da65
...
44f06a88f5
| Author | SHA1 | Date | |
|---|---|---|---|
| 44f06a88f5 | |||
| c3e2ce5576 | |||
|
|
bf900922e2 | ||
|
|
19a841a661 | ||
|
|
ce3c24485a | ||
|
|
cfee88277d | ||
|
|
4c5bf5dbc2 | ||
|
|
7cb171ebe4 | ||
|
|
bc6ffe66a1 | ||
|
|
9384447bb7 | ||
|
|
4831ccc230 | ||
|
|
e137cdb22f | ||
|
|
73c9f6ea2b | ||
|
|
a25bec7f3e | ||
|
|
9584a93494 | ||
| 9614ab5f57 | |||
| f722575332 | |||
| 2472f35c6d | |||
| c6ca355147 | |||
| 9ddd7272dc | |||
| 696ad06683 | |||
| f8bb6311fb | |||
| fa7313d562 | |||
| 2073dd7108 | |||
| d1afa99127 | |||
| f93e8c76a0 | |||
| 722ec433ac | |||
| 72f4dccbde | |||
| a1d74ec321 | |||
| 9657818cf7 | |||
| 9578729076 | |||
| da3dec1c2a | |||
| d7c11e588f | |||
| 1f7ce27466 | |||
| 747990f9df | |||
| 5b0e348b8a | |||
| 65b9873aa4 | |||
| f1e7886832 | |||
| 310318d37c | |||
| 1684949871 | |||
| ca3f99d262 | |||
| 90596e429b | |||
| 5e4ecb67d2 | |||
| 5ecd5b285d | |||
| f63058da06 | |||
| 532b47591c | |||
| 3cfc618abb | |||
| cac6d5c6fb | |||
| 809f7df6ef | |||
| a4e6c3ff1f | |||
| ebdec61396 | |||
| 8c8067feb4 | |||
| 6811ea29a7 | |||
| 0ee5eaf0b9 | |||
| 183ddbcc81 | |||
| 301183eaf4 | |||
| 1465335692 | |||
| db6336257b | |||
| 8ab8827ce5 | |||
| c41b19fa4c | |||
| e05f8cdc53 | |||
| c102fb87ff | |||
| b7d324e501 | |||
| 7e8fde6443 | |||
| b6fae43ed3 | |||
| d4df169c27 | |||
| b0d2c5529c | |||
| 3c9d11e04d | |||
| d8bb3aa306 | |||
| b601bb6051 | |||
| b25c760332 | |||
| 9051fc78aa | |||
| 9e41f16328 | |||
| 78c3c8c26f | |||
| 1eaa724f31 | |||
| d426fd50a1 | |||
| 245cd7c246 | |||
| 6bcc5b3529 | |||
| 5bb16da035 | |||
| 7f7f15793c | |||
| db31ddce2c | |||
| 085efc3714 | |||
| 8f4e575f8a | |||
| 36cb6ec8b4 | |||
| 1c1f33fe83 | |||
| 7dad4dbb39 | |||
|
|
abaa96b5ec | ||
| 4f19a5e0a7 | |||
|
|
a63401893c | ||
| b4fc845571 | |||
| ef7ed97924 | |||
|
|
9d6eaa0dff | ||
|
|
49f803307c | ||
|
|
1896ea06a6 | ||
|
|
1b681b2304 | ||
|
|
64e7486a63 | ||
|
|
adc83a2d9b | ||
|
|
114d1b26bf | ||
|
|
837d464242 | ||
| 7ec429d68f | |||
| d6aa17cb2a | |||
| 3e6592ca97 | |||
| d6fa96bfab | |||
| 844b7eeb8d | |||
| 9445c71156 | |||
| 4acb896a18 | |||
| e60bf48bc0 | |||
| 37be9cd774 | |||
| 96fe87d3cc | |||
| 84e18b4b17 | |||
| a9c5e8796c | |||
|
|
58b90626a7 | ||
| e45a260ac0 | |||
| f2aff0aee0 | |||
| 40df7e1c24 | |||
| 5c83d560cc | |||
| b77196fac7 | |||
| e4723d91af | |||
| 16e8dcac6e | |||
| b5d73f0c3e | |||
| 070efdc3ab | |||
| 39e3e65525 | |||
| fd7453fdb3 | |||
| a04a699f41 | |||
| c1ddf4fe57 | |||
|
|
418b45b618 | ||
|
|
6ddac5c4b0 | ||
|
|
ed30a83b08 | ||
|
|
793c4ebca3 | ||
|
|
b499a2da0f | ||
|
|
9e8239ff60 | ||
| 1f2ed83a98 | |||
| bf1518747c | |||
| b4ab037e00 | |||
|
|
95eea9549e | ||
|
|
41579384e3 | ||
|
|
4d637981af | ||
|
|
46fec4da09 | ||
| be4470539f | |||
| e00be95066 | |||
| c95e8273d6 | |||
| 8aa614bd50 | |||
| 2ea9771d3d | |||
| 77f37cfec9 | |||
| bd4c21a677 | |||
| c869c7cee4 | |||
| edec5b4e73 | |||
| d8b442aa5c | |||
| 9624890f58 | |||
| 66427b0695 | |||
| 011fa9c86f | |||
| 25b85ab844 | |||
| 7b607666e4 | |||
| e3c0fbee86 | |||
| 0c77d6e2a7 | |||
| d8cde63987 | |||
| b2141103ea | |||
|
|
6f72347616 | ||
| 83358d7d7c | |||
| 96406541d1 | |||
| 2e930db6ee | |||
| b66f97db07 | |||
| cf8d3af11e | |||
| b4e3d67f22 | |||
| ecb7256e42 | |||
| 5b2bb5b432 | |||
| 39ba8417b9 | |||
| 91e5a31f72 | |||
| 3b6fbba0f7 | |||
| e2384130ae | |||
| 12e51e95be | |||
| 425fdf81a7 | |||
| cac98e1009 | |||
| 050269b94d | |||
| 11fbbdb173 | |||
| 4a946c5d66 | |||
| 50f35d3073 | |||
| 4e778095ed | |||
| 6d48b0899e | |||
| 1fee1194db | |||
| 22f4ecb3c2 | |||
| 506fa61eea | |||
| 91f9f7dc65 | |||
| c1f7efbc60 | |||
| 34554f31b6 | |||
| 621b8f65b9 | |||
| e8a56800f4 | |||
| 7b26dacc6d | |||
| 5990bfb50a | |||
| 797a2dbbad | |||
| ac6d7b7496 | |||
| ad668122b3 | |||
| 98aab1c0d9 | |||
| cb4df67008 | |||
| f70456a805 | |||
| 32cffc8864 | |||
| ef113d197a | |||
| 6cc230b3f5 | |||
| 893e2a1e5b | |||
| 768560f893 | |||
| 16c65dca89 | |||
| 7235012a0d |
651
Cargo.lock
generated
651
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"nac3ld",
|
||||
"nac3binutils",
|
||||
"nac3ast",
|
||||
"nac3parser",
|
||||
"nac3core",
|
||||
@@ -9,7 +9,7 @@ members = [
|
||||
"nac3artiq",
|
||||
"runkernel",
|
||||
]
|
||||
resolver = "2"
|
||||
resolver = "3"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
|
||||
@@ -15,7 +15,7 @@ NAC3 has a modular design and its applicability reaches beyond ARTIQ. The ``nac3
|
||||
|
||||
NAC3 is packaged using the [Nix](https://nixos.org) Flakes system. Enable flakes by adding ``experimental-features = nix-command flakes`` to ``nix.conf`` (e.g. ``~/.config/nix/nix.conf``).
|
||||
|
||||
Use ``nix shell git+https://github.com/m-labs/artiq.git?ref=nac3`` to get a shell with the NAC3 version of ARTIQ. See the ``examples`` directory in ARTIQ (``nac3`` Git branch) for some samples of NAC3 kernel code.
|
||||
Use ``nix shell git+https://git.m-labs.hk/M-Labs/artiq.git?ref=nac3`` to get a shell with the NAC3 version of ARTIQ. See the ``examples`` directory in ARTIQ (``nac3`` Git branch) for some samples of NAC3 kernel code.
|
||||
|
||||
### Windows
|
||||
|
||||
@@ -52,7 +52,7 @@ This repository contains:
|
||||
- ``nac3parser``: Python parser (based on RustPython).
|
||||
- ``nac3core``: Core compiler library, containing type-checking and code generation.
|
||||
- ``nac3standalone``: Standalone compiler tool (core language only).
|
||||
- ``nac3ld``: Minimalist RISC-V and ARM linker.
|
||||
- ``nac3binutils``: Contains binary tools (linker, symbolizer, etc.)
|
||||
- ``nac3artiq``: Integration with ARTIQ and implementation of ARTIQ-specific extensions to the core language.
|
||||
- ``runkernel``: Simple program that runs compiled ARTIQ kernels on the host and displays RTIO operations. Useful for testing without hardware.
|
||||
|
||||
|
||||
6
flake.lock
generated
6
flake.lock
generated
@@ -2,11 +2,11 @@
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1753694789,
|
||||
"narHash": "sha256-cKgvtz6fKuK1Xr5LQW/zOUiAC0oSQoA9nOISB0pJZqM=",
|
||||
"lastModified": 1757347588,
|
||||
"narHash": "sha256-tLdkkC6XnsY9EOZW9TlpesTclELy8W7lL2ClL+nma8o=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "dc9637876d0dcc8c9e5e22986b857632effeb727",
|
||||
"rev": "b599843bad24621dcaa5ab60dac98f9b0eb1cabe",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
||||
387
flake.nix
387
flake.nix
@@ -3,207 +3,208 @@
|
||||
|
||||
inputs.nixpkgs.url = github:NixOS/nixpkgs/nixos-unstable;
|
||||
|
||||
outputs = { self, nixpkgs }:
|
||||
let
|
||||
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||
pkgs32 = import nixpkgs { system = "i686-linux"; };
|
||||
in rec {
|
||||
packages.x86_64-linux = rec {
|
||||
llvm-nac3 = pkgs.callPackage ./nix/llvm {};
|
||||
llvm-tools-irrt = pkgs.runCommandNoCC "llvm-tools-irrt" {}
|
||||
''
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
}: let
|
||||
pkgs = import nixpkgs {system = "x86_64-linux";};
|
||||
pkgs32 = import nixpkgs {system = "i686-linux";};
|
||||
in rec {
|
||||
packages.x86_64-linux = rec {
|
||||
llvm-nac3 = pkgs.callPackage ./nix/llvm {};
|
||||
llvm-tools-irrt =
|
||||
pkgs.runCommandNoCC "llvm-tools-irrt" {}
|
||||
''
|
||||
mkdir -p $out/bin
|
||||
ln -s ${pkgs.llvmPackages_16.clang-unwrapped}/bin/clang $out/bin/clang-irrt
|
||||
ln -s ${pkgs.llvmPackages_16.llvm.out}/bin/llvm-as $out/bin/llvm-as-irrt
|
||||
'';
|
||||
demo-linalg-stub = pkgs.rustPlatform.buildRustPackage {
|
||||
name = "demo-linalg-stub";
|
||||
src = ./nac3standalone/demo/linalg;
|
||||
'';
|
||||
demo-linalg-stub = pkgs.rustPlatform.buildRustPackage {
|
||||
name = "demo-linalg-stub";
|
||||
src = ./nac3standalone/demo/linalg;
|
||||
cargoLock = {
|
||||
lockFile = ./nac3standalone/demo/linalg/Cargo.lock;
|
||||
};
|
||||
doCheck = false;
|
||||
};
|
||||
demo-linalg-stub32 = pkgs32.rustPlatform.buildRustPackage {
|
||||
name = "demo-linalg-stub32";
|
||||
src = ./nac3standalone/demo/linalg;
|
||||
cargoLock = {
|
||||
lockFile = ./nac3standalone/demo/linalg/Cargo.lock;
|
||||
};
|
||||
doCheck = false;
|
||||
};
|
||||
nac3artiq = pkgs.python3Packages.toPythonModule (
|
||||
pkgs.rustPlatform.buildRustPackage rec {
|
||||
name = "nac3artiq";
|
||||
outputs = ["out" "runkernel" "standalone"];
|
||||
src = self;
|
||||
cargoLock = {
|
||||
lockFile = ./nac3standalone/demo/linalg/Cargo.lock;
|
||||
lockFile = ./Cargo.lock;
|
||||
};
|
||||
doCheck = false;
|
||||
};
|
||||
demo-linalg-stub32 = pkgs32.rustPlatform.buildRustPackage {
|
||||
name = "demo-linalg-stub32";
|
||||
src = ./nac3standalone/demo/linalg;
|
||||
cargoLock = {
|
||||
lockFile = ./nac3standalone/demo/linalg/Cargo.lock;
|
||||
};
|
||||
doCheck = false;
|
||||
};
|
||||
nac3artiq = pkgs.python3Packages.toPythonModule (
|
||||
pkgs.rustPlatform.buildRustPackage rec {
|
||||
name = "nac3artiq";
|
||||
outputs = [ "out" "runkernel" "standalone" ];
|
||||
src = self;
|
||||
cargoLock = {
|
||||
lockFile = ./Cargo.lock;
|
||||
};
|
||||
passthru.cargoLock = cargoLock;
|
||||
nativeBuildInputs = [ pkgs.python3 (pkgs.wrapClangMulti pkgs.llvmPackages_16.clang) llvm-tools-irrt pkgs.llvmPackages_16.llvm.out pkgs.llvmPackages_16.bintools llvm-nac3 ];
|
||||
buildInputs = [ pkgs.python3 llvm-nac3 pkgs.stdenv.cc.cc.lib ];
|
||||
checkInputs = [ (pkgs.python3.withPackages(ps: [ ps.numpy ps.scipy ])) ];
|
||||
checkPhase =
|
||||
''
|
||||
echo "Checking nac3standalone demos..."
|
||||
pushd nac3standalone/demo
|
||||
patchShebangs .
|
||||
export DEMO_LINALG_STUB=${demo-linalg-stub}/lib/liblinalg.a
|
||||
export DEMO_LINALG_STUB32=${demo-linalg-stub32}/lib/liblinalg.a
|
||||
./check_demos.sh -i686
|
||||
popd
|
||||
echo "Running Cargo tests..."
|
||||
cargoCheckHook
|
||||
'';
|
||||
installPhase =
|
||||
''
|
||||
PYTHON_SITEPACKAGES=$out/${pkgs.python3Packages.python.sitePackages}
|
||||
mkdir -p $PYTHON_SITEPACKAGES
|
||||
cp target/x86_64-unknown-linux-gnu/release/libnac3artiq.so $PYTHON_SITEPACKAGES/nac3artiq.so
|
||||
|
||||
mkdir -p $runkernel/bin
|
||||
cp target/x86_64-unknown-linux-gnu/release/runkernel $runkernel/bin
|
||||
|
||||
mkdir -p $standalone/bin
|
||||
cp target/x86_64-unknown-linux-gnu/release/nac3standalone $standalone/bin
|
||||
'';
|
||||
}
|
||||
);
|
||||
python3-mimalloc = pkgs.python3 // rec {
|
||||
withMimalloc = pkgs.python3.buildEnv.override({ makeWrapperArgs = [ "--set LD_PRELOAD ${pkgs.mimalloc}/lib/libmimalloc.so" ]; });
|
||||
withPackages = f: let packages = f pkgs.python3.pkgs; in withMimalloc.override { extraLibs = packages; };
|
||||
};
|
||||
|
||||
# LLVM PGO support
|
||||
llvm-nac3-instrumented = pkgs.callPackage ./nix/llvm {
|
||||
stdenv = pkgs.llvmPackages_16.stdenv;
|
||||
extraCmakeFlags = [ "-DLLVM_BUILD_INSTRUMENTED=IR" ];
|
||||
};
|
||||
nac3artiq-instrumented = pkgs.python3Packages.toPythonModule (
|
||||
pkgs.rustPlatform.buildRustPackage {
|
||||
name = "nac3artiq-instrumented";
|
||||
src = self;
|
||||
inherit (nac3artiq) cargoLock;
|
||||
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt pkgs.llvmPackages_16.bintools llvm-nac3-instrumented ];
|
||||
buildInputs = [ pkgs.python3 llvm-nac3-instrumented ];
|
||||
cargoBuildFlags = [ "--package" "nac3artiq" "--features" "init-llvm-profile" ];
|
||||
doCheck = false;
|
||||
configurePhase =
|
||||
''
|
||||
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C link-arg=-L${pkgs.llvmPackages_16.compiler-rt}/lib/linux -C link-arg=-lclang_rt.profile-x86_64"
|
||||
'';
|
||||
installPhase =
|
||||
''
|
||||
TARGET_DIR=$out/${pkgs.python3Packages.python.sitePackages}
|
||||
mkdir -p $TARGET_DIR
|
||||
cp target/x86_64-unknown-linux-gnu/release/libnac3artiq.so $TARGET_DIR/nac3artiq.so
|
||||
'';
|
||||
}
|
||||
);
|
||||
nac3artiq-profile = pkgs.stdenvNoCC.mkDerivation {
|
||||
name = "nac3artiq-profile";
|
||||
srcs = [
|
||||
(pkgs.fetchFromGitHub {
|
||||
owner = "m-labs";
|
||||
repo = "sipyco";
|
||||
rev = "dd3fc30a5b530279f8d75ae77b4de51f3b9870a3";
|
||||
sha256 = "sha256-ZOTF/NX7SltbYg+OGSfyjo3uTiaCZJJiITZriKt4YdY=";
|
||||
})
|
||||
(pkgs.fetchFromGitHub {
|
||||
owner = "m-labs";
|
||||
repo = "artiq";
|
||||
rev = "9b61fe3e32f3beca6d22975c68eae7adf5c9aa74";
|
||||
sha256 = "sha256-UETeIBZJyLRvx22F901toSDZibEtke1LA5MPAgPUJRE=";
|
||||
})
|
||||
];
|
||||
buildInputs = [
|
||||
(python3-mimalloc.withPackages(ps: [ ps.numpy ps.scipy ps.jsonschema ps.lmdb ps.platformdirs nac3artiq-instrumented ]))
|
||||
pkgs.llvmPackages_16.llvm.out
|
||||
pkgs.llvmPackages_16.bintools
|
||||
];
|
||||
phases = [ "buildPhase" "installPhase" ];
|
||||
buildPhase =
|
||||
''
|
||||
srcs=($srcs)
|
||||
sipyco=''${srcs[0]}
|
||||
artiq=''${srcs[1]}
|
||||
export PYTHONPATH=$sipyco:$artiq
|
||||
python -m artiq.frontend.artiq_ddb_template $artiq/artiq/examples/nac3devices/nac3devices.json > device_db.py
|
||||
cp $artiq/artiq/examples/nac3devices/nac3devices.py .
|
||||
python -m artiq.frontend.artiq_compile nac3devices.py
|
||||
'';
|
||||
installPhase =
|
||||
''
|
||||
mkdir $out
|
||||
llvm-profdata merge -o $out/llvm.profdata /build/llvm/build/profiles/*
|
||||
'';
|
||||
};
|
||||
llvm-nac3-pgo = pkgs.callPackage ./nix/llvm {
|
||||
stdenv = pkgs.llvmPackages_16.stdenv;
|
||||
extraCmakeFlags = [ "-DLLVM_PROFDATA_FILE=${nac3artiq-profile}/llvm.profdata" ];
|
||||
};
|
||||
nac3artiq-pgo = pkgs.python3Packages.toPythonModule (
|
||||
pkgs.rustPlatform.buildRustPackage {
|
||||
name = "nac3artiq-pgo";
|
||||
src = self;
|
||||
inherit (nac3artiq) cargoLock;
|
||||
nativeBuildInputs = [ pkgs.python3 packages.x86_64-linux.llvm-tools-irrt pkgs.llvmPackages_16.bintools llvm-nac3-pgo ];
|
||||
buildInputs = [ pkgs.python3 llvm-nac3-pgo ];
|
||||
cargoBuildFlags = [ "--package" "nac3artiq" ];
|
||||
cargoTestFlags = [ "--package" "nac3ast" "--package" "nac3parser" "--package" "nac3core" "--package" "nac3artiq" ];
|
||||
installPhase =
|
||||
''
|
||||
TARGET_DIR=$out/${pkgs.python3Packages.python.sitePackages}
|
||||
mkdir -p $TARGET_DIR
|
||||
cp target/x86_64-unknown-linux-gnu/release/libnac3artiq.so $TARGET_DIR/nac3artiq.so
|
||||
'';
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
packages.x86_64-w64-mingw32 = import ./nix/windows { inherit pkgs; };
|
||||
|
||||
devShells.x86_64-linux.default = pkgs.mkShell {
|
||||
name = "nac3-dev-shell";
|
||||
buildInputs = with pkgs; [
|
||||
# build dependencies
|
||||
packages.x86_64-linux.llvm-nac3
|
||||
(pkgs.wrapClangMulti llvmPackages_16.clang) llvmPackages_16.llvm.out llvmPackages_16.bintools # for running nac3standalone demos
|
||||
packages.x86_64-linux.llvm-tools-irrt
|
||||
cargo
|
||||
rustc
|
||||
# runtime dependencies
|
||||
lld_16 # for running kernels on the host
|
||||
(packages.x86_64-linux.python3-mimalloc.withPackages(ps: [ ps.numpy ps.scipy ]))
|
||||
# development tools
|
||||
cargo-insta
|
||||
clippy
|
||||
pre-commit
|
||||
rustfmt
|
||||
];
|
||||
shellHook =
|
||||
''
|
||||
export DEMO_LINALG_STUB=${packages.x86_64-linux.demo-linalg-stub}/lib/liblinalg.a
|
||||
export DEMO_LINALG_STUB32=${packages.x86_64-linux.demo-linalg-stub32}/lib/liblinalg.a
|
||||
passthru.cargoLock = cargoLock;
|
||||
nativeBuildInputs = [pkgs.python3 (pkgs.wrapClangMulti pkgs.llvmPackages_16.clang) llvm-tools-irrt pkgs.llvmPackages_16.llvm.out pkgs.llvmPackages_16.bintools llvm-nac3];
|
||||
buildInputs = [pkgs.python3 llvm-nac3 pkgs.stdenv.cc.cc.lib];
|
||||
checkInputs = [(pkgs.python3.withPackages (ps: [ps.numpy ps.scipy]))];
|
||||
checkPhase = ''
|
||||
echo "Checking nac3standalone demos..."
|
||||
pushd nac3standalone/demo
|
||||
patchShebangs .
|
||||
export DEMO_LINALG_STUB=${demo-linalg-stub}/lib/liblinalg.a
|
||||
export DEMO_LINALG_STUB32=${demo-linalg-stub32}/lib/liblinalg.a
|
||||
./check_demos.sh -i686
|
||||
popd
|
||||
echo "Running Cargo tests..."
|
||||
cargoCheckHook
|
||||
'';
|
||||
};
|
||||
devShells.x86_64-linux.msys2 = pkgs.mkShell {
|
||||
name = "nac3-dev-shell-msys2";
|
||||
buildInputs = with pkgs; [
|
||||
curl
|
||||
pacman
|
||||
fakeroot
|
||||
packages.x86_64-w64-mingw32.wine-msys2
|
||||
];
|
||||
};
|
||||
installPhase = ''
|
||||
PYTHON_SITEPACKAGES=$out/${pkgs.python3Packages.python.sitePackages}
|
||||
mkdir -p $PYTHON_SITEPACKAGES
|
||||
cp target/x86_64-unknown-linux-gnu/release/libnac3artiq.so $PYTHON_SITEPACKAGES/nac3artiq.so
|
||||
|
||||
hydraJobs = {
|
||||
inherit (packages.x86_64-linux) llvm-nac3 nac3artiq nac3artiq-pgo;
|
||||
llvm-nac3-msys2 = packages.x86_64-w64-mingw32.llvm-nac3;
|
||||
nac3artiq-msys2 = packages.x86_64-w64-mingw32.nac3artiq;
|
||||
nac3artiq-msys2-pkg = packages.x86_64-w64-mingw32.nac3artiq-pkg;
|
||||
mkdir -p $runkernel/bin
|
||||
cp target/x86_64-unknown-linux-gnu/release/runkernel $runkernel/bin
|
||||
|
||||
mkdir -p $standalone/bin
|
||||
cp target/x86_64-unknown-linux-gnu/release/nac3standalone $standalone/bin
|
||||
'';
|
||||
}
|
||||
);
|
||||
python3-mimalloc =
|
||||
pkgs.python3
|
||||
// rec {
|
||||
withMimalloc = pkgs.python3.buildEnv.override {makeWrapperArgs = ["--set LD_PRELOAD ${pkgs.mimalloc}/lib/libmimalloc.so"];};
|
||||
withPackages = f: let packages = f pkgs.python3.pkgs; in withMimalloc.override {extraLibs = packages;};
|
||||
};
|
||||
|
||||
# LLVM PGO support
|
||||
llvm-nac3-instrumented = pkgs.callPackage ./nix/llvm {
|
||||
stdenv = pkgs.llvmPackages_16.stdenv;
|
||||
extraCmakeFlags = ["-DLLVM_BUILD_INSTRUMENTED=IR"];
|
||||
};
|
||||
nac3artiq-instrumented = pkgs.python3Packages.toPythonModule (
|
||||
pkgs.rustPlatform.buildRustPackage {
|
||||
name = "nac3artiq-instrumented";
|
||||
src = self;
|
||||
inherit (nac3artiq) cargoLock;
|
||||
nativeBuildInputs = [pkgs.python3 packages.x86_64-linux.llvm-tools-irrt pkgs.llvmPackages_16.bintools llvm-nac3-instrumented];
|
||||
buildInputs = [pkgs.python3 llvm-nac3-instrumented];
|
||||
cargoBuildFlags = ["--package" "nac3artiq" "--features" "init-llvm-profile"];
|
||||
doCheck = false;
|
||||
configurePhase = ''
|
||||
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C link-arg=-L${pkgs.llvmPackages_16.compiler-rt}/lib/linux -C link-arg=-lclang_rt.profile-x86_64"
|
||||
'';
|
||||
installPhase = ''
|
||||
TARGET_DIR=$out/${pkgs.python3Packages.python.sitePackages}
|
||||
mkdir -p $TARGET_DIR
|
||||
cp target/x86_64-unknown-linux-gnu/release/libnac3artiq.so $TARGET_DIR/nac3artiq.so
|
||||
'';
|
||||
}
|
||||
);
|
||||
nac3artiq-profile = pkgs.stdenvNoCC.mkDerivation {
|
||||
name = "nac3artiq-profile";
|
||||
srcs = [
|
||||
(pkgs.fetchFromGitHub {
|
||||
owner = "m-labs";
|
||||
repo = "sipyco";
|
||||
rev = "96fcefbea490a9b42c862393860d2e586b05d744";
|
||||
sha256 = "sha256-DkcgZ0K6lsxzBWc31GTyufuSOpcorVv5OsZLHphHBtg=";
|
||||
})
|
||||
(pkgs.fetchFromGitHub {
|
||||
owner = "m-labs";
|
||||
repo = "artiq";
|
||||
rev = "ee9ccd39afeb5eab60277b745c164f3c26a1f569";
|
||||
sha256 = "sha256-J8ininS9qHz7hbHzsznpqnU870PdyxYqfLI9itj3Gi8=";
|
||||
})
|
||||
];
|
||||
buildInputs = [
|
||||
(python3-mimalloc.withPackages (ps: [ps.numpy ps.scipy ps.jsonschema ps.lmdb ps.platformdirs nac3artiq-instrumented]))
|
||||
pkgs.llvmPackages_16.llvm.out
|
||||
pkgs.llvmPackages_16.bintools
|
||||
];
|
||||
phases = ["buildPhase" "installPhase"];
|
||||
buildPhase = ''
|
||||
srcs=($srcs)
|
||||
sipyco=''${srcs[0]}
|
||||
artiq=''${srcs[1]}
|
||||
export PYTHONPATH=$sipyco:$artiq
|
||||
python -m artiq.frontend.artiq_ddb_template $artiq/artiq/examples/nac3devices/master.json -s 1 $artiq/artiq/examples/nac3devices/satellite.json > device_db.py
|
||||
cp $artiq/artiq/examples/nac3devices/nac3devices.py .
|
||||
python -m artiq.frontend.artiq_compile nac3devices.py
|
||||
'';
|
||||
installPhase = ''
|
||||
mkdir $out
|
||||
llvm-profdata merge -o $out/llvm.profdata /build/llvm/build/profiles/*
|
||||
'';
|
||||
};
|
||||
llvm-nac3-pgo = pkgs.callPackage ./nix/llvm {
|
||||
stdenv = pkgs.llvmPackages_16.stdenv;
|
||||
extraCmakeFlags = ["-DLLVM_PROFDATA_FILE=${nac3artiq-profile}/llvm.profdata"];
|
||||
};
|
||||
nac3artiq-pgo = pkgs.python3Packages.toPythonModule (
|
||||
pkgs.rustPlatform.buildRustPackage {
|
||||
name = "nac3artiq-pgo";
|
||||
src = self;
|
||||
inherit (nac3artiq) cargoLock;
|
||||
nativeBuildInputs = [pkgs.python3 packages.x86_64-linux.llvm-tools-irrt pkgs.llvmPackages_16.bintools llvm-nac3-pgo];
|
||||
buildInputs = [pkgs.python3 llvm-nac3-pgo];
|
||||
cargoBuildFlags = ["--package" "nac3artiq"];
|
||||
cargoTestFlags = ["--package" "nac3ast" "--package" "nac3parser" "--package" "nac3core" "--package" "nac3artiq"];
|
||||
installPhase = ''
|
||||
TARGET_DIR=$out/${pkgs.python3Packages.python.sitePackages}
|
||||
mkdir -p $TARGET_DIR
|
||||
cp target/x86_64-unknown-linux-gnu/release/libnac3artiq.so $TARGET_DIR/nac3artiq.so
|
||||
'';
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
packages.x86_64-w64-mingw32 = import ./nix/windows {inherit pkgs;};
|
||||
|
||||
formatter.x86_64-linux = pkgs.alejandra;
|
||||
|
||||
devShells.x86_64-linux.default = pkgs.mkShell {
|
||||
name = "nac3-dev-shell";
|
||||
buildInputs = with pkgs; [
|
||||
# build dependencies
|
||||
packages.x86_64-linux.llvm-nac3
|
||||
(pkgs.wrapClangMulti llvmPackages_16.clang)
|
||||
llvmPackages_16.llvm.out
|
||||
llvmPackages_16.bintools # for running nac3standalone demos
|
||||
packages.x86_64-linux.llvm-tools-irrt
|
||||
cargo
|
||||
rustc
|
||||
# runtime dependencies
|
||||
lld_16 # for running kernels on the host
|
||||
(packages.x86_64-linux.python3-mimalloc.withPackages (ps: [ps.numpy ps.scipy]))
|
||||
# development tools
|
||||
cargo-insta
|
||||
clippy
|
||||
pre-commit
|
||||
rustfmt
|
||||
];
|
||||
shellHook = ''
|
||||
export DEMO_LINALG_STUB=${packages.x86_64-linux.demo-linalg-stub}/lib/liblinalg.a
|
||||
export DEMO_LINALG_STUB32=${packages.x86_64-linux.demo-linalg-stub32}/lib/liblinalg.a
|
||||
'';
|
||||
};
|
||||
devShells.x86_64-linux.msys2 = pkgs.mkShell {
|
||||
name = "nac3-dev-shell-msys2";
|
||||
buildInputs = with pkgs; [
|
||||
curl
|
||||
pacman
|
||||
fakeroot
|
||||
packages.x86_64-w64-mingw32.wine-msys2
|
||||
];
|
||||
};
|
||||
|
||||
hydraJobs = {
|
||||
inherit (packages.x86_64-linux) llvm-nac3 nac3artiq nac3artiq-pgo;
|
||||
llvm-nac3-msys2 = packages.x86_64-w64-mingw32.llvm-nac3;
|
||||
nac3artiq-msys2 = packages.x86_64-w64-mingw32.nac3artiq;
|
||||
nac3artiq-msys2-pkg = packages.x86_64-w64-mingw32.nac3artiq-pkg;
|
||||
};
|
||||
};
|
||||
|
||||
nixConfig = {
|
||||
|
||||
@@ -9,13 +9,13 @@ name = "nac3artiq"
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
indexmap = "2.9"
|
||||
indexmap = "2.12"
|
||||
itertools = "0.14"
|
||||
pyo3 = { version = "0.25", features = ["extension-module"] }
|
||||
pyo3 = { version = "0.27", features = ["extension-module"] }
|
||||
parking_lot = "0.12"
|
||||
tempfile = "3.20"
|
||||
tempfile = "3.22"
|
||||
nac3core = { path = "../nac3core" }
|
||||
nac3ld = { path = "../nac3ld" }
|
||||
nac3binutils = { path = "../nac3binutils" }
|
||||
|
||||
[features]
|
||||
init-llvm-profile = []
|
||||
|
||||
102
nac3artiq/demo/circular_generic_typevar.py
Normal file
102
nac3artiq/demo/circular_generic_typevar.py
Normal file
@@ -0,0 +1,102 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from min_artiq import *
|
||||
from numpy import int32, int64
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
|
||||
@compile
|
||||
class ProtoRev8:
|
||||
"""Has back-reference to CPLD[ProtoRev8] - this is key to triggering the bug."""
|
||||
cpld: KernelInvariant[CPLD[ProtoRev8]]
|
||||
cfg_reg: Kernel[int64]
|
||||
|
||||
def __init__(self, cpld: CPLD[ProtoRev8]):
|
||||
self.cpld = cpld
|
||||
self.cfg_reg = int64(0)
|
||||
|
||||
@kernel
|
||||
def cfg_write(self, cfg: int64):
|
||||
pass
|
||||
|
||||
@kernel
|
||||
def sta_read(self) -> int32:
|
||||
return int32(0)
|
||||
|
||||
|
||||
@compile
|
||||
class ProtoRev9:
|
||||
"""Has back-reference to CPLD[ProtoRev9] - this is key to triggering the bug."""
|
||||
cpld: KernelInvariant[CPLD[ProtoRev9]]
|
||||
cfg_reg: Kernel[int64]
|
||||
|
||||
def __init__(self, cpld: CPLD[ProtoRev9]):
|
||||
self.cpld = cpld
|
||||
self.cfg_reg = int64(0)
|
||||
|
||||
@kernel
|
||||
def cfg_write(self, cfg: int64):
|
||||
pass
|
||||
|
||||
@kernel
|
||||
def sta_read(self) -> int32:
|
||||
return int32(0)
|
||||
|
||||
|
||||
V = TypeVar("V", ProtoRev8, ProtoRev9)
|
||||
|
||||
|
||||
@compile
|
||||
class CPLD(Generic[V]):
|
||||
core: KernelInvariant[Core]
|
||||
version: KernelInvariant[V]
|
||||
cfg_reg: Kernel[int64]
|
||||
|
||||
def __init__(self):
|
||||
self.core = Core()
|
||||
# Create version with back-reference to self
|
||||
self.version = ProtoRev9(self)
|
||||
self.cfg_reg = int64(0)
|
||||
|
||||
@kernel
|
||||
def cfg_write(self, cfg: int64):
|
||||
self.version.cfg_write(cfg)
|
||||
|
||||
@kernel
|
||||
def sta_read(self) -> int32:
|
||||
return self.version.sta_read()
|
||||
|
||||
|
||||
@compile
|
||||
class AD9910:
|
||||
core: KernelInvariant[Core]
|
||||
cpld: KernelInvariant[CPLD[ProtoRev9]]
|
||||
|
||||
def __init__(self, cpld: CPLD[ProtoRev9]):
|
||||
self.core = Core()
|
||||
self.cpld = cpld
|
||||
|
||||
@kernel
|
||||
def init(self):
|
||||
self.cpld.cfg_write(self.cpld.cfg_reg)
|
||||
|
||||
|
||||
@compile
|
||||
class Test:
|
||||
core: KernelInvariant[Core]
|
||||
cpld: KernelInvariant[CPLD[ProtoRev9]]
|
||||
dds: KernelInvariant[AD9910]
|
||||
|
||||
def __init__(self):
|
||||
self.core = Core()
|
||||
self.cpld = CPLD()
|
||||
self.dds = AD9910(self.cpld)
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.cpld.cfg_write(int64(0))
|
||||
self.dds.init()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Test().run()
|
||||
@@ -1,9 +1,12 @@
|
||||
from inspect import getfullargspec, getmodule
|
||||
from functools import wraps
|
||||
from math import floor, ceil
|
||||
import numpy as np
|
||||
from scipy import special
|
||||
from scipy import linalg
|
||||
from numpy import int32, int64, uint32, uint64, float64, bool_, str_, ndarray
|
||||
from types import GenericAlias, ModuleType, SimpleNamespace
|
||||
from typing import _GenericAlias, Generic, TypeVar
|
||||
from typing import _GenericAlias, Generic, Literal, TypeVar
|
||||
|
||||
import nac3artiq
|
||||
|
||||
@@ -152,6 +155,20 @@ builtins = {
|
||||
"list": list,
|
||||
"tuple": tuple,
|
||||
"Exception": Exception,
|
||||
"range": range,
|
||||
"enumerate": enumerate,
|
||||
"round": round,
|
||||
"round64": round64,
|
||||
"floor": floor,
|
||||
"floor64": floor64,
|
||||
"ceil": ceil,
|
||||
"ceil64": ceil64,
|
||||
"len": len,
|
||||
"min": min,
|
||||
"max": max,
|
||||
"abs": abs,
|
||||
"some": Some,
|
||||
"staticmethod": staticmethod,
|
||||
|
||||
"types": {
|
||||
"GenericAlias": GenericAlias,
|
||||
@@ -160,7 +177,9 @@ builtins = {
|
||||
|
||||
"typing": {
|
||||
"_GenericAlias": _GenericAlias,
|
||||
"Generic": Generic,
|
||||
"TypeVar": TypeVar,
|
||||
"Literal": Literal,
|
||||
},
|
||||
|
||||
"numpy": {
|
||||
@@ -172,6 +191,86 @@ builtins = {
|
||||
"bool_": bool_,
|
||||
"str_": str_,
|
||||
"ndarray": ndarray,
|
||||
|
||||
"np_empty": np.empty,
|
||||
"np_zeros": np.zeros,
|
||||
"np_ones": np.ones,
|
||||
"np_full": np.full,
|
||||
"np_array": np.array,
|
||||
"np_eye": np.eye,
|
||||
"np_identity": np.identity,
|
||||
|
||||
"np_size": np.size,
|
||||
"np_shape": np.shape,
|
||||
|
||||
"np_broadcast_to": np.broadcast_to,
|
||||
"np_transpose": np.transpose,
|
||||
"np_reshape": np.reshape,
|
||||
|
||||
"np_round": np.round,
|
||||
"np_floor": np.floor,
|
||||
"np_ceil": np.ceil,
|
||||
"np_min": np.min,
|
||||
"np_minimum": np.minimum,
|
||||
"np_max": np.max,
|
||||
"np_maximum": np.maximum,
|
||||
"np_argmin": np.argmin,
|
||||
"np_argmax": np.argmax,
|
||||
"np_isnan": np.isnan,
|
||||
"np_isinf": np.isinf,
|
||||
"np_sin": np.sin,
|
||||
"np_cos": np.cos,
|
||||
"np_exp": np.exp,
|
||||
"np_exp2": np.exp2,
|
||||
"np_log": np.log,
|
||||
"np_log10": np.log10,
|
||||
"np_log2": np.log2,
|
||||
"np_fabs": np.fabs,
|
||||
"np_sqrt": np.sqrt,
|
||||
"np_rint": np.rint,
|
||||
"np_tan": np.tan,
|
||||
"np_arcsin": np.arcsin,
|
||||
"np_arccos": np.arccos,
|
||||
"np_arctan": np.arctan,
|
||||
"np_sinh": np.sinh,
|
||||
"np_cosh": np.cosh,
|
||||
"np_tanh": np.tanh,
|
||||
"np_arcsinh": np.arcsinh,
|
||||
"np_arccosh": np.arccosh,
|
||||
"np_arctanh": np.arctanh,
|
||||
"np_expm1": np.expm1,
|
||||
"np_cbrt": np.cbrt,
|
||||
|
||||
"sp_spec_erf": special.erf,
|
||||
"sp_spec_erfc": special.erfc,
|
||||
"sp_spec_gamma": special.gamma,
|
||||
"sp_spec_gammaln": special.gammaln,
|
||||
"sp_spec_j0": special.j0,
|
||||
"sp_spec_j1": special.j1,
|
||||
|
||||
"np_arctan2": np.arctan2,
|
||||
"np_copysign": np.copysign,
|
||||
"np_fmax": np.fmax,
|
||||
"np_fmin": np.fmin,
|
||||
"np_ldexp": np.ldexp,
|
||||
"np_hypot": np.hypot,
|
||||
"np_nextafter": np.nextafter,
|
||||
|
||||
"np_any": np.any,
|
||||
"np_all": np.all,
|
||||
|
||||
"np_dot": np.dot,
|
||||
"np_linalg_cholesky": np.linalg.cholesky,
|
||||
"np_linalg_qr": np.linalg.qr,
|
||||
"np_linalg_svd": np.linalg.svd,
|
||||
"np_linalg_inv": np.linalg.inv,
|
||||
"np_linalg_pinv": np.linalg.pinv,
|
||||
"np_linalg_matrix_power": np.linalg.matrix_power,
|
||||
"np_linalg_det": np.linalg.det,
|
||||
|
||||
"sp_linalg_lu": linalg.lu,
|
||||
"sp_linalg_schur": linalg.schur,
|
||||
"sp_linalg_hessenberg": linalg.hessenberg,
|
||||
},
|
||||
|
||||
"artiq": {
|
||||
@@ -298,7 +397,7 @@ class Core:
|
||||
obj = method
|
||||
name = ""
|
||||
|
||||
compiler.compile_method_to_file(obj, name, args, "module.elf", embedding)
|
||||
compiler.compile_method_to_file(obj, name, args, "module.elf", "debug.elf", embedding)
|
||||
|
||||
@kernel
|
||||
def reset(self):
|
||||
|
||||
23
nac3artiq/demo/mod_prefixed_builtin_annotation.py
Normal file
23
nac3artiq/demo/mod_prefixed_builtin_annotation.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from typing import Literal
|
||||
|
||||
from min_artiq import Core, KernelInvariant, compile, kernel
|
||||
import numpy as np
|
||||
|
||||
# Tests the special case where `sys.__file__` is not present
|
||||
import sys
|
||||
|
||||
@compile
|
||||
class FindTrapResonance():
|
||||
core: KernelInvariant[Core]
|
||||
frequencies: KernelInvariant[np.ndarray[float, Literal[1]]]
|
||||
|
||||
def __init__(self):
|
||||
self.core = Core()
|
||||
self.frequencies = np.zeros((1,))
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
pass
|
||||
|
||||
if __name__ == "__main__":
|
||||
FindTrapResonance().run()
|
||||
60
nac3artiq/demo/type_conversion.py
Normal file
60
nac3artiq/demo/type_conversion.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from min_artiq import *
|
||||
import numpy as np
|
||||
|
||||
enumerated_tuple = enumerate((1, 2, 3))
|
||||
enumerated_tuple2 = enumerate((1.1, 2.2, 3.3, 4.4))
|
||||
enumerated_tuple3 = enumerate(())
|
||||
enumerated_list = enumerate([1, 2, 3])
|
||||
enumerated_list2 = enumerate(["a", "b", "c", "d"])
|
||||
enumerated_list3 = enumerate([])
|
||||
|
||||
@compile
|
||||
class Demo:
|
||||
core: KernelInvariant[Core]
|
||||
led0: KernelInvariant[TTLOut]
|
||||
led1: KernelInvariant[TTLOut]
|
||||
|
||||
def __init__(self):
|
||||
self.core = Core()
|
||||
self.led0 = TTLOut(self.core, 18)
|
||||
self.led1 = TTLOut(self.core, 19)
|
||||
|
||||
@kernel
|
||||
def test(self):
|
||||
a = enumerated_tuple
|
||||
b = enumerated_tuple2
|
||||
c = enumerated_list
|
||||
d = enumerated_list2
|
||||
|
||||
for x in enumerated_tuple:
|
||||
x[0]
|
||||
x[1]
|
||||
for y in enumerated_tuple2:
|
||||
y[0]
|
||||
y[1]
|
||||
for z in enumerated_tuple3:
|
||||
z[0]
|
||||
z[1]
|
||||
for (h, u) in enumerated_tuple2:
|
||||
h
|
||||
u
|
||||
for p in enumerated_list:
|
||||
p[0]
|
||||
p[1]
|
||||
for q in enumerated_list2:
|
||||
q[0]
|
||||
q[1]
|
||||
for r in enumerated_list3:
|
||||
r[0]
|
||||
r[1]
|
||||
for (m, n) in enumerated_list2:
|
||||
m
|
||||
n
|
||||
|
||||
def run(self):
|
||||
self.test()
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
Demo().run()
|
||||
49
nac3artiq/demo/typevar.py
Normal file
49
nac3artiq/demo/typevar.py
Normal file
@@ -0,0 +1,49 @@
|
||||
from min_artiq import *
|
||||
from typing import Generic, TypeVar
|
||||
|
||||
|
||||
@compile
|
||||
class TTLInOut:
|
||||
@kernel
|
||||
def on(self):
|
||||
pass
|
||||
|
||||
|
||||
@compile
|
||||
class TTLOut:
|
||||
@kernel
|
||||
def on(self):
|
||||
pass
|
||||
|
||||
|
||||
T = TypeVar("T", TTLOut, TTLInOut)
|
||||
|
||||
|
||||
@compile
|
||||
class TurnOn(Generic[T]):
|
||||
ttl: KernelInvariant[T]
|
||||
|
||||
def __init__(self, ttl):
|
||||
self.ttl = ttl
|
||||
|
||||
@kernel
|
||||
def turn_on(self):
|
||||
self.ttl.on()
|
||||
|
||||
|
||||
@compile
|
||||
class TypeVarTest:
|
||||
core: KernelInvariant[Core]
|
||||
turn_on: KernelInvariant[TurnOn[TTLOut]]
|
||||
|
||||
def __init__(self):
|
||||
self.core = Core()
|
||||
self.turn_on = TurnOn(TTLOut())
|
||||
|
||||
@kernel
|
||||
def run(self):
|
||||
self.turn_on.turn_on()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
TypeVarTest().run()
|
||||
@@ -7,28 +7,30 @@ use std::{
|
||||
|
||||
use itertools::Itertools;
|
||||
use pyo3::{
|
||||
PyObject, PyResult, Python,
|
||||
PyResult, Python,
|
||||
prelude::*,
|
||||
types::{PyDict, PyList},
|
||||
};
|
||||
|
||||
use nac3core::{
|
||||
codegen::{
|
||||
CodeGenContext, CodeGenerator, basic_type_all,
|
||||
CodeGenContext, CodeGenerator, VarValue, basic_type_all, bool_to_i1,
|
||||
expr::{call_extern, destructure_range, gen_call},
|
||||
llvm_intrinsics::{call_int_smax, call_memcpy, call_stackrestore, call_stacksave},
|
||||
stmt::{gen_block, gen_for_callback_incrementing, gen_if_callback, gen_with},
|
||||
type_aligned_alloca,
|
||||
types::{RangeType, ndarray::NDArrayType},
|
||||
values::{
|
||||
ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ListValue, ProxyValue,
|
||||
UntypedArrayLikeAccessor,
|
||||
stmt::{
|
||||
gen_array_var, gen_block, gen_dyn_array_var, gen_for_callback_incrementing,
|
||||
gen_if_callback, gen_var, gen_with,
|
||||
},
|
||||
typed_store,
|
||||
types::{
|
||||
ArrayLikeIndexer, ArraySliceValue, ExceptionType, ListType, NDArrayType, ProxyTypeExt,
|
||||
RangeType, field,
|
||||
},
|
||||
},
|
||||
inkwell::{
|
||||
AddressSpace, IntPredicate,
|
||||
IntPredicate,
|
||||
module::Linkage,
|
||||
types::{BasicType, IntType},
|
||||
types::IntType,
|
||||
values::{BasicValueEnum, IntValue, PointerValue, StructValue},
|
||||
},
|
||||
nac3parser::ast::{Expr, ExprKind, Located, Stmt, StmtKind, StrRef},
|
||||
@@ -92,7 +94,7 @@ impl<'a> ArtiqCodeGenerator<'a> {
|
||||
name: String,
|
||||
timeline: &'a (dyn TimeFns + Sync),
|
||||
special_ids: SpecialPythonId,
|
||||
) -> ArtiqCodeGenerator<'a> {
|
||||
) -> Self {
|
||||
ArtiqCodeGenerator {
|
||||
name,
|
||||
name_counter: 0,
|
||||
@@ -112,11 +114,7 @@ impl<'a> ArtiqCodeGenerator<'a> {
|
||||
/// closest parent `with` statement is a `with parallel` block.
|
||||
fn timeline_reset_start(&mut self, ctx: &mut CodeGenContext<'_, '_>) -> Result<(), String> {
|
||||
if let Some(start) = self.start.clone() {
|
||||
let start_val = self.gen_expr(ctx, &start)?.unwrap().to_basic_value_enum(
|
||||
ctx,
|
||||
self,
|
||||
start.custom.unwrap(),
|
||||
)?;
|
||||
let start_val = self.gen_expr(ctx, &start)?.to_basic_value_enum(ctx)?;
|
||||
self.timeline.emit_at_mu(ctx, start_val);
|
||||
}
|
||||
|
||||
@@ -142,11 +140,7 @@ impl<'a> ArtiqCodeGenerator<'a> {
|
||||
store_name: Option<&str>,
|
||||
) -> Result<(), String> {
|
||||
if let Some(end) = end {
|
||||
let old_end = self.gen_expr(ctx, &end)?.unwrap().to_basic_value_enum(
|
||||
ctx,
|
||||
self,
|
||||
end.custom.unwrap(),
|
||||
)?;
|
||||
let old_end = self.gen_expr(ctx, &end)?.to_basic_value_enum(ctx)?;
|
||||
let now = self.timeline.emit_now_mu(ctx);
|
||||
let max =
|
||||
call_int_smax(ctx, old_end.into_int_value(), now.into_int_value(), Some("smax"));
|
||||
@@ -157,7 +151,7 @@ impl<'a> ArtiqCodeGenerator<'a> {
|
||||
store_name.map(|name| format!("{name}.addr")).as_deref(),
|
||||
)?
|
||||
.unwrap();
|
||||
ctx.builder.build_store(end_store, max).unwrap();
|
||||
typed_store(&ctx.builder, end_store, max);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -240,17 +234,15 @@ impl CodeGenerator for ArtiqCodeGenerator<'_> {
|
||||
// parallel block, and we should update the max end value.
|
||||
if let ExprKind::Name { id, ctx: name_ctx } = &item.context_expr.node {
|
||||
let resolver = ctx.resolver.clone();
|
||||
if let Some(static_value) =
|
||||
if let Some((_ptr, static_value, _counter)) = ctx.var_assignment.get(id) {
|
||||
static_value.clone()
|
||||
} else if let Some(ValueEnum::Static(val)) =
|
||||
resolver.get_symbol_value(*id, ctx, self)
|
||||
{
|
||||
Some(val)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
if let Some(static_value) = if let Some(VarValue { static_value, .. }) =
|
||||
ctx.var_assignment.get(id)
|
||||
{
|
||||
static_value.clone()
|
||||
} else if let Some(ValueEnum::Static(val)) = resolver.get_symbol_value(*id, ctx) {
|
||||
Some(val)
|
||||
} else {
|
||||
None
|
||||
} {
|
||||
let python_id = static_value.get_unique_identifier();
|
||||
if python_id == self.special_ids.parallel
|
||||
|| python_id == self.special_ids.legacy_parallel
|
||||
@@ -260,11 +252,7 @@ impl CodeGenerator for ArtiqCodeGenerator<'_> {
|
||||
let old_parallel_mode = self.parallel_mode;
|
||||
|
||||
let now = if let Some(old_start) = &old_start {
|
||||
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum(
|
||||
ctx,
|
||||
self,
|
||||
old_start.custom.unwrap(),
|
||||
)?
|
||||
self.gen_expr(ctx, old_start)?.to_basic_value_enum(ctx)?
|
||||
} else {
|
||||
self.timeline.emit_now_mu(ctx)
|
||||
};
|
||||
@@ -288,7 +276,7 @@ impl CodeGenerator for ArtiqCodeGenerator<'_> {
|
||||
let start = self
|
||||
.gen_store_target(ctx, &start_expr, Some("start.addr"))?
|
||||
.unwrap();
|
||||
ctx.builder.build_store(start, now).unwrap();
|
||||
typed_store(&ctx.builder, start, now);
|
||||
Ok(Some(start_expr)) as Result<_, String>
|
||||
},
|
||||
|v| Ok(Some(v)),
|
||||
@@ -301,7 +289,7 @@ impl CodeGenerator for ArtiqCodeGenerator<'_> {
|
||||
custom: Some(ctx.primitives.int64),
|
||||
};
|
||||
let end = self.gen_store_target(ctx, &end_expr, Some("end.addr"))?.unwrap();
|
||||
ctx.builder.build_store(end, now).unwrap();
|
||||
typed_store(&ctx.builder, end, now);
|
||||
self.end = Some(end_expr);
|
||||
self.name_counter += 1;
|
||||
self.parallel_mode = if python_id == self.special_ids.parallel {
|
||||
@@ -332,11 +320,7 @@ impl CodeGenerator for ArtiqCodeGenerator<'_> {
|
||||
|
||||
// set duration
|
||||
let end_expr = self.end.take().unwrap();
|
||||
let end_val = self.gen_expr(ctx, &end_expr)?.unwrap().to_basic_value_enum(
|
||||
ctx,
|
||||
self,
|
||||
end_expr.custom.unwrap(),
|
||||
)?;
|
||||
let end_val = self.gen_expr(ctx, &end_expr)?.to_basic_value_enum(ctx)?;
|
||||
|
||||
// inside a sequential block
|
||||
if old_start.is_none() {
|
||||
@@ -387,8 +371,6 @@ fn gen_rpc_tag(
|
||||
ty: Type,
|
||||
buffer: &mut Vec<u8>,
|
||||
) -> Result<(), String> {
|
||||
use nac3core::typecheck::typedef::TypeEnum::*;
|
||||
|
||||
let PrimitiveStore { int32, int64, float, bool, str, none, .. } = ctx.primitives;
|
||||
|
||||
if ctx.unifier.unioned(ty, int32) {
|
||||
@@ -406,22 +388,22 @@ fn gen_rpc_tag(
|
||||
} else {
|
||||
let ty_enum = ctx.unifier.get_ty(ty);
|
||||
match &*ty_enum {
|
||||
TTuple { ty, is_vararg_ctx: false } => {
|
||||
TypeEnum::TTuple { ty, is_vararg_ctx: false } => {
|
||||
buffer.push(b't');
|
||||
buffer.push(ty.len() as u8);
|
||||
for ty in ty {
|
||||
gen_rpc_tag(ctx, *ty, buffer)?;
|
||||
}
|
||||
}
|
||||
TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
TypeEnum::TObj { obj_id, params, .. } if *obj_id == PrimDef::List.id() => {
|
||||
let ty = iter_type_vars(params).next().unwrap().ty;
|
||||
|
||||
buffer.push(b'l');
|
||||
gen_rpc_tag(ctx, ty, buffer)?;
|
||||
}
|
||||
TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
|
||||
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
|
||||
let (ndarray_dtype, ndarray_ndims) = unpack_ndarray_var_tys(&mut ctx.unifier, ty);
|
||||
let ndarray_ndims = if let TLiteral { values, .. } =
|
||||
let ndarray_ndims = if let TypeEnum::TLiteral { values, .. } =
|
||||
&*ctx.unifier.get_ty_immutable(ndarray_ndims)
|
||||
{
|
||||
if values.len() != 1 {
|
||||
@@ -456,12 +438,11 @@ fn gen_rpc_tag(
|
||||
///
|
||||
/// See `artiq/firmware/libproto_artiq/rpc_proto.rs` for the expected format.
|
||||
fn format_rpc_arg<'ctx>(
|
||||
generator: &mut dyn CodeGenerator,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
(arg, arg_ty, arg_idx): (BasicValueEnum<'ctx>, Type, usize),
|
||||
) -> PointerValue<'ctx> {
|
||||
let llvm_i8 = ctx.ctx.i8_type();
|
||||
let llvm_pi8 = llvm_i8.ptr_type(AddressSpace::default());
|
||||
let llvm_i8 = ctx.i8;
|
||||
let llvm_pi8 = ctx.ptr;
|
||||
|
||||
let arg_slot = match &*ctx.unifier.get_ty_immutable(arg_ty) {
|
||||
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
|
||||
@@ -471,53 +452,41 @@ fn format_rpc_arg<'ctx>(
|
||||
let (elem_ty, ndims) = unpack_ndarray_var_tys(&mut ctx.unifier, arg_ty);
|
||||
let ndims = extract_ndims(&ctx.unifier, ndims);
|
||||
let dtype = ctx.get_llvm_type(elem_ty);
|
||||
let ndarray = NDArrayType::new(ctx, dtype, ndims)
|
||||
.map_pointer_value(arg.into_pointer_value(), None);
|
||||
|
||||
let ndims = ctx.size_t.const_int(ndims, false);
|
||||
let ndarray =
|
||||
NDArrayType::new(ctx, dtype, ndims).map_value(arg.into_pointer_value(), None);
|
||||
|
||||
// `ndarray.data` is possibly not contiguous, and we need it to be contiguous for
|
||||
// the reader.
|
||||
// Turning it into a ContiguousNDArray to get a `data` that is contiguous.
|
||||
let carray = ndarray.make_contiguous_ndarray(generator, ctx);
|
||||
let carray = ndarray.make_contiguous_ndarray(ctx);
|
||||
|
||||
let sizeof_usize = ctx.size_t.size_of();
|
||||
let sizeof_usize =
|
||||
ctx.builder.build_int_truncate_or_bit_cast(sizeof_usize, ctx.size_t, "").unwrap();
|
||||
|
||||
let sizeof_pdata = dtype.ptr_type(AddressSpace::default()).size_of();
|
||||
let sizeof_pdata =
|
||||
ctx.builder.build_int_truncate_or_bit_cast(sizeof_pdata, ctx.size_t, "").unwrap();
|
||||
|
||||
let sizeof_buf_shape = ctx.builder.build_int_mul(sizeof_usize, ndims, "").unwrap();
|
||||
let sizeof_buf = ctx.builder.build_int_add(sizeof_buf_shape, sizeof_pdata, "").unwrap();
|
||||
let sizeof_usize = ctx.sizeof(ctx.size_t);
|
||||
let sizeof_pdata = ctx.sizeof(ctx.ptr);
|
||||
let sizeof_buf_shape = sizeof_usize * ndims;
|
||||
let sizeof_buf = sizeof_buf_shape + sizeof_pdata;
|
||||
|
||||
// buf = { data: void*, shape: [size_t; ndims]; }
|
||||
let buf = ctx.builder.build_array_alloca(llvm_i8, sizeof_buf, "rpc.arg").unwrap();
|
||||
let buf = ArraySliceValue::from_ptr_val(buf, sizeof_buf, Some("rpc.arg"));
|
||||
let buf_data = buf.base_ptr(ctx, generator);
|
||||
let buf_shape =
|
||||
unsafe { buf.ptr_offset_unchecked(ctx, generator, &sizeof_pdata, None) };
|
||||
let buf = gen_array_var(ctx, llvm_i8, sizeof_buf, Some("rpc.arg"));
|
||||
let buf_data = buf.value.0;
|
||||
let sizeof_pdata_ = ctx.size_t.const_int(sizeof_pdata, false);
|
||||
let buf_shape = buf.ptr_offset_unchecked(ctx, &sizeof_pdata_, None);
|
||||
|
||||
// Write to `buf->data`
|
||||
let carray_data = carray.load_data(ctx);
|
||||
let carray_data = carray.load(ctx, field!(data));
|
||||
let carray_data = ctx.builder.build_pointer_cast(carray_data, llvm_pi8, "").unwrap();
|
||||
call_memcpy(ctx, buf_data, carray_data, sizeof_pdata);
|
||||
call_memcpy(ctx, buf_data, carray_data, sizeof_pdata_);
|
||||
|
||||
// Write to `buf->shape`
|
||||
let carray_shape = ndarray.shape().base_ptr(ctx, generator);
|
||||
let carray_shape_i8 =
|
||||
ctx.builder.build_pointer_cast(carray_shape, llvm_pi8, "").unwrap();
|
||||
call_memcpy(ctx, buf_shape, carray_shape_i8, sizeof_buf_shape);
|
||||
let carray_shape = ndarray.shape(ctx).value.0;
|
||||
let sizeof_buf_shape_ = ctx.size_t.const_int(sizeof_buf_shape, false);
|
||||
call_memcpy(ctx, buf_shape, carray_shape, sizeof_buf_shape_);
|
||||
|
||||
buf.base_ptr(ctx, generator)
|
||||
buf.value.0
|
||||
}
|
||||
|
||||
_ => {
|
||||
let arg_slot = generator
|
||||
.gen_var_alloc(ctx, arg.get_type(), Some(&format!("rpc.arg{arg_idx}")))
|
||||
.unwrap();
|
||||
ctx.builder.build_store(arg_slot, arg).unwrap();
|
||||
let arg_slot = gen_var(ctx, arg.get_type(), Some(&format!("rpc.arg{arg_idx}")));
|
||||
typed_store(&ctx.builder, arg_slot, arg);
|
||||
|
||||
ctx.builder
|
||||
.build_bit_cast(arg_slot, llvm_pi8, "rpc.arg")
|
||||
@@ -533,7 +502,6 @@ fn format_rpc_arg<'ctx>(
|
||||
|
||||
/// Formats an RPC return value to conform to the expected format required by NAC3.
|
||||
fn format_rpc_ret<'ctx>(
|
||||
generator: &mut dyn CodeGenerator,
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
ret_ty: Type,
|
||||
) -> Option<BasicValueEnum<'ctx>> {
|
||||
@@ -547,11 +515,8 @@ fn format_rpc_ret<'ctx>(
|
||||
// else *(T*)ret_ptr
|
||||
// }
|
||||
|
||||
let llvm_i8 = ctx.ctx.i8_type();
|
||||
let llvm_i32 = ctx.i32;
|
||||
let llvm_i8_8 = ctx.ctx.struct_type(&[llvm_i8.array_type(8).into()], false);
|
||||
let llvm_pi8 = llvm_i8.ptr_type(AddressSpace::default());
|
||||
let llvm_pusize = ctx.size_t.ptr_type(AddressSpace::default());
|
||||
let llvm_pi8 = ctx.ptr;
|
||||
|
||||
let rpc_recv =
|
||||
ctx.declare_external("rpc_recv", Some(llvm_i32.into()), &[llvm_pi8.into()], false, &[]);
|
||||
@@ -602,8 +567,7 @@ fn format_rpc_ret<'ctx>(
|
||||
let (dtype, ndims) = unpack_ndarray_var_tys(&mut ctx.unifier, ret_ty);
|
||||
let dtype_llvm = ctx.get_llvm_type(dtype);
|
||||
let ndims = extract_ndims(&ctx.unifier, ndims);
|
||||
let ndarray = NDArrayType::new(ctx, dtype_llvm, ndims)
|
||||
.construct_uninitialized(generator, ctx, None);
|
||||
let ndarray = NDArrayType::new(ctx, dtype_llvm, ndims).construct(ctx, None);
|
||||
|
||||
// NOTE: Current content of `ndarray`:
|
||||
// - * `data` - **NOT YET** allocated.
|
||||
@@ -612,34 +576,20 @@ fn format_rpc_ret<'ctx>(
|
||||
// - * `shape` - allocated; has uninitialized values.
|
||||
// - * `strides` - allocated; has uninitialized values.
|
||||
|
||||
let itemsize = ndarray.load_itemsize(ctx); // Same as doing a `ctx.get_llvm_type` on `dtype` and get its `size_of()`.
|
||||
|
||||
// Allocates a buffer for the initial RPC'ed object, which is guaranteed to be
|
||||
// (4 + 4 * ndims) bytes with 8-byte alignment
|
||||
let sizeof_usize = ctx.size_t.size_of();
|
||||
let sizeof_usize =
|
||||
ctx.builder.build_int_truncate_or_bit_cast(sizeof_usize, ctx.size_t, "").unwrap();
|
||||
|
||||
let sizeof_ptr = llvm_i8.ptr_type(AddressSpace::default()).size_of();
|
||||
let sizeof_ptr =
|
||||
ctx.builder.build_int_z_extend_or_bit_cast(sizeof_ptr, ctx.size_t, "").unwrap();
|
||||
|
||||
let ndims = ndarray.load_ndims(ctx);
|
||||
let sizeof_shape = ctx.builder.build_int_mul(ndims, sizeof_usize, "").unwrap();
|
||||
|
||||
// Size of the buffer for the initial `rpc_recv()`.
|
||||
let unaligned_buffer_size =
|
||||
ctx.builder.build_int_add(sizeof_ptr, sizeof_shape, "").unwrap();
|
||||
|
||||
let stackptr = call_stacksave(ctx, None);
|
||||
let buffer = type_aligned_alloca(
|
||||
generator,
|
||||
ctx,
|
||||
llvm_i8_8,
|
||||
unaligned_buffer_size,
|
||||
Some("rpc.buffer"),
|
||||
);
|
||||
let buffer = ArraySliceValue::from_ptr_val(buffer, unaligned_buffer_size, None);
|
||||
|
||||
let itemsize = ctx.sizeof(ndarray.ty.dtype);
|
||||
let sizeof_ptr = ctx.sizeof(ctx.ptr);
|
||||
let sizeof_shape = ndims * ctx.sizeof(ctx.size_t);
|
||||
// Size of the buffer for the initial `rpc_recv()`.
|
||||
let unaligned_buffer_size = sizeof_ptr + sizeof_shape;
|
||||
|
||||
// Force an aligned allocation.
|
||||
let chunks = unaligned_buffer_size.div_ceil(8);
|
||||
let aligned_alloc_ty = ctx.ctx.struct_type(&[ctx.i8.array_type(8).into()], false);
|
||||
let ptr = gen_array_var(ctx, aligned_alloc_ty, chunks, Some("rpc.buffer")).value.0;
|
||||
let buffer_bytes = ctx.size_t.const_int(chunks * 8, false);
|
||||
let buffer = ArraySliceValue::new(ctx.i8.into(), ptr, buffer_bytes, None);
|
||||
|
||||
// The first call to `rpc_recv` reads the top-level ndarray object: [pdata, shape]
|
||||
//
|
||||
@@ -647,7 +597,7 @@ fn format_rpc_ret<'ctx>(
|
||||
let ndarray_nbytes = ctx
|
||||
.build_call_or_invoke(
|
||||
&rpc_recv,
|
||||
&[buffer.base_ptr(ctx, generator).into()], // Reads [usize; ndims]
|
||||
&[buffer.value.0.into()], // Reads [usize; ndims]
|
||||
"rpc.size.next",
|
||||
)
|
||||
.map(BasicValueEnum::into_int_value)
|
||||
@@ -661,7 +611,6 @@ fn format_rpc_ret<'ctx>(
|
||||
.unwrap();
|
||||
|
||||
ctx.make_assert(
|
||||
generator,
|
||||
cmp,
|
||||
"0:AssertionError",
|
||||
"Unexpected RPC termination for ndarray - Expected data buffer next",
|
||||
@@ -672,13 +621,9 @@ fn format_rpc_ret<'ctx>(
|
||||
|
||||
// Copy shape from the buffer to `ndarray.shape`.
|
||||
// We need to skip the first `sizeof(uint8_t*)` bytes to skip the `pdata` in `[pdata, shape]`.
|
||||
let pbuffer_shape =
|
||||
unsafe { buffer.ptr_offset_unchecked(ctx, generator, &sizeof_ptr, None) };
|
||||
let pbuffer_shape =
|
||||
ctx.builder.build_pointer_cast(pbuffer_shape, llvm_pusize, "").unwrap();
|
||||
|
||||
// Copy shape from buffer to `ndarray.shape`
|
||||
ndarray.copy_shape_from_array(generator, ctx, pbuffer_shape);
|
||||
let sizeof_ptr = ctx.size_t.const_int(sizeof_ptr, false);
|
||||
let pbuffer_shape = buffer.ptr_offset_unchecked(ctx, &sizeof_ptr, None);
|
||||
ndarray.shape(ctx).memcpy_from(ctx, pbuffer_shape);
|
||||
|
||||
// Restore stack from before allocation of buffer
|
||||
call_stackrestore(ctx, stackptr);
|
||||
@@ -686,8 +631,9 @@ fn format_rpc_ret<'ctx>(
|
||||
// Allocate `ndarray.data`.
|
||||
// `ndarray.shape` must be initialized beforehand in this implementation
|
||||
// (for ndarray.create_data() to know how many elements to allocate)
|
||||
unsafe { ndarray.create_data(generator, ctx) }; // NOTE: the strides of `ndarray` has also been set to contiguous in `create_data`.
|
||||
ndarray.create_data(ctx); // NOTE: the strides of `ndarray` has also been set to contiguous in `create_data`.
|
||||
|
||||
let itemsize = ctx.size_t.const_int(itemsize, false);
|
||||
// debug_assert(nelems * sizeof(T) >= ndarray_nbytes)
|
||||
if ctx.registry.codegen_options.debug {
|
||||
let num_elements = ndarray.size(ctx);
|
||||
@@ -705,7 +651,6 @@ fn format_rpc_ret<'ctx>(
|
||||
.unwrap();
|
||||
|
||||
ctx.make_assert(
|
||||
generator,
|
||||
cmp,
|
||||
"0:AssertionError",
|
||||
"Unexpected allocation size request for ndarray data - Expected up to {0} bytes, got {1} bytes",
|
||||
@@ -714,7 +659,7 @@ fn format_rpc_ret<'ctx>(
|
||||
);
|
||||
}
|
||||
|
||||
let ndarray_data = ndarray.data().base_ptr(ctx, generator);
|
||||
let ndarray_data = ndarray.load(ctx, field!(data));
|
||||
|
||||
let entry_bb = ctx.builder.get_insert_block().unwrap();
|
||||
ctx.builder.build_unconditional_branch(head_bb).unwrap();
|
||||
@@ -739,26 +684,17 @@ fn format_rpc_ret<'ctx>(
|
||||
ctx.builder.position_at_end(alloc_bb);
|
||||
// Align the allocation to sizeof(T)
|
||||
let alloc_size = round_up(ctx, alloc_size, itemsize);
|
||||
// TODO(Derppening): Candidate for refactor into type_aligned_alloca
|
||||
let alloc_ptr = ctx
|
||||
.builder
|
||||
.build_array_alloca(
|
||||
dtype_llvm,
|
||||
ctx.builder.build_int_unsigned_div(alloc_size, itemsize, "").unwrap(),
|
||||
"rpc.alloc",
|
||||
)
|
||||
.unwrap();
|
||||
let alloc_ptr =
|
||||
ctx.builder.build_pointer_cast(alloc_ptr, llvm_pi8, "rpc.alloc.ptr").unwrap();
|
||||
let size = ctx.builder.build_int_unsigned_div(alloc_size, itemsize, "").unwrap();
|
||||
let alloc_ptr = gen_dyn_array_var(ctx, dtype_llvm, size, Some("rpc.alloc")).value.0;
|
||||
phi.add_incoming(&[(&alloc_ptr, alloc_bb)]);
|
||||
ctx.builder.build_unconditional_branch(head_bb).unwrap();
|
||||
|
||||
ctx.builder.position_at_end(tail_bb);
|
||||
ndarray.as_abi_value(ctx).into()
|
||||
ndarray.value.into()
|
||||
}
|
||||
|
||||
_ => {
|
||||
let slot = ctx.builder.build_alloca(llvm_ret_ty, "rpc.ret.slot").unwrap();
|
||||
let slot = gen_var(ctx, llvm_ret_ty, Some("rpc.ret.slot"));
|
||||
let slotgen = ctx.builder.build_bit_cast(slot, llvm_pi8, "rpc.ret.ptr").unwrap();
|
||||
ctx.builder.build_unconditional_branch(head_bb).unwrap();
|
||||
ctx.builder.position_at_end(head_bb);
|
||||
@@ -777,10 +713,7 @@ fn format_rpc_ret<'ctx>(
|
||||
ctx.builder.build_conditional_branch(is_done, tail_bb, alloc_bb).unwrap();
|
||||
ctx.builder.position_at_end(alloc_bb);
|
||||
|
||||
let alloc_ptr =
|
||||
ctx.builder.build_array_alloca(llvm_pi8, alloc_size, "rpc.alloc").unwrap();
|
||||
let alloc_ptr =
|
||||
ctx.builder.build_bit_cast(alloc_ptr, llvm_pi8, "rpc.alloc.ptr").unwrap();
|
||||
let alloc_ptr = gen_dyn_array_var(ctx, llvm_pi8, alloc_size, Some("rpc.alloc")).value.0;
|
||||
phi.add_incoming(&[(&alloc_ptr, alloc_bb)]);
|
||||
ctx.builder.build_unconditional_branch(head_bb).unwrap();
|
||||
|
||||
@@ -797,13 +730,12 @@ fn rpc_codegen_callback_fn<'ctx>(
|
||||
obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
fun: (&FunSignature, DefinitionId),
|
||||
args: Vec<(Option<StrRef>, ValueEnum<'ctx>)>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
is_async: bool,
|
||||
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
|
||||
let int8 = ctx.ctx.i8_type();
|
||||
let int8 = ctx.i8;
|
||||
let int32 = ctx.i32;
|
||||
let size_type = ctx.size_t;
|
||||
let ptr_type = int8.ptr_type(AddressSpace::default());
|
||||
let ptr_type = ctx.ptr;
|
||||
let tag_ptr_type = ctx.ctx.struct_type(&[ptr_type.into(), size_type.into()], false);
|
||||
|
||||
let service_id = int32.const_int(fun.1.0 as u64, false);
|
||||
@@ -848,13 +780,10 @@ fn rpc_codegen_callback_fn<'ctx>(
|
||||
})
|
||||
.as_pointer_value();
|
||||
|
||||
let arg_length = args.len() + usize::from(obj.is_some());
|
||||
let arg_length = args.len() as u64 + u64::from(obj.is_some());
|
||||
|
||||
let stackptr = call_stacksave(ctx, Some("rpc.stack"));
|
||||
let args_ptr = ctx
|
||||
.builder
|
||||
.build_array_alloca(ptr_type, ctx.i32.const_int(arg_length as u64, false), "argptr")
|
||||
.unwrap();
|
||||
let args_ptr = gen_array_var(ctx, ctx.ptr, arg_length, Some("argptr"));
|
||||
|
||||
// -- rpc args handling
|
||||
let mut keys = fun.0.args.clone();
|
||||
@@ -864,8 +793,7 @@ fn rpc_codegen_callback_fn<'ctx>(
|
||||
}
|
||||
// default value handling
|
||||
for k in keys {
|
||||
mapping
|
||||
.insert(k.name, ctx.gen_symbol_val(generator, &k.default_value.unwrap(), k.ty).into());
|
||||
mapping.insert(k.name, ctx.gen_symbol_val(&k.default_value.unwrap(), k.ty).into());
|
||||
}
|
||||
// reorder the parameters
|
||||
let mut real_params = fun
|
||||
@@ -876,13 +804,13 @@ fn rpc_codegen_callback_fn<'ctx>(
|
||||
mapping
|
||||
.remove(&arg.name)
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator, arg.ty)
|
||||
.to_basic_value_enum(ctx, arg.ty)
|
||||
.map(|llvm_val| (llvm_val, arg.ty))
|
||||
})
|
||||
.collect::<Result<Vec<(_, _)>, _>>()?;
|
||||
if let Some(obj) = obj {
|
||||
if let ValueEnum::Static(obj_val) = obj.1 {
|
||||
real_params.insert(0, (obj_val.get_const_obj(ctx, generator), obj.0));
|
||||
real_params.insert(0, (obj_val.get_const_obj(ctx), obj.0));
|
||||
} else {
|
||||
// should be an error here...
|
||||
panic!("only host object is allowed");
|
||||
@@ -890,20 +818,14 @@ fn rpc_codegen_callback_fn<'ctx>(
|
||||
}
|
||||
|
||||
for (i, (arg, arg_ty)) in real_params.iter().enumerate() {
|
||||
let arg_slot = format_rpc_arg(generator, ctx, (*arg, *arg_ty, i));
|
||||
let arg_ptr = unsafe {
|
||||
ctx.builder.build_gep(
|
||||
args_ptr,
|
||||
&[int32.const_int(i as u64, false)],
|
||||
&format!("rpc.arg{i}"),
|
||||
)
|
||||
}
|
||||
.unwrap();
|
||||
ctx.builder.build_store(arg_ptr, arg_slot).unwrap();
|
||||
let arg_slot = format_rpc_arg(ctx, (*arg, *arg_ty, i));
|
||||
let name = format!("rpc.arg{i}");
|
||||
let i = ctx.size_t.const_int(i as u64, false);
|
||||
args_ptr.set_unchecked(ctx, &i, arg_slot, Some(&name));
|
||||
}
|
||||
|
||||
call_extern!(ctx: void "rpc.send" =
|
||||
(if is_async { "rpc_send_async" } else { "rpc_send" })(service_id, tag_ptr, args_ptr));
|
||||
(if is_async { "rpc_send_async" } else { "rpc_send" })(service_id, tag_ptr, args_ptr.value.0));
|
||||
|
||||
// reclaim stack space used by arguments
|
||||
call_stackrestore(ctx, stackptr);
|
||||
@@ -912,7 +834,7 @@ fn rpc_codegen_callback_fn<'ctx>(
|
||||
// async RPCs do not return any values
|
||||
Ok(None)
|
||||
} else {
|
||||
let result = format_rpc_ret(generator, ctx, fun.0.ret);
|
||||
let result = format_rpc_ret(ctx, fun.0.ret);
|
||||
|
||||
// Here we call `basic_type_all` to ensure that the return type is not, nor contains, a
|
||||
// pointer type which may require further allocation, in which case the stack should not
|
||||
@@ -927,31 +849,28 @@ fn rpc_codegen_callback_fn<'ctx>(
|
||||
|
||||
pub fn attributes_writeback<'ctx>(
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
inner_resolver: &InnerResolver,
|
||||
host_attributes: &PyObject,
|
||||
host_attributes: &Py<PyAny>,
|
||||
return_obj: Option<(Type, ValueEnum<'ctx>)>,
|
||||
) -> Result<(), String> {
|
||||
Python::with_gil(|py| -> PyResult<Result<(), String>> {
|
||||
let host_attributes = host_attributes.downcast_bound::<PyList>(py)?;
|
||||
let top_levels = ctx.top_level.definitions.read();
|
||||
let globals = inner_resolver.global_value_ids.read();
|
||||
Python::attach(|py| -> PyResult<Result<(), String>> {
|
||||
let host_attributes = host_attributes.cast_bound::<PyList>(py)?;
|
||||
let int32 = ctx.i32;
|
||||
let zero = int32.const_zero();
|
||||
let mut values = Vec::new();
|
||||
let mut scratch_buffer = Vec::new();
|
||||
|
||||
if let Some((ty, obj)) = return_obj {
|
||||
values.push((ty, obj.to_basic_value_enum(ctx, generator, ty).unwrap()));
|
||||
values.push((ty, obj.to_basic_value_enum(ctx, ty).unwrap()));
|
||||
}
|
||||
|
||||
for val in (*globals).values() {
|
||||
for val in (*inner_resolver.global_value_ids.read()).values() {
|
||||
let val = val.bind(py);
|
||||
let ty = inner_resolver.get_obj_type(
|
||||
py,
|
||||
val,
|
||||
&mut ctx.unifier,
|
||||
&top_levels,
|
||||
&ctx.top_level.definitions.read(),
|
||||
&ctx.primitives,
|
||||
)?;
|
||||
if let Err(ty) = ty {
|
||||
@@ -966,10 +885,7 @@ pub fn attributes_writeback<'ctx>(
|
||||
let pydict = PyDict::new(py);
|
||||
pydict.set_item("obj", val)?;
|
||||
host_attributes.append(pydict)?;
|
||||
values.push((
|
||||
ty,
|
||||
inner_resolver.get_obj_value(py, val, ctx, generator, ty)?.unwrap(),
|
||||
));
|
||||
values.push((ty, inner_resolver.get_obj_value(py, val, ctx, ty)?.unwrap()));
|
||||
}
|
||||
}
|
||||
TypeEnum::TObj { fields, obj_id, .. }
|
||||
@@ -978,7 +894,7 @@ pub fn attributes_writeback<'ctx>(
|
||||
// we only care about primitive attributes
|
||||
// for non-primitive attributes, they should be in another global
|
||||
let mut attributes = Vec::new();
|
||||
let obj = inner_resolver.get_obj_value(py, val, ctx, generator, ty)?.unwrap();
|
||||
let obj = inner_resolver.get_obj_value(py, val, ctx, ty)?.unwrap();
|
||||
for (name, (field_ty, attr_kind)) in fields {
|
||||
if !attr_kind.is_mutable() {
|
||||
continue;
|
||||
@@ -1022,9 +938,7 @@ pub fn attributes_writeback<'ctx>(
|
||||
};
|
||||
let args: Vec<_> =
|
||||
values.into_iter().map(|(_, val)| (None, ValueEnum::Dynamic(val))).collect();
|
||||
if let Err(e) =
|
||||
rpc_codegen_callback_fn(ctx, None, (&fun, PrimDef::Int32.id()), args, generator, true)
|
||||
{
|
||||
if let Err(e) = rpc_codegen_callback_fn(ctx, None, (&fun, DefinitionId(0)), args, true) {
|
||||
return Ok(Err(e));
|
||||
}
|
||||
Ok(Ok(()))
|
||||
@@ -1034,8 +948,8 @@ pub fn attributes_writeback<'ctx>(
|
||||
}
|
||||
|
||||
pub fn rpc_codegen_callback(is_async: bool) -> Arc<GenCall> {
|
||||
Arc::new(GenCall::new(Box::new(move |ctx, obj, fun, args, generator| {
|
||||
rpc_codegen_callback_fn(ctx, obj, fun, args, generator, is_async)
|
||||
Arc::new(GenCall::new(Box::new(move |ctx, obj, fun, args| {
|
||||
rpc_codegen_callback_fn(ctx, obj, fun, args, is_async)
|
||||
})))
|
||||
}
|
||||
|
||||
@@ -1077,31 +991,28 @@ fn get_fprintf_format_constant<'ctx>(
|
||||
/// * `as_rtio` - Whether to print to `rtio_log` instead of `core_log`.
|
||||
fn polymorphic_print<'ctx>(
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
values: &[(Type, ValueEnum<'ctx>)],
|
||||
separator: &str,
|
||||
suffix: Option<&str>,
|
||||
as_repr: bool,
|
||||
as_rtio: bool,
|
||||
) -> Result<(), String> {
|
||||
let printf = |ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
fmt: String,
|
||||
args: Vec<BasicValueEnum<'ctx>>| {
|
||||
debug_assert!(!fmt.is_empty());
|
||||
debug_assert_eq!(fmt.as_bytes().last().unwrap(), &0u8);
|
||||
let printf =
|
||||
|ctx: &mut CodeGenContext<'ctx, '_>, fmt: String, args: Vec<BasicValueEnum<'ctx>>| {
|
||||
debug_assert!(!fmt.is_empty());
|
||||
debug_assert_eq!(fmt.as_bytes().last().unwrap(), &0u8);
|
||||
|
||||
let llvm_i32 = ctx.i32;
|
||||
let llvm_i32 = ctx.i32;
|
||||
|
||||
let fmt = ctx.gen_string(generator, fmt);
|
||||
let fmt = unsafe { fmt.get_field_at_index_unchecked(0) }.into_pointer_value();
|
||||
let fmt = ctx.gen_string(fmt);
|
||||
let fmt = unsafe { fmt.get_field_at_index_unchecked(0) }.into_pointer_value();
|
||||
|
||||
if as_rtio {
|
||||
call_extern!(ctx: void _ = "rtio_log"(fmt; ...args));
|
||||
} else {
|
||||
call_extern!(ctx: llvm_i32 _ = "core_log"(fmt; ...args));
|
||||
}
|
||||
};
|
||||
if as_rtio {
|
||||
call_extern!(ctx: void _ = "rtio_log"(fmt; ...args));
|
||||
} else {
|
||||
call_extern!(ctx: llvm_i32 _ = "core_log"(fmt; ...args));
|
||||
}
|
||||
};
|
||||
|
||||
let llvm_i32 = ctx.i32;
|
||||
let llvm_i64 = ctx.i64;
|
||||
@@ -1113,18 +1024,17 @@ fn polymorphic_print<'ctx>(
|
||||
let mut args = Vec::new();
|
||||
|
||||
let flush = |ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
fmt: &mut String,
|
||||
args: &mut Vec<BasicValueEnum<'ctx>>| {
|
||||
if !fmt.is_empty() {
|
||||
fmt.push('\0');
|
||||
printf(ctx, generator, mem::take(fmt), mem::take(args));
|
||||
printf(ctx, mem::take(fmt), mem::take(args));
|
||||
}
|
||||
};
|
||||
|
||||
for (ty, value) in values {
|
||||
let ty = *ty;
|
||||
let value = value.clone().to_basic_value_enum(ctx, generator, ty).unwrap();
|
||||
let value = value.to_basic_value_enum(ctx, ty).unwrap();
|
||||
|
||||
if !fmt.is_empty() {
|
||||
fmt.push_str(separator);
|
||||
@@ -1133,13 +1043,13 @@ fn polymorphic_print<'ctx>(
|
||||
match &*ctx.unifier.get_ty_immutable(ty) {
|
||||
TypeEnum::TTuple { ty: tys, is_vararg_ctx: false } => {
|
||||
let pvalue = {
|
||||
let pvalue = generator.gen_var_alloc(ctx, value.get_type(), None).unwrap();
|
||||
ctx.builder.build_store(pvalue, value).unwrap();
|
||||
let pvalue = gen_var(ctx, value.get_type(), None);
|
||||
typed_store(&ctx.builder, pvalue, value);
|
||||
pvalue
|
||||
};
|
||||
|
||||
fmt.push('(');
|
||||
flush(ctx, generator, &mut fmt, &mut args);
|
||||
flush(ctx, &mut fmt, &mut args);
|
||||
|
||||
let tuple_vals = tys
|
||||
.iter()
|
||||
@@ -1154,7 +1064,7 @@ fn polymorphic_print<'ctx>(
|
||||
})
|
||||
.collect_vec();
|
||||
|
||||
polymorphic_print(ctx, generator, &tuple_vals, ", ", None, true, as_rtio)?;
|
||||
polymorphic_print(ctx, &tuple_vals, ", ", None, true, as_rtio)?;
|
||||
|
||||
if tuple_vals.len() == 1 {
|
||||
fmt.push_str(",)");
|
||||
@@ -1171,17 +1081,17 @@ fn polymorphic_print<'ctx>(
|
||||
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::Bool.id() => {
|
||||
fmt.push_str("%.*s");
|
||||
|
||||
let true_str = ctx.gen_string(generator, "True");
|
||||
let true_str = ctx.gen_string("True");
|
||||
let true_data =
|
||||
unsafe { true_str.get_field_at_index_unchecked(0) }.into_pointer_value();
|
||||
let true_len = unsafe { true_str.get_field_at_index_unchecked(1) }.into_int_value();
|
||||
let false_str = ctx.gen_string(generator, "False");
|
||||
let false_str = ctx.gen_string("False");
|
||||
let false_data =
|
||||
unsafe { false_str.get_field_at_index_unchecked(0) }.into_pointer_value();
|
||||
let false_len =
|
||||
unsafe { false_str.get_field_at_index_unchecked(1) }.into_int_value();
|
||||
|
||||
let bool_val = generator.bool_to_i1(ctx, value.into_int_value());
|
||||
let bool_val = bool_to_i1(ctx, value.into_int_value());
|
||||
|
||||
args.extend([
|
||||
ctx.builder.build_select(bool_val, true_len, false_len, "").unwrap(),
|
||||
@@ -1234,120 +1144,104 @@ fn polymorphic_print<'ctx>(
|
||||
let elem_ty = *params.iter().next().unwrap().1;
|
||||
|
||||
fmt.push('[');
|
||||
flush(ctx, generator, &mut fmt, &mut args);
|
||||
flush(ctx, &mut fmt, &mut args);
|
||||
|
||||
let val =
|
||||
ListValue::from_pointer_value(value.into_pointer_value(), llvm_usize, None);
|
||||
let len = val.load_size(ctx, None);
|
||||
let val = ListType::from_unifier_type(ctx, ty)
|
||||
.map_value(value.into_pointer_value(), None);
|
||||
let len = val.load(ctx, field!(len));
|
||||
let last =
|
||||
ctx.builder.build_int_sub(len, llvm_usize.const_int(1, false), "").unwrap();
|
||||
|
||||
gen_for_callback_incrementing(
|
||||
generator,
|
||||
&mut (),
|
||||
ctx,
|
||||
None,
|
||||
llvm_usize.const_zero(),
|
||||
(len, false),
|
||||
|generator, ctx, _, i| {
|
||||
let elem = unsafe { val.data().get_unchecked(ctx, generator, &i, None) };
|
||||
|(), ctx, _, i| {
|
||||
let elem = val.data(ctx).get_unchecked(ctx, &i, None);
|
||||
|
||||
polymorphic_print(
|
||||
ctx,
|
||||
generator,
|
||||
&[(elem_ty, elem.into())],
|
||||
"",
|
||||
None,
|
||||
true,
|
||||
as_rtio,
|
||||
)?;
|
||||
polymorphic_print(ctx, &[(elem_ty, elem)], "", None, true, as_rtio)?;
|
||||
|
||||
gen_if_callback(
|
||||
generator,
|
||||
&mut (),
|
||||
ctx,
|
||||
|_, ctx| {
|
||||
|(), ctx| {
|
||||
Ok(ctx
|
||||
.builder
|
||||
.build_int_compare(IntPredicate::ULT, i, last, "")
|
||||
.unwrap())
|
||||
},
|
||||
|generator, ctx| {
|
||||
printf(ctx, generator, ", \0".into(), Vec::default());
|
||||
|(), ctx| {
|
||||
printf(ctx, ", \0".into(), Vec::default());
|
||||
|
||||
Ok(())
|
||||
},
|
||||
|_, _| Ok(()),
|
||||
|(), _| Ok(()),
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
},
|
||||
llvm_usize.const_int(1, false),
|
||||
|(), _| Ok(()),
|
||||
)?;
|
||||
|
||||
fmt.push(']');
|
||||
flush(ctx, generator, &mut fmt, &mut args);
|
||||
flush(ctx, &mut fmt, &mut args);
|
||||
}
|
||||
|
||||
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::NDArray.id() => {
|
||||
fmt.push_str("array([");
|
||||
flush(ctx, generator, &mut fmt, &mut args);
|
||||
flush(ctx, &mut fmt, &mut args);
|
||||
|
||||
let (dtype, _) = unpack_ndarray_var_tys(&mut ctx.unifier, ty);
|
||||
let ndarray = NDArrayType::from_unifier_type(ctx, ty)
|
||||
.map_pointer_value(value.into_pointer_value(), None);
|
||||
.map_value(value.into_pointer_value(), None);
|
||||
|
||||
let num_0 = llvm_usize.const_zero();
|
||||
|
||||
// Print `ndarray` as a flat list delimited by interspersed with ", \0"
|
||||
ndarray.foreach(generator, ctx, |generator, ctx, _, hdl| {
|
||||
ndarray.foreach(ctx, |ctx, _, hdl| {
|
||||
let i = hdl.get_index(ctx);
|
||||
let scalar = hdl.get_scalar(ctx);
|
||||
|
||||
// if (i != 0) puts(", ");
|
||||
gen_if_callback(
|
||||
generator,
|
||||
&mut (),
|
||||
ctx,
|
||||
|_, ctx| {
|
||||
|(), ctx| {
|
||||
let not_first = ctx
|
||||
.builder
|
||||
.build_int_compare(IntPredicate::NE, i, num_0, "")
|
||||
.unwrap();
|
||||
Ok(not_first)
|
||||
},
|
||||
|generator, ctx| {
|
||||
printf(ctx, generator, ", \0".into(), Vec::default());
|
||||
|(), ctx| {
|
||||
printf(ctx, ", \0".into(), Vec::default());
|
||||
Ok(())
|
||||
},
|
||||
|_, _| Ok(()),
|
||||
|(), _| Ok(()),
|
||||
)?;
|
||||
|
||||
// Print element
|
||||
polymorphic_print(
|
||||
ctx,
|
||||
generator,
|
||||
&[(dtype, scalar.into())],
|
||||
"",
|
||||
None,
|
||||
true,
|
||||
as_rtio,
|
||||
)?;
|
||||
polymorphic_print(ctx, &[(dtype, scalar.into())], "", None, true, as_rtio)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
fmt.push_str(")]");
|
||||
flush(ctx, generator, &mut fmt, &mut args);
|
||||
flush(ctx, &mut fmt, &mut args);
|
||||
}
|
||||
|
||||
TypeEnum::TObj { obj_id, .. } if *obj_id == PrimDef::Range.id() => {
|
||||
fmt.push_str("range(");
|
||||
flush(ctx, generator, &mut fmt, &mut args);
|
||||
flush(ctx, &mut fmt, &mut args);
|
||||
|
||||
let val = RangeType::new(ctx).map_pointer_value(value.into_pointer_value(), None);
|
||||
let val = RangeType::new(ctx).map_value(value.into_pointer_value(), None);
|
||||
|
||||
let (start, stop, step) = destructure_range(ctx, val);
|
||||
|
||||
polymorphic_print(
|
||||
ctx,
|
||||
generator,
|
||||
&[
|
||||
(ctx.primitives.int32, start.into()),
|
||||
(ctx.primitives.int32, stop.into()),
|
||||
@@ -1369,35 +1263,11 @@ fn polymorphic_print<'ctx>(
|
||||
get_fprintf_format_constant(llvm_usize, llvm_i64, false),
|
||||
);
|
||||
|
||||
let exn = value.into_pointer_value();
|
||||
let name = ctx
|
||||
.build_in_bounds_gep_and_load(
|
||||
exn,
|
||||
&[llvm_i32.const_zero(), llvm_i32.const_zero()],
|
||||
None,
|
||||
)
|
||||
.into_int_value();
|
||||
let param0 = ctx
|
||||
.build_in_bounds_gep_and_load(
|
||||
exn,
|
||||
&[llvm_i32.const_zero(), llvm_i32.const_int(6, false)],
|
||||
None,
|
||||
)
|
||||
.into_int_value();
|
||||
let param1 = ctx
|
||||
.build_in_bounds_gep_and_load(
|
||||
exn,
|
||||
&[llvm_i32.const_zero(), llvm_i32.const_int(7, false)],
|
||||
None,
|
||||
)
|
||||
.into_int_value();
|
||||
let param2 = ctx
|
||||
.build_in_bounds_gep_and_load(
|
||||
exn,
|
||||
&[llvm_i32.const_zero(), llvm_i32.const_int(8, false)],
|
||||
None,
|
||||
)
|
||||
.into_int_value();
|
||||
let exn = ExceptionType::new(ctx).map_value(value.into_pointer_value(), None);
|
||||
let name = exn.load(ctx, field!(name));
|
||||
let param0 = exn.load(ctx, field!(param0));
|
||||
let param1 = exn.load(ctx, field!(param1));
|
||||
let param2 = exn.load(ctx, field!(param2));
|
||||
|
||||
fmt.push_str(fmt_str.as_str());
|
||||
args.extend_from_slice(&[name.into(), param0.into(), param1.into(), param2.into()]);
|
||||
@@ -1411,7 +1281,7 @@ fn polymorphic_print<'ctx>(
|
||||
}
|
||||
|
||||
fmt.push_str(suffix);
|
||||
flush(ctx, generator, &mut fmt, &mut args);
|
||||
flush(ctx, &mut fmt, &mut args);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1419,12 +1289,11 @@ fn polymorphic_print<'ctx>(
|
||||
/// Invokes the `core_log` intrinsic function.
|
||||
pub fn call_core_log_impl<'ctx>(
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
arg: (Type, BasicValueEnum<'ctx>),
|
||||
) -> Result<(), String> {
|
||||
let (arg_ty, arg_val) = arg;
|
||||
|
||||
polymorphic_print(ctx, generator, &[(arg_ty, arg_val.into())], " ", Some("\n"), false, false)?;
|
||||
polymorphic_print(ctx, &[(arg_ty, arg_val.into())], " ", Some("\n"), false, false)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1432,7 +1301,6 @@ pub fn call_core_log_impl<'ctx>(
|
||||
/// Invokes the `rtio_log` intrinsic function.
|
||||
pub fn call_rtio_log_impl<'ctx>(
|
||||
ctx: &mut CodeGenContext<'ctx, '_>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
channel: StructValue<'ctx>,
|
||||
arg: (Type, BasicValueEnum<'ctx>),
|
||||
) -> Result<(), String> {
|
||||
@@ -1440,14 +1308,13 @@ pub fn call_rtio_log_impl<'ctx>(
|
||||
|
||||
polymorphic_print(
|
||||
ctx,
|
||||
generator,
|
||||
&[(ctx.primitives.str, channel.into())],
|
||||
" ",
|
||||
Some("\x1E"),
|
||||
false,
|
||||
true,
|
||||
)?;
|
||||
polymorphic_print(ctx, generator, &[(arg_ty, arg_val.into())], " ", Some("\x1D"), false, true)?;
|
||||
polymorphic_print(ctx, &[(arg_ty, arg_val.into())], " ", Some("\x1D"), false, true)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1458,15 +1325,14 @@ pub fn gen_core_log<'ctx>(
|
||||
obj: Option<&(Type, ValueEnum<'ctx>)>,
|
||||
fun: (&FunSignature, DefinitionId),
|
||||
args: &[(Option<StrRef>, ValueEnum<'ctx>)],
|
||||
generator: &mut dyn CodeGenerator,
|
||||
) -> Result<(), String> {
|
||||
assert!(obj.is_none());
|
||||
assert_eq!(args.len(), 1);
|
||||
|
||||
let value_ty = fun.0.args[0].ty;
|
||||
let value_arg = args[0].1.clone().to_basic_value_enum(ctx, generator, value_ty)?;
|
||||
let value_arg = args[0].1.clone().to_basic_value_enum(ctx, value_ty)?;
|
||||
|
||||
call_core_log_impl(ctx, generator, (value_ty, value_arg))
|
||||
call_core_log_impl(ctx, (value_ty, value_arg))
|
||||
}
|
||||
|
||||
/// Generates a call to `rtio_log`.
|
||||
@@ -1475,17 +1341,15 @@ pub fn gen_rtio_log<'ctx>(
|
||||
obj: Option<&(Type, ValueEnum<'ctx>)>,
|
||||
fun: (&FunSignature, DefinitionId),
|
||||
args: &[(Option<StrRef>, ValueEnum<'ctx>)],
|
||||
generator: &mut dyn CodeGenerator,
|
||||
) -> Result<(), String> {
|
||||
assert!(obj.is_none());
|
||||
assert_eq!(args.len(), 2);
|
||||
|
||||
let channel_ty = fun.0.args[0].ty;
|
||||
assert!(ctx.unifier.unioned(channel_ty, ctx.primitives.str));
|
||||
let channel_arg =
|
||||
args[0].1.clone().to_basic_value_enum(ctx, generator, channel_ty)?.into_struct_value();
|
||||
let channel_arg = args[0].1.clone().to_basic_value_enum(ctx, channel_ty)?.into_struct_value();
|
||||
let value_ty = fun.0.args[1].ty;
|
||||
let value_arg = args[1].1.clone().to_basic_value_enum(ctx, generator, value_ty)?;
|
||||
let value_arg = args[1].1.clone().to_basic_value_enum(ctx, value_ty)?;
|
||||
|
||||
call_rtio_log_impl(ctx, generator, channel_arg, (value_ty, value_arg))
|
||||
call_rtio_log_impl(ctx, channel_arg, (value_ty, value_arg))
|
||||
}
|
||||
|
||||
@@ -29,220 +29,191 @@ impl InnerResolver {
|
||||
let mut str = String::new();
|
||||
str.push_str("nac3artiq::InnerResolver {");
|
||||
|
||||
{
|
||||
let id_to_type = self.id_to_type.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tid_to_type: {{{}}},",
|
||||
fmt_elems(
|
||||
id_to_type
|
||||
.iter()
|
||||
.sorted_by_cached_key(|(k, _)| k.to_string())
|
||||
.map(|(k, v)| {
|
||||
let ty_str = unifier.as_ref().map_or_else(
|
||||
|| format!("{v:?}"),
|
||||
|unifier| unifier.stringify(*v),
|
||||
);
|
||||
format!("\t\t{k} -> {ty_str}")
|
||||
})
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
),
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tid_to_type: {{{}}},",
|
||||
fmt_elems(
|
||||
self.id_to_type
|
||||
.read()
|
||||
.iter()
|
||||
.sorted_by_cached_key(|(k, _)| k.to_string())
|
||||
.map(|(k, v)| {
|
||||
let ty_str = unifier
|
||||
.as_ref()
|
||||
.map_or_else(|| format!("{v:?}"), |unifier| unifier.stringify(*v));
|
||||
format!("\t\t{k} -> {ty_str}")
|
||||
})
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
),
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
{
|
||||
let id_to_def = self.id_to_def.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tid_to_def: {{{}}},",
|
||||
fmt_elems(
|
||||
id_to_def
|
||||
.iter()
|
||||
.sorted_by_cached_key(|(k, _)| k.to_string())
|
||||
.map(|(k, v)| {
|
||||
let tld_str = tld.map_or_else(
|
||||
|| format!("{v:?}"),
|
||||
|tlds| stringify_tld(&tlds[v.0]),
|
||||
);
|
||||
format!("\t\t{k} -> {tld_str}")
|
||||
})
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tid_to_def: {{{}}},",
|
||||
fmt_elems(
|
||||
self.id_to_def
|
||||
.read()
|
||||
.iter()
|
||||
.sorted_by_cached_key(|(k, _)| k.to_string())
|
||||
.map(|(k, v)| {
|
||||
let tld_str = tld
|
||||
.map_or_else(|| format!("{v:?}"), |tlds| stringify_tld(&tlds[v.0]));
|
||||
format!("\t\t{k} -> {tld_str}")
|
||||
})
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
{
|
||||
let id_to_pyval = self.id_to_pyval.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tid_to_pyval: {{{}}},",
|
||||
fmt_elems(
|
||||
id_to_pyval
|
||||
.iter()
|
||||
.sorted_by_cached_key(|(k, _)| k.to_string())
|
||||
.map(|(k, v)| { format!("\t\t{k} -> {}", stringify_pyvalue_handle(v)) })
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tid_to_pyval: {{{}}},",
|
||||
fmt_elems(
|
||||
self.id_to_pyval
|
||||
.read()
|
||||
.iter()
|
||||
.sorted_by_cached_key(|(k, _)| k.to_string())
|
||||
.map(|(k, v)| { format!("\t\t{k} -> {}", stringify_pyvalue_handle(v)) })
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
{
|
||||
let id_to_primitive = self.id_to_primitive.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tid_to_primitive: {{{}}},",
|
||||
fmt_elems(
|
||||
id_to_primitive
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| { format!("\t\t{k} -> {v:?}") })
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tid_to_primitive: {{{}}},",
|
||||
fmt_elems(
|
||||
self.id_to_primitive
|
||||
.read()
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| { format!("\t\t{k} -> {v:?}") })
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
{
|
||||
let field_to_val = self.field_to_val.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tfield_to_val: {{{}}},",
|
||||
fmt_elems(
|
||||
field_to_val
|
||||
.iter()
|
||||
.sorted_by_key(|((id, _), _)| *id)
|
||||
.map(|((id, name), pyval)| {
|
||||
format!(
|
||||
"\t\t({id}, {name}) -> {}",
|
||||
pyval.as_ref().map_or_else(
|
||||
|| String::from("None"),
|
||||
|pyval| format!(
|
||||
"Some({})",
|
||||
stringify_pyvalue_handle(pyval)
|
||||
)
|
||||
)
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tfield_to_val: {{{}}},",
|
||||
fmt_elems(
|
||||
self.field_to_val
|
||||
.read()
|
||||
.iter()
|
||||
.sorted_by_key(|((id, _), _)| *id)
|
||||
.map(|((id, name), pyval)| {
|
||||
format!(
|
||||
"\t\t({id}, {name}) -> {}",
|
||||
pyval.as_ref().map_or_else(
|
||||
|| String::from("None"),
|
||||
|pyval| format!("Some({})", stringify_pyvalue_handle(pyval))
|
||||
)
|
||||
})
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
)
|
||||
})
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
{
|
||||
let global_value_ids = self.global_value_ids.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tglobal_value_ids: {{{}}},",
|
||||
fmt_elems(
|
||||
global_value_ids
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| format!("\t\t{k} -> {v}"))
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tglobal_value_ids: {{{}}},",
|
||||
fmt_elems(
|
||||
self.global_value_ids
|
||||
.read()
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| format!("\t\t{k} -> {v}"))
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
{
|
||||
let pyid_to_def = self.pyid_to_def.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tpyid_to_def: {{{}}},",
|
||||
fmt_elems(
|
||||
pyid_to_def
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| {
|
||||
let tld_str = tld.map_or_else(
|
||||
|| format!("{v:?}"),
|
||||
|tlds| stringify_tld(&tlds[v.0]),
|
||||
);
|
||||
format!("\t\t{k} -> {tld_str}")
|
||||
})
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tpyid_to_def: {{{}}},",
|
||||
fmt_elems(
|
||||
self.pyid_to_def
|
||||
.read()
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| {
|
||||
let tld_str = tld
|
||||
.map_or_else(|| format!("{v:?}"), |tlds| stringify_tld(&tlds[v.0]));
|
||||
format!("\t\t{k} -> {tld_str}")
|
||||
})
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
{
|
||||
let pyid_to_type = self.pyid_to_type.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tpyid_to_type: {{{}}},",
|
||||
fmt_elems(
|
||||
pyid_to_type
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| {
|
||||
let ty_str = unifier.as_ref().map_or_else(
|
||||
|| format!("{v:?}"),
|
||||
|unifier| unifier.stringify(*v),
|
||||
);
|
||||
format!("\t\t{k} -> {ty_str}")
|
||||
})
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tpyid_to_type: {{{}}},",
|
||||
fmt_elems(
|
||||
self.pyid_to_type
|
||||
.read()
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| {
|
||||
let ty_str = unifier
|
||||
.as_ref()
|
||||
.map_or_else(|| format!("{v:?}"), |unifier| unifier.stringify(*v));
|
||||
format!("\t\t{k} -> {ty_str}")
|
||||
})
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
{
|
||||
let string_store = self.string_store.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tstring_store: {{{}}},",
|
||||
fmt_elems(
|
||||
string_store
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| format!("\t\t{k} -> {v}"))
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\tstring_store: {{{}}},",
|
||||
fmt_elems(
|
||||
self.string_store
|
||||
.read()
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| format!("\t\t{k} -> {v}"))
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
{
|
||||
let exception_ids = self.exception_ids.read();
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\texception_ids: {{{}}},",
|
||||
fmt_elems(
|
||||
exception_ids
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| format!("\t\t{k} -> {v}"))
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
str.push_str(
|
||||
format!(
|
||||
"\n\texception_ids: {{{}}},",
|
||||
fmt_elems(
|
||||
self.exception_ids
|
||||
.read()
|
||||
.iter()
|
||||
.sorted_by_key(|(k, _)| *k)
|
||||
.map(|(k, v)| format!("\t\t{k} -> {v}"))
|
||||
.join(",\n")
|
||||
.as_str()
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
}
|
||||
)
|
||||
.as_str(),
|
||||
);
|
||||
|
||||
let name_to_pyid = &self.name_to_pyid;
|
||||
str.push_str(
|
||||
|
||||
1005
nac3artiq/src/lib.rs
1005
nac3artiq/src/lib.rs
File diff suppressed because it is too large
Load Diff
@@ -24,13 +24,13 @@ pub use builtins::*;
|
||||
pub mod builtins {
|
||||
use pyo3::{
|
||||
prelude::*,
|
||||
sync::GILOnceCell,
|
||||
sync::PyOnceLock,
|
||||
types::{PyAnyMethods, PyBool, PyCFunction, PyInt, PyModule, PyString, PyType},
|
||||
};
|
||||
|
||||
/// Returns a reference to this module.
|
||||
pub fn module(py: Python<'_>) -> PyResult<&Bound<'_, PyModule>> {
|
||||
static MODULE: GILOnceCell<Py<PyModule>> = GILOnceCell::new();
|
||||
static MODULE: PyOnceLock<Py<PyModule>> = PyOnceLock::new();
|
||||
|
||||
MODULE
|
||||
.get_or_try_init(py, || {
|
||||
@@ -43,7 +43,7 @@ pub mod builtins {
|
||||
/// Returns a reference to the
|
||||
/// [`Exception`](https://docs.python.org/3/library/exceptions.html#Exception) class.
|
||||
pub fn get_exception_class(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> {
|
||||
static EXCEPTION_CLASS: GILOnceCell<Py<PyType>> = GILOnceCell::new();
|
||||
static EXCEPTION_CLASS: PyOnceLock<Py<PyType>> = PyOnceLock::new();
|
||||
|
||||
EXCEPTION_CLASS.import(py, "builtins", "Exception")
|
||||
}
|
||||
@@ -51,7 +51,7 @@ pub mod builtins {
|
||||
/// Returns a reference to the [`id`](https://docs.python.org/3/library/functions.html#id)
|
||||
/// function.
|
||||
pub fn id_fn(py: Python<'_>) -> PyResult<&Bound<'_, PyCFunction>> {
|
||||
static ID_FN: GILOnceCell<Py<PyCFunction>> = GILOnceCell::new();
|
||||
static ID_FN: PyOnceLock<Py<PyCFunction>> = PyOnceLock::new();
|
||||
|
||||
ID_FN.import(py, "builtins", "id")
|
||||
}
|
||||
@@ -59,13 +59,13 @@ pub mod builtins {
|
||||
/// Invokes [`id(object)`][id_fn], extracting its value and returning a [`u64`] representing
|
||||
/// the result.
|
||||
pub fn extract_id(object: &Bound<'_, PyAny>) -> PyResult<u64> {
|
||||
id_fn(object.py())?.call1((object,))?.downcast_into::<PyInt>()?.extract()
|
||||
id_fn(object.py())?.call1((object,))?.cast_into::<PyInt>()?.extract()
|
||||
}
|
||||
|
||||
/// Returns a reference to the
|
||||
/// [`issubclass`](https://docs.python.org/3/library/functions.html#issubclass) function.
|
||||
pub fn issubclass_fn(py: Python<'_>) -> PyResult<&Bound<'_, PyCFunction>> {
|
||||
static ISSUBCLASS_FN: GILOnceCell<Py<PyCFunction>> = GILOnceCell::new();
|
||||
static ISSUBCLASS_FN: PyOnceLock<Py<PyCFunction>> = PyOnceLock::new();
|
||||
|
||||
ISSUBCLASS_FN.import(py, "builtins", "issubclass")
|
||||
}
|
||||
@@ -76,13 +76,13 @@ pub mod builtins {
|
||||
class: &Bound<'_, PyAny>,
|
||||
classinfo: &Bound<'_, PyAny>,
|
||||
) -> PyResult<bool> {
|
||||
issubclass_fn(class.py())?.call1((class, classinfo))?.downcast_into::<PyBool>()?.extract()
|
||||
issubclass_fn(class.py())?.call1((class, classinfo))?.cast_into::<PyBool>()?.extract()
|
||||
}
|
||||
|
||||
/// Returns a reference to the [`len`](https://docs.python.org/3/library/functions.html#len)
|
||||
/// function.
|
||||
pub fn len_fn(py: Python<'_>) -> PyResult<&Bound<'_, PyCFunction>> {
|
||||
static LEN_FN: GILOnceCell<Py<PyCFunction>> = GILOnceCell::new();
|
||||
static LEN_FN: PyOnceLock<Py<PyCFunction>> = PyOnceLock::new();
|
||||
|
||||
LEN_FN.import(py, "builtins", "len")
|
||||
}
|
||||
@@ -90,13 +90,13 @@ pub mod builtins {
|
||||
/// Invokes [`len(object)`][len_fn], extracting its value and returning a [`usize`]
|
||||
/// representing the result.
|
||||
pub fn extract_len(object: &Bound<'_, PyAny>) -> PyResult<usize> {
|
||||
len_fn(object.py())?.call1((object,))?.downcast_into::<PyInt>()?.extract()
|
||||
len_fn(object.py())?.call1((object,))?.cast_into::<PyInt>()?.extract()
|
||||
}
|
||||
|
||||
/// Returns a reference to the
|
||||
/// [`repr`](https://docs.python.org/3/library/functions.html#repr) function.
|
||||
pub fn repr_fn(py: Python<'_>) -> PyResult<&Bound<'_, PyCFunction>> {
|
||||
static REPR_FN: GILOnceCell<Py<PyCFunction>> = GILOnceCell::new();
|
||||
static REPR_FN: PyOnceLock<Py<PyCFunction>> = PyOnceLock::new();
|
||||
|
||||
REPR_FN.import(py, "builtins", "repr")
|
||||
}
|
||||
@@ -104,20 +104,20 @@ pub mod builtins {
|
||||
/// Invokes [`repr(object)`][repr_fn], extracting its value and returning a [`String`]
|
||||
/// representing the result.
|
||||
pub fn extract_repr(object: &Bound<'_, PyAny>) -> PyResult<String> {
|
||||
repr_fn(object.py())?.call1((object,))?.downcast_into::<PyString>()?.extract()
|
||||
repr_fn(object.py())?.call1((object,))?.cast_into::<PyString>()?.extract()
|
||||
}
|
||||
|
||||
/// Returns a reference to the
|
||||
/// [`type`](https://docs.python.org/3/library/functions.html#type) class.
|
||||
pub fn get_type_class(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> {
|
||||
static TYPE_FN: GILOnceCell<Py<PyType>> = GILOnceCell::new();
|
||||
static TYPE_FN: PyOnceLock<Py<PyType>> = PyOnceLock::new();
|
||||
|
||||
TYPE_FN.import(py, "builtins", "type")
|
||||
}
|
||||
|
||||
/// Invokes [`type(object)`][type_fn], returning a [`PyType`] representing the result.
|
||||
pub fn call_type<'py>(object: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyType>> {
|
||||
Ok(get_type_class(object.py())?.call1((object,))?.downcast_into()?)
|
||||
Ok(get_type_class(object.py())?.call1((object,))?.cast_into()?)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,14 +125,14 @@ pub mod builtins {
|
||||
pub mod typing {
|
||||
use pyo3::{
|
||||
prelude::*,
|
||||
sync::GILOnceCell,
|
||||
sync::PyOnceLock,
|
||||
types::{PyAnyMethods, PyFunction, PyModule, PyTuple},
|
||||
};
|
||||
|
||||
/// Returns a reference to this module.
|
||||
#[allow(dead_code, reason = "For API consistency between all `py_interp` modules.")]
|
||||
pub fn module(py: Python<'_>) -> PyResult<&Bound<'_, PyModule>> {
|
||||
static MODULE: GILOnceCell<Py<PyModule>> = GILOnceCell::new();
|
||||
static MODULE: PyOnceLock<Py<PyModule>> = PyOnceLock::new();
|
||||
|
||||
MODULE
|
||||
.get_or_try_init(py, || {
|
||||
@@ -145,7 +145,7 @@ pub mod typing {
|
||||
/// Returns a reference to the
|
||||
/// [`typing.get_args`](https://docs.python.org/3/library/typing.html#typing.get_args) function.
|
||||
pub fn get_args_fn(py: Python<'_>) -> PyResult<&Bound<'_, PyFunction>> {
|
||||
static GET_ARGS_FN: GILOnceCell<Py<PyFunction>> = GILOnceCell::new();
|
||||
static GET_ARGS_FN: PyOnceLock<Py<PyFunction>> = PyOnceLock::new();
|
||||
|
||||
GET_ARGS_FN.import(py, "typing", "get_args")
|
||||
}
|
||||
@@ -153,14 +153,14 @@ pub mod typing {
|
||||
/// Invokes [`typing.get_args(tp)`][get_args_fn], returning a [`PyTuple`] representing the
|
||||
/// result.
|
||||
pub fn call_get_args<'py>(tp: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyTuple>> {
|
||||
Ok(get_args_fn(tp.py())?.call1((tp,))?.downcast_into()?)
|
||||
Ok(get_args_fn(tp.py())?.call1((tp,))?.cast_into()?)
|
||||
}
|
||||
|
||||
/// Returns a reference to the
|
||||
/// [`typing.get_origin`](https://docs.python.org/3/library/typing.html#typing.get_origin)
|
||||
/// function.
|
||||
pub fn get_origin_fn(py: Python<'_>) -> PyResult<&Bound<'_, PyFunction>> {
|
||||
static GET_ORIGIN_FN: GILOnceCell<Py<PyFunction>> = GILOnceCell::new();
|
||||
static GET_ORIGIN_FN: PyOnceLock<Py<PyFunction>> = PyOnceLock::new();
|
||||
|
||||
GET_ORIGIN_FN.import(py, "typing", "get_origin")
|
||||
}
|
||||
@@ -168,6 +168,6 @@ pub mod typing {
|
||||
/// Invokes [`typing.get_origin(tp)`][get_origin_fn], returning a [`PyAny`] representing the
|
||||
/// result.
|
||||
pub fn call_get_origin<'py>(tp: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyAny>> {
|
||||
Ok(get_origin_fn(tp.py())?.call1((tp,))?.downcast_into()?)
|
||||
Ok(get_origin_fn(tp.py())?.call1((tp,))?.cast_into()?)
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
use nac3core::{
|
||||
codegen::{CodeGenContext, expr::call_extern},
|
||||
codegen::{CodeGenContext, expr::call_extern, typed_store},
|
||||
inkwell::{AddressSpace, AtomicOrdering, values::BasicValueEnum},
|
||||
};
|
||||
|
||||
@@ -86,14 +86,10 @@ impl TimeFns for NowPinningTimeFns64 {
|
||||
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(2, false)], "now.lo.addr")
|
||||
}
|
||||
.unwrap();
|
||||
ctx.builder
|
||||
.build_store(now_hiptr, time_hi)
|
||||
.unwrap()
|
||||
typed_store(&ctx.builder, now_hiptr, time_hi)
|
||||
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
||||
.unwrap();
|
||||
ctx.builder
|
||||
.build_store(now_loptr, time_lo)
|
||||
.unwrap()
|
||||
typed_store(&ctx.builder, now_loptr, time_lo)
|
||||
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
||||
.unwrap();
|
||||
}
|
||||
@@ -147,14 +143,10 @@ impl TimeFns for NowPinningTimeFns64 {
|
||||
.unwrap();
|
||||
let time_lo = ctx.builder.build_int_truncate(time, i32_type, "time.lo").unwrap();
|
||||
|
||||
ctx.builder
|
||||
.build_store(now_hiptr, time_hi)
|
||||
.unwrap()
|
||||
typed_store(&ctx.builder, now_hiptr, time_hi)
|
||||
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
||||
.unwrap();
|
||||
ctx.builder
|
||||
.build_store(now_loptr, time_lo)
|
||||
.unwrap()
|
||||
typed_store(&ctx.builder, now_loptr, time_lo)
|
||||
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
||||
.unwrap();
|
||||
}
|
||||
@@ -213,14 +205,10 @@ impl TimeFns for NowPinningTimeFns {
|
||||
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now.lo.addr")
|
||||
}
|
||||
.unwrap();
|
||||
ctx.builder
|
||||
.build_store(now_hiptr, time_hi)
|
||||
.unwrap()
|
||||
typed_store(&ctx.builder, now_hiptr, time_hi)
|
||||
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
||||
.unwrap();
|
||||
ctx.builder
|
||||
.build_store(now_loptr, time_lo)
|
||||
.unwrap()
|
||||
typed_store(&ctx.builder, now_loptr, time_lo)
|
||||
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
||||
.unwrap();
|
||||
}
|
||||
@@ -264,14 +252,10 @@ impl TimeFns for NowPinningTimeFns {
|
||||
ctx.builder.build_gep(now_hiptr, &[i32_type.const_int(1, false)], "now.lo.addr")
|
||||
}
|
||||
.unwrap();
|
||||
ctx.builder
|
||||
.build_store(now_hiptr, time_hi)
|
||||
.unwrap()
|
||||
typed_store(&ctx.builder, now_hiptr, time_hi)
|
||||
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
||||
.unwrap();
|
||||
ctx.builder
|
||||
.build_store(now_loptr, time_lo)
|
||||
.unwrap()
|
||||
typed_store(&ctx.builder, now_loptr, time_lo)
|
||||
.set_atomic_ordering(AtomicOrdering::SequentiallyConsistent)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// File automatically generated by ast/asdl_rs.py.
|
||||
|
||||
#![allow(clippy::nursery)]
|
||||
|
||||
pub use crate::constant::*;
|
||||
pub use crate::location::Location;
|
||||
|
||||
|
||||
@@ -5,40 +5,40 @@ pub enum Constant {
|
||||
Str(String),
|
||||
Bytes(Vec<u8>),
|
||||
Int(i128),
|
||||
Tuple(Vec<Constant>),
|
||||
Tuple(Vec<Self>),
|
||||
Float(f64),
|
||||
Complex { real: f64, imag: f64 },
|
||||
Ellipsis,
|
||||
}
|
||||
|
||||
impl From<String> for Constant {
|
||||
fn from(s: String) -> Constant {
|
||||
fn from(s: String) -> Self {
|
||||
Self::Str(s)
|
||||
}
|
||||
}
|
||||
impl From<Vec<u8>> for Constant {
|
||||
fn from(b: Vec<u8>) -> Constant {
|
||||
fn from(b: Vec<u8>) -> Self {
|
||||
Self::Bytes(b)
|
||||
}
|
||||
}
|
||||
impl From<bool> for Constant {
|
||||
fn from(b: bool) -> Constant {
|
||||
fn from(b: bool) -> Self {
|
||||
Self::Bool(b)
|
||||
}
|
||||
}
|
||||
impl From<i32> for Constant {
|
||||
fn from(i: i32) -> Constant {
|
||||
fn from(i: i32) -> Self {
|
||||
Self::Int(i128::from(i))
|
||||
}
|
||||
}
|
||||
impl From<i64> for Constant {
|
||||
fn from(i: i64) -> Constant {
|
||||
fn from(i: i64) -> Self {
|
||||
Self::Int(i128::from(i))
|
||||
}
|
||||
}
|
||||
|
||||
/// Transforms a value prior to formatting it.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum ConversionFlag {
|
||||
/// Converts by calling `str(<value>)`.
|
||||
@@ -51,7 +51,7 @@ pub enum ConversionFlag {
|
||||
|
||||
impl ConversionFlag {
|
||||
#[must_use]
|
||||
pub fn try_from_byte(b: u8) -> Option<Self> {
|
||||
pub const fn try_from_byte(b: u8) -> Option<Self> {
|
||||
match b {
|
||||
b's' => Some(Self::Str),
|
||||
b'a' => Some(Self::Ascii),
|
||||
@@ -71,7 +71,7 @@ pub struct ConstantOptimizer {
|
||||
impl ConstantOptimizer {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
pub const fn new() -> Self {
|
||||
Self { _priv: () }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{StrRef, constant, fold::Fold};
|
||||
|
||||
pub(crate) trait Foldable<T, U> {
|
||||
pub trait Foldable<T, U> {
|
||||
type Mapped;
|
||||
fn fold<F: Fold<T, TargetU = U> + ?Sized>(
|
||||
self,
|
||||
|
||||
@@ -5,16 +5,14 @@ impl<U> ExprKind<U> {
|
||||
#[must_use]
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
ExprKind::BoolOp { .. } | ExprKind::BinOp { .. } | ExprKind::UnaryOp { .. } => {
|
||||
"operator"
|
||||
}
|
||||
ExprKind::Subscript { .. } => "subscript",
|
||||
ExprKind::Await { .. } => "await expression",
|
||||
ExprKind::Yield { .. } | ExprKind::YieldFrom { .. } => "yield expression",
|
||||
ExprKind::Compare { .. } => "comparison",
|
||||
ExprKind::Attribute { .. } => "attribute",
|
||||
ExprKind::Call { .. } => "function call",
|
||||
ExprKind::Constant { value, .. } => match value {
|
||||
Self::BoolOp { .. } | Self::BinOp { .. } | Self::UnaryOp { .. } => "operator",
|
||||
Self::Subscript { .. } => "subscript",
|
||||
Self::Await { .. } => "await expression",
|
||||
Self::Yield { .. } | Self::YieldFrom { .. } => "yield expression",
|
||||
Self::Compare { .. } => "comparison",
|
||||
Self::Attribute { .. } => "attribute",
|
||||
Self::Call { .. } => "function call",
|
||||
Self::Constant { value, .. } => match value {
|
||||
Constant::Str(_)
|
||||
| Constant::Int(_)
|
||||
| Constant::Float(_)
|
||||
@@ -24,28 +22,28 @@ impl<U> ExprKind<U> {
|
||||
Constant::Bool(_) | Constant::None => "keyword",
|
||||
Constant::Ellipsis => "ellipsis",
|
||||
},
|
||||
ExprKind::List { .. } => "list",
|
||||
ExprKind::Tuple { .. } => "tuple",
|
||||
ExprKind::Dict { .. } => "dict display",
|
||||
ExprKind::Set { .. } => "set display",
|
||||
ExprKind::ListComp { .. } => "list comprehension",
|
||||
ExprKind::DictComp { .. } => "dict comprehension",
|
||||
ExprKind::SetComp { .. } => "set comprehension",
|
||||
ExprKind::GeneratorExp { .. } => "generator expression",
|
||||
ExprKind::Starred { .. } => "starred",
|
||||
ExprKind::Slice { .. } => "slice",
|
||||
ExprKind::JoinedStr { values } => {
|
||||
if values.iter().any(|e| matches!(e.node, ExprKind::JoinedStr { .. })) {
|
||||
Self::List { .. } => "list",
|
||||
Self::Tuple { .. } => "tuple",
|
||||
Self::Dict { .. } => "dict display",
|
||||
Self::Set { .. } => "set display",
|
||||
Self::ListComp { .. } => "list comprehension",
|
||||
Self::DictComp { .. } => "dict comprehension",
|
||||
Self::SetComp { .. } => "set comprehension",
|
||||
Self::GeneratorExp { .. } => "generator expression",
|
||||
Self::Starred { .. } => "starred",
|
||||
Self::Slice { .. } => "slice",
|
||||
Self::JoinedStr { values } => {
|
||||
if values.iter().any(|e| matches!(e.node, Self::JoinedStr { .. })) {
|
||||
"f-string expression"
|
||||
} else {
|
||||
"literal"
|
||||
}
|
||||
}
|
||||
ExprKind::FormattedValue { .. } => "f-string expression",
|
||||
ExprKind::Name { .. } => "name",
|
||||
ExprKind::Lambda { .. } => "lambda",
|
||||
ExprKind::IfExp { .. } => "conditional expression",
|
||||
ExprKind::NamedExpr { .. } => "named expression",
|
||||
Self::FormattedValue { .. } => "f-string expression",
|
||||
Self::Name { .. } => "name",
|
||||
Self::Lambda { .. } => "lambda",
|
||||
Self::IfExp { .. } => "conditional expression",
|
||||
Self::NamedExpr { .. } => "named expression",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![deny(future_incompatible, let_underscore, nonstandard_style, clippy::all)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![warn(clippy::pedantic, clippy::nursery)]
|
||||
|
||||
#[allow(
|
||||
clippy::missing_errors_doc,
|
||||
|
||||
@@ -7,13 +7,13 @@ use crate::StrRef;
|
||||
pub struct FileName(pub StrRef);
|
||||
impl Default for FileName {
|
||||
fn default() -> Self {
|
||||
FileName("unknown".into())
|
||||
Self("unknown".into())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for FileName {
|
||||
fn from(s: String) -> Self {
|
||||
FileName(s.into())
|
||||
Self(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,34 +82,34 @@ impl Location {
|
||||
|
||||
impl Location {
|
||||
#[must_use]
|
||||
pub fn new(row: usize, column: usize, file: FileName) -> Self {
|
||||
Location { row, column, file }
|
||||
pub const fn new(row: usize, column: usize, file: FileName) -> Self {
|
||||
Self { row, column, file }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn row(&self) -> usize {
|
||||
pub const fn row(&self) -> usize {
|
||||
self.row
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn column(&self) -> usize {
|
||||
pub const fn column(&self) -> usize {
|
||||
self.column
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
pub const fn reset(&mut self) {
|
||||
self.row = 1;
|
||||
self.column = 1;
|
||||
}
|
||||
|
||||
pub fn go_right(&mut self) {
|
||||
pub const fn go_right(&mut self) {
|
||||
self.column += 1;
|
||||
}
|
||||
|
||||
pub fn go_left(&mut self) {
|
||||
pub const fn go_left(&mut self) {
|
||||
self.column -= 1;
|
||||
}
|
||||
|
||||
pub fn newline(&mut self) {
|
||||
pub const fn newline(&mut self) {
|
||||
self.row += 1;
|
||||
self.column = 1;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[package]
|
||||
name = "nac3ld"
|
||||
name = "nac3binutils"
|
||||
version = "0.1.0"
|
||||
authors = ["M-Labs"]
|
||||
edition = "2024"
|
||||
@@ -1,29 +1,10 @@
|
||||
#![allow(non_camel_case_types, non_upper_case_globals)]
|
||||
#![allow(nonstandard_style, non_upper_case_globals, clippy::wildcard_imports)]
|
||||
|
||||
use std::mem;
|
||||
use crate::include::dwarf::*;
|
||||
use std::{mem, str};
|
||||
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
|
||||
pub const DW_EH_PE_omit: u8 = 0xFF;
|
||||
pub const DW_EH_PE_absptr: u8 = 0x00;
|
||||
|
||||
pub const DW_EH_PE_uleb128: u8 = 0x01;
|
||||
pub const DW_EH_PE_udata2: u8 = 0x02;
|
||||
pub const DW_EH_PE_udata4: u8 = 0x03;
|
||||
pub const DW_EH_PE_udata8: u8 = 0x04;
|
||||
pub const DW_EH_PE_sleb128: u8 = 0x09;
|
||||
pub const DW_EH_PE_sdata2: u8 = 0x0A;
|
||||
pub const DW_EH_PE_sdata4: u8 = 0x0B;
|
||||
pub const DW_EH_PE_sdata8: u8 = 0x0C;
|
||||
|
||||
pub const DW_EH_PE_pcrel: u8 = 0x10;
|
||||
pub const DW_EH_PE_textrel: u8 = 0x20;
|
||||
pub const DW_EH_PE_datarel: u8 = 0x30;
|
||||
pub const DW_EH_PE_funcrel: u8 = 0x40;
|
||||
pub const DW_EH_PE_aligned: u8 = 0x50;
|
||||
|
||||
pub const DW_EH_PE_indirect: u8 = 0x80;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DwarfReader<'a> {
|
||||
pub slice: &'a [u8],
|
||||
@@ -31,7 +12,7 @@ pub struct DwarfReader<'a> {
|
||||
}
|
||||
|
||||
impl DwarfReader<'_> {
|
||||
pub fn new(slice: &[u8], virt_addr: u32) -> DwarfReader {
|
||||
pub const fn new(slice: &[u8], virt_addr: u32) -> DwarfReader<'_> {
|
||||
DwarfReader { slice, virt_addr }
|
||||
}
|
||||
|
||||
@@ -82,6 +63,31 @@ impl DwarfReader<'_> {
|
||||
self.virt_addr += 1;
|
||||
val
|
||||
}
|
||||
|
||||
pub fn read_i8(&mut self) -> i8 {
|
||||
let val = self.slice[0] as i8;
|
||||
self.slice = &self.slice[1..];
|
||||
self.virt_addr += 1;
|
||||
val
|
||||
}
|
||||
|
||||
pub const fn read_slice(&mut self, len: usize) -> &[u8] {
|
||||
let (slice, remaining) = self.slice.split_at(len);
|
||||
self.slice = remaining;
|
||||
self.virt_addr += len as u32;
|
||||
slice
|
||||
}
|
||||
|
||||
pub fn read_str(&mut self) -> &str {
|
||||
let str_len = self
|
||||
.slice
|
||||
.iter()
|
||||
.position(|byte| *byte == 0)
|
||||
.expect("string should be null-terminated");
|
||||
unsafe {
|
||||
str::from_utf8_unchecked(&self.read_slice(str_len + 1)[..str_len]) // null-terminator
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_read_fn {
|
||||
@@ -108,13 +114,156 @@ impl_read_fn!(
|
||||
i64, read_i64
|
||||
);
|
||||
|
||||
macro_rules! read_block_fn {
|
||||
( $($read_block_fn: ident, $read_length_fn: ident);* ) => {
|
||||
impl<'a> DwarfReader<'a> {
|
||||
$(
|
||||
pub fn $read_block_fn(&mut self) -> &[u8] {
|
||||
let len = self.$read_length_fn() as usize;
|
||||
self.read_slice(len)
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
read_block_fn!(
|
||||
read_form_block, read_uleb128;
|
||||
read_form_block1, read_u8;
|
||||
read_form_block2, read_u16;
|
||||
read_form_block4, read_u32
|
||||
);
|
||||
|
||||
macro_rules! alias_read_fn {
|
||||
( $($type: ty, $read_form_fn: ident, $aliased_fn: ident);* ) => {
|
||||
impl<'a> DwarfReader<'a> {
|
||||
$(
|
||||
pub fn $read_form_fn(&mut self) -> $type {
|
||||
self.$aliased_fn()
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
alias_read_fn!(
|
||||
u32, read_form_addr, read_u32;
|
||||
u8, read_form_data1, read_u8;
|
||||
u16, read_form_data2, read_u16;
|
||||
u32, read_form_data4, read_u32;
|
||||
u64, read_form_data8, read_u64;
|
||||
i64, read_form_sdata, read_i64;
|
||||
u64, read_form_udata, read_u64;
|
||||
&[u8], read_form_exprloc, read_form_block;
|
||||
u8, read_form_flag, read_u8;
|
||||
u32, read_form_sec_offset, read_u32
|
||||
);
|
||||
|
||||
impl DwarfReader<'_> {
|
||||
// Helper to perform constant read
|
||||
// Everything is casted as u64 (as the broadest type).
|
||||
// Use at your own risk.
|
||||
pub fn read_form_constant(&mut self, attr_form: u64) -> u64 {
|
||||
match attr_form {
|
||||
DW_FORM_data1 => u64::from(self.read_form_data1()),
|
||||
DW_FORM_data2 => u64::from(self.read_form_data2()),
|
||||
DW_FORM_data4 => u64::from(self.read_form_data4()),
|
||||
DW_FORM_data8 => self.read_form_data8(),
|
||||
DW_FORM_sdata => self.read_form_sdata() as u64,
|
||||
DW_FORM_udata => self.read_form_udata(),
|
||||
_ => unreachable!("form should be a constant"),
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to perform reference read
|
||||
// Everything is casted as u64 (as the broadest type).
|
||||
// Use at your own risk.
|
||||
pub fn read_form_reference(&mut self, attr_form: u64) -> u64 {
|
||||
match attr_form {
|
||||
DW_FORM_ref1 => u64::from(self.read_form_data1()),
|
||||
DW_FORM_ref2 => u64::from(self.read_form_data2()),
|
||||
DW_FORM_ref4 => u64::from(self.read_form_data4()),
|
||||
DW_FORM_ref8 => self.read_form_data8(),
|
||||
DW_FORM_ref_addr => u64::from(self.read_form_addr()),
|
||||
DW_FORM_ref_udata => self.read_form_udata(),
|
||||
DW_FORM_ref_sig8 => self.read_u64(),
|
||||
_ => unreachable!("form should be a reference"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn skip_form(&mut self, attr_form: u64) {
|
||||
match attr_form {
|
||||
DW_FORM_addr | DW_FORM_ref_addr | DW_FORM_strp => {
|
||||
self.read_form_addr();
|
||||
}
|
||||
DW_FORM_data1 | DW_FORM_ref1 => {
|
||||
self.read_form_data1();
|
||||
}
|
||||
DW_FORM_data2 | DW_FORM_ref2 => {
|
||||
self.read_form_data2();
|
||||
}
|
||||
DW_FORM_data4 | DW_FORM_ref4 => {
|
||||
self.read_form_data4();
|
||||
}
|
||||
DW_FORM_data8 | DW_FORM_ref8 => {
|
||||
self.read_form_data8();
|
||||
}
|
||||
DW_FORM_sdata => {
|
||||
self.read_form_sdata();
|
||||
}
|
||||
DW_FORM_udata | DW_FORM_ref_udata => {
|
||||
self.read_form_udata();
|
||||
}
|
||||
DW_FORM_flag => {
|
||||
self.read_form_flag();
|
||||
}
|
||||
DW_FORM_block => {
|
||||
self.read_form_block();
|
||||
}
|
||||
DW_FORM_exprloc => {
|
||||
self.read_form_exprloc();
|
||||
}
|
||||
DW_FORM_block1 => {
|
||||
self.read_form_block1();
|
||||
}
|
||||
DW_FORM_block2 => {
|
||||
self.read_form_block2();
|
||||
}
|
||||
DW_FORM_block4 => {
|
||||
self.read_form_block4();
|
||||
}
|
||||
DW_FORM_string => {
|
||||
self.read_str();
|
||||
}
|
||||
DW_FORM_ref_sig8 => {
|
||||
self.read_u64();
|
||||
}
|
||||
DW_FORM_flag_present => (),
|
||||
|
||||
DW_FORM_sec_offset => {
|
||||
self.read_form_sec_offset();
|
||||
}
|
||||
|
||||
DW_FORM_indirect => {
|
||||
// Section 7.5.3
|
||||
// ... the attribute value itself ... begins with an unsigned
|
||||
// LEB128 number that represents its form.
|
||||
let inner_form = self.read_uleb128();
|
||||
self.skip_form(inner_form);
|
||||
}
|
||||
|
||||
_ => unreachable!("unrecognized attribute form"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DwarfWriter<'a> {
|
||||
pub slice: &'a mut [u8],
|
||||
pub offset: usize,
|
||||
}
|
||||
|
||||
impl DwarfWriter<'_> {
|
||||
pub fn new(slice: &mut [u8]) -> DwarfWriter {
|
||||
pub const fn new(slice: &mut [u8]) -> DwarfWriter<'_> {
|
||||
DwarfWriter { slice, offset: 0 }
|
||||
}
|
||||
|
||||
@@ -191,7 +340,7 @@ fn read_encoded_pointer_with_pc(reader: &mut DwarfReader, encoding: u8) -> Resul
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn round_up(unrounded: usize, align: usize) -> Result<usize, ()> {
|
||||
const fn round_up(unrounded: usize, align: usize) -> Result<usize, ()> {
|
||||
if align.is_power_of_two() { Ok((unrounded + align - 1) & !(align - 1)) } else { Err(()) }
|
||||
}
|
||||
|
||||
@@ -207,7 +356,7 @@ pub struct EH_Frame<'a> {
|
||||
impl<'a> EH_Frame<'a> {
|
||||
/// Creates an [`EH_Frame`] using the bytes in the `.eh_frame` section and its address in the
|
||||
/// ELF file.
|
||||
pub fn new(eh_frame_slice: &[u8], eh_frame_addr: u32) -> EH_Frame {
|
||||
pub const fn new(eh_frame_slice: &[u8], eh_frame_addr: u32) -> EH_Frame<'_> {
|
||||
EH_Frame { reader: DwarfReader::new(eh_frame_slice, eh_frame_addr) }
|
||||
}
|
||||
|
||||
@@ -233,7 +382,7 @@ pub struct CFI_Record<'a> {
|
||||
}
|
||||
|
||||
impl<'a> CFI_Record<'a> {
|
||||
pub fn from_reader(cie_reader: &mut DwarfReader<'a>) -> Result<CFI_Record<'a>, ()> {
|
||||
pub fn from_reader(cie_reader: &mut DwarfReader<'a>) -> Result<Self, ()> {
|
||||
let length = cie_reader.read_u32();
|
||||
let fde_reader = match length {
|
||||
// eh_frame with 0 lengths means the CIE is terminated
|
||||
@@ -428,7 +577,7 @@ impl EH_Frame_Hdr<'_> {
|
||||
eh_frame_hdr_slice: &mut [u8],
|
||||
eh_frame_hdr_addr: u32,
|
||||
eh_frame_addr: u32,
|
||||
) -> EH_Frame_Hdr {
|
||||
) -> EH_Frame_Hdr<'_> {
|
||||
let mut writer = DwarfWriter::new(eh_frame_hdr_slice);
|
||||
|
||||
writer.write_u8(1); // version
|
||||
@@ -445,7 +594,7 @@ impl EH_Frame_Hdr<'_> {
|
||||
|
||||
/// The offset of the `fde_count` value relative to the start of the `.eh_frame_hdr` section in
|
||||
/// bytes.
|
||||
fn fde_count_offset() -> usize {
|
||||
const fn fde_count_offset() -> usize {
|
||||
8
|
||||
}
|
||||
|
||||
374
nac3binutils/src/include/dwarf.rs
Normal file
374
nac3binutils/src/include/dwarf.rs
Normal file
@@ -0,0 +1,374 @@
|
||||
/* automatically generated by rust-bindgen 0.72.1 */
|
||||
/* with manual modifications to remove unused specification */
|
||||
#![allow(non_camel_case_types, non_snake_case, non_upper_case_globals, dead_code, clippy::pedantic)]
|
||||
|
||||
pub const DW_AT_subscr_data: u32 = 10;
|
||||
pub const DW_AT_element_list: u32 = 15;
|
||||
pub const DW_AT_member: u32 = 20;
|
||||
pub const DW_ADDR_none: u32 = 0;
|
||||
|
||||
pub const DW_TAG_array_type: DW_TAG = 1;
|
||||
pub const DW_TAG_class_type: DW_TAG = 2;
|
||||
pub const DW_TAG_entry_point: DW_TAG = 3;
|
||||
pub const DW_TAG_enumeration_type: DW_TAG = 4;
|
||||
pub const DW_TAG_formal_parameter: DW_TAG = 5;
|
||||
pub const DW_TAG_imported_declaration: DW_TAG = 8;
|
||||
pub const DW_TAG_label: DW_TAG = 10;
|
||||
pub const DW_TAG_lexical_block: DW_TAG = 11;
|
||||
pub const DW_TAG_member: DW_TAG = 13;
|
||||
pub const DW_TAG_pointer_type: DW_TAG = 15;
|
||||
pub const DW_TAG_reference_type: DW_TAG = 16;
|
||||
pub const DW_TAG_compile_unit: DW_TAG = 17;
|
||||
pub const DW_TAG_string_type: DW_TAG = 18;
|
||||
pub const DW_TAG_structure_type: DW_TAG = 19;
|
||||
pub const DW_TAG_subroutine_type: DW_TAG = 21;
|
||||
pub const DW_TAG_typedef: DW_TAG = 22;
|
||||
pub const DW_TAG_union_type: DW_TAG = 23;
|
||||
pub const DW_TAG_unspecified_parameters: DW_TAG = 24;
|
||||
pub const DW_TAG_variant: DW_TAG = 25;
|
||||
pub const DW_TAG_common_block: DW_TAG = 26;
|
||||
pub const DW_TAG_common_inclusion: DW_TAG = 27;
|
||||
pub const DW_TAG_inheritance: DW_TAG = 28;
|
||||
pub const DW_TAG_inlined_subroutine: DW_TAG = 29;
|
||||
pub const DW_TAG_module: DW_TAG = 30;
|
||||
pub const DW_TAG_ptr_to_member_type: DW_TAG = 31;
|
||||
pub const DW_TAG_set_type: DW_TAG = 32;
|
||||
pub const DW_TAG_subrange_type: DW_TAG = 33;
|
||||
pub const DW_TAG_with_stmt: DW_TAG = 34;
|
||||
pub const DW_TAG_access_declaration: DW_TAG = 35;
|
||||
pub const DW_TAG_base_type: DW_TAG = 36;
|
||||
pub const DW_TAG_catch_block: DW_TAG = 37;
|
||||
pub const DW_TAG_const_type: DW_TAG = 38;
|
||||
pub const DW_TAG_constant: DW_TAG = 39;
|
||||
pub const DW_TAG_enumerator: DW_TAG = 40;
|
||||
pub const DW_TAG_file_type: DW_TAG = 41;
|
||||
pub const DW_TAG_friend: DW_TAG = 42;
|
||||
pub const DW_TAG_namelist: DW_TAG = 43;
|
||||
pub const DW_TAG_namelist_item: DW_TAG = 44;
|
||||
pub const DW_TAG_packed_type: DW_TAG = 45;
|
||||
pub const DW_TAG_subprogram: DW_TAG = 46;
|
||||
pub const DW_TAG_template_type_parameter: DW_TAG = 47;
|
||||
pub const DW_TAG_template_value_parameter: DW_TAG = 48;
|
||||
pub const DW_TAG_thrown_type: DW_TAG = 49;
|
||||
pub const DW_TAG_try_block: DW_TAG = 50;
|
||||
pub const DW_TAG_variant_part: DW_TAG = 51;
|
||||
pub const DW_TAG_variable: DW_TAG = 52;
|
||||
pub const DW_TAG_volatile_type: DW_TAG = 53;
|
||||
pub const DW_TAG_dwarf_procedure: DW_TAG = 54;
|
||||
pub const DW_TAG_restrict_type: DW_TAG = 55;
|
||||
pub const DW_TAG_interface_type: DW_TAG = 56;
|
||||
pub const DW_TAG_namespace: DW_TAG = 57;
|
||||
pub const DW_TAG_imported_module: DW_TAG = 58;
|
||||
pub const DW_TAG_unspecified_type: DW_TAG = 59;
|
||||
pub const DW_TAG_partial_unit: DW_TAG = 60;
|
||||
pub const DW_TAG_imported_unit: DW_TAG = 61;
|
||||
pub const DW_TAG_condition: DW_TAG = 63;
|
||||
pub const DW_TAG_shared_type: DW_TAG = 64;
|
||||
pub const DW_TAG_type_unit: DW_TAG = 65;
|
||||
pub const DW_TAG_rvalue_reference_type: DW_TAG = 66;
|
||||
pub const DW_TAG_template_alias: DW_TAG = 67;
|
||||
pub const DW_TAG_coarray_type: DW_TAG = 68;
|
||||
pub const DW_TAG_generic_subrange: DW_TAG = 69;
|
||||
pub const DW_TAG_dynamic_type: DW_TAG = 70;
|
||||
pub const DW_TAG_atomic_type: DW_TAG = 71;
|
||||
pub const DW_TAG_call_site: DW_TAG = 72;
|
||||
pub const DW_TAG_call_site_parameter: DW_TAG = 73;
|
||||
pub const DW_TAG_skeleton_unit: DW_TAG = 74;
|
||||
pub const DW_TAG_immutable_type: DW_TAG = 75;
|
||||
pub const DW_TAG_lo_user: DW_TAG = 16512;
|
||||
pub const DW_TAG_MIPS_loop: DW_TAG = 16513;
|
||||
pub const DW_TAG_format_label: DW_TAG = 16641;
|
||||
pub const DW_TAG_function_template: DW_TAG = 16642;
|
||||
pub const DW_TAG_class_template: DW_TAG = 16643;
|
||||
pub const DW_TAG_GNU_BINCL: DW_TAG = 16644;
|
||||
pub const DW_TAG_GNU_EINCL: DW_TAG = 16645;
|
||||
pub const DW_TAG_GNU_template_template_param: DW_TAG = 16646;
|
||||
pub const DW_TAG_GNU_template_parameter_pack: DW_TAG = 16647;
|
||||
pub const DW_TAG_GNU_formal_parameter_pack: DW_TAG = 16648;
|
||||
pub const DW_TAG_GNU_call_site: DW_TAG = 16649;
|
||||
pub const DW_TAG_GNU_call_site_parameter: DW_TAG = 16650;
|
||||
pub const DW_TAG_hi_user: DW_TAG = 65535;
|
||||
pub type DW_TAG = u64; // ULEB128
|
||||
|
||||
pub const DW_CHILDREN_no: DW_CHILDREN = 0;
|
||||
pub const DW_CHILDREN_yes: DW_CHILDREN = 1;
|
||||
pub type DW_CHILDREN = u8; // 7.5.3 Abbreviations Tables: Encoded as a byte
|
||||
|
||||
pub const DW_AT_sibling: DW_AT = 1;
|
||||
pub const DW_AT_location: DW_AT = 2;
|
||||
pub const DW_AT_name: DW_AT = 3;
|
||||
pub const DW_AT_ordering: DW_AT = 9;
|
||||
pub const DW_AT_byte_size: DW_AT = 11;
|
||||
pub const DW_AT_bit_offset: DW_AT = 12;
|
||||
pub const DW_AT_bit_size: DW_AT = 13;
|
||||
pub const DW_AT_stmt_list: DW_AT = 16;
|
||||
pub const DW_AT_low_pc: DW_AT = 17;
|
||||
pub const DW_AT_high_pc: DW_AT = 18;
|
||||
pub const DW_AT_language: DW_AT = 19;
|
||||
pub const DW_AT_discr: DW_AT = 21;
|
||||
pub const DW_AT_discr_value: DW_AT = 22;
|
||||
pub const DW_AT_visibility: DW_AT = 23;
|
||||
pub const DW_AT_import: DW_AT = 24;
|
||||
pub const DW_AT_string_length: DW_AT = 25;
|
||||
pub const DW_AT_common_reference: DW_AT = 26;
|
||||
pub const DW_AT_comp_dir: DW_AT = 27;
|
||||
pub const DW_AT_const_value: DW_AT = 28;
|
||||
pub const DW_AT_containing_type: DW_AT = 29;
|
||||
pub const DW_AT_default_value: DW_AT = 30;
|
||||
pub const DW_AT_inline: DW_AT = 32;
|
||||
pub const DW_AT_is_optional: DW_AT = 33;
|
||||
pub const DW_AT_lower_bound: DW_AT = 34;
|
||||
pub const DW_AT_producer: DW_AT = 37;
|
||||
pub const DW_AT_prototyped: DW_AT = 39;
|
||||
pub const DW_AT_return_addr: DW_AT = 42;
|
||||
pub const DW_AT_start_scope: DW_AT = 44;
|
||||
pub const DW_AT_bit_stride: DW_AT = 46;
|
||||
pub const DW_AT_upper_bound: DW_AT = 47;
|
||||
pub const DW_AT_abstract_origin: DW_AT = 49;
|
||||
pub const DW_AT_accessibility: DW_AT = 50;
|
||||
pub const DW_AT_address_class: DW_AT = 51;
|
||||
pub const DW_AT_artificial: DW_AT = 52;
|
||||
pub const DW_AT_base_types: DW_AT = 53;
|
||||
pub const DW_AT_calling_convention: DW_AT = 54;
|
||||
pub const DW_AT_count: DW_AT = 55;
|
||||
pub const DW_AT_data_member_location: DW_AT = 56;
|
||||
pub const DW_AT_decl_column: DW_AT = 57;
|
||||
pub const DW_AT_decl_file: DW_AT = 58;
|
||||
pub const DW_AT_decl_line: DW_AT = 59;
|
||||
pub const DW_AT_declaration: DW_AT = 60;
|
||||
pub const DW_AT_discr_list: DW_AT = 61;
|
||||
pub const DW_AT_encoding: DW_AT = 62;
|
||||
pub const DW_AT_external: DW_AT = 63;
|
||||
pub const DW_AT_frame_base: DW_AT = 64;
|
||||
pub const DW_AT_friend: DW_AT = 65;
|
||||
pub const DW_AT_identifier_case: DW_AT = 66;
|
||||
pub const DW_AT_macro_info: DW_AT = 67;
|
||||
pub const DW_AT_namelist_item: DW_AT = 68;
|
||||
pub const DW_AT_priority: DW_AT = 69;
|
||||
pub const DW_AT_segment: DW_AT = 70;
|
||||
pub const DW_AT_specification: DW_AT = 71;
|
||||
pub const DW_AT_static_link: DW_AT = 72;
|
||||
pub const DW_AT_type: DW_AT = 73;
|
||||
pub const DW_AT_use_location: DW_AT = 74;
|
||||
pub const DW_AT_variable_parameter: DW_AT = 75;
|
||||
pub const DW_AT_virtuality: DW_AT = 76;
|
||||
pub const DW_AT_vtable_elem_location: DW_AT = 77;
|
||||
pub const DW_AT_allocated: DW_AT = 78;
|
||||
pub const DW_AT_associated: DW_AT = 79;
|
||||
pub const DW_AT_data_location: DW_AT = 80;
|
||||
pub const DW_AT_byte_stride: DW_AT = 81;
|
||||
pub const DW_AT_entry_pc: DW_AT = 82;
|
||||
pub const DW_AT_use_UTF8: DW_AT = 83;
|
||||
pub const DW_AT_extension: DW_AT = 84;
|
||||
pub const DW_AT_ranges: DW_AT = 85;
|
||||
pub const DW_AT_trampoline: DW_AT = 86;
|
||||
pub const DW_AT_call_column: DW_AT = 87;
|
||||
pub const DW_AT_call_file: DW_AT = 88;
|
||||
pub const DW_AT_call_line: DW_AT = 89;
|
||||
pub const DW_AT_description: DW_AT = 90;
|
||||
pub const DW_AT_binary_scale: DW_AT = 91;
|
||||
pub const DW_AT_decimal_scale: DW_AT = 92;
|
||||
pub const DW_AT_small: DW_AT = 93;
|
||||
pub const DW_AT_decimal_sign: DW_AT = 94;
|
||||
pub const DW_AT_digit_count: DW_AT = 95;
|
||||
pub const DW_AT_picture_string: DW_AT = 96;
|
||||
pub const DW_AT_mutable: DW_AT = 97;
|
||||
pub const DW_AT_threads_scaled: DW_AT = 98;
|
||||
pub const DW_AT_explicit: DW_AT = 99;
|
||||
pub const DW_AT_object_pointer: DW_AT = 100;
|
||||
pub const DW_AT_endianity: DW_AT = 101;
|
||||
pub const DW_AT_elemental: DW_AT = 102;
|
||||
pub const DW_AT_pure: DW_AT = 103;
|
||||
pub const DW_AT_recursive: DW_AT = 104;
|
||||
pub const DW_AT_signature: DW_AT = 105;
|
||||
pub const DW_AT_main_subprogram: DW_AT = 106;
|
||||
pub const DW_AT_data_bit_offset: DW_AT = 107;
|
||||
pub const DW_AT_const_expr: DW_AT = 108;
|
||||
pub const DW_AT_enum_class: DW_AT = 109;
|
||||
pub const DW_AT_linkage_name: DW_AT = 110;
|
||||
pub const DW_AT_string_length_bit_size: DW_AT = 111;
|
||||
pub const DW_AT_string_length_byte_size: DW_AT = 112;
|
||||
pub const DW_AT_rank: DW_AT = 113;
|
||||
pub const DW_AT_str_offsets_base: DW_AT = 114;
|
||||
pub const DW_AT_addr_base: DW_AT = 115;
|
||||
pub const DW_AT_rnglists_base: DW_AT = 116;
|
||||
pub const DW_AT_dwo_name: DW_AT = 118;
|
||||
pub const DW_AT_reference: DW_AT = 119;
|
||||
pub const DW_AT_rvalue_reference: DW_AT = 120;
|
||||
pub const DW_AT_macros: DW_AT = 121;
|
||||
pub const DW_AT_call_all_calls: DW_AT = 122;
|
||||
pub const DW_AT_call_all_source_calls: DW_AT = 123;
|
||||
pub const DW_AT_call_all_tail_calls: DW_AT = 124;
|
||||
pub const DW_AT_call_return_pc: DW_AT = 125;
|
||||
pub const DW_AT_call_value: DW_AT = 126;
|
||||
pub const DW_AT_call_origin: DW_AT = 127;
|
||||
pub const DW_AT_call_parameter: DW_AT = 128;
|
||||
pub const DW_AT_call_pc: DW_AT = 129;
|
||||
pub const DW_AT_call_tail_call: DW_AT = 130;
|
||||
pub const DW_AT_call_target: DW_AT = 131;
|
||||
pub const DW_AT_call_target_clobbered: DW_AT = 132;
|
||||
pub const DW_AT_call_data_location: DW_AT = 133;
|
||||
pub const DW_AT_call_data_value: DW_AT = 134;
|
||||
pub const DW_AT_noreturn: DW_AT = 135;
|
||||
pub const DW_AT_alignment: DW_AT = 136;
|
||||
pub const DW_AT_export_symbols: DW_AT = 137;
|
||||
pub const DW_AT_deleted: DW_AT = 138;
|
||||
pub const DW_AT_defaulted: DW_AT = 139;
|
||||
pub const DW_AT_loclists_base: DW_AT = 140;
|
||||
pub const DW_AT_lo_user: DW_AT = 8192;
|
||||
pub const DW_AT_MIPS_fde: DW_AT = 8193;
|
||||
pub const DW_AT_MIPS_loop_begin: DW_AT = 8194;
|
||||
pub const DW_AT_MIPS_tail_loop_begin: DW_AT = 8195;
|
||||
pub const DW_AT_MIPS_epilog_begin: DW_AT = 8196;
|
||||
pub const DW_AT_MIPS_loop_unroll_factor: DW_AT = 8197;
|
||||
pub const DW_AT_MIPS_software_pipeline_depth: DW_AT = 8198;
|
||||
pub const DW_AT_MIPS_linkage_name: DW_AT = 8199;
|
||||
pub const DW_AT_MIPS_stride: DW_AT = 8200;
|
||||
pub const DW_AT_MIPS_abstract_name: DW_AT = 8201;
|
||||
pub const DW_AT_MIPS_clone_origin: DW_AT = 8202;
|
||||
pub const DW_AT_MIPS_has_inlines: DW_AT = 8203;
|
||||
pub const DW_AT_MIPS_stride_byte: DW_AT = 8204;
|
||||
pub const DW_AT_MIPS_stride_elem: DW_AT = 8205;
|
||||
pub const DW_AT_MIPS_ptr_dopetype: DW_AT = 8206;
|
||||
pub const DW_AT_MIPS_allocatable_dopetype: DW_AT = 8207;
|
||||
pub const DW_AT_MIPS_assumed_shape_dopetype: DW_AT = 8208;
|
||||
pub const DW_AT_MIPS_assumed_size: DW_AT = 8209;
|
||||
pub const DW_AT_sf_names: DW_AT = 8449;
|
||||
pub const DW_AT_src_info: DW_AT = 8450;
|
||||
pub const DW_AT_mac_info: DW_AT = 8451;
|
||||
pub const DW_AT_src_coords: DW_AT = 8452;
|
||||
pub const DW_AT_body_begin: DW_AT = 8453;
|
||||
pub const DW_AT_body_end: DW_AT = 8454;
|
||||
pub const DW_AT_GNU_vector: DW_AT = 8455;
|
||||
pub const DW_AT_GNU_guarded_by: DW_AT = 8456;
|
||||
pub const DW_AT_GNU_pt_guarded_by: DW_AT = 8457;
|
||||
pub const DW_AT_GNU_guarded: DW_AT = 8458;
|
||||
pub const DW_AT_GNU_pt_guarded: DW_AT = 8459;
|
||||
pub const DW_AT_GNU_locks_excluded: DW_AT = 8460;
|
||||
pub const DW_AT_GNU_exclusive_locks_required: DW_AT = 8461;
|
||||
pub const DW_AT_GNU_shared_locks_required: DW_AT = 8462;
|
||||
pub const DW_AT_GNU_odr_signature: DW_AT = 8463;
|
||||
pub const DW_AT_GNU_template_name: DW_AT = 8464;
|
||||
pub const DW_AT_GNU_call_site_value: DW_AT = 8465;
|
||||
pub const DW_AT_GNU_call_site_data_value: DW_AT = 8466;
|
||||
pub const DW_AT_GNU_call_site_target: DW_AT = 8467;
|
||||
pub const DW_AT_GNU_call_site_target_clobbered: DW_AT = 8468;
|
||||
pub const DW_AT_GNU_tail_call: DW_AT = 8469;
|
||||
pub const DW_AT_GNU_all_tail_call_sites: DW_AT = 8470;
|
||||
pub const DW_AT_GNU_all_call_sites: DW_AT = 8471;
|
||||
pub const DW_AT_GNU_all_source_call_sites: DW_AT = 8472;
|
||||
pub const DW_AT_GNU_locviews: DW_AT = 8503;
|
||||
pub const DW_AT_GNU_entry_view: DW_AT = 8504;
|
||||
pub const DW_AT_GNU_macros: DW_AT = 8473;
|
||||
pub const DW_AT_GNU_deleted: DW_AT = 8474;
|
||||
pub const DW_AT_GNU_dwo_name: DW_AT = 8496;
|
||||
pub const DW_AT_GNU_dwo_id: DW_AT = 8497;
|
||||
pub const DW_AT_GNU_ranges_base: DW_AT = 8498;
|
||||
pub const DW_AT_GNU_addr_base: DW_AT = 8499;
|
||||
pub const DW_AT_GNU_pubnames: DW_AT = 8500;
|
||||
pub const DW_AT_GNU_pubtypes: DW_AT = 8501;
|
||||
pub const DW_AT_GNU_numerator: DW_AT = 8963;
|
||||
pub const DW_AT_GNU_denominator: DW_AT = 8964;
|
||||
pub const DW_AT_GNU_bias: DW_AT = 8965;
|
||||
pub const DW_AT_hi_user: DW_AT = 16383;
|
||||
pub type DW_AT = u64; // ULEB128
|
||||
// By attribute specifications in 7.5.3 Abbreviations Tables.
|
||||
|
||||
pub const DW_FORM_addr: DW_FORM = 1;
|
||||
pub const DW_FORM_block2: DW_FORM = 3;
|
||||
pub const DW_FORM_block4: DW_FORM = 4;
|
||||
pub const DW_FORM_data2: DW_FORM = 5;
|
||||
pub const DW_FORM_data4: DW_FORM = 6;
|
||||
pub const DW_FORM_data8: DW_FORM = 7;
|
||||
pub const DW_FORM_string: DW_FORM = 8;
|
||||
pub const DW_FORM_block: DW_FORM = 9;
|
||||
pub const DW_FORM_block1: DW_FORM = 10;
|
||||
pub const DW_FORM_data1: DW_FORM = 11;
|
||||
pub const DW_FORM_flag: DW_FORM = 12;
|
||||
pub const DW_FORM_sdata: DW_FORM = 13;
|
||||
pub const DW_FORM_strp: DW_FORM = 14;
|
||||
pub const DW_FORM_udata: DW_FORM = 15;
|
||||
pub const DW_FORM_ref_addr: DW_FORM = 16;
|
||||
pub const DW_FORM_ref1: DW_FORM = 17;
|
||||
pub const DW_FORM_ref2: DW_FORM = 18;
|
||||
pub const DW_FORM_ref4: DW_FORM = 19;
|
||||
pub const DW_FORM_ref8: DW_FORM = 20;
|
||||
pub const DW_FORM_ref_udata: DW_FORM = 21;
|
||||
pub const DW_FORM_indirect: DW_FORM = 22;
|
||||
pub const DW_FORM_sec_offset: DW_FORM = 23;
|
||||
pub const DW_FORM_exprloc: DW_FORM = 24;
|
||||
pub const DW_FORM_flag_present: DW_FORM = 25;
|
||||
pub const DW_FORM_strx: DW_FORM = 26;
|
||||
pub const DW_FORM_addrx: DW_FORM = 27;
|
||||
pub const DW_FORM_ref_sup4: DW_FORM = 28;
|
||||
pub const DW_FORM_strp_sup: DW_FORM = 29;
|
||||
pub const DW_FORM_data16: DW_FORM = 30;
|
||||
pub const DW_FORM_line_strp: DW_FORM = 31;
|
||||
pub const DW_FORM_ref_sig8: DW_FORM = 32;
|
||||
pub const DW_FORM_implicit_const: DW_FORM = 33;
|
||||
pub const DW_FORM_loclistx: DW_FORM = 34;
|
||||
pub const DW_FORM_rnglistx: DW_FORM = 35;
|
||||
pub const DW_FORM_ref_sup8: DW_FORM = 36;
|
||||
pub const DW_FORM_strx1: DW_FORM = 37;
|
||||
pub const DW_FORM_strx2: DW_FORM = 38;
|
||||
pub const DW_FORM_strx3: DW_FORM = 39;
|
||||
pub const DW_FORM_strx4: DW_FORM = 40;
|
||||
pub const DW_FORM_addrx1: DW_FORM = 41;
|
||||
pub const DW_FORM_addrx2: DW_FORM = 42;
|
||||
pub const DW_FORM_addrx3: DW_FORM = 43;
|
||||
pub const DW_FORM_addrx4: DW_FORM = 44;
|
||||
pub const DW_FORM_GNU_addr_index: DW_FORM = 7937;
|
||||
pub const DW_FORM_GNU_str_index: DW_FORM = 7938;
|
||||
pub const DW_FORM_GNU_ref_alt: DW_FORM = 7968;
|
||||
pub const DW_FORM_GNU_strp_alt: DW_FORM = 7969;
|
||||
pub type DW_FORM = u64; // ULEB128
|
||||
// By attribute specifications in 7.5.3 Abbreviations Tables.
|
||||
|
||||
pub const DW_LNS_copy: DW_LNS = 1;
|
||||
pub const DW_LNS_advance_pc: DW_LNS = 2;
|
||||
pub const DW_LNS_advance_line: DW_LNS = 3;
|
||||
pub const DW_LNS_set_file: DW_LNS = 4;
|
||||
pub const DW_LNS_set_column: DW_LNS = 5;
|
||||
pub const DW_LNS_negate_stmt: DW_LNS = 6;
|
||||
pub const DW_LNS_set_basic_block: DW_LNS = 7;
|
||||
pub const DW_LNS_const_add_pc: DW_LNS = 8;
|
||||
pub const DW_LNS_fixed_advance_pc: DW_LNS = 9;
|
||||
pub const DW_LNS_set_prologue_end: DW_LNS = 10;
|
||||
pub const DW_LNS_set_epilogue_begin: DW_LNS = 11;
|
||||
pub const DW_LNS_set_isa: DW_LNS = 12;
|
||||
pub type DW_LNS = u8; // ubyte
|
||||
// 6.2.3 Line Number Program Instructions: standard opcodes
|
||||
|
||||
pub const DW_LNE_end_sequence: DW_LNE = 1;
|
||||
pub const DW_LNE_set_address: DW_LNE = 2;
|
||||
pub const DW_LNE_define_file: DW_LNE = 3;
|
||||
pub const DW_LNE_set_discriminator: DW_LNE = 4;
|
||||
pub const DW_LNE_lo_user: DW_LNE = 128;
|
||||
pub const DW_LNE_hi_user: DW_LNE = 255;
|
||||
pub type DW_LNE = u8; // ubyte
|
||||
// 6.2.3 Line Number Program Instructions: extended opcodes
|
||||
|
||||
pub const DW_CIE_ID_32: u32 = 0xffffffff;
|
||||
pub const DW_CIE_ID_64: u64 = 0xffffffffffffffff;
|
||||
|
||||
pub const DW_EH_PE_absptr: DW_EH_PE = 0;
|
||||
pub const DW_EH_PE_omit: DW_EH_PE = 255;
|
||||
pub const DW_EH_PE_uleb128: DW_EH_PE = 1;
|
||||
pub const DW_EH_PE_udata2: DW_EH_PE = 2;
|
||||
pub const DW_EH_PE_udata4: DW_EH_PE = 3;
|
||||
pub const DW_EH_PE_udata8: DW_EH_PE = 4;
|
||||
pub const DW_EH_PE_sleb128: DW_EH_PE = 9;
|
||||
pub const DW_EH_PE_sdata2: DW_EH_PE = 10;
|
||||
pub const DW_EH_PE_sdata4: DW_EH_PE = 11;
|
||||
pub const DW_EH_PE_sdata8: DW_EH_PE = 12;
|
||||
pub const DW_EH_PE_signed: DW_EH_PE = 8;
|
||||
pub const DW_EH_PE_pcrel: DW_EH_PE = 16;
|
||||
pub const DW_EH_PE_textrel: DW_EH_PE = 32;
|
||||
pub const DW_EH_PE_datarel: DW_EH_PE = 48;
|
||||
pub const DW_EH_PE_funcrel: DW_EH_PE = 64;
|
||||
pub const DW_EH_PE_aligned: DW_EH_PE = 80;
|
||||
pub const DW_EH_PE_indirect: DW_EH_PE = 128;
|
||||
pub type DW_EH_PE = u8; // A mixture of these flags apply to a ubyte
|
||||
// which specifies the data types in .eh_frame and .eh_frame_hdr
|
||||
// See Linux Standard Base Core Specification, Generic Part
|
||||
// 10.5.1. DWARF Exception Header Encoding
|
||||
@@ -2872,22 +2872,22 @@ impl Clone for Elf64_Lib {
|
||||
}
|
||||
pub type Elf32_Conflict = Elf32_Addr;
|
||||
|
||||
pub fn ELF32_R_SYM(info: Elf32_Word) -> Elf32_Word {
|
||||
pub const fn ELF32_R_SYM(info: Elf32_Word) -> Elf32_Word {
|
||||
info >> 8
|
||||
}
|
||||
pub fn ELF32_R_TYPE(info: Elf32_Word) -> u8 {
|
||||
pub const fn ELF32_R_TYPE(info: Elf32_Word) -> u8 {
|
||||
info as u8
|
||||
}
|
||||
pub fn ELF32_R_INFO(sym: Elf32_Word, ty: u8) -> Elf32_Word {
|
||||
pub const fn ELF32_R_INFO(sym: Elf32_Word, ty: u8) -> Elf32_Word {
|
||||
(sym << 8) | ty as Elf32_Word
|
||||
}
|
||||
|
||||
pub fn ELF32_ST_BIND(info: u8) -> u8 {
|
||||
pub const fn ELF32_ST_BIND(info: u8) -> u8 {
|
||||
info >> 4
|
||||
}
|
||||
pub fn ELF32_ST_TYPE(info: u8) -> u8 {
|
||||
pub const fn ELF32_ST_TYPE(info: u8) -> u8 {
|
||||
info & 0xf
|
||||
}
|
||||
pub fn ELF32_ST_INFO(bind: u8, ty: u8) -> u8 {
|
||||
pub const fn ELF32_ST_INFO(bind: u8, ty: u8) -> u8 {
|
||||
(bind << 4) | (ty & 0xf)
|
||||
}
|
||||
2
nac3binutils/src/include/mod.rs
Normal file
2
nac3binutils/src/include/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod dwarf;
|
||||
pub mod elf;
|
||||
20
nac3binutils/src/lib.rs
Normal file
20
nac3binutils/src/lib.rs
Normal file
@@ -0,0 +1,20 @@
|
||||
#![deny(future_incompatible, let_underscore, nonstandard_style, clippy::all)]
|
||||
#![warn(clippy::pedantic, clippy::nursery)]
|
||||
#![allow(
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_possible_wrap,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::cognitive_complexity,
|
||||
clippy::missing_errors_doc,
|
||||
clippy::missing_panics_doc,
|
||||
clippy::similar_names,
|
||||
clippy::too_many_lines,
|
||||
clippy::wildcard_imports
|
||||
)]
|
||||
|
||||
mod dwarf;
|
||||
mod include;
|
||||
mod linker;
|
||||
pub mod symbolizer;
|
||||
|
||||
pub use linker::Linker;
|
||||
@@ -1,28 +1,12 @@
|
||||
#![deny(future_incompatible, let_underscore, nonstandard_style, clippy::all)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![allow(
|
||||
clippy::cast_possible_truncation,
|
||||
clippy::cast_possible_wrap,
|
||||
clippy::cast_sign_loss,
|
||||
clippy::enum_glob_use,
|
||||
clippy::missing_errors_doc,
|
||||
clippy::missing_panics_doc,
|
||||
clippy::similar_names,
|
||||
clippy::too_many_lines
|
||||
)]
|
||||
|
||||
use std::{collections::HashMap, mem, ptr, slice, str};
|
||||
|
||||
use byteorder::{ByteOrder, LittleEndian};
|
||||
|
||||
use dwarf::{EH_Frame, EH_Frame_Hdr};
|
||||
#[allow(clippy::wildcard_imports)]
|
||||
use elf::*;
|
||||
use crate::dwarf::{EH_Frame, EH_Frame_Hdr};
|
||||
|
||||
mod dwarf;
|
||||
mod elf;
|
||||
use crate::include::elf::*;
|
||||
|
||||
#[derive(PartialEq, Clone, Copy)]
|
||||
#[derive(PartialEq, Eq, Clone, Copy)]
|
||||
pub enum Isa {
|
||||
CortexA9,
|
||||
RiscV32,
|
||||
@@ -35,8 +19,8 @@ pub enum Error {
|
||||
}
|
||||
|
||||
impl From<&'static str> for Error {
|
||||
fn from(desc: &'static str) -> Error {
|
||||
Error::Parsing(desc)
|
||||
fn from(desc: &'static str) -> Self {
|
||||
Self::Parsing(desc)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,13 +67,17 @@ impl Relocatable for Elf32_Rela {
|
||||
// `__aeabi_unwind_cpp_pr0` in exception-throwing code.
|
||||
const R_TYPE_NONE: u8 = 0;
|
||||
|
||||
// Number of program header
|
||||
const ELF_PHNUM: usize = 5;
|
||||
const DEBUG_PHNUM: usize = 0; // Debug image is not loadable
|
||||
|
||||
struct SectionRecord<'a> {
|
||||
shdr: Elf32_Shdr,
|
||||
name: &'a str,
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
fn read_unaligned<T: Copy>(data: &[u8], offset: usize) -> Option<T> {
|
||||
const fn read_unaligned<T: Copy>(data: &[u8], offset: usize) -> Option<T> {
|
||||
if data.len() < offset + mem::size_of::<T>() {
|
||||
None
|
||||
} else {
|
||||
@@ -99,7 +87,7 @@ fn read_unaligned<T: Copy>(data: &[u8], offset: usize) -> Option<T> {
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn get_ref_slice<T: Copy>(data: &[u8], offset: usize, len: usize) -> Option<&[T]> {
|
||||
pub const fn get_ref_slice<T: Copy>(data: &[u8], offset: usize, len: usize) -> Option<&[T]> {
|
||||
if data.len() < offset + mem::size_of::<T>() * len {
|
||||
None
|
||||
} else {
|
||||
@@ -113,11 +101,11 @@ fn from_struct_slice<T>(struct_vec: &[T]) -> Vec<u8> {
|
||||
unsafe { slice::from_raw_parts(ptr.cast(), mem::size_of_val(struct_vec)) }.to_vec()
|
||||
}
|
||||
|
||||
fn to_struct_slice<T>(bytes: &[u8]) -> &[T] {
|
||||
const fn to_struct_slice<T>(bytes: &[u8]) -> &[T] {
|
||||
unsafe { slice::from_raw_parts(bytes.as_ptr().cast(), bytes.len() / mem::size_of::<T>()) }
|
||||
}
|
||||
|
||||
fn to_struct_mut_slice<T>(bytes: &mut [u8]) -> &mut [T] {
|
||||
const fn to_struct_mut_slice<T>(bytes: &mut [u8]) -> &mut [T] {
|
||||
unsafe {
|
||||
slice::from_raw_parts_mut(bytes.as_mut_ptr().cast(), bytes.len() / mem::size_of::<T>())
|
||||
}
|
||||
@@ -165,29 +153,114 @@ struct SymbolTableReader<'a> {
|
||||
impl SymbolTableReader<'_> {
|
||||
pub fn find_index_by_name(&self, sym_name: &[u8]) -> Option<usize> {
|
||||
self.symtab.iter().position(|sym| {
|
||||
if let Ok(dynsym_name) = name_starting_at_slice(self.strtab, sym.st_name as usize) {
|
||||
sym_name == dynsym_name
|
||||
} else {
|
||||
false
|
||||
}
|
||||
name_starting_at_slice(self.strtab, sym.st_name as usize)
|
||||
.is_ok_and(|dynsym_name| sym_name == dynsym_name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct Image {
|
||||
data: mem::MaybeUninit<Vec<u8>>,
|
||||
load_offset: u32,
|
||||
shdr_offset: u32,
|
||||
}
|
||||
|
||||
impl Image {
|
||||
pub const fn register<'a>(
|
||||
&mut self,
|
||||
shdr: &Elf32_Shdr,
|
||||
sh_name_str: &'a str,
|
||||
data: Vec<u8>,
|
||||
) -> SectionRecord<'a> {
|
||||
let mut elf_shdr = *shdr;
|
||||
|
||||
// Maintain alignment requirement specified in sh_addralign
|
||||
let align = shdr.sh_addralign;
|
||||
let load_padding = (align - (self.load_offset % align)) % align;
|
||||
let image_padding = (align - (self.shdr_offset % align)) % align;
|
||||
|
||||
let section_load_offset = if (shdr.sh_flags as usize & SHF_ALLOC) == SHF_ALLOC {
|
||||
self.load_offset + load_padding
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let section_image_offset = self.shdr_offset + image_padding;
|
||||
|
||||
elf_shdr.sh_addr = section_load_offset;
|
||||
elf_shdr.sh_offset = section_image_offset;
|
||||
|
||||
if (shdr.sh_flags as usize & SHF_ALLOC) == SHF_ALLOC {
|
||||
self.load_offset = section_load_offset + shdr.sh_size;
|
||||
}
|
||||
if shdr.sh_type as usize != SHT_NOBITS {
|
||||
self.shdr_offset = section_image_offset + shdr.sh_size;
|
||||
}
|
||||
|
||||
SectionRecord { shdr: elf_shdr, name: sh_name_str, data }
|
||||
}
|
||||
|
||||
pub fn finalize(&mut self, shdr_recs: &[SectionRecord]) {
|
||||
let header_alignment = 4;
|
||||
|
||||
let mut final_len = self.shdr_offset;
|
||||
final_len += (header_alignment - (final_len % header_alignment)) % header_alignment;
|
||||
self.shdr_offset = final_len;
|
||||
let final_len = final_len as usize + shdr_recs.len() * mem::size_of::<Elf32_Shdr>();
|
||||
|
||||
self.data.write({
|
||||
// This leaks the memory, but ...
|
||||
let mut temp_vec: mem::ManuallyDrop<Vec<u32>> =
|
||||
mem::ManuallyDrop::new(Vec::with_capacity(final_len / header_alignment as usize));
|
||||
// This new vector picks up the memory.
|
||||
// The new vector is responsible to free the memory instead.
|
||||
unsafe { Vec::from_raw_parts(temp_vec.as_mut_ptr().cast(), final_len, final_len) }
|
||||
});
|
||||
let owned_buffer = unsafe { self.data.assume_init_mut() };
|
||||
|
||||
let mut shdr_ptr =
|
||||
unsafe { owned_buffer.as_mut_ptr().add(self.shdr_offset as usize).cast() };
|
||||
|
||||
for shdr_rec in shdr_recs {
|
||||
if shdr_rec.shdr.sh_type as usize != SHT_NOBITS {
|
||||
owned_buffer[shdr_rec.shdr.sh_offset as usize..][..shdr_rec.data.len()]
|
||||
.clone_from_slice(&shdr_rec.data);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
*shdr_ptr = shdr_rec.shdr;
|
||||
shdr_ptr = shdr_ptr.add(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_mut_ref<T>(&mut self, offset: usize, len: usize) -> &mut [T] {
|
||||
unsafe {
|
||||
let borrowed_buf = self.data.assume_init_mut();
|
||||
assert!(borrowed_buf.len() >= offset + len, "out of bound access to image buffer");
|
||||
slice::from_raw_parts_mut(borrowed_buf.as_mut_ptr().add(offset).cast(), len)
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn take(self) -> Vec<u8> {
|
||||
unsafe { self.data.assume_init() }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Linker<'a> {
|
||||
isa: Isa,
|
||||
symtab: &'a [Elf32_Sym],
|
||||
strtab: &'a [u8],
|
||||
elf_shdrs: Vec<SectionRecord<'a>>,
|
||||
debug_shdrs: Vec<SectionRecord<'a>>,
|
||||
section_map: HashMap<usize, usize>,
|
||||
image: Vec<u8>,
|
||||
load_offset: u32,
|
||||
image_offset: u32,
|
||||
debug_section_map: HashMap<usize, usize>,
|
||||
dyn_lib_image: Image,
|
||||
debug_image: Image,
|
||||
rela_dyn_relas: Vec<Elf32_Rela>,
|
||||
}
|
||||
|
||||
impl<'a> Linker<'a> {
|
||||
fn get_dynamic_symbol_table(&self) -> Result<SymbolTableReader, Error> {
|
||||
fn get_dynamic_symbol_table(&self) -> Result<SymbolTableReader<'_>, Error> {
|
||||
let dynsym_rec = get_section_by_name!(self, ".dynsym")
|
||||
.ok_or("cannot make SymbolTableReader using .dynsym")?;
|
||||
Ok(SymbolTableReader {
|
||||
@@ -197,38 +270,24 @@ impl<'a> Linker<'a> {
|
||||
}
|
||||
|
||||
fn load_section(&mut self, shdr: &Elf32_Shdr, sh_name_str: &'a str, data: Vec<u8>) -> usize {
|
||||
let mut elf_shdr = *shdr;
|
||||
|
||||
// Maintain alignment requirement specified in sh_addralign
|
||||
let align = shdr.sh_addralign;
|
||||
let load_padding = (align - (self.load_offset % align)) % align;
|
||||
let image_padding = (align - (self.image_offset % align)) % align;
|
||||
|
||||
let section_load_offset = if (shdr.sh_flags as usize & SHF_ALLOC) == SHF_ALLOC {
|
||||
self.load_offset + load_padding
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let section_image_offset = self.image_offset + image_padding;
|
||||
|
||||
elf_shdr.sh_addr = section_load_offset;
|
||||
elf_shdr.sh_offset = section_image_offset;
|
||||
self.elf_shdrs.push(SectionRecord { shdr: elf_shdr, name: sh_name_str, data });
|
||||
|
||||
if (shdr.sh_flags as usize & SHF_ALLOC) == SHF_ALLOC {
|
||||
self.load_offset = section_load_offset + shdr.sh_size;
|
||||
}
|
||||
if shdr.sh_type as usize != SHT_NOBITS {
|
||||
self.image_offset = section_image_offset + shdr.sh_size;
|
||||
}
|
||||
|
||||
self.elf_shdrs.push(self.dyn_lib_image.register(shdr, sh_name_str, data));
|
||||
self.elf_shdrs.len() - 1
|
||||
}
|
||||
|
||||
fn load_debug_section(
|
||||
&mut self,
|
||||
shdr: &Elf32_Shdr,
|
||||
sh_name_str: &'a str,
|
||||
data: Vec<u8>,
|
||||
) -> usize {
|
||||
self.debug_shdrs.push(self.debug_image.register(shdr, sh_name_str, data));
|
||||
self.debug_shdrs.len() - 1
|
||||
}
|
||||
|
||||
// Perform relocation according to the relocation entries
|
||||
// Only symbols that support relative addressing would be resolved
|
||||
// This is because the loading address is not known yet
|
||||
fn resolve_relocatables<R: Relocatable>(
|
||||
fn resolve_relocatables<R: Relocatable + std::fmt::Debug>(
|
||||
&mut self,
|
||||
relocs: &[R],
|
||||
target_section: Elf32_Word,
|
||||
@@ -242,6 +301,42 @@ impl<'a> Linker<'a> {
|
||||
pub relocate: Option<Box<RelocateFn>>,
|
||||
}
|
||||
|
||||
macro_rules! get_referred_section {
|
||||
($target_section: ident, $return_cb: expr) => {
|
||||
if let Some(shdr_index) = self.section_map.get(&($target_section as usize)) {
|
||||
Ok($return_cb(true, &self.elf_shdrs[*shdr_index], *shdr_index))
|
||||
} else if let Some(debug_index) =
|
||||
self.debug_section_map.get(&($target_section as usize))
|
||||
{
|
||||
Ok($return_cb(false, &self.debug_shdrs[*debug_index], *debug_index))
|
||||
} else {
|
||||
Err(Error::Parsing("Cannot find section with matching sh_index"))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let (loaded, target_index) =
|
||||
get_referred_section!(target_section, |loaded, _, idx| (loaded, idx)).unwrap();
|
||||
|
||||
macro_rules! get_shdr_attr {
|
||||
($index: ident, $attr: ident) => {
|
||||
if loaded { &self.elf_shdrs[$index].$attr } else { &self.debug_shdrs[$index].$attr }
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! get_mut_shdr_attr {
|
||||
($index: ident, $attr: ident) => {
|
||||
if loaded {
|
||||
&mut self.elf_shdrs[$index].$attr
|
||||
} else {
|
||||
&mut self.debug_shdrs[$index].$attr
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let target_section_alloc =
|
||||
get_shdr_attr!(target_index, shdr).sh_flags as usize & SHF_ALLOC == SHF_ALLOC;
|
||||
|
||||
for reloc in relocs {
|
||||
if reloc.type_info() == R_TYPE_NONE {
|
||||
continue;
|
||||
@@ -261,28 +356,16 @@ impl<'a> Linker<'a> {
|
||||
match sym.st_shndx {
|
||||
SHN_UNDEF => Err(Error::Lookup("undefined symbol")),
|
||||
SHN_ABS => Ok(sym.st_value),
|
||||
sec_ind => self
|
||||
.section_map
|
||||
.get(&(sec_ind as usize))
|
||||
.map(|&elf_sec_ind: &usize| {
|
||||
// Unlike the code in artiq libdyld, the image offset value is
|
||||
// irrelevant in this case.
|
||||
// The .elf dynamic library can be linked to an arbitrary address
|
||||
// within the kernel address space
|
||||
self.elf_shdrs[elf_sec_ind].shdr.sh_offset as Elf32_Word
|
||||
+ sym.st_value
|
||||
})
|
||||
.ok_or(Error::Parsing("section not mapped to the ELF file")),
|
||||
// Section index may refer to either a debug section or a loaded section
|
||||
// A debug relocation can still refer to a loaded section for symbol resolution
|
||||
sec_ind => get_referred_section!(sec_ind, |_, rec: &SectionRecord, _| rec
|
||||
.shdr
|
||||
.sh_addr
|
||||
as Elf32_Word
|
||||
+ sym.st_value),
|
||||
}
|
||||
};
|
||||
|
||||
let get_target_section_index = || -> Result<usize, Error> {
|
||||
self.section_map
|
||||
.get(&(target_section as usize))
|
||||
.copied()
|
||||
.ok_or(Error::Parsing("Cannot find section with matching sh_index"))
|
||||
};
|
||||
|
||||
let classify = |reloc: &R, sym_option: Option<&Elf32_Sym>| -> Option<RelocInfo<R>> {
|
||||
let defined_val = sym_option.is_none_or(|sym| {
|
||||
sym.st_shndx != SHN_UNDEF || ELF32_ST_BIND(sym.st_info) == STB_LOCAL
|
||||
@@ -315,7 +398,13 @@ impl<'a> Linker<'a> {
|
||||
defined_val,
|
||||
indirect_reloc: None,
|
||||
pc_relative: false,
|
||||
relocate: None,
|
||||
relocate: if target_section_alloc {
|
||||
None
|
||||
} else {
|
||||
Some(Box::new(|target_word, value| {
|
||||
LittleEndian::write_u32(target_word, value);
|
||||
}))
|
||||
},
|
||||
}),
|
||||
_ => None,
|
||||
},
|
||||
@@ -412,7 +501,13 @@ impl<'a> Linker<'a> {
|
||||
defined_val,
|
||||
indirect_reloc: None,
|
||||
pc_relative: false,
|
||||
relocate: None,
|
||||
relocate: if target_section_alloc {
|
||||
None
|
||||
} else {
|
||||
Some(Box::new(|target_word, value| {
|
||||
LittleEndian::write_u32(target_word, value);
|
||||
}))
|
||||
},
|
||||
}),
|
||||
|
||||
R_RISCV_SET32 => Some(RelocInfo {
|
||||
@@ -532,28 +627,24 @@ impl<'a> Linker<'a> {
|
||||
|
||||
let reloc_info =
|
||||
classify(reloc, sym).ok_or(Error::Parsing("unsupported relocation"))?;
|
||||
let target_index = get_target_section_index()?;
|
||||
let target_sec_off = self.elf_shdrs[target_index].shdr.sh_offset;
|
||||
let target_sec_off = get_shdr_attr!(target_index, shdr).sh_offset;
|
||||
|
||||
if reloc_info.defined_val {
|
||||
let (sym_addr, rela_off) = {
|
||||
let (refed_sym, refed_reloc) =
|
||||
if let Some(indirect_reloc) = reloc_info.indirect_reloc {
|
||||
(Some(&self.symtab[indirect_reloc.sym_info() as usize]), indirect_reloc)
|
||||
} else {
|
||||
(sym, reloc)
|
||||
};
|
||||
(resolve_symbol_addr(refed_sym)?, target_sec_off + refed_reloc.offset())
|
||||
};
|
||||
let (refed_sym, refed_reloc) =
|
||||
if let Some(indirect_reloc) = reloc_info.indirect_reloc {
|
||||
(Some(&self.symtab[indirect_reloc.sym_info() as usize]), indirect_reloc)
|
||||
} else {
|
||||
(sym, reloc)
|
||||
};
|
||||
let sym_addr = resolve_symbol_addr(refed_sym)?;
|
||||
let rela_off = target_sec_off + refed_reloc.offset();
|
||||
|
||||
let target_sec_image = &mut self.elf_shdrs[target_index].data;
|
||||
let value = if reloc_info.pc_relative {
|
||||
sym_addr
|
||||
.wrapping_sub(rela_off)
|
||||
.wrapping_add(reloc.addend(target_sec_image) as Elf32_Word)
|
||||
} else {
|
||||
sym_addr.wrapping_add(reloc.addend(target_sec_image) as Elf32_Word)
|
||||
};
|
||||
let target_sec_image = get_mut_shdr_attr!(target_index, data);
|
||||
let mut value =
|
||||
sym_addr.wrapping_add(refed_reloc.addend(target_sec_image) as Elf32_Word);
|
||||
if reloc_info.pc_relative {
|
||||
value = value.wrapping_sub(rela_off);
|
||||
}
|
||||
|
||||
if let Some(relocate) = reloc_info.relocate {
|
||||
let target_word = &mut target_sec_image[reloc.offset() as usize..];
|
||||
@@ -572,7 +663,7 @@ impl<'a> Linker<'a> {
|
||||
});
|
||||
}
|
||||
} else {
|
||||
let target_sec_image = &self.elf_shdrs[target_index].data;
|
||||
let target_sec_image = &get_shdr_attr!(target_index, data);
|
||||
|
||||
let sym_name = name_starting_at_slice(self.strtab, sym.unwrap().st_name as usize)
|
||||
.map_err(|_| "cannot read symbol name from original .strtab")?;
|
||||
@@ -588,6 +679,7 @@ impl<'a> Linker<'a> {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -629,7 +721,7 @@ impl<'a> Linker<'a> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn ld(data: &'a [u8]) -> Result<Vec<u8>, Error> {
|
||||
pub fn ld(data: &'a [u8]) -> Result<(Vec<u8>, Vec<u8>), Error> {
|
||||
fn allocate_rela_dyn<R: Relocatable>(
|
||||
linker: &Linker,
|
||||
relocs: &[R],
|
||||
@@ -756,16 +848,52 @@ impl<'a> Linker<'a> {
|
||||
name: "",
|
||||
data: vec![0; 0],
|
||||
}];
|
||||
let elf_sh_data_off = mem::size_of::<Elf32_Ehdr>() + mem::size_of::<Elf32_Phdr>() * 5;
|
||||
// Debug object also needs a starting NULL record
|
||||
let debug_shdrs = vec![SectionRecord {
|
||||
shdr: Elf32_Shdr {
|
||||
sh_name: 0,
|
||||
sh_type: 0,
|
||||
sh_flags: 0,
|
||||
sh_addr: 0,
|
||||
sh_offset: 0,
|
||||
sh_size: 0,
|
||||
sh_link: 0,
|
||||
sh_info: 0,
|
||||
sh_addralign: 0,
|
||||
sh_entsize: 0,
|
||||
},
|
||||
name: "",
|
||||
data: vec![0; 0],
|
||||
}];
|
||||
|
||||
let elf_sh_data_off =
|
||||
mem::size_of::<Elf32_Ehdr>() + mem::size_of::<Elf32_Phdr>() * ELF_PHNUM;
|
||||
let debug_sh_data_off =
|
||||
mem::size_of::<Elf32_Ehdr>() + mem::size_of::<Elf32_Phdr>() * DEBUG_PHNUM;
|
||||
|
||||
// Image of the linked dynamic library, to be formalized incrementally
|
||||
// just as the section table eventually does
|
||||
let image: Vec<u8> = vec![0; elf_sh_data_off];
|
||||
let dyn_lib_image = Image {
|
||||
data: mem::MaybeUninit::uninit(),
|
||||
load_offset: elf_sh_data_off as u32,
|
||||
shdr_offset: elf_sh_data_off as u32,
|
||||
};
|
||||
|
||||
// Debug image
|
||||
// Only to the symbolizer for traceback generation
|
||||
let debug_image = Image {
|
||||
data: mem::MaybeUninit::uninit(),
|
||||
load_offset: debug_sh_data_off as u32,
|
||||
shdr_offset: debug_sh_data_off as u32,
|
||||
};
|
||||
|
||||
// Section relocation table
|
||||
// A map of the original index of copied sections to the new sections
|
||||
let section_map = HashMap::new();
|
||||
|
||||
// Section relocation table, but for debug sections
|
||||
let debug_section_map = HashMap::new();
|
||||
|
||||
// Vector of relocation entries in .rela.dyn
|
||||
let rela_dyn_relas = Vec::new();
|
||||
|
||||
@@ -774,10 +902,11 @@ impl<'a> Linker<'a> {
|
||||
symtab,
|
||||
strtab,
|
||||
elf_shdrs,
|
||||
debug_shdrs,
|
||||
section_map,
|
||||
image,
|
||||
load_offset: elf_sh_data_off as u32,
|
||||
image_offset: elf_sh_data_off as u32,
|
||||
debug_section_map,
|
||||
dyn_lib_image,
|
||||
debug_image,
|
||||
rela_dyn_relas,
|
||||
};
|
||||
|
||||
@@ -1362,19 +1491,28 @@ impl<'a> Linker<'a> {
|
||||
last_elf_shdr_index as Elf32_Section
|
||||
);
|
||||
|
||||
// Load unallocated PROGBITS sections
|
||||
// Mainly for debugging symbols
|
||||
for (i, shdr) in shdrs.iter().enumerate() {
|
||||
if (shdr.sh_type as usize != SHT_PROGBITS)
|
||||
|| (shdr.sh_flags as usize & SHF_ALLOC == SHF_ALLOC)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let section_name = name_starting_at_slice(strtab, shdr.sh_name as usize)
|
||||
.map_err(|_| "cannot read section name")?;
|
||||
let elf_shdrs_index = linker.load_debug_section(
|
||||
shdr,
|
||||
str::from_utf8(section_name).unwrap(),
|
||||
data[shdr.sh_offset as usize..(shdr.sh_offset + shdr.sh_size) as usize].to_vec(),
|
||||
);
|
||||
linker.debug_section_map.insert(i, elf_shdrs_index);
|
||||
}
|
||||
|
||||
for shdr in shdrs
|
||||
.iter()
|
||||
.filter(|shdr| shdr.sh_type as usize == SHT_RELA || shdr.sh_type as usize == SHT_REL)
|
||||
{
|
||||
// If the reloction refers to a section that will not be loaded,
|
||||
// do not process the relocations. The section will not be loaded
|
||||
let referred_shdr = shdrs
|
||||
.get(shdr.sh_info as usize)
|
||||
.ok_or("relocation is not specified to a valid section number")?;
|
||||
if (referred_shdr.sh_flags as usize & SHF_ALLOC) != SHF_ALLOC {
|
||||
continue;
|
||||
}
|
||||
|
||||
reloc_invariant!(shdr, |relocs| linker.resolve_relocatables(relocs, shdr.sh_info))?;
|
||||
}
|
||||
|
||||
@@ -1385,6 +1523,7 @@ impl<'a> Linker<'a> {
|
||||
let rela_dyn_slice =
|
||||
to_struct_mut_slice::<Elf32_Rela>(rela_dyn_rec.data.as_mut_slice());
|
||||
|
||||
assert_eq!(linker.rela_dyn_relas.iter().len(), rela_dyn_slice.len());
|
||||
for (i, &rela) in linker.rela_dyn_relas.iter().enumerate() {
|
||||
rela_dyn_slice[i] = rela;
|
||||
}
|
||||
@@ -1417,140 +1556,171 @@ impl<'a> Linker<'a> {
|
||||
sh_entsize: 0,
|
||||
};
|
||||
|
||||
// Same for the debug sections
|
||||
let mut debug_shstrtab = Vec::new();
|
||||
for shdr_rec in &mut linker.debug_shdrs {
|
||||
let shstrtab_index = debug_shstrtab.len();
|
||||
debug_shstrtab.extend(shdr_rec.name.as_bytes());
|
||||
debug_shstrtab.push(0);
|
||||
shdr_rec.shdr.sh_name = shstrtab_index as Elf32_Word;
|
||||
}
|
||||
// Add en entry for .shstrtab
|
||||
let debug_shstrtab_shdr_sh_name = debug_shstrtab.len();
|
||||
debug_shstrtab.extend(b".shstrtab");
|
||||
debug_shstrtab.push(0);
|
||||
|
||||
let debug_shstrtab_shdr = Elf32_Shdr {
|
||||
sh_name: debug_shstrtab_shdr_sh_name as Elf32_Word,
|
||||
sh_type: SHT_STRTAB as Elf32_Word,
|
||||
sh_flags: 0,
|
||||
sh_addr: 0,
|
||||
sh_offset: 0,
|
||||
sh_size: debug_shstrtab.len() as Elf32_Word,
|
||||
sh_link: 0,
|
||||
sh_info: 0,
|
||||
sh_addralign: 1,
|
||||
sh_entsize: 0,
|
||||
};
|
||||
|
||||
let shstrtab_elf_index = linker.load_section(&shstrtab_shdr, ".shstrtab", shstrtab);
|
||||
let debug_shstrtab_elf_index =
|
||||
linker.load_debug_section(&debug_shstrtab_shdr, ".shstrtab", debug_shstrtab);
|
||||
|
||||
// Edit .eh_frame_hdr content
|
||||
if linker.isa == Isa::RiscV32 {
|
||||
linker.implement_eh_frame_hdr()?;
|
||||
}
|
||||
|
||||
// Load all non-NOBITS section data into the image
|
||||
for rec in &linker.elf_shdrs[1..] {
|
||||
if rec.shdr.sh_type as usize != SHT_NOBITS {
|
||||
linker.image.extend(vec![0; (rec.shdr.sh_offset as usize) - linker.image.len()]);
|
||||
linker.image.extend(&rec.data);
|
||||
}
|
||||
}
|
||||
|
||||
// Load all section headers to the image
|
||||
let alignment = (4 - (linker.image.len() % 4)) % 4;
|
||||
let sec_headers_offset = linker.image.len() + alignment;
|
||||
linker.image.extend(vec![0; alignment]);
|
||||
for rec in &linker.elf_shdrs {
|
||||
let shdr = rec.shdr;
|
||||
linker.image.extend(unsafe {
|
||||
slice::from_raw_parts(ptr::addr_of!(shdr).cast(), mem::size_of::<Elf32_Shdr>())
|
||||
});
|
||||
}
|
||||
linker.dyn_lib_image.finalize(&linker.elf_shdrs);
|
||||
linker.debug_image.finalize(&linker.debug_shdrs);
|
||||
|
||||
// Update the PHDRs
|
||||
let phdr_offset = mem::size_of::<Elf32_Ehdr>();
|
||||
unsafe {
|
||||
let phdr_ptr = linker.image.as_mut_ptr().add(phdr_offset).cast();
|
||||
let phdr_slice = slice::from_raw_parts_mut(phdr_ptr, 5);
|
||||
// List of program headers:
|
||||
// 1. ELF headers & program headers
|
||||
// 2. Read-only sections
|
||||
// 3. All other A-flag sections
|
||||
// 4. Dynamic
|
||||
// 5. EH frame & its header
|
||||
let header_size = mem::size_of::<Elf32_Ehdr>() + mem::size_of::<Elf32_Phdr>() * 5;
|
||||
phdr_slice[0] = Elf32_Phdr {
|
||||
p_type: PT_LOAD,
|
||||
p_offset: 0,
|
||||
p_vaddr: 0,
|
||||
p_paddr: 0,
|
||||
p_filesz: header_size as Elf32_Word,
|
||||
p_memsz: header_size as Elf32_Word,
|
||||
p_flags: PF_R as Elf32_Word,
|
||||
p_align: 0x1000,
|
||||
};
|
||||
let last_ro_shdr = linker.elf_shdrs[first_writable_sec_elf_index - 1].shdr;
|
||||
let last_ro_addr = last_ro_shdr.sh_offset + last_ro_shdr.sh_size;
|
||||
let ro_load_size = last_ro_addr - header_size as Elf32_Word;
|
||||
phdr_slice[1] = Elf32_Phdr {
|
||||
p_type: PT_LOAD,
|
||||
p_offset: header_size as Elf32_Off,
|
||||
p_vaddr: header_size as Elf32_Addr,
|
||||
p_paddr: header_size as Elf32_Addr,
|
||||
p_filesz: ro_load_size,
|
||||
p_memsz: ro_load_size,
|
||||
p_flags: (PF_R | PF_X) as Elf32_Word,
|
||||
p_align: 0x1000,
|
||||
};
|
||||
let first_w_shdr = linker.elf_shdrs[first_writable_sec_elf_index].shdr;
|
||||
let first_w_addr = first_w_shdr.sh_offset;
|
||||
let last_w_shdr = linker.elf_shdrs[last_w_sec_elf_index].shdr;
|
||||
// According to the specification, regarding PT_LOAD program header when filesz < memsz:
|
||||
// The ``extra`` bytes are defined to hold the value 0 and to follow the segment's initialized area.
|
||||
//
|
||||
// We use this specified behavior to handle NOBITS.
|
||||
let w_fsize = last_w_shdr.sh_offset + last_w_shdr.sh_size - first_w_addr;
|
||||
let w_msize = end_load_addr - first_w_addr;
|
||||
phdr_slice[2] = Elf32_Phdr {
|
||||
p_type: PT_LOAD,
|
||||
p_offset: first_w_addr as Elf32_Off,
|
||||
p_vaddr: first_w_addr as Elf32_Addr,
|
||||
p_paddr: first_w_addr as Elf32_Addr,
|
||||
p_filesz: w_fsize,
|
||||
p_memsz: w_msize,
|
||||
p_flags: (PF_R | PF_W) as Elf32_Word,
|
||||
p_align: 0x1000,
|
||||
};
|
||||
let dynamic_shdr = linker.elf_shdrs[dynamic_elf_index].shdr;
|
||||
phdr_slice[3] = Elf32_Phdr {
|
||||
p_type: PT_DYNAMIC,
|
||||
p_offset: dynamic_shdr.sh_offset,
|
||||
p_vaddr: dynamic_shdr.sh_offset,
|
||||
p_paddr: dynamic_shdr.sh_offset,
|
||||
p_filesz: dynamic_shdr.sh_size,
|
||||
p_memsz: dynamic_shdr.sh_size,
|
||||
p_flags: (PF_R | PF_W) as Elf32_Word,
|
||||
p_align: 4,
|
||||
};
|
||||
let (eh_type, eh_shdr_name) = match linker.isa {
|
||||
Isa::CortexA9 => (PT_ARM_EXIDX, ".ARM.exidx"),
|
||||
Isa::RiscV32 => (PT_GNU_EH_FRAME, ".eh_frame_hdr"),
|
||||
};
|
||||
let eh_shdr = get_section_by_name!(linker, eh_shdr_name)
|
||||
.ok_or("cannot read error handling section when finalizing phdrs")?
|
||||
.shdr;
|
||||
phdr_slice[4] = Elf32_Phdr {
|
||||
p_type: eh_type,
|
||||
p_offset: eh_shdr.sh_offset,
|
||||
p_vaddr: eh_shdr.sh_offset,
|
||||
p_paddr: eh_shdr.sh_offset,
|
||||
p_filesz: eh_shdr.sh_size,
|
||||
p_memsz: eh_shdr.sh_size,
|
||||
p_flags: PF_R as Elf32_Word,
|
||||
p_align: 4,
|
||||
};
|
||||
}
|
||||
let phdr_slice: &mut [Elf32_Phdr] =
|
||||
linker.dyn_lib_image.get_mut_ref(phdr_offset, ELF_PHNUM);
|
||||
// List of program headers:
|
||||
// 1. ELF headers & program headers
|
||||
// 2. Read-only sections
|
||||
// 3. All other A-flag sections
|
||||
// 4. Dynamic
|
||||
// 5. EH frame & its header
|
||||
let header_size = mem::size_of::<Elf32_Ehdr>() + mem::size_of::<Elf32_Phdr>() * 5;
|
||||
phdr_slice[0] = Elf32_Phdr {
|
||||
p_type: PT_LOAD,
|
||||
p_offset: 0,
|
||||
p_vaddr: 0,
|
||||
p_paddr: 0,
|
||||
p_filesz: header_size as Elf32_Word,
|
||||
p_memsz: header_size as Elf32_Word,
|
||||
p_flags: PF_R as Elf32_Word,
|
||||
p_align: 0x1000,
|
||||
};
|
||||
let last_ro_shdr = linker.elf_shdrs[first_writable_sec_elf_index - 1].shdr;
|
||||
let last_ro_addr = last_ro_shdr.sh_offset + last_ro_shdr.sh_size;
|
||||
let ro_load_size = last_ro_addr - header_size as Elf32_Word;
|
||||
phdr_slice[1] = Elf32_Phdr {
|
||||
p_type: PT_LOAD,
|
||||
p_offset: header_size as Elf32_Off,
|
||||
p_vaddr: header_size as Elf32_Addr,
|
||||
p_paddr: header_size as Elf32_Addr,
|
||||
p_filesz: ro_load_size,
|
||||
p_memsz: ro_load_size,
|
||||
p_flags: (PF_R | PF_X) as Elf32_Word,
|
||||
p_align: 0x1000,
|
||||
};
|
||||
let first_w_shdr = linker.elf_shdrs[first_writable_sec_elf_index].shdr;
|
||||
let first_w_addr = first_w_shdr.sh_offset;
|
||||
let last_w_shdr = linker.elf_shdrs[last_w_sec_elf_index].shdr;
|
||||
// According to the specification, regarding PT_LOAD program header when filesz < memsz:
|
||||
// The ``extra`` bytes are defined to hold the value 0 and to follow the segment's initialized area.
|
||||
//
|
||||
// We use this specified behavior to handle NOBITS.
|
||||
let w_fsize = last_w_shdr.sh_offset + last_w_shdr.sh_size - first_w_addr;
|
||||
let w_msize = end_load_addr - first_w_addr;
|
||||
phdr_slice[2] = Elf32_Phdr {
|
||||
p_type: PT_LOAD,
|
||||
p_offset: first_w_addr as Elf32_Off,
|
||||
p_vaddr: first_w_addr as Elf32_Addr,
|
||||
p_paddr: first_w_addr as Elf32_Addr,
|
||||
p_filesz: w_fsize,
|
||||
p_memsz: w_msize,
|
||||
p_flags: (PF_R | PF_W) as Elf32_Word,
|
||||
p_align: 0x1000,
|
||||
};
|
||||
let dynamic_shdr = linker.elf_shdrs[dynamic_elf_index].shdr;
|
||||
phdr_slice[3] = Elf32_Phdr {
|
||||
p_type: PT_DYNAMIC,
|
||||
p_offset: dynamic_shdr.sh_offset,
|
||||
p_vaddr: dynamic_shdr.sh_offset,
|
||||
p_paddr: dynamic_shdr.sh_offset,
|
||||
p_filesz: dynamic_shdr.sh_size,
|
||||
p_memsz: dynamic_shdr.sh_size,
|
||||
p_flags: (PF_R | PF_W) as Elf32_Word,
|
||||
p_align: 4,
|
||||
};
|
||||
let (eh_type, eh_shdr_name) = match linker.isa {
|
||||
Isa::CortexA9 => (PT_ARM_EXIDX, ".ARM.exidx"),
|
||||
Isa::RiscV32 => (PT_GNU_EH_FRAME, ".eh_frame_hdr"),
|
||||
};
|
||||
let eh_shdr = get_section_by_name!(linker, eh_shdr_name)
|
||||
.ok_or("cannot read error handling section when finalizing phdrs")?
|
||||
.shdr;
|
||||
phdr_slice[4] = Elf32_Phdr {
|
||||
p_type: eh_type,
|
||||
p_offset: eh_shdr.sh_offset,
|
||||
p_vaddr: eh_shdr.sh_offset,
|
||||
p_paddr: eh_shdr.sh_offset,
|
||||
p_filesz: eh_shdr.sh_size,
|
||||
p_memsz: eh_shdr.sh_size,
|
||||
p_flags: PF_R as Elf32_Word,
|
||||
p_align: 4,
|
||||
};
|
||||
|
||||
// Update the EHDR
|
||||
let ehdr_ptr = linker.image.as_mut_ptr().cast();
|
||||
unsafe {
|
||||
*ehdr_ptr = Elf32_Ehdr {
|
||||
e_ident: ehdr.e_ident,
|
||||
e_type: ET_DYN,
|
||||
e_machine: ehdr.e_machine,
|
||||
e_version: ehdr.e_version,
|
||||
e_entry: elf_sh_data_off as Elf32_Addr,
|
||||
e_phoff: phdr_offset as Elf32_Off,
|
||||
e_shoff: sec_headers_offset as Elf32_Off,
|
||||
e_flags: match linker.isa {
|
||||
Isa::RiscV32 => ehdr.e_flags,
|
||||
Isa::CortexA9 => ehdr.e_flags | EF_ARM_ABI_FLOAT_HARD as Elf32_Word,
|
||||
},
|
||||
e_ehsize: mem::size_of::<Elf32_Ehdr>() as Elf32_Half,
|
||||
e_phentsize: mem::size_of::<Elf32_Phdr>() as Elf32_Half,
|
||||
e_phnum: 5,
|
||||
e_shentsize: mem::size_of::<Elf32_Shdr>() as Elf32_Half,
|
||||
e_shnum: linker.elf_shdrs.len() as Elf32_Half,
|
||||
e_shstrndx: shstrtab_elf_index as Elf32_Half,
|
||||
}
|
||||
}
|
||||
let dyn_lib_e_shoff = linker.dyn_lib_image.shdr_offset;
|
||||
let ehdr_ptr: &mut [Elf32_Ehdr] = linker.dyn_lib_image.get_mut_ref(0, 1);
|
||||
ehdr_ptr[0] = Elf32_Ehdr {
|
||||
e_ident: ehdr.e_ident,
|
||||
e_type: ET_DYN,
|
||||
e_machine: ehdr.e_machine,
|
||||
e_version: ehdr.e_version,
|
||||
e_entry: elf_sh_data_off as Elf32_Addr,
|
||||
e_phoff: phdr_offset as Elf32_Off,
|
||||
e_shoff: dyn_lib_e_shoff,
|
||||
e_flags: match linker.isa {
|
||||
Isa::RiscV32 => ehdr.e_flags,
|
||||
Isa::CortexA9 => ehdr.e_flags | EF_ARM_ABI_FLOAT_HARD as Elf32_Word,
|
||||
},
|
||||
e_ehsize: mem::size_of::<Elf32_Ehdr>() as Elf32_Half,
|
||||
e_phentsize: mem::size_of::<Elf32_Phdr>() as Elf32_Half,
|
||||
e_phnum: ELF_PHNUM as Elf32_Half,
|
||||
e_shentsize: mem::size_of::<Elf32_Shdr>() as Elf32_Half,
|
||||
e_shnum: linker.elf_shdrs.len() as Elf32_Half,
|
||||
e_shstrndx: shstrtab_elf_index as Elf32_Half,
|
||||
};
|
||||
|
||||
Ok(linker.image)
|
||||
let debug_e_shoff = linker.debug_image.shdr_offset;
|
||||
let ehdr_ptr: &mut [Elf32_Ehdr] = linker.debug_image.get_mut_ref(0, 1);
|
||||
ehdr_ptr[0] = Elf32_Ehdr {
|
||||
e_ident: ehdr.e_ident,
|
||||
e_type: ET_DYN,
|
||||
e_machine: ehdr.e_machine,
|
||||
e_version: ehdr.e_version,
|
||||
e_entry: debug_sh_data_off as Elf32_Addr,
|
||||
e_phoff: phdr_offset as Elf32_Off,
|
||||
e_shoff: debug_e_shoff,
|
||||
e_flags: match linker.isa {
|
||||
Isa::RiscV32 => ehdr.e_flags,
|
||||
Isa::CortexA9 => ehdr.e_flags | EF_ARM_ABI_FLOAT_HARD as Elf32_Word,
|
||||
},
|
||||
e_ehsize: mem::size_of::<Elf32_Ehdr>() as Elf32_Half,
|
||||
e_phentsize: mem::size_of::<Elf32_Phdr>() as Elf32_Half,
|
||||
e_phnum: DEBUG_PHNUM as Elf32_Half,
|
||||
e_shentsize: mem::size_of::<Elf32_Shdr>() as Elf32_Half,
|
||||
e_shnum: linker.debug_shdrs.len() as Elf32_Half,
|
||||
e_shstrndx: debug_shstrtab_elf_index as Elf32_Half,
|
||||
};
|
||||
|
||||
Ok((linker.dyn_lib_image.take(), linker.debug_image.take()))
|
||||
}
|
||||
}
|
||||
832
nac3binutils/src/symbolizer.rs
Normal file
832
nac3binutils/src/symbolizer.rs
Normal file
@@ -0,0 +1,832 @@
|
||||
#![allow(nonstandard_style)]
|
||||
|
||||
use std::{cmp, collections::HashMap, fmt::Error, mem, ptr, slice};
|
||||
|
||||
use crate::dwarf::DwarfReader;
|
||||
use crate::include::dwarf::*;
|
||||
use crate::include::elf::*;
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
enum NameRef {
|
||||
Concrete(&'static str),
|
||||
Abstract(usize),
|
||||
Unknown,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CallRecord {
|
||||
name: NameRef,
|
||||
pub address: Option<u32>,
|
||||
pub line: u32,
|
||||
pub column: u32,
|
||||
pub file: &'static str,
|
||||
pub dir: Option<&'static str>,
|
||||
}
|
||||
|
||||
impl CallRecord {
|
||||
#[must_use]
|
||||
pub fn get_name(&self) -> &'static str {
|
||||
if let NameRef::Concrete(name) = self.name {
|
||||
name
|
||||
} else {
|
||||
panic!("name reference should always be finalzied when read")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct AbbreviationEntry {
|
||||
tag: DW_TAG,
|
||||
has_child: DW_CHILDREN,
|
||||
attribute_specs: Vec<(DW_AT, DW_FORM)>,
|
||||
}
|
||||
|
||||
impl AbbreviationEntry {
|
||||
// Returns (abbreviation code, entry)
|
||||
pub fn new(reader: &mut DwarfReader) -> Option<(u64, Self)> {
|
||||
let abbrev_code = reader.read_uleb128();
|
||||
if abbrev_code != 0 {
|
||||
let mut entry = Self { tag: 0, has_child: 0, attribute_specs: Vec::new() };
|
||||
entry.tag = reader.read_uleb128();
|
||||
entry.has_child = reader.read_u8();
|
||||
let mut attr = (reader.read_uleb128(), reader.read_uleb128());
|
||||
while attr != (0, 0) {
|
||||
entry.attribute_specs.push(attr);
|
||||
attr = (reader.read_uleb128(), reader.read_uleb128());
|
||||
}
|
||||
Some((abbrev_code, entry))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Special section names
|
||||
#[allow(clippy::struct_field_names)]
|
||||
struct DebugInfoReader {
|
||||
debug_info: &'static [u8],
|
||||
debug_abbrev: &'static [u8],
|
||||
debug_line: &'static [u8],
|
||||
debug_ranges: &'static [u8],
|
||||
debug_str: &'static [u8],
|
||||
}
|
||||
|
||||
impl DebugInfoReader {
|
||||
// -debug_infos are centralized into a section
|
||||
// We should not have partial compile unit
|
||||
pub fn search(&self, pc: u32) -> Vec<CallRecord> {
|
||||
self.search_compilation_units(DwarfReader::new(self.debug_info, 0), pc)
|
||||
}
|
||||
|
||||
fn parse_die_attributes(
|
||||
&self,
|
||||
reader: &mut DwarfReader,
|
||||
attr_specs: &Vec<(DW_AT, DW_FORM)>,
|
||||
file_ptrs: &[(&'static str, Option<&'static str>)],
|
||||
pc: u32,
|
||||
start_addr: u32,
|
||||
cu_origin: Option<u32>,
|
||||
) -> (bool, u32, NameRef, Option<u32>, CallRecord) {
|
||||
// Compute PC range
|
||||
let mut in_range = false;
|
||||
let mut low_pc: Option<u32> = None;
|
||||
let mut high_pc_relative = false;
|
||||
let mut high_pc: Option<u32> = None;
|
||||
let mut stmt_list_offset: u32 = 0;
|
||||
let mut name_ref: NameRef = NameRef::Unknown;
|
||||
let mut call_record = CallRecord {
|
||||
// This is different from the `name_ref` variable
|
||||
// The call site's name is only resolvable by parent DIE.
|
||||
name: NameRef::Unknown,
|
||||
address: None,
|
||||
line: 0,
|
||||
column: 0,
|
||||
file: "",
|
||||
dir: None,
|
||||
};
|
||||
|
||||
for (attr_name, attr_form) in attr_specs {
|
||||
match *attr_name {
|
||||
DW_AT_low_pc => {
|
||||
assert_eq!(
|
||||
*attr_form, DW_FORM_addr,
|
||||
"DW_AT_low_pc should be specified by an address"
|
||||
);
|
||||
low_pc = Some(reader.read_form_addr());
|
||||
}
|
||||
DW_AT_high_pc => match *attr_form {
|
||||
DW_FORM_addr => {
|
||||
high_pc = Some(reader.read_form_addr());
|
||||
}
|
||||
DW_FORM_data1 | DW_FORM_data2 | DW_FORM_data4 | DW_FORM_data8
|
||||
| DW_FORM_sdata | DW_FORM_udata => {
|
||||
high_pc_relative = true;
|
||||
high_pc = Some(reader.read_form_constant(*attr_form) as u32);
|
||||
}
|
||||
_ => panic!("DW_AT_high_pc value should either be an address or a constant"),
|
||||
},
|
||||
DW_AT_ranges => {
|
||||
assert_eq!(
|
||||
*attr_form, DW_FORM_sec_offset,
|
||||
"DW_AT_ranges should be specified by an address"
|
||||
);
|
||||
let debug_ranges_offset = reader.read_form_addr() as usize;
|
||||
let mut range_reader =
|
||||
DwarfReader::new(&self.debug_ranges[debug_ranges_offset..], 0);
|
||||
let mut begin_offset = range_reader.read_u32();
|
||||
let mut end_offset = range_reader.read_u32();
|
||||
while (begin_offset, end_offset) != (0, 0) {
|
||||
if (begin_offset..end_offset).contains(&(pc - start_addr)) {
|
||||
in_range = true;
|
||||
break;
|
||||
}
|
||||
begin_offset = range_reader.read_u32();
|
||||
end_offset = range_reader.read_u32();
|
||||
}
|
||||
}
|
||||
DW_AT_stmt_list => {
|
||||
assert_eq!(
|
||||
*attr_form, DW_FORM_sec_offset,
|
||||
"DW_AT_ranges should be specified by an address"
|
||||
);
|
||||
stmt_list_offset = reader.read_form_addr();
|
||||
}
|
||||
DW_AT_name => match *attr_form {
|
||||
DW_FORM_string => {
|
||||
name_ref = unsafe {
|
||||
NameRef::Concrete(mem::transmute::<&'_ str, &'static str>(
|
||||
reader.read_str(),
|
||||
))
|
||||
};
|
||||
}
|
||||
DW_FORM_strp => {
|
||||
let debug_str_offset = reader.read_form_addr() as usize;
|
||||
let str_head = &self.debug_str[debug_str_offset..];
|
||||
let str_len = str_head
|
||||
.iter()
|
||||
.position(|byte| *byte == 0)
|
||||
.expect("string should be null terminated");
|
||||
name_ref = unsafe {
|
||||
NameRef::Concrete(str::from_utf8_unchecked(&str_head[..str_len]))
|
||||
};
|
||||
}
|
||||
_ => panic!("name should be a string"),
|
||||
},
|
||||
// Inlined procedure may only include an abstract reference to another DIE
|
||||
// See Section 7.5.4 Attribute Encodings for reference type specifications
|
||||
// This replaces DW_AT_name, so we fetch DW_AT_name from that referred entry
|
||||
DW_AT_abstract_origin => {
|
||||
let referred_die_addr = match *attr_form {
|
||||
DW_FORM_ref1 | DW_FORM_ref2 | DW_FORM_ref4 | DW_FORM_ref8
|
||||
| DW_FORM_ref_udata
|
||||
if cu_origin.is_some() =>
|
||||
{
|
||||
let cu_relative_addr = reader.read_form_reference(*attr_form) as usize;
|
||||
cu_origin.unwrap() as usize + cu_relative_addr
|
||||
}
|
||||
|
||||
DW_FORM_ref_addr => reader.read_form_addr() as usize,
|
||||
|
||||
_ => unreachable!(
|
||||
"DW_AT_abstract_origin expects an apparopriate pointer type to a DIE"
|
||||
),
|
||||
};
|
||||
|
||||
name_ref = NameRef::Abstract(referred_die_addr);
|
||||
}
|
||||
DW_AT_call_file => {
|
||||
// call_record.file_idx = reader.read_form_constant(*attr_form) as u32;
|
||||
let file_idx = reader.read_form_constant(*attr_form) as usize;
|
||||
(call_record.file, call_record.dir) = file_ptrs[file_idx - 1];
|
||||
}
|
||||
DW_AT_call_line => {
|
||||
call_record.line = reader.read_form_constant(*attr_form) as u32;
|
||||
}
|
||||
DW_AT_call_column => {
|
||||
call_record.column = reader.read_form_constant(*attr_form) as u32;
|
||||
}
|
||||
_ => {
|
||||
// Unrecognized attributes
|
||||
// They are valid, but we cannot process them
|
||||
reader.skip_form(*attr_form);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if pc is within range
|
||||
let die_relevant = in_range || {
|
||||
low_pc.is_some_and(|low_pc| {
|
||||
let high_pc = high_pc.map_or_else(
|
||||
|| low_pc + 4,
|
||||
|high_pc| if high_pc_relative { low_pc + high_pc } else { high_pc },
|
||||
);
|
||||
(low_pc..high_pc).contains(&pc)
|
||||
})
|
||||
};
|
||||
|
||||
(die_relevant, stmt_list_offset, name_ref, low_pc, call_record)
|
||||
}
|
||||
|
||||
fn skip_die_attributes(reader: &mut DwarfReader, attr_specs: &Vec<(DW_AT, DW_FORM)>) {
|
||||
for (_attr_name, attr_form) in attr_specs {
|
||||
reader.skip_form(*attr_form);
|
||||
}
|
||||
}
|
||||
|
||||
fn search_compilation_units(&self, mut reader: DwarfReader, pc: u32) -> Vec<CallRecord> {
|
||||
while !reader.slice.is_empty() {
|
||||
let cu_origin = reader.virt_addr;
|
||||
|
||||
// 7.5.1.1 Compilation Unit Header
|
||||
let unit_length = reader.read_u32();
|
||||
let mut next_reader = reader.clone();
|
||||
next_reader.offset(unit_length);
|
||||
assert_eq!(reader.read_u16(), 4, "expected DWARF version 4");
|
||||
let debug_abbrev_offset = reader.read_u32() as usize;
|
||||
assert_eq!(reader.read_u8(), 4, "only 32-bit system is supported");
|
||||
|
||||
let abbrev_table = self.parse_abbrev(debug_abbrev_offset);
|
||||
|
||||
// Parse the actual DW_TAG_compile_unit, skip the partially parsed unit if irrelevant
|
||||
let compile_unit_abbrev_code = reader.read_uleb128();
|
||||
let abbrev_entry = abbrev_table.get(&compile_unit_abbrev_code).expect(
|
||||
"all non-zero abbreviation code should be resolvable by the abbreviation table",
|
||||
);
|
||||
assert_eq!(
|
||||
abbrev_entry.tag, DW_TAG_compile_unit,
|
||||
"a normal compile unit should start with DW_TAG_compile_unit"
|
||||
);
|
||||
|
||||
// FIXME: The start address is called base address in the docs
|
||||
//
|
||||
// The base address of a compilation unit is defined as the value of the DW_AT_low_pc attribute,
|
||||
// if present; otherwise, it is undefined
|
||||
let (cu_die_relevant, cu_stmt_list_offset, _cu_name_ref, start_addr, _cu_call_record) =
|
||||
self.parse_die_attributes(
|
||||
&mut reader,
|
||||
&abbrev_entry.attribute_specs,
|
||||
&[],
|
||||
pc,
|
||||
0,
|
||||
Some(cu_origin),
|
||||
);
|
||||
|
||||
if cu_die_relevant {
|
||||
let (immediate_call_record, file_ptrs) =
|
||||
self.parse_line_info(cu_stmt_list_offset as usize, pc);
|
||||
|
||||
let mut call_sites = vec![immediate_call_record];
|
||||
|
||||
assert!(
|
||||
self.search_dies(
|
||||
&mut reader,
|
||||
&abbrev_table,
|
||||
&file_ptrs,
|
||||
pc,
|
||||
start_addr.unwrap(),
|
||||
&mut call_sites,
|
||||
cu_origin,
|
||||
),
|
||||
"exhausted all debugging information entries (DIE)"
|
||||
);
|
||||
|
||||
// Resolve name references
|
||||
for rec in &mut call_sites {
|
||||
while let NameRef::Abstract(die_offset) = rec.name {
|
||||
let referred_die = &self.debug_info[die_offset..];
|
||||
let mut referred_reader = DwarfReader::new(referred_die, 0);
|
||||
let abbrev_code = referred_reader.read_uleb128();
|
||||
let abbrev_entry = abbrev_table.get(&abbrev_code).expect("all non-zero abbreviation code should be resolvable by the abbreviation table");
|
||||
let (_die_relevant, _stmt_list_offset, name_ref, _low_pc, _call_record) =
|
||||
self.parse_die_attributes(
|
||||
&mut referred_reader,
|
||||
&abbrev_entry.attribute_specs,
|
||||
&file_ptrs,
|
||||
pc,
|
||||
start_addr.unwrap(),
|
||||
None,
|
||||
);
|
||||
rec.name = name_ref;
|
||||
}
|
||||
assert!(
|
||||
!(rec.name == NameRef::Unknown),
|
||||
"found a call site with an unknown subroutine name"
|
||||
);
|
||||
}
|
||||
|
||||
return call_sites;
|
||||
}
|
||||
|
||||
reader = next_reader;
|
||||
}
|
||||
|
||||
unreachable!("no relevant debugging info to pc: {}", pc);
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[must_use]
|
||||
fn search_dies(
|
||||
&self,
|
||||
reader: &mut DwarfReader,
|
||||
abbrev_table: &HashMap<u64, AbbreviationEntry>,
|
||||
file_ptrs: &Vec<(&'static str, Option<&'static str>)>,
|
||||
pc: u32,
|
||||
start_addr: u32,
|
||||
call_sites: &mut Vec<CallRecord>,
|
||||
cu_origin: u32,
|
||||
) -> bool {
|
||||
let mut abbrev_code = reader.read_uleb128();
|
||||
while abbrev_code != 0 {
|
||||
let abbrev_entry = abbrev_table.get(&abbrev_code).expect(
|
||||
"all non-zero abbreviation code should be resolvable by the abbreviation table",
|
||||
);
|
||||
let (die_relevant, _stmt_list_offset, name_ref, _start_addr, call_record) = self
|
||||
.parse_die_attributes(
|
||||
reader,
|
||||
&abbrev_entry.attribute_specs,
|
||||
file_ptrs,
|
||||
pc,
|
||||
start_addr,
|
||||
Some(cu_origin),
|
||||
);
|
||||
|
||||
if die_relevant {
|
||||
if abbrev_entry.has_child != 0 {
|
||||
// It is possible to not find more appropriate entires in any subtrees
|
||||
// e.g. exception is raised in a function that is also a caller to some other functions
|
||||
let _ = self.search_dies(
|
||||
reader,
|
||||
abbrev_table,
|
||||
file_ptrs,
|
||||
pc,
|
||||
start_addr,
|
||||
call_sites,
|
||||
cu_origin,
|
||||
);
|
||||
}
|
||||
|
||||
let last_name_ref = call_sites.last_mut().unwrap();
|
||||
if last_name_ref.name == NameRef::Unknown {
|
||||
last_name_ref.name = name_ref;
|
||||
}
|
||||
// Only inlined subprogram has a call record
|
||||
if abbrev_entry.tag == DW_TAG_inlined_subroutine {
|
||||
call_sites.push(call_record);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if abbrev_entry.has_child != 0 {
|
||||
// Moving directly to its sibling is impossible if there are children
|
||||
// The entries are arranged with prefix ordering
|
||||
Self::skip_dies(reader, abbrev_table);
|
||||
}
|
||||
|
||||
abbrev_code = reader.read_uleb128();
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn skip_dies(reader: &mut DwarfReader, abbrev_table: &HashMap<u64, AbbreviationEntry>) {
|
||||
let mut abbrev_code = reader.read_uleb128();
|
||||
while abbrev_code != 0 {
|
||||
let abbrev_entry = abbrev_table.get(&abbrev_code).expect(
|
||||
"all non-zero abbreviation code should be resolvable by the abbreviation table",
|
||||
);
|
||||
|
||||
Self::skip_die_attributes(reader, &abbrev_entry.attribute_specs);
|
||||
|
||||
if abbrev_entry.has_child != 0 {
|
||||
// Moving directly to its sibling is impossible if there are children
|
||||
// The entries are arranged with prefix ordering
|
||||
Self::skip_dies(reader, abbrev_table);
|
||||
}
|
||||
|
||||
abbrev_code = reader.read_uleb128();
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_abbrev(&self, abbrev_offset: usize) -> HashMap<u64, AbbreviationEntry> {
|
||||
let mut reader = DwarfReader::new(&self.debug_abbrev[abbrev_offset..], 0);
|
||||
let mut table: HashMap<u64, AbbreviationEntry> = HashMap::new();
|
||||
|
||||
while let Some((code, entry)) = AbbreviationEntry::new(&mut reader) {
|
||||
table.insert(code, entry);
|
||||
}
|
||||
|
||||
table
|
||||
}
|
||||
|
||||
fn parse_line_info(
|
||||
&self,
|
||||
stmt_list_offset: usize,
|
||||
pc: u32,
|
||||
) -> (CallRecord, Vec<(&'static str, Option<&'static str>)>) {
|
||||
let mut header_reader = DwarfReader::new(&self.debug_line[stmt_list_offset..], 0);
|
||||
|
||||
// header begins
|
||||
let _unit_length = header_reader.read_u32(); // eventually consume all
|
||||
assert_eq!(header_reader.read_u16(), 4, "expected DWARF version 4 for .debug_line");
|
||||
let header_length = header_reader.read_u32();
|
||||
// Create a line program reader
|
||||
// So we can find the encoded call site first, then decode it directly
|
||||
let mut program_reader = header_reader.clone();
|
||||
program_reader.offset(header_length);
|
||||
|
||||
let minimum_instruction_length = u32::from(header_reader.read_u8());
|
||||
let maximum_operations_per_instruction = u32::from(header_reader.read_u8());
|
||||
let default_is_stmt = header_reader.read_u8();
|
||||
let line_base = header_reader.read_i8();
|
||||
let line_range = header_reader.read_u8();
|
||||
let opcode_base = header_reader.read_u8();
|
||||
// var-len array
|
||||
// We simply take the reference to the array and index it as necessary
|
||||
// However, the array starts with index = 1.
|
||||
// There are only opcode_base - 1 elements since opcode 0 is reserved
|
||||
// for the preamble of extended opcode.
|
||||
let standard_opcode_lengths = unsafe {
|
||||
mem::transmute::<&'_ [u8], &'static [u8]>(
|
||||
header_reader.read_slice(opcode_base as usize - 1),
|
||||
)
|
||||
};
|
||||
|
||||
{
|
||||
// Standard opcodes, if defined, must match the standard arities
|
||||
const MAX_STANDARD_OPCODES: usize = 12;
|
||||
const EXPECTED_ARITIES: [u8; MAX_STANDARD_OPCODES] =
|
||||
[0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1];
|
||||
let standard_opcode_num = cmp::min(MAX_STANDARD_OPCODES, opcode_base as usize - 1);
|
||||
assert_eq!(
|
||||
standard_opcode_lengths[standard_opcode_num..],
|
||||
EXPECTED_ARITIES[standard_opcode_num..]
|
||||
);
|
||||
}
|
||||
|
||||
let mut include_directories: Vec<&'static str> = vec![];
|
||||
let mut dir_str = header_reader.read_str();
|
||||
while !dir_str.is_empty() {
|
||||
include_directories.push(unsafe { mem::transmute::<&'_ str, &'static str>(dir_str) });
|
||||
dir_str = header_reader.read_str();
|
||||
}
|
||||
|
||||
let mut file_ptrs: Vec<(&'static str, Option<&'static str>)> = vec![];
|
||||
loop {
|
||||
let path_name =
|
||||
unsafe { mem::transmute::<&'_ str, &'static str>(header_reader.read_str()) };
|
||||
if path_name.is_empty() {
|
||||
break;
|
||||
}
|
||||
let dir_index = header_reader.read_uleb128();
|
||||
let dir_name = if dir_index == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(include_directories[dir_index as usize - 1])
|
||||
};
|
||||
let _last_modified = header_reader.read_uleb128();
|
||||
let _file_len = header_reader.read_uleb128();
|
||||
|
||||
file_ptrs.push((path_name, dir_name));
|
||||
}
|
||||
|
||||
// We execute the line program first
|
||||
// TODO: Register state can be trimmed for non-VLIW targets
|
||||
|
||||
// To clippy: This struct only makes sense here.
|
||||
// Why would I care about line program register when not parsing it?
|
||||
// Starting a new scope does not sound clearer in my opinion.
|
||||
#[allow(clippy::items_after_statements, clippy::struct_excessive_bools)]
|
||||
#[derive(Copy, Clone)]
|
||||
struct Register {
|
||||
address: u32,
|
||||
op_index: u32,
|
||||
file: u32,
|
||||
line: u32,
|
||||
column: u32,
|
||||
is_stmt: bool,
|
||||
basic_block: bool,
|
||||
end_sequence: bool,
|
||||
prologue_end: bool,
|
||||
epilogue_begin: bool,
|
||||
isa: u32,
|
||||
discriminator: u32,
|
||||
}
|
||||
|
||||
// The provisionally accepted entry
|
||||
// last_entry.address < (pc - start_addr)
|
||||
let mut last_entry = Register {
|
||||
address: 0,
|
||||
op_index: 0,
|
||||
file: 1,
|
||||
line: 1,
|
||||
column: 0,
|
||||
is_stmt: default_is_stmt != 0,
|
||||
basic_block: false,
|
||||
end_sequence: false,
|
||||
prologue_end: false,
|
||||
epilogue_begin: false,
|
||||
isa: 0,
|
||||
discriminator: 0,
|
||||
};
|
||||
|
||||
// From spec: the address of each entries in the matrix is strictly increasing
|
||||
// It means that we don't need to maintain a var-len matrix, keeping
|
||||
// adjacent entries is sufficient.
|
||||
let mut curr_entry = last_entry;
|
||||
|
||||
// Macro is avoidable by passing in the predetermined constants
|
||||
// But I would not like to pollute the arg list
|
||||
macro_rules! advance_address {
|
||||
($operation_advance: ident) => {
|
||||
let unadjusted_op_index = curr_entry.op_index + $operation_advance;
|
||||
|
||||
curr_entry.address += minimum_instruction_length
|
||||
* (unadjusted_op_index / maximum_operations_per_instruction);
|
||||
curr_entry.op_index += unadjusted_op_index % maximum_operations_per_instruction;
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! handle_special_opcode {
|
||||
($opcode: expr, $update_line: literal) => {
|
||||
let adjusted_opcode = $opcode - opcode_base;
|
||||
let operation_advance = u32::from(adjusted_opcode / line_range);
|
||||
|
||||
advance_address!(operation_advance);
|
||||
|
||||
if $update_line {
|
||||
curr_entry.line = curr_entry
|
||||
.line
|
||||
.wrapping_add_signed(i32::from(line_base))
|
||||
.wrapping_add(u32::from(adjusted_opcode % line_range));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! push_row_or_break {
|
||||
($last_entry: expr, $curr_entry: expr, $pc: expr) => {
|
||||
// This is the moment that we know if we have found the entry
|
||||
// Indicated by the address overtaking pc
|
||||
if (last_entry.address..curr_entry.address).contains($pc) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Update last entry otherwise
|
||||
last_entry = curr_entry;
|
||||
};
|
||||
}
|
||||
|
||||
while !curr_entry.end_sequence {
|
||||
// Decode opcode
|
||||
let opcode = program_reader.read_u8();
|
||||
match opcode {
|
||||
// Extended opcode
|
||||
0 => {
|
||||
let insn_len = program_reader.read_uleb128();
|
||||
let mut extended_opcode_reader = program_reader.clone();
|
||||
extended_opcode_reader.slice =
|
||||
&extended_opcode_reader.slice[..insn_len as usize];
|
||||
program_reader.offset(insn_len as u32);
|
||||
|
||||
let extended_opcode = extended_opcode_reader.read_u8();
|
||||
match extended_opcode {
|
||||
DW_LNE_end_sequence => {
|
||||
// No operands
|
||||
curr_entry.end_sequence = true;
|
||||
|
||||
push_row_or_break!(last_entry, curr_entry, &pc);
|
||||
}
|
||||
DW_LNE_set_address => {
|
||||
let address = extended_opcode_reader.read_u32();
|
||||
curr_entry.address = address;
|
||||
curr_entry.op_index = 0;
|
||||
}
|
||||
DW_LNE_define_file => {
|
||||
let path_name = unsafe {
|
||||
mem::transmute::<&'_ str, &'static str>(
|
||||
extended_opcode_reader.read_str(),
|
||||
)
|
||||
};
|
||||
let dir_index = extended_opcode_reader.read_uleb128();
|
||||
let dir_name = if dir_index == 0 {
|
||||
None
|
||||
} else {
|
||||
Some(include_directories[dir_index as usize - 1])
|
||||
};
|
||||
let _last_modified = extended_opcode_reader.read_uleb128();
|
||||
let _file_len = extended_opcode_reader.read_uleb128();
|
||||
|
||||
file_ptrs.push((path_name, dir_name));
|
||||
}
|
||||
DW_LNE_set_discriminator => {
|
||||
let discriminator = extended_opcode_reader.read_uleb128() as u32;
|
||||
curr_entry.discriminator = discriminator;
|
||||
}
|
||||
// It is posslbe that other user defined instruction appears
|
||||
// But, we do not support them, nor do we know what to do about them
|
||||
// Hence we simply skip these instructions
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
// Standard opcode
|
||||
DW_LNS_copy if DW_LNS_copy < opcode_base => {
|
||||
// No operands
|
||||
push_row_or_break!(last_entry, curr_entry, &pc);
|
||||
}
|
||||
DW_LNS_advance_pc if DW_LNS_advance_pc < opcode_base => {
|
||||
let operation_advance = program_reader.read_uleb128() as u32;
|
||||
advance_address!(operation_advance);
|
||||
}
|
||||
DW_LNS_advance_line if DW_LNS_advance_line < opcode_base => {
|
||||
let line_advance = program_reader.read_sleb128();
|
||||
curr_entry.line = curr_entry.line.wrapping_add_signed(line_advance as i32);
|
||||
}
|
||||
DW_LNS_set_file if DW_LNS_set_file < opcode_base => {
|
||||
let file = program_reader.read_uleb128();
|
||||
curr_entry.file = file as u32;
|
||||
}
|
||||
DW_LNS_set_column if DW_LNS_set_column < opcode_base => {
|
||||
let column = program_reader.read_uleb128();
|
||||
curr_entry.column = column as u32;
|
||||
}
|
||||
DW_LNS_negate_stmt if DW_LNS_negate_stmt < opcode_base => {
|
||||
// No operands
|
||||
curr_entry.is_stmt = !curr_entry.is_stmt;
|
||||
}
|
||||
DW_LNS_set_basic_block if DW_LNS_set_basic_block < opcode_base => {
|
||||
// No operands
|
||||
curr_entry.basic_block = true;
|
||||
}
|
||||
DW_LNS_const_add_pc if DW_LNS_const_add_pc < opcode_base => {
|
||||
// No operands
|
||||
handle_special_opcode!(0xff, false);
|
||||
}
|
||||
DW_LNS_fixed_advance_pc if DW_LNS_fixed_advance_pc < opcode_base => {
|
||||
let address_advance = u32::from(program_reader.read_u16());
|
||||
curr_entry.address += address_advance;
|
||||
curr_entry.op_index = 0;
|
||||
}
|
||||
DW_LNS_set_prologue_end if DW_LNS_set_prologue_end < opcode_base => {
|
||||
// No operands
|
||||
curr_entry.prologue_end = true;
|
||||
}
|
||||
DW_LNS_set_epilogue_begin if DW_LNS_set_epilogue_begin < opcode_base => {
|
||||
// No operands
|
||||
curr_entry.epilogue_begin = true;
|
||||
}
|
||||
DW_LNS_set_isa if DW_LNS_set_isa < opcode_base => {
|
||||
let isa = program_reader.read_uleb128() as u32;
|
||||
curr_entry.isa = isa;
|
||||
}
|
||||
// vendor specific extensions
|
||||
_ if (0..opcode_base).contains(&opcode) => {
|
||||
// Skip an appropriate amount of operands
|
||||
for _ in 0..standard_opcode_lengths[opcode as usize] {
|
||||
program_reader.read_uleb128();
|
||||
}
|
||||
}
|
||||
// Special opcode
|
||||
_ if (opcode_base..=0xff).contains(&opcode) => {
|
||||
handle_special_opcode!(opcode, true);
|
||||
curr_entry.basic_block = false;
|
||||
curr_entry.prologue_end = false;
|
||||
curr_entry.epilogue_begin = false;
|
||||
curr_entry.discriminator = 0;
|
||||
push_row_or_break!(last_entry, curr_entry, &pc);
|
||||
}
|
||||
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
(
|
||||
CallRecord {
|
||||
name: NameRef::Unknown,
|
||||
address: Some(pc),
|
||||
line: last_entry.line,
|
||||
file: file_ptrs[last_entry.file as usize - 1].0,
|
||||
dir: file_ptrs[last_entry.file as usize - 1].1,
|
||||
column: last_entry.column,
|
||||
},
|
||||
file_ptrs,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn symbolize(elf_byte: &[u8], pc_list: Vec<u32>) -> Vec<CallRecord> {
|
||||
let elf_ptr = elf_byte.as_ptr();
|
||||
let ehdr = unsafe { ptr::read::<Elf32_Ehdr>(elf_ptr.cast()) };
|
||||
let shdrs = unsafe {
|
||||
slice::from_raw_parts::<Elf32_Shdr>(
|
||||
elf_ptr.add(ehdr.e_shoff as usize).cast(),
|
||||
ehdr.e_shnum as usize,
|
||||
)
|
||||
};
|
||||
|
||||
// Read .strtab
|
||||
let strtab = {
|
||||
let strtab_shdr = shdrs[ehdr.e_shstrndx as usize];
|
||||
unsafe {
|
||||
slice::from_raw_parts::<u8>(
|
||||
elf_ptr.add(strtab_shdr.sh_offset as usize).cast(),
|
||||
strtab_shdr.sh_size as usize,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let get_str = |str_offset: usize| -> Result<&[u8], Error> {
|
||||
let strtab_trimmed = &strtab[str_offset..];
|
||||
let str_len = strtab_trimmed
|
||||
.iter()
|
||||
.position(|&x| x == 0)
|
||||
.expect("string in string table should be null-terminated");
|
||||
Ok(&strtab_trimmed[..str_len])
|
||||
};
|
||||
|
||||
// Retrieve these debug sections from the .elf file
|
||||
// .debug_info
|
||||
// .debug_line
|
||||
// .debug_abbrev
|
||||
// .debug_ranges (may)
|
||||
// .debug_str
|
||||
|
||||
let debug_info = {
|
||||
let debug_info_shdr = shdrs
|
||||
.iter()
|
||||
.find(|shdr| get_str(shdr.sh_name as usize).unwrap() == b".debug_info")
|
||||
.expect("missing .debug_info section");
|
||||
unsafe {
|
||||
slice::from_raw_parts::<u8>(
|
||||
elf_ptr.add(debug_info_shdr.sh_offset as usize),
|
||||
debug_info_shdr.sh_size as usize,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let debug_line = {
|
||||
let debug_line_shdr = shdrs
|
||||
.iter()
|
||||
.find(|shdr| get_str(shdr.sh_name as usize).unwrap() == b".debug_line")
|
||||
.expect("missing .debug_line section");
|
||||
unsafe {
|
||||
slice::from_raw_parts::<u8>(
|
||||
elf_ptr.add(debug_line_shdr.sh_offset as usize),
|
||||
debug_line_shdr.sh_size as usize,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let debug_abbrev = {
|
||||
let debug_abbrev_shdr = shdrs
|
||||
.iter()
|
||||
.find(|shdr| get_str(shdr.sh_name as usize).unwrap() == b".debug_abbrev")
|
||||
.expect("missing .debug_abbrev section");
|
||||
unsafe {
|
||||
slice::from_raw_parts::<u8>(
|
||||
elf_ptr.add(debug_abbrev_shdr.sh_offset as usize),
|
||||
debug_abbrev_shdr.sh_size as usize,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let debug_ranges = {
|
||||
// It is tempting to just cast as slice of u32, but
|
||||
// .debug_* sections do not have the concept of alignment
|
||||
//
|
||||
// TODO: Coerce nac3ld to align the debug sections.
|
||||
shdrs
|
||||
.iter()
|
||||
.find(|shdr| get_str(shdr.sh_name as usize).unwrap() == b".debug_ranges")
|
||||
.map_or_else(
|
||||
|| unsafe { slice::from_raw_parts::<u8>(elf_ptr.cast(), 0) },
|
||||
|debug_ranges_shdr| unsafe {
|
||||
slice::from_raw_parts::<u8>(
|
||||
elf_ptr.add(debug_ranges_shdr.sh_offset as usize).cast(),
|
||||
debug_ranges_shdr.sh_size as usize,
|
||||
)
|
||||
},
|
||||
)
|
||||
};
|
||||
|
||||
let debug_str = {
|
||||
let debug_str_shdr = shdrs
|
||||
.iter()
|
||||
.find(|shdr| get_str(shdr.sh_name as usize).unwrap() == b".debug_str")
|
||||
.expect("missing .debug_str section");
|
||||
unsafe {
|
||||
slice::from_raw_parts::<u8>(
|
||||
elf_ptr.add(debug_str_shdr.sh_offset as usize).cast(),
|
||||
debug_str_shdr.sh_size as usize,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let info_reader =
|
||||
DebugInfoReader { debug_info, debug_abbrev, debug_line, debug_ranges, debug_str };
|
||||
|
||||
let mut call_sites: Vec<CallRecord> = vec![];
|
||||
for pc in pc_list {
|
||||
call_sites.append(&mut info_reader.search(pc));
|
||||
}
|
||||
call_sites
|
||||
}
|
||||
@@ -12,7 +12,7 @@ no-escape-analysis = []
|
||||
[dependencies]
|
||||
itertools = "0.14"
|
||||
crossbeam = "0.8"
|
||||
indexmap = "2.9"
|
||||
indexmap = "2.12"
|
||||
parking_lot = "0.12"
|
||||
nac3core_derive = { path = "nac3core_derive", optional = true }
|
||||
nac3parser = { path = "../nac3parser" }
|
||||
@@ -20,15 +20,15 @@ strum = "0.27"
|
||||
strum_macros = "0.27"
|
||||
|
||||
[dependencies.inkwell]
|
||||
version = "0.6.0"
|
||||
version = "0.8"
|
||||
default-features = false
|
||||
features = ["llvm16-0-prefer-dynamic", "target-x86", "target-arm", "target-riscv", "no-libffi-linking", "typed-pointers"]
|
||||
|
||||
[dev-dependencies]
|
||||
test-case = "3.3"
|
||||
indoc = "2.0"
|
||||
insta = "1.43"
|
||||
insta = "1.44"
|
||||
function_name = "0.3"
|
||||
|
||||
[build-dependencies]
|
||||
regex = "1.11"
|
||||
regex = "1.12"
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#![deny(clippy::all)]
|
||||
#![warn(clippy::cargo, clippy::pedantic, clippy::nursery)]
|
||||
#![allow(clippy::cargo_common_metadata)]
|
||||
|
||||
use std::{
|
||||
env,
|
||||
fs::File,
|
||||
@@ -9,6 +13,10 @@ use std::{
|
||||
use regex::Regex;
|
||||
|
||||
fn main() {
|
||||
// For debugging
|
||||
// Doing `DEBUG_DUMP_IRRT=1 cargo build -p nac3core` dumps the LLVM IR generated
|
||||
const DEBUG_DUMP_IRRT: &str = "DEBUG_DUMP_IRRT";
|
||||
|
||||
let out_dir = env::var("OUT_DIR").unwrap();
|
||||
let out_dir = Path::new(&out_dir);
|
||||
let irrt_dir = Path::new("irrt");
|
||||
@@ -88,9 +96,6 @@ fn main() {
|
||||
.unwrap()
|
||||
.replace_all(&filtered_output, "");
|
||||
|
||||
// For debugging
|
||||
// Doing `DEBUG_DUMP_IRRT=1 cargo build -p nac3core` dumps the LLVM IR generated
|
||||
const DEBUG_DUMP_IRRT: &str = "DEBUG_DUMP_IRRT";
|
||||
println!("cargo:rerun-if-env-changed={DEBUG_DUMP_IRRT}");
|
||||
if env::var(DEBUG_DUMP_IRRT).is_ok() {
|
||||
let mut file = File::create(out_dir.join("irrt.ll")).unwrap();
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "irrt/debug.hpp"
|
||||
#include "irrt/exception.hpp"
|
||||
#include "irrt/int_types.hpp"
|
||||
|
||||
namespace {
|
||||
@@ -44,4 +45,4 @@ using namespace range;
|
||||
SliceIndex __nac3_range_slice_len(const SliceIndex start, const SliceIndex end, const SliceIndex step) {
|
||||
return len(start, end, step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,13 +6,8 @@ edition = "2024"
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[[test]]
|
||||
name = "structfields_tests"
|
||||
path = "tests/structfields_test.rs"
|
||||
|
||||
[dev-dependencies]
|
||||
nac3core = { path = ".." }
|
||||
trybuild = { version = "1.0", features = ["diff"] }
|
||||
|
||||
[dependencies]
|
||||
proc-macro2 = "1.0"
|
||||
|
||||
@@ -1,165 +1,10 @@
|
||||
#![deny(future_incompatible, let_underscore, nonstandard_style, clippy::all)]
|
||||
#![warn(clippy::pedantic)]
|
||||
#![warn(clippy::pedantic, clippy::nursery)]
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use proc_macro_error::{abort, proc_macro_error};
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
Data, DataStruct, Expr, ExprField, ExprMethodCall, ExprPath, GenericArgument, Ident, LitStr,
|
||||
Path, PathArguments, Type, TypePath, parse_macro_input, spanned::Spanned,
|
||||
};
|
||||
|
||||
/// Extracts all generic arguments of a [`Type`] into a [`Vec`].
|
||||
///
|
||||
/// Returns [`Some`] of a possibly-empty [`Vec`] if the path of `ty` matches with
|
||||
/// `expected_ty_name`, otherwise returns [`None`].
|
||||
fn extract_generic_args(expected_ty_name: &'static str, ty: &Type) -> Option<Vec<GenericArgument>> {
|
||||
let Type::Path(TypePath { qself: None, path, .. }) = ty else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let segments = &path.segments;
|
||||
if segments.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
| ||||