/*
 * Copyright 2009 Nicolai Hähnle <nhaehnle@gmail.com>
 * SPDX-License-Identifier: MIT
 */

#ifndef RADEON_COMPILER_H
#define RADEON_COMPILER_H

#include <stdbool.h>
#include <string.h>

#include "radeon_code.h"
#include "radeon_program.h"

#include "util/ralloc.h"

#define RC_DBG_LOG (1 << 0)

#define RC_MATH_DX   0x00
#define RC_MATH_IEEE 0x01
#define RC_MATH_FF   0x02

struct rc_swizzle_caps;

enum rc_program_type { RC_VERTEX_PROGRAM,
                       RC_FRAGMENT_PROGRAM,
                       RC_NUM_PROGRAM_TYPES };

/**
 * Generic helper for growing an array that has separate size/count
 * and reserved counters to accommodate up to num new elements.
 *
 * \note Size is not changed by this macro.
 *
 * \warning Array, Size, Reserved have to be lvalues and may be evaluated
 * several times.
 */
#define rc_array_reserve(pool, type, array, size, reserved, num)           \
   do {                                                                    \
      unsigned int _num = (num);                                           \
      if ((size) + _num > (reserved)) {                                    \
         unsigned int newreserve = (reserved) * 2;                         \
         type *newarray;                                                   \
         if (newreserve < _num)                                            \
            newreserve = 4 * _num; /* arbitrary heuristic */               \
         newarray = linear_alloc_array((pool), type, newreserve);          \
         if (size)                                                         \
            memcpy(newarray, (array), (size) * sizeof(type));              \
         (array) = newarray;                                               \
         (reserved) = newreserve;                                          \
      }                                                                    \
   } while (0)

struct radeon_compiler {
   linear_ctx *Pool;
   struct rc_program Program;
   const struct rc_regalloc_state *regalloc_state;
   struct util_debug_callback *debug;
   enum rc_program_type type;
   unsigned Debug : 2;
   unsigned Error : 1;
   char *ErrorMsg;

   /* Hardware specification. */
   unsigned is_r400 : 1;
   unsigned is_r500 : 1;
   unsigned has_half_swizzles : 1;
   unsigned has_presub : 1;
   unsigned has_omod : 1;
   unsigned disable_optimizations : 1;
   unsigned max_temp_regs;
   unsigned max_constants;
   int max_alu_insts;
   unsigned max_tex_insts;

   int max_temp_index;

   /* Whether to remove unused constants and empty holes in constant space. */
   unsigned remove_unused_constants : 1;

   /* Math compatibility mode, for some PVS opcodes and for multiply by zero rules on R5xx */
   unsigned math_rules : 2;

   /**
    * Variables used internally, not be touched by callers
    * of the compiler
    */
   /*@{*/
   const struct rc_swizzle_caps *SwizzleCaps;
   /*@}*/
};

void rc_init(struct radeon_compiler *c, const struct rc_regalloc_state *rs);
void rc_destroy(struct radeon_compiler *c);

void rc_debug(struct radeon_compiler *c, const char *fmt, ...);
void rc_error(struct radeon_compiler *c, const char *fmt, ...);

int rc_if_fail_helper(struct radeon_compiler *c, const char *file, int line, const char *assertion);

/**
 * This macro acts like an if-statement that can be used to implement
 * non-aborting assertions in the compiler.
 *
 * It checks whether \p cond is true. If not, an internal compiler error is
 * flagged and the if-clause is run.
 *
 * A typical use-case would be:
 *
 *  if (rc_assert(c, condition-that-must-be-true))
 *  	return;
 */
#define rc_assert(c, cond) (!(cond) && rc_if_fail_helper(c, __FILE__, __LINE__, #cond))

void rc_mark_unused_channels(struct radeon_compiler *c, void *user);
void rc_calculate_inputs_outputs(struct radeon_compiler *c);
void rc_transform_fragment_face(struct radeon_compiler *c, unsigned face);

struct r300_fragment_program_compiler {
   struct radeon_compiler Base;
   struct rX00_fragment_program_code *code;
   /* Optional transformations and features. */
   struct r300_fragment_program_external_state state;
   /* Register corresponding to the depthbuffer. */
   unsigned OutputDepth;
   /* Registers corresponding to the four colorbuffers. */
   unsigned OutputColor[4];

   void *UserData;
   void (*AllocateHwInputs)(struct r300_fragment_program_compiler *c,
                            void (*allocate)(void *data, unsigned input, unsigned hwreg),
                            void *mydata);
};

void r3xx_compile_fragment_program(struct r300_fragment_program_compiler *c);

struct r300_vertex_program_compiler {
   struct radeon_compiler Base;
   struct r300_vertex_program_code *code;
   uint32_t RequiredOutputs;

   void *UserData;
   void (*SetHwInputOutput)(struct r300_vertex_program_compiler *c);
};

void r3xx_compile_vertex_program(struct r300_vertex_program_compiler *c);
void rc_vert_fc(struct radeon_compiler *compiler, void *user);
void r300_vertex_program_dump(struct radeon_compiler *compiler, void *user);

struct radeon_compiler_pass {
   const char *name;                                   /* Name of the pass. */
   int dump;                                           /* Dump the program if Debug == 1? */
   int predicate;                                      /* Run this pass? */
   void (*run)(struct radeon_compiler *c, void *user); /* The main entrypoint. */
   void *user;                                         /* Optional parameter which is passed to the run function. */
};

struct rc_program_stats {
   enum rc_program_type type;
   unsigned num_cycles;
   unsigned num_consts;
   unsigned num_insts;
   unsigned num_fc_insts;
   unsigned num_tex_insts;
   unsigned num_rgb_insts;
   unsigned num_alpha_insts;
   unsigned num_pred_insts;
   unsigned num_presub_ops;
   unsigned num_temp_regs;
   unsigned num_omod_ops;
   unsigned num_inline_literals;
   unsigned num_loops;
};

void rc_get_stats(struct radeon_compiler *c, struct rc_program_stats *s);

/* Executes a list of compiler passes given in the parameter 'list'. */
bool rc_run_compiler_passes(struct radeon_compiler *c, struct radeon_compiler_pass *list);
void rc_run_compiler(struct radeon_compiler *c, struct radeon_compiler_pass *list);
void rc_validate_final_shader(struct radeon_compiler *c, void *user);

#endif /* RADEON_COMPILER_H */
