///|
// LoongArch 64 (la64) target constants, LP64D ABI.
// Mirrors target.mbt / target_rv64.mbt but for the LoongArch64 architecture.
// Register tmp ids are renumbered (QBE-style) instead of using the raw
// machine numbers so that the allocatable ranges stay contiguous.

///|
// LoongArch 64 register numbers (as virtual tmp ids).

///|
// The hard-wired zero register ($zero / $r0).
pub const LA_ZERO : Int = 0

///|
// Caller-save GPR temporaries (t0-t7; t8 is reserved as emitter scratch).
pub const LA_T0 : Int = 1

///|
pub const LA_T1 : Int = 2

///|
pub const LA_T2 : Int = 3

///|
pub const LA_T3 : Int = 4

///|
pub const LA_T4 : Int = 5

///|
pub const LA_T5 : Int = 6

///|
pub const LA_T6 : Int = 7

///|
pub const LA_T7 : Int = 8

///|
// Argument registers (a0-a7)
pub const LA_A0 : Int = 9

///|
pub const LA_A1 : Int = 10

///|
pub const LA_A2 : Int = 11

///|
pub const LA_A3 : Int = 12

///|
pub const LA_A4 : Int = 13

///|
pub const LA_A5 : Int = 14

///|
pub const LA_A6 : Int = 15

///|
pub const LA_A7 : Int = 16

///|
// Callee-save registers (s0-s8)
pub const LA_S0 : Int = 17

///|
pub const LA_S1 : Int = 18

///|
pub const LA_S2 : Int = 19

///|
pub const LA_S3 : Int = 20

///|
pub const LA_S4 : Int = 21

///|
pub const LA_S5 : Int = 22

///|
pub const LA_S6 : Int = 23

///|
pub const LA_S7 : Int = 24

///|
pub const LA_S8 : Int = 25

///|
// Globally live registers
pub const LA_FP : Int = 26

///|
pub const LA_SP : Int = 27

///|
pub const LA_TP : Int = 28

///|
pub const LA_RA : Int = 29

///|
// Reserved scratch register (t8), used by the emitter and for env arguments.
pub const LA_T8 : Int = 30

///|
// FP caller-save registers (ft0-ft15)
pub const LA_FT0 : Int = 32

///|
pub const LA_FT1 : Int = 33

///|
pub const LA_FT2 : Int = 34

///|
pub const LA_FT3 : Int = 35

///|
pub const LA_FT4 : Int = 36

///|
pub const LA_FT5 : Int = 37

///|
pub const LA_FT6 : Int = 38

///|
pub const LA_FT7 : Int = 39

///|
pub const LA_FT8 : Int = 40

///|
pub const LA_FT9 : Int = 41

///|
pub const LA_FT10 : Int = 42

///|
pub const LA_FT11 : Int = 43

///|
pub const LA_FT12 : Int = 44

///|
pub const LA_FT13 : Int = 45

///|
pub const LA_FT14 : Int = 46

///|
pub const LA_FT15 : Int = 47

///|
// FP argument registers (fa0-fa7)
pub const LA_FA0 : Int = 48

///|
pub const LA_FA1 : Int = 49

///|
pub const LA_FA2 : Int = 50

///|
pub const LA_FA3 : Int = 51

///|
pub const LA_FA4 : Int = 52

///|
pub const LA_FA5 : Int = 53

///|
pub const LA_FA6 : Int = 54

///|
pub const LA_FA7 : Int = 55

///|
// FP callee-save registers (fs0-fs7)
pub const LA_FS0 : Int = 56

///|
pub const LA_FS1 : Int = 57

///|
pub const LA_FS2 : Int = 58

///|
pub const LA_FS3 : Int = 59

///|
pub const LA_FS4 : Int = 60

///|
pub const LA_FS5 : Int = 61

///|
pub const LA_FS6 : Int = 62

///|
pub const LA_FS7 : Int = 63

///|
// Tmp0 - first non-register temporary
pub const La64Tmp0 : Int = 64

///|
// Number of general purpose registers (LA_T0-LA_RA)
pub const La64NGPR : Int = 29

///|
// Number of float registers (LA_FT0-LA_FS7)
pub const La64NFPR : Int = 32

///|
// Number of caller-save GPRs (LA_T0-LA_A7)
pub const La64NGPS : Int = 16

///|
// Number of caller-save FPRs (LA_FT0-LA_FA7)
pub const La64NFPS : Int = 24

///|
// Number of callee-save registers (LA_S0-LA_S8, LA_FS0-LA_FS7)
pub const La64NCLR : Int = 17

///|
// Number of globally live registers (LA_FP, LA_SP, LA_TP, LA_RA)
pub const La64NRGLOB : Int = 4

///|
// LoongArch caller-save register list (C la64_rsave in a hypothetical
// upstream targ.c; not present in vendor/qbe).
pub let la64_rsave : Array[Int] = [
  LA_T0,
  LA_T1,
  LA_T2,
  LA_T3,
  LA_T4,
  LA_T5,
  LA_T6,
  LA_T7,
  LA_A0,
  LA_A1,
  LA_A2,
  LA_A3,
  LA_A4,
  LA_A5,
  LA_A6,
  LA_A7,
  LA_FA0,
  LA_FA1,
  LA_FA2,
  LA_FA3,
  LA_FA4,
  LA_FA5,
  LA_FA6,
  LA_FA7,
  LA_FT0,
  LA_FT1,
  LA_FT2,
  LA_FT3,
  LA_FT4,
  LA_FT5,
  LA_FT6,
  LA_FT7,
  LA_FT8,
  LA_FT9,
  LA_FT10,
  LA_FT11,
  LA_FT12,
  LA_FT13,
  LA_FT14,
  LA_FT15,
]

