kit

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

mc.h (9615B)


      1 #ifndef KIT_INTERNAL_ARCH_MC_H
      2 #define KIT_INTERNAL_ARCH_MC_H
      3 
      4 #include <kit/compile.h>
      5 
      6 #include "cg/cgir.h"
      7 #include "core/core.h"
      8 #include "obj/obj.h"
      9 
     10 /* Machine-code / object emission interface. One generic implementation in
     11  * src/arch/mc.c serves every machine-code arch; arch-specific behavior enters
     12  * only via ArchImpl.apply_label_fixup (label reloc encoding) and the
     13  * ArchImpl.cfi_* constants (eh_frame CIE defaults). Pulled out of arch.h so
     14  * the many emission-only consumers (per-arch emit/ops/alloc TUs, the
     15  * assembler, the Debug producer) don't transitively depend on the
     16  * decode/disasm/emu/dbg surfaces. */
     17 
     18 /* Forward-declared so CgTarget can carry an optional Debug* without
     19  * pulling debug/debug.h into every translation unit that includes this
     20  * header. Per doc/DWARF.md §3.2 the backend gets exactly one new dependency
     21  * on Debug: this forward decl plus debug_emit_row (declared by the few
     22  * backend TUs that actually emit line rows). */
     23 typedef struct Debug Debug;
     24 /* mc.h speaks the CG operand vocabulary (cgir.h) and only passes an opaque
     25  * CgTarget* through arch_lower_indexed, so it forward-declares the contract
     26  * rather than depending on cgtarget.h. */
     27 typedef struct CgTarget CgTarget;
     28 
     29 /* Native-only register id. The semantic CgTarget surface uses CGLocal; this
     30  * remains here for MC/native helpers and disabled native backends. */
     31 typedef u32 Reg;
     32 #define REG_NONE 0xffffffffu
     33 
     34 typedef u32 MCLabel;
     35 #define MC_LABEL_NONE 0u
     36 
     37 typedef struct ArchLabelFixup {
     38   ObjBuilder* obj;
     39   u32 sec_id;
     40   u32 offset;
     41   u32 width;
     42   RelocKind kind;
     43   i64 disp;
     44   ObjSymId cur_func_sym;
     45   u32 cur_func_start;
     46 } ArchLabelFixup;
     47 
     48 typedef struct MCEmitter MCEmitter;
     49 struct MCEmitter {
     50   /* Machine/object emission context. Subclasses extend. */
     51   Compiler* c;
     52   ObjBuilder* obj;
     53   u32 section_id;
     54   /* Byte buffer of the active section, resolved once per set_section so the
     55    * per-instruction emit_bytes path skips obj_write's Sections_at deref +
     56    * nobits branch. NULL when the active section is NOBITS/.bss (or none): emit
     57    * then falls back to obj_write so bss_size accounting still runs. The pointer
     58    * is stable across emits (SegVec elements don't move); only set_section
     59    * re-points it. */
     60   Buf* cur_bytes;
     61 
     62   /* Typed-store fast-path cursor into the active section's tail chunk. The
     63    * per-instruction emit_word path (mc_emit32) writes a fixed-width word
     64    * straight through `cur` and bumps `cur_chunk->used` + `cur_bytes->total`
     65    * inline, so the Buf stays coherent at all times — no deferred sync, no
     66    * libc memcpy call. `cur` always equals `cur_chunk->data + cur_chunk->used`
     67    * and `cur_end` is `cur_chunk->data + cur_chunk->cap`; the cursor is
     68    * (re)pointed from cur_bytes->tail by mc_cursor_repoint() at set_section and
     69    * after any non-cursor write (mc_emit_bytes / fills) that may have grown or
     70    * replaced the tail chunk. All three are NULL/empty when the active section
     71    * is NOBITS/.bss (or none): emit then falls to obj_write via mc_emit_bytes.
     72    * Because used/total stay coherent, every random-access reader (obj_pos,
     73    * buf_patch, mc_pos, arch-direct obj_pos/obj_patch) sees the live frontier
     74    * without any explicit sync. */
     75   BufChunk* cur_chunk;
     76   u8* cur;
     77   u8* cur_end;
     78 
     79   /* Pending source location, updated by set_loc. Promoted to the base so
     80    * arch backends' emit-bytes choke point can read it without reaching
     81    * into the per-arch impl (used to feed debug_emit_row). */
     82   SrcLoc loc;
     83 
     84   /* Optional Debug producer. NULL means -g is off and the per-instruction
     85    * line-row fanout is skipped. Set after construction by cg_new (or by
     86    * the cg_test harness, which is the parser stand-in). Per doc/DWARF.md
     87    * §3.2 this is the backend's only new dependency on Debug. */
     88   Debug* debug;
     89 
     90   /* Currently active function. Backends manage these via the
     91    * mc_begin_function / mc_end_function helpers from their func_begin /
     92    * func_end once they've computed the post-alignment function start
     93    * position. emit_label_data_reloc reads them to compute reloc
     94    * addends that resolve to the runtime address of an intra-function
     95    * label. */
     96   ObjSymId cur_func_sym;
     97   u32 cur_func_section;
     98   u32 cur_func_start;
     99 };
    100 
    101 /* MCEmitter has exactly one implementation (this file), so its operations are
    102  * plain extern functions, not a per-instance vtable: the arch backends call
    103  * these mc_* functions directly. (They are deliberately NOT `static inline`:
    104  * inlining the hot emit_bytes/pos into the per-arch leaf emitters bloats them
    105  * enough to break the inliner's own cascade — measured a net +21M instructions
    106  * on sqlite -c — so a direct call to an out-of-line body is the win, removing
    107  * the indirect call + fn-ptr load without disturbing leaf inlining.) */
    108 
    109 /* Construct the right target/emitter pair for c->target. */
    110 MCEmitter* mc_new(Compiler*, ObjBuilder*);
    111 void mc_free(MCEmitter*);
    112 
    113 /* Append machine-code bytes to the active section (the hot emit path). */
    114 void mc_emit_bytes(MCEmitter*, const u8* data, size_t n);
    115 /* Append one little-endian 4-byte instruction word to the active section.
    116  * Typed-store fast path: const-width store straight through the section
    117  * cursor (no u8[4] staging, no out-of-line memcpy call). Fixed-width-insn
    118  * arches (aarch64, riscv64) call this from their leaf emitter instead of
    119  * staging a word into u8[4] + mc_emit_bytes. Variable-width arches (x86-64)
    120  * keep using mc_emit_bytes. Keeps the Buf coherent, so any random-access
    121  * reader still sees the live frontier. Falls to the buf_write path when the
    122  * tail chunk can't hold the word or the section is NOBITS. */
    123 void mc_emit32(MCEmitter*, u32 word);
    124 /* Current byte offset within the active section. */
    125 u32 mc_pos(MCEmitter*);
    126 /* Stamp the pending source location read by the per-arch emit choke point. */
    127 void mc_set_loc(MCEmitter*, SrcLoc);
    128 
    129 void mc_set_section(MCEmitter*, u32 section_id);
    130 MCLabel mc_label_new(MCEmitter*);
    131 void mc_label_place(MCEmitter*, MCLabel);
    132 void mc_emit_fill(MCEmitter*, size_t n, u8 byte);
    133 void mc_emit_align(MCEmitter*, u32 align, u8 fill);
    134 void mc_emit_reloc(MCEmitter*, RelocKind, ObjSymId, i64 addend);
    135 void mc_emit_reloc_at(MCEmitter*, u32 section_id, u32 offset, RelocKind, ObjSymId,
    136                       i64 addend, int explicit_addend, int pair);
    137 void mc_emit_label_ref(MCEmitter*, MCLabel, RelocKind, u32 width, i64 addend);
    138 /* Emit a relocation at (data_sec, data_offset) that resolves at link time to
    139  * the runtime address of `label` (an intra-function code label). Generated
    140  * against the active function symbol; emitted immediately if `label` is placed,
    141  * else queued and emitted at label_place. Requires an active function. */
    142 void mc_emit_label_data_reloc(MCEmitter*, u32 data_sec, u32 data_offset,
    143                               MCLabel label, RelocKind kind, u32 width,
    144                               i64 extra_addend);
    145 /* ---- CFI / unwind ---- buffered per-function, emitted into .eh_frame at TU
    146  * finalize; byte-position-bound so they live on MCEmitter. Discarded when the
    147  * CG was constructed with Debug=NULL. */
    148 void mc_cfi_startproc(MCEmitter*);
    149 void mc_cfi_endproc(MCEmitter*);
    150 void mc_cfi_def_cfa(MCEmitter*, u32 reg, i32 ofs);
    151 void mc_cfi_offset(MCEmitter*, u32 reg, i32 ofs);
    152 /* Override the PC offset used by the *next* cfi_* directive (sticky until
    153  * cfi_endproc) — for backends that emit the CFI batch in func_end. */
    154 void mc_cfi_set_next_pc_offset(MCEmitter*, u32 pc_offset);
    155 
    156 /* Lazily mint (and return) a per-label SB_LOCAL symbol defined at `label`'s
    157  * placement. Backends use this to reference a code location relocatably —
    158  * `&&label` address-takes emit a PC-relative reloc against it instead of baking
    159  * a fixed displacement, so a re-encoding assembler (clang) recomputes the right
    160  * address. Forward-ref safe: if `label` is not yet placed the symbol is created
    161  * undefined and defined in label_place. */
    162 ObjSymId mc_label_symbol(MCEmitter*, MCLabel label);
    163 
    164 /* Per-function context helpers. Backends call mc_begin_function from
    165  * their CgTarget func_begin (after computing the post-alignment function
    166  * start) and mc_end_function from func_end. The pair sets / clears
    167  * MCEmitter.cur_func_* — the metadata that emit_label_data_reloc reads
    168  * to resolve deferred intra-function label fixups in data sections. */
    169 void mc_begin_function(MCEmitter*, ObjSymId sym, u32 section_id,
    170                        u32 start_offset);
    171 void mc_end_function(MCEmitter*);
    172 
    173 /* Flush buffered CFI state into a .eh_frame section in the ObjBuilder.
    174  * No-op when no functions called cfi_startproc. Idempotent. */
    175 void mc_emit_eh_frame(MCEmitter*);
    176 
    177 /* Construct the MCEmitter + (optionally) Debug pair that a machine-code
    178  * CGBackend's `make` typically needs. On success, sets *out_mc to a fresh
    179  * MCEmitter; sets *out_debug to a Debug producer (and wires mc->debug) when
    180  * opts->debug_info is true, else NULL. On allocation failure returns
    181  * KIT_NOMEM with both outputs left NULL and any partial state cleaned up.
    182  * c_target's backend ignores this and does not create either. */
    183 KitStatus cg_mc_debug_new(Compiler*, ObjBuilder*, const KitCodeOptions*,
    184                           MCEmitter** out_mc, Debug** out_debug);
    185 
    186 /* Helper for backends without a native indexed addressing mode. If addr has
    187  * an index (addr.v.ind.index != REG_NONE), materializes
    188  * base + (index << log2_scale) into `scratch` and returns a plain
    189  * OPK_INDIRECT(scratch, ofs). Otherwise returns `addr` unchanged. The caller
    190  * supplies the scratch register from its scratch pool. */
    191 Operand arch_lower_indexed(CgTarget*, Operand addr, Reg scratch);
    192 
    193 #endif