Branch: slice/S.8c. Commit only there; never to trunk.
Needs: S.8a (the tree arena) and S.8b (the microbit machine) merged.
Read first:
docs/phases/S-small-targets.md, slice S.8.docs/tasks/S.8a.mdandsrc/glint/shell/tree.cr: the nodes.docs/tasks/S.8b.mdandspec/qemu/microbit_spec.cr: how a raw image is run and how semihosting ends it.src/glint/shell/matcher.cr: the no-heap style that image code keeps.
Claim. emit turns a tree of S.8a's nodes into Cortex-M0+ (ARMv6-M,
Thumb) code that computes the same Int32 Crystal computes, raising on
overflow as Crystal does, and the code runs on QEMU's Cortex-M0.
Passage. The maintainer: "an emit method that walks the ast to
generate m0+ code that can be saved, loaded and run for fast performance."
Make:
src/glint/shell/thumb.cr, an encoder for the ARMv6-M instructionsemitand the harness use, each writing 16-bit halfwords (32 forBL) little-endian into a caller's buffer. Only ARMv6-M encodings: no Thumb-2 instructions such asMOVW,CBZ,ITor wideLDR.src/glint/shell/emit.cr,Emit.function(arena, root, out : UInt8*, capacity : Int32, base : UInt32, overflow : UInt32) : Int32. It writes one function (BL-callable, result inr0, up to 8 local slots in its stack frame) and returns its size in bytes, or a negative status:FULL(buffer too small),RANGE(a branch can't reach),BAD_TREE. No heap, no exceptions, no constants ofStaticArray.- What Crystal means, kept:
ADDandSUBcheck signed overflow (V flag) and branch to theoverflowaddress, as Crystal's+and-raiseOverflowError.LTgives a Bool in Crystal, so it may only be the condition ofIForWHILE; anywhere else isBAD_TREE.WHILEgivesnil, so it may only be the first child ofSEQ.IFneeds an else in this first set (otherwise its value can benil);c = NONEisBAD_TREE.LOCALof a slot never assigned earlier in evaluation order isBAD_TREE(Crystal would reject it as undefined).- Every Int32 literal, including negative values and
Int32::MIN, with no literal pool unless you report why one is simpler.
- Conditional branches reach ±2 KiB: an inverted short branch over an
unconditional
B. Beyond that,RANGE. spec/support/thumb_image.crbuilds a raw flash image with the same encoder: vector table (SP 0x20004000; reset; HardFault to a stub that writeshardfaultand exits 2), a harness that calls the function withBL, writesr0as 8 lowercase hex digits and a newline withSYS_WRITE0, and exits 0; and the overflow stub, which writesoverflowand exits 1.spec/shell/emit_spec.cr:- Encoder cases:
MOVS r0, #42is2a 20;ADDS r0, r1, r0is08 18;SUBS r0, r1, r0is08 1a;CMP r1, r0is81 42;BKPT 0xabisab be;PUSH {lr}is00 b5;POP {pc}is00 bd. These are written from the ARMv6-M manual by the reviewer, not yet checked by a disassembler, because x86-1's LLVM has no ARM target; if QEMU disagrees, say so. - Each program below is built in an
Arena, emitted, run under-M microbit, and its output and exit status checked. The spec keeps each program's Crystal text beside it:
Crystal Output Exit 420000002a0 -7fffffff90 21474836477fffffff0 -2147483648800000000 x = 5; y = x + 3; y - 10fffffffe0 x = 0; while x < 10; x = x + 3; end; x0000000c0 if 3 < 2; 1; else; 2; end000000020 x = 0; i = 0; while i < 1000; x = x + i; i = i + 1; end; x00079f2c0 x = 2147483647; x + 1overflow1 x = -2147483648; x - 1overflow1 - Each
BAD_TREErule above,FULLfor a one-byte buffer, andRANGEfor aWHILEwhose body emits more than 2 KiB (use a larger arena).
- Encoder cases:
Size: report
emit.crandthumb.crlines, and the bytes emitted for the 1000-iteration program.
Constraints. emit.cr and thumb.cr compile into image code later, so
they follow S.8a's constraints. The harness and specs may use the standard
library. Nothing in the image requires them yet. crystal tool format
--check passes.