(* A machine word is 4 bytes. *)

let word =
  4l

(* The MIPS CPU contains 32 general-purpose 32-bit registers
   that are numbered 0 to 31.

   Register 0, known as [$0], always contains the hardwired value 0.

   Register 1, known as [$at], is conventionally reserved for use by
   the assembler for expanding certain pseudo-instructions into actual
   hardware instructions. This means that we cannot use it.

   Registers 2 and 3, known as [$v0] and [$v1], are normally used to
   return values from functions. In our compiler, only [$v0] is used
   for this purpose. We use [$v1] as a reserved register for spilling
   and refer to it as [$st0].

   Registers 4 to 7, known as [$a0] to [$a3], are used to pass the
   first four arguments to functions. Remaining arguments are passed
   on the stack.

   Registers 8 to 15, 24 and 25, known as [$t0] to [$t9], are
   caller-saved registers.

   Registers 16 to 23, known as [$s0] to [$s7], are callee-saved
   registers.

   Registers 26 and 27, known as [$k0] and [$k1], are reserved for use
   by the operating system kernel.

   Register 28, known as [$gp] for global pointer, points into the
   middle of a 64K block of memory in the heap that holds constants
   and global variables.

   Register 29, known as [$sp] for stack pointer, points to the last
   location in use on the stack.

   Register 30, known as [$fp] for frame pointer, is used in our
   compiler as a reserved register for spilling. We refer to it as
   [$st1].

   Register 31, known as [$ra], contains the return address for the
   current function. It is written by the [jal] instruction. *)

type register =
    int

let equal : register -> register -> bool = (=)

module RegisterSet = struct

  include Set.Make (struct
    type t = register
    let compare = (-)
  end)

  let disjoint s1 s2 =
    is_empty (inter s1 s2)

  let of_list rs =
    List.fold_right add rs empty

end

module RegisterMap = struct

  include Map.Make (struct
    type t = register
    let compare = (-)
  end)

  let lift f s =
    RegisterSet.fold (fun x m ->
      add x (f x) m
    ) s empty

end

(* Naming convention. *)

let zero =
  0

let v0, v1, a0, a1, a2, a3 =
  2, 3, 4, 5, 6, 7

let t0, t1, t2, t3, t4, t5, t6, t7, t8, t9 =
  8, 9, 10, 11, 12, 13, 14, 15, 24, 25

let s0, s1, s2, s3, s4, s5, s6, s7 =
  16, 17, 18, 19, 20, 21, 22, 23

let gp =
  28

let sp, fp =
  29, 30

let ra =
  31

let st0, st1 =
  v1, fp

let print = function
  | 0 ->
      "zero"
  | 1 ->
      "at"
  | 2 ->
      "v0"
  | 4 ->
      "a0"
  | 5 ->
      "a1"
  | 6 ->
      "a2"
  | 7 ->
      "a3"
  | 8 ->
      "t0"
  | 9 ->
      "t1"
  | 10 ->
      "t2"
  | 11 ->
      "t3"
  | 12 ->
      "t4"
  | 13 ->
      "t5"
  | 14 ->
      "t6"
  | 15 ->
      "t7"
  | 24 ->
      "t8"
  | 25 ->
      "t9"
  | 16 ->
      "s0"
  | 17 ->
      "s1"
  | 18 ->
      "s2"
  | 19 ->
      "s3"
  | 20 ->
      "s4"
  | 21 ->
      "s5"
  | 22 ->
      "s6"
  | 23 ->
      "s7"
  | 3 ->
      "v1"
  | 30 ->
      "fp"
  | 28 ->
      "gp"
  | 29 ->
      "sp"
  | 31 ->
      "ra"
  | _ ->
      assert false

(* Calling convention. *)

let parameters =
  [ a0; a1; a2; a3 ]

let result =
  v0

let caller_saved =
  RegisterSet.of_list (
    if Settings.few then
      [ v0; a0; a1; a2; a3; t0; t1; ra ]
    else
      [ v0; a0; a1; a2; a3; t0; t1; t2; t3; t4; t5; t6; t7; t8; t9; ra ]
  )

let callee_saved =
  RegisterSet.of_list (
    if Settings.few then
      [ s0; s1 ]
    else
      [ s0; s1; s2; s3; s4; s5; s6; s7 ]
  )

let allocatable =
  (* The register [$zero] can be viewed as allocatable, but this requires building
     ad hoc interference edges -- see [Build]. In TD7, in order to simplify things,
     this register is made non-allocatable. *)
  RegisterSet.add zero (RegisterSet.union caller_saved callee_saved)

let registers =
  RegisterSet.union allocatable
    (RegisterSet.of_list [ gp; sp; st0; st1 ])