Repository contents
Table with columns: Path, What it is| Path | What it is |
|---|
adapter_model.safetensors, adapter_config.json | the r3 LoRA adapter (fp32, 870 MB), in the exact form used for evaluation and serving |
base-nvfp4/ | the NVIDIA ModelOpt NVFP4 build of Qwen3.8-27B the adapter runs on (17 GB; unmodified copy of a third-party build, see NOTICE) |
LICENSE, NOTICE | Apache-2.0 and attribution |
Model details
Table | |
|---|
| Developed by | momusdigital |
| Model type | LoRA adapter (PEFT 0.18.1), rank 32, alpha 64, no dropout |
| Base model | Qwen/Qwen3.8-27B (hybrid linear/full attention, 64 layers) |
| Targeted modules | q_proj, k_proj, v_proj, o_proj (full attention); in_proj_qkv, in_proj_z, out_proj (linear attention); gate_proj, up_proj, down_proj (MLP). 400 matrices, 217.6 M trainable parameters (0.79%) |
| Language | English prompts; C++ output |
| License | Apache-2.0 (same as the base model) |
adapter_model.safetensors sha256 | dc88662ee33095ff9bbfa9de512c1aafecfda76e98b19f7b3733013f59fe1f48 |
| Lineage | stock model → round 1 → round 2 → round 3 (each round continues the previous adapter) |
Which weights this was trained and tested against
The adapter was trained on BF16 weights dequantized from the NVFP4 checkpoint in base-nvfp4/, and evaluated only when
served on top of that NVFP4 checkpoint with vLLM's LoRA support. Applying it to the official BF16 weights or to another
quantization is untested. The adapter's changes are small (about 0.1% of a typical weight's magnitude), so it should transfer
approximately, but that has not been measured. Merging it into the 4-bit weights does not work: the changes are smaller than
one 4-bit step and are rounded away, so it is served as a LoRA on top of the NVFP4 base.
adapter_config.json is kept exactly as served: base_model_name_or_path is the local served name qwen3.8-27b-nvfp4, so
load the base explicitly rather than relying on AutoPeftModel.
Intended use
- Research on LLM-assisted matching decompilation: proposing C++ for one function at a time inside a loop where a real
compiler and a byte comparator grade each attempt and return feedback.
- Use inside a checker loop. The adapter proposes; the compiler verifies. Output that was not verified against the original
bytes should be treated as a guess.
A system message plus one user message ("packet"), roughly:
## TARGET function name/address/size and the calling-convention note## UNIT PRELUDE C++ declarations already in scope for the translation unit## IDENTITIES labels used in the assembly (callee names, globals)## ... similar solved functions, hints, tips ...## ORIGINAL ASSEMBLY Intel-syntax x86 listing of the one functionReply with the complete C++ definition of the function in one ```cpp block.
Replies are a single ```cpp block, with thinking off. After a failed attempt the harness sends the compiler error or an
aligned instruction diff and asks for a corrected definition; the adapter was trained on that multi-turn pattern.
Out of scope
- General coding assistance, chat, or any task other than the packet format above. Quality outside it was not measured.
- Source that is functionally equivalent but not byte-identical: that is not what it was optimized for.
- Large functions. It is useful up to roughly 255 bytes of machine code and rarely beyond (see Results).
Serving (vLLM, one 32 GB GPU)
Serve the NVFP4 base with the adapter loaded at startup; request "model": "r3" for the adapted model, or
"model": "qwen3.8-27b-nvfp4" for the un-adapted base on the same endpoint.
# download this repo to ./r3 (adapter at ./r3, base at ./r3/base-nvfp4)docker run -d --name qwen38-27b-r3 --gpus all --ipc=host -p 127.0.0.1:8020:8000 \ -v "$PWD/r3/base-nvfp4:/models/qwen3.8-27b-nvfp4:ro" \ -v "$PWD/r3:/adapters/r3:ro" \ --env PYTORCH_ALLOC_CONF=expandable_segments:True \ --env VLLM_FLASHINFER_WORKSPACE_BUFFER_SIZE=134217728 \ vllm/vllm-openai@sha256:c1c9f6fd5c109ba7f0546a59f5b2f15fb87f64c77782e90a27b648b42a8e67c3 \ /models/qwen3.8-27b-nvfp4 --host 0.0.0.0 --port 8000 --served-model-name qwen3.8-27b-nvfp4 \ --tensor-parallel-size 1 --max-model-len 24576 --gpu-memory-utilization 0.96 \ --max-num-seqs 16 --max-num-batched-tokens 2048 \ --kv-cache-dtype fp8_e4m3 --kv-cache-memory-bytes 12884901888 \ --quantization modelopt --cpu-offload-gb 0 \ --mamba-cache-mode align --mamba-ssm-cache-dtype float32 \ --reasoning-parser qwen3 --trust-remote-code --generation-config auto \ --compilation-config '{"mode":3,"cudagraph_mode":"FULL_DECODE_ONLY","cudagraph_capture_sizes":[1,2,4,8,12,16],"max_cudagraph_capture_size":16}' \ --attention-config '{"use_trtllm_attention":false}' --kernel-config '{"enable_jit_warmup":false}' \ --enable-prefix-caching --language-model-only --skip-mm-profiling \ --default-chat-template-kwargs '{"enable_thinking":false}' \ --enable-lora --max-lora-rank 32 --max-loras 1 --max-cpu-loras 4 --lora-modules r3=/adapters/r3
That configuration serves 16 concurrent sessions of up to 24,576 tokens on one RTX 5090 (about 12 GiB of FP8 KV cache). Only
one adapter is active on the GPU at a time (--max-loras 1).
Results
All numbers come from a private evaluation harness. The same harness, check budget (8 attempts plus 1 per 256 B), temperature
(0.6), context window and thinking-off setting were used for every model compared.
Held-out evaluation (300 functions, 12–511 bytes, held out by source unit, all known to be solvable; two runs per model):
Table with columns: Model, Functions matched, 64–255 B band (200 functions)| Model | Functions matched | 64–255 B band (200 functions) |
|---|
| Qwen3.8-27B (stock, NVFP4) | 120 / 130 | 65 / 67 |
| Round 1 | 182 / 184 | 111 / 110 |
| Round 2 | 203 / 193 | 128 / 116 |
| Round 3 (this adapter) | 204 / 211 | 130 / 133 |
Live test on real, previously unattempted functions (64–255 B, 300 functions; stock, round 1 and round 2 measured on identical
functions): stock 8, round 1 21, round 2 37 matched. Round 3 was not run on that exact sample. In a separate paired
test on 213 fresh functions of 64–255 B, round 3 matched 20 against round 2's 17 (not statistically significant), and
in lane use it matched about 7% of functions at 192–255 B against about 2.5% for round 2 (unpaired).
Where it stops working: on 100 fresh functions of 256–1023 bytes, round 3 matched 1, round 2 matched 0. It writes
compilable code for larger functions more often, but its best attempts remain far (median ~130 instructions) from byte-identical.
Success falls roughly 3–5× each time function size doubles.
Held-out numbers are optimistic relative to real use: the live rate on unseen functions is far lower (roughly 3% for the stock
model and 12% for round 2 at 64–255 B).
Training
- Method: supervised LoRA fine-tuning; loss only on the final assistant turn; prompts rendered exactly as the serving stack
renders them with thinking off (empty
<think></think> block).
- Optimizer: AdamW (betas 0.9/0.99, no weight decay), gradient clipping 1.0, cosine learning-rate schedule with warmup.
- Hardware: two NVIDIA DGX Spark (GB10, 128 GB unified memory) nodes, PyTorch DDP, one GPU each, BF16.
Table with columns: Round, Start, Examples, Tokens, Steps, LR peak, Wall time| Round | Start | Examples | Tokens | Steps | LR peak | Wall time |
|---|
| 1 | stock | 3,204 | 16.8 M | 100 | 1e-4 | 13.1 h |
| 2 | round 1 | 2,926 (1,470 new + 1,470 replay) | 16.9 M | 91 | 5e-5 | ~13.5 h |
| 3 | round 2 | 790 (395 new + 401 replay) | 7.3 M | 49 | 5e-5 | 6.1 h |
Final held-out dev loss: 0.1108 (round 1) → 0.1041 (round 2) → 0.1081 (round 3; it rose because round 3's new data was all
256 B–1 KB while the dev set is mostly smaller functions).
Data: pairs of (task packet → source verified to compile to the original bytes), plus multi-turn fix trajectories. Every
example's target was re-compiled and re-compared byte for byte against its packet before use; examples relying on shortcuts
(padded structs or filler arrays, unused locals added to size a stack frame, dummy virtual stand-ins, raw vtable-index casts or
pointer writes, inline assembly, hard-coded addresses) were removed; the evaluation functions, their source units and their
clones were held out, with a contamination check on every build. The data is not released.
Limitations
- Output is a candidate, not an answer. A "match" means the bytes are identical when compiled by one specific compiler with
one specific set of flags.
- Narrow competence. It is tuned to one prompt format, one compiler family and one codebase's conventions (class layouts,
naming, header structure). Behavior on other binaries or compilers is unknown.
- Longer outputs: it reaches the 4,096-token output cap slightly more often than the previous round (about 2.7% of calls).
- Small-function strength: the large gains are on functions up to about 255 bytes.
How to use (illustrative, BF16 base untested; see above)
from transformers import AutoModelForCausalLMfrom peft import PeftModel base = AutoModelForCausalLM.from_pretrained("Qwen/Qwen3.8-27B", dtype="bfloat16", device_map="auto")model = PeftModel.from_pretrained(base, "momusdigital/Qwen3.8-27B-MSVC9-Matcher-LoRA")# build a packet in the format above, apply the chat template with thinking disabled, then generate
Citation
@misc{msvc9_matcher_lora_2026, title = {Qwen3.8-27B MSVC9 Matcher (LoRA)}, author = {momusdigital}, year = {2026}, howpublished = {Hugging Face model repository}}
Acknowledgements
Built on Qwen3.8-27B (Apache-2.0, Qwen team / Alibaba Cloud).