Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/unit-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,8 @@ jobs:
- name: Probe every declared LLVM-NVPTX target
run: |
# Stock runners do not carry the recorded CUDA 13.3 ptxas binary.
# This still checks the pinned llc identity and exact emitted PTX.
# Identity is llc version plus the rustc commit from upstream.lock;
# binary bytes are provenance only.
cargo run -p cuda-intrinsics-gen -- probe --all --skip-terminal --per-target

- name: Check append-only intrinsic ABI ledger
Expand Down
4 changes: 2 additions & 2 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ test-cuda:
# against 7 that are hand-written.
#
# Needs no CUDA toolkit: `--skip-terminal` is CI's own flag for runners without
# the recorded CUDA 13.3 ptxas, leaving the pinned llc identity and the exact
# emitted PTX as what gets checked, and llc comes from the pinned toolchain.
# the recorded CUDA 13.3 ptxas. Identity is llc version plus the rustc commit
# from upstream.lock; binary bytes are provenance only.
# The whole recipe is ~13s.
#
# `base_ref` is what the append-only ABI ledger is compared against. CI uses the
Expand Down
53 changes: 53 additions & 0 deletions crates/cuda-intrinsics-gen/src/extract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,59 @@ fn llvm_name(source_record: &str, record: &Value) -> Result<String> {
mod tests {
use super::*;
use serde_json::json;
use std::sync::atomic::{AtomicU64, Ordering};

struct TestRepo(PathBuf);

impl Drop for TestRepo {
fn drop(&mut self) {
let _ = fs::remove_dir_all(&self.0);
}
}

fn repo_with_lock(contents: &str) -> TestRepo {
static NEXT: AtomicU64 = AtomicU64::new(0);
let root = std::env::temp_dir().join(format!(
"cuda-intrinsics-upstream-lock-test-{}-{}",
std::process::id(),
NEXT.fetch_add(1, Ordering::Relaxed)
));
fs::create_dir_all(root.join("intrinsics")).unwrap();
fs::write(root.join("intrinsics/upstream.lock"), contents).unwrap();
TestRepo(root)
}

fn committed_lock() -> (&'static str, UpstreamLock) {
let text = include_str!("../../../intrinsics/upstream.lock");
let lock = toml::from_str(text).unwrap();
(text, lock)
}

#[test]
fn upstream_lock_requires_rust_toolchain_table() {
let (original, parsed) = committed_lock();
let commit = format!("commit_hash = \"{}\"", parsed.rust_toolchain.commit_hash);
let mutated = original
.replace("[rust_toolchain]", "")
.replace(&commit, "");
assert_ne!(mutated, original);
let repo = repo_with_lock(&mutated);
let error = read_upstream_lock(&repo.0).unwrap_err();
assert!(format!("{error:#}").contains("rust_toolchain"));
}

#[test]
fn upstream_lock_rejects_malformed_rust_toolchain_commit_hash() {
let (original, parsed) = committed_lock();
let commit = format!("commit_hash = \"{}\"", parsed.rust_toolchain.commit_hash);
let mutated = original.replace(&commit, "commit_hash = \"not-a-commit\"");
assert_ne!(mutated, original);
let repo = repo_with_lock(&mutated);
let error = read_upstream_lock(&repo.0).unwrap_err();
let message = format!("{error:#}");
assert!(message.contains("commit_hash"), "{message}");
assert!(message.contains("40 lowercase hexadecimal"), "{message}");
}

#[test]
fn joins_selection_by_def_reference_not_printable_text() {
Expand Down
45 changes: 45 additions & 0 deletions crates/cuda-intrinsics-gen/src/model/imported.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,63 @@
*/

use serde::{Deserialize, Serialize};
use std::fmt;
use std::str::FromStr;

#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct UpstreamLock {
pub schema: u32,
pub rust_toolchain: LockedRustToolchain,
pub llvm: LockedLlvm,
pub llvm_tblgen: LockedTool,
#[serde(default)]
pub comparison_tools: Vec<LockedTool>,
pub dumps: LockedDumps,
}

#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct LockedRustToolchain {
pub commit_hash: RustcCommit,
}

#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(try_from = "String")]
pub struct RustcCommit(String);

impl FromStr for RustcCommit {
type Err = String;

fn from_str(value: &str) -> Result<Self, Self::Err> {
if value.len() == 40
&& value
.bytes()
.all(|byte| matches!(byte, b'0'..=b'9' | b'a'..=b'f'))
{
Ok(Self(value.to_owned()))
} else {
Err(format!(
"expected 40 lowercase hexadecimal characters, found {value:?}"
))
}
}
}

impl TryFrom<String> for RustcCommit {
type Error = String;

fn try_from(value: String) -> Result<Self, Self::Error> {
value.parse()
}
}

impl fmt::Display for RustcCommit {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(formatter)
}
}

#[derive(Debug, Clone, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct LockedLlvm {
Expand Down
Loading
Loading