Class Thumb2AsmBackend

java.lang.Object
io.github.jabrena.juno.backend.Thumb2AsmBackend

public final class Thumb2AsmBackend extends Object
Emits GNU ARM Thumb-2 assembly straight from Juno IR, restricted to instructions common to the UNO R4's Cortex-M4 and the UNO Q's Cortex-M33, so one code generator serves every board; per-core runtime differences live in CoreRuntime. Every reachable method becomes its own function (real bl calls between them, full AAPCS parameter passing including stack-passed arguments beyond the first four), with branches, switch, int arithmetic/comparisons, fixed-size arrays, arena-allocated objects with fields, mutable static fields, GPIO/delay, LedMatrix, Serial, Mouse, Wifi, HttpClient/HttpsClient, Json, compiler-generated constant arrays used by enums, and long/float/double support. Calls Juno can't statically resolve, and instruction kinds with no codegen yet fail loudly with CompileException rather than emitting code that looks plausible but was never checked.

long/float/double

64-bit values don't fit a single register and the generated assembly keeps float values in core registers (never VFP registers), so — consistent with this class's existing shim-delegation philosophy — every nontrivial long/float/double operation (arithmetic, comparison, conversion) is a call to a small extern "C" runtime-shim function (see ShimLibraries's long/float/double helpers) rather than hand-rolled soft-float assembly. A long keeps this backend's existing split-low/high-word representation (two ordinary int32 stack slots); float/double are new JunoType.FLOAT32/JunoType.FLOAT64 stack slots (4/8 bytes, holding the raw IEEE-754 bit pattern) — see FrameLayout. Each JVM local slot is one 32-bit word; long/double use the two consecutive slots reserved by the JVMS.

HttpClient/HttpsClient/Json

Backed by an extern "C" HTTP/1.1 codec and allocation-free JSON scanner in the generated runtime shim. A call needing more than four argument words (e.g. Json.getString's five) spills the overflow onto a transient stack area via AsmEmitter#emitShimCall, the same mechanism emitCall(StringBuilder, FrameLayout, IrInstruction.Call) already uses for user methods.

Storage model

Unlike a register-allocating backend, every live IR Value and every JVM local variable slot lives in a stack-frame offset (never in a register across instructions) — see FrameLayout. Non-overlapping block-local value lifetimes reuse offsets. This trades code density for a design that can't run out of registers regardless of how many live values a method has, which matters once methods stop being one-block-with-three-intrinsic-calls (helper methods with many int parameters, or LedMatrixAsciiScroll, whose full-ASCII-font dispatch tree reaches 116 methods). Registers are used only as scratch within a single instruction's codegen. Arena objects and mutable static fields follow the same principle, without needing any class-file metadata this backend doesn't already have: an object's fields are exactly whichever ones some LoadField/StoreField in the whole program actually touches, laid out in FieldRef.displayName() order; a static field gets one .bss slot, addressed via ldr Rd,=symbol.

Runtime shim

Some things the generated code calls aren't free C functions Juno can reach directly: ArduinoLEDMatrix and Serial (C++-only objects — ArduinoLEDMatrix's methods are inline-only with private timer/frame state, and Serial is a HardwareSerial instance with virtual dispatch; neither has a stable symbol to call without knowing its private layout or vtable), and the arena allocator/panic handler (simple, but writing a bump-pointer allocator directly in hand-rolled assembly buys nothing over calling a five-line C function). generate(IrProgram) returns a small extern "C" shim with plain wrapper functions for these — compiled once as ordinary C++, so the generated assembly only ever calls plain functions taking/returning plain ints, never a mangled name or a this pointer.

The output is a .S file (plus the shim .cpp, always present) meant to sit alongside a tiny .ino wrapper that declares the entry point's generated function extern "C" and calls it from setup().

Structure

This class drives code generation — function labels, prologues/epilogues, calls between generated methods, control flow, and the per-instruction dispatch — and delegates the rest to package-private collaborators: ProgramLayout (object/field/static/literal storage and the data sections), AsmEmitter (stack-slot moves, immediates, AAPCS shim calls, labels), IntArithmeticLowering, WideArithmeticLowering, ArrayLowering, IntrinsicLowering (a table of intrinsic-to-shim-call lowerings), and RuntimeShim (the C++ shim, assembled from the ShimFeatures the lowerings recorded).
  • Constructor Details

    • Thumb2AsmBackend

      public Thumb2AsmBackend()
    • Thumb2AsmBackend

      public Thumb2AsmBackend(boolean gcLoggingEnabled)
    • Thumb2AsmBackend

      public Thumb2AsmBackend(boolean gcLoggingEnabled, Board board)
      Parameters:
      gcLoggingEnabled - when true, juno_gc_collect() in the generated runtime shim prints one Serial line per collection (arena bytes used before/after), letting juno:monitor show reclamation happening in real time. false (the default) costs zero extra flash/RAM/time: the print statements aren't emitted at all, not merely disabled at runtime.
      board - the target board; its Arduino core selects the CoreRuntime glue, and its capabilities gate optional shim blocks
    • Thumb2AsmBackend

      public Thumb2AsmBackend(boolean gcLoggingEnabled, Board board, RuntimeConfig runtimeConfig)
      Parameters:
      runtimeConfig - the arena and task-stack capacities of the generated runtime shim
  • Method Details