forked from M-Labs/nac3
Compare commits
7 Commits
Author | SHA1 | Date |
---|---|---|
lyken | c1369ea5bd | |
lyken | ef28138291 | |
lyken | 984843a46a | |
lyken | c5626e4947 | |
lyken | e4ba5e6411 | |
lyken | 31d0fdd818 | |
lyken | 3f0e7e28b8 |
|
@ -0,0 +1,3 @@
|
||||||
|
BasedOnStyle: Google
|
||||||
|
IndentWidth: 4
|
||||||
|
ReflowComments: false
|
|
@ -13,6 +13,7 @@
|
||||||
''
|
''
|
||||||
mkdir -p $out/bin
|
mkdir -p $out/bin
|
||||||
ln -s ${pkgs.llvmPackages_14.clang-unwrapped}/bin/clang $out/bin/clang-irrt
|
ln -s ${pkgs.llvmPackages_14.clang-unwrapped}/bin/clang $out/bin/clang-irrt
|
||||||
|
ln -s ${pkgs.llvmPackages_14.clang}/bin/clang $out/bin/clang-irrt-test
|
||||||
ln -s ${pkgs.llvmPackages_14.llvm.out}/bin/llvm-as $out/bin/llvm-as-irrt
|
ln -s ${pkgs.llvmPackages_14.llvm.out}/bin/llvm-as $out/bin/llvm-as-irrt
|
||||||
'';
|
'';
|
||||||
nac3artiq = pkgs.python3Packages.toPythonModule (
|
nac3artiq = pkgs.python3Packages.toPythonModule (
|
||||||
|
@ -23,6 +24,7 @@
|
||||||
cargoLock = {
|
cargoLock = {
|
||||||
lockFile = ./Cargo.lock;
|
lockFile = ./Cargo.lock;
|
||||||
};
|
};
|
||||||
|
cargoTestFlags = [ "--features" "test" ];
|
||||||
passthru.cargoLock = cargoLock;
|
passthru.cargoLock = cargoLock;
|
||||||
nativeBuildInputs = [ pkgs.python3 (pkgs.wrapClangMulti pkgs.llvmPackages_14.clang) llvm-tools-irrt pkgs.llvmPackages_14.llvm.out llvm-nac3 ];
|
nativeBuildInputs = [ pkgs.python3 (pkgs.wrapClangMulti pkgs.llvmPackages_14.clang) llvm-tools-irrt pkgs.llvmPackages_14.llvm.out llvm-nac3 ];
|
||||||
buildInputs = [ pkgs.python3 llvm-nac3 ];
|
buildInputs = [ pkgs.python3 llvm-nac3 ];
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
[features]
|
||||||
|
test = []
|
||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "nac3core"
|
name = "nac3core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|
|
@ -3,20 +3,34 @@ use std::{
|
||||||
env,
|
env,
|
||||||
fs::File,
|
fs::File,
|
||||||
io::Write,
|
io::Write,
|
||||||
path::Path,
|
path::{Path, PathBuf},
|
||||||
process::{Command, Stdio},
|
process::{Command, Stdio},
|
||||||
};
|
};
|
||||||
|
|
||||||
fn main() {
|
const CMD_IRRT_CLANG: &str = "clang-irrt";
|
||||||
const FILE: &str = "src/codegen/irrt/irrt.cpp";
|
const CMD_IRRT_CLANG_TEST: &str = "clang-irrt-test";
|
||||||
|
const CMD_IRRT_LLVM_AS: &str = "llvm-as-irrt";
|
||||||
|
|
||||||
|
fn get_out_dir() -> PathBuf {
|
||||||
|
PathBuf::from(env::var("OUT_DIR").unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_irrt_dir() -> &'static Path {
|
||||||
|
Path::new("irrt")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compile `irrt.cpp` for use in `src/codegen`
|
||||||
|
fn compile_irrt_cpp() {
|
||||||
|
let out_dir = get_out_dir();
|
||||||
|
let irrt_dir = get_irrt_dir();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* HACK: Sadly, clang doesn't let us emit generic LLVM bitcode.
|
* HACK: Sadly, clang doesn't let us emit generic LLVM bitcode.
|
||||||
* Compiling for WASM32 and filtering the output with regex is the closest we can get.
|
* Compiling for WASM32 and filtering the output with regex is the closest we can get.
|
||||||
*/
|
*/
|
||||||
|
let irrt_cpp_path = irrt_dir.join("irrt.cpp");
|
||||||
let flags: &[&str] = &[
|
let flags: &[&str] = &[
|
||||||
"--target=wasm32",
|
"--target=wasm32",
|
||||||
FILE,
|
|
||||||
"-x",
|
"-x",
|
||||||
"c++",
|
"c++",
|
||||||
"-fno-discard-value-names",
|
"-fno-discard-value-names",
|
||||||
|
@ -33,13 +47,16 @@ fn main() {
|
||||||
"-Wextra",
|
"-Wextra",
|
||||||
"-o",
|
"-o",
|
||||||
"-",
|
"-",
|
||||||
|
"-I",
|
||||||
|
irrt_dir.to_str().unwrap(),
|
||||||
|
irrt_cpp_path.to_str().unwrap(),
|
||||||
];
|
];
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed={FILE}");
|
// Tell Cargo to rerun if any file under `irrt_dir` (recursive) changes
|
||||||
let out_dir = env::var("OUT_DIR").unwrap();
|
println!("cargo:rerun-if-changed={}", irrt_dir.to_str().unwrap());
|
||||||
let out_path = Path::new(&out_dir);
|
|
||||||
|
|
||||||
let output = Command::new("clang-irrt")
|
// Compile IRRT and capture the LLVM IR output
|
||||||
|
let output = Command::new(CMD_IRRT_CLANG)
|
||||||
.args(flags)
|
.args(flags)
|
||||||
.output()
|
.output()
|
||||||
.map(|o| {
|
.map(|o| {
|
||||||
|
@ -52,7 +69,17 @@ fn main() {
|
||||||
let output = std::str::from_utf8(&output.stdout).unwrap().replace("\r\n", "\n");
|
let output = std::str::from_utf8(&output.stdout).unwrap().replace("\r\n", "\n");
|
||||||
let mut filtered_output = String::with_capacity(output.len());
|
let mut filtered_output = String::with_capacity(output.len());
|
||||||
|
|
||||||
let regex_filter = Regex::new(r"(?ms:^define.*?\}$)|(?m:^declare.*?$)").unwrap();
|
// Filter out irrelevant IR
|
||||||
|
//
|
||||||
|
// Regex:
|
||||||
|
// - `(?ms:^define.*?\}$)` captures LLVM `define` blocks
|
||||||
|
// - `(?m:^declare.*?$)` captures LLVM `declare` lines
|
||||||
|
// - `(?m:^%.+?=\s*type\s*\{.+?\}$)` captures LLVM `type` declarations
|
||||||
|
// - `(?m:^@.+?=.+$)` captures global constants
|
||||||
|
let regex_filter = Regex::new(
|
||||||
|
r"(?ms:^define.*?\}$)|(?m:^declare.*?$)|(?m:^%.+?=\s*type\s*\{.+?\}$)|(?m:^@.+?=.+$)",
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
for f in regex_filter.captures_iter(&output) {
|
for f in regex_filter.captures_iter(&output) {
|
||||||
assert_eq!(f.len(), 1);
|
assert_eq!(f.len(), 1);
|
||||||
filtered_output.push_str(&f[0]);
|
filtered_output.push_str(&f[0]);
|
||||||
|
@ -63,20 +90,71 @@ fn main() {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.replace_all(&filtered_output, "");
|
.replace_all(&filtered_output, "");
|
||||||
|
|
||||||
println!("cargo:rerun-if-env-changed=DEBUG_DUMP_IRRT");
|
// For debugging
|
||||||
if env::var("DEBUG_DUMP_IRRT").is_ok() {
|
// Doing `DEBUG_DUMP_IRRT=1 cargo build -p nac3core` dumps the LLVM IR generated
|
||||||
let mut file = File::create(out_path.join("irrt.ll")).unwrap();
|
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();
|
||||||
file.write_all(output.as_bytes()).unwrap();
|
file.write_all(output.as_bytes()).unwrap();
|
||||||
let mut file = File::create(out_path.join("irrt-filtered.ll")).unwrap();
|
|
||||||
|
let mut file = File::create(out_dir.join("irrt-filtered.ll")).unwrap();
|
||||||
file.write_all(filtered_output.as_bytes()).unwrap();
|
file.write_all(filtered_output.as_bytes()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut llvm_as = Command::new("llvm-as-irrt")
|
// Assemble the emitted and filtered IR to .bc
|
||||||
|
// That .bc will be integrated into nac3core's codegen
|
||||||
|
let mut llvm_as = Command::new(CMD_IRRT_LLVM_AS)
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.arg("-o")
|
.arg("-o")
|
||||||
.arg(out_path.join("irrt.bc"))
|
.arg(out_dir.join("irrt.bc"))
|
||||||
.spawn()
|
.spawn()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
llvm_as.stdin.as_mut().unwrap().write_all(filtered_output.as_bytes()).unwrap();
|
llvm_as.stdin.as_mut().unwrap().write_all(filtered_output.as_bytes()).unwrap();
|
||||||
assert!(llvm_as.wait().unwrap().success());
|
assert!(llvm_as.wait().unwrap().success());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Compile `irrt_test.cpp` for testing
|
||||||
|
fn compile_irrt_test_cpp() {
|
||||||
|
let out_dir = get_out_dir();
|
||||||
|
let irrt_dir = get_irrt_dir();
|
||||||
|
|
||||||
|
let exe_path = out_dir.join("irrt_test.out"); // Output path of the compiled test executable
|
||||||
|
let irrt_test_cpp_path = irrt_dir.join("irrt_test.cpp");
|
||||||
|
let flags: &[&str] = &[
|
||||||
|
irrt_test_cpp_path.to_str().unwrap(),
|
||||||
|
"-x",
|
||||||
|
"c++",
|
||||||
|
"-I",
|
||||||
|
irrt_dir.to_str().unwrap(),
|
||||||
|
"-g",
|
||||||
|
"-fno-discard-value-names",
|
||||||
|
"-O0",
|
||||||
|
"-Wall",
|
||||||
|
"-Wextra",
|
||||||
|
"-Werror=return-type",
|
||||||
|
"-lm", // for `tgamma()`, `lgamma()`
|
||||||
|
"-o",
|
||||||
|
exe_path.to_str().unwrap(),
|
||||||
|
];
|
||||||
|
|
||||||
|
Command::new(CMD_IRRT_CLANG_TEST)
|
||||||
|
.args(flags)
|
||||||
|
.output()
|
||||||
|
.map(|o| {
|
||||||
|
assert!(o.status.success(), "{}", std::str::from_utf8(&o.stderr).unwrap());
|
||||||
|
o
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
println!("cargo:rerun-if-changed={}", irrt_dir.to_str().unwrap());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
compile_irrt_cpp();
|
||||||
|
|
||||||
|
// https://github.com/rust-lang/cargo/issues/2549
|
||||||
|
// `cargo test -F test` to also build `irrt_test.cpp
|
||||||
|
if cfg!(feature = "test") {
|
||||||
|
compile_irrt_test_cpp();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
#define IRRT_DEFINE_TYPEDEF_INTS
|
||||||
|
#include <irrt_everything.hpp>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All IRRT implementations.
|
||||||
|
*
|
||||||
|
* We don't have pre-compiled objects, so we are writing all implementations in
|
||||||
|
* headers and concatenate them with `#include` into one massive source file that
|
||||||
|
* contains all the IRRT stuff.
|
||||||
|
*/
|
|
@ -1,27 +1,17 @@
|
||||||
using int8_t = _BitInt(8);
|
#pragma once
|
||||||
using uint8_t = unsigned _BitInt(8);
|
|
||||||
using int32_t = _BitInt(32);
|
#include <irrt/int_defs.hpp>
|
||||||
using uint32_t = unsigned _BitInt(32);
|
#include <irrt/utils.hpp>
|
||||||
using int64_t = _BitInt(64);
|
|
||||||
using uint64_t = unsigned _BitInt(64);
|
|
||||||
|
|
||||||
// NDArray indices are always `uint32_t`.
|
// NDArray indices are always `uint32_t`.
|
||||||
using NDIndex = uint32_t;
|
using NDIndex = uint32_t;
|
||||||
// The type of an index or a value describing the length of a range/slice is always `int32_t`.
|
// The type of an index or a value describing the length of a
|
||||||
|
// range/slice is always `int32_t`.
|
||||||
using SliceIndex = int32_t;
|
using SliceIndex = int32_t;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
template <typename T>
|
// adapted from GNU Scientific Library:
|
||||||
const T& max(const T& a, const T& b) {
|
// https://git.savannah.gnu.org/cgit/gsl.git/tree/sys/pow_int.c
|
||||||
return a > b ? a : b;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
const T& min(const T& a, const T& b) {
|
|
||||||
return a > b ? b : a;
|
|
||||||
}
|
|
||||||
|
|
||||||
// adapted from GNU Scientific Library: https://git.savannah.gnu.org/cgit/gsl.git/tree/sys/pow_int.c
|
|
||||||
// need to make sure `exp >= 0` before calling this function
|
// need to make sure `exp >= 0` before calling this function
|
||||||
template <typename T>
|
template <typename T>
|
||||||
T __nac3_int_exp_impl(T base, T exp) {
|
T __nac3_int_exp_impl(T base, T exp) {
|
||||||
|
@ -38,12 +28,8 @@ T __nac3_int_exp_impl(T base, T exp) {
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename SizeT>
|
template <typename SizeT>
|
||||||
SizeT __nac3_ndarray_calc_size_impl(
|
SizeT __nac3_ndarray_calc_size_impl(const SizeT* list_data, SizeT list_len,
|
||||||
const SizeT* list_data,
|
SizeT begin_idx, SizeT end_idx) {
|
||||||
SizeT list_len,
|
|
||||||
SizeT begin_idx,
|
|
||||||
SizeT end_idx
|
|
||||||
) {
|
|
||||||
__builtin_assume(end_idx <= list_len);
|
__builtin_assume(end_idx <= list_len);
|
||||||
|
|
||||||
SizeT num_elems = 1;
|
SizeT num_elems = 1;
|
||||||
|
@ -56,12 +42,8 @@ SizeT __nac3_ndarray_calc_size_impl(
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename SizeT>
|
template <typename SizeT>
|
||||||
void __nac3_ndarray_calc_nd_indices_impl(
|
void __nac3_ndarray_calc_nd_indices_impl(SizeT index, const SizeT* dims,
|
||||||
SizeT index,
|
SizeT num_dims, NDIndex* idxs) {
|
||||||
const SizeT* dims,
|
|
||||||
SizeT num_dims,
|
|
||||||
NDIndex* idxs
|
|
||||||
) {
|
|
||||||
SizeT stride = 1;
|
SizeT stride = 1;
|
||||||
for (SizeT dim = 0; dim < num_dims; dim++) {
|
for (SizeT dim = 0; dim < num_dims; dim++) {
|
||||||
SizeT i = num_dims - dim - 1;
|
SizeT i = num_dims - dim - 1;
|
||||||
|
@ -72,12 +54,9 @@ void __nac3_ndarray_calc_nd_indices_impl(
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename SizeT>
|
template <typename SizeT>
|
||||||
SizeT __nac3_ndarray_flatten_index_impl(
|
SizeT __nac3_ndarray_flatten_index_impl(const SizeT* dims, SizeT num_dims,
|
||||||
const SizeT* dims,
|
|
||||||
SizeT num_dims,
|
|
||||||
const NDIndex* indices,
|
const NDIndex* indices,
|
||||||
SizeT num_indices
|
SizeT num_indices) {
|
||||||
) {
|
|
||||||
SizeT idx = 0;
|
SizeT idx = 0;
|
||||||
SizeT stride = 1;
|
SizeT stride = 1;
|
||||||
for (SizeT i = 0; i < num_dims; ++i) {
|
for (SizeT i = 0; i < num_dims; ++i) {
|
||||||
|
@ -93,18 +72,17 @@ SizeT __nac3_ndarray_flatten_index_impl(
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename SizeT>
|
template <typename SizeT>
|
||||||
void __nac3_ndarray_calc_broadcast_impl(
|
void __nac3_ndarray_calc_broadcast_impl(const SizeT* lhs_dims, SizeT lhs_ndims,
|
||||||
const SizeT* lhs_dims,
|
const SizeT* rhs_dims, SizeT rhs_ndims,
|
||||||
SizeT lhs_ndims,
|
SizeT* out_dims) {
|
||||||
const SizeT* rhs_dims,
|
|
||||||
SizeT rhs_ndims,
|
|
||||||
SizeT* out_dims
|
|
||||||
) {
|
|
||||||
SizeT max_ndims = lhs_ndims > rhs_ndims ? lhs_ndims : rhs_ndims;
|
SizeT max_ndims = lhs_ndims > rhs_ndims ? lhs_ndims : rhs_ndims;
|
||||||
|
|
||||||
for (SizeT i = 0; i < max_ndims; ++i) {
|
for (SizeT i = 0; i < max_ndims; ++i) {
|
||||||
const SizeT* lhs_dim_sz = i < lhs_ndims ? &lhs_dims[lhs_ndims - i - 1] : nullptr;
|
const SizeT* lhs_dim_sz =
|
||||||
const SizeT* rhs_dim_sz = i < rhs_ndims ? &rhs_dims[rhs_ndims - i - 1] : nullptr;
|
i < lhs_ndims ? &lhs_dims[lhs_ndims - i - 1] : nullptr;
|
||||||
|
const SizeT* rhs_dim_sz =
|
||||||
|
i < rhs_ndims ? &rhs_dims[rhs_ndims - i - 1] : nullptr;
|
||||||
|
|
||||||
SizeT* out_dim = &out_dims[max_ndims - i - 1];
|
SizeT* out_dim = &out_dims[max_ndims - i - 1];
|
||||||
|
|
||||||
if (lhs_dim_sz == nullptr) {
|
if (lhs_dim_sz == nullptr) {
|
||||||
|
@ -124,12 +102,10 @@ void __nac3_ndarray_calc_broadcast_impl(
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename SizeT>
|
template <typename SizeT>
|
||||||
void __nac3_ndarray_calc_broadcast_idx_impl(
|
void __nac3_ndarray_calc_broadcast_idx_impl(const SizeT* src_dims,
|
||||||
const SizeT* src_dims,
|
|
||||||
SizeT src_ndims,
|
SizeT src_ndims,
|
||||||
const NDIndex* in_idx,
|
const NDIndex* in_idx,
|
||||||
NDIndex* out_idx
|
NDIndex* out_idx) {
|
||||||
) {
|
|
||||||
for (SizeT i = 0; i < src_ndims; ++i) {
|
for (SizeT i = 0; i < src_ndims; ++i) {
|
||||||
SizeT src_i = src_ndims - i - 1;
|
SizeT src_i = src_ndims - i - 1;
|
||||||
out_idx[src_i] = src_dims[src_i] == 1 ? 0 : in_idx[src_i];
|
out_idx[src_i] = src_dims[src_i] == 1 ? 0 : in_idx[src_i];
|
||||||
|
@ -139,14 +115,14 @@ void __nac3_ndarray_calc_broadcast_idx_impl(
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#define DEF_nac3_int_exp_(T) \
|
#define DEF_nac3_int_exp_(T) \
|
||||||
T __nac3_int_exp_##T(T base, T exp) {\
|
T __nac3_int_exp_##T(T base, T exp) { \
|
||||||
return __nac3_int_exp_impl(base, exp);\
|
return __nac3_int_exp_impl(base, exp); \
|
||||||
}
|
}
|
||||||
|
|
||||||
DEF_nac3_int_exp_(int32_t)
|
DEF_nac3_int_exp_(int32_t);
|
||||||
DEF_nac3_int_exp_(int64_t)
|
DEF_nac3_int_exp_(int64_t);
|
||||||
DEF_nac3_int_exp_(uint32_t)
|
DEF_nac3_int_exp_(uint32_t);
|
||||||
DEF_nac3_int_exp_(uint64_t)
|
DEF_nac3_int_exp_(uint64_t);
|
||||||
|
|
||||||
SliceIndex __nac3_slice_index_bound(SliceIndex i, const SliceIndex len) {
|
SliceIndex __nac3_slice_index_bound(SliceIndex i, const SliceIndex len) {
|
||||||
if (i < 0) {
|
if (i < 0) {
|
||||||
|
@ -160,11 +136,8 @@ SliceIndex __nac3_slice_index_bound(SliceIndex i, const SliceIndex len) {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
SliceIndex __nac3_range_slice_len(
|
SliceIndex __nac3_range_slice_len(const SliceIndex start, const SliceIndex end,
|
||||||
const SliceIndex start,
|
const SliceIndex step) {
|
||||||
const SliceIndex end,
|
|
||||||
const SliceIndex step
|
|
||||||
) {
|
|
||||||
SliceIndex diff = end - start;
|
SliceIndex diff = end - start;
|
||||||
if (diff > 0 && step > 0) {
|
if (diff > 0 && step > 0) {
|
||||||
return ((diff - 1) / step) + 1;
|
return ((diff - 1) / step) + 1;
|
||||||
|
@ -180,62 +153,52 @@ SliceIndex __nac3_range_slice_len(
|
||||||
// - All the index must *not* be out-of-bound or negative,
|
// - All the index must *not* be out-of-bound or negative,
|
||||||
// - The end index is *inclusive*,
|
// - The end index is *inclusive*,
|
||||||
// - The length of src and dest slice size should already
|
// - The length of src and dest slice size should already
|
||||||
// be checked: if dest.step == 1 then len(src) <= len(dest) else len(src) == len(dest)
|
// be checked: if dest.step == 1 then len(src) <= len(dest) else
|
||||||
|
// len(src) == len(dest)
|
||||||
SliceIndex __nac3_list_slice_assign_var_size(
|
SliceIndex __nac3_list_slice_assign_var_size(
|
||||||
SliceIndex dest_start,
|
SliceIndex dest_start, SliceIndex dest_end, SliceIndex dest_step,
|
||||||
SliceIndex dest_end,
|
uint8_t* dest_arr, SliceIndex dest_arr_len, SliceIndex src_start,
|
||||||
SliceIndex dest_step,
|
SliceIndex src_end, SliceIndex src_step, uint8_t* src_arr,
|
||||||
uint8_t* dest_arr,
|
SliceIndex src_arr_len, const SliceIndex size) {
|
||||||
SliceIndex dest_arr_len,
|
/* if dest_arr_len == 0, do nothing since we do not support
|
||||||
SliceIndex src_start,
|
* extending list
|
||||||
SliceIndex src_end,
|
*/
|
||||||
SliceIndex src_step,
|
|
||||||
uint8_t* src_arr,
|
|
||||||
SliceIndex src_arr_len,
|
|
||||||
const SliceIndex size
|
|
||||||
) {
|
|
||||||
/* if dest_arr_len == 0, do nothing since we do not support extending list */
|
|
||||||
if (dest_arr_len == 0) return dest_arr_len;
|
if (dest_arr_len == 0) return dest_arr_len;
|
||||||
/* if both step is 1, memmove directly, handle the dropping of the list, and shrink size */
|
/* if both step is 1, memmove directly, handle the dropping of
|
||||||
|
* the list, and shrink size */
|
||||||
if (src_step == dest_step && dest_step == 1) {
|
if (src_step == dest_step && dest_step == 1) {
|
||||||
const SliceIndex src_len = (src_end >= src_start) ? (src_end - src_start + 1) : 0;
|
const SliceIndex src_len =
|
||||||
const SliceIndex dest_len = (dest_end >= dest_start) ? (dest_end - dest_start + 1) : 0;
|
(src_end >= src_start) ? (src_end - src_start + 1) : 0;
|
||||||
|
const SliceIndex dest_len =
|
||||||
|
(dest_end >= dest_start) ? (dest_end - dest_start + 1) : 0;
|
||||||
if (src_len > 0) {
|
if (src_len > 0) {
|
||||||
__builtin_memmove(
|
__builtin_memmove(dest_arr + dest_start * size,
|
||||||
dest_arr + dest_start * size,
|
src_arr + src_start * size, src_len * size);
|
||||||
src_arr + src_start * size,
|
|
||||||
src_len * size
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (dest_len > 0) {
|
if (dest_len > 0) {
|
||||||
/* dropping */
|
/* dropping */
|
||||||
__builtin_memmove(
|
__builtin_memmove(dest_arr + (dest_start + src_len) * size,
|
||||||
dest_arr + (dest_start + src_len) * size,
|
|
||||||
dest_arr + (dest_end + 1) * size,
|
dest_arr + (dest_end + 1) * size,
|
||||||
(dest_arr_len - dest_end - 1) * size
|
(dest_arr_len - dest_end - 1) * size);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
/* shrink size */
|
/* shrink size */
|
||||||
return dest_arr_len - (dest_len - src_len);
|
return dest_arr_len - (dest_len - src_len);
|
||||||
}
|
}
|
||||||
/* if two range overlaps, need alloca */
|
/* if two range overlaps, need alloca */
|
||||||
uint8_t need_alloca =
|
uint8_t need_alloca =
|
||||||
(dest_arr == src_arr)
|
(dest_arr == src_arr) &&
|
||||||
&& !(
|
!(max(dest_start, dest_end) < min(src_start, src_end) ||
|
||||||
max(dest_start, dest_end) < min(src_start, src_end)
|
max(src_start, src_end) < min(dest_start, dest_end));
|
||||||
|| max(src_start, src_end) < min(dest_start, dest_end)
|
|
||||||
);
|
|
||||||
if (need_alloca) {
|
if (need_alloca) {
|
||||||
uint8_t* tmp = reinterpret_cast<uint8_t *>(__builtin_alloca(src_arr_len * size));
|
uint8_t* tmp =
|
||||||
|
reinterpret_cast<uint8_t*>(__builtin_alloca(src_arr_len * size));
|
||||||
__builtin_memcpy(tmp, src_arr, src_arr_len * size);
|
__builtin_memcpy(tmp, src_arr, src_arr_len * size);
|
||||||
src_arr = tmp;
|
src_arr = tmp;
|
||||||
}
|
}
|
||||||
SliceIndex src_ind = src_start;
|
SliceIndex src_ind = src_start;
|
||||||
SliceIndex dest_ind = dest_start;
|
SliceIndex dest_ind = dest_start;
|
||||||
for (;
|
for (; (src_step > 0) ? (src_ind <= src_end) : (src_ind >= src_end);
|
||||||
(src_step > 0) ? (src_ind <= src_end) : (src_ind >= src_end);
|
src_ind += src_step, dest_ind += dest_step) {
|
||||||
src_ind += src_step, dest_ind += dest_step
|
|
||||||
) {
|
|
||||||
/* for constant optimization */
|
/* for constant optimization */
|
||||||
if (size == 1) {
|
if (size == 1) {
|
||||||
__builtin_memcpy(dest_arr + dest_ind, src_arr + src_ind, 1);
|
__builtin_memcpy(dest_arr + dest_ind, src_arr + src_ind, 1);
|
||||||
|
@ -244,30 +207,26 @@ SliceIndex __nac3_list_slice_assign_var_size(
|
||||||
} else if (size == 8) {
|
} else if (size == 8) {
|
||||||
__builtin_memcpy(dest_arr + dest_ind * 8, src_arr + src_ind * 8, 8);
|
__builtin_memcpy(dest_arr + dest_ind * 8, src_arr + src_ind * 8, 8);
|
||||||
} else {
|
} else {
|
||||||
/* memcpy for var size, cannot overlap after previous alloca */
|
/* memcpy for var size, cannot overlap after previous
|
||||||
__builtin_memcpy(dest_arr + dest_ind * size, src_arr + src_ind * size, size);
|
* alloca */
|
||||||
|
__builtin_memcpy(dest_arr + dest_ind * size,
|
||||||
|
src_arr + src_ind * size, size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* only dest_step == 1 can we shrink the dest list. */
|
/* only dest_step == 1 can we shrink the dest list. */
|
||||||
/* size should be ensured prior to calling this function */
|
/* size should be ensured prior to calling this function */
|
||||||
if (dest_step == 1 && dest_end >= dest_start) {
|
if (dest_step == 1 && dest_end >= dest_start) {
|
||||||
__builtin_memmove(
|
__builtin_memmove(
|
||||||
dest_arr + dest_ind * size,
|
dest_arr + dest_ind * size, dest_arr + (dest_end + 1) * size,
|
||||||
dest_arr + (dest_end + 1) * size,
|
(dest_arr_len - dest_end - 1) * size + size + size + size);
|
||||||
(dest_arr_len - dest_end - 1) * size
|
|
||||||
);
|
|
||||||
return dest_arr_len - (dest_end - dest_ind) - 1;
|
return dest_arr_len - (dest_end - dest_ind) - 1;
|
||||||
}
|
}
|
||||||
return dest_arr_len;
|
return dest_arr_len;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t __nac3_isinf(double x) {
|
int32_t __nac3_isinf(double x) { return __builtin_isinf(x); }
|
||||||
return __builtin_isinf(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t __nac3_isnan(double x) {
|
int32_t __nac3_isnan(double x) { return __builtin_isnan(x); }
|
||||||
return __builtin_isnan(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
double tgamma(double arg);
|
double tgamma(double arg);
|
||||||
|
|
||||||
|
@ -320,95 +279,71 @@ double __nac3_j0(double x) {
|
||||||
return j0(x);
|
return j0(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t __nac3_ndarray_calc_size(
|
uint32_t __nac3_ndarray_calc_size(const uint32_t* list_data, uint32_t list_len,
|
||||||
const uint32_t* list_data,
|
uint32_t begin_idx, uint32_t end_idx) {
|
||||||
uint32_t list_len,
|
return __nac3_ndarray_calc_size_impl(list_data, list_len, begin_idx,
|
||||||
uint32_t begin_idx,
|
end_idx);
|
||||||
uint32_t end_idx
|
|
||||||
) {
|
|
||||||
return __nac3_ndarray_calc_size_impl(list_data, list_len, begin_idx, end_idx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t __nac3_ndarray_calc_size64(
|
uint64_t __nac3_ndarray_calc_size64(const uint64_t* list_data,
|
||||||
const uint64_t* list_data,
|
uint64_t list_len, uint64_t begin_idx,
|
||||||
uint64_t list_len,
|
uint64_t end_idx) {
|
||||||
uint64_t begin_idx,
|
return __nac3_ndarray_calc_size_impl(list_data, list_len, begin_idx,
|
||||||
uint64_t end_idx
|
end_idx);
|
||||||
) {
|
|
||||||
return __nac3_ndarray_calc_size_impl(list_data, list_len, begin_idx, end_idx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void __nac3_ndarray_calc_nd_indices(
|
void __nac3_ndarray_calc_nd_indices(uint32_t index, const uint32_t* dims,
|
||||||
uint32_t index,
|
uint32_t num_dims, NDIndex* idxs) {
|
||||||
const uint32_t* dims,
|
|
||||||
uint32_t num_dims,
|
|
||||||
NDIndex* idxs
|
|
||||||
) {
|
|
||||||
__nac3_ndarray_calc_nd_indices_impl(index, dims, num_dims, idxs);
|
__nac3_ndarray_calc_nd_indices_impl(index, dims, num_dims, idxs);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __nac3_ndarray_calc_nd_indices64(
|
void __nac3_ndarray_calc_nd_indices64(uint64_t index, const uint64_t* dims,
|
||||||
uint64_t index,
|
uint64_t num_dims, NDIndex* idxs) {
|
||||||
const uint64_t* dims,
|
|
||||||
uint64_t num_dims,
|
|
||||||
NDIndex* idxs
|
|
||||||
) {
|
|
||||||
__nac3_ndarray_calc_nd_indices_impl(index, dims, num_dims, idxs);
|
__nac3_ndarray_calc_nd_indices_impl(index, dims, num_dims, idxs);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t __nac3_ndarray_flatten_index(
|
uint32_t __nac3_ndarray_flatten_index(const uint32_t* dims, uint32_t num_dims,
|
||||||
const uint32_t* dims,
|
|
||||||
uint32_t num_dims,
|
|
||||||
const NDIndex* indices,
|
const NDIndex* indices,
|
||||||
uint32_t num_indices
|
uint32_t num_indices) {
|
||||||
) {
|
return __nac3_ndarray_flatten_index_impl(dims, num_dims, indices,
|
||||||
return __nac3_ndarray_flatten_index_impl(dims, num_dims, indices, num_indices);
|
num_indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t __nac3_ndarray_flatten_index64(
|
uint64_t __nac3_ndarray_flatten_index64(const uint64_t* dims, uint64_t num_dims,
|
||||||
const uint64_t* dims,
|
|
||||||
uint64_t num_dims,
|
|
||||||
const NDIndex* indices,
|
const NDIndex* indices,
|
||||||
uint64_t num_indices
|
uint64_t num_indices) {
|
||||||
) {
|
return __nac3_ndarray_flatten_index_impl(dims, num_dims, indices,
|
||||||
return __nac3_ndarray_flatten_index_impl(dims, num_dims, indices, num_indices);
|
num_indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __nac3_ndarray_calc_broadcast(
|
void __nac3_ndarray_calc_broadcast(const uint32_t* lhs_dims, uint32_t lhs_ndims,
|
||||||
const uint32_t* lhs_dims,
|
const uint32_t* rhs_dims, uint32_t rhs_ndims,
|
||||||
uint32_t lhs_ndims,
|
uint32_t* out_dims) {
|
||||||
const uint32_t* rhs_dims,
|
return __nac3_ndarray_calc_broadcast_impl(lhs_dims, lhs_ndims, rhs_dims,
|
||||||
uint32_t rhs_ndims,
|
rhs_ndims, out_dims);
|
||||||
uint32_t* out_dims
|
|
||||||
) {
|
|
||||||
return __nac3_ndarray_calc_broadcast_impl(lhs_dims, lhs_ndims, rhs_dims, rhs_ndims, out_dims);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void __nac3_ndarray_calc_broadcast64(
|
void __nac3_ndarray_calc_broadcast64(const uint64_t* lhs_dims,
|
||||||
const uint64_t* lhs_dims,
|
|
||||||
uint64_t lhs_ndims,
|
uint64_t lhs_ndims,
|
||||||
const uint64_t* rhs_dims,
|
const uint64_t* rhs_dims,
|
||||||
uint64_t rhs_ndims,
|
uint64_t rhs_ndims, uint64_t* out_dims) {
|
||||||
uint64_t* out_dims
|
return __nac3_ndarray_calc_broadcast_impl(lhs_dims, lhs_ndims, rhs_dims,
|
||||||
) {
|
rhs_ndims, out_dims);
|
||||||
return __nac3_ndarray_calc_broadcast_impl(lhs_dims, lhs_ndims, rhs_dims, rhs_ndims, out_dims);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void __nac3_ndarray_calc_broadcast_idx(
|
void __nac3_ndarray_calc_broadcast_idx(const uint32_t* src_dims,
|
||||||
const uint32_t* src_dims,
|
|
||||||
uint32_t src_ndims,
|
uint32_t src_ndims,
|
||||||
const NDIndex* in_idx,
|
const NDIndex* in_idx,
|
||||||
NDIndex* out_idx
|
NDIndex* out_idx) {
|
||||||
) {
|
__nac3_ndarray_calc_broadcast_idx_impl(src_dims, src_ndims, in_idx,
|
||||||
__nac3_ndarray_calc_broadcast_idx_impl(src_dims, src_ndims, in_idx, out_idx);
|
out_idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void __nac3_ndarray_calc_broadcast_idx64(
|
void __nac3_ndarray_calc_broadcast_idx64(const uint64_t* src_dims,
|
||||||
const uint64_t* src_dims,
|
|
||||||
uint64_t src_ndims,
|
uint64_t src_ndims,
|
||||||
const NDIndex* in_idx,
|
const NDIndex* in_idx,
|
||||||
NDIndex* out_idx
|
NDIndex* out_idx) {
|
||||||
) {
|
__nac3_ndarray_calc_broadcast_idx_impl(src_dims, src_ndims, in_idx,
|
||||||
__nac3_ndarray_calc_broadcast_idx_impl(src_dims, src_ndims, in_idx, out_idx);
|
out_idx);
|
||||||
}
|
}
|
||||||
} // extern "C"
|
} // extern "C"
|
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// This is made toggleable since `irrt_test.cpp` itself would include
|
||||||
|
// headers that define these typedefs
|
||||||
|
#ifdef IRRT_DEFINE_TYPEDEF_INTS
|
||||||
|
using int8_t = _BitInt(8);
|
||||||
|
using uint8_t = unsigned _BitInt(8);
|
||||||
|
using int32_t = _BitInt(32);
|
||||||
|
using uint32_t = unsigned _BitInt(32);
|
||||||
|
using int64_t = _BitInt(64);
|
||||||
|
using uint64_t = unsigned _BitInt(64);
|
||||||
|
#endif
|
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
template <typename T>
|
||||||
|
const T& max(const T& a, const T& b) {
|
||||||
|
return a > b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
const T& min(const T& a, const T& b) {
|
||||||
|
return a > b ? b : a;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool arrays_match(int len, T* as, T* bs) {
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (as[i] != bs[i]) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace
|
|
@ -0,0 +1,5 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <irrt/core.hpp>
|
||||||
|
#include <irrt/int_defs.hpp>
|
||||||
|
#include <irrt/utils.hpp>
|
|
@ -0,0 +1,12 @@
|
||||||
|
// This file will be compiled like a real C++ program,
|
||||||
|
// and we do have the luxury to use the standard libraries.
|
||||||
|
// That is if the nix flakes do not have issues... especially on msys2...
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <test/test_core.hpp>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
test::core::run();
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <irrt_everything.hpp>
|
||||||
|
#include <test/util.hpp>
|
||||||
|
|
||||||
|
/*
|
||||||
|
Include this header for every test_*.cpp
|
||||||
|
*/
|
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <test/includes.hpp>
|
||||||
|
|
||||||
|
namespace test {
|
||||||
|
namespace core {
|
||||||
|
void test_int_exp() {
|
||||||
|
BEGIN_TEST();
|
||||||
|
|
||||||
|
assert_values_match(125, __nac3_int_exp_impl<int32_t>(5, 3));
|
||||||
|
assert_values_match(3125, __nac3_int_exp_impl<int32_t>(5, 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
void run() { test_int_exp(); }
|
||||||
|
} // namespace core
|
||||||
|
} // namespace test
|
|
@ -0,0 +1,116 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void print_value(const T& value);
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void print_value(const int8_t& value) {
|
||||||
|
printf("%d", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void print_value(const int32_t& value) {
|
||||||
|
printf("%d", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void print_value(const uint8_t& value) {
|
||||||
|
printf("%u", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void print_value(const uint32_t& value) {
|
||||||
|
printf("%u", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void print_value(const float& value) {
|
||||||
|
printf("%f", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void print_value(const double& value) {
|
||||||
|
printf("%f", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __begin_test(const char* function_name, const char* file, int line) {
|
||||||
|
printf("######### Running %s @ %s:%d\n", function_name, file, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define BEGIN_TEST() __begin_test(__FUNCTION__, __FILE__, __LINE__)
|
||||||
|
|
||||||
|
void test_fail() {
|
||||||
|
printf("[!] Test failed. Exiting with status code 1.\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void debug_print_array(int len, const T* as) {
|
||||||
|
printf("[");
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (i != 0) printf(", ");
|
||||||
|
print_value(as[i]);
|
||||||
|
}
|
||||||
|
printf("]");
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_assertion_passed(const char* file, int line) {
|
||||||
|
printf("[*] Assertion passed on %s:%d\n", file, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void print_assertion_failed(const char* file, int line) {
|
||||||
|
printf("[!] Assertion failed on %s:%d\n", file, line);
|
||||||
|
}
|
||||||
|
|
||||||
|
void __assert_true(const char* file, int line, bool cond) {
|
||||||
|
if (cond) {
|
||||||
|
print_assertion_passed(file, line);
|
||||||
|
} else {
|
||||||
|
print_assertion_failed(file, line);
|
||||||
|
test_fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define assert_true(cond) __assert_true(__FILE__, __LINE__, cond)
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void __assert_arrays_match(const char* file, int line, int len,
|
||||||
|
const T* expected, const T* got) {
|
||||||
|
if (arrays_match(len, expected, got)) {
|
||||||
|
print_assertion_passed(file, line);
|
||||||
|
} else {
|
||||||
|
print_assertion_failed(file, line);
|
||||||
|
printf("Expect = ");
|
||||||
|
debug_print_array(len, expected);
|
||||||
|
printf("\n");
|
||||||
|
printf(" Got = ");
|
||||||
|
debug_print_array(len, got);
|
||||||
|
printf("\n");
|
||||||
|
test_fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define assert_arrays_match(len, expected, got) \
|
||||||
|
__assert_arrays_match(__FILE__, __LINE__, len, expected, got)
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void __assert_values_match(const char* file, int line, T expected, T got) {
|
||||||
|
if (expected == got) {
|
||||||
|
print_assertion_passed(file, line);
|
||||||
|
} else {
|
||||||
|
print_assertion_failed(file, line);
|
||||||
|
printf("Expect = ");
|
||||||
|
print_value(expected);
|
||||||
|
printf("\n");
|
||||||
|
printf(" Got = ");
|
||||||
|
print_value(got);
|
||||||
|
printf("\n");
|
||||||
|
test_fail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define assert_values_match(expected, got) \
|
||||||
|
__assert_values_match(__FILE__, __LINE__, expected, got)
|
|
@ -1,5 +1,7 @@
|
||||||
use crate::typecheck::typedef::Type;
|
use crate::typecheck::typedef::Type;
|
||||||
|
|
||||||
|
mod test;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
classes::{
|
classes::{
|
||||||
ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ListValue, NDArrayValue,
|
ArrayLikeIndexer, ArrayLikeValue, ArraySliceValue, ListValue, NDArrayValue,
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::{path::Path, process::Command};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn run_irrt_test() {
|
||||||
|
assert!(
|
||||||
|
cfg!(feature = "test"),
|
||||||
|
"Please do `cargo test -F test` to compile `irrt_test.out` and run test"
|
||||||
|
);
|
||||||
|
|
||||||
|
let irrt_test_out_path = Path::new(concat!(env!("OUT_DIR"), "/irrt_test.out"));
|
||||||
|
let output = Command::new(irrt_test_out_path.to_str().unwrap()).output().unwrap();
|
||||||
|
|
||||||
|
if !output.status.success() {
|
||||||
|
eprintln!("irrt_test failed with status {}:", output.status);
|
||||||
|
eprintln!("====== stdout ======");
|
||||||
|
eprintln!("{}", String::from_utf8(output.stdout).unwrap());
|
||||||
|
eprintln!("====== stderr ======");
|
||||||
|
eprintln!("{}", String::from_utf8(output.stderr).unwrap());
|
||||||
|
eprintln!("====================");
|
||||||
|
|
||||||
|
panic!("irrt_test failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue