Commit Graph
20174 Commits
Author SHA1 Message Date
Brendan PunskyandClaude Opus 5.5 ec7256a481 rexcode/arm32: MVE from llvm-mc, and VPT blocks
MVE's rows were, in the table's own words, educated estimates pending
LLVM verification: one element size per mnemonic, contiguous loads given
a 12-bit offset, the accumulating and plain reductions sharing a word,
VPT's mask in the wrong bits. On the M-profile target 2114 random words
decoded to nothing, 410 to the wrong instruction, and the Cortex-M55
object failed on 300 words.

tablegen/mve_table.odin replaces them: a flat list, every row of it read
off llvm-mc. Each instruction is assembled, each operand varied over its
range to find the bits it occupies -- which must be the bits of the
encoding the row names -- and every other bit fixed. Where one word has
several spellings (vmladav is vmlav, vldrb.s8 is vldrb.u8, vmla.s16 is
vmla.i16) the one llvm prints decodes and the others are encode-only.
VPT is VCMP opening a block, so its rows are VCMP's with the mask freed.

What MVE needed that nothing had:

  * Addresses: [Rn, #imm7] scaled by the element, by the memory element
    for the widening loads, a vector of bases [Qm, #imm], a vector of
    offsets [Rn, Qm, uxtw #s], and the interleaving loads' [Rn]!.
  * VPT and VPST masks, spelled on the mnemonic as IT's are, and the
    T or E an instruction inside the block takes, which the decoder now
    follows and the printer writes -- `vaddt.i32`. Without it a listing
    with a VPT block does not reassemble.
  * VCMP's three condition bits, lanes and lane pairs of Q registers,
    MVE's shift amounts, which lack Advanced SIMD's L bit, VCMUL's
    rotation, and the scalar long shifts' register pair -- which may not
    be SP, and where the decoder now falls back to ORRS.
  * The mnemonics llvm has and the table did not: VMULH, VADC, VMAXA,
    VMOVLB, ASRL, SQRSHRL and the rest of the scalar shifts. VMLALB and
    its kin, which do not exist, are gone.

Also on v8.1-M: VLDR and VSTR of the floating-point system registers,
BFCSEL's else target and condition, which it printed as a number,
LDC2L and STC2L on coprocessors 10 and 11 where llvm takes them.

The harness has a VPT-block suite, and the corpus mode takes VPT blocks
as units the way it takes IT blocks. tests/pipeline.odin has an MVE
golden listing, assembled by llvm-mc, that round-trips byte for byte.
Every suite now passes outright -- A32, T16, T32, IT and VPT -- with no
word decoded wrong, extra, missing or unassemblable, and every decoded
word re-encodes; so do the Cortex-M0, M7, M33 and M55 and the A15 and
A53 objects.

The last #partial switch statements in the package are exhaustive now,
and the sweep's fill values are a table.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbUtG4UwNiMydtMW2qGhw4
2026-10-21 19:29:58 -04:00
Brendan PunskyandClaude Opus 5.5 903a232734 rexcode/arm32: Thumb corners, half-precision VFP, profile-aware printing
What was left of 32-bit Thumb outside MVE:

  * PAC, PACBTI and AUT printed `ip!`: the decoder took bit 21 as the
    writeback of any form with an implicit SP. Only SRS's SP is a base.
  * v8.1-M's conditional selects took AL, NV and SP; DLS, WLS and their
    tail-predicated forms took SP and PC; LCTP is DLSTP with PC at any
    element size, not just one; CLRM -- the LDM whose base would be PC --
    was missing, with its APSR in the list.
  * A B<c>.W with condition 111x decoded as a branch: those words are the
    miscellaneous control space.
  * The saturates and bitfield operations left bit 26 and bit 5 free, a
    store addressed relative to PC decoded, and a saturate shifted right
    by nothing is the SSAT16 space, not an `asr #32`.
  * AUTG and BXAUT lost to SMMUL and SMMULR, whose words they are with
    Rd 1111; ERET and SUBS PC, LR are not in the M profile; the SYSm forms
    of MRS and MSR are not in the A profile; Advanced SIMD is the A
    profile's even where its feature is not (VFMA's is VFPv4).

Half precision. v8.2 adds a .f16 form, on coprocessor 9, of most VFP data
processing; the table had a dozen of them. The generator now derives one
from every single-precision form the architecture gives a half-precision
twin, where the table does not list it already.

Printing depends on the profile in one place: v8-M has no load-only
barriers, so `dmb ishld` is `dmb #9` there. Instruction carries whether
it was decoded as M-profile code in its last spare bit, and the printer
follows it.

The harness gave up on any word whose llvm text held a branch target
(`wls lr, sp, 0xcef4`), as llvm had resolved it against its own layout.
It now gives the target back as an offset from the word, the way it does
for rexcode's own text, and asks whether llvm's text reassembles.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbUtG4UwNiMydtMW2qGhw4
2026-10-21 18:55:52 -04:00
Brendan PunskyandClaude Opus 5.5 61314729cb rexcode/arm32: Advanced SIMD from its encoding classes
The A32 Advanced SIMD rows were written out by hand, one mnemonic at a
time, and it showed: most mnemonics had one element size where the
architecture has three or four, masks left the size and the Q bit free,
so one word decoded as whichever row sorted first, the shift-by-immediate
family read the element size as part of the shift, and the element and
structure loads knew neither alignment nor writeback. Against llvm, 1243
random A32 words decoded where llvm decodes nothing and 139 decoded to
the wrong instruction.

Those rows are gone. tablegen/simd_table.odin describes the space the
way the Architecture Reference Manual lays it out -- three registers of
the same length, of different lengths, by scalar, by shift, two-register
miscellaneous, modified immediate, the loads and stores, the v8.x dot
product, complex, FHM, BFloat16 and matrix additions -- and expands each
class into a row per element size, data type and register shape. The
size and type are fixed bits of every row, so a word decodes to one
`.<dt>` and the encoder picks the row by it. The Thumb forms follow
from these as they did before.

The fields whose meaning depends on the rest of the word -- a structure
load's register count, spacing, lane and alignment; L:imm6, whose
leading one is the element size and the rest the shift -- are read off
the whole word (simd.odin), by the decoder from the word and by the
encoder from the form. That replaces thirty-eight per-size encodings
with twelve. Memory gains an alignment (`[r0:64]`) and a post-increment
mode (`[r0]!`), the Advanced SIMD loads' writeback; disp gives up three
of its nineteen bits for it.

Along the way:

  * The decoder reused one Instruction across candidate rows, so a row
    turned away after unpacking left its operands for the next one: an
    LDC2 picked up a fourth operand from a VCMLA it was not.
  * VMRS and VMSR reach different system registers per profile; a
    register run that is empty or passes D31 is not an instruction;
    VDUP from a core register had one size of three and left its
    should-be-zero bits free; FLDMX and FSTMX start in D0-D15; SWP's
    address is an address; MSR names its PSR fields instead of a
    number; PSB CSYNC is AArch64's, and in AArch32 is HINT #17.
  * `vmov.i8` is the byte, not four copies of it.

The harness had one blind spot of its own: a literal load's address in
llvm's trailing comment stopped it from asking whether llvm's text
reassembles. And llvm prints a D list that runs past D31 with the names
of the registers after D31 in its own table -- `{d31, fpinst2}` -- and
takes that back; the harness counts that word as having no text, which
is what it is.

The random A32 suite now passes outright: of 42,408 words none decodes
wrong, extra, missing or unassemblable, and every one re-encodes. The
Cortex-A15 and A53 objects, which had 150 Advanced SIMD words rexcode
could not decode, are exact.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbUtG4UwNiMydtMW2qGhw4
2026-10-21 18:42:23 -04:00
Brendan PunskyandClaude Opus 5.5 738bf0a0a7 rexcode/arm32: A32 field fixes, CDE, and an encoder check on real words
Most of A32's remaining residue came from a handful of systematic gaps:

  * A conditional row matched a word whose condition is 1111. That is
    the unconditional space -- other instructions -- and the decoder
    now refuses it before trying the row.
  * Load and store had no register-shifted, pre-indexed or post-indexed
    register forms; LDM and STM had no writeback-free or `^` forms; the
    16-bit immediates of MOVW, MOVT, BKPT, HVC, UDF and HLT were read
    from one field instead of two. A modified immediate whose rotation
    is not the one an assembler would pick now decodes to its parts,
    `#imm8, #rot`, which is the only text that gives the word back.
  * The extends ignored their rotation. CPS, SETPAN, VEXT, the BLX
    halfword bit, the exclusive pairs and VMOVL each read a field from
    the wrong place.
  * Generic coprocessor instructions decoded in coprocessors 10 and 11,
    which are the floating-point unit's; on v8.1-M also in 8, 9, 14 and
    15 (MVE's). MRC's Rt of 1111 is APSR_nzcv. LDC and STC had no
    unindexed form, `[Rn], {option}`.
  * A register pair (LDRD, LDREXD, STREXD, the CDE D forms) starts on an
    even register; the decoder accepted an odd one and the encoder took
    any second register at all, not just the next.

CDE was laid out wrong throughout, and is re-specified from llvm-mc
bit by bit. Whether coprocessors 0 to 7 hold CDE or generic coprocessor
instructions is a property of the core, not of the code, so decode takes
a CDE_Coprocessors set, empty by default.

T32 data processing (shifted register) did not pin its bit 15, and LDM,
STM, PUSH and POP did not keep SP and PC out of their lists.

The encoder had bugs no check reached: SVC emitted a branch relocation
in place of its immediate, MRS could not name SPSR, and VMRS and VMSR
ORed the register number onto a canonical one left in the form's bits.
The llvm harness could not see any of them, since llvm only ever reads
rexcode's text. It now also re-encodes every word rexcode decodes, with
rexcode's own encoder, and requires the bytes back wherever llvm agrees
the word is the one way to write that text. The sweep fills operands
around the bits a form pins, so a form that requires an even register
gets one.

Dead code removed: the printer's unused data-type inference, with the
#partial switch and the temporary allocation it carried, and nineteen
Operand_Encodings no row used.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BbUtG4UwNiMydtMW2qGhw4
2026-10-21 18:10:21 -04:00
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
gingerBill 779c8c0a47 Merge pull request #7974 from Barinzaya/fix-simd-ashr-overflow
Fix SIMD shift LLVM error and negative overflow behavior
2026-10-19 12:08:52 +01:00
gingerBill 75b5ddadd1 Merge pull request #7982 from harold-b/hb.fix-selector-missing-objc-recevier
Explicitly handle missing receivers in Objective-C selector calls instead of asserting
2026-10-19 12:08:10 +01:00
gingerBill eeb6bba3d7 Merge pull request #7984 from wintermi/fix-2039-using-pointer-subtype
Follow 'using' pointer fields when converting to a subtype
2026-10-19 12:07:49 +01:00
gingerBill 67cd40cbc0 Merge pull request #7989 from BigBoyBarney/negative-reserve
Handle negative reserves for maps and dynarrays
2026-10-19 12:07:23 +01:00
gingerBill aa8b9207df Merge pull request #7994 from corleypc/transmute-aggr-align-fix
Fixes transmuting from aggregate types less aligned than the destination type
2026-10-19 11:57:25 +01:00
corleypc 52ab077b7e fixes trasmuting from src aggregate type lesss aligned than the dest type 2026-10-18 23:56:32 +03:00
Sylphrena 1113c51b5e Fixed test memory leak 2026-10-18 14:20:42 +02:00
gingerBill c6e2f0ba9f Merge pull request #7981 from wintermi/fix-7560-global-matrix-runtime-index
Fix runtime indexing of a global matrix
2026-10-18 12:29:54 +01:00
gingerBill da986c99a0 Merge pull request #7977 from alexthed1rk/impl-simd-x86-sse-cleanup
Impl simd x86 sse cleanup
2026-10-18 12:29:37 +01:00
gingerBill e75d7ca32a Merge pull request #7988 from wintermi/fix-7975-7976-constant-min-max-clamp-types
Unify the types of constant arguments to `min`, `max` and `clamp`
2026-10-18 12:26:34 +01:00
gingerBill f49193212c Export declaration attributes in -export-semantics 2026-10-18 12:14:32 +01:00
Sylphrena 4c7cc43fa4 Handle negative reserves for maps and dynarrays 2026-10-18 12:47:08 +02:00
Matthew Winter aaba3a3346 Unify the types of constant arguments to min, max and clamp
When every argument was constant, the result took the value and type of the
selected argument without checking the arguments against each other, so
mismatched constants like `min(42, "string_literal")`, `min(i32(1), u8(2))`
and `clamp(1.5, 42, "string_test")` were accepted, and `min(1, 2.5)` was an
`int`. Unify the argument types before the constant and non-constant paths, as
the non-constant path already did.

Fixes #7975
Fixes #7976
2026-10-18 21:38:58 +11:00
Matthew Winter 4c725d61fa Follow 'using' pointer fields when converting to a subtype
The checker accepts converting a struct to a type reached through a
`using` pointer field, e.g. `Foo_Again :: struct { using foo: ^Foo }` to
`^VTable` where `Foo` has `using vtable: VTable`, but the backend's
subtype selection lookup only descended into struct fields, so the
conversion (and `foo_again->bar()`) panicked in `lb_emit_conv`. Descend
into pointer-to-struct fields too, and when converting from a value
return the pointer into the referenced value rather than loading it.

Fixes #2039
2026-10-18 15:26:34 +11:00
Harold Brenes ed03d6c9aa Explicitly handle missing receivers in Objective-C selector calls instead of asserting. 2026-10-17 21:58:44 -04:00
Matthew Winter 6aacc08c9f Fix runtime indexing of a global matrix
lb_emit_matrix_ep built a constant GEP whenever the matrix pointer was
constant, even when the computed index was a runtime value, producing
invalid IR ("Use of instruction is not an instruction!"). Only use a
constant GEP when the index is also constant.

Fixes #7560
2026-10-18 12:49:10 +11:00
Alexander Zhura 569a095d1e Impl simd x86 sse comment inline asm _mm_stream_ps 2026-10-18 00:48:29 +03:00
Alexander Zhura a70deef241 Impl simd x86 sse inline asm fix 2026-10-18 00:30:42 +03:00
Alexander Zhura 033166ef8b Impl simd x86 sse crypto backtype fix 2026-10-18 00:16:29 +03:00
Alexander Zhura 73885f1d08 Impl simd x86 sse cleanup 2026-10-17 23:40:22 +03:00
Barinzaya 1233c7fc69 Fixed SIMD shift behavior.
This addresses two issues:
- LLVM errors if the sizes of the two sides don't match, e.g.
  `simd_shr(#simd[4]i32, #simd[4]u8)`
- Negative values on the LHS becoming 0 if a shr overflows.
2026-10-17 16:11:57 -04:00
Jeroen van Rijn afa720bc4d Merge pull request #7963 from odin-lang/bill/diff
`core:diff`
2026-10-17 10:22:46 -07:00
gingerBill 448a23bc7b core:diff: pass interned sides separately 2026-10-17 16:59:46 +01:00
gingerBill 7430cb59f4 Add core:diff 2026-10-17 16:39:11 +01:00
gingerBill eab306ec0f Merge pull request #7962 from odin-lang/bill/allocator-fixes
Fix and improve resize in `core:mem` allocators
2026-10-17 12:21:37 +01:00
gingerBill 64872e01b6 core:mem: Resize last allocations in place and general bug fixes 2026-10-17 12:13:28 +01:00
gingerBill aed1b20e9f core:mem/virtual: Allow for shrinking in-place with Arena 2026-10-17 12:10:04 +01:00
gingerBill 35d622e1dd Merge pull request #7956 from wintermi/fix-7938-or-else-viral-flags
Propagate viral state flags through or_else, or_return and or_branch
2026-10-17 11:41:27 +01:00
Matthew Winter 22c015bc89 Propagate viral state flags through or_else, or_return and or_branch
The checker did not carry its operands' viral state flags up to these
expressions, so an `or_break` nested within them was invisible to the
break analysis. An infinite `for` loop whose only exit was such an
`or_break` was treated as never terminating, allowing a missing return
and lowering the break target to `unreachable`.

Fixes #7938
2026-10-17 01:28:26 +11:00
gingerBill 151d53f44e Merge pull request #7952 from gitsomebit/asd-fix-register
fix: asm instruction-form selection
2026-10-16 15:26:18 +01:00
gingerBill ee164762f6 Merge branch 'master' into asd-fix-register 2026-10-16 15:26:09 +01:00
gingerBill ed48991189 Merge pull request #7912 from alexthed1rk/impl-simd-x86-avx2-gather
Impl simd x86 avx2 gather intrinsics
2026-10-16 15:23:23 +01:00
gingerBill 0444fb2e7d Merge pull request #7955 from odin-lang/bill/asm-compiler-improvements
asm: Defer checking template bodies and fix target feature checks
2026-10-16 15:21:17 +01:00
gingerBill 99dd6e97bc Merge pull request #7933 from aocoronel/master
Android: Fix TLS segment underalignment in ARM64 Bionic
2026-10-16 15:19:38 +01:00
gingerBill 9e199eb211 Merge pull request #7944 from corleypc/by-ref-union-type-switch-fix
Fixes by-reference type switches on custom #align unions
2026-10-16 15:19:16 +01:00
gingerBill b626031b71 Update tests/issues/run.(bat|sh) 2026-10-16 15:08:20 +01:00
gingerBill 16e46158eb asm: Require callers to enable a template's features 2026-10-16 15:07:47 +01:00
gingerBill 334a576a0c asm: Add tests for deferred template checking 2026-10-16 14:55:25 +01:00
gingerBill 3ed4c3d261 asm: global dependency walker now only traverses asm template's signature 2026-10-16 14:49:59 +01:00
gingerBill 377e507449 asm: Defer checking template specs and bodies 2026-10-16 14:37:38 +01:00
gingerBill 6ff0313f8b asm: Split template checking into signature and body 2026-10-16 14:25:55 +01:00
gingerBill 9cf02f8d7e asm: Resolve constants in templates like any identifier 2026-10-16 14:20:38 +01:00
gingerBill 1bcd7076b0 Fix PBI_Flag_Bits.PC_KILL aliasing PC_SUSP 2026-10-16 12:27:29 +01:00
gingerBill 16f39cb279 Make runtime ~ on bit_sets clear bits outside set 2026-10-16 12:24:24 +01:00
gingerBill 583eafbea8 Name field bits of mixed darwin flag enums 2026-10-16 12:23:50 +01:00