
O primeiro verificador de hazards do mundo para NVIDIA Blackwell (sm_120), com um assembler e agendador correspondidos byte por byte ao próprio compilador deles. A verificação que eles nunca lançaram.
O problema · Quais GPUs · Como funciona · Início rápido · Medido, não presumido · Descobertas · API · Método · Roteiro · Sala limpa
Uma instrução de GPU NVIDIA tem 128 bits, e 21 deles não são a instrução. São uma palavra de controle de escalonamento, de stall a reuse: quantos ciclos esperar antes de emitir a próxima instrução, quais scoreboards sinalizar, em quais aguardar e quais operandos podem ser servidos pelo cache de reuso.
O hardware não verifica nada disso. No sm_120 não há intertravamento em instruções de latência fixa. O silício confia em quem quer que tenha produzido a palavra de controle. Se uma contagem de stall for menor que a latência de um valor consumido pela próxima instrução, nada falha, nada trava e nenhum aviso é emitido. A instrução lê um registrador que ainda não foi escrito e calcula sobre dados obsoletos, em velocidade máxima, todas as vezes.
Esse é um tipo estranho de bug. Ele não derruba o programa. Não aparece em um depurador. Produz números apenas errados, o que em uma multiplicação de matrizes ou em um kernel de atenção significa um modelo que treina levemente mal, em vez de um que quebra visivelmente.
As três codificações mais baratas são as quebradas, e nada em lugar nenhum reporta isso. Note também o que a barra à esquerda está fazendo: um stall de zero não é zero ciclos, é uma codificação segura distinta que aguarda resultados pendentes e custa cerca de nove vezes uma instrução escalonada. Um verificador que lesse isso como zero chamaria programas corretos de quebrados.
Ferramentas que geram código de máquina para esta arquitetura atribuem esses bits de controle a partir de um modelo de latência. basalt é a coisa que verifica a resposta.
A NVIDIA dá a você um compilador que escreve esses 21 bits. Ela não dá nada que os leia de volta e diga que são seguros, e ninguém mais dá.
Montadores para GPUs NVIDIA existem há uma década, a codificação Blackwell foi
engenharia reversa antes, existem caracterizações publicadas em nível de ciclo do sm_120,
e um montador público para esta arquitetura já atribui os bits de controle de escalonamento
ele mesmo e executa seus próprios kernels em uma placa para ver se as respostas saem certas.
Tudo verdade, e nada disso é a afirmação:
Nada mais pode receber um cubin que não produziu e ser instruído a dizer se seus bits de controle de escalonamento são seguros.
Seu compilador emitiu aquele cubin, ou uma biblioteca o enviou, ou alguém o escreveu à mão, e até agora não havia como perguntar. Em uma arquitetura sem intertravamento de hardware, essa é a diferença entre "rodou" e "está correto", e a diferença é invisível: um stall um ciclo curto lê um registrador obsoleto e retorna um número errado em velocidade máxima, sem falha e sem aviso, todas as vezes.
Todo o resto aqui existe para tornar essa frase testável. O montador é o que constrói um programa com um stall deliberadamente encurtado. O escalonador é o que força o modelo a se comprometer com uma resposta em vez de avaliar a de outra pessoa. E a auditoria é onde a frase deixa de ser uma ausência e se torna uma medição: basalt apontado para 2.473 cubins sm_120 que a NVIDIA distribui no cuBLAS, cuSOLVER, cuSPARSE, NPP e demais.
Por que não existia, nas próprias palavras do campo. O montador de SASS mais usado diz em sua própria documentação que "verificar o rigor da correção do programa inteiro … [está] longe de ser possível sem suporte oficial. Então, fica para o usuário garantir a correção do programa, com ajuda muito limitada do montador." SIP, sobre autotuning de escalonamentos SASS, afirma que "a validação é impossível para código assembly nativo de GPU porque a semântica formal do sass é fechada."
Ambos tratam da correção semântica: se um kernel calcula o que deveria. basalt não responde isso, e nada aqui afirma responder. Ele responde a uma pergunta estritamente menor, e o ponto é que a pergunta menor é decidível sem a semântica:
Os bits de controle deste programa cobrem suas próprias dependências de dados?
Isso exige a estrutura de dependências, que a codificação entrega, e um modelo de latência, que o silício entrega sob medição. Nenhum dos dois exige saber o que qualquer instrução calcula. Um kernel pode passar nesta verificação e ainda ser o algoritmo errado; o que não pode fazer é ler um registrador antes de o valor chegar.
Segundo, nada mais é medido contra os próprios bytes do fornecedor. A referência do basalt é
a saída do ptxas, então uma discordância é bug do basalt até que se prove o contrário. Seu montador tem
de reproduzir os 128 bits exatos do compilador. Seu escalonador tem de descartar cada bit de controle
que o compilador escolheu, calcular novos e fazer a GPU calcular a mesma resposta.
Um padrão para tudo isso: concordar exatamente com o fornecedor, ou dizer por quê.
| Componente | O que faz | Como é verificado | Resultado |
|---|---|---|---|
| Montador | Texto SASS para a palavra de 128 bits | Remontar cada instrução emitida pelo ptxas e comparar bytes, no corpus e em 5,2 M de instruções de código de biblioteca distribuído que ele nunca tinha visto | 59.693 de 59.760 instruções do corpus e 4.585.336 de 5.237.448 distribuídas exatas, o restante recusado pelo nome, 0 erradas em ambos |
| Verificador | Lê um escalonamento, reporta riscos | A saída do próprio fornecedor precisa ser validada limpa, e um stall deliberadamente encurtado precisa ser capturado | 0 erros em 1.323 pares de kernel e nível de otimização do fornecedor, 0 perdidos em 233 quebrados |
| Auditoria | O mesmo verificador, em bibliotecas distribuídas | Executá-lo sobre kernels sm_120 de produção retidos de todas as tabelas que ele lê | 0 erros em 2.762 kernels e 10.218.030 dependências, todos os 2.762 totalmente analisados |
| Escalonador | Atribui cada bit de controle do zero | Descartar os do fornecedor, calcular novos, executar ambos na GPU contra oito entradas, comparar bytes de saída | 439 de 439 kernels comparáveis byte-idênticos, nos três níveis de otimização |
E a parte sobre a qual um escalonador costuma ficar calado: o que a correção custa. Os escalonamentos do basalt gastam 1,05x os ciclos de emissão do fornecedor, mais lentos em 111 dos 1.323 pares de kernel e nível de otimização e mais baratos em 842, com todo kernel comparável ainda byte-idêntico na GPU.
A terceira linha é a que mudou as outras três. Um verificador calibrado em um corpus não pode falhar nesse corpus: a folga mais apertada que o compilador foi visto deixar é o piso, por construção, exatamente para o código em que foi medido. Na primeira vez em que este viu código de outro lugar, reportou vinte e seis riscos por kernel em um decodificador JPEG que nunca retornou um pixel errado, e todos os 6.593 eram do basalt. Corrigi-los levou a zero, e zero sobre uma biblioteca também não era evidência: ampliar o conjunto retido para três bibliotecas e 5,2 milhões de instruções levou direto de volta a 940 e encontrou mais cinco erros de modelo, além dos primeiros oito. Treze correções, nenhuma da NVIDIA, e o requisito re-minerado de 24.311 kernels distribuídos colocou um predicado de guarda em 13 ciclos em 229.567 observações, que é o número que a injeção de falhas havia medido nesta placa ao quebrar um programa de propósito. Veja descoberta 32.
Ser mais barato que o fornecedor não é motivo para se vangloriar. o basalt escalona toda dependência
na folga mais apertada que ptxas foi visto deixar para exatamente aquele par, e ptxas está
equilibrando pressão de registradores e memória junto com latência de emissão enquanto isto otimiza um
número. Também não foi acreditado à primeira vista: na primeira vez em que a razão ficou abaixo de 1,0, o
round trip de hardware quebrou, e o número só se sustentou depois que o bug que expôs foi corrigido. A
razão é fixada dos dois lados na suíte de testes por esse motivo.
A coluna do meio é o ponto. Um verificador e um escalonador que compartilham um modelo de latência concordam entre si enquanto ambos estão errados, então nenhum é evidência para o outro; apenas o silício não tem interesse na discussão. Executar o escalonador sobre sete kernels escritos à mão passou sete de sete por muito tempo. Executá-lo sobre trezentos encontrou quarenta e um errados, e toda correção em descobertas saiu de observar esse número se mover.
O mesmo se aplica às entradas. Uma leitura obsoleta só muda a resposta quando o valor obsoleto e o novo diferem, então um padrão de bytes é uma chance de notar, e executar cada kernel contra um segundo, terceiro e quarto padrões encontrou imediatamente um predicado de carry-out que o modelo de operandos vinha lendo como fonte desde o início. Ele tinha sobrevivido a todos os controles até aquele ponto, incluindo o próprio round trip.
A mesma disciplina decide o que o montador tem permissão de fazer, e vale separar os dois números que ele tem.
A cobertura é de 99,9% do corpus e 87,5% do código de biblioteca distribuído. A correção é de 100%, e é esse o número fixado por um teste. A lacuna entre eles é instruções que o basalt recusa, cada uma nomeando o campo que não conseguiu posicionar, porque uma ferramenta que adivinhasse alcançaria cobertura total emitindo palavras que desmontam para o texto certo e calculam outra coisa. Ele nunca emitiu uma, em 59.760 instruções do corpus e 5.237.448 distribuídas.
Ele só chegou lá após oito rodadas separadas de estar confiantemente incorreto:
Cada um deles produziu uma palavra que monta, desmonta de volta exatamente para o texto de onde veio e calcula outra coisa. Essa é a mesma falha que o resto deste repositório existe para capturar, e é por isso que todos os oito agora são recusados com uma razão nomeando o que o campo realmente guarda, e por que a contagem de instruções que montam para os bytes errados é um teste fixado em zero, em vez de um número em uma tabela.
Uma nona apareceu na primeira vez em que o montador foi apontado para código de máquina que ele não
havia produzido, e era de um tipo diferente. c[0x0][UR4] indexa seu deslocamento por um registrador onde
a forma registrada guarda um número, e o codificador levantou uma exceção em vez de recusar. Uma falha em
entrada estranha é pior do que um veredito errado, porque quem chama não recebe nenhum dos dois.
sm_120 não é um número de modelo. É a capacidade de computação compartilhada por toda a linha
Blackwell de consumo, então a codificação de instruções, o banco de dados, o montador e o verificador
se aplicam a todas as placas dela:
| Placa | Capacidade de computação | Coberta |
|---|---|---|
| GeForce RTX 5090, 5090D | 12.0 (sm_120) | sim |
| GeForce RTX 5080, 5070 Ti, 5070 | 12.0 (sm_120) | sim |
| GeForce RTX 5060 Ti, 5060, 5050 | 12.0 (sm_120) | sim |
| Modelos laptop da série GeForce RTX 50 | 12.0 (sm_120) | sim |
| Placas de estação RTX PRO Blackwell | 12.0 (sm_120) | sim |
| Blackwell de datacenter (B100, B200, GB200) | 10.0 (sm_100) | não, codificação diferente |
ptxas também tem como alvo sm_121, um chip diferente da mesma família. o basalt nunca rodou em um
e não afirma suportá-lo. O que pode dizer é medido: o compilador emite
código byte-idêntico, palavras de controle incluídas, para todos os seis alvos que oferece aqui, então o
escalonamento de que um kernel precisa é uma propriedade da arquitetura, em vez da peça
(descoberta 28). Se isso não fosse verdade, o próprio compilador da NVIDIA estaria
emitindo um escalonamento inseguro para um deles.
Todo número medido em silício vem de uma placa física, nomeada exatamente, porque "uma 5070 Ti" não é suficiente para reproduzir uma execução:
| A placa | O que é exatamente |
|---|---|
| Placa | Gigabyte GeForce RTX 5070 Ti EAGLE OC |
| Reportada pelo driver | NVIDIA GeForce RTX 5070 Ti |
| Capacidade de computação | 12.0 |
| Multiprocessadores de streaming | 70 |
| Clock de boost | 2542 MHz |
| Toolchain | CUDA 13.3.1, ptxas V13.3.73 |
A maior parte do basalt não precisa de GPU alguma. Ambos os oráculos, o banco de dados de instruções, o
montador e o verificador de riscos funcionam contra ptxas e nvdisasm como
subprocessos comuns, e é por isso que rodam em CI em uma máquina sem placa gráfica. 237 dos
252 testes estão nesse grupo, e 200 não precisam nem de placa nem dos binários da NVIDIA.
Uma GPU é necessária para exatamente três coisas, e são as três que transformam uma ferramenta plausível em uma crível:
| Precisa de placa | Por quê |
|---|---|
measure, probe-stalls | Medir o tempo de uma instrução e descobrir o que uma dependência realmente exige quebrando-a |
scripts/roundtrip_corpus.py | Re-escalonar cada kernel do corpus e executar ambas as versões para comparar bytes de saída |
scripts/agreement_sweep.py | Encurtar uma dependência por kernel e perguntar ao silício se o basalt estava certo |
O overclock de fábrica não move as medições. Toda latência aqui está em ciclos,
que é uma propriedade do pipeline, em vez do clock, e o valor de boost é registrado
ao lado delas apenas para que uma comparação de tempo de parede continue possível. O que a placa
afeta é a reprodutibilidade, e é por isso que basalt measure --board a registra.
Tudo medido aqui foi medido em uma placa, e o basalt registra o SKU junto com cada medição, em vez de apresentá-las como universais. Uma 5090 tem mais que o dobro de SMs e seu próprio comportamento de clock; a codificação será idêntica e as latências devem ser remedidas em vez de presumidas:```bash python -m basalt.cli measure -o my-card.json python -m basalt.cli verify kernel.cubin --latencies my-card.json
Isso não é modéstia. Um modelo de latência compartilhado entre um verificador e um agendador é exatamente
onde um número errado se esconde, então um segundo cartão é a coisa mais útil que alguém pode contribuir.
</details>
## Como funciona
Tudo depende de dois oráculos, ambos binários NVIDIA padrão executados como processos externos. Nenhum código-fonte, cabeçalho ou biblioteca da NVIDIA é usado ou redistribuído.
| Oráculo | Invocação | O que fornece |
| :--- | :--- | :--- |
| **Verdade de base** | `ptxas` → cubin → `nvdisasm -c -hex` | Codificações que o compilador do fabricante realmente emite. Semântica indiscutível. |
| **Sonda** | `nvdisasm -b SM120a` sobre bytes brutos | Decodifica palavras que o `ptxas` nunca emitirá, o que transforma o espaço de codificação em algo pesquisável em vez de algo para adivinhar. |
O oráculo de sondagem é o que importa. Uma ferramenta limitada à saída do compilador só pode redescobrir o que o compilador já faz. Alimentar palavras sintetizadas de 128 bits diretamente no decodificador significa que o conjunto de instruções pode ser *medido*.
Nenhum dos oráculos precisa de GPU, portanto todo o banco de dados de instruções é reconstruído no CI em qualquer máquina.
### Derivando a codificação ao alterá-la
O basalt não lê uma tabela de opcodes de lugar nenhum. Ele pega uma codificação que foi montada, inverte um bit, decodifica o resultado e registra o que mudou. Um bit que altera o registrador de destino é um bit de destino; um bit que altera o mnemônico é um seletor; um bit que não altera nada observável é inerte.
Executado contra `IADD R5, R5, 0x2a`, a medição resulta em:```
operand[0] bits 16:23 flip 16 -> R4, flip 17 -> R7 destination register
operand[1] bits 24:31 plus bit 72, which negates it source register
operand[2] bits 32:63 flip 32 -> 0x2b, flip 33 -> 0x28 32-bit immediate
opcode bits 2, 4, 12:15
inert 36 bits no observable effect
invalid 11 bits the decoder rejects the mutation
Campos de registradores de oito bits e um imediato de 32 bits, obtidos por experimentação em vez de suposição.
O layout se valida por si só no contato. Em um kernel trivial, S2R define write_barrier=0 e o IMAD que consome seu resultado carrega wait_mask=0x01; LDCU.64 define write_barrier=1 e o STG.E dependente carrega wait_mask=0x02. Cada par produtor-consumidor se alinha, e as instruções que o nvdisasm anota com .reuse têm o bit de reutilização correspondente definido.
Sem instalação do CUDA e sem GPU. O script da toolchain busca redistribuíveis fixados, aproximadamente 45 MB, sem direitos de administrador, sem nada adicionado ao seu PATH.```bash git clone https://github.com/sunnypatell/basalt.git cd basalt python -m venv .venv && source .venv/bin/activate # Windows: ..venv\Scripts\Activate.ps1 pip install -e ".[dev]"
python scripts/fetch_toolchain.py # pinned ptxas + nvdisasm python -m basalt.cli doctor # verify both oracles end to end python scripts/verify_all.py # every control in this README, in order
No content provided to translate.```console
$ python -m basalt.cli doctor
ok toolchain V13.3.73 in third_party/cuda/13.3.1/bin
ok ptxas assembled sm_120a
ok cubin oracle 16 instructions with encodings
ok probe oracle 16/16 mnemonics round-tripped
both oracles healthy. no GPU required for anything above.
pip install basalt-sass instala a CLI, a biblioteca e as três tabelas medidas, sem
dependências de tempo de execução. Use isto se você já tiver CUDA na máquina; o checkout acima
é o que você quer se não tiver, ou se pretende reproduzir as medições.```bash
pip install basalt-sass
basalt doctor
basalt verify kernel.cubin
### Onde procura por `ptxas` e `nvdisasm`
O basalt executa ambos como processos externos e não redistribui nenhum deles, então precisa encontrar uma cópia.
Ele usa o primeiro que responder, e **qualquer instalação do CUDA 13 serve**: nada precisa ser o
redistribuível fixado.
| Ordem | Onde |
| ---: | :--- |
| 1 | `--cuda-bin`, passado na linha de comando |
| 2 | `BASALT_CUDA_BIN`, um diretório contendo ambos os binários |
| 3 | `CUDA_PATH`, `CUDA_HOME` ou `CUDA_ROOT`, cada um mais `/bin` |
| 4 | `ptxas` no seu `PATH` |
| 5 | `third_party/cuda/<version>/bin` em um checkout, do mais recente para o mais antigo |
`basalt doctor` mostra qual foi resolvido e sai com código não zero quando não consegue encontrar um; assim,
funciona como pré-condição de etapa de build, e não apenas como algo para ler.
Consultar o banco de dados de instruções não precisa de nenhuma toolchain, porque o banco de dados é medido
antecipadamente e vem incluso no pacote:```bash
basalt isa --stats
basalt isa --opcode QMMA
Reconstrua o banco de dados de instruções do zero ou consulte o que foi commitado:```bash python -m basalt.cli build-isa # harvest, probe, write src/basalt/data/isa/sm_120a.json python -m basalt.cli isa --stats python -m basalt.cli isa IMAD.WIDE.U32 # one form, with its measured field layout python -m basalt.cli isa --opcode QMMA # every form of one opcode
### Verifique o código de máquina que você não escreveu
Esta é a parte que nada mais faz, e não precisa de GPU nem de argumentos. O modelo
de latência medido e a tabela de requisitos minerada estão ambos commitados, então um clone novo pode ser
apontado diretamente para um cubin, não importa o que o tenha produzido:```bash
python -m basalt.cli verify kernel.cubin
I'm ready to translate the chunk, but the input content appears to be missing—the message ends with "INPUT:" and no actual Markdown content follows.
Since I must not fabricate content or ask questions, and there is no text to translate, I will output nothing. Please provide the chunk text and I'll translate it immediately.```console $ python -m basalt.cli verify nvjpeg.sm_120.cubin 25 kernels, 0 with an error 7984 instructions in 789 blocks, 11580 dependencies checked across blocks: 0 errors, 3 warnings pair data: 3957 pairings from 25634 kernels, 427 producers with enough observations to use latency model: measured on NVIDIA GeForce RTX 5070 Ti
Uma biblioteca ELF contém centenas de kernels e cada um é verificado individualmente, porque os offsets recomeçam em zero e nada passa de um para o próximo. Adicione `--strict` para sair com código de status não zero em caso de perigo, que é o que uma etapa de build espera. Se você tiver uma placa sm_120 e quiser medir o modelo no seu próprio silício em vez de no que está neste repositório:```bash
python -m basalt.cli measure -o my-card.json # needs a GPU, once
python -m basalt.cli verify kernel.cubin --latencies my-card.json
Tudo o que a CLI faz é importável, e a superfície da biblioteca com exemplos executáveis está
em docs/API.md.
E os dois controles que mantêm o restante confiável. O primeiro precisa de uma placa; o segundo precisa das bibliotecas fornecidas e nenhum hardware:```bash python scripts/roundtrip_corpus.py # reschedule all 441 corpus kernels, run both on the GPU
python scripts/fetch_toolchain.py --libs # ~1.2 GB, no admin, nothing on PATH python scripts/audit_shipped.py --libs third_party/cuda/13.3.1/libs
O conteúdo de entrada está vazio — não há texto para traduzir neste chunk.```console
$ python -m basalt.cli verify kernel.cubin --latencies src/basalt/data/latency/rtx-5070-ti.json
kernel.cubin
32 instructions in 3 blocks, 23 dependencies checked: clean
latency model: measured on NVIDIA GeForce RTX 5070 Ti
Os números aqui são impressos pela ferramenta e regenerados a partir de um checkout limpo. Os comandos acima são a fonte da verdade; estas tabelas são instantâneos.
Banco de dados de instruções. Cada entrada carrega uma codificação que foi realmente montada e a build do compilador que a produziu.
| Banco de dados de instruções |
|---|
A cobertura de tensor é onde vive o hardware de baixa precisão: HMMA e IMMA, QMMA nos tipos FP8, FP6 e FP4, incluindo pares de operandos assimétricos, as formas de fator de escala QMMA.SF e OMMA.SF que carregam um expoente por bloco, IMMA.SP esparso, e as instruções de movimentação de matriz LDSM, STSM e MOVM em todas as formas, incluindo as variantes de transposição.
Latência, em uma RTX 5070 Ti. 70 SMs, todos os ajustes com R² ≥ 0,9998. Medida cronometrando cadeias dependentes e tomando a inclinação, com o comprimento da cadeia lido do SASS compilado em vez de presumido.
Três delas contradizem o modelo presumido com o qual o basalt foi lançado: DADD foi presumido como 48, POPC foi presumido como 4, e cada conversão foi presumida como 6, contra 24 medidos para a ida e volta.
Um modelo de latência presumido não é uma pequena aproximação de um medido, o que é todo o argumento para medir.
E um stall de zero não é zero ciclos. É uma codificação segura distinta que espera por resultados pendentes, custando cerca de 37 ciclos onde uma instrução agendada custa 4. É por isso que ptxas -O0 emite uma palavra de controle totalmente zerada e o código ainda assim computa corretamente, aproximadamente nove vezes mais devagar.
Ele concorda com o compilador do fornecedor em todo kernel do corpus. Todo kernel que o ptxas compila a partir do corpus é verificado contra o seu próprio agendamento, em todo nível de otimização que agenda: 30.421 dependências, zero erros. Essa varredura roda na CI a cada push, e todo erro de modelagem que este projeto cometeu foi capturado por ela em vez de por raciocínio.
Os veredictos correspondem ao silício. Para todo stall codificável em um produtor dependente, a resposta estática do basalt e o que o hardware realmente computa concordam, incluindo o caso do zero. Isso é mantido como um teste, não afirmado aqui. A evidência completa, incluindo três métodos independentes para o stall necessário e as correções feitas ao longo do caminho, está em descobertas.
E quando ele diz que um agendamento é inseguro, o silício concorda. Pegue o próprio agendamento funcional do fornecedor para 233 kernels, encurte uma dependência real em cada um e compare o veredicto do basalt com o que a GPU computa: 79 que ele chamou de quebrados estavam quebrados, e nada que ele chamou de seguro computou uma resposta errada. Esse número começou em 34 não detectados em vez de zero, e descobertas diz qual foi a causa e o que custou corrigi-la em falsos alarmes, porque uma varredura que só relatasse seu número final valeria menos do que uma que relatou o primeiro.
O verificador responde se um agendamento é seguro. O agendador responde como seria um agendamento seguro, a partir das mesmas medições: ele descarta todo bit de controle que o ptxas produziu, computa os seus próprios, entrega o resultado de volta ao verificador e então o executa na GPU ao lado da versão do fornecedor do mesmo kernel.
Executado sobre todo o corpus na placa, em todo nível de otimização que produz um agendamento, todos os 439 kernels comparáveis produzem resultados byte-idênticos ao agendamento do fornecedor, a partir de bits de controle que o basalt calculou sozinho. Os 2 que são excluídos leem o clock e o id da grade, então também não concordam consigo mesmos, e descobertas diz isso em vez de incluí-los em uma porcentagem.
Esse controle é a razão pela qual todo o resto é confiável. O verificador e o agendador leem o mesmo modelo de latência, então uma entrada errada nele satisfaz ambos ao mesmo tempo, e eles concordam entre si enquanto ambos estão errados. Apenas o silício não tem nada em jogo nessa discussão. Executar o agendador sobre sete kernels escritos à mão passou sete de sete por muito tempo; executá-lo sobre trezentos encontrou quarenta e um errados, e toda correção de modelo desde então veio de observar esse número se mover.
Esse ciclo é de onde vieram os bugs reais. Stall gasto fora da janela entre um produtor e seu consumidor não conta nada, e gastá-lo ali encerra a busca com um programa que ainda está curto. Um stall fixado na codificação segura estava sendo sobrescrito por uma passagem posterior, substituindo uma garantia por um número pequeno. Operandos fp64 ocupam pares de registradores sem nada no mnemônico que o indique, então metade de toda dependência fp64 era invisível tanto para o verificador quanto para o agendador. Um predicado usado como guarda de uma instrução precisa de treze ciclos, onde o mesmo predicado lido como dado precisa de cinco, porque uma guarda precisa ser resolvida antes mesmo que a instrução seja emitida. E esperar por um scoreboard não resolve uma dependência completamente: o produtor ainda deve um pequeno stall próprio, dois ciclos para adição fp64, e um ciclo a menos é silenciosamente errado. Nenhum desses foi encontrado por raciocínio; cada um foi encontrado executando a saída e obtendo o número errado.
[!NOTE] 1.0, e específico sobre o que isso significa. O que está feito: ambos os oráculos, o banco de dados de instruções com seus campos comprovadamente graváveis, o verificador de hazards sobre um grafo de fluxo de controle real, a latência medida em um SKU por três métodos independentes, um agendador que percorre todo kernel comparável do corpus pelo hardware byte por byte, e uma auditoria de 2.762 kernels distribuídos, mantidos fora de toda tabela que o verificador lê. O que não está: 12 kernels do corpus que não são executáveis por construção e 2 cuja saída do fornecedor não é determinística, todos nomeados nas descobertas; dez opcodes ainda carregam uma latência presumida em vez de medida, nenhum deles jamais um produtor em nenhum dos dois corpos de código; e apenas uma GPU foi medida, o que a descoberta 28 mostra importar menos do que parece. Onde algo é inferido em vez de medido, a ferramenta diz isso em vez de tratá-lo como um fato. Veja o roadmap e o método.
```
src/basalt/
toolchain.py Locating and driving ptxas / nvdisasm
encoding.py The 128-bit instruction word and its control fields
disasm.py Both oracles: cubin ground truth and raw-word probe
harvest/ PTX corpus generation and encoding extraction
probe/ Differential bit probing and field inference
isa/ The generated instruction database and its builder
asm/ The assembler, and the ELF reader that rewrites words in place
sched/ Assigning the control bits, and costing the result
verify/ Register def-use analysis, hazard model, latency checking
gpu/ Driver-API bindings and the latency measurement harness
src/basalt/data/ The measured tables, inside the package so an installed copy
has them: the ISA database, the latency model and the mined
stall requirement
docs/ Findings, method, the Python API, roadmap, artwork sources
scripts/ Toolchain fetch, asset rendering, drift check, and the two
hardware controls: the corpus round trip and the agreement sweep
tests/ Unit tests, plus toolchain- and GPU-marked suites
</details>
## Posição de clean-room
basalt é um trabalho independente, em clean-room, para interoperabilidade. Não contém código-fonte, cabeçalhos, bibliotecas ou documentação da NVIDIA, e não redistribui nada disso. Ele observa o comportamento de executáveis distribuídos publicamente e o registra, que é a base sobre a qual esse tipo de trabalho se sustenta há mais de uma década.
NVIDIA, CUDA e Blackwell são marcas comerciais da NVIDIA Corporation. Este projeto não é afiliado, endossado ou patrocinado pela NVIDIA.
Licenciado sob [Apache-2.0](https://github.com/sunnypatell/basalt/blob/main/LICENSE). Apache de propósito, em vez de algo restritivo: uma ferramenta de correção que ninguém pode usar como base é uma ferramenta de correção que ninguém executa, e a concessão de patentes importa para um trabalho tão próximo do hardware.
## Contribuindo
A contribuição de maior valor é uma codificação que o basalt erra. Consulte [`CONTRIBUTING.md`](https://github.com/sunnypatell/basalt/blob/main/CONTRIBUTING.md) e o [modelo de lacuna de ISA](https://github.com/sunnypatell/basalt/blob/main/.github/ISSUE_TEMPLATE/isa_gap.yml), que coleta informações suficientes para reproduzir sem a sua máquina.
[`SUPPORT.md`](https://github.com/sunnypatell/basalt/blob/main/SUPPORT.md) indica para onde enviar uma pergunta, [`GOVERNANCE.md`](https://github.com/sunnypatell/basalt/blob/main/GOVERNANCE.md) o que uma alteração precisa cumprir, [`RELEASING.md`](https://github.com/sunnypatell/basalt/blob/main/RELEASING.md) como um lançamento é criado e verificado, e [`SECURITY.md`](https://github.com/sunnypatell/basalt/blob/main/SECURITY.md) como reportar de forma privada.
## Citando o basalt
Se o basalt fundamenta um artigo, uma ferramenta, um modelo ou um relatório de bug, cite-o. O GitHub lê
[`CITATION.cff`](https://github.com/sunnypatell/basalt/blob/main/CITATION.cff) nativamente; portanto, **Citar este repositório** na barra lateral fornece
APA e BibTeX sem transcrição. Esse mesmo arquivo é o que o Zenodo e os gerenciadores de citação
interpretam, e é o registro autoritativo de autoria.
A chave abaixo é a que o GitHub gera, portanto copiar daqui e copiar da barra lateral
produz a mesma entrada, em vez de duas que parecem trabalhos diferentes:```bibtex
@software{Patel_basalt_a_hazard_2026,
author = {Patel, Sunny},
license = {Apache-2.0},
month = aug,
title = {{basalt: a hazard checker, assembler and scheduler for NVIDIA consumer Blackwell (sm\_120)}},
doi = {10.5281/zenodo.22072811},
url = {https://github.com/sunnypatell/basalt},
version = {1.0.0},
year = {2026}
}
| Campo | Bits | Significado |
|---|
stall | 108:105 | Ciclos de espera antes de emitir a próxima instrução |
yield | 109 | Dica de que o escalonador de warps pode alternar warps |
write_barrier | 112:110 | Scoreboard para sinalizar no write-back (7 = nenhum) |
read_barrier | 115:113 | Scoreboard para sinalizar na leitura de operando (7 = nenhum) |
wait_mask | 121:116 | Scoreboards que devem estar limpos antes da emissão |
reuse | 125:122 | Flags de cache de reutilização de operandos, uma por slot de origem |
| Comando | O que faz | Precisa de GPU |
|---|
doctor | Verifica ambos os oráculos de ponta a ponta | não |
build-isa | Coleta e testa, escreve o banco de dados de instruções | não |
isa | Consulta um formulário, um opcode ou a cobertura | não |
validate-isa | Prova que os campos medidos podem ser escritos | não |
mine-stalls | Aprende requisitos por par a partir do que o compilador agenda | não |
verify | Verifica os bits de controle de um cubin quanto a riscos de dados | não |
schedule | Atribui os bits de controle de um cubin do zero e verifica o resultado | não |
assemble | Codifica texto SASS, ou um cubin inteiro, e o lê de volta para provar | não |
measure | Cronometra a latência de instruções em silício real | sim |
probe-stalls | Encontra a parada necessária quebrando programas de propósito | sim |
| Contagem |
|---|
| Formas de instrução | 345 |
| Opcodes distintos | 90 |
| Formas com mapa completo de operandos | 339 |
| Formas de tensor-core | 46 |
| Construído com | ptxas V13.3.73 |
| Instruções | Ciclos |
|---|
IMAD IADD3 FFMA FADD FMUL LOP3 SHF | 4 |
POPC | 18 |
I2FP + F2I juntos | 24 |
MUFU | 44 |
DADD DFMA | 64 |
stall | ciclos/instrução | resultado |
|---|
| 0 | 36,85 | correto |
| 1 | 4,88 | errado |
| 2 | 4,88 | errado |
| 3 | 5,88 | errado |
| 4 | 6,88 | correto |
Cite o DOI do conceito, 10.5281/zenodo.22072811, em vez
de um DOI de versão ou desta URL. Ele resolve para o lançamento mais recente, então permanece correto sem
nunca precisar ser editado novamente. O CITATION.cff o contém, portanto já está em ambas as formas acima.
Se você reutilizar as tabelas medidas (src/basalt/data/) ou reproduzir uma figura, cite o lançamento
de onde elas vieram em vez de main: os números são regenerados por scripts/verify_all.py em
um commit específico, e uma tag é o que torna isso reproduzível.
A atribuição é um termo de licença, não uma cortesia. A Apache-2.0 §4 exige que
LICENSE e NOTICE acompanhem qualquer redistribuição ou trabalho derivado,
e o NOTICE carrega a autoria e a declaração de clean-room. Forks, cópias vendorizadas e
wheels reempacotados mantêm ambos os arquivos.
Sunny Patel · sunnypatel.net · github.com/sunnypatell