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
2 changes: 1 addition & 1 deletion .github/workflows/_ut-npu-a2a3.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
run:
name: ut-a2a3
runs-on: ${{ fromJSON(inputs.runs_on) }}
timeout-minutes: 30
timeout-minutes: 45
env:
SIMPLER_SCHEDULER_TIMEOUT_MS: "2000"
SIMPLER_OP_EXECUTE_TIMEOUT_US: "3000000"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ extern "C" __aicore__ __attribute__((always_inline)) void kernel_entry(__gm__ in
// its row loop. Flush the one cache line ([L=16,1] INT32 = 64 B) so its HBM
// value is visible regardless of dispatch/consumer timing. (recv_x/recv_w/
// recv_idx go out via TSTORE and need no dcci.)
dcci((__gm__ void *)recv_count_out, SINGLE_CACHE_LINE, CACHELINE_OUT);
dcci((__gm__ void *)recv_count_out, cache_line_t::SINGLE_CACHE_LINE, dcci_dst_t::CACHELINE_OUT);

// ------------------------------------------------------------------
// payload_push: push x / weight / idx payloads via TPUT.
Expand Down
2 changes: 1 addition & 1 deletion pto_isa.pin
Original file line number Diff line number Diff line change
@@ -1 +1 @@
83d01313d9bfc247c4b7c8bcf969d1019f0d106f
0cefc9a5a1c24c62655cc345d408559595a8af32
145 changes: 84 additions & 61 deletions simpler_setup/pto_isa.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
as-is — it already *is* the pinned ISA, so no checkout and no network.
3. Otherwise (missing, wrong revision, or dirty) obtain the pin fresh: clone
over HTTPS (``--no-checkout`` so the default branch is never materialized)
and force-check-out the pin.
and force-check-out the pin. GitHub is attempted three times; if it cannot
provide the complete pinned checkout, fall back to the GitCode mirror.
4. Verify HEAD exactly matches the pin before returning.

Two deliberate choices:
Expand Down Expand Up @@ -51,14 +52,15 @@
from .environment import PROJECT_ROOT

# A fresh clone is only needed when no usable local checkout exists; when it is,
# guard the single network hop against transient failures (e.g. GitHub
# SSL_ERROR_SYSCALL) with a few backed-off retries before giving up.
# guard the complete GitHub acquisition (clone plus landing on the pin) against
# transient failures before falling back to the GitCode mirror.
_CLONE_ATTEMPTS = 3
_CLONE_RETRY_BACKOFF_S = 2

logger = logging.getLogger(__name__)

_PTO_ISA_HTTPS = "https://github.com/hw-native-sys/pto-isa.git"
_PTO_ISA_GITHUB_HTTPS = "https://github.com/hw-native-sys/pto-isa.git"
_PTO_ISA_GITCODE_HTTPS = "https://gitcode.com/luohuan40/pto-isa.git"
_PTO_ISA_PIN_RE = re.compile(r"^[0-9a-fA-F]{40}$")
PTO_ISA_PIN_FILE = "pto_isa.pin"
PTO_ISA_BUILD_METADATA = "pto_isa_build.json"
Expand Down Expand Up @@ -293,25 +295,12 @@ def _run_git_resilient(
return result


def _discard_incomplete_clone(target: Path, verbose: bool) -> None:
"""Remove `target` if it exists but is not a usable clone."""
if not (target.exists() or target.is_symlink()) or _is_cloned(target):
return
if verbose:
logger.warning(f"Removing incomplete pto-isa clone at {target}")
if target.is_dir() and not target.is_symlink():
shutil.rmtree(target, ignore_errors=True)
else:
target.unlink(missing_ok=True)


def _remove_clone(target: Path, verbose: bool) -> None:
"""Unconditionally remove `target` so a fresh clone can replace it.
"""Unconditionally remove `target` so a fresh pinned clone can replace it.

Unlike ``_discard_incomplete_clone``, this removes even a *valid* clone —
used when the managed checkout is at the wrong (or a dirty) revision and we
re-clone at the pinned commit instead of checking out over local changes.
Handles a directory, a plain file, or a (possibly broken) symlink.
This removes even a *valid* clone when it is at the wrong (or a dirty)
revision, and also clears partial clones between source attempts. Handles a
directory, a plain file, or a (possibly broken) symlink.
"""
if not (target.exists() or target.is_symlink()):
return
Expand All @@ -334,15 +323,27 @@ def _land_on_commit(clone_path: Path, commit: str, verbose: bool) -> bool:
would be overwritten." Forcing discards that pseudo-dirt and lands exactly on
the pin. A full clone already carries every branch's history, but keep a
fetch fallback in case the pin is not reachable from the default fetch.
Fetch the exact pin first so a stale remote-ref advertisement cannot make a
newly-pushed commit invisible; fall back to the advertised refs for servers
that do not allow fetching a reachable object by SHA.
"""
try:
result = _run_git_resilient(
["checkout", "--detach", "--force", commit], cwd=clone_path, timeout=30, verbose=verbose
)
if result.returncode != 0:
if verbose:
logger.info(f"pto-isa commit {commit} missing locally, fetching origin...")
_run_git_resilient(["fetch", "origin"], cwd=clone_path, timeout=120, check=True, verbose=verbose)
logger.info(f"pto-isa commit {commit} missing locally, fetching the exact pin from origin...")
fetch_result = _run_git_resilient(
["fetch", "--no-tags", "origin", commit],
cwd=clone_path,
timeout=120,
verbose=verbose,
)
if fetch_result.returncode != 0:
if verbose:
logger.info("Exact PTO-ISA pin fetch failed; falling back to advertised origin refs...")
_run_git_resilient(["fetch", "origin"], cwd=clone_path, timeout=120, check=True, verbose=verbose)
_run_git_resilient(
["checkout", "--detach", "--force", commit], cwd=clone_path, timeout=30, check=True, verbose=verbose
)
Expand Down Expand Up @@ -382,12 +383,53 @@ def _is_pristine_at_commit(clone_path: Path, commit: str, verbose: bool) -> bool
return result.returncode == 0 and not result.stdout.strip()


def _clone_from_remote(target: Path, commit: str, remote: str, attempts: int, verbose: bool) -> bool:
"""Try complete clone-and-pin acquisitions from one remote."""
for attempt in range(1, attempts + 1):
if attempt > 1:
time.sleep(_CLONE_RETRY_BACKOFF_S * (attempt - 1))
_remove_clone(target, verbose)
logger.info(
f"Cloning pto-isa from {remote} to {target} at {commit} "
f"(attempt {attempt}/{attempts}, may take up to a minute)..."
)

failure = ""
try:
# --no-checkout: never materialize the default branch. Its working
# tree carries case-colliding docs/isa/TADDDEQRELU* paths; only lay
# down the pinned commit's tree in _land_on_commit.
result = _run_git(["clone", "--no-checkout", remote, str(target)], timeout=300)
if result.returncode != 0:
failure = result.stderr.strip() or f"git clone exited with status {result.returncode}"
elif _land_on_commit(target, commit, verbose=verbose):
if verbose:
logger.info(f"pto-isa cloned from {remote} at {commit}: {target}")
return True
else:
failure = f"clone succeeded but commit {commit} could not be checked out"
except subprocess.TimeoutExpired:
failure = "clone operation timed out"
except Exception as e: # noqa: BLE001
failure = str(e)

if verbose:
logger.warning(f"pto-isa acquisition from {remote} attempt {attempt}/{attempts} failed:\n{failure}")
# A successful clone that could not land on the pin is still unusable;
# remove it just like a partial clone before retrying or changing source.
_remove_clone(target, verbose)

return False


def _clone(target: Path, commit: str, verbose: bool) -> bool:
"""Fresh-clone PTO-ISA to `target` over HTTPS and land it on `commit`.

Any existing checkout at `target` is removed first, so this always yields a
clean tree at exactly `commit` — the sync can never be blocked by local
modifications in a preexisting (cached/preset) managed checkout.
modifications in a preexisting (cached/preset) managed checkout. GitHub is
tried three times for the complete clone-and-pin operation before GitCode is
used as a fallback source.
"""
if not _is_git_available():
if verbose:
Expand All @@ -401,46 +443,25 @@ def _clone(target: Path, commit: str, verbose: bool) -> bool:
logger.warning(f"Failed to create clone parent dir: {e}")
return False

_remove_clone(target, verbose)
logger.info(f"Cloning pto-isa to {target} over HTTPS at {commit} (may take up to a minute)...")

try:
# --no-checkout: never materialize the default branch. Its working tree
# is what carries the case-colliding docs/isa/TADDDEQRELU* paths; we only
# ever want the pinned commit's tree, laid down by _land_on_commit.
result = _run_git(["clone", "--no-checkout", _PTO_ISA_HTTPS, str(target)], timeout=300)
for attempt in range(2, _CLONE_ATTEMPTS + 1):
if result.returncode == 0:
break
if verbose:
logger.warning(f"pto-isa clone attempt {attempt - 1}/{_CLONE_ATTEMPTS} failed:\n{result.stderr}")
_remove_clone(target, verbose) # clear the partial clone before retrying
time.sleep(_CLONE_RETRY_BACKOFF_S * (attempt - 1))
result = _run_git(["clone", "--no-checkout", _PTO_ISA_HTTPS, str(target)], timeout=300)
if result.returncode != 0:
if verbose:
logger.warning(f"Failed to clone pto-isa after {_CLONE_ATTEMPTS} attempts:\n{result.stderr}")
_discard_incomplete_clone(target, verbose)
return False
if not _land_on_commit(target, commit, verbose=verbose):
# The clone succeeded but sits at the wrong commit (default HEAD),
# so it looks like a valid clone and `_discard_incomplete_clone`
# would leave it. Remove it outright so no wrong-revision checkout
# is stranded on disk for a later run to mistake for the pin.
_remove_clone(target, verbose)
return False
if verbose:
logger.info(f"pto-isa cloned at {commit}: {target}")
return True
except subprocess.TimeoutExpired:
if verbose:
logger.warning("Clone operation timed out")
_discard_incomplete_clone(target, verbose)
return False
if _clone_from_remote(
target,
commit,
_PTO_ISA_GITHUB_HTTPS,
attempts=_CLONE_ATTEMPTS,
verbose=verbose,
):
return True

logger.warning(
f"GitHub could not provide PTO-ISA commit {commit} after {_CLONE_ATTEMPTS} attempts; "
f"falling back to {_PTO_ISA_GITCODE_HTTPS}"
)
return _clone_from_remote(target, commit, _PTO_ISA_GITCODE_HTTPS, attempts=1, verbose=verbose)
except Exception as e: # noqa: BLE001
if verbose:
logger.warning(f"Failed to clone pto-isa: {e}")
_discard_incomplete_clone(target, verbose)
_remove_clone(target, verbose)
return False


Expand All @@ -460,7 +481,9 @@ def ensure_pto_isa_root(verbose: bool = False) -> str:
f"PTO-ISA not available.\n"
f" The managed checkout must live at {clone_path} and match {PROJECT_ROOT / PTO_ISA_PIN_FILE}.\n"
f" If auto-clone failed, manually run:\n"
f" git clone {_PTO_ISA_HTTPS} {clone_path}"
f" git clone {_PTO_ISA_GITHUB_HTTPS} {clone_path}\n"
f" Or use the fallback mirror:\n"
f" git clone {_PTO_ISA_GITCODE_HTTPS} {clone_path}"
)
return resolved

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ static_assert(
// carries the slot_state pointer in `addr` so the consumer can finalize the
// AsyncWaitEntry.slot_state binding for tasks whose conditions arrived
// before the FIN thread saw task_complete. New kinds may be added in future
// without growing the message — the `_pad[5]` slack is reserved for
// without growing the message — the `_pad[3]` slack is reserved for
// kind-specific payload extension.
#define MSG_KIND_CONDITION 0u
#define MSG_KIND_TASK_NORMAL_DONE 1u
Expand All @@ -52,15 +52,16 @@ struct AICoreCompletionMailboxMessage {
// other fields below as a side effect, so they stay plain.
std::atomic<uint64_t> seq;
PTO2TaskId task_token;
// CONDITION: completion observation addr (counter / SDMA event record).
// CONDITION: completion observation addr (counter / backend record).
// TASK_NORMAL_DONE: PTO2TaskSlotState pointer carried over to the consumer
// so it can finalize the AsyncWaitEntry.slot_state binding.
uint64_t addr;
uint64_t backend_cookie;
uint32_t expected_value;
uint32_t engine;
int32_t completion_type;
uint32_t kind;
uint32_t _pad[5];
uint32_t _pad[3];
};

static_assert(sizeof(AICoreCompletionMailboxMessage) == PTO2_ALIGN_SIZE, "AICoreCompletionMailboxMessage layout drift");
Expand All @@ -79,6 +80,7 @@ static_assert(
struct AICoreCompletionMsgView {
PTO2TaskId task_token{PTO2TaskId::invalid()};
uint64_t addr{0};
uint64_t backend_cookie{0};
uint32_t expected_value{0};
uint32_t engine{0};
int32_t completion_type{0};
Expand Down Expand Up @@ -115,6 +117,13 @@ struct AICoreCompletionMailbox {
// independent of the AsyncWaitList::busy lock.
bool try_push_condition(
PTO2TaskId task_token, uint64_t addr, uint32_t expected_value, uint32_t engine, int32_t completion_type
) {
return try_push_condition(task_token, addr, 0, expected_value, engine, completion_type);
}

bool try_push_condition(
PTO2TaskId task_token, uint64_t addr, uint64_t backend_cookie, uint32_t expected_value, uint32_t engine,
int32_t completion_type
) {
while (true) {
uint64_t h = head.load(std::memory_order_relaxed);
Expand All @@ -125,6 +134,7 @@ struct AICoreCompletionMailbox {
AICoreCompletionMailboxMessage *slot = &entries[h & AICORE_COMPLETION_MAILBOX_MASK];
slot->task_token.raw = task_token.raw;
slot->addr = addr;
slot->backend_cookie = backend_cookie;
slot->expected_value = expected_value;
slot->engine = engine;
slot->completion_type = completion_type;
Expand All @@ -149,6 +159,7 @@ struct AICoreCompletionMailbox {
AICoreCompletionMailboxMessage *slot = &entries[h & AICORE_COMPLETION_MAILBOX_MASK];
slot->task_token.raw = task_token.raw;
slot->addr = slot_state_addr;
slot->backend_cookie = 0;
slot->expected_value = 0;
slot->engine = 0;
slot->completion_type = 0;
Expand All @@ -173,6 +184,7 @@ struct AICoreCompletionMailbox {
if (slot->seq.load(std::memory_order_acquire) != t + 1) return false;
out.task_token.raw = slot->task_token.raw;
out.addr = slot->addr;
out.backend_cookie = slot->backend_cookie;
out.expected_value = slot->expected_value;
out.engine = slot->engine;
out.completion_type = slot->completion_type;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,14 @@ inline constexpr int32_t MAX_COMPLETIONS_PER_TASK = 64;
// pin the compiler against caching / reordering on either side.
struct DeferredCompletionEntry {
uint64_t addr;
uint64_t backend_cookie;
uint32_t expected_value;
uint32_t engine;
int32_t completion_type;
uint32_t _pad;
};

static_assert(sizeof(DeferredCompletionEntry) == 24, "DeferredCompletionEntry layout drift");
static_assert(sizeof(DeferredCompletionEntry) == 32, "DeferredCompletionEntry layout drift");

struct alignas(PTO2_ALIGN_SIZE) DeferredCompletionSlab {
volatile uint32_t count;
Expand Down
Loading
Loading