kit

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

arch.h (18194B)


      1 #ifndef KIT_INTERNAL_ARCH_H
      2 #define KIT_INTERNAL_ARCH_H
      3 
      4 #include <kit/arch.h>
      5 #include <kit/compile.h>
      6 #include <kit/disasm.h>
      7 
      8 #include "abi/abi.h"
      9 #include "arch/mc.h"
     10 #include "cg/cgtarget.h"
     11 #include "core/core.h"
     12 #include "obj/obj.h"
     13 
     14 typedef struct AsmDriver AsmDriver;
     15 
     16 typedef struct ArchAsm ArchAsm;
     17 struct ArchAsm {
     18   void (*insn)(ArchAsm*, AsmDriver*, Sym mnemonic);
     19   void (*destroy)(ArchAsm*);
     20 };
     21 
     22 /* ---- Disassembler hook ----
     23  * Bytes -> records, not frontend-driven lowering, so this is a separate
     24  * hook from CgTarget/MCEmitter. The internal implementation may share
     25  * encoding tables with the per-arch backend (sequencing concern, not an
     26  * interface concern). Constructed for c->target.
     27  *
     28  * arch_disasm_decode returns the number of bytes consumed, or 0 if input
     29  * is too short or undecodable (in which case the public iterator advances
     30  * by the arch's minimum unit). ArchDisasm owns the mnemonic / operands /
     31  * annotation string buffers placed into *out; they are valid until the
     32  * next decode or arch_disasm_free, whichever comes first. */
     33 typedef struct ArchDisasm ArchDisasm;
     34 struct ArchDisasm {
     35   u32 (*decode)(ArchDisasm*, const u8* bytes, size_t len, u64 vaddr,
     36                 KitInsn* out);
     37   void (*destroy)(ArchDisasm*);
     38 };
     39 
     40 #define KIT_DECODE_MAX_OPERANDS 6u
     41 
     42 typedef enum KitDecodeFlag {
     43   KIT_DECODE_TERMINATOR = 1u << 0,
     44   KIT_DECODE_BRANCH = 1u << 1,
     45   KIT_DECODE_CALL = 1u << 2,
     46   KIT_DECODE_RET = 1u << 3,
     47   KIT_DECODE_MEMORY = 1u << 4,
     48   KIT_DECODE_TRAP = 1u << 5,
     49 } KitDecodeFlag;
     50 
     51 typedef enum KitDecodedOperandKind {
     52   KIT_DECOP_NONE,
     53   KIT_DECOP_REG,
     54   KIT_DECOP_IMM,
     55   KIT_DECOP_MEM,
     56   KIT_DECOP_PCREL,
     57   KIT_DECOP_SYSREG,
     58 } KitDecodedOperandKind;
     59 
     60 typedef struct KitDecodedOperand {
     61   u8 kind;
     62   u8 width_bits;
     63   u16 flags;
     64   u32 reg;
     65   u32 index_reg;
     66   i64 imm;
     67   u8 scale;
     68   u8 pad[7];
     69 } KitDecodedOperand;
     70 
     71 typedef struct KitDecodedInsn {
     72   u64 pc;
     73   const u8* bytes;
     74   u8 nbytes;
     75   u8 noperands;
     76   u16 flags;
     77   u32 opcode;      /* Arch-owned stable opcode id. */
     78   u32 encoding_id; /* Optional row/table id for formatting. */
     79   KitDecodedOperand operands[KIT_DECODE_MAX_OPERANDS];
     80   u64 arch[2]; /* Small arch-private payload. */
     81 } KitDecodedInsn;
     82 
     83 typedef struct ArchInsnFormatter ArchInsnFormatter;
     84 typedef struct KitCg KitCg;
     85 typedef struct EmuCPUState EmuCPUState;
     86 typedef struct EmuLiftCtx EmuLiftCtx;
     87 typedef struct EmuProcess EmuProcess;
     88 typedef struct EmuThread EmuThread;
     89 struct ArchInsnFormatter {
     90   KitStatus (*format)(ArchInsnFormatter*, const KitDecodedInsn*, KitInsn* out);
     91   void (*destroy)(ArchInsnFormatter*);
     92 };
     93 
     94 typedef struct ArchDecodeOps {
     95   u8 min_insn_len;
     96   u8 max_insn_len;
     97 
     98   KitStatus (*decode_one)(Compiler*, const u8* bytes, size_t len, u64 pc,
     99                           KitDecodedInsn* out);
    100   KitStatus (*decode_block)(Compiler*, const u8* bytes, size_t len, u64 pc,
    101                             KitDecodedInsn* out, u32 cap, u32* n_out);
    102 
    103   ArchInsnFormatter* (*formatter_new)(Compiler*);
    104   KitStatus (*format)(ArchInsnFormatter*, const KitDecodedInsn*, KitInsn* out);
    105   void (*formatter_free)(ArchInsnFormatter*);
    106 } ArchDecodeOps;
    107 
    108 typedef struct ArchEmuOps {
    109   EmuCPUState* (*cpu_new)(Compiler*, u64 initial_pc, u64 initial_sp);
    110   KitCgTypeId (*block_fn_type)(Compiler*);
    111   KitStatus (*lift_block)(Compiler*, KitCg*, const KitDecodedInsn*, u32 n,
    112                           const EmuLiftCtx*);
    113   u64 (*get_gpr)(EmuThread*, u32 reg);
    114   void (*set_gpr)(EmuThread*, u32 reg, u64 value);
    115   u64 (*get_syscall_no)(EmuThread*);
    116   u64 (*get_syscall_arg)(EmuThread*, u32 index);
    117   void (*set_syscall_result)(EmuThread*, u64 value);
    118   u64 (*get_sp)(EmuThread*);
    119   void (*set_sp)(EmuThread*, u64 value);
    120   u64 (*get_tp)(EmuThread*);
    121   void (*set_tp)(EmuThread*, u64 value);
    122   u64 (*signal_context_size)(EmuProcess*, EmuThread*);
    123   KitStatus (*save_signal_context)(EmuProcess*, EmuThread*, u8* dst, u64 size);
    124   KitStatus (*restore_signal_context)(EmuProcess*, EmuThread*, const u8* src,
    125                                       u64 size);
    126   KitStatus (*set_signal_handler_args)(EmuProcess*, EmuThread*, int signo,
    127                                        u64 siginfo, u64 ucontext);
    128   u64 (*signal_stack_align)(EmuProcess*, EmuThread*);
    129   u32 import_thunk_size;
    130   KitStatus (*emit_import_thunk)(EmuProcess*, u64 thunk_vaddr);
    131   void* (*resolve_runtime_helper)(void* emu, KitSlice name);
    132 } ArchEmuOps;
    133 
    134 typedef struct LinkArchDesc LinkArchDesc;
    135 
    136 typedef struct ArchDwarfOps {
    137   /* DWARF .debug_line minimum instruction length and maximum operations per
    138    * instruction. Fixed-width ISAs normally use their instruction width; x86_64
    139    * uses 1 because line-program PC advances are byte granular. */
    140   u8 min_inst_len;
    141   u8 max_ops_per_inst;
    142   u8 pad[2];
    143 } ArchDwarfOps;
    144 
    145 typedef struct ArchTargetFeature {
    146   const char* name;
    147 } ArchTargetFeature;
    148 
    149 #define ARCH_DBG_MAX_TRAP_BYTES 8u
    150 #define ARCH_DBG_MAX_INSN_BYTES 15u
    151 
    152 typedef struct ArchDbgInsn {
    153   u64 pc;
    154   u8 bytes[ARCH_DBG_MAX_INSN_BYTES];
    155   u32 len;
    156 } ArchDbgInsn;
    157 
    158 typedef struct ArchDbgOps {
    159   u32 min_insn_len;
    160   u32 max_insn_len;
    161 
    162   KitStatus (*breakpoint_patch)(u8* out, u32 cap, u32* len_out);
    163   u64 (*breakpoint_addr_from_fault_pc)(u64 fault_pc);
    164 
    165   KitStatus (*decode_insn)(const u8* bytes, u32 len, u64 pc, ArchDbgInsn* out);
    166   KitStatus (*build_displaced_shim)(const ArchDbgInsn* insn,
    167                                     void* scratch_write, u64 scratch_runtime,
    168                                     u32 scratch_cap, u32* sentinel_off,
    169                                     u64* fallthrough_pc);
    170   int (*is_call)(const ArchDbgInsn* insn);
    171   KitStatus (*direct_call_target)(const ArchDbgInsn* insn, u64* target_out);
    172   KitStatus (*direct_jump_target)(const ArchDbgInsn* insn, u64* target_out);
    173   KitStatus (*link_register_return_address)(const KitUnwindFrame* frame,
    174                                             u64* target_out);
    175 } ArchDbgOps;
    176 
    177 /* ---- textual-assembly operand syntax (printer <-> parser) ----------------
    178  *
    179  * How a relocated operand is spelled in `cc -S` output. The shape selects
    180  * which part of the disassembled operand text the symbolizer rewrites; the
    181  * prefix/suffix are the relocation-modifier spelling for the target object
    182  * format (e.g. aarch64 ELF `:lo12:sym` is a prefix; Mach-O `sym@PAGEOFF` is a
    183  * suffix). At most one of prefix/suffix is non-empty for a given (kind, fmt).
    184  * This is the inverse of the arch assembler's operand reloc-modifier parser. */
    185 typedef enum ArchRelocSurg {
    186   ARCH_RELOC_SURG_NONE = 0, /* not symbolizable here; keep numeric operand */
    187   ARCH_RELOC_SURG_TAIL, /* replace last comma component (or whole operand) */
    188   ARCH_RELOC_SURG_MEM,  /* rewrite the offset inside [...]  (aarch64 ldst) */
    189   ARCH_RELOC_SURG_RIP,  /* insert sym before disp(%rip)     (x86-64 RIP-rel) */
    190   /* RISC-V `%pcrel_lo`/`%lo` low-half operand. A single reloc kind covers two
    191    * disassembled shapes: a register-immediate ADDI (printed as `mv rd, rs`
    192    * when the immediate is 0) where the modifier becomes a new trailing
    193    * operand (`mv rd, rs, %pcrel_lo(L)`, which the assembler folds back into
    194    * ADDI), and a `disp(base)` load/store where the modifier replaces the
    195    * displacement (`%pcrel_lo(L)(base)`). The shape is picked from the operand
    196    * text: a trailing `(...)` group selects the memory form. */
    197   ARCH_RELOC_SURG_RV_LO12,
    198 } ArchRelocSurg;
    199 
    200 typedef struct ArchRelocOperand {
    201   ArchRelocSurg surg;
    202   const char* prefix; /* e.g. ":lo12:" (ELF); "" if none */
    203   const char* suffix; /* e.g. "@PAGEOFF" / "@GOTPCREL"; "" if none */
    204   /* Added to the relocation's stored addend before spelling `sym[+/-N]`. Undoes
    205    * an instruction-encoding bias so the printed offset is the *symbol* offset:
    206    * 0 for aarch64; +4 for x86-64 rel32 (PC32/PLT32/GOTPCREL store addend-4). */
    207   int addend_bias;
    208   /* hi/lo anchor pairing (RISC-V `%pcrel_hi`/`%pcrel_lo`). A high-half reloc
    209    * (AUIPC `%pcrel_hi(sym)`) sets `emit_anchor` so the symbolizer defines a
    210    * unique local label at this instruction. The paired low-half reloc
    211    * (`%pcrel_lo`) sets `ref_anchor`: its operand references that synthesized
    212    * anchor label (the nearest preceding anchor) instead of the reloc's own
    213    * symbol — matching the RISC-V ABI, where `%pcrel_lo` names the AUIPC's
    214    * label, not the target symbol. Other arches leave both 0. */
    215   u8 emit_anchor;
    216   u8 ref_anchor;
    217 } ArchRelocOperand;
    218 
    219 typedef struct ArchAsmOps {
    220   /* Map (reloc kind, target object format) to the operand syntax the cc -S
    221    * symbolizer must emit (and that this arch's .s parser accepts back).
    222    * Returns 1 and fills *out when the kind is symbolizable for fmt; 0
    223    * otherwise (printer keeps the numeric operand). The symbolizer picks the
    224    * surgery site from the operand text (an `(%rip)` operand always uses RIP
    225    * surgery regardless of `out->surg`), so a single reloc kind can serve both
    226    * a branch target and a RIP-relative memory operand (x86-64 R_PC32). */
    227   int (*reloc_operand)(u16 reloc_kind, KitObjFmt fmt, ArchRelocOperand* out);
    228   /* 1 if `mnemonic` is an intra-section local branch whose un-relocated
    229    * numeric target the symbolizer should replace with a synthesized label
    230    * (aarch64 b/b.cc/cbz/...; x86-64 jmp/jcc). Calls are excluded — they carry
    231    * relocations. NULL hook = no local-branch symbolization for the arch. */
    232   int (*is_local_branch)(KitSlice mnemonic);
    233   /* Fuse a relocation that the disassembler renders as a 2-instruction pair
    234    * back into a single relocated pseudo-instruction line. RISC-V R_RV_CALL
    235    * sits on an AUIPC whose JALR partner carries no reloc; the canonical `.s`
    236    * spelling is a single `call`/`tail sym`. When `kind` names such a reloc,
    237    * the hook returns 1 and sets *mnemonic_out to the fused mnemonic — the
    238    * symbolizer then emits "<mnemonic>\t<sym[+addend]>" in place of BOTH
    239    * instructions (skipping the partner). `pair_mnemonic`/`pair_ops` are the
    240    * SECOND instruction's disassembled text (the JALR), used to disambiguate
    241    * (e.g. call vs tail by its link register). Returns 0 to leave the pair
    242    * un-fused (per-instruction operand symbolization applies). NULL hook = no
    243    * pair fusion for the arch. */
    244   int (*reloc_call_pair)(u16 reloc_kind, KitSlice pair_mnemonic,
    245                          KitSlice pair_ops, const char** mnemonic_out);
    246 } ArchAsmOps;
    247 
    248 typedef struct ArchImpl {
    249   /* First field, so `(const CGBackend*)&arch_impl_x` is the arch's backend
    250    * view. Every machine-code arch is a CGBackend by composition; c_target
    251    * is a standalone CGBackend with no ArchImpl. */
    252   CGBackend backend;
    253 
    254   KitArchKind kind;
    255   const char* name;
    256 
    257   /* Low-level CgTarget constructor: caller supplies the MCEmitter. Tests use
    258    * this directly via the cgtarget_new() wrapper; the arch's `backend.make`
    259    * also calls it after creating an MCEmitter internally. */
    260   CgTarget* (*cgtarget_new)(Compiler*, ObjBuilder*, MCEmitter*);
    261   ArchAsm* (*asm_new)(Compiler*);
    262   ArchDisasm* (*disasm_new)(Compiler*);
    263   int (*apply_label_fixup)(Compiler*, const ArchLabelFixup*);
    264 
    265   const ArchDecodeOps* decode;
    266   const ArchEmuOps* emu;
    267   const LinkArchDesc* link;
    268   const ArchDwarfOps* dwarf;
    269   const ArchDbgOps* dbg;
    270   const ArchAsmOps*
    271       asm_ops; /* textual-asm operand syntax; NULL = keep numeric */
    272 
    273   const KitPredefinedMacro* predefined_macros;
    274   u32 npredefined_macros;
    275   const ArchTargetFeature* target_features;
    276   u32 ntarget_features;
    277   void (*target_feature_defaults)(const Target*, u64* words, u32 nwords);
    278   KitStatus (*target_feature_apply_isa)(const Target*, KitSlice isa, u64* words,
    279                                         u32 nwords);
    280 
    281   const char* (*register_name)(uint32_t dwarf_idx);
    282   int (*register_index)(const char* name, uint32_t* idx_out);
    283   uint32_t (*register_count)(void);
    284   int (*register_at)(uint32_t idx, KitArchReg* out);
    285 
    286   /* DWARF CFI defaults per psABI, used by the CIE the .eh_frame
    287    * producer emits. cfi_cfa_init_{reg,offset} describe the at-entry
    288    * CFA state — before any cfi_def_cfa override — so an unwinder can
    289    * recover the caller's stack pointer at the very first instruction. */
    290   u32 cfi_return_addr_reg;
    291   i32 cfi_code_align_factor;
    292   i32 cfi_data_align_factor;
    293   u32 cfi_cfa_init_reg;
    294   i32 cfi_cfa_init_offset;
    295 
    296   /* === Generic-layer capability queries =====================================
    297    * Let generic (non-backend) code in src/cg and src/link decide by capability
    298    * instead of by arch identity (target.arch == KIT_ARCH_*). Each backend
    299    * declares its answer here once. */
    300 
    301   /* Backend codegen capability bitmask (KitCgBackendFeatureFlag). Per-arch
    302    * constant: the x86 family sets UNALIGNED_MEMORY|RED_ZONE|SIMD, every other
    303    * arch sets STRICT_ALIGNMENT. Read via kit_cg_target_backend_features. */
    304   u64 backend_features;
    305 
    306   /* Largest power-of-two byte width this arch lowers as a lock-free native
    307    * atomic: 8 for aa64/x64/rv64/wasm, 4 for rv32 (no lr.d/sc.d/amo*.d). The
    308    * single source of truth for kit_cg_atomic_is_lock_free and the C front-end's
    309    * __atomic_always_lock_free. */
    310   u32 atomic_lock_free_max;
    311 
    312   /* 1 if call convention `cc` is selectable for this compiler's (arch, os).
    313    * KIT_CG_CC_TARGET_C is handled generically (always 1); INTERRUPT is 0. May
    314    * read c->target.os (a property, not arch identity). Read via
    315    * kit_cg_target_supports_call_conv. */
    316   int (*supports_call_conv)(const Compiler* c, KitCgCallConv cc);
    317 
    318   /* 1 if this arch has a legal lowering for `intrin`. Kept in sync with the
    319    * backend's IntrinKind lowering switch (x64_intrinsic / aa_intrinsic /
    320    * rv_intrinsic / wasm_intrinsic). Read via kit_cg_target_supports_intrinsic.
    321    */
    322   int (*supports_intrinsic)(const Compiler* c, KitCgIntrinsic intrin);
    323 
    324   /* Resolve & validate the float ABI for the target being constructed, given
    325    * the explicit -mabi string (`abi`, empty for none) and the already-resolved
    326    * -march feature bits (`feature_words`/`nfeature_words`). On success returns
    327    * KIT_OK with `spec->float_abi` set to the chosen KitFloatAbi. On a bad/
    328    * mismatched ABI returns KIT_INVALID and writes a NUL-terminated message into
    329    * `err` (capacity `errcap`) for the caller to surface as a diagnostic. NULL
    330    * hook means the arch has no float-ABI axis: leave spec->float_abi at
    331    * KIT_FLOAT_ABI_DEFAULT (the arch_resolve_float_abi wrapper no-ops). Set for
    332    * RISC-V (handles rv32 + rv64); read via arch_resolve_float_abi. */
    333   KitStatus (*resolve_float_abi)(const struct ArchImpl* impl,
    334                                  KitTargetSpec* spec, const u64* feature_words,
    335                                  u32 nfeature_words, KitSlice abi, char* err,
    336                                  size_t errcap);
    337 } ArchImpl;
    338 
    339 const ArchImpl* arch_lookup(KitArchKind);
    340 const ArchImpl* arch_for_compiler(const Compiler*);
    341 int arch_target_feature_index(const ArchImpl*, KitSlice name, u32* idx_out);
    342 void arch_target_feature_defaults(const ArchImpl*, const Target*, u64* words,
    343                                   u32 nwords);
    344 KitStatus arch_target_feature_apply_isa(const ArchImpl*, const Target*,
    345                                         KitSlice isa, u64* words, u32 nwords);
    346 
    347 /* Resolve & validate `spec->float_abi` from the explicit -mabi string and the
    348  * resolved -march feature bits, dispatching to `impl->resolve_float_abi`. When
    349  * `impl` is NULL or the arch sets no hook this is a no-op returning KIT_OK and
    350  * leaving spec->float_abi untouched (KIT_FLOAT_ABI_DEFAULT) — exactly the old
    351  * "non-RISC-V arches leave the float ABI at default" behavior. On a bad ABI the
    352  * hook returns KIT_INVALID and fills `err` (NUL-terminated, capacity `errcap`).
    353  * Header-only thin dispatch: the matching `arch_reloc_*` wrappers live in
    354  * src/arch/registry.c, but float-ABI resolution runs during target
    355  * construction (no Compiler yet), so this wrapper takes the ArchImpl directly.
    356  */
    357 static inline KitStatus arch_resolve_float_abi(const ArchImpl* impl,
    358                                                KitTargetSpec* spec,
    359                                                const u64* feature_words,
    360                                                u32 nfeature_words, KitSlice abi,
    361                                                char* err, size_t errcap) {
    362   if (!impl || !impl->resolve_float_abi) return KIT_OK;
    363   return impl->resolve_float_abi(impl, spec, feature_words, nfeature_words, abi,
    364                                  err, errcap);
    365 }
    366 
    367 /* Spelling for a relocated operand in `cc -S` text, for the compiler's target
    368  * arch+format. Returns 1 and fills *out when symbolizable, 0 to keep numeric
    369  * (also when the arch provides no asm_ops). Thin dispatch over ArchAsmOps. */
    370 int arch_reloc_operand(const Compiler* c, u16 reloc_kind,
    371                        ArchRelocOperand* out);
    372 
    373 /* 1 if `mnemonic` is an intra-section local branch for the compiler's target
    374  * arch (so cc -S synthesizes a label at its un-relocated target). 0 when the
    375  * arch has no asm_ops/is_local_branch hook. */
    376 int arch_is_local_branch(const Compiler* c, KitSlice mnemonic);
    377 
    378 /* 1 if `reloc_kind` names a 2-instruction call pair the symbolizer should fuse
    379  * into a single pseudo line (RISC-V R_RV_CALL -> `call`/`tail`), with
    380  * *mnemonic_out set to the fused mnemonic. `pair_*` are the partner (second)
    381  * instruction's disassembled text. 0 when not fused / no hook. Thin dispatch
    382  * over ArchAsmOps.reloc_call_pair. */
    383 int arch_reloc_call_pair(const Compiler* c, u16 reloc_kind,
    384                          KitSlice pair_mnemonic, KitSlice pair_ops,
    385                          const char** mnemonic_out);
    386 
    387 ArchDisasm* arch_disasm_new(Compiler*);
    388 u32 arch_disasm_decode(ArchDisasm*, const u8* bytes, size_t len, u64 vaddr,
    389                        KitInsn* out);
    390 void arch_disasm_free(ArchDisasm*);
    391 KitStatus arch_decode_one(Compiler*, const u8* bytes, size_t len, u64 pc,
    392                           KitDecodedInsn* out);
    393 KitStatus arch_decode_block(Compiler*, const u8* bytes, size_t len, u64 pc,
    394                             KitDecodedInsn* out, u32 cap, u32* n_out);
    395 ArchInsnFormatter* arch_insn_formatter_new(Compiler*);
    396 KitStatus arch_format_insn(ArchInsnFormatter*, const KitDecodedInsn*,
    397                            KitInsn* out);
    398 void arch_insn_formatter_free(ArchInsnFormatter*);
    399 
    400 #endif