radeon: make texture logging more useful
[mesa.git] / src / gallium / drivers / r600 / sb / sb_expr.cpp
index 65a764153a7231b4fc39c07090cb604399d175fe..52c0c1796ba744c8e92dd7d4cff01c90592f32db 100644 (file)
@@ -49,6 +49,14 @@ value* get_select_value_for_em(shader& sh, value* em) {
        return d0;
 }
 
+void convert_to_mov(alu_node &n, value *src, bool neg, bool abs) {
+       n.src.resize(1);
+       n.src[0] = src;
+       n.bc.src[0].abs = abs;
+       n.bc.src[0].neg = neg;
+       n.bc.set_op(ALU_OP1_MOV);
+}
+
 expr_handler::expr_handler(shader& sh) : sh(sh), vt(sh.vt) {}
 
 value * expr_handler::get_const(const literal &l) {
@@ -177,18 +185,110 @@ bool expr_handler::fold(container_node& n) {
 
 bool expr_handler::fold_setcc(alu_node &n) {
 
-       // TODO
+       value* v0 = n.src[0]->gvalue();
+       value* v1 = n.src[1]->gvalue();
+
+       assert(v0 && v1 && n.dst[0]);
+
+       unsigned flags = n.bc.op_ptr->flags;
+       unsigned cc = flags & AF_CC_MASK;
+       unsigned cmp_type = flags & AF_CMP_TYPE_MASK;
+       unsigned dst_type = flags & AF_DST_TYPE_MASK;
+
+       bool cond_result;
+       bool have_result = false;
+
+       bool isc0 = v0->is_const();
+       bool isc1 = v1->is_const();
+
+       literal dv, cv0, cv1;
+
+       if (isc0) {
+               cv0 = v0->get_const_value();
+               apply_alu_src_mod(n.bc, 0, cv0);
+       }
+
+       if (isc1) {
+               cv1 = v1->get_const_value();
+               apply_alu_src_mod(n.bc, 1, cv1);
+       }
+
+       if (isc0 && isc1) {
+               cond_result = evaluate_condition(flags, cv0, cv1);
+               have_result = true;
+       } else if (isc1) {
+               if (cmp_type == AF_FLOAT_CMP) {
+                       if (n.bc.src[0].abs && !n.bc.src[0].neg) {
+                               if (cv1.f < 0.0f && (cc == AF_CC_GT || cc == AF_CC_NE)) {
+                                       cond_result = true;
+                                       have_result = true;
+                               } else if (cv1.f <= 0.0f && cc == AF_CC_GE) {
+                                       cond_result = true;
+                                       have_result = true;
+                               }
+                       } else if (n.bc.src[0].abs && n.bc.src[0].neg) {
+                               if (cv1.f > 0.0f && (cc == AF_CC_GE || cc == AF_CC_E)) {
+                                       cond_result = false;
+                                       have_result = true;
+                               } else if (cv1.f >= 0.0f && cc == AF_CC_GT) {
+                                       cond_result = false;
+                                       have_result = true;
+                               }
+                       }
+               } else if (cmp_type == AF_UINT_CMP && cv1.u == 0 && cc == AF_CC_GE) {
+                       cond_result = true;
+                       have_result = true;
+               }
+       } else if (isc0) {
+               if (cmp_type == AF_FLOAT_CMP) {
+                       if (n.bc.src[1].abs && !n.bc.src[1].neg) {
+                               if (cv0.f <= 0.0f && cc == AF_CC_GT) {
+                                       cond_result = false;
+                                       have_result = true;
+                               } else if (cv0.f < 0.0f && (cc == AF_CC_GE || cc == AF_CC_E)) {
+                                       cond_result = false;
+                                       have_result = true;
+                               }
+                       } else if (n.bc.src[1].abs && n.bc.src[1].neg) {
+                               if (cv0.f >= 0.0f && cc == AF_CC_GE) {
+                                       cond_result = true;
+                                       have_result = true;
+                               } else if (cv0.f > 0.0f && (cc == AF_CC_GT || cc == AF_CC_NE)) {
+                                       cond_result = true;
+                                       have_result = true;
+                               }
+                       }
+               } else if (cmp_type == AF_UINT_CMP && cv0.u == 0 && cc == AF_CC_GT) {
+                       cond_result = false;
+                       have_result = true;
+               }
+       } else if (v0 == v1) {
+               bc_alu_src &s0 = n.bc.src[0], &s1 = n.bc.src[1];
+               if (s0.abs == s1.abs && s0.neg == s1.neg && cmp_type != AF_FLOAT_CMP) {
+                       // NOTE can't handle float comparisons here because of NaNs
+                       cond_result = (cc == AF_CC_E || cc == AF_CC_GE);
+                       have_result = true;
+               }
+       }
+
+       if (have_result) {
+               literal result;
+
+               if (cond_result)
+                       result = dst_type != AF_FLOAT_DST ?
+                                       literal(0xFFFFFFFFu) : literal(1.0f);
+               else
+                       result = literal(0);
+
+               convert_to_mov(n, sh.get_const_value(result));
+               return fold_alu_op1(n);
+       }
 
        return false;
 }
 
 bool expr_handler::fold(alu_node& n) {
 
-       if (n.bc.op_ptr->flags & (AF_PRED | AF_KILL)) {
-               fold_setcc(n);
-               return false;
-       }
-
        switch (n.bc.op_ptr->src_count) {
        case 1: return fold_alu_op1(n);
        case 2: return fold_alu_op2(n);
@@ -283,11 +383,23 @@ bool expr_handler::fold_alu_op1(alu_node& n) {
        if (n.src.empty())
                return false;
 
-       value* v0 = n.src[0];
+       value* v0 = n.src[0]->gvalue();
 
        assert(v0 && n.dst[0]);
 
        if (!v0->is_const()) {
+               // handle (MOV -(MOV -x)) => (MOV x)
+               if (n.bc.op == ALU_OP1_MOV && n.bc.src[0].neg && !n.bc.src[1].abs
+                               && v0->def && v0->def->is_alu_op(ALU_OP1_MOV)) {
+                       alu_node *sd = static_cast<alu_node*>(v0->def);
+                       if (!sd->bc.clamp && !sd->bc.omod && !sd->bc.src[0].abs &&
+                                       sd->bc.src[0].neg) {
+                               n.src[0] = sd->src[0];
+                               n.bc.src[0].neg = 0;
+                               v0 = n.src[0]->gvalue();
+                       }
+               }
+
                if ((n.bc.op == ALU_OP1_MOV || n.bc.op == ALU_OP1_MOVA_INT ||
                                n.bc.op == ALU_OP1_MOVA_GPR_INT)
                                && n.bc.clamp == 0 && n.bc.omod == 0
@@ -337,7 +449,7 @@ bool expr_handler::fold_alu_op1(alu_node& n) {
        case ALU_OP1_RECIP_FF:
        case ALU_OP1_RECIP_IEEE: dv = 1.0f / cv.f; break;
 //     case ALU_OP1_RECIP_INT:
-//     case ALU_OP1_RECIP_UINT:
+       case ALU_OP1_RECIP_UINT: dv.u = (1ull << 32) / cv.u; break;
 //     case ALU_OP1_RNDNE: dv = floor(cv.f + 0.5f); break;
        case ALU_OP1_SIN: dv = sin(cv.f * 2.0f * M_PI); break;
        case ALU_OP1_SQRT_IEEE: dv = sqrt(cv.f); break;
@@ -352,15 +464,318 @@ bool expr_handler::fold_alu_op1(alu_node& n) {
        return true;
 }
 
+bool expr_handler::fold_mul_add(alu_node *n) {
+
+       bool ieee;
+       value* v0 = n->src[0]->gvalue();
+
+       alu_node *d0 = (v0->def && v0->def->is_alu_inst()) ?
+                       static_cast<alu_node*>(v0->def) : NULL;
+
+       if (d0) {
+               if (d0->is_alu_op(ALU_OP2_MUL_IEEE))
+                       ieee = true;
+               else if (d0->is_alu_op(ALU_OP2_MUL))
+                       ieee = false;
+               else
+                       return false;
+
+               if (!d0->bc.src[0].abs && !d0->bc.src[1].abs &&
+                               !n->bc.src[1].abs && !n->bc.src[0].abs && !d0->bc.omod &&
+                               !d0->bc.clamp && !n->bc.omod &&
+                               (!d0->src[0]->is_kcache() || !d0->src[1]->is_kcache() ||
+                                               !n->src[1]->is_kcache())) {
+
+                       bool mul_neg = n->bc.src[0].neg;
+
+                       n->src.resize(3);
+                       n->bc.set_op(ieee ? ALU_OP3_MULADD_IEEE : ALU_OP3_MULADD);
+                       n->src[2] = n->src[1];
+                       n->bc.src[2] = n->bc.src[1];
+                       n->src[0] = d0->src[0];
+                       n->bc.src[0] = d0->bc.src[0];
+                       n->src[1] = d0->src[1];
+                       n->bc.src[1] = d0->bc.src[1];
+
+                       n->bc.src[0].neg ^= mul_neg;
+
+                       fold_alu_op3(*n);
+                       return true;
+               }
+       }
+
+       value* v1 = n->src[1]->gvalue();
+
+       alu_node *d1 = (v1->def && v1->def->is_alu_inst()) ?
+                       static_cast<alu_node*>(v1->def) : NULL;
+
+       if (d1) {
+               if (d1->is_alu_op(ALU_OP2_MUL_IEEE))
+                       ieee = true;
+               else if (d1->is_alu_op(ALU_OP2_MUL))
+                       ieee = false;
+               else
+                       return false;
+
+               if (!d1->bc.src[1].abs && !d1->bc.src[0].abs &&
+                               !n->bc.src[0].abs && !n->bc.src[1].abs && !d1->bc.omod &&
+                               !d1->bc.clamp && !n->bc.omod &&
+                               (!d1->src[0]->is_kcache() || !d1->src[1]->is_kcache() ||
+                                               !n->src[0]->is_kcache())) {
+
+                       bool mul_neg = n->bc.src[1].neg;
+
+                       n->src.resize(3);
+                       n->bc.set_op(ieee ? ALU_OP3_MULADD_IEEE : ALU_OP3_MULADD);
+                       n->src[2] = n->src[0];
+                       n->bc.src[2] = n->bc.src[0];
+                       n->src[1] = d1->src[1];
+                       n->bc.src[1] = d1->bc.src[1];
+                       n->src[0] = d1->src[0];
+                       n->bc.src[0] = d1->bc.src[0];
+
+                       n->bc.src[1].neg ^= mul_neg;
+
+                       fold_alu_op3(*n);
+                       return true;
+               }
+       }
+
+       return false;
+}
+
+bool expr_handler::eval_const_op(unsigned op, literal &r,
+                                 literal cv0, literal cv1) {
+
+       switch (op) {
+       case ALU_OP2_ADD: r = cv0.f + cv1.f; break;
+       case ALU_OP2_ADDC_UINT:
+               r = (uint32_t)(((uint64_t)cv0.u + cv1.u)>>32); break;
+       case ALU_OP2_ADD_INT: r = cv0.i + cv1.i; break;
+       case ALU_OP2_AND_INT: r = cv0.i & cv1.i; break;
+       case ALU_OP2_ASHR_INT: r = cv0.i >> (cv1.i & 0x1F); break;
+       case ALU_OP2_BFM_INT:
+               r = (((1 << (cv0.i & 0x1F)) - 1) << (cv1.i & 0x1F)); break;
+       case ALU_OP2_LSHL_INT: r = cv0.i << cv1.i; break;
+       case ALU_OP2_LSHR_INT: r = cv0.u >> cv1.u; break;
+       case ALU_OP2_MAX:
+       case ALU_OP2_MAX_DX10: r = cv0.f > cv1.f ? cv0.f : cv1.f; break;
+       case ALU_OP2_MAX_INT: r = cv0.i > cv1.i ? cv0.i : cv1.i; break;
+       case ALU_OP2_MAX_UINT: r = cv0.u > cv1.u ? cv0.u : cv1.u; break;
+       case ALU_OP2_MIN:
+       case ALU_OP2_MIN_DX10: r = cv0.f < cv1.f ? cv0.f : cv1.f; break;
+       case ALU_OP2_MIN_INT: r = cv0.i < cv1.i ? cv0.i : cv1.i; break;
+       case ALU_OP2_MIN_UINT: r = cv0.u < cv1.u ? cv0.u : cv1.u; break;
+       case ALU_OP2_MUL:
+       case ALU_OP2_MUL_IEEE: r = cv0.f * cv1.f; break;
+       case ALU_OP2_MULHI_INT:
+               r = (int32_t)(((int64_t)cv0.u * cv1.u)>>32); break;
+       case ALU_OP2_MULHI_UINT:
+               r = (uint32_t)(((uint64_t)cv0.u * cv1.u)>>32); break;
+       case ALU_OP2_MULLO_INT:
+               r = (int32_t)(((int64_t)cv0.u * cv1.u) & 0xFFFFFFFF); break;
+       case ALU_OP2_MULLO_UINT:
+               r = (uint32_t)(((uint64_t)cv0.u * cv1.u) & 0xFFFFFFFF); break;
+       case ALU_OP2_OR_INT: r = cv0.i | cv1.i; break;
+       case ALU_OP2_SUB_INT: r = cv0.i - cv1.i; break;
+       case ALU_OP2_XOR_INT: r = cv0.i ^ cv1.i; break;
+
+       default:
+               return false;
+       }
+
+       return true;
+}
+
+// fold the chain of associative ops, e.g. (ADD 2, (ADD x, 3)) => (ADD x, 5)
+bool expr_handler::fold_assoc(alu_node *n) {
+
+       alu_node *a = n;
+       literal cr;
+
+       int last_arg = -3;
+
+       unsigned op = n->bc.op;
+       bool allow_neg = false, cur_neg = false;
+
+       switch(op) {
+       case ALU_OP2_ADD:
+       case ALU_OP2_MUL:
+       case ALU_OP2_MUL_IEEE:
+               allow_neg = true;
+               break;
+       case ALU_OP3_MULADD:
+               allow_neg = true;
+               op = ALU_OP2_MUL;
+               break;
+       case ALU_OP3_MULADD_IEEE:
+               allow_neg = true;
+               op = ALU_OP2_MUL_IEEE;
+               break;
+       default:
+               if (n->bc.op_ptr->src_count != 2)
+                       return false;
+       }
+
+       // check if we can evaluate the op
+       if (!eval_const_op(op, cr, literal(0), literal(0)))
+               return false;
+
+       while (true) {
+
+               value *v0 = a->src[0]->gvalue();
+               value *v1 = a->src[1]->gvalue();
+
+               last_arg = -2;
+
+               if (v1->is_const()) {
+                       literal arg = v1->get_const_value();
+                       apply_alu_src_mod(a->bc, 1, arg);
+                       if (cur_neg)
+                               arg.f = -arg.f;
+
+                       if (a == n)
+                               cr = arg;
+                       else
+                               eval_const_op(op, cr, cr, arg);
+
+                       if (v0->def) {
+                               alu_node *d0 = static_cast<alu_node*>(v0->def);
+                               if ((d0->is_alu_op(op) ||
+                                               (op == ALU_OP2_MUL_IEEE &&
+                                                               d0->is_alu_op(ALU_OP2_MUL))) &&
+                                               !d0->bc.omod && !d0->bc.clamp &&
+                                               (!a->bc.src[0].neg || allow_neg)) {
+                                       cur_neg ^= a->bc.src[0].neg;
+                                       a = d0;
+                                       continue;
+                               }
+                       }
+                       last_arg = 0;
+
+               }
+
+               if (v0->is_const()) {
+                       literal arg = v0->get_const_value();
+                       apply_alu_src_mod(a->bc, 0, arg);
+                       if (cur_neg)
+                               arg.f = -arg.f;
+
+                       if (last_arg == 0) {
+                               eval_const_op(op, cr, cr, arg);
+                               last_arg = -1;
+                               break;
+                       }
+
+                       if (a == n)
+                               cr = arg;
+                       else
+                               eval_const_op(op, cr, cr, arg);
+
+                       if (v1->def) {
+                               alu_node *d1 = static_cast<alu_node*>(v1->def);
+                               if ((d1->is_alu_op(op) ||
+                                               (op == ALU_OP2_MUL_IEEE &&
+                                                               d1->is_alu_op(ALU_OP2_MUL))) &&
+                                               !d1->bc.omod && !d1->bc.clamp &&
+                                               (!a->bc.src[1].neg || allow_neg)) {
+                                       cur_neg ^= a->bc.src[1].neg;
+                                       a = d1;
+                                       continue;
+                               }
+                       }
+
+                       last_arg = 1;
+               }
+
+               break;
+       };
+
+       if (last_arg == -1) {
+               // result is const
+               apply_alu_dst_mod(n->bc, cr);
+
+               if (n->bc.op == op) {
+                       convert_to_mov(*n, sh.get_const_value(cr));
+                       fold_alu_op1(*n);
+                       return true;
+               } else { // MULADD => ADD
+                       n->src[0] = n->src[2];
+                       n->bc.src[0] = n->bc.src[2];
+                       n->src[1] = sh.get_const_value(cr);
+                       memset(&n->bc.src[1], 0, sizeof(bc_alu_src));
+
+                       n->src.resize(2);
+                       n->bc.set_op(ALU_OP2_ADD);
+               }
+       } else if (last_arg >= 0) {
+               n->src[0] = a->src[last_arg];
+               n->bc.src[0] = a->bc.src[last_arg];
+               n->bc.src[0].neg ^= cur_neg;
+               n->src[1] = sh.get_const_value(cr);
+               memset(&n->bc.src[1], 0, sizeof(bc_alu_src));
+       }
+
+       return false;
+}
+
 bool expr_handler::fold_alu_op2(alu_node& n) {
 
        if (n.src.size() < 2)
                return false;
 
-       value* v0 = n.src[0];
-       value* v1 = n.src[1];
+       unsigned flags = n.bc.op_ptr->flags;
 
-       assert(v0 && v1 && n.dst[0]);
+       if (flags & AF_SET) {
+               return fold_setcc(n);
+       }
+
+       if (!sh.safe_math && (flags & AF_M_ASSOC)) {
+               if (fold_assoc(&n))
+                       return true;
+       }
+
+       value* v0 = n.src[0]->gvalue();
+       value* v1 = n.src[1]->gvalue();
+
+       assert(v0 && v1);
+
+       // handle some operations with equal args, e.g. x + x => x * 2
+       if (v0 == v1) {
+               if (n.bc.src[0].neg == n.bc.src[1].neg &&
+                               n.bc.src[0].abs == n.bc.src[1].abs) {
+                       switch (n.bc.op) {
+                       case ALU_OP2_MIN: // (MIN x, x) => (MOV x)
+                       case ALU_OP2_MAX:
+                               convert_to_mov(n, v0, n.bc.src[0].neg, n.bc.src[0].abs);
+                               return fold_alu_op1(n);
+                       case ALU_OP2_ADD:  // (ADD x, x) => (MUL x, 2)
+                               if (!sh.safe_math) {
+                                       n.src[1] = sh.get_const_value(2.0f);
+                                       memset(&n.bc.src[1], 0, sizeof(bc_alu_src));
+                                       n.bc.set_op(ALU_OP2_MUL);
+                                       return fold_alu_op2(n);
+                               }
+                               break;
+                       }
+               }
+               if (n.bc.src[0].neg != n.bc.src[1].neg &&
+                               n.bc.src[0].abs == n.bc.src[1].abs) {
+                       switch (n.bc.op) {
+                       case ALU_OP2_ADD:  // (ADD x, -x) => (MOV 0)
+                               if (!sh.safe_math) {
+                                       convert_to_mov(n, sh.get_const_value(literal(0)));
+                                       return fold_alu_op1(n);
+                               }
+                               break;
+                       }
+               }
+       }
+
+       if (n.bc.op == ALU_OP2_ADD) {
+               if (fold_mul_add(&n))
+                       return true;
+       }
 
        bool isc0 = v0->is_const();
        bool isc1 = v1->is_const();
@@ -381,48 +796,68 @@ bool expr_handler::fold_alu_op2(alu_node& n) {
        }
 
        if (isc0 && isc1) {
-               switch (n.bc.op) {
-               case ALU_OP2_ADD: dv = cv0.f + cv1.f; break;
-               case ALU_OP2_ADDC_UINT:
-                       dv = (uint32_t)(((uint64_t)cv0.u + cv1.u)>>32); break;
-               case ALU_OP2_ADD_INT: dv = cv0.i + cv1.i; break;
-               case ALU_OP2_AND_INT: dv = cv0.i & cv1.i; break;
-               case ALU_OP2_ASHR_INT: dv = cv0.i >> (cv1.i & 0x1F); break;
-               case ALU_OP2_BFM_INT:
-                       dv = (((1 << (cv0.i & 0x1F)) - 1) << (cv1.i & 0x1F)); break;
-               case ALU_OP2_LSHL_INT: dv = cv0.i << cv1.i; break;
-               case ALU_OP2_LSHR_INT: dv = cv0.u >> cv1.u; break;
-               case ALU_OP2_MAX:
-               case ALU_OP2_MAX_DX10: dv = cv0.f > cv1.f ? cv0.f : cv1.f; break;
-               case ALU_OP2_MAX_INT: dv = cv0.i > cv1.i ? cv0.i : cv1.i; break;
-               case ALU_OP2_MAX_UINT: dv = cv0.u > cv1.u ? cv0.u : cv1.u; break;
-               case ALU_OP2_MIN:
-               case ALU_OP2_MIN_DX10: dv = cv0.f < cv1.f ? cv0.f : cv1.f; break;
-               case ALU_OP2_MIN_INT: dv = cv0.i < cv1.i ? cv0.i : cv1.i; break;
-               case ALU_OP2_MIN_UINT: dv = cv0.u < cv1.u ? cv0.u : cv1.u; break;
-               case ALU_OP2_MUL:
-               case ALU_OP2_MUL_IEEE: dv = cv0.f * cv1.f; break;
-               case ALU_OP2_MULHI_INT:
-                       dv = (int32_t)(((int64_t)cv0.u * cv1.u)>>32); break;
-               case ALU_OP2_MULHI_UINT:
-                       dv = (uint32_t)(((uint64_t)cv0.u * cv1.u)>>32); break;
-               case ALU_OP2_MULLO_INT:
-                       dv = (int32_t)(((int64_t)cv0.u * cv1.u) & 0xFFFFFFFF); break;
-               case ALU_OP2_MULLO_UINT:
-                       dv = (uint32_t)(((uint64_t)cv0.u * cv1.u) & 0xFFFFFFFF); break;
-               case ALU_OP2_OR_INT: dv = cv0.i | cv1.i; break;
-               case ALU_OP2_SUB_INT: dv = cv0.i - cv1.i; break;
-               case ALU_OP2_XOR_INT: dv = cv0.i ^ cv1.i; break;
-
-               case ALU_OP2_SETE: dv = cv0.f == cv1.f ? 1.0f : 0.0f; break;
 
-               default:
+               if (!eval_const_op(n.bc.op, dv, cv0, cv1))
                        return false;
-               }
 
        } else { // one source is const
 
-               // TODO handle 1 * anything, 0 * anything, 0 + anything, etc
+               if (isc0 && cv0 == literal(0)) {
+                       switch (n.bc.op) {
+                       case ALU_OP2_ADD:
+                       case ALU_OP2_ADD_INT:
+                       case ALU_OP2_MAX_UINT:
+                       case ALU_OP2_OR_INT:
+                       case ALU_OP2_XOR_INT:
+                               convert_to_mov(n, n.src[1], n.bc.src[1].neg,  n.bc.src[1].abs);
+                               return fold_alu_op1(n);
+                       case ALU_OP2_AND_INT:
+                       case ALU_OP2_ASHR_INT:
+                       case ALU_OP2_LSHL_INT:
+                       case ALU_OP2_LSHR_INT:
+                       case ALU_OP2_MIN_UINT:
+                       case ALU_OP2_MUL:
+                       case ALU_OP2_MULHI_UINT:
+                       case ALU_OP2_MULLO_UINT:
+                               convert_to_mov(n, sh.get_const_value(literal(0)));
+                               return fold_alu_op1(n);
+                       }
+               } else if (isc1 && cv1 == literal(0)) {
+                       switch (n.bc.op) {
+                       case ALU_OP2_ADD:
+                       case ALU_OP2_ADD_INT:
+                       case ALU_OP2_ASHR_INT:
+                       case ALU_OP2_LSHL_INT:
+                       case ALU_OP2_LSHR_INT:
+                       case ALU_OP2_MAX_UINT:
+                       case ALU_OP2_OR_INT:
+                       case ALU_OP2_SUB_INT:
+                       case ALU_OP2_XOR_INT:
+                               convert_to_mov(n, n.src[0], n.bc.src[0].neg,  n.bc.src[0].abs);
+                               return fold_alu_op1(n);
+                       case ALU_OP2_AND_INT:
+                       case ALU_OP2_MIN_UINT:
+                       case ALU_OP2_MUL:
+                       case ALU_OP2_MULHI_UINT:
+                       case ALU_OP2_MULLO_UINT:
+                               convert_to_mov(n, sh.get_const_value(literal(0)));
+                               return fold_alu_op1(n);
+                       }
+               } else if (isc0 && cv0 == literal(1.0f)) {
+                       switch (n.bc.op) {
+                       case ALU_OP2_MUL:
+                       case ALU_OP2_MUL_IEEE:
+                               convert_to_mov(n, n.src[1], n.bc.src[1].neg,  n.bc.src[1].abs);
+                               return fold_alu_op1(n);
+                       }
+               } else if (isc1 && cv1 == literal(1.0f)) {
+                       switch (n.bc.op) {
+                       case ALU_OP2_MUL:
+                       case ALU_OP2_MUL_IEEE:
+                               convert_to_mov(n, n.src[0], n.bc.src[0].neg,  n.bc.src[0].abs);
+                               return fold_alu_op1(n);
+                       }
+               }
 
                return false;
        }
@@ -489,9 +924,14 @@ bool expr_handler::fold_alu_op3(alu_node& n) {
        if (n.src.size() < 3)
                return false;
 
-       value* v0 = n.src[0];
-       value* v1 = n.src[1];
-       value* v2 = n.src[2];
+       if (!sh.safe_math && (n.bc.op_ptr->flags & AF_M_ASSOC)) {
+               if (fold_assoc(&n))
+                       return true;
+       }
+
+       value* v0 = n.src[0]->gvalue();
+       value* v1 = n.src[1]->gvalue();
+       value* v2 = n.src[2]->gvalue();
 
        assert(v0 && v1 && v2 && n.dst[0]);
 
@@ -516,11 +956,12 @@ bool expr_handler::fold_alu_op3(alu_node& n) {
                apply_alu_src_mod(n.bc, 2, cv2);
        }
 
-       if (n.bc.op_ptr->flags & AF_CMOV) {
+       unsigned flags = n.bc.op_ptr->flags;
+
+       if (flags & AF_CMOV) {
                int src = 0;
 
-               if (v1->gvalue() == v2->gvalue() &&
-                               n.bc.src[1].neg == n.bc.src[2].neg) {
+               if (v1 == v2 && n.bc.src[1].neg == n.bc.src[2].neg) {
                        // result doesn't depend on condition, convert to MOV
                        src = 1;
                } else if (isc0) {
@@ -532,19 +973,88 @@ bool expr_handler::fold_alu_op3(alu_node& n) {
 
                if (src) {
                        // if src is selected, convert to MOV
-                       n.bc.src[0] = n.bc.src[src];
-                       n.src[0] = n.src[src];
-                       n.src.resize(1);
-                       n.bc.set_op(ALU_OP1_MOV);
+                       convert_to_mov(n, n.src[src], n.bc.src[src].neg);
                        return fold_alu_op1(n);
                }
        }
 
+       // handle (MULADD a, x, MUL (x, b)) => (MUL x, ADD (a, b))
+       if (!sh.safe_math && (n.bc.op == ALU_OP3_MULADD ||
+                       n.bc.op == ALU_OP3_MULADD_IEEE)) {
+
+               unsigned op = n.bc.op == ALU_OP3_MULADD_IEEE ?
+                               ALU_OP2_MUL_IEEE : ALU_OP2_MUL;
+
+               if (!isc2 && v2->def && v2->def->is_alu_op(op)) {
+
+                       alu_node *md = static_cast<alu_node*>(v2->def);
+                       value *mv0 = md->src[0]->gvalue();
+                       value *mv1 = md->src[1]->gvalue();
+
+                       int es0 = -1, es1;
+
+                       if (v0 == mv0) {
+                               es0 = 0;
+                               es1 = 0;
+                       } else if (v0 == mv1) {
+                               es0 = 0;
+                               es1 = 1;
+                       } else if (v1 == mv0) {
+                               es0 = 1;
+                               es1 = 0;
+                       } else if (v1 == mv1) {
+                               es0 = 1;
+                               es1 = 1;
+                       }
+
+                       if (es0 != -1) {
+                               value *va0 = es0 == 0 ? v1 : v0;
+                               value *va1 = es1 == 0 ? mv1 : mv0;
+
+                               alu_node *add = sh.create_alu();
+                               add->bc.set_op(ALU_OP2_ADD);
+
+                               add->dst.resize(1);
+                               add->src.resize(2);
+
+                               value *t = sh.create_temp_value();
+                               t->def = add;
+                               add->dst[0] = t;
+                               add->src[0] = va0;
+                               add->src[1] = va1;
+                               add->bc.src[0] = n.bc.src[!es0];
+                               add->bc.src[1] = md->bc.src[!es1];
+
+                               add->bc.src[1].neg ^= n.bc.src[2].neg ^
+                                               (n.bc.src[es0].neg != md->bc.src[es1].neg);
+
+                               n.insert_before(add);
+                               vt.add_value(t);
+
+                               t = t->gvalue();
+
+                               if (es0 == 1) {
+                                       n.src[0] = n.src[1];
+                                       n.bc.src[0] = n.bc.src[1];
+                               }
+
+                               n.src[1] = t;
+                               memset(&n.bc.src[1], 0, sizeof(bc_alu_src));
+
+                               n.src.resize(2);
+
+                               n.bc.set_op(op);
+                               return fold_alu_op2(n);
+                       }
+               }
+       }
+
        if (!isc0 && !isc1 && !isc2)
                return false;
 
        if (isc0 && isc1 && isc2) {
                switch (n.bc.op) {
+               case ALU_OP3_MULADD_IEEE:
                case ALU_OP3_MULADD: dv = cv0.f * cv1.f + cv2.f; break;
 
                // TODO
@@ -552,10 +1062,51 @@ bool expr_handler::fold_alu_op3(alu_node& n) {
                default:
                        return false;
                }
-
        } else {
+               if (isc0 && isc1) {
+                       switch (n.bc.op) {
+                       case ALU_OP3_MULADD:
+                       case ALU_OP3_MULADD_IEEE:
+                               dv = cv0.f * cv1.f;
+                               n.bc.set_op(ALU_OP2_ADD);
+                               n.src[0] = sh.get_const_value(dv);
+                               memset(&n.bc.src[0], 0, sizeof(bc_alu_src));
+                               n.src[1] = n.src[2];
+                               n.bc.src[1] = n.bc.src[2];
+                               n.src.resize(2);
+                               return fold_alu_op2(n);
+                       }
+               }
 
-               // TODO
+               if (n.bc.op == ALU_OP3_MULADD) {
+                       if ((isc0 && cv0 == literal(0)) || (isc1 && cv1 == literal(0))) {
+                               convert_to_mov(n, n.src[2], n.bc.src[2].neg,  n.bc.src[2].abs);
+                               return fold_alu_op1(n);
+                       }
+               }
+
+               if (n.bc.op == ALU_OP3_MULADD || n.bc.op == ALU_OP3_MULADD_IEEE) {
+                       unsigned op = n.bc.op == ALU_OP3_MULADD_IEEE ?
+                                       ALU_OP2_MUL_IEEE : ALU_OP2_MUL;
+
+                       if (isc1 && v0 == v2) {
+                               cv1.f += (n.bc.src[2].neg != n.bc.src[0].neg ? -1.0f : 1.0f);
+                               n.src[1] = sh.get_const_value(cv1);
+                               n.bc.src[1].neg = 0;
+                               n.bc.src[1].abs = 0;
+                               n.bc.set_op(op);
+                               n.src.resize(2);
+                               return fold_alu_op2(n);
+                       } else if (isc0 && v1 == v2) {
+                               cv0.f += (n.bc.src[2].neg != n.bc.src[1].neg ? -1.0f : 1.0f);
+                               n.src[0] = sh.get_const_value(cv0);
+                               n.bc.src[0].neg = 0;
+                               n.bc.src[0].abs = 0;
+                               n.bc.set_op(op);
+                               n.src.resize(2);
+                               return fold_alu_op2(n);
+                       }
+               }
 
                return false;
        }
@@ -698,6 +1249,30 @@ unsigned get_killcc_op(unsigned cc, unsigned cmp_type) {
        return ~0u;
 }
 
+unsigned get_cndcc_op(unsigned cc, unsigned cmp_type) {
+
+       switch(cmp_type) {
+       case AF_FLOAT_CMP: {
+               switch (cc) {
+               case AF_CC_E: return ALU_OP3_CNDE;
+               case AF_CC_GT: return ALU_OP3_CNDGT;
+               case AF_CC_GE: return ALU_OP3_CNDGE;
+               }
+               break;
+       }
+       case AF_INT_CMP: {
+               switch (cc) {
+               case AF_CC_E: return ALU_OP3_CNDE_INT;
+               case AF_CC_GT: return ALU_OP3_CNDGT_INT;
+               case AF_CC_GE: return ALU_OP3_CNDGE_INT;
+               }
+               break;
+       }
+       }
+
+       assert(!"unexpected cc&cmp_type combination");
+       return ~0u;
+}
 
 
 void convert_predset_to_set(shader& sh, alu_node* a) {