kit

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

native_asm.h (9421B)


      1 #ifndef KIT_CG_NATIVE_ASM_H
      2 #define KIT_CG_NATIVE_ASM_H
      3 
      4 /* Shared, target-neutral native helpers that touch only the base NativeTarget
      5  * (its Compiler and MCEmitter): the file-scope-asm assembler entry and the
      6  * eh-frame finalize step. Both were byte-identical across the aa64/rv64/x64
      7  * backends, so they live here as the single source of truth; each backend's
      8  * vtable points its file_scope_asm / finalize slots straight at these. */
      9 
     10 #include "arch/native_target.h"
     11 
     12 /* Top-level __asm__("...") — assemble through the generic .s parser, which
     13  * dispatches instruction lines to the active arch asm driver and handles
     14  * directives (.data/.word/.globl/.text/...) itself. */
     15 void native_file_scope_asm(NativeTarget* t, const char* src, size_t len);
     16 
     17 /* Emit the function's eh-frame (CFI) once code emission is complete. */
     18 void native_finalize(NativeTarget* t);
     19 
     20 /* ---- Inline-asm constraint-string helpers ----
     21  *
     22  * Pure, target-neutral parsing of a GCC-style operand constraint string.
     23  * Definitions wrap the shared public inline helpers in kit/asm_constraints.h so
     24  * frontend, CG, native, and test code all use the same grammar.
     25  */
     26 
     27 /* Skip the leading modifier flags ('=', '+', '&', and the '=&' early-clobber
     28  * pair) and return a pointer to the constraint body (e.g. "r", "w", "m", "i",
     29  * or a matching-constraint digit run). Returns "" for a NULL string. */
     30 const char* native_asm_constraint_body(const char* s);
     31 
     32 /* Whether the constraint marks an early-clobber output ('=&' or a leading
     33  * '&'). */
     34 int native_asm_constraint_early(const char* s);
     35 
     36 /* For a matching constraint (a leading decimal digit run naming an output
     37  * operand index), return that index; -1 when the constraint is not a matching
     38  * constraint. */
     39 int native_asm_match_index(const char* s);
     40 
     41 /* Expand the arch-neutral clobber-ABI sets (KitCgAsmClobberAbiSet bits) into
     42  * this target's live per-class caller/callee-saved register masks. */
     43 void native_asm_abi_clobber_masks(NativeTarget* t, u32 abi_sets, u32* int_mask,
     44                                   u32* fp_mask);
     45 
     46 typedef void (*NativeAsmClobberMasksFn)(Compiler*, SrcLoc, const Sym*, u32,
     47                                         u32*, u32*);
     48 typedef int (*NativeAsmCalleeSavedRegFn)(NativeTarget*, NativeAllocClass, Reg);
     49 
     50 /* Merge allocator-used and inline-asm-clobbered callee-saved registers for a
     51  * known frame. Target parsing remains behind clobber_masks; is_callee_saved
     52  * defines the live ABI's preservable register set and excludes structural
     53  * registers such as the frame pointer. */
     54 u32 native_asm_known_callee_saves(
     55     NativeTarget* t, SrcLoc loc, const NativeKnownFrameDesc* frame, u32* out,
     56     u32 cap, NativeAsmClobberMasksFn clobber_masks,
     57     NativeAsmCalleeSavedRegFn is_callee_saved);
     58 
     59 typedef enum NativeAsmRegPinStatus {
     60   NATIVE_ASM_REG_PIN_ABSENT = 0,
     61   NATIVE_ASM_REG_PIN_OK = 1,
     62   NATIVE_ASM_REG_PIN_UNKNOWN = -1,
     63   NATIVE_ASM_REG_PIN_FORBIDDEN = -2,
     64   NATIVE_ASM_REG_PIN_BAD_CONSTRAINT = -3,
     65   NATIVE_ASM_REG_PIN_CLASS_MISMATCH = -4,
     66   NATIVE_ASM_REG_PIN_FIXED_MISMATCH = -5,
     67 } NativeAsmRegPinStatus;
     68 
     69 typedef struct NativeAsmRegPin {
     70   Reg reg;
     71   NativeAllocClass cls;
     72 } NativeAsmRegPin;
     73 
     74 typedef struct NativeAsmConstraintInfo {
     75   NativeAllocClass cls;
     76   Reg fixed_reg; /* REG_NONE when the constraint only names a register class. */
     77   u32 allowed_mask; /* 0 means any valid register in cls. */
     78 } NativeAsmConstraintInfo;
     79 
     80 int native_asm_constraint_reg_info(NativeTarget* t, const char* constraint,
     81                                    NativeAsmConstraintInfo* out);
     82 int native_asm_constraint_is_reg(NativeTarget* t, const char* constraint);
     83 
     84 /* Resolve and validate an inline-asm operand's explicit hard-register pin
     85  * (AsmConstraint.reg, from a GNU local register variable). Distinguishes no pin
     86  * from invalid pins, and verifies that the operand uses a register constraint
     87  * of the matching target class. */
     88 NativeAsmRegPinStatus native_asm_resolve_pin(NativeTarget* t, Sym reg,
     89                                              const char* constraint,
     90                                              NativeAsmRegPin* out);
     91 const char* native_asm_pin_status_message(NativeAsmRegPinStatus st);
     92 int native_asm_constraint_reg_class(const char* constraint,
     93                                     NativeAllocClass* cls_out);
     94 
     95 /* ---- Direct (-O0) inline-asm operand binding ----
     96  *
     97  * The direct CgTarget path lowers an inline-asm block by self-allocating a
     98  * register/memory location for every operand, saving/restoring the
     99  * callee-saved registers the asm clobbers, pre-loading INOUT outputs and all
    100  * inputs into their bound registers, running the template, then storing the
    101  * register outputs back. That orchestration is arch-neutral: it was a
    102  * ~170-line near-identical copy in each of the aa64/x64/rv64 backends. It lives
    103  * here as the single source of truth, parameterized over the genuinely
    104  * arch-specific bits via NativeAsmDirectHooks; each backend's
    105  * *_direct_asm_block is a thin wrapper that supplies the hooks.
    106  *
    107  * The shared driver depends on NativeDirectTarget only through pointers, so the
    108  * full struct is needed at the call site (the .c includes it), not here. */
    109 typedef struct NativeDirectTarget NativeDirectTarget;
    110 
    111 typedef struct NativeAsmDirectHooks {
    112   /* Arch inline-operand discriminators: Operand.kind for a bound register
    113    * pseudo-operand, and the Operand.pad[0] value marking the FP class. */
    114   u8 opk_reg;
    115   u8 opcls_fp;
    116   u8 pad[2];
    117 
    118   /* Panic with the arch's inline-asm message prefix. Does not return. */
    119   void (*panic)(NativeDirectTarget* d, const char* msg);
    120   /* Build the arch's bound register / memory pseudo-operand. */
    121   void (*bound_reg)(Operand* out, KitCgTypeId type, NativeAllocClass cls,
    122                     Reg reg);
    123   void (*bound_mem)(Operand* out, KitCgTypeId type, Reg base);
    124   /* Parse the asm clobber list into per-class register masks (arch register
    125    * naming). */
    126   void (*clobber_masks)(Compiler* c, SrcLoc loc, const Sym* clobbers, u32 nclob,
    127                         u32* int_mask, u32* fp_mask);
    128   /* Spill the callee-saved registers the asm clobbers or the direct binder
    129    * uses for operand/address staging, returning an opaque, block-lived save
    130    * record plus its element count. restore_one undoes element idx; the driver
    131    * replays them in reverse. */
    132   void* (*save_callee_clobbers)(NativeDirectTarget* d, u32 int_mask,
    133                                 u32 fp_mask, u32* nsaved_out);
    134   void (*restore_one)(NativeDirectTarget* d, void* saved, u32 idx);
    135   /* Load a semantic operand's value / address into a register, or store a
    136    * register back to a semantic operand. */
    137   void (*load_operand_to_reg)(NativeDirectTarget* d, Operand op, NativeLoc dst);
    138   void (*load_address_to_reg)(NativeDirectTarget* d, Operand op, NativeLoc dst);
    139   void (*store_reg_to_operand)(NativeDirectTarget* d, Operand op,
    140                                NativeLoc src);
    141   /* Open the arch asm assembler, bind the resolved operands, run the template,
    142    * and close — emitting the asm body into the target's MCEmitter. */
    143   void (*run_template)(NativeDirectTarget* d, const char* tmpl,
    144                        const AsmConstraint* outs, u32 nout, Operand* bound_outs,
    145                        const AsmConstraint* ins, u32 nin, Operand* bound_ins,
    146                        const Sym* clobbers, u32 nclob);
    147 } NativeAsmDirectHooks;
    148 
    149 /* Drive the arch-neutral direct inline-asm orchestration: out-loop / in-loop /
    150  * save / load / run / store / restore. Behavior is identical to the former
    151  * per-arch *_direct_asm_block bodies; the hooks supply every arch-specific
    152  * step. */
    153 void native_asm_bind_direct_operands(NativeDirectTarget* d, const char* tmpl,
    154                                      const AsmConstraint* outs, u32 nout,
    155                                      Operand* out_ops, const AsmConstraint* ins,
    156                                      u32 nin, const Operand* in_ops,
    157                                      const Sym* clobbers, u32 nclob,
    158                                      u32 clobber_abi_sets,
    159                                      const NativeAsmDirectHooks* h);
    160 
    161 /* ---- Optimized NativeTarget inline-asm binding ----
    162  *
    163  * The optimizer's native emitter owns register placement, staging, and output
    164  * writeback. This shared driver is deliberately narrower: it validates and
    165  * binds the concrete register/immediate locations supplied by the emitter,
    166  * materializes memory-operand bases through the backend, and runs the arch
    167  * assembler. */
    168 typedef struct NativeAsmNativeHooks {
    169   void (*panic)(NativeTarget* t, SrcLoc loc, const char* msg);
    170   void (*bound_reg)(Operand* out, KitCgTypeId type, NativeAllocClass cls,
    171                     Reg reg);
    172   void (*bound_mem)(Operand* out, KitCgTypeId type, Reg base);
    173   Reg (*mem_base)(NativeTarget* t, SrcLoc loc, NativeLoc src, u32* ntmp);
    174   void (*run_template)(NativeTarget* t, const char* tmpl,
    175                        const AsmConstraint* outs, u32 nout, Operand* bound_outs,
    176                        const AsmConstraint* ins, u32 nin, Operand* bound_ins,
    177                        const Sym* clobbers, u32 nclob);
    178 } NativeAsmNativeHooks;
    179 
    180 void native_asm_bind_native_operands(
    181     NativeTarget* t, SrcLoc loc, const char* tmpl, const AsmConstraint* outs,
    182     u32 nout, NativeLoc* out_locs, const AsmConstraint* ins, u32 nin,
    183     const NativeLoc* in_locs, const Sym* clobbers, u32 nclob,
    184     const NativeAsmNativeHooks* h);
    185 
    186 #endif