orgnaizing, improving asm annotation

This commit is contained in:
ed
2026-06-14 17:27:52 -04:00
parent 0040f6326e
commit 2c3d0c4af7
5 changed files with 223 additions and 203 deletions
+4
View File
@@ -33,6 +33,7 @@
#define align_(value) __attribute__((aligned (value))) // for easy alignment #define align_(value) __attribute__((aligned (value))) // for easy alignment
#define C_(type,data) ((type)(data)) // for enforced precedence #define C_(type,data) ((type)(data)) // for enforced precedence
#define expect_(x, y) __builtin_expect(x, y) // so compiler knows the common path #define expect_(x, y) __builtin_expect(x, y) // so compiler knows the common path
#define cexpr_ __builtin_constant_p
#define I_ internal inline #define I_ internal inline
#define FI_ inline __attribute__((always_inline)) // inline always #define FI_ inline __attribute__((always_inline)) // inline always
#define NI_ internal __attribute__((noinline)) // inline never #define NI_ internal __attribute__((noinline)) // inline never
@@ -128,6 +129,9 @@ typedef __UINT32_TYPE__ TSet_(B4);
#define u4_v(value) C_(U4 V_*, value) #define u4_v(value) C_(U4 V_*, value)
enum { false = 0, true = 1, true_overflow, }; enum { false = 0, true = 1, true_overflow, };
#define u4_lo(value) ((value) & 0xFFFFU)
#define u4_hi(value) ((value) >> 12)
typedef void Proc_(VoidFn) (void); typedef void Proc_(VoidFn) (void);
#define kilo(n) (C_(U4, n) << 10) #define kilo(n) (C_(U4, n) << 10)
+116 -97
View File
@@ -4,15 +4,125 @@
#endif #endif
/* ============================================================================ /* ============================================================================
* GCC INLINE ASSEMBLY MACRO DSL * GCC INLINE ASM STATEMENT DSL
* * ============================================================================
* * A complete GCC inline-asm statement has up to 5 sections separated by `:`
* ============================================================================ */ * asm volatile ( "code template" : OUTPUTS : INPUTS : CLOBBERS : GOTO_LABELS );
*/
// Below are used purely for annotation.
#define asm_out // OUTPUTS section /* cannot be used with asm_words */
#define asm_in // INPUTS section /* can be appended onto after asm_words for pinned registers */
#define asm_clobber // CLOBBERS section
// Pinned Registers after asm_words list (Semantic marker)
// We aren't starting a new offical section, its just a continuation of the input section.
// asm_words(...) // ".words " code word ids... : : code_words...
// asm_rpins, r_use(r0), ... // , pinned registers...
// asm_clobber:
#define asm_rpins
/* --- Logic & Control Flow --- */
/* Annotation for the 'Goto' section of 'asm volatile goto'.
* Allows you to jump from assembly directly to a C label. */
#define asm_goto // Annotate the last `:` in an asm expression.
/* `asm_words(...)` dispatches into `_INL_<count>` to emit up to 99 encoded
* instruction words. This is the "compiled-instruction" form of `asm_code`.
*
* Result is a 2-colon body WITHOUT the final clobber section:
* ".word %c0, %c1, ..." : --- empty --- : "i"(p0), "i"(p1), ...
* |------ code --------| |--- outputs ---| |------- inputs -------|
*
* Use it inside `asm volatile( ... )` like so:
* asm volatile(
* asm_words(w0, w1, w3)
* asm_clobber: clobbers
* )
* which expands to:
* asm volatile(".word %c0, %c1, %c2"
* asm_out: // empty outputs
* asm_in: "i"(w0), "i"(w1), "i"(w2)
* asm_clobber: "$2", "$8", ...
* )
*/
#define asm_words(...) m_expand(glue(GCC_ASM_INL_, GCC_ASM_COUNT_ARGS(__VA_ARGS__))(__VA_ARGS__))
// Very nasty macro expansion. See the Cruft pragma region after all the DSL defines
/* reg_str(n) — Stringify an integer register id into the GCC asm
* string form (e.g. 12 → "$12"). Use this anywhere GCC's parser
* expects a literal string identifying a register: clobber lists,
* asm templates, etc. The two-level macro is the standard preprocessor
* idiom for forcing one level of expansion before stringify — without
* it, `#n` would stringify the macro name `R_T4` to `"R_T4"` instead
* of expanding `R_T4` to its value first.
*
* For declaring a register variable bound to a specific GPR, use the
* `rgcc(n)` bundle from gcc_asm.h instead — it adds the `__asm__()`
* qualifier around the string.
*
* register V3_S2* p0 __asm__(reg_str(R_T4)) = ...; // verbose
* register V3_S2* p0 rgcc(R_T4) = ...; // bundled
*
* asm volatile("nop" : : : reg_str(R_RA), "memory"); // clobber list */
#define rlit_impl(n) "$" #n
#define rlit(n) rlit_impl(n)
/* ------------------------------------------------------------------------ *
* rgcc(n) — GCC-specific bundle for register-variable declarations.
*
* Produces `__asm__(reg_str(tmpl(n, Code)))` at expansion time.
* The `tmpl(n, Code)` indirection derives the preprocessor-visible `_Code`
* form from the enum name (which the preprocessor can't expand on its own).
* So a call is: register V3_S2* p rgcc(R_T4) = verts[0].ptr;
* expands (via tmpl) to: register V3_S2* p __asm__(rlit(R_T4_Code)) = verts[0].ptr;
* which (via reg_str) becomes: register V3_S2* p __asm__("$12") = verts[0].ptr;
*
* Why bundle the `__asm__()` wrapper?
* - The integer R_T4 (= 12, via R_T4_Code) already indicates the register.
* - The string "$12" is derived from it via reg_str, so they cannot drift apart.
* - Spelling `__asm__(reg_str(R_T4_Code))` at every call site is noise.
*
* tmpl defined in dsl.h (the token-paste glue).
* rgcc define here (gcc_asm.h) because the `__asm__` keyword is GCC-specific.
* Anyone porting to a different compiler's asm dialect overrides rgcc,
* and the integer→string derivation in rlit can be retargeted in one place.
*
* For clobber lists and asm-template strings, use the bare `rlit(R_T4_Code)`.
* ------------------------------------------------------------------------ */
#define rgcc_(n) __asm__(rlit(tmpl(n, Code)))
#define rgcc(n) rgcc_(n)
/* rgcc_ref(n) — GCC operand-reference form "%N". Not currently used
* by the placeholder-pun macros (the .word bodies are fully baked
* at compile time and have no runtime operand references), but kept
* here for completeness in case a future asm template needs to refer
* to a runtime input by position. Mirror of rgcc but produces "%N"
* instead of "$N". */
#define rgcc_ref_(n) "%" #n
#define rgcc_ref(n) rgcc_ref_(n)
/* --- Register Constraint Aliases (for Pinned Variables) --- */
#define r_use(var) "r"(var) /* General Purpose Register */
#define r_set(var) "=r"(var) /* Write-only output */
#define r_mod(var) "+r"(var) /* Read-write */
#define r_imm(val) "i"(val) /* Immediate / Constant */
/* Memory: Forces GCC to sync the variable to RAM before the asm runs.
* Essential for DMA buffers or when the hardware reads from memory. */
#define r_mem(var) "m"(var)
#define r_imm(val) "i"(val) /* Immediate: Forces a compile-time constant. */
#define r_fpu(var) "f"(var) /* FPU (PS2/MIPS III/IV): Use for COP1 floating point registers. */
#define r_acc(var) "a"(var) /* Accumulator: Use for HI/LO register results (multiplication/division). */
#define clb_mem_drain "memory"
// C Preprocessor Iterative Expansion Jank
#pragma region Cruft #pragma region Cruft
/* --- 1. The Argument Counter --- */ /* --- 1. The Argument Counter --- */
#define _ASM_COUNT_ARGS_IMPL( \ #define GCC_ASM_COUNT_ARGS_IMPL( \
_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \ _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, \
_11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \ _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
_21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \ _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
@@ -24,7 +134,7 @@
_81,_82,_83,_84,_85,_86,_87,_88,_89,_90, \ _81,_82,_83,_84,_85,_86,_87,_88,_89,_90, \
_91,_92,_93,_94,_95,_96,_97,_98,_99, N, ...) N _91,_92,_93,_94,_95,_96,_97,_98,_99, N, ...) N
#define _ASM_COUNT_ARGS(...) m_expand(_ASM_COUNT_ARGS_IMPL(__VA_ARGS__, \ #define GCC_ASM_COUNT_ARGS(...) m_expand(GCC_ASM_COUNT_ARGS_IMPL(__VA_ARGS__, \
99, 98, 97, 96, 95, 94, 93, 92, 91, 90, \ 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, \
89, 88, 87, 86, 85, 84, 83, 82, 81, 80, \ 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, \
79, 78, 77, 76, 75, 74, 73, 72, 71, 70, \ 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, \
@@ -345,94 +455,3 @@
#define GCC_ASM_INL_99(a, ...) ".word " GCC_ASM_W99 : : GCC_ASM_I99(a, __VA_ARGS__) #define GCC_ASM_INL_99(a, ...) ".word " GCC_ASM_W99 : : GCC_ASM_I99(a, __VA_ARGS__)
#pragma endregion Cruft #pragma endregion Cruft
/* ============================================================================
* AST BUILDERS — assemble a complete inline-asm block
* ============================================================================
*
* A complete GCC inline-asm statement has up to 4 sections separated by `:`:
* asm volatile ( "code" : OUTPUTS : INPUTS : CLOBBERS );
* Below are used purely for annotation.
*/
#define asm_out
#define asm_in
#define asm_clobber
/* `asm_inline(...)` dispatches into `_INL_<count>` to emit up to 99 encoded
* instruction words. This is the "compiled-instruction" form of `asm_code`.
*
* Result is a 2-colon body WITHOUT the final clobber section:
* ".word %c0, %c1, ..." : --- empty --- : "i"(p0), "i"(p1), ...
* |------ code --------| |--- outputs ---| |------- inputs -------|
*
* Use it inside `asm volatile( ... )` like so:
* asm volatile(
* asm_inline(w0, w1, w3)
* asm_clobber: clobbers
* )
* which expands to:
* asm volatile(".word %c0, %c1, %c2"
* asm_out: // empty outputs
* asm_in: "i"(w0), "i"(w1), "i"(w2)
* asm_clobber: "$2", "$8", ...
* )
*/
#define asm_words(...) m_expand(glue(GCC_ASM_INL_, _ASM_COUNT_ARGS(__VA_ARGS__))(__VA_ARGS__))
/* reg_str(n) — Stringify an integer register id into the GCC asm
* string form (e.g. 12 → "$12"). Use this anywhere GCC's parser
* expects a literal string identifying a register: clobber lists,
* asm templates, etc. The two-level macro is the standard preprocessor
* idiom for forcing one level of expansion before stringify — without
* it, `#n` would stringify the macro name `R_T4` to `"R_T4"` instead
* of expanding `R_T4` to its value first.
*
* For declaring a register variable bound to a specific GPR, use the
* `rgcc(n)` bundle from gcc_asm.h instead — it adds the `__asm__()`
* qualifier around the string.
*
* register V3_S2* p0 __asm__(reg_str(R_T4)) = ...; // verbose
* register V3_S2* p0 rgcc(R_T4) = ...; // bundled
*
* asm volatile("nop" : : : reg_str(R_RA), "memory"); // clobber list */
#define rlit_impl(n) "$" #n
#define rlit(n) rlit_impl(n)
/* ------------------------------------------------------------------------ *
* rgcc(n) — GCC-specific bundle for register-variable declarations.
*
* Produces `__asm__(reg_str(tmpl(n, Code)))` at expansion time.
* The `tmpl(n, Code)` indirection derives the preprocessor-visible `_Code`
* form from the enum name (which the preprocessor can't expand on its own).
* So a call is: register V3_S2* p rgcc(R_T4) = verts[0].ptr;
* expands (via tmpl) to: register V3_S2* p __asm__(rlit(R_T4_Code)) = verts[0].ptr;
* which (via reg_str) becomes: register V3_S2* p __asm__("$12") = verts[0].ptr;
*
* Why bundle the `__asm__()` wrapper?
* - The integer R_T4 (= 12, via R_T4_Code) already indicates the register.
* - The string "$12" is derived from it via reg_str, so they cannot drift apart.
* - Spelling `__asm__(reg_str(R_T4_Code))` at every call site is noise.
*
* tmpl defined in dsl.h (the token-paste glue).
* rgcc define here (gcc_asm.h) because the `__asm__` keyword is GCC-specific.
* Anyone porting to a different compiler's asm dialect overrides rgcc,
* and the integer→string derivation in rlit can be retargeted in one place.
*
* For clobber lists and asm-template strings, use the bare `rlit(R_T4_Code)`.
* ------------------------------------------------------------------------ */
#define rgcc_(n) __asm__(rlit(tmpl(n, Code)))
#define rgcc(n) rgcc_(n)
/* rgcc_ref(n) — GCC operand-reference form "%N". Not currently used
* by the placeholder-pun macros (the .word bodies are fully baked
* at compile time and have no runtime operand references), but kept
* here for completeness in case a future asm template needs to refer
* to a runtime input by position. Mirror of rgcc but produces "%N"
* instead of "$N". */
#define rgcc_ref_(n) "%" #n
#define rgcc_ref(n) rgcc_ref_(n)
/* --- Register Constraint Aliases (for Pinned Variables) --- */
#define r_use(var) "r"(var) /* Input: Tells GCC we are reading this pinned register */
#define r_set(var) "=r"(var) /* Output: Tells GCC we are overwriting this pinned register */
#define r_mod(var) "+r"(var) /* Modify: Tells GCC we are reading AND writing this pinned register */
+45 -43
View File
@@ -271,6 +271,9 @@ enum { _C2_OPS_ = 0
* - rd: COP2 control register index (0..31) */ * - rd: COP2 control register index (0..31) */
#define enc_gte_tx(sub, rt, rd) (enc_op(op_cop2) | enc_rs(sub) | enc_rt(rt) | enc_rd(rd)) #define enc_gte_tx(sub, rt, rd) (enc_op(op_cop2) | enc_rs(sub) | enc_rt(rt) | enc_rd(rd))
#define gte_mt(rt, rd) enc_gte_tx(cop_mt, (rt), (rd)) /* Move GPR (rt) to GTE Control Register (rd) */
#define gte_mf(rt, rd) enc_gte_tx(cop_mf, (rt), (rd)) /* Move GTE Control Register (rd) to GPR (rt) */
/* COP2 Data Load (lwc2): `lwc2 rt, off(rs)` /* COP2 Data Load (lwc2): `lwc2 rt, off(rs)`
* Layout: [op_lwc2:6][rs:5][rt:5][imm:16] * Layout: [op_lwc2:6][rs:5][rt:5][imm:16]
* - rs: GPR base address * - rs: GPR base address
@@ -332,7 +335,7 @@ enum { _C2_OPS_ = 0
* *
* Decomposition (per the `enc_gte_<field>` definitions above): * Decomposition (per the `enc_gte_<field>` definitions above):
* gte_cmdw_<name> = gte_cmd_base | enc_gte_cmd(<cmd>) * gte_cmdw_<name> = gte_cmd_base | enc_gte_cmd(<cmd>)
*
* The SF/MX/V/CV/LM fields are all zero in the common cases (standard * The SF/MX/V/CV/LM fields are all zero in the common cases (standard
* rotation-matrix, no scaling factor, V0 vector, translation vector, * rotation-matrix, no scaling factor, V0 vector, translation vector,
* no clamp), so the only varying bits are the `cmd` field. * no clamp), so the only varying bits are the `cmd` field.
@@ -401,12 +404,12 @@ enum {
GTE_Z_Offset = 4 GTE_Z_Offset = 4
}; };
#define gte_load_word_v0(base) enc_gte_lw(gte_in_v0_xy, (base), 0) #define gte_lw_v0(base) enc_gte_lw(gte_in_v0_xy, (base), 0)
#define gte_load_word_v0z(base) enc_gte_lw(gte_in_v0_z, (base), GTE_Z_Offset) #define gte_lw_v0z(base) enc_gte_lw(gte_in_v0_z, (base), GTE_Z_Offset)
#define gte_load_word_v1(base) enc_gte_lw(gte_in_v1_xy, (base), 0) #define gte_lw_v1(base) enc_gte_lw(gte_in_v1_xy, (base), 0)
#define gte_load_word_v1z(base) enc_gte_lw(gte_in_v1_z, (base), GTE_Z_Offset) #define gte_lw_v1z(base) enc_gte_lw(gte_in_v1_z, (base), GTE_Z_Offset)
#define gte_load_word_v2(base) enc_gte_lw(gte_in_v2_xy, (base), 0) #define gte_lw_v2(base) enc_gte_lw(gte_in_v2_xy, (base), 0)
#define gte_load_word_v2z(base) enc_gte_lw(gte_in_v2_z, (base), GTE_Z_Offset) #define gte_lw_v2z(base) enc_gte_lw(gte_in_v2_z, (base), GTE_Z_Offset)
/* gte_load_vN(r_ptr, base) — placeholder-punned lwc2 loaders /* gte_load_vN(r_ptr, base) — placeholder-punned lwc2 loaders
* *
@@ -445,25 +448,22 @@ enum {
* *
* The `asm_clobber(...)` helper from gcc_asm.h prepends the colon that * The `asm_clobber(...)` helper from gcc_asm.h prepends the colon that
* starts the clobbers section. */ * starts the clobbers section. */
#define gte_load_v0(r_ptr, base) \ #define gte_load_v0(r_ptr, base) asm volatile( \
asm volatile( \ asm_words( gte_lw_v0(base), gte_lw_v0z(base) ) \
asm_words( gte_load_word_v0(base), gte_load_word_v0z(base) ) \ asm_rpins, r_use(r_ptr) \
, "r"(r_ptr) \ asm_clobber: rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), clb_mem_drain \
asm_clobber: rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), "memory" \
) )
#define gte_load_v1(r_ptr, base) \ #define gte_load_v1(r_ptr, base) asm volatile( \
asm volatile( \ asm_words( gte_lw_v1(base), gte_lw_v1z(base) ) \
asm_words( gte_load_word_v1(base), gte_load_word_v1z(base) ) \ asm_rpins, r_use(r_ptr) \
, "r"(r_ptr) \ asm_clobber: rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), clb_mem_drain \
asm_clobber: rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), "memory" \
) )
#define gte_load_v2(r_ptr, base) \ #define gte_load_v2(r_ptr, base) asm volatile( \
asm volatile( \ asm_words( gte_lw_v2(base), gte_lw_v2z(base) ) \
asm_words( gte_load_word_v2(base), gte_load_word_v2z(base) ) \ asm_rpins, r_use(r_ptr) \
, "r"(r_ptr) \ asm_clobber: rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), clb_mem_drain \
asm_clobber: rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), "memory" \
) )
/* gte_load_v0v1v2(p0, p1, p2, b0, b1, b2) — the canonical prelude to gte_cmd_rtpt. /* gte_load_v0v1v2(p0, p1, p2, b0, b1, b2) — the canonical prelude to gte_cmd_rtpt.
@@ -478,13 +478,14 @@ enum {
* gte_load_v0v1v2(p0, p1, p2, R_T4, R_T5, R_T6); * gte_load_v0v1v2(p0, p1, p2, R_T4, R_T5, R_T6);
* gte_rtpt(); * gte_rtpt();
*/ */
#define gte_load_v0v1v2(p0, p1, p2, b0, b1, b2) \ #define gte_load_v0v1v2(p0, p1, p2, b0, b1, b2) asm volatile( \
asm volatile( \ asm_words( \
asm_words( gte_lwc2_v0(b0), gte_lwc2_v0z(b0), \ gte_lw_v0(b0), gte_lw_v0z(b0), \
gte_lwc2_v1(b1), gte_lwc2_v1z(b1), \ gte_lw_v1(b1), gte_lw_v1z(b1), \
gte_lwc2_v2(b2), gte_lwc2_v2z(b2) ) \ gte_lw_v2(b2), gte_lw_v2z(b2) ) \
, "r"(p0), "r"(p1), "r"(p2) \ asm_rpins \
asm_clobber( rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), "memory" ) \ , r_use(p0), r_use(p1), r_use(p2) \
asm_clobber: rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), clb_mem_drain \
) )
/** /**
@@ -623,19 +624,20 @@ enum {
* get stale-RT2x/RT3x artifacts in RTPS/RTPT/MVMVA output. * get stale-RT2x/RT3x artifacts in RTPS/RTPT/MVMVA output.
*/ */
#define asm_gte_matrix_set_rotation(r0) \ #define asm_gte_matrix_set_rotation(r0) \
asm volatile(asm_words( \ asm volatile( \
load_imm(R_T4, r0, 0) \ asm_words( \
, load_imm(R_T5, r0, 4) \ load_word(R_T5, R_T4, 0) \
, enc_cop2_tx(cop_mt, R_T4, 0) \ , load_word(R_T6, R_T4, 4) \
, enc_cop2_tx(cop_mt, R_T5, 1) \ , gte_mt( R_T5, 0) \
, load_imm(R_T4, r0, 8) \ , gte_mt( R_T6, 1) \
, load_imm(R_T5, r0, 12) \ , load_word(R_T5, R_T4, 8) \
, load_imm(R_T6, r0, 16) \ , load_word(R_T6, R_T4, 12) \
, enc_cop2_tx(cop_mt, R_T4, 2) \ , load_word(R_T4, R_T4, 16) \
, enc_cop2_tx(cop_mt, R_T5, 3) \ , gte_mt( R_T5, 2) \
, enc_cop2_tx(cop_mt, R_T6, 4) \ , gte_mt( R_T6, 3) \
, gte_mt( R_T4, 4) \
) \ ) \
asm_clobber: clb_system, reg_str(R_T4_Code), reg_str(R_T5_Code), reg_str(R_T6_Code) \ , r_use(r0) \
: \ asm_clobber: clb_system, rlit(R_T4_Code), rlit(R_T5_Code), rlit(R_T6_Code) \
: "r"(r0) \
) )
+1 -1
View File
@@ -10,7 +10,7 @@
assert((point) <= (end)); \ assert((point) <= (end)); \
} while(0) } while(0)
inline U4 align_pow2(U4 x, U4 b) { I_ U4 align_pow2(U4 x, U4 b) {
assert(b != 0); assert(b != 0);
assert((b & (b - 1)) == 0); // Check power of 2 assert((b & (b - 1)) == 0); // Check power of 2
return ((x + b - 1) & (~(b - 1))); return ((x + b - 1) & (~(b - 1)));
+37 -42
View File
@@ -80,6 +80,7 @@ enum {
, rtmp_2 = R_T2 /* Temporary (Caller saved) */ , rtmp_2 = R_T2 /* Temporary (Caller saved) */
, rtmp_3 = R_T3 /* Temporary (Caller saved) */ , rtmp_3 = R_T3 /* Temporary (Caller saved) */
, rtmp_4 = R_T4 /* Temporary (Caller saved) — common GTE base pointer */ , rtmp_4 = R_T4 /* Temporary (Caller saved) — common GTE base pointer */
, rtmp_9 = R_T9 /* Temporary (Caller saved) — common GTE base pointer */
, rstatic_0 = R_S0 /* Static (Callee saved, preserved across calls) */ , rstatic_0 = R_S0 /* Static (Callee saved, preserved across calls) */
, rstatic_1 = R_S1 , rstatic_1 = R_S1
, rstatic_2 = R_S2 , rstatic_2 = R_S2
@@ -240,10 +241,10 @@ enum { _BitOffsets = 0
#define load_half_u(rt, base, off) enc_i(op_lhu, (base), (rt), (off)) #define load_half_u(rt, base, off) enc_i(op_lhu, (base), (rt), (off))
#define store_word(rt, base, off) enc_i(op_sw, (base), (rt), (off)) #define store_word(rt, base, off) enc_i(op_sw, (base), (rt), (off))
#define add_ui(rt, rs, imm) enc_i(op_addiu, (rs), (rt), (imm)) #define add_ui(rt, rs, imm) enc_i(op_addiu, (rs), (rt), (imm))
#define andi_op(rt, rs, imm) enc_i(op_andi, (rs), (rt), (imm)) #define and_si(rt, rs, imm) enc_i(op_andi, (rs), (rt), (imm))
#define ori_op(rt, rs, imm) enc_i(op_ori, (rs), (rt), (imm)) #define or_i(rt, rs, imm) enc_i(op_ori, (rs), (rt), (imm))
#define xori_op(rt, rs, imm) enc_i(op_xori, (rs), (rt), (imm)) #define xor_i(rt, rs, imm) enc_i(op_xori, (rs), (rt), (imm))
#define lui_op(rt, imm) enc_i(op_lui, R_0, (rt), (imm)) #define load_ui(rt, imm) enc_i(op_lui, R_0, (rt), (imm))
/* Shift family (R-type). shift_ll/lr/ra: `sll rd, rt, shamt` */ /* Shift family (R-type). shift_ll/lr/ra: `sll rd, rt, shamt` */
#define shift_ll(rd, rt, shamt) enc_r(op_special, R_0, (rt), (rd), (shamt), fc_sll) #define shift_ll(rd, rt, shamt) enc_r(op_special, R_0, (rt), (rd), (shamt), fc_sll)
@@ -332,7 +333,7 @@ enum { _BitOffsets = 0
#define breakpoint() enc_r(op_special, R_0, R_0, R_0, 0, fc_break) #define breakpoint() enc_r(op_special, R_0, R_0, R_0, 0, fc_break)
/* --- Shift-amount alias (matches the gas convention `\p3 = shamt`) --- */ /* --- Shift-amount alias (matches the gas convention `\p3 = shamt`) --- */
#define shamt(rd, rt, n) shift_ll(rd, rt, n) #define shift_amount(rd, rt, n) shift_ll(rd, rt, n)
/* nop — canonical sll $0, $0, 0 */ /* nop — canonical sll $0, $0, 0 */
#define nop shift_ll(rdiscard, rdiscard, 0) #define nop shift_ll(rdiscard, rdiscard, 0)
@@ -361,21 +362,18 @@ enum { _BitOffsets = 0
* Statement-level (not expression-level): emits its own `asm volatile(...)`. * Statement-level (not expression-level): emits its own `asm volatile(...)`.
*/ */
#define load_imm_2w(rt, imm) do { \ #define load_imm_2w(rt, imm) do { \
U4 _li2_imm_ = (U4)(imm); \ if (u4_low(imm) <= 0x7FFFU) { \
U4 _li2_lo_ = _li2_imm_ & 0xFFFFU; \
U4 _li2_hi_ = _li2_imm_ >> 16; \
if (_li2_lo_ <= 0x7FFFU) { \
asm volatile( \ asm volatile( \
asm_words(lui_op((rt), _li2_hi_), \ asm_words(load_ui((rt), u4_hi(imm), \
add_si((rt), (rt), (S2)(U2)_li2_lo_)) \ add_si((rt), (rt), (S2)C_(U2,u4_lo(imm))) \
asm_clobber(rlit(R_AT_Code), "memory") \ asm_clobber: rlit(R_AT_Code), clb_mem_drain \
); \ ); \
} \ } \
else { \ else { \
asm volatile( \ asm volatile(asm_words( \
asm_words(lui_op((rt), _li2_hi_), \ load_ui((rt), u4_hi(imm)), \
ori_op((rt), (rt), (U2)_li2_lo_)) \ or_i((rt), (rt), C_(U2,u4_lo(imm)) \
asm_clobber(rlit(R_AT_Code), "memory") \ asm_clobber: rlit(R_AT_Code), clb_mem_drain \
); \ ); \
} \ } \
} while (0) } while (0)
@@ -383,11 +381,10 @@ enum { _BitOffsets = 0
/* load_imm_2w_ori — force the `lui` + `ori` form regardless of lo16 sign. /* load_imm_2w_ori — force the `lui` + `ori` form regardless of lo16 sign.
* Use when you specifically need zero-extension in the lo half. */ * Use when you specifically need zero-extension in the lo half. */
#define load_imm_2w_ori(rt, imm) do { \ #define load_imm_2w_ori(rt, imm) do { \
U4 _li2o_imm_ = (U4)(imm); \
asm volatile( \ asm volatile( \
asm_words(lui_op((rt), _li2o_imm_ >> 16), \ asm_words(load_ui((rt), u4_lo(imm)), \
ori_op((rt), (rt), (U2)(_li2o_imm_ & 0xFFFFU))) \ or_i((rt), (rt), C_(U2,u4_hi(imm))) ) \
asm_clobber(rlit(R_AT_Code), "memory") \ asm_clobber: rlit(R_AT_Code), clb_mem_drain \
); \ ); \
} while (0) } while (0)
@@ -396,11 +393,11 @@ enum { _BitOffsets = 0
* signed downstream) and you want a smaller effective instruction * signed downstream) and you want a smaller effective instruction
* (the assembler/MIPS hardware will sign-extend the imm16). */ * (the assembler/MIPS hardware will sign-extend the imm16). */
#define load_imm_2w_addi(rt, imm) do { \ #define load_imm_2w_addi(rt, imm) do { \
U4 _li2a_imm_ = (U4)(imm); \ /*U4 _li2a_imm_ = (U4)(imm);*/ \
asm volatile( \ asm volatile(asm_words( \
asm_words(lui_op((rt), _li2a_imm_ >> 16), \ lui_op((rt), u4_lo(imm)), \
add_si((rt), (rt), (S2)(U2)(_li2a_imm_ & 0xFFFFU))) \ add_si((rt), (rt), (S2)C_(U2,u4_hi(imm))) ) \
asm_clobber(rlit(R_AT_Code), "memory") \ asm_clobber: rlit(R_AT_Code), clb_mem_drain \
); \ ); \
} while (0) } while (0)
@@ -424,18 +421,18 @@ enum { _BitOffsets = 0
* but that path is unusual (load_imm is most useful with literal * but that path is unusual (load_imm is most useful with literal
* addresses and magic numbers). */ * addresses and magic numbers). */
#define load_imm(rt, imm) do { \ #define load_imm(rt, imm) do { \
if (__builtin_constant_p(imm) && ((U4)(imm) <= 0x7FFFU)) { \ if (cexpr_(imm) && ((imm) <= 0x7FFFU)) { \
/* Small positive: addi rt, $0, imm */ \ /* Small positive: addi rt, $0, imm */ \
asm volatile( \ asm volatile( \
asm_words(add_si((rt), R_0, (imm))) \ asm_words(add_si((rt), R_0, (imm))) \
asm_clobber(rlit(R_AT_Code), "memory") \ asm_clobber: rlit(R_AT_Code), clb_mem_drain \
); \ ); \
} \ } \
else if (__builtin_constant_p(imm) && ((U4)(imm) <= 0xFFFFU)) { \ else if (cexpr_(imm) && ((U4)(imm) <= 0xFFFFU)) { \
/* 0x8000..0xFFFF: ori rt, $0, imm (zero-extends) */ \ /* 0x8000..0xFFFF: ori rt, $0, imm (zero-extends) */ \
asm volatile( \ asm volatile( \
asm_words(ori_op((rt), R_0, (imm))) \ asm_words(or_i((rt), R_0, (imm))) \
asm_clobber(rlit(R_AT_Code), "memory") \ asm_clobber: rlit(R_AT_Code), clb_mem_drain \
); \ ); \
} \ } \
else \ else \
@@ -443,20 +440,18 @@ enum { _BitOffsets = 0
/* > 16 bits: lui + (ori | addi). \ /* > 16 bits: lui + (ori | addi). \
* If lo16 is in [0, 0x7FFF] use addi (sign-ext is harmless \ * If lo16 is in [0, 0x7FFF] use addi (sign-ext is harmless \
* since the high half cleared bits 15..0). Otherwise ori. */ \ * since the high half cleared bits 15..0). Otherwise ori. */ \
U4 _li_imm_ = (U4)(imm); \ if (u4_lo(imm) <= 0x7FFFU) { \
U4 _li_lo_ = _li_imm_ & 0xFFFFU; \
U4 _li_hi_ = _li_imm_ >> 16; \
if (_li_lo_ <= 0x7FFFU) { \
asm volatile(asm_words( \ asm volatile(asm_words( \
lui_op((rt), _li_hi_), \ load_ui((rt), u4_hi(imm)), \
add_si((rt), (rt), (S2)(U2)_li_lo_)) \ add_si((rt), (rt), (S2)C_(U2,u4_lo(imm))) \
asm_clobber(rlit(R_AT_Code), "memory") \ asm_clobber: rlit(R_AT_Code), clb_mem_drain \
); \ ); \
} \ } \
else { \ else { \
asm volatile(asm_words( \ asm volatile(asm_words( \
lui_op((rt), _li_hi_), ori_op((rt), (rt), (U2)_li_lo_)) \ load_ui((rt), u4_hi(imm)), \
asm_clobber: rlit(R_AT_Code), "memory" \ or_i((rt), (rt), C_(U2,u4_lo(imm)) \
asm_clobber: rlit(R_AT_Code), clb_mem_drain \
); \ ); \
} \ } \
} \ } \
@@ -465,7 +460,7 @@ enum { _BitOffsets = 0
// Binary Metaprogramming // Binary Metaprogramming
typedef U4 const Code; typedef U4 const Code;
#define CodeBlob_(sym) tmpl(codeblob,sym) [] align_(4) = #define CodeBlob_(sym) tmpl(code,sym) [] align_(4) =
enum { enum {
bios_flushcache = 0x44, bios_flushcache = 0x44,
@@ -495,13 +490,13 @@ Code CodeBlob_(mips_flush_icache) {
, jump_reg(rret_addr) /* jr $ra */ , jump_reg(rret_addr) /* jr $ra */
, add_ui(rstack_ptr, rstack_ptr, 8) /* sp += 8 (BD) */ , add_ui(rstack_ptr, rstack_ptr, 8) /* sp += 8 (BD) */
}; };
FI_ void mips_flush_icache(void) { C_(VoidFn*, codeblob_mips_flush_icache)(); } FI_ void mips_flush_icache(void) { C_(VoidFn*, code_mips_flush_icache)(); }
/* Standard clobber list for pure-MIPS asm volatile blocks: caller-saved /* Standard clobber list for pure-MIPS asm volatile blocks: caller-saved
* GPRs that the kernel treats as volatile (v0/v1/t0/t1/ra) plus the * GPRs that the kernel treats as volatile (v0/v1/t0/t1/ra) plus the
* "memory" barrier. The register ids are passed through `rlit` so * "memory" barrier. The register ids are passed through `rlit` so
* the R_*_Code `#define`s are stringified into "$N" at expansion time. */ * the R_*_Code `#define`s are stringified into "$N" at expansion time. */
#define clb_system rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), "memory" #define clb_system rlit(R_V0_Code), rlit(R_T0_Code), rlit(R_T1_Code), rlit(R_RA_Code), clb_mem_drain
#define asm_mips_flush_icache() asm volatile( asm_words( \ #define asm_mips_flush_icache() asm volatile( asm_words( \
add_ui(rstack_ptr, rstack_ptr, -8) \ add_ui(rstack_ptr, rstack_ptr, -8) \