diff options
author | Jonathan Gray <jsg@cvs.openbsd.org> | 2015-11-22 02:46:45 +0000 |
---|---|---|
committer | Jonathan Gray <jsg@cvs.openbsd.org> | 2015-11-22 02:46:45 +0000 |
commit | 0784c49c0f8fcc8b3abd4c9286d9fd8bc089dd7d (patch) | |
tree | a6394e3e264a0f80b57f4ce0f5d9526aa543d4b0 /lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_tgsi.c | |
parent | d91d0007eecf589ea5699e34aa4d748fce2c57b2 (diff) |
import Mesa 11.0.6
Diffstat (limited to 'lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_tgsi.c')
-rw-r--r-- | lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_tgsi.c | 531 |
1 files changed, 531 insertions, 0 deletions
diff --git a/lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_tgsi.c b/lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_tgsi.c new file mode 100644 index 000000000..c4ae30461 --- /dev/null +++ b/lib/mesa/src/gallium/auxiliary/gallivm/lp_bld_tgsi.c @@ -0,0 +1,531 @@ +/************************************************************************** + * + * Copyright 2011-2012 Advanced Micro Devices, Inc. + * Copyright 2010 VMware, Inc. + * Copyright 2009 VMware, Inc. + * Copyright 2007-2008 VMware, Inc. + * All Rights Reserved. + * + * 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, sub license, 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 NON-INFRINGEMENT. + * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS 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 "gallivm/lp_bld_tgsi.h" + +#include "gallivm/lp_bld_arit.h" +#include "gallivm/lp_bld_gather.h" +#include "gallivm/lp_bld_init.h" +#include "gallivm/lp_bld_intr.h" +#include "tgsi/tgsi_info.h" +#include "tgsi/tgsi_parse.h" +#include "tgsi/tgsi_util.h" +#include "util/u_memory.h" + +/* The user is responsible for freeing list->instructions */ +unsigned lp_bld_tgsi_list_init(struct lp_build_tgsi_context * bld_base) +{ + bld_base->instructions = (struct tgsi_full_instruction *) + MALLOC( LP_MAX_INSTRUCTIONS * sizeof(struct tgsi_full_instruction) ); + if (!bld_base->instructions) { + return 0; + } + bld_base->max_instructions = LP_MAX_INSTRUCTIONS; + return 1; +} + + +unsigned lp_bld_tgsi_add_instruction( + struct lp_build_tgsi_context * bld_base, + const struct tgsi_full_instruction *inst_to_add) +{ + + if (bld_base->num_instructions == bld_base->max_instructions) { + struct tgsi_full_instruction *instructions; + instructions = REALLOC(bld_base->instructions, bld_base->max_instructions + * sizeof(struct tgsi_full_instruction), + (bld_base->max_instructions + LP_MAX_INSTRUCTIONS) + * sizeof(struct tgsi_full_instruction)); + if (!instructions) { + return 0; + } + bld_base->instructions = instructions; + bld_base->max_instructions += LP_MAX_INSTRUCTIONS; + } + memcpy(bld_base->instructions + bld_base->num_instructions, inst_to_add, + sizeof(bld_base->instructions[0])); + + bld_base->num_instructions++; + + return 1; +} + + +/** + * This function assumes that all the args in emit_data have been set. + */ +static void +lp_build_action_set_dst_type( + struct lp_build_emit_data * emit_data, + struct lp_build_tgsi_context *bld_base, + unsigned tgsi_opcode) +{ + if (emit_data->arg_count == 0) { + emit_data->dst_type = LLVMVoidTypeInContext(bld_base->base.gallivm->context); + } else { + /* XXX: Not all opcodes have the same src and dst types. */ + emit_data->dst_type = LLVMTypeOf(emit_data->args[0]); + } +} + +void +lp_build_tgsi_intrinsic( + const struct lp_build_tgsi_action * action, + struct lp_build_tgsi_context * bld_base, + struct lp_build_emit_data * emit_data) +{ + struct lp_build_context * base = &bld_base->base; + emit_data->output[emit_data->chan] = lp_build_intrinsic( + base->gallivm->builder, action->intr_name, + emit_data->dst_type, emit_data->args, emit_data->arg_count, 0); +} + +LLVMValueRef +lp_build_emit_llvm( + struct lp_build_tgsi_context *bld_base, + unsigned tgsi_opcode, + struct lp_build_emit_data * emit_data) +{ + struct lp_build_tgsi_action * action = &bld_base->op_actions[tgsi_opcode]; + /* XXX: Assert that this is a componentwise or replicate instruction */ + + lp_build_action_set_dst_type(emit_data, bld_base, tgsi_opcode); + emit_data->chan = 0; + assert(action->emit); + action->emit(action, bld_base, emit_data); + return emit_data->output[0]; +} + +LLVMValueRef +lp_build_emit_llvm_unary( + struct lp_build_tgsi_context *bld_base, + unsigned tgsi_opcode, + LLVMValueRef arg0) +{ + struct lp_build_emit_data emit_data; + emit_data.arg_count = 1; + emit_data.args[0] = arg0; + return lp_build_emit_llvm(bld_base, tgsi_opcode, &emit_data); +} + +LLVMValueRef +lp_build_emit_llvm_binary( + struct lp_build_tgsi_context *bld_base, + unsigned tgsi_opcode, + LLVMValueRef arg0, + LLVMValueRef arg1) +{ + struct lp_build_emit_data emit_data; + emit_data.arg_count = 2; + emit_data.args[0] = arg0; + emit_data.args[1] = arg1; + return lp_build_emit_llvm(bld_base, tgsi_opcode, &emit_data); +} + +LLVMValueRef +lp_build_emit_llvm_ternary( + struct lp_build_tgsi_context *bld_base, + unsigned tgsi_opcode, + LLVMValueRef arg0, + LLVMValueRef arg1, + LLVMValueRef arg2) +{ + struct lp_build_emit_data emit_data; + emit_data.arg_count = 3; + emit_data.args[0] = arg0; + emit_data.args[1] = arg1; + emit_data.args[2] = arg2; + return lp_build_emit_llvm(bld_base, tgsi_opcode, &emit_data); +} + +/** + * The default fetch implementation. + */ +void lp_build_fetch_args( + struct lp_build_tgsi_context * bld_base, + struct lp_build_emit_data * emit_data) +{ + unsigned src; + for (src = 0; src < emit_data->info->num_src; src++) { + emit_data->args[src] = lp_build_emit_fetch(bld_base, emit_data->inst, src, + emit_data->src_chan); + } + emit_data->arg_count = emit_data->info->num_src; + lp_build_action_set_dst_type(emit_data, bld_base, + emit_data->inst->Instruction.Opcode); +} + +/** + * with doubles src and dst channels aren't 1:1. + * check the src/dst types for the opcode, + * 1. if neither is double then src == dst; + * 2. if dest is double + * - don't store to y or w + * - if src is double then src == dst. + * - else for f2d, d.xy = s.x + * - else for f2d, d.zw = s.y + * 3. if dst is single, src is double + * - map dst x,z to src xy; + * - map dst y,w to src zw; + */ +static int get_src_chan_idx(unsigned opcode, + int dst_chan_index) +{ + enum tgsi_opcode_type dtype = tgsi_opcode_infer_dst_type(opcode); + enum tgsi_opcode_type stype = tgsi_opcode_infer_src_type(opcode); + + if (dtype != TGSI_TYPE_DOUBLE && stype != TGSI_TYPE_DOUBLE) + return dst_chan_index; + if (dtype == TGSI_TYPE_DOUBLE) { + if (dst_chan_index == 1 || dst_chan_index == 3) + return -1; + if (stype == TGSI_TYPE_DOUBLE) + return dst_chan_index; + if (dst_chan_index == 0) + return 0; + if (dst_chan_index == 2) + return 1; + } else { + if (dst_chan_index == 0 || dst_chan_index == 2) + return 0; + if (dst_chan_index == 1 || dst_chan_index == 3) + return 2; + } + return -1; +} + +/* XXX: COMMENT + * It should be assumed that this function ignores writemasks + */ +boolean +lp_build_tgsi_inst_llvm( + struct lp_build_tgsi_context * bld_base, + const struct tgsi_full_instruction * inst) +{ + unsigned tgsi_opcode = inst->Instruction.Opcode; + const struct tgsi_opcode_info * info = tgsi_get_opcode_info(tgsi_opcode); + const struct lp_build_tgsi_action * action = + &bld_base->op_actions[tgsi_opcode]; + struct lp_build_emit_data emit_data; + unsigned chan_index; + LLVMValueRef val; + bld_base->pc++; + + if (bld_base->emit_debug) { + bld_base->emit_debug(bld_base, inst, info); + } + + /* Ignore deprecated instructions */ + switch (inst->Instruction.Opcode) { + + case TGSI_OPCODE_UP2H: + case TGSI_OPCODE_UP2US: + case TGSI_OPCODE_UP4B: + case TGSI_OPCODE_UP4UB: + case TGSI_OPCODE_PUSHA: + case TGSI_OPCODE_POPA: + case TGSI_OPCODE_SAD: + /* deprecated? */ + assert(0); + return FALSE; + break; + } + + /* Check if the opcode has been implemented */ + if (!action->emit) { + return FALSE; + } + + memset(&emit_data, 0, sizeof(emit_data)); + + assert(info->num_dst <= 1); + if (info->num_dst) { + TGSI_FOR_EACH_DST0_ENABLED_CHANNEL( inst, chan_index ) { + emit_data.output[chan_index] = bld_base->base.undef; + } + } + + emit_data.inst = inst; + emit_data.info = info; + + /* Emit the instructions */ + if (info->output_mode == TGSI_OUTPUT_COMPONENTWISE && bld_base->soa) { + TGSI_FOR_EACH_DST0_ENABLED_CHANNEL(inst, chan_index) { + int src_index = get_src_chan_idx(inst->Instruction.Opcode, chan_index); + /* ignore channels 1/3 in double dst */ + if (src_index == -1) + continue; + emit_data.chan = chan_index; + emit_data.src_chan = src_index; + if (!action->fetch_args) { + lp_build_fetch_args(bld_base, &emit_data); + } else { + action->fetch_args(bld_base, &emit_data); + } + action->emit(action, bld_base, &emit_data); + } + } else { + emit_data.chan = LP_CHAN_ALL; + if (action->fetch_args) { + action->fetch_args(bld_base, &emit_data); + } + /* Make sure the output value is stored in emit_data.output[0], unless + * the opcode is channel dependent */ + if (info->output_mode != TGSI_OUTPUT_CHAN_DEPENDENT) { + emit_data.chan = 0; + } + action->emit(action, bld_base, &emit_data); + + /* Replicate the output values */ + if (info->output_mode == TGSI_OUTPUT_REPLICATE && bld_base->soa) { + val = emit_data.output[0]; + memset(emit_data.output, 0, sizeof(emit_data.output)); + TGSI_FOR_EACH_DST0_ENABLED_CHANNEL(inst, chan_index) { + emit_data.output[chan_index] = val; + } + } + } + + if (info->num_dst > 0) { + bld_base->emit_store(bld_base, inst, info, emit_data.output); + } + return TRUE; +} + + +LLVMValueRef +lp_build_emit_fetch( + struct lp_build_tgsi_context *bld_base, + const struct tgsi_full_instruction *inst, + unsigned src_op, + const unsigned chan_index) +{ + const struct tgsi_full_src_register *reg = &inst->Src[src_op]; + unsigned swizzle; + LLVMValueRef res; + enum tgsi_opcode_type stype = tgsi_opcode_infer_src_type(inst->Instruction.Opcode); + + if (chan_index == LP_CHAN_ALL) { + swizzle = ~0; + } else { + swizzle = tgsi_util_get_full_src_register_swizzle(reg, chan_index); + if (swizzle > 3) { + assert(0 && "invalid swizzle in emit_fetch()"); + return bld_base->base.undef; + } + } + + assert(reg->Register.Index <= bld_base->info->file_max[reg->Register.File]); + + if (bld_base->emit_fetch_funcs[reg->Register.File]) { + res = bld_base->emit_fetch_funcs[reg->Register.File](bld_base, reg, stype, + swizzle); + } else { + assert(0 && "invalid src register in emit_fetch()"); + return bld_base->base.undef; + } + + if (reg->Register.Absolute) { + switch (stype) { + case TGSI_TYPE_FLOAT: + case TGSI_TYPE_DOUBLE: + case TGSI_TYPE_UNTYPED: + /* modifiers on movs assume data is float */ + res = lp_build_emit_llvm_unary(bld_base, TGSI_OPCODE_ABS, res); + break; + case TGSI_TYPE_UNSIGNED: + case TGSI_TYPE_SIGNED: + case TGSI_TYPE_VOID: + default: + /* abs modifier is only legal on floating point types */ + assert(0); + break; + } + } + + if (reg->Register.Negate) { + switch (stype) { + case TGSI_TYPE_FLOAT: + case TGSI_TYPE_UNTYPED: + /* modifiers on movs assume data is float */ + res = lp_build_negate( &bld_base->base, res ); + break; + case TGSI_TYPE_DOUBLE: + /* no double build context */ + assert(0); + break; + case TGSI_TYPE_SIGNED: + case TGSI_TYPE_UNSIGNED: + res = lp_build_negate( &bld_base->int_bld, res ); + break; + case TGSI_TYPE_VOID: + default: + assert(0); + break; + } + } + + /* + * Swizzle the argument + */ + + if (swizzle == ~0) { + res = bld_base->emit_swizzle(bld_base, res, + reg->Register.SwizzleX, + reg->Register.SwizzleY, + reg->Register.SwizzleZ, + reg->Register.SwizzleW); + } + + return res; + +} + + +LLVMValueRef +lp_build_emit_fetch_texoffset( + struct lp_build_tgsi_context *bld_base, + const struct tgsi_full_instruction *inst, + unsigned tex_off_op, + const unsigned chan_index) +{ + const struct tgsi_texture_offset *off = &inst->TexOffsets[tex_off_op]; + struct tgsi_full_src_register reg; + unsigned swizzle; + LLVMValueRef res; + enum tgsi_opcode_type stype = TGSI_TYPE_SIGNED; + + /* convert offset "register" to ordinary register so can use normal emit funcs */ + memset(®, 0, sizeof(reg)); + reg.Register.File = off->File; + reg.Register.Index = off->Index; + reg.Register.SwizzleX = off->SwizzleX; + reg.Register.SwizzleY = off->SwizzleY; + reg.Register.SwizzleZ = off->SwizzleZ; + + if (chan_index == LP_CHAN_ALL) { + swizzle = ~0; + } else { + assert(chan_index < TGSI_SWIZZLE_W); + swizzle = tgsi_util_get_src_register_swizzle(®.Register, chan_index); + } + + assert(off->Index <= bld_base->info->file_max[off->File]); + + if (bld_base->emit_fetch_funcs[off->File]) { + res = bld_base->emit_fetch_funcs[off->File](bld_base, ®, stype, + swizzle); + } else { + assert(0 && "invalid src register in emit_fetch_texoffset()"); + return bld_base->base.undef; + } + + /* + * Swizzle the argument + */ + + if (swizzle == ~0) { + res = bld_base->emit_swizzle(bld_base, res, + off->SwizzleX, + off->SwizzleY, + off->SwizzleZ, + /* there's no 4th channel */ + off->SwizzleX); + } + + return res; + +} + + +boolean +lp_build_tgsi_llvm( + struct lp_build_tgsi_context * bld_base, + const struct tgsi_token *tokens) +{ + struct tgsi_parse_context parse; + + if (bld_base->emit_prologue) { + bld_base->emit_prologue(bld_base); + } + + if (!lp_bld_tgsi_list_init(bld_base)) { + return FALSE; + } + + tgsi_parse_init( &parse, tokens ); + + while( !tgsi_parse_end_of_tokens( &parse ) ) { + tgsi_parse_token( &parse ); + + switch( parse.FullToken.Token.Type ) { + case TGSI_TOKEN_TYPE_DECLARATION: + /* Inputs already interpolated */ + bld_base->emit_declaration(bld_base, &parse.FullToken.FullDeclaration); + break; + + case TGSI_TOKEN_TYPE_INSTRUCTION: + lp_bld_tgsi_add_instruction(bld_base, &parse.FullToken.FullInstruction); + break; + + case TGSI_TOKEN_TYPE_IMMEDIATE: + bld_base->emit_immediate(bld_base, &parse.FullToken.FullImmediate); + break; + + case TGSI_TOKEN_TYPE_PROPERTY: + break; + + default: + assert( 0 ); + } + } + + while (bld_base->pc != -1) { + const struct tgsi_full_instruction *instr = + bld_base->instructions + bld_base->pc; + const struct tgsi_opcode_info *opcode_info = + tgsi_get_opcode_info(instr->Instruction.Opcode); + if (!lp_build_tgsi_inst_llvm(bld_base, instr)) { + _debug_printf("warning: failed to translate tgsi opcode %s to LLVM\n", + opcode_info->mnemonic); + return FALSE; + } + } + + tgsi_parse_free(&parse); + + FREE(bld_base->instructions); + + if (bld_base->emit_epilogue) { + bld_base->emit_epilogue(bld_base); + } + + return TRUE; +} |