kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit 4fe46147cf5ededdb020009c7d0b93e5842cc34c
parent fd743afc8033264bba9f456a36810a97df568326
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed, 17 Jun 2026 01:12:50 -0700

arm32 as: SP-relative add/str/ldr, ';' statement separator, __ELF__

Harden the standalone assembler for hand-written runtime asm (the kit self-built
rt's AEABI helpers + coroutine switch):
- add rd,sp,#imm / add sp,sp,#imm and word str/ldr [sp,#imm] selected the
  hi-register / low-register forms, dropping the immediate or truncating sp(r13)
  to r5. Dispatch by operand shape to the SP-relative T1/T2 encodings.
- ';' is the GNU-as statement separator (line comment is @/// on these targets,
  never ';'); a macro packing several statements onto one logical line via a ';'
  SEPARATOR depends on it. The lexer now surfaces it as a newline boundary.
- arm-none-eabi is ELF with no symbol prefix; define __ELF__ and an empty
  __USER_LABEL_PREFIX__ so compiler-rt's assembly.h takes the ELF path (.globl/
  .type %function/.size) instead of the COFF path (.def/.scl/.endef, leading _).

test-isa (314) + test-asm green; coro self-builds, AEABI advances to GNU numeric
local labels (next).

Diffstat:
Msrc/arch/arm32/arch.c | 6++++++
Msrc/arch/arm32/asm.c | 23+++++++++++++++++++++++
Msrc/asm/asm_lex.c | 14++++++++++++++
3 files changed, 43 insertions(+), 0 deletions(-)

diff --git a/src/arch/arm32/arch.c b/src/arch/arm32/arch.c @@ -117,6 +117,12 @@ static int arm32_apply_label_fixup(Compiler* c, const ArchLabelFixup* fx) { static const KitPredefinedMacro arm32_predefined_macros[] = { {KIT_SLICE_LIT("__arm__"), KIT_SLICE_LIT("1")}, {KIT_SLICE_LIT("__ARMEL__"), KIT_SLICE_LIT("1")}, + /* arm-none-eabi is an ELF target with no symbol-name prefix; gcc/clang + * define both. Without them, hand-written runtime asm that branches on the + * object format (compiler-rt's assembly.h) falls through to the COFF path + * (`.def/.scl/.endef`, a leading `_`). __USER_LABEL_PREFIX__ is empty. */ + {KIT_SLICE_LIT("__ELF__"), KIT_SLICE_LIT("1")}, + {KIT_SLICE_LIT("__USER_LABEL_PREFIX__"), KIT_SLICE_LIT("")}, {KIT_SLICE_LIT("__thumb__"), KIT_SLICE_LIT("1")}, {KIT_SLICE_LIT("__thumb2__"), KIT_SLICE_LIT("1")}, {KIT_SLICE_LIT("__THUMBEL__"), KIT_SLICE_LIT("1")}, diff --git a/src/arch/arm32/asm.c b/src/arch/arm32/asm.c @@ -746,6 +746,20 @@ static void assemble_one(AsmDriver* d, const Arm32InsnDesc* desc, u32 cond) { return; } rm = parse_reg(d); + /* `add rd, sp, #imm` / `add sp, sp, #imm` (a third immediate operand + * through SP) use the SP-relative encodings: ADD rd,sp,#imm8*4 (T1, + * 0xA800, rd<=r7) or ADD sp,sp,#imm7*4 (T2, 0xB000). The hi-register form + * below has no immediate and would drop it. */ + if (slice_eq_cstr(mn, "add") && rm == 13u && asm_driver_eat_comma(d)) { + u32 imm = (u32)parse_imm(d); + if (rdn == 13u) + emit_t16(d, (u16)(0xb000u | ((imm / 4u) & 0x7fu))); + else if (rdn <= 7u) + emit_t16(d, (u16)(0xa800u | ((rdn & 7u) << 8) | ((imm / 4u) & 0xffu))); + else + asm_driver_panic(d, "arm32 asm: add sp immediate needs add.w"); + return; + } emit_t16(d, (u16)((desc->match & 0xff00u) | (((rdn >> 3) & 1u) << 7) | ((rm & 0xfu) << 3) | (rdn & 7u))); return; @@ -756,6 +770,15 @@ static void assemble_one(AsmDriver* d, const Arm32InsnDesc* desc, u32 cond) { u32 op, scale, imm5; expect_comma(d); parse_mem(d, &base, &disp); + /* A word str/ldr through SP uses the dedicated SP-relative T2 encoding + * (0x9000/0x9800, imm8*4); the low-register T1 form here would truncate + * sp(r13) to r5. (strb/strh/ldrb/ldrh have no SP T2 form — they would need + * the 32-bit T3, not yet emitted from this 16-bit handler.) */ + if (base == 13u && (slice_eq_cstr(mn, "str") || slice_eq_cstr(mn, "ldr"))) { + u32 sp_op = slice_eq_cstr(mn, "ldr") ? 0x9800u : 0x9000u; + emit_t16(d, (u16)(sp_op | ((rt & 7u) << 8) | (((u32)disp / 4u) & 0xffu))); + return; + } op = (desc->match >> 11) & 0x1fu; scale = (op <= 0x0du) ? 4u : (op <= 0x0fu) ? 1u : 2u; imm5 = ((u32)disp / scale) & 0x1fu; diff --git a/src/asm/asm_lex.c b/src/asm/asm_lex.c @@ -312,6 +312,20 @@ AsmTok asm_lex_next(AsmLexer* l) { l->had_space = 0; return t; } + /* `;` is the GNU-as statement separator (the line comment on these targets + * is `@`/`//`/`#`, never `;`). A macro that packs several directives/labels/ + * instructions onto one logical line via a `;` SEPARATOR (e.g. compiler-rt's + * DEFINE_AEABI_*) depends on this — surface it as a newline boundary so each + * statement is parsed on its own. */ + if (peek(l, 0) == ';') { + tloc = asm_lex_here(l); + bump(l); + t.kind = ASM_TOK_NEWLINE; + t.loc = tloc; + l->at_bol = 1; + l->had_space = 0; + return t; + } break; }