Brendan PunskyandClaude Opus 5.5 bc2d511607 rexcode/arm32: Thumb, and the checks that let it go wrong unnoticed
A Cortex-M7 image decoded to nonsense. Partly the caller asked for A32,
which is the default -- but in Thumb mode it was no better. The first
four words of the image came out as a PUSH whose list was read from the
wrong bits, an LDR whose literal and an LDRB whose base came from fields
that are not theirs, and a CBNZ landing four bytes short. Thumb had
been sketched rather than built: most 16-bit forms left their immediates
and registers unencoded, there were no VFP or Advanced SIMD Thumb forms
at all, IT blocks were not followed, and no branch target in either
instruction set counted the PC bias.

The 16-bit and 32-bit integer Thumb instruction set is re-specified in
tablegen/thumb_table.odin, a flat list in the order the Architecture
Reference Manual's encoding index gives it, merged into each mnemonic's
forms by the generator. The VFP, Advanced SIMD and coprocessor Thumb
forms are not listed: they are their A32 encodings with the condition
fixed, or with the U bit moved from 24 to 28, and the generator derives
them -- so the two instruction sets cannot drift apart.

What Thumb needs that A32 did not have:

  * IT blocks. The decoder and encoder both keep ITSTATE. Inside a block
    an instruction takes the block's condition, and most 16-bit data
    processing stops setting the flags -- `adds` outside, `add` inside --
    so forms carry a flags_outside_it flag and the encoder picks by it.
  * `.w`, printed exactly when a 16-bit form could also encode the
    instruction, which is what an assembler would otherwise choose. Not
    more often: llvm rejects `.w` on instructions with no narrow form.
    MOV with an immediate always needs it, or `mov r0, #648` is MOVW.
  * Operand ranges. Shape alone cannot pick between `add r0, r1, r2` and
    `add r8, r9, r10`; operands_fit checks each value against its field,
    for every encoding (an exhaustive switch -- a new encoding has to
    say whether it narrows anything).
  * Profiles. A- and M-profile cores disagree about part of the 32-bit
    space: MVE sits in Advanced SIMD's words, v8.1-M's CSEL in ORRS's
    should-be-zero bit, BXNS in BX's. decode takes a Profile (A-profile
    by default, as before) and filters forms by it.

Decoded branch and literal targets are now absolute offsets, PC bias
and Align(PC, 4) included, as arm64 already does; the A32 ones were
eight bytes short too.

Shared rows that were wrong in both sets came out on the way: VFP
arithmetic masks that let coprocessor 14 and 15 decode as VADD, an
operand list count three bits wide that cut `vpush {d8-d15}` to
nothing, fixed-point VCVT reading a second register out of the fraction
bits, VMOV scalar-to-core with only the .32 lane, VSEL's condition never
encoded, NEON modified immediates that dropped odd cmodes and truncated
.i64, FLDMX/FSTMX missing, and MVE rows on NEON register fields.

None of it was caught before because nothing compared against an
assembler. The table sweep checked only the fixed bits after a
decode-encode round trip, so an operand read from the wrong field passed
as long as the encoder put it back there; it compares the whole word
now. tools/verify_against_llvm.odin replaces a mnemonic-name comparison
that read files from /tmp and passed when they were missing: it decodes
every 16-bit Thumb halfword, every table form with random operand
fields, random A32 and Thumb words and every IT shape, prints them,
assembles the text with llvm-mc, and requires the original bytes back,
under A- and M-profile targets. Object files named on its command line
are checked the same way, instruction by instruction; a Cortex-M0, M7
and M33 build of core:fmt, strconv and math (some 120,000 instructions)
and the reporter's own objects now round-trip byte for byte, as does a
golden Thumb listing that also lives in the pipeline test.

What the verifier still finds is older A32 trouble -- register-offset
shifts and post-indexed register forms of LDR/STR, MOVW/MOVT and BKPT
immediates, non-canonical modified-immediate rotations, NEON structure
loads and the shift-by-immediate family -- and MVE and CDE rows that
need re-deriving. Those come next.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbUtG4UwNiMydtMW2qGhw4
2026-10-21 17:32:16 -04:00
…
…
…
…
…
…
…
…
…
…
…

Odin logo
The Data-Oriented Language for Sane Software Development.


The Odin Programming Language

Odin is a general-purpose programming language with distinct typing, built for high performance, modern systems, and built-in data-oriented data types. The Odin Programming Language, the C alternative for the joy of programming.

Website: https://odin-lang.org/

package main

import "core:fmt"

main :: proc() {
	program := "+ + * 😃 - /"
	accumulator := 0

	for token in program {
		switch token {
		case '+': accumulator += 1
		case '-': accumulator -= 1
		case '*': accumulator *= 2
		case '/': accumulator /= 2
		case '😃': accumulator *= accumulator
		case: // Ignore everything else
		}
	}

	fmt.printf("The program \"%s\" calculates the value %d\n",
	           program, accumulator)
}

Documentation

Getting Started

Instructions for downloading and installing the Odin compiler and libraries.

Nightly Builds

Get the latest nightly builds of Odin.

Learning Odin

Overview of Odin

An overview of the Odin programming language.

Frequently Asked Questions (FAQ)

Answers to common questions about Odin.

Packages

Documentation for all the official packages part of the core and vendor library collections.

Examples

Examples on how to write idiomatic Odin code. Shows how to accomplish specific tasks in Odin, as well as how to use packages from core and vendor.

Odin Documentation

Documentation for the Odin language itself.

Odin Discord

Get live support and talk with other Odin programmers on the Odin Discord.

Articles

The Odin Blog

The official blog of the Odin programming language, featuring announcements, news, and in-depth articles by the Odin team and guests.

Warnings

  • The Odin compiler is still in development.
S
Description
No description provided
Readme Zlib
843 MiB
0 Stars 1 Watchers 0 Forks
Languages
Odin 86.7%
C++ 9.4%
C 3.2%
Python 0.4%
Lua 0.1%