+static LLVMValueRef get_primitive_id(struct lp_build_tgsi_context *bld_base,
+ unsigned swizzle)
+{
+ struct si_shader_context *si_shader_ctx = si_shader_context(bld_base);
+
+ if (swizzle > 0)
+ return bld_base->uint_bld.zero;
+
+ switch (si_shader_ctx->type) {
+ case TGSI_PROCESSOR_TESS_CTRL:
+ return LLVMGetParam(si_shader_ctx->radeon_bld.main_fn,
+ SI_PARAM_PATCH_ID);
+ case TGSI_PROCESSOR_TESS_EVAL:
+ return LLVMGetParam(si_shader_ctx->radeon_bld.main_fn,
+ si_shader_ctx->param_tes_patch_id);
+ case TGSI_PROCESSOR_GEOMETRY:
+ return LLVMGetParam(si_shader_ctx->radeon_bld.main_fn,
+ SI_PARAM_PRIMITIVE_ID);
+ default:
+ assert(0);
+ return bld_base->uint_bld.zero;
+ }
+}
+
+/**
+ * Return the value of tgsi_ind_register for indexing.
+ * This is the indirect index with the constant offset added to it.
+ */
+static LLVMValueRef get_indirect_index(struct si_shader_context *si_shader_ctx,
+ const struct tgsi_ind_register *ind,
+ int rel_index)
+{
+ struct gallivm_state *gallivm = si_shader_ctx->radeon_bld.soa.bld_base.base.gallivm;
+ LLVMValueRef result;
+
+ result = si_shader_ctx->radeon_bld.soa.addr[ind->Index][ind->Swizzle];
+ result = LLVMBuildLoad(gallivm->builder, result, "");
+ result = LLVMBuildAdd(gallivm->builder, result,
+ lp_build_const_int32(gallivm, rel_index), "");
+ return result;
+}
+
+/**
+ * Calculate a dword address given an input or output register and a stride.
+ */
+static LLVMValueRef get_dw_address(struct si_shader_context *si_shader_ctx,
+ const struct tgsi_full_dst_register *dst,
+ const struct tgsi_full_src_register *src,
+ LLVMValueRef vertex_dw_stride,
+ LLVMValueRef base_addr)
+{
+ struct gallivm_state *gallivm = si_shader_ctx->radeon_bld.soa.bld_base.base.gallivm;
+ struct tgsi_shader_info *info = &si_shader_ctx->shader->selector->info;
+ ubyte *name, *index, *array_first;
+ int first, param;
+ struct tgsi_full_dst_register reg;
+
+ /* Set the register description. The address computation is the same
+ * for sources and destinations. */
+ if (src) {
+ reg.Register.File = src->Register.File;
+ reg.Register.Index = src->Register.Index;
+ reg.Register.Indirect = src->Register.Indirect;
+ reg.Register.Dimension = src->Register.Dimension;
+ reg.Indirect = src->Indirect;
+ reg.Dimension = src->Dimension;
+ reg.DimIndirect = src->DimIndirect;
+ } else
+ reg = *dst;
+
+ /* If the register is 2-dimensional (e.g. an array of vertices
+ * in a primitive), calculate the base address of the vertex. */
+ if (reg.Register.Dimension) {
+ LLVMValueRef index;
+
+ if (reg.Dimension.Indirect)
+ index = get_indirect_index(si_shader_ctx, ®.DimIndirect,
+ reg.Dimension.Index);
+ else
+ index = lp_build_const_int32(gallivm, reg.Dimension.Index);
+
+ base_addr = LLVMBuildAdd(gallivm->builder, base_addr,
+ LLVMBuildMul(gallivm->builder, index,
+ vertex_dw_stride, ""), "");
+ }
+
+ /* Get information about the register. */
+ if (reg.Register.File == TGSI_FILE_INPUT) {
+ name = info->input_semantic_name;
+ index = info->input_semantic_index;
+ array_first = info->input_array_first;
+ } else if (reg.Register.File == TGSI_FILE_OUTPUT) {
+ name = info->output_semantic_name;
+ index = info->output_semantic_index;
+ array_first = info->output_array_first;
+ } else {
+ assert(0);
+ return NULL;
+ }
+
+ if (reg.Register.Indirect) {
+ /* Add the relative address of the element. */
+ LLVMValueRef ind_index;
+
+ if (reg.Indirect.ArrayID)
+ first = array_first[reg.Indirect.ArrayID];
+ else
+ first = reg.Register.Index;
+
+ ind_index = get_indirect_index(si_shader_ctx, ®.Indirect,
+ reg.Register.Index - first);
+
+ base_addr = LLVMBuildAdd(gallivm->builder, base_addr,
+ LLVMBuildMul(gallivm->builder, ind_index,
+ lp_build_const_int32(gallivm, 4), ""), "");
+
+ param = si_shader_io_get_unique_index(name[first], index[first]);
+ } else {
+ param = si_shader_io_get_unique_index(name[reg.Register.Index],
+ index[reg.Register.Index]);
+ }
+
+ /* Add the base address of the element. */
+ return LLVMBuildAdd(gallivm->builder, base_addr,
+ lp_build_const_int32(gallivm, param * 4), "");
+}
+
+/**
+ * Load from LDS.
+ *
+ * \param type output value type
+ * \param swizzle offset (typically 0..3); it can be ~0, which loads a vec4
+ * \param dw_addr address in dwords
+ */
+static LLVMValueRef lds_load(struct lp_build_tgsi_context *bld_base,
+ enum tgsi_opcode_type type, unsigned swizzle,
+ LLVMValueRef dw_addr)
+{
+ struct si_shader_context *si_shader_ctx = si_shader_context(bld_base);
+ struct gallivm_state *gallivm = bld_base->base.gallivm;
+ LLVMValueRef value;
+
+ if (swizzle == ~0) {
+ LLVMValueRef values[TGSI_NUM_CHANNELS];
+
+ for (unsigned chan = 0; chan < TGSI_NUM_CHANNELS; chan++)
+ values[chan] = lds_load(bld_base, type, chan, dw_addr);
+
+ return lp_build_gather_values(bld_base->base.gallivm, values,
+ TGSI_NUM_CHANNELS);
+ }
+
+ dw_addr = lp_build_add(&bld_base->uint_bld, dw_addr,
+ lp_build_const_int32(gallivm, swizzle));
+
+ value = build_indexed_load(si_shader_ctx, si_shader_ctx->lds, dw_addr);
+ return LLVMBuildBitCast(gallivm->builder, value,
+ tgsi2llvmtype(bld_base, type), "");
+}
+
+/**
+ * Store to LDS.
+ *
+ * \param swizzle offset (typically 0..3)
+ * \param dw_addr address in dwords
+ * \param value value to store
+ */
+static void lds_store(struct lp_build_tgsi_context * bld_base,
+ unsigned swizzle, LLVMValueRef dw_addr,
+ LLVMValueRef value)
+{
+ struct si_shader_context *si_shader_ctx = si_shader_context(bld_base);
+ struct gallivm_state *gallivm = bld_base->base.gallivm;
+
+ dw_addr = lp_build_add(&bld_base->uint_bld, dw_addr,
+ lp_build_const_int32(gallivm, swizzle));
+
+ value = LLVMBuildBitCast(gallivm->builder, value,
+ LLVMInt32TypeInContext(gallivm->context), "");
+ build_indexed_store(si_shader_ctx, si_shader_ctx->lds,
+ dw_addr, value);
+}
+
+static LLVMValueRef fetch_input_tcs(
+ struct lp_build_tgsi_context *bld_base,
+ const struct tgsi_full_src_register *reg,
+ enum tgsi_opcode_type type, unsigned swizzle)
+{
+ struct si_shader_context *si_shader_ctx = si_shader_context(bld_base);
+ LLVMValueRef dw_addr, stride;
+
+ stride = unpack_param(si_shader_ctx, SI_PARAM_TCS_IN_LAYOUT, 13, 8);
+ dw_addr = get_tcs_in_current_patch_offset(si_shader_ctx);
+ dw_addr = get_dw_address(si_shader_ctx, NULL, reg, stride, dw_addr);
+
+ return lds_load(bld_base, type, swizzle, dw_addr);
+}
+
+static LLVMValueRef fetch_output_tcs(
+ struct lp_build_tgsi_context *bld_base,
+ const struct tgsi_full_src_register *reg,
+ enum tgsi_opcode_type type, unsigned swizzle)
+{
+ struct si_shader_context *si_shader_ctx = si_shader_context(bld_base);
+ LLVMValueRef dw_addr, stride;
+
+ if (reg->Register.Dimension) {
+ stride = unpack_param(si_shader_ctx, SI_PARAM_TCS_OUT_LAYOUT, 13, 8);
+ dw_addr = get_tcs_out_current_patch_offset(si_shader_ctx);
+ dw_addr = get_dw_address(si_shader_ctx, NULL, reg, stride, dw_addr);
+ } else {
+ dw_addr = get_tcs_out_current_patch_data_offset(si_shader_ctx);
+ dw_addr = get_dw_address(si_shader_ctx, NULL, reg, NULL, dw_addr);
+ }
+
+ return lds_load(bld_base, type, swizzle, dw_addr);
+}
+
+static LLVMValueRef fetch_input_tes(
+ struct lp_build_tgsi_context *bld_base,
+ const struct tgsi_full_src_register *reg,
+ enum tgsi_opcode_type type, unsigned swizzle)
+{
+ struct si_shader_context *si_shader_ctx = si_shader_context(bld_base);
+ LLVMValueRef dw_addr, stride;
+
+ if (reg->Register.Dimension) {
+ stride = unpack_param(si_shader_ctx, SI_PARAM_TCS_OUT_LAYOUT, 13, 8);
+ dw_addr = get_tcs_out_current_patch_offset(si_shader_ctx);
+ dw_addr = get_dw_address(si_shader_ctx, NULL, reg, stride, dw_addr);
+ } else {
+ dw_addr = get_tcs_out_current_patch_data_offset(si_shader_ctx);
+ dw_addr = get_dw_address(si_shader_ctx, NULL, reg, NULL, dw_addr);
+ }
+
+ return lds_load(bld_base, type, swizzle, dw_addr);
+}
+
+static void store_output_tcs(struct lp_build_tgsi_context * bld_base,
+ const struct tgsi_full_instruction * inst,
+ const struct tgsi_opcode_info * info,
+ LLVMValueRef dst[4])
+{
+ struct si_shader_context *si_shader_ctx = si_shader_context(bld_base);
+ const struct tgsi_full_dst_register *reg = &inst->Dst[0];
+ unsigned chan_index;
+ LLVMValueRef dw_addr, stride;
+
+ /* Only handle per-patch and per-vertex outputs here.
+ * Vectors will be lowered to scalars and this function will be called again.
+ */
+ if (reg->Register.File != TGSI_FILE_OUTPUT ||
+ (dst[0] && LLVMGetTypeKind(LLVMTypeOf(dst[0])) == LLVMVectorTypeKind)) {
+ radeon_llvm_emit_store(bld_base, inst, info, dst);
+ return;
+ }
+
+ if (reg->Register.Dimension) {
+ stride = unpack_param(si_shader_ctx, SI_PARAM_TCS_OUT_LAYOUT, 13, 8);
+ dw_addr = get_tcs_out_current_patch_offset(si_shader_ctx);
+ dw_addr = get_dw_address(si_shader_ctx, reg, NULL, stride, dw_addr);
+ } else {
+ dw_addr = get_tcs_out_current_patch_data_offset(si_shader_ctx);
+ dw_addr = get_dw_address(si_shader_ctx, reg, NULL, NULL, dw_addr);
+ }
+
+ TGSI_FOR_EACH_DST0_ENABLED_CHANNEL(inst, chan_index) {
+ LLVMValueRef value = dst[chan_index];
+
+ if (inst->Instruction.Saturate)
+ value = radeon_llvm_saturate(bld_base, value);
+
+ lds_store(bld_base, chan_index, dw_addr, value);
+ }
+}
+