/*
 * Copyright © 2019 Valve Corporation.
 * Copyright © 2016 Red Hat.
 * Copyright © 2016 Bas Nieuwenhuizen
 *
 * based in part on anv driver which is:
 * Copyright © 2015 Intel Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include "radv_private.h"
#include "radv_shader.h"
#include "radv_shader_args.h"

static void
set_loc(struct radv_userdata_info *ud_info, uint8_t *sgpr_idx,
	uint8_t num_sgprs)
{
	ud_info->sgpr_idx = *sgpr_idx;
	ud_info->num_sgprs = num_sgprs;
	*sgpr_idx += num_sgprs;
}

static void
set_loc_shader(struct radv_shader_args *args, int idx, uint8_t *sgpr_idx,
	       uint8_t num_sgprs)
{
	struct radv_userdata_info *ud_info =
		&args->shader_info->user_sgprs_locs.shader_data[idx];
	assert(ud_info);

	set_loc(ud_info, sgpr_idx, num_sgprs);
}

static void
set_loc_shader_ptr(struct radv_shader_args *args, int idx, uint8_t *sgpr_idx)
{
	bool use_32bit_pointers = idx != AC_UD_SCRATCH_RING_OFFSETS;

	set_loc_shader(args, idx, sgpr_idx, use_32bit_pointers ? 1 : 2);
}

static void
set_loc_desc(struct radv_shader_args *args, int idx, uint8_t *sgpr_idx)
{
	struct radv_userdata_locations *locs =
		&args->shader_info->user_sgprs_locs;
	struct radv_userdata_info *ud_info = &locs->descriptor_sets[idx];
	assert(ud_info);

	set_loc(ud_info, sgpr_idx, 1);

	locs->descriptor_sets_enabled |= 1u << idx;
}

struct user_sgpr_info {
	bool indirect_all_descriptor_sets;
	uint8_t remaining_sgprs;
};

static bool needs_view_index_sgpr(struct radv_shader_args *args,
				  gl_shader_stage stage)
{
	switch (stage) {
	case MESA_SHADER_VERTEX:
		if (args->shader_info->needs_multiview_view_index ||
		    (!args->options->key.vs_common_out.as_es && !args->options->key.vs_common_out.as_ls && args->options->key.has_multiview_view_index))
			return true;
		break;
	case MESA_SHADER_TESS_EVAL:
		if (args->shader_info->needs_multiview_view_index || (!args->options->key.vs_common_out.as_es && args->options->key.has_multiview_view_index))
			return true;
		break;
	case MESA_SHADER_TESS_CTRL:
		if (args->shader_info->needs_multiview_view_index)
			return true;
		break;
	case MESA_SHADER_GEOMETRY:
		if (args->shader_info->needs_multiview_view_index ||
		    (args->options->key.vs_common_out.as_ngg &&
		     args->options->key.has_multiview_view_index))
			return true;
		break;
	default:
		break;
	}
	return false;
}

static uint8_t
count_vs_user_sgprs(struct radv_shader_args *args)
{
	uint8_t count = 0;

	if (args->shader_info->vs.has_vertex_buffers)
		count++;
	count += args->shader_info->vs.needs_draw_id ? 3 : 2;

	return count;
}

static void allocate_inline_push_consts(struct radv_shader_args *args,
					struct user_sgpr_info *user_sgpr_info)
{
	uint8_t remaining_sgprs = user_sgpr_info->remaining_sgprs;

	/* Only supported if shaders use push constants. */
	if (args->shader_info->min_push_constant_used == UINT8_MAX)
		return;

	/* Only supported if shaders don't have indirect push constants. */
	if (args->shader_info->has_indirect_push_constants)
		return;

	/* Only supported for 32-bit push constants. */
	if (!args->shader_info->has_only_32bit_push_constants)
		return;

	uint8_t num_push_consts =
		(args->shader_info->max_push_constant_used -
		 args->shader_info->min_push_constant_used) / 4;

	/* Check if the number of user SGPRs is large enough. */
	if (num_push_consts < remaining_sgprs) {
		args->shader_info->num_inline_push_consts = num_push_consts;
	} else {
		args->shader_info->num_inline_push_consts = remaining_sgprs;
	}

	/* Clamp to the maximum number of allowed inlined push constants. */
	if (args->shader_info->num_inline_push_consts > AC_MAX_INLINE_PUSH_CONSTS)
		args->shader_info->num_inline_push_consts = AC_MAX_INLINE_PUSH_CONSTS;

	if (args->shader_info->num_inline_push_consts == num_push_consts &&
	    !args->shader_info->loads_dynamic_offsets) {
		/* Disable the default push constants path if all constants are
		 * inlined and if shaders don't use dynamic descriptors.
		 */
		args->shader_info->loads_push_constants = false;
	}

	args->shader_info->base_inline_push_consts =
		args->shader_info->min_push_constant_used / 4;
}

