Skip to content

Commit

Permalink
Add a rust+zig example for hexagon
Browse files Browse the repository at this point in the history
Signed-off-by: Brian Cain <[email protected]>
  • Loading branch information
androm3da committed Oct 15, 2024
1 parent 5c360da commit f9b038b
Show file tree
Hide file tree
Showing 10 changed files with 225 additions and 0 deletions.
9 changes: 9 additions & 0 deletions examples/contrived/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

[build]
target = [ "hexagon-unknown-linux-musl"]

[target.hexagon-unknown-linux-musl]

linker = "hexagon-unknown-linux-musl-clang"
runner = "qemu-hexagon"

4 changes: 4 additions & 0 deletions examples/contrived/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/target
*.swp
*.a
*.a.o
Empty file added examples/contrived/.gitmodules
Empty file.
13 changes: 13 additions & 0 deletions examples/contrived/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[package]
name = "contrived"
version = "0.1.0"
edition = "2021"
build = "build.rs"

[build-dependencies]
cc = "1.1.21"
bindgen = "0.70.1"
build-target = "0.4.0"

[dependencies]

62 changes: 62 additions & 0 deletions examples/contrived/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Contrived example

This is a contrived example to demonstrate some toolchains targeting
hexagon linux. Here we illustrate how to use Rust, Zig, and C++ to
write programs portable to hexagon similarly to how one would
target other architectures.

We can use the upstream Rust toolchain with lld to make programs for hexagon
linux. There might be yet-undiscovered bugs, but there's no known limitations
for this platform.

As of September 2024, Zig does not yet have support for hexagon linux, only
`hexagon-freestanding` (baremetal hexagon). This is primarily because the
C library support is not yet upstreamed to `musl`. As a consequence we
cannot yet `@import("std")`. Also, with any zig code, compiler-emitted
calls to the compiler builtin libraries could result in some "unresolved
symbol" link errors. These could probably be solved with some creative
workarounds/linker command-line overrides. See the [issue at the
zig project for details](https://github.com/ziglang/zig/issues/21579).

## Dependencies

[Download zig](https://ziglang.org/download/), unpack the tarball and put
the `zig` executable in your `PATH`.

[Install rust using the instructions from the Rust Project website](https://www.rust-lang.org/learn/get-started)
and then install the nightly:

rustup toolchain install nightly
rustup override set nightly
rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu

Download and install the hexagon open source toolchain from https://github.com/quic/toolchain_for_hexagon/releases - version 19.1.0 or later.

## Setup

Edit the `.cargo/config` to point to your toolchain's C library:

...
runner = "qemu-hexagon -L /inst/clang+llvm-19.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
...


## Build and run

Build/run the demo with QEMU hexagon:

export TARGET_CC=hexagon-unknown-linux-musl-clang
export PATH=/inst/clang+llvm-19.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/:$PATH
export QEMU_LD_PREFIX=/inst/clang+llvm-19.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-musl/target/hexagon-unknown-linux-musl/usr/

cargo +nightly build --target=hexagon-unknown-linux-musl -Zbuild-std -Zbuild-std-features=llvm-libunwind
cargo +nightly run --target=hexagon-unknown-linux-musl -Zbuild-std -Zbuild-std-features=llvm-libunwind

Try experimenting with some different input arguments to see how this impacts
the cycles consumed.

As a simpler reference, you can also run this natively on an `x86_64` host
(you might need to manually clean up some `*.a` / `*.a.o` files):

CXX=clang++ CC=clang cargo run --target=x86_64-unknown-linux-gnu

36 changes: 36 additions & 0 deletions examples/contrived/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright(c) 2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
// SPDX-License-Identifier: BSD-3-Clause

use std::process::Command;

fn main() {
println!("cargo::rerun-if-changed=src/hex_sys.cpp");

cc::Build::new()
.file("src/some_cxx.cpp")
.compile("libsome_cxx.a");
println!("cargo:rustc-link-lib=some_cxx");

let arch = build_target::target_arch().unwrap();
let arch = arch.as_str();

let zig_tgt = match arch {
"hexagon" => "hexagon-freestanding",
"x86_64" => "x86_64-linux-gnu",
_ => panic!("unsupported target"),
};

println!("cargo::rerun-if-changed=src/some_sys.zig");
let rc = Command::new("zig")
.arg("build-lib")
.arg("-fPIC")
.arg("-target")
.arg(zig_tgt)
.arg("src/some_sys.zig")
.status()
.expect("failed to spawn process");
assert!(rc.success());

println!("cargo:rustc-link-search=.");
println!("cargo:rustc-link-lib=some_sys");
}
Binary file added examples/contrived/src/lut.dat
Binary file not shown.
39 changes: 39 additions & 0 deletions examples/contrived/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// Copyright(c) 2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
// SPDX-License-Identifier: BSD-3-Clause

use std::env;

extern "C" {
fn read_activity() -> u64;
fn get_cycles() -> u64;
fn get_time() -> u64;
}

fn fibonacci(n: u32) -> u32 {
match n {
0 => 1,
1 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}

fn main() {
let arg: String = env::args().nth(1).unwrap_or("16".to_string());
let in_val = arg.parse::<u32>().unwrap_or(16);

let t0 = unsafe { read_activity() };
let v = fibonacci(in_val);
let dur_cycl = unsafe { read_activity() } - t0;

println!("fibonacci({}): {}", in_val, v);
println!("cycles elapsed: {}", dur_cycl);

let t0 = unsafe { get_cycles() };
let v_ = fibonacci(in_val);
let t_end = unsafe { get_cycles() } - t0;

println!("fibonacci({}): {}", in_val, v_);
println!("cycles elapsed: {}", t_end);

println!("cur time: {}", unsafe { get_time() });
}
31 changes: 31 additions & 0 deletions examples/contrived/src/some_cxx.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright(c) 2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
// SPDX-License-Identifier: BSD-3-Clause

#include <stdint.h>

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wc23-extensions"

static const int32_t raw_data[] = {
#embed "lut.dat"
};
#pragma clang diagnostic pop


#define ARRAY_ELTS(x) (sizeof(x) / sizeof(x[0]))

int32_t get_val(unsigned int index) {
return raw_data[index % ARRAY_ELTS(raw_data)];
}

extern "C" uint64_t get_cycles() {
return __builtin_readcyclecounter();
}

#if !defined(__clang_major__) || __clang_major__ < 19
#error "requires clang 19.1.x or later"
#endif

extern "C" uint64_t get_time() {
return __builtin_readsteadycounter();
}
31 changes: 31 additions & 0 deletions examples/contrived/src/some_sys.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright(c) 2024 Qualcomm Innovation Center, Inc. All Rights Reserved.
// SPDX-License-Identifier: BSD-3-Clause

const builtin = @import("builtin");

// Ehh...it's not really the same thing between these
// architectures, but ¯\_(ツ)_/¯ we are just illustrating
// concepts here...

export fn read_activity() u64 {
if (comptime builtin.cpu.arch == .hexagon) {
return asm volatile ("%[value] = upcycle"
: [value] "=&r" (-> u64),
);
} else if (comptime builtin.cpu.arch == .x86_64) {
var high: u64 = 0;
var low: u64 = 0;

asm volatile (
\\rdtsc
: [low] "={eax}" (low),
[high] "={edx}" (high),
);
return (@as(u64, high) << 32) | @as(u64, low);
} else {
const std = @import("std");
const msg = std.fmt.comptimePrint("unsupported architecture: '{s}'", .{@tagName(builtin.cpu.arch)});

@compileError(msg);
}
}

0 comments on commit f9b038b

Please sign in to comment.