| /* |
| * Copyright (C) 2009 Nicolai Haehnle. |
| * |
| * 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, 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 COPYRIGHT OWNER(S) 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 "radeon_program_pair.h" |
| |
| #include "radeon_compiler.h" |
| #include "radeon_compiler_util.h" |
| |
| |
| /** |
| * Finally rewrite ADD, MOV, MUL as the appropriate native instruction |
| * and reverse the order of arguments for CMP. |
| */ |
| static void final_rewrite(struct rc_sub_instruction *inst) |
| { |
| struct rc_src_register tmp; |
| |
| switch(inst->Opcode) { |
| case RC_OPCODE_ADD: |
| inst->SrcReg[2] = inst->SrcReg[1]; |
| inst->SrcReg[1].File = RC_FILE_NONE; |
| inst->SrcReg[1].Swizzle = RC_SWIZZLE_1111; |
| inst->SrcReg[1].Negate = RC_MASK_NONE; |
| inst->Opcode = RC_OPCODE_MAD; |
| break; |
| case RC_OPCODE_CMP: |
| tmp = inst->SrcReg[2]; |
| inst->SrcReg[2] = inst->SrcReg[0]; |
| inst->SrcReg[0] = tmp; |
| break; |
| case RC_OPCODE_MOV: |
| /* AMD say we should use CMP. |
| * However, when we transform |
| * KIL -r0; |
| * into |
| * CMP tmp, -r0, -r0, 0; |
| * KIL tmp; |
| * we get incorrect behaviour on R500 when r0 == 0.0. |
| * It appears that the R500 KIL hardware treats -0.0 as less |
| * than zero. |
| */ |
| inst->SrcReg[1].File = RC_FILE_NONE; |
| inst->SrcReg[1].Swizzle = RC_SWIZZLE_1111; |
| inst->SrcReg[2].File = RC_FILE_NONE; |
| inst->SrcReg[2].Swizzle = RC_SWIZZLE_0000; |
| inst->Opcode = RC_OPCODE_MAD; |
| break; |
| case RC_OPCODE_MUL: |
| inst->SrcReg[2].File = RC_FILE_NONE; |
| inst->SrcReg[2].Swizzle = RC_SWIZZLE_0000; |
| inst->Opcode = RC_OPCODE_MAD; |
| break; |
| default: |
| /* nothing to do */ |
| break; |
| } |
| } |
| |
| |
| /** |
| * Classify an instruction according to which ALUs etc. it needs |
| */ |
| static void classify_instruction(struct rc_sub_instruction * inst, |
| int * needrgb, int * needalpha, int * istranscendent) |
| { |
| *needrgb = (inst->DstReg.WriteMask & RC_MASK_XYZ) ? 1 : 0; |
| *needalpha = (inst->DstReg.WriteMask & RC_MASK_W) ? 1 : 0; |
| *istranscendent = 0; |
| |
| if (inst->WriteALUResult == RC_ALURESULT_X) |
| *needrgb = 1; |
| else if (inst->WriteALUResult == RC_ALURESULT_W) |
| *needalpha = 1; |
| |
| switch(inst->Opcode) { |
| case RC_OPCODE_ADD: |
| case RC_OPCODE_CMP: |
| case RC_OPCODE_CND: |
| case RC_OPCODE_DDX: |
| case RC_OPCODE_DDY: |
| case RC_OPCODE_FRC: |
| case RC_OPCODE_MAD: |
| case RC_OPCODE_MAX: |
| case RC_OPCODE_MIN: |
| case RC_OPCODE_MOV: |
| case RC_OPCODE_MUL: |
| break; |
| case RC_OPCODE_COS: |
| case RC_OPCODE_EX2: |
| case RC_OPCODE_LG2: |
| case RC_OPCODE_RCP: |
| case RC_OPCODE_RSQ: |
| case RC_OPCODE_SIN: |
| *istranscendent = 1; |
| *needalpha = 1; |
| break; |
| case RC_OPCODE_DP4: |
| *needalpha = 1; |
| /* fall through */ |
| case RC_OPCODE_DP3: |
| *needrgb = 1; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| static void src_uses(struct rc_src_register src, unsigned int * rgb, |
| unsigned int * alpha) |
| { |
| int j; |
| for(j = 0; j < 4; ++j) { |
| unsigned int swz = GET_SWZ(src.Swizzle, j); |
| if (swz < 3) |
| *rgb = 1; |
| else if (swz < 4) |
| *alpha = 1; |
| } |
| } |
| |
| /** |
| * Fill the given ALU instruction's opcodes and source operands into the given pair, |
| * if possible. |
| */ |
| static void set_pair_instruction(struct r300_fragment_program_compiler *c, |
| struct rc_pair_instruction * pair, |
| struct rc_sub_instruction * inst) |
| { |
| int needrgb, needalpha, istranscendent; |
| const struct rc_opcode_info * opcode; |
| int i; |
| |
| memset(pair, 0, sizeof(struct rc_pair_instruction)); |
| |
| classify_instruction(inst, &needrgb, &needalpha, &istranscendent); |
| |
| if (needrgb) { |
| if (istranscendent) |
| pair->RGB.Opcode = RC_OPCODE_REPL_ALPHA; |
| else |
| pair->RGB.Opcode = inst->Opcode; |
| if (inst->SaturateMode == RC_SATURATE_ZERO_ONE) |
| pair->RGB.Saturate = 1; |
| } |
| if (needalpha) { |
| pair->Alpha.Opcode = inst->Opcode; |
| if (inst->SaturateMode == RC_SATURATE_ZERO_ONE) |
| pair->Alpha.Saturate = 1; |
| } |
| |
| opcode = rc_get_opcode_info(inst->Opcode); |
| |
| /* Presubtract handling: |
| * We need to make sure that the values used by the presubtract |
| * operation end up in src0 or src1. */ |
| if(inst->PreSub.Opcode != RC_PRESUB_NONE) { |
| /* rc_pair_alloc_source() will fill in data for |
| * pair->{RGB,ALPHA}.Src[RC_PAIR_PRESUB_SRC] */ |
| int j; |
| for(j = 0; j < 3; j++) { |
| int src_regs; |
| if(inst->SrcReg[j].File != RC_FILE_PRESUB) |
| continue; |
| |
| src_regs = rc_presubtract_src_reg_count( |
| inst->PreSub.Opcode); |
| for(i = 0; i < src_regs; i++) { |
| unsigned int rgb = 0; |
| unsigned int alpha = 0; |
| src_uses(inst->SrcReg[j], &rgb, &alpha); |
| if(rgb) { |
| pair->RGB.Src[i].File = |
| inst->PreSub.SrcReg[i].File; |
| pair->RGB.Src[i].Index = |
| inst->PreSub.SrcReg[i].Index; |
| pair->RGB.Src[i].Used = 1; |
| } |
| if(alpha) { |
| pair->Alpha.Src[i].File = |
| inst->PreSub.SrcReg[i].File; |
| pair->Alpha.Src[i].Index = |
| inst->PreSub.SrcReg[i].Index; |
| pair->Alpha.Src[i].Used = 1; |
| } |
| } |
| } |
| } |
| |
| for(i = 0; i < opcode->NumSrcRegs; ++i) { |
| int source; |
| if (needrgb && !istranscendent) { |
| unsigned int srcrgb = 0; |
| unsigned int srcalpha = 0; |
| unsigned int srcmask = 0; |
| int j; |
| /* We don't care about the alpha channel here. We only |
| * want the part of the swizzle that writes to rgb, |
| * since we are creating an rgb instruction. */ |
| for(j = 0; j < 3; ++j) { |
| unsigned int swz = GET_SWZ(inst->SrcReg[i].Swizzle, j); |
| |
| if (swz < RC_SWIZZLE_W) |
| srcrgb = 1; |
| else if (swz == RC_SWIZZLE_W) |
| srcalpha = 1; |
| |
| if (swz < RC_SWIZZLE_UNUSED) |
| srcmask |= 1 << j; |
| } |
| source = rc_pair_alloc_source(pair, srcrgb, srcalpha, |
| inst->SrcReg[i].File, inst->SrcReg[i].Index); |
| if (source < 0) { |
| rc_error(&c->Base, "Failed to translate " |
| "rgb instruction.\n"); |
| return; |
| } |
| pair->RGB.Arg[i].Source = source; |
| pair->RGB.Arg[i].Swizzle = |
| rc_init_swizzle(inst->SrcReg[i].Swizzle, 3); |
| pair->RGB.Arg[i].Abs = inst->SrcReg[i].Abs; |
| pair->RGB.Arg[i].Negate = !!(srcmask & inst->SrcReg[i].Negate & (RC_MASK_X | RC_MASK_Y | RC_MASK_Z)); |
| } |
| if (needalpha) { |
| unsigned int srcrgb = 0; |
| unsigned int srcalpha = 0; |
| unsigned int swz; |
| if (istranscendent) { |
| swz = rc_get_scalar_src_swz(inst->SrcReg[i].Swizzle); |
| } else { |
| swz = GET_SWZ(inst->SrcReg[i].Swizzle, 3); |
| } |
| |
| if (swz < 3) |
| srcrgb = 1; |
| else if (swz < 4) |
| srcalpha = 1; |
| source = rc_pair_alloc_source(pair, srcrgb, srcalpha, |
| inst->SrcReg[i].File, inst->SrcReg[i].Index); |
| if (source < 0) { |
| rc_error(&c->Base, "Failed to translate " |
| "alpha instruction.\n"); |
| return; |
| } |
| pair->Alpha.Arg[i].Source = source; |
| pair->Alpha.Arg[i].Swizzle = rc_init_swizzle(swz, 1); |
| pair->Alpha.Arg[i].Abs = inst->SrcReg[i].Abs; |
| |
| if (istranscendent) { |
| pair->Alpha.Arg[i].Negate = |
| !!(inst->SrcReg[i].Negate & |
| inst->DstReg.WriteMask); |
| } else { |
| pair->Alpha.Arg[i].Negate = |
| !!(inst->SrcReg[i].Negate & RC_MASK_W); |
| } |
| } |
| } |
| |
| /* Destination handling */ |
| if (inst->DstReg.File == RC_FILE_OUTPUT) { |
| if (inst->DstReg.Index == c->OutputDepth) { |
| pair->Alpha.DepthWriteMask |= GET_BIT(inst->DstReg.WriteMask, 3); |
| } else { |
| for (i = 0; i < 4; i++) { |
| if (inst->DstReg.Index == c->OutputColor[i]) { |
| pair->RGB.Target = i; |
| pair->Alpha.Target = i; |
| pair->RGB.OutputWriteMask |= |
| inst->DstReg.WriteMask & RC_MASK_XYZ; |
| pair->Alpha.OutputWriteMask |= |
| GET_BIT(inst->DstReg.WriteMask, 3); |
| break; |
| } |
| } |
| } |
| } else { |
| if (needrgb) { |
| pair->RGB.DestIndex = inst->DstReg.Index; |
| pair->RGB.WriteMask |= inst->DstReg.WriteMask & RC_MASK_XYZ; |
| } |
| |
| if (needalpha) { |
| pair->Alpha.WriteMask |= (GET_BIT(inst->DstReg.WriteMask, 3) << 3); |
| if (pair->Alpha.WriteMask) { |
| pair->Alpha.DestIndex = inst->DstReg.Index; |
| } |
| } |
| } |
| |
| if (needrgb) { |
| pair->RGB.Omod = inst->Omod; |
| } |
| if (needalpha) { |
| pair->Alpha.Omod = inst->Omod; |
| } |
| |
| if (inst->WriteALUResult) { |
| pair->WriteALUResult = inst->WriteALUResult; |
| pair->ALUResultCompare = inst->ALUResultCompare; |
| } |
| } |
| |
| |
| static void check_opcode_support(struct r300_fragment_program_compiler *c, |
| struct rc_sub_instruction *inst) |
| { |
| const struct rc_opcode_info * opcode = rc_get_opcode_info(inst->Opcode); |
| |
| if (opcode->HasDstReg) { |
| if (inst->SaturateMode == RC_SATURATE_MINUS_PLUS_ONE) { |
| rc_error(&c->Base, "Fragment program does not support signed Saturate.\n"); |
| return; |
| } |
| } |
| |
| for (unsigned i = 0; i < opcode->NumSrcRegs; i++) { |
| if (inst->SrcReg[i].RelAddr) { |
| rc_error(&c->Base, "Fragment program does not support relative addressing " |
| " of source operands.\n"); |
| return; |
| } |
| } |
| } |
| |
| |
| /** |
| * Translate all ALU instructions into corresponding pair instructions, |
| * performing no other changes. |
| */ |
| void rc_pair_translate(struct radeon_compiler *cc, void *user) |
| { |
| struct r300_fragment_program_compiler *c = (struct r300_fragment_program_compiler*)cc; |
| |
| for(struct rc_instruction * inst = c->Base.Program.Instructions.Next; |
| inst != &c->Base.Program.Instructions; |
| inst = inst->Next) { |
| const struct rc_opcode_info * opcode; |
| struct rc_sub_instruction copy; |
| |
| if (inst->Type != RC_INSTRUCTION_NORMAL) |
| continue; |
| |
| opcode = rc_get_opcode_info(inst->U.I.Opcode); |
| |
| if (opcode->HasTexture || opcode->IsFlowControl || opcode->Opcode == RC_OPCODE_KIL) |
| continue; |
| |
| copy = inst->U.I; |
| |
| check_opcode_support(c, ©); |
| |
| final_rewrite(©); |
| inst->Type = RC_INSTRUCTION_PAIR; |
| set_pair_instruction(c, &inst->U.P, ©); |
| } |
| } |