static void allocate_user_sgprs(struct radv_shader_args *args,
				gl_shader_stage stage,
				bool has_previous_stage,
				gl_shader_stage previous_stage,
				bool needs_view_index,
				struct user_sgpr_info *user_sgpr_info)
{
	uint8_t user_sgpr_count = 0;

	memset(user_sgpr_info, 0, sizeof(struct user_sgpr_info));

	/* 2 user sgprs will always be allocated for scratch/rings */
	user_sgpr_count += 2;

	switch (stage) {
	case MESA_SHADER_COMPUTE:
		if (args->shader_info->cs.uses_grid_size)
			user_sgpr_count += 3;
		break;
	case MESA_SHADER_FRAGMENT:
		user_sgpr_count += args->shader_info->ps.needs_sample_positions;
		break;
	case MESA_SHADER_VERTEX:
		if (!args->is_gs_copy_shader)
			user_sgpr_count += count_vs_user_sgprs(args);
		break;
	case MESA_SHADER_TESS_CTRL:
		if (has_previous_stage) {
			if (previous_stage == MESA_SHADER_VERTEX)
				user_sgpr_count += count_vs_user_sgprs(args);
		}
		break;
	case MESA_SHADER_TESS_EVAL:
		break;
	case MESA_SHADER_GEOMETRY:
		if (has_previous_stage) {
			if (previous_stage == MESA_SHADER_VERTEX) {
				user_sgpr_count += count_vs_user_sgprs(args);
			}
		}
		break;
	default:
		break;
	}

	if (needs_view_index)
		user_sgpr_count++;

	if (args->shader_info->loads_push_constants)
		user_sgpr_count++;

	if (args->shader_info->so.num_outputs)
		user_sgpr_count++;

	uint32_t available_sgprs = args->options->chip_class >= GFX9 && stage != MESA_SHADER_COMPUTE ? 32 : 16;
	uint32_t remaining_sgprs = available_sgprs - user_sgpr_count;
	uint32_t num_desc_set =
		util_bitcount(args->shader_info->desc_set_used_mask);

	if (remaining_sgprs < num_desc_set) {
		user_sgpr_info->indirect_all_descriptor_sets = true;
		user_sgpr_info->remaining_sgprs = remaining_sgprs - 1;
	} else {
		user_sgpr_info->remaining_sgprs = remaining_sgprs - num_desc_set;
	}

	allocate_inline_push_consts(args, user_sgpr_info);
}

static void
declare_global_input_sgprs(struct radv_shader_args *args,
			   const struct user_sgpr_info *user_sgpr_info)
{
	/* 1 for each descriptor set */
	if (!user_sgpr_info->indirect_all_descriptor_sets) {
		uint32_t mask = args->shader_info->desc_set_used_mask;

		while (mask) {
			int i = u_bit_scan(&mask);

			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_CONST_PTR,
				   &args->descriptor_sets[i]);
		}
	} else {
		ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_CONST_PTR_PTR,
			   &args->descriptor_sets[0]);
	}

	if (args->shader_info->loads_push_constants) {
		/* 1 for push constants and dynamic descriptors */
		ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_CONST_PTR,
			   &args->ac.push_constants);
	}

	for (unsigned i = 0; i < args->shader_info->num_inline_push_consts; i++) {
		ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
			   &args->ac.inline_push_consts[i]);
	}
	args->ac.num_inline_push_consts = args->shader_info->num_inline_push_consts;
	args->ac.base_inline_push_consts = args->shader_info->base_inline_push_consts;

	if (args->shader_info->so.num_outputs) {
		ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_CONST_DESC_PTR,
			   &args->streamout_buffers);
	}
}

