← Voltar aos artigos

Instruções condicionais em Thumb: IT blocks e execução condicional

No artigo anterior a gente viu o Thumb comprimindo instruções para 16 bits. Mas tem um detalhe que perdemos: no ARM, quase toda instrução é condicional (basta mudar os 4 bits do topo). No Thumb-1, poucas instruções aceitam condição — e o processador precisa de BLT, BGE etc. para decidir. O Thumb-2 resolve isso com os IT blocks (If-Then): permitem executar até 4 instruções condicionalmente, sem branch. É o que este texto explica — com bytes reais.

Por que execução condicional importa

Quando o processador encontra um BLT, ele precisa adivinhar se o desvio vai acontecer (branch prediction). Se errar, o pipeline é descarregado e 2–3 ciclos se perdem. Com execução condicional, o processador simplesmente não executa a instrução se a condição for falsa — sem branch, sem penalty, sem flush. É mais rápido e mais previsível.

Thumb-1: poucas condições

No Thumb-1, apenas algumas instruções têm formas condicionais: CMP, B (todos os desvios), MOV, ADD, SUB com registradores, e LSL/LSR/ASR. Para o resto, você precisa de um branch:

00008000<_start>:
8000:2003movsr0, #3
8002:2805cmpr0, #5
8004:db01blt.n800a <less>
8006:2101movsr1, #1
8008:e000b.n800c <done>
800a:2100movsr1, #0
800c:2701movsr7, #1
800e:df00svc0

Oito instruções, 16 bytes. Dois branches (BLT e B) — cada um pode causar penalty se o predictor errar.

Thumb-2: os IT blocks

O IT (If-Then) é uma instrução de 16 bits que diz ao processador: "as próximas 1 a 4 instruções são condicionais". A máscara (bits 3..0) tem 4 bits e define quantas instruções seguem e quais são THEN/ELSE. Os padrões comuns são:

  • IT — 1 THEN.
  • ITE — 1 THEN + 1 ELSE.
  • ITT — 2 THEN.
  • ITEE — 1 THEN + 2 ELSE.
  • ITTE — 2 THEN + 1 ELSE.
  • ITTT — 3 THEN.
  • ITEEE — 1 THEN + 3 ELSE.
  • ITEET — 1 THEN + 1 ELSE + 1 ELSE + 1 THEN.
  • ITETT — 1 THEN + 1 ELSE + 2 THEN.
  • ITTTE — 3 THEN + 1 ELSE.
  • ITTTT — 4 THEN.

Nota:ITT, ITTT e ITTTT normalmente usam condição AL (sempre executar) — servem para converter código ARM para Thumb sem mudar a lógica, preservando a execução incondicional.

O mesmo exemplo com IT block (bytes reais, ARMv7-A):

00008000<_start>:
8000:f04f 0003mov.wr0, #3
8004:2805cmpr0, #5
8006:bfb4itelt
8008:2100movltr1, #0
800a:2101movger1, #1
800c:f04f 0701mov.wr7, #1
8010:df00svc0

Sete instruções, 18 bytes. Parece maior — mas não tem nenhum branch. O ITE instrui o processador a executar movlt se a condição for verdadeira, ou movge se for falsa, sem desvio. Em loops pequenos ou blocos condicionais críticos, isso elimina penalties de pipeline.

Decodificando o IT

ITE LT = 0xbfb4. Vamos bit a bit:

0xbfb4 = ite lt — 16 bits
  • O firstcond (bits 7..4) é o código da condição do primeiro bloco (THEN). Para LT, é 1011.
    Tabela de Condições
    CódigoCondição
    0000EQ (Equal)
    0001NE (Not Equal)
    0010CS / HS (Carry Set / Unsigned Higher or Same)
    0011CC / LO (Carry Clear / Unsigned Lower)
    0100MI (Minus / Negative)
    0101PL (Plus / Positive or Zero)
    0110VS (Overflow)
    0111VC (No Overflow)
    1000HI (Unsigned Higher)
    1001LS (Unsigned Lower or Same)
    1010GE (Signed Greater than or Equal)
    1011LT (Signed Less Than)
    1100GT (Signed Greater Than)
    1101LE (Signed Less than or Equal)
    1110AL (Always / Unconditional)
    1111Reserved / Unpredictable
  • A máscara (bits 3..0) tem 4 bits e define quantas instruções seguem e quais são THEN/ELSE. O 1 menos significativo indica o fim da cadeia e e os bits anteriores indicam se a instrução é THEN (T) ou ELSE (E) baseado no bit menos significativo da firstcond. Se o bit for igual a firstcond[0], então é THEN (T), caso contrário é ELSE (E). Por exemplo, para ITE LT:
    1. firstcond = LT = 1011 → O bit menos significativo (firstcond[0]) é 1.
    2. máscara = 0100 → bits 3..0 = 0100.
    3. O bit 1 mais à direita está na posição do bit 2 (os bits são 3, 2, 1, 0). Ele atua exclusivamente como terminador, avisando a CPU que o bloco possui 2 instruções no total (a base + 1 extra).
    4. O bit restante acima do terminador (o bit 3) dita a condição da segunda instrução. O valor dele é 0.
    5. Como o bit 3 (0) é diferente de firstcond[0] (1), a segunda instrução recebe a condição oposta, ou seja, um ELSE (E).
    Portanto, a sequência final une o THEN obrigatório da primeira instrução com o ELSE calculado da segunda: ITE.
    Tabela de máscaras possíveis e como elas se traduzem em instruções THEN/ELSE dependendo do valor de firstcond[0]
    maskTerminadorQtd. de InstruçõesSe firstcond[0] == 0Se firstcond[0] == 1
    1000Bit 31ITIT
    0100Bit 22ITTITE
    1100Bit 22ITEITT
    0010Bit 13ITTTITEE
    0110Bit 13ITTEITET
    1010Bit 13ITETITTE
    1110Bit 13ITEEITTT
    0001Bit 04ITTTTITEEE
    0011Bit 04ITTTEITEET
    0101Bit 04ITTETITETE
    0111Bit 04ITTEEITETT
    1001Bit 04ITETTITTEE
    1011Bit 04ITETEITTET
    1101Bit 04ITEETITTTE
    1111Bit 04ITEEEITTTT
  • O assembler automaticamente inverte a condição para ELSE — por isso ITE LT gera MOVLT e MOVGE (GE = NOT LT).

Importante: as condições nos mnemônicos (MOVLT, MOVGE) são apenas documentação para o programador. O que realmente define a condição é a instrução IT — o processador lê a máscara do IT e aplica as condições automaticamente. Se você escrever MOVLT fora de um bloco IT, o assembler vai reclamar: sem o IT, não existe execução condicional em Thumb.

Quando usar IT vs branches

  • IT: blocos curtos (1–4 instruções), condições simples, quando a previsibilidade é crítica (tempo real, DSP).
  • Branches: blocos longos (>4 instruções), quando o predictor tem boa acurácia, ou quando o código é mais legível com labels.

Glossário: instruções e comandos

Instruções IT e comandos relacionados
Instrução / ComandoO que faz
IT {cond}Inicia bloco de 1 instrução condicional.
ITE {cond}Inicia bloco: 1 THEN + 1 ELSE.
MOV{cond} Rd, #immMovimentação condicional (dentro de IT).
-march=armv7-a -mthumbMonta Thumb-2 com IT blocks.
arm-none-eabi-objdump -dMostra os bytes IT + instruções condicionais.

O que o arm-jitter faz com isso

Quando o arm-box executa com --arch=thumb2, o arm-jitter decoda o IT e registra quais instruções seguintes são condicionais. Para cada uma, ele verifica o CPSR antes de executar — se a condição for falsa, a instrução é pulada (como um NOP invisível). É a mesma semântica do hardware: sem branch, sem penalty, só execução condicional.

Próximo passo

Com ARM, Thumb, Thumb-2 e IT blocks no arsenal, o próximo texto pode explorar AArch64 (o modo de 64 bits do ARM, suportado pelo arm-box com --arch=aarch64) ou semihosting e syscalls (como o arm-box traduz chamadas de sistema EABI para o host). Até lá, tente montar um ITE com 3 instruções ELSE e conte os bytes — a codificação da máscara vai fazer sentido.