///|
// LoongArch callee-save register list (fp is globally live, not saved here).
pub let la64_rclob : Array[Int] = [
  LA_S0,
  LA_S1,
  LA_S2,
  LA_S3,
  LA_S4,
  LA_S5,
  LA_S6,
  LA_S7,
  LA_S8,
  LA_FS0,
  LA_FS1,
  LA_FS2,
  LA_FS3,
  LA_FS4,
  LA_FS5,
  LA_FS6,
  LA_FS7,
]

///|
// LoongArch globally live registers bitmask (FP | SP | TP | RA).
pub fn la64_rglob_mask() -> UInt64 {
  let one : UInt64 = 1
  (one << LA_FP) | (one << LA_SP) | (one << LA_TP) | (one << LA_RA)
}

///|
// The registers returned by a call, given its RCall ref.
// Returns (bits, ngp, nfpr): a0/a1 and fa0/fa1.
pub fn Ref::la64_retregs(self : Ref) -> (UInt64, Int, Int) {
  let mut b : UInt64 = 0
  let v = self.call_val()
  let ni = v & 3
  let nf = (v >> 2) & 3
  if ni >= 1 {
    b = b | (1UL << LA_A0)
  }
  if ni >= 2 {
    b = b | (1UL << LA_A1)
  }
  if nf >= 1 {
    b = b | (1UL << LA_FA0)
  }
  if nf >= 2 {
    b = b | (1UL << LA_FA1)
  }
  (b, ni, nf)
}

///|
// The registers used to pass arguments, given an RCall ref.
// Returns (bits, ngp, nfpr): a0.. and fa0.., plus t8 for the env argument.
pub fn Ref::la64_argregs(self : Ref) -> (UInt64, Int, Int) {
  let mut b : UInt64 = 0
  let v = self.call_val()
  let ni = (v >> 4) & 15
  let nf = (v >> 8) & 15
  let env = (v >> 12) & 1
  for j in 0.. String {
  match reg {
    LA_FP => "fp"
    LA_SP => "sp"
    LA_TP => "tp"
    LA_RA => "ra"
    LA_T0 => "t0"
    LA_T1 => "t1"
    LA_T2 => "t2"
    LA_T3 => "t3"
    LA_T4 => "t4"
    LA_T5 => "t5"
    LA_T6 => "t6"
    LA_T7 => "t7"
    LA_T8 => "t8"
    LA_A0 => "a0"
    LA_A1 => "a1"
    LA_A2 => "a2"
    LA_A3 => "a3"
    LA_A4 => "a4"
    LA_A5 => "a5"
    LA_A6 => "a6"
    LA_A7 => "a7"
    LA_S0 => "s0"
    LA_S1 => "s1"
    LA_S2 => "s2"
    LA_S3 => "s3"
    LA_S4 => "s4"
    LA_S5 => "s5"
    LA_S6 => "s6"
    LA_S7 => "s7"
    LA_S8 => "s8"
    LA_FT0 => "ft0"
    LA_FT1 => "ft1"
    LA_FT2 => "ft2"
    LA_FT3 => "ft3"
    LA_FT4 => "ft4"
    LA_FT5 => "ft5"
    LA_FT6 => "ft6"
    LA_FT7 => "ft7"
    LA_FT8 => "ft8"
    LA_FT9 => "ft9"
    LA_FT10 => "ft10"
    LA_FT11 => "ft11"
    LA_FT12 => "ft12"
    LA_FT13 => "ft13"
    LA_FT14 => "ft14"
    LA_FT15 => "ft15"
    LA_FA0 => "fa0"
    LA_FA1 => "fa1"
    LA_FA2 => "fa2"
    LA_FA3 => "fa3"
    LA_FA4 => "fa4"
    LA_FA5 => "fa5"
    LA_FA6 => "fa6"
    LA_FA7 => "fa7"
    LA_FS0 => "fs0"
    LA_FS1 => "fs1"
    LA_FS2 => "fs2"
    LA_FS3 => "fs3"
    LA_FS4 => "fs4"
    LA_FS5 => "fs5"
    LA_FS6 => "fs6"
    LA_FS7 => "fs7"
    _ => "?"
  }
}

///|
// LoongArch pointer size: Kl (64-bit).
pub const La64KmIsWide : Int = 1

///|
// LoongArch call return registers: LA_A0/LA_A1 and LA_FA0/LA_FA1.
fn la64_target_retregs(r : Ref) -> (UInt64, Int, Int) {
  r.la64_retregs()
}

///|
// LoongArch call argument registers: LA_A0.. and LA_FA0...
fn la64_target_argregs(r : Ref) -> (UInt64, Int, Int) {
  r.la64_argregs()
}

///|
// Select the la64 target (LoongArch64 LP64D).
pub fn init_la64_target() -> Unit {
  target_cfg.gpr_base = LA_T0
  target_cfg.fpr_base = LA_FT0
  target_cfg.ngpr = La64NGPR
  target_cfg.nfpr = La64NFPR
  target_cfg.fpr_class_base = LA_FT0
  target_cfg.post_call_gpr = La64NGPS
  target_cfg.post_call_fpr = La64NFPS
  target_cfg.rglob_mask = la64_rglob_mask()
  target_cfg.rsave = la64_rsave
  target_cfg.retregs = la64_target_retregs
  target_cfg.argregs = la64_target_argregs
}