static void
declare_vs_specific_input_sgprs(struct radv_shader_args *args,
				gl_shader_stage stage,
				bool has_previous_stage,
				gl_shader_stage previous_stage)
{
	if (!args->is_gs_copy_shader &&
	    (stage == MESA_SHADER_VERTEX ||
	     (has_previous_stage && previous_stage == MESA_SHADER_VERTEX))) {
		if (args->shader_info->vs.has_vertex_buffers) {
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_CONST_DESC_PTR,
				   &args->vertex_buffers);
		}
		ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, &args->ac.base_vertex);
		ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, &args->ac.start_instance);
		if (args->shader_info->vs.needs_draw_id) {
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, &args->ac.draw_id);
		}
	}
}

static void
declare_vs_input_vgprs(struct radv_shader_args *args)
{
	ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, &args->ac.vertex_id);
	if (!args->is_gs_copy_shader) {
		if (args->options->key.vs_common_out.as_ls) {
			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, &args->rel_auto_id);
			if (args->options->chip_class >= GFX10) {
				ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, NULL); /* user vgpr */
				ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, &args->ac.instance_id);
			} else {
				ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, &args->ac.instance_id);
				ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, NULL); /* unused */
			}
		} else {
			if (args->options->chip_class >= GFX10) {
				if (args->options->key.vs_common_out.as_ngg) {
					ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, NULL); /* user vgpr */
					ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, NULL); /* user vgpr */
					ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, &args->ac.instance_id);
				} else {
					ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, NULL); /* unused */
					ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, &args->vs_prim_id);
					ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, &args->ac.instance_id);
				}
			} else {
				ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, &args->ac.instance_id);
				ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, &args->vs_prim_id);
				ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, NULL); /* unused */
			}
		}
	}
}

static void
declare_streamout_sgprs(struct radv_shader_args *args, gl_shader_stage stage)
{
	int i;

	if (args->options->use_ngg_streamout) {
		if (stage == MESA_SHADER_TESS_EVAL)
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, NULL);
		return;
	}

	/* Streamout SGPRs. */
	if (args->shader_info->so.num_outputs) {
		assert(stage == MESA_SHADER_VERTEX ||
		       stage == MESA_SHADER_TESS_EVAL);

		ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, &args->streamout_config);
		ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, &args->streamout_write_idx);
	} else if (stage == MESA_SHADER_TESS_EVAL) {
		ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, NULL);
	}

	/* A streamout buffer offset is loaded if the stride is non-zero. */
	for (i = 0; i < 4; i++) {
		if (!args->shader_info->so.strides[i])
			continue;

		ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, &args->streamout_offset[i]);
	}
}

static void
declare_tes_input_vgprs(struct radv_shader_args *args)
{
	ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_FLOAT, &args->tes_u);
	ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_FLOAT, &args->tes_v);
	ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, &args->tes_rel_patch_id);
	ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, &args->ac.tes_patch_id);
}

static void
set_global_input_locs(struct radv_shader_args *args,
		      const struct user_sgpr_info *user_sgpr_info,
		      uint8_t *user_sgpr_idx)
{
	uint32_t mask = args->shader_info->desc_set_used_mask;

	if (!user_sgpr_info->indirect_all_descriptor_sets) {
		while (mask) {
			int i = u_bit_scan(&mask);

			set_loc_desc(args, i, user_sgpr_idx);
		}
	} else {
		set_loc_shader_ptr(args, AC_UD_INDIRECT_DESCRIPTOR_SETS,
				   user_sgpr_idx);

		args->shader_info->need_indirect_descriptor_sets = true;
	}

	if (args->shader_info->loads_push_constants) {
		set_loc_shader_ptr(args, AC_UD_PUSH_CONSTANTS, user_sgpr_idx);
	}

	if (args->shader_info->num_inline_push_consts) {
		set_loc_shader(args, AC_UD_INLINE_PUSH_CONSTANTS, user_sgpr_idx,
			       args->shader_info->num_inline_push_consts);
	}

	if (args->streamout_buffers.used) {
		set_loc_shader_ptr(args, AC_UD_STREAMOUT_BUFFERS,
				   user_sgpr_idx);
	}
}

static void
set_vs_specific_input_locs(struct radv_shader_args *args,
			   gl_shader_stage stage, bool has_previous_stage,
			   gl_shader_stage previous_stage,
			   uint8_t *user_sgpr_idx)
{
	if (!args->is_gs_copy_shader &&
	    (stage == MESA_SHADER_VERTEX ||
	     (has_previous_stage && previous_stage == MESA_SHADER_VERTEX))) {
		if (args->shader_info->vs.has_vertex_buffers) {
			set_loc_shader_ptr(args, AC_UD_VS_VERTEX_BUFFERS,
					   user_sgpr_idx);
		}

		unsigned vs_num = 2;
		if (args->shader_info->vs.needs_draw_id)
			vs_num++;

		set_loc_shader(args, AC_UD_VS_BASE_VERTEX_START_INSTANCE,
			       user_sgpr_idx, vs_num);
	}
}

/* Returns whether the stage is a stage that can be directly before the GS */
static bool is_pre_gs_stage(gl_shader_stage stage)
{
	return stage == MESA_SHADER_VERTEX || stage == MESA_SHADER_TESS_EVAL;
}

void
radv_declare_shader_args(struct radv_shader_args *args,
			 gl_shader_stage stage,
			 bool has_previous_stage,
			 gl_shader_stage previous_stage)
{
	struct user_sgpr_info user_sgpr_info;
	bool needs_view_index = needs_view_index_sgpr(args, stage);

	if (args->options->chip_class >= GFX10) {
		if (is_pre_gs_stage(stage) && args->options->key.vs_common_out.as_ngg) {
			/* On GFX10, VS is merged into GS for NGG. */
			previous_stage = stage;
			stage = MESA_SHADER_GEOMETRY;
			has_previous_stage = true;
		}
	}

	for (int i = 0; i < MAX_SETS; i++)
		args->shader_info->user_sgprs_locs.descriptor_sets[i].sgpr_idx = -1;
	for (int i = 0; i < AC_UD_MAX_UD; i++)
		args->shader_info->user_sgprs_locs.shader_data[i].sgpr_idx = -1;


	allocate_user_sgprs(args, stage, has_previous_stage,
			    previous_stage, needs_view_index, &user_sgpr_info);

	if (args->options->explicit_scratch_args) {
		ac_add_arg(&args->ac, AC_ARG_SGPR, 2, AC_ARG_CONST_DESC_PTR,
			   &args->ring_offsets);
	}

	switch (stage) {
	case MESA_SHADER_COMPUTE:
		declare_global_input_sgprs(args, &user_sgpr_info);

		if (args->shader_info->cs.uses_grid_size) {
			ac_add_arg(&args->ac, AC_ARG_SGPR, 3, AC_ARG_INT,
				   &args->ac.num_work_groups);
		}

		for (int i = 0; i < 3; i++) {
			if (args->shader_info->cs.uses_block_id[i]) {
				ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
					   &args->ac.workgroup_ids[i]);
			}
		}

		if (args->shader_info->cs.uses_local_invocation_idx) {
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
				   &args->ac.tg_size);
		}

		if (args->options->explicit_scratch_args) {
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
				   &args->scratch_offset);
		}

		ac_add_arg(&args->ac, AC_ARG_VGPR, 3, AC_ARG_INT,
			   &args->ac.local_invocation_ids);
		break;
	case MESA_SHADER_VERTEX:
		declare_global_input_sgprs(args, &user_sgpr_info);

		declare_vs_specific_input_sgprs(args, stage, has_previous_stage,
						previous_stage);

		if (needs_view_index) {
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
				   &args->ac.view_index);
		}

		if (args->options->key.vs_common_out.as_es) {
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
				&args->es2gs_offset);
		} else if (args->options->key.vs_common_out.as_ls) {
			/* no extra parameters */
		} else {
			declare_streamout_sgprs(args, stage);
		}

		if (args->options->explicit_scratch_args) {
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
				   &args->scratch_offset);
		}

		declare_vs_input_vgprs(args);
		break;
	case MESA_SHADER_TESS_CTRL:
		if (has_previous_stage) {
			// First 6 system regs
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, &args->oc_lds);
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
				   &args->merged_wave_info);
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
				   &args->tess_factor_offset);

			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, &args->scratch_offset);
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, NULL); // unknown
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, NULL); // unknown

			declare_global_input_sgprs(args, &user_sgpr_info);

			declare_vs_specific_input_sgprs(args, stage,
							has_previous_stage,
							previous_stage);

			if (needs_view_index) {
				ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
					   &args->ac.view_index);
			}

			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT,
				  &args->ac.tcs_patch_id);
			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT,
				   &args->ac.tcs_rel_ids);

			declare_vs_input_vgprs(args);
		} else {
			declare_global_input_sgprs(args, &user_sgpr_info);

			if (needs_view_index) {
				ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
					   &args->ac.view_index);
			}

			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, &args->oc_lds);
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
				   &args->tess_factor_offset);
			if (args->options->explicit_scratch_args) {
				ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
					   &args->scratch_offset);
			}
			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT,
				   &args->ac.tcs_patch_id);
			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT,
				   &args->ac.tcs_rel_ids);
		}
		break;
	case MESA_SHADER_TESS_EVAL:
		declare_global_input_sgprs(args, &user_sgpr_info);

		if (needs_view_index)
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
				&args->ac.view_index);

		if (args->options->key.vs_common_out.as_es) {
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, &args->oc_lds);
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, NULL);
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
				&args->es2gs_offset);
		} else {
			declare_streamout_sgprs(args, stage);
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, &args->oc_lds);
		}
		if (args->options->explicit_scratch_args) {
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
				   &args->scratch_offset);
		}
		declare_tes_input_vgprs(args);
		break;
	case MESA_SHADER_GEOMETRY:
		if (has_previous_stage) {
			// First 6 system regs
			if (args->options->key.vs_common_out.as_ngg) {
				ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
					&args->gs_tg_info);
			} else {
				ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
					&args->gs2vs_offset);
			}

			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
				   &args->merged_wave_info);
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, &args->oc_lds);

			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, &args->scratch_offset);
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, NULL); // unknown
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, NULL); // unknown

			declare_global_input_sgprs(args, &user_sgpr_info);

			if (previous_stage != MESA_SHADER_TESS_EVAL) {
				declare_vs_specific_input_sgprs(args, stage,
								has_previous_stage,
								previous_stage);
			}

			if (needs_view_index) {
				ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
					   &args->ac.view_index);
			}

			if (args->options->key.vs_common_out.as_ngg) {
				ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
					   &args->ngg_gs_state);
			}

			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT,
				   &args->gs_vtx_offset[0]);
			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT,
				   &args->gs_vtx_offset[2]);
			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT,
				   &args->ac.gs_prim_id);
			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT,
				   &args->ac.gs_invocation_id);
			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT,
				   &args->gs_vtx_offset[4]);

			if (previous_stage == MESA_SHADER_VERTEX) {
				declare_vs_input_vgprs(args);
			} else {
				declare_tes_input_vgprs(args);
			}
		} else {
			declare_global_input_sgprs(args, &user_sgpr_info);

			if (needs_view_index) {
				ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
					   &args->ac.view_index);
			}

			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, &args->gs2vs_offset);
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, &args->gs_wave_id);
			if (args->options->explicit_scratch_args) {
				ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
					   &args->scratch_offset);
			}
			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT,
				   &args->gs_vtx_offset[0]);
			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT,
				   &args->gs_vtx_offset[1]);
			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT,
				   &args->ac.gs_prim_id);
			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT,
				   &args->gs_vtx_offset[2]);
			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT,
				   &args->gs_vtx_offset[3]);
			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT,
				   &args->gs_vtx_offset[4]);
			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT,
				   &args->gs_vtx_offset[5]);
			ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT,
				   &args->ac.gs_invocation_id);
		}
		break;
	case MESA_SHADER_FRAGMENT:
		declare_global_input_sgprs(args, &user_sgpr_info);

		ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT, &args->ac.prim_mask);
		if (args->options->explicit_scratch_args) {
			ac_add_arg(&args->ac, AC_ARG_SGPR, 1, AC_ARG_INT,
				   &args->scratch_offset);
		}
		ac_add_arg(&args->ac, AC_ARG_VGPR, 2, AC_ARG_INT, &args->ac.persp_sample);
		ac_add_arg(&args->ac, AC_ARG_VGPR, 2, AC_ARG_INT, &args->ac.persp_center);
		ac_add_arg(&args->ac, AC_ARG_VGPR, 2, AC_ARG_INT, &args->ac.persp_centroid);
		ac_add_arg(&args->ac, AC_ARG_VGPR, 3, AC_ARG_INT, &args->ac.pull_model);
		ac_add_arg(&args->ac, AC_ARG_VGPR, 2, AC_ARG_INT, &args->ac.linear_sample);
		ac_add_arg(&args->ac, AC_ARG_VGPR, 2, AC_ARG_INT, &args->ac.linear_center);
		ac_add_arg(&args->ac, AC_ARG_VGPR, 2, AC_ARG_INT, &args->ac.linear_centroid);
		ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_FLOAT, NULL);  /* line stipple tex */
		ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_FLOAT, &args->ac.frag_pos[0]);
		ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_FLOAT, &args->ac.frag_pos[1]);
		ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_FLOAT, &args->ac.frag_pos[2]);
		ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_FLOAT, &args->ac.frag_pos[3]);
		ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, &args->ac.front_face);
		ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, &args->ac.ancillary);
		ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, &args->ac.sample_coverage);
		ac_add_arg(&args->ac, AC_ARG_VGPR, 1, AC_ARG_INT, NULL);  /* fixed pt */
		break;
	default:
		unreachable("Shader stage not implemented");
	}

	args->shader_info->num_input_vgprs = 0;
	args->shader_info->num_input_sgprs = 2;
	args->shader_info->num_input_sgprs += args->ac.num_sgprs_used;
	args->shader_info->num_input_vgprs = args->ac.num_vgprs_used;

	uint8_t user_sgpr_idx = 0;

	set_loc_shader_ptr(args, AC_UD_SCRATCH_RING_OFFSETS,
			   &user_sgpr_idx);

	/* For merged shaders the user SGPRs start at 8, with 8 system SGPRs in front (including
	 * the rw_buffers at s0/s1. With user SGPR0 = s8, lets restart the count from 0 */
	if (has_previous_stage)
		user_sgpr_idx = 0;

	set_global_input_locs(args, &user_sgpr_info, &user_sgpr_idx);

	switch (stage) {
	case MESA_SHADER_COMPUTE:
		if (args->shader_info->cs.uses_grid_size) {
			set_loc_shader(args, AC_UD_CS_GRID_SIZE,
				       &user_sgpr_idx, 3);
		}
		break;
	case MESA_SHADER_VERTEX:
		set_vs_specific_input_locs(args, stage, has_previous_stage,
					   previous_stage, &user_sgpr_idx);
		if (args->ac.view_index.used)
			set_loc_shader(args, AC_UD_VIEW_INDEX, &user_sgpr_idx, 1);
		break;
	case MESA_SHADER_TESS_CTRL:
		set_vs_specific_input_locs(args, stage, has_previous_stage,
					   previous_stage, &user_sgpr_idx);
		if (args->ac.view_index.used)
			set_loc_shader(args, AC_UD_VIEW_INDEX, &user_sgpr_idx, 1);
		break;
	case MESA_SHADER_TESS_EVAL:
		if (args->ac.view_index.used)
			set_loc_shader(args, AC_UD_VIEW_INDEX, &user_sgpr_idx, 1);
		break;
	case MESA_SHADER_GEOMETRY:
		if (has_previous_stage) {
			if (previous_stage == MESA_SHADER_VERTEX)
				set_vs_specific_input_locs(args, stage,
							   has_previous_stage,
							   previous_stage,
							   &user_sgpr_idx);
		}
		if (args->ac.view_index.used)
			set_loc_shader(args, AC_UD_VIEW_INDEX, &user_sgpr_idx, 1);

		if (args->ngg_gs_state.used)
			set_loc_shader(args, AC_UD_NGG_GS_STATE, &user_sgpr_idx, 1);
		break;
	case MESA_SHADER_FRAGMENT:
		break;
	default:
		unreachable("Shader stage not implemented");
	}

	args->shader_info->num_user_sgprs = user_sgpr_idx;
}

