diff --git a/iron/applications/llama_3.2_1b/test.py b/iron/applications/llama_3.2_1b/test.py index d02c7925..add64d39 100644 --- a/iron/applications/llama_3.2_1b/test.py +++ b/iron/applications/llama_3.2_1b/test.py @@ -5,6 +5,7 @@ import subprocess import pytest import os +import sys from pathlib import Path test_dir = Path(__file__).parent @@ -27,6 +28,13 @@ def generate_test_params(): params, names = generate_test_params() +@pytest.mark.skipif( + not ( + (weights_dir / "llama3.2-1b" / "model.safetensors").exists() + and (weights_dir / "llama3.2-1b" / "tokenizer.model").exists() + ), + reason="llama3.2-1b weights not found", +) @pytest.mark.supported_devices("npu2") @pytest.mark.metrics( TTFT=r"\[Prefill\]\s*Time to first token:\s*(?P[\d\.e\+-]+) s", @@ -34,7 +42,7 @@ def generate_test_params(): ) @pytest.mark.parametrize("prompt_len,num_tokens", params, ids=names) def test_llama_3_2_1b(prompt_len, num_tokens): - command = f"python3 {test_dir}/llama_npu.py {weights_dir}/llama3.2-1b/model.safetensors {weights_dir}/llama3.2-1b/tokenizer.model --num-tokens {num_tokens} --prompt-len {prompt_len}" + command = f"{sys.executable} {test_dir}/llama_npu.py {weights_dir}/llama3.2-1b/model.safetensors {weights_dir}/llama3.2-1b/tokenizer.model --num-tokens {num_tokens} --prompt-len {prompt_len}" result = subprocess.run( command, diff --git a/iron/common/compilation/base.py b/iron/common/compilation/base.py index 2aefba18..da15df03 100644 --- a/iron/common/compilation/base.py +++ b/iron/common/compilation/base.py @@ -795,20 +795,37 @@ def _prefix_symbols(self, artifact, prefix): nm_path = self._find_working_tool("llvm-nm") symbol_map_file = artifact.filename + ".symbol_map" - # Extract defined symbols and build the redefine-syms map. Run nm to a - # file, THEN awk (joined by `&&`) rather than `nm | awk`: a pipe reports - # only awk's exit status, so a failing nm silently produces an EMPTY map - # and the prefix rename is skipped, surfacing much later as - # `undefined symbol: {prefix}` at the per-core link. With `&&` a - # failed nm aborts here loudly instead. - nm_cmd = [ - "sh", - "-c", - f"{nm_path} --defined-only --extern-only {artifact.filename} " - f"> {symbol_map_file}.syms && " - f"awk '{{print $3 \" {prefix}\" $3}}' {symbol_map_file}.syms " - f"> {symbol_map_file}", - ] + if os.name == "nt": + # Pure python code execution block wrapped cleanly for Windows + python_script = f""" +import subprocess +nm_cmd = [{repr(nm_path)}, '--defined-only', '--extern-only', {repr(artifact.filename)}] +res = subprocess.run(nm_cmd, capture_output=True, text=True, check=True) +lines = [] +for line in res.stdout.splitlines(): + parts = line.strip().split() + if len(parts) >= 3: + sym = parts[-1] + lines.append(f"{{sym}} {prefix}{{sym}}\\n") +with open({repr(symbol_map_file)}, 'w') as f: + f.writelines(lines) +""" + nm_cmd = [sys.executable, "-c", python_script.strip()] + else: + # Extract defined symbols and build the redefine-syms map. Run nm to a + # file, THEN awk (joined by `&&`) rather than `nm | awk`: a pipe reports + # only awk's exit status, so a failing nm silently produces an EMPTY map + # and the prefix rename is skipped, surfacing much later as + # `undefined symbol: {prefix}` at the per-core link. With `&&` a + # failed nm aborts here loudly instead. + nm_cmd = [ + "sh", + "-c", + f"{nm_path} --defined-only --extern-only {artifact.filename} " + f"> {symbol_map_file}.syms && " + f"awk '{{print $3 \" {prefix}\" $3}}' {symbol_map_file}.syms " + f"> {symbol_map_file}", + ] # Apply the renaming using the symbol map objcopy_cmd = [