1 /* 2 * Copyright 2010 Intel Corporation 3 * 4 * Permission is hereby granted, free of charge, to any person obtaining a 5 * copy of this software and associated documentation files (the "Software"), 6 * to deal in the Software without restriction, including without limitation 7 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 * and/or sell copies of the Software, and to permit persons to whom the 9 * Software is furnished to do so, subject to the following conditions: 10 * 11 * The above copyright notice and this permission notice (including the next 12 * paragraph) shall be included in all copies or substantial portions of the 13 * Software. 14 * 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 * DEALINGS IN THE SOFTWARE. 22 */ 23 24 /** 25 * \file lower_mat_op_to_vec.cpp 26 * 27 * Breaks matrix operation expressions down to a series of vector operations. 28 * 29 * Generally this is how we have to codegen matrix operations for a 30 * GPU, so this gives us the chance to constant fold operations on a 31 * column or row. 32 */ 33 34 #include "ir.h" 35 #include "ir_expression_flattening.h" 36 #include "glsl_types.h" 37 38 class ir_mat_op_to_vec_visitor : public ir_hierarchical_visitor { 39 public: 40 ir_mat_op_to_vec_visitor() 41 { 42 this->made_progress = false; 43 this->mem_ctx = NULL; 44 } 45 46 ir_visitor_status visit_leave(ir_assignment *); 47 48 ir_dereference *get_column(ir_dereference *val, int col); 49 ir_rvalue *get_element(ir_dereference *val, int col, int row); 50 51 void do_mul_mat_mat(ir_dereference *result, 52 ir_dereference *a, ir_dereference *b); 53 void do_mul_mat_vec(ir_dereference *result, 54 ir_dereference *a, ir_dereference *b); 55 void do_mul_vec_mat(ir_dereference *result, 56 ir_dereference *a, ir_dereference *b); 57 void do_mul_mat_scalar(ir_dereference *result, 58 ir_dereference *a, ir_dereference *b); 59 void do_equal_mat_mat(ir_dereference *result, ir_dereference *a, 60 ir_dereference *b, bool test_equal); 61 62 void *mem_ctx; 63 bool made_progress; 64 }; 65 66 static bool 67 mat_op_to_vec_predicate(ir_instruction *ir) 68 { 69 ir_expression *expr = ir->as_expression(); 70 unsigned int i; 71 72 if (!expr) 73 return false; 74 75 for (i = 0; i < expr->get_num_operands(); i++) { 76 if (expr->operands[i]->type->is_matrix()) 77 return true; 78 } 79 80 return false; 81 } 82 83 bool 84 do_mat_op_to_vec(exec_list *instructions) 85 { 86 ir_mat_op_to_vec_visitor v; 87 88 /* Pull out any matrix expression to a separate assignment to a 89 * temp. This will make our handling of the breakdown to 90 * operations on the matrix's vector components much easier. 91 */ 92 do_expression_flattening(instructions, mat_op_to_vec_predicate); 93 94 visit_list_elements(&v, instructions); 95 96 return v.made_progress; 97 } 98 99 ir_rvalue * 100 ir_mat_op_to_vec_visitor::get_element(ir_dereference *val, int col, int row) 101 { 102 val = get_column(val, col); 103 104 return new(mem_ctx) ir_swizzle(val, row, 0, 0, 0, 1); 105 } 106 107 ir_dereference * 108 ir_mat_op_to_vec_visitor::get_column(ir_dereference *val, int row) 109 { 110 val = val->clone(mem_ctx, NULL); 111 112 if (val->type->is_matrix()) { 113 val = new(mem_ctx) ir_dereference_array(val, 114 new(mem_ctx) ir_constant(row)); 115 } 116 117 return val; 118 } 119 120 void 121 ir_mat_op_to_vec_visitor::do_mul_mat_mat(ir_dereference *result, 122 ir_dereference *a, 123 ir_dereference *b) 124 { 125 int b_col, i; 126 ir_assignment *assign; 127 ir_expression *expr; 128 129 for (b_col = 0; b_col < b->type->matrix_columns; b_col++) { 130 /* first column */ 131 expr = new(mem_ctx) ir_expression(ir_binop_mul, 132 get_column(a, 0), 133 get_element(b, b_col, 0)); 134 135 /* following columns */ 136 for (i = 1; i < a->type->matrix_columns; i++) { 137 ir_expression *mul_expr; 138 139 mul_expr = new(mem_ctx) ir_expression(ir_binop_mul, 140 get_column(a, i), 141 get_element(b, b_col, i)); 142 expr = new(mem_ctx) ir_expression(ir_binop_add, 143 expr, 144 mul_expr); 145 } 146 147 assign = new(mem_ctx) ir_assignment(get_column(result, b_col), expr); 148 base_ir->insert_before(assign); 149 } 150 } 151 152 void 153 ir_mat_op_to_vec_visitor::do_mul_mat_vec(ir_dereference *result, 154 ir_dereference *a, 155 ir_dereference *b) 156 { 157 int i; 158 ir_assignment *assign; 159 ir_expression *expr; 160 161 /* first column */ 162 expr = new(mem_ctx) ir_expression(ir_binop_mul, 163 get_column(a, 0), 164 get_element(b, 0, 0)); 165 166 /* following columns */ 167 for (i = 1; i < a->type->matrix_columns; i++) { 168 ir_expression *mul_expr; 169 170 mul_expr = new(mem_ctx) ir_expression(ir_binop_mul, 171 get_column(a, i), 172 get_element(b, 0, i)); 173 expr = new(mem_ctx) ir_expression(ir_binop_add, expr, mul_expr); 174 } 175 176 result = result->clone(mem_ctx, NULL); 177 assign = new(mem_ctx) ir_assignment(result, expr); 178 base_ir->insert_before(assign); 179 } 180 181 void 182 ir_mat_op_to_vec_visitor::do_mul_vec_mat(ir_dereference *result, 183 ir_dereference *a, 184 ir_dereference *b) 185 { 186 int i; 187 188 for (i = 0; i < b->type->matrix_columns; i++) { 189 ir_rvalue *column_result; 190 ir_expression *column_expr; 191 ir_assignment *column_assign; 192 193 column_result = result->clone(mem_ctx, NULL); 194 column_result = new(mem_ctx) ir_swizzle(column_result, i, 0, 0, 0, 1); 195 196 column_expr = new(mem_ctx) ir_expression(ir_binop_dot, 197 a->clone(mem_ctx, NULL), 198 get_column(b, i)); 199 200 column_assign = new(mem_ctx) ir_assignment(column_result, 201 column_expr); 202 base_ir->insert_before(column_assign); 203 } 204 } 205 206 void 207 ir_mat_op_to_vec_visitor::do_mul_mat_scalar(ir_dereference *result, 208 ir_dereference *a, 209 ir_dereference *b) 210 { 211 int i; 212 213 for (i = 0; i < a->type->matrix_columns; i++) { 214 ir_expression *column_expr; 215 ir_assignment *column_assign; 216 217 column_expr = new(mem_ctx) ir_expression(ir_binop_mul, 218 get_column(a, i), 219 b->clone(mem_ctx, NULL)); 220 221 column_assign = new(mem_ctx) ir_assignment(get_column(result, i), 222 column_expr); 223 base_ir->insert_before(column_assign); 224 } 225 } 226 227 void 228 ir_mat_op_to_vec_visitor::do_equal_mat_mat(ir_dereference *result, 229 ir_dereference *a, 230 ir_dereference *b, 231 bool test_equal) 232 { 233 /* This essentially implements the following GLSL: 234 * 235 * bool equal(mat4 a, mat4 b) 236 * { 237 * return !any(bvec4(a[0] != b[0], 238 * a[1] != b[1], 239 * a[2] != b[2], 240 * a[3] != b[3]); 241 * } 242 * 243 * bool nequal(mat4 a, mat4 b) 244 * { 245 * return any(bvec4(a[0] != b[0], 246 * a[1] != b[1], 247 * a[2] != b[2], 248 * a[3] != b[3]); 249 * } 250 */ 251 const unsigned columns = a->type->matrix_columns; 252 const glsl_type *const bvec_type = 253 glsl_type::get_instance(GLSL_TYPE_BOOL, columns, 1); 254 255 ir_variable *const tmp_bvec = 256 new(this->mem_ctx) ir_variable(bvec_type, "mat_cmp_bvec", 257 ir_var_temporary); 258 this->base_ir->insert_before(tmp_bvec); 259 260 for (unsigned i = 0; i < columns; i++) { 261 ir_expression *const cmp = 262 new(this->mem_ctx) ir_expression(ir_binop_any_nequal, 263 get_column(a, i), 264 get_column(b, i)); 265 266 ir_dereference *const lhs = 267 new(this->mem_ctx) ir_dereference_variable(tmp_bvec); 268 269 ir_assignment *const assign = 270 new(this->mem_ctx) ir_assignment(lhs, cmp, NULL, (1U << i)); 271 272 this->base_ir->insert_before(assign); 273 } 274 275 ir_rvalue *const val = new(this->mem_ctx) ir_dereference_variable(tmp_bvec); 276 ir_expression *any = new(this->mem_ctx) ir_expression(ir_unop_any, val); 277 278 if (test_equal) 279 any = new(this->mem_ctx) ir_expression(ir_unop_logic_not, any); 280 281 ir_assignment *const assign = 282 new(mem_ctx) ir_assignment(result->clone(mem_ctx, NULL), any); 283 base_ir->insert_before(assign); 284 } 285 286 static bool 287 has_matrix_operand(const ir_expression *expr, unsigned &columns) 288 { 289 for (unsigned i = 0; i < expr->get_num_operands(); i++) { 290 if (expr->operands[i]->type->is_matrix()) { 291 columns = expr->operands[i]->type->matrix_columns; 292 return true; 293 } 294 } 295 296 return false; 297 } 298 299 300 ir_visitor_status 301 ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *orig_assign) 302 { 303 ir_expression *orig_expr = orig_assign->rhs->as_expression(); 304 unsigned int i, matrix_columns = 1; 305 ir_dereference *op[2]; 306 307 if (!orig_expr) 308 return visit_continue; 309 310 if (!has_matrix_operand(orig_expr, matrix_columns)) 311 return visit_continue; 312 313 assert(orig_expr->get_num_operands() <= 2); 314 315 mem_ctx = ralloc_parent(orig_assign); 316 317 ir_dereference_variable *result = 318 orig_assign->lhs->as_dereference_variable(); 319 assert(result); 320 321 /* Store the expression operands in temps so we can use them 322 * multiple times. 323 */ 324 for (i = 0; i < orig_expr->get_num_operands(); i++) { 325 ir_assignment *assign; 326 ir_dereference *deref = orig_expr->operands[i]->as_dereference(); 327 328 /* Avoid making a temporary if we don't need to to avoid aliasing. */ 329 if (deref && 330 deref->variable_referenced() != result->variable_referenced()) { 331 op[i] = deref; 332 continue; 333 } 334 335 /* Otherwise, store the operand in a temporary generally if it's 336 * not a dereference. 337 */ 338 ir_variable *var = new(mem_ctx) ir_variable(orig_expr->operands[i]->type, 339 "mat_op_to_vec", 340 ir_var_temporary); 341 base_ir->insert_before(var); 342 343 /* Note that we use this dereference for the assignment. That means 344 * that others that want to use op[i] have to clone the deref. 345 */ 346 op[i] = new(mem_ctx) ir_dereference_variable(var); 347 assign = new(mem_ctx) ir_assignment(op[i], orig_expr->operands[i]); 348 base_ir->insert_before(assign); 349 } 350 351 /* OK, time to break down this matrix operation. */ 352 switch (orig_expr->operation) { 353 case ir_unop_neg: { 354 /* Apply the operation to each column.*/ 355 for (i = 0; i < matrix_columns; i++) { 356 ir_expression *column_expr; 357 ir_assignment *column_assign; 358 359 column_expr = new(mem_ctx) ir_expression(orig_expr->operation, 360 get_column(op[0], i)); 361 362 column_assign = new(mem_ctx) ir_assignment(get_column(result, i), 363 column_expr); 364 assert(column_assign->write_mask != 0); 365 base_ir->insert_before(column_assign); 366 } 367 break; 368 } 369 case ir_binop_add: 370 case ir_binop_sub: 371 case ir_binop_div: 372 case ir_binop_mod: { 373 /* For most operations, the matrix version is just going 374 * column-wise through and applying the operation to each column 375 * if available. 376 */ 377 for (i = 0; i < matrix_columns; i++) { 378 ir_expression *column_expr; 379 ir_assignment *column_assign; 380 381 column_expr = new(mem_ctx) ir_expression(orig_expr->operation, 382 get_column(op[0], i), 383 get_column(op[1], i)); 384 385 column_assign = new(mem_ctx) ir_assignment(get_column(result, i), 386 column_expr); 387 assert(column_assign->write_mask != 0); 388 base_ir->insert_before(column_assign); 389 } 390 break; 391 } 392 case ir_binop_mul: 393 if (op[0]->type->is_matrix()) { 394 if (op[1]->type->is_matrix()) { 395 do_mul_mat_mat(result, op[0], op[1]); 396 } else if (op[1]->type->is_vector()) { 397 do_mul_mat_vec(result, op[0], op[1]); 398 } else { 399 assert(op[1]->type->is_scalar()); 400 do_mul_mat_scalar(result, op[0], op[1]); 401 } 402 } else { 403 assert(op[1]->type->is_matrix()); 404 if (op[0]->type->is_vector()) { 405 do_mul_vec_mat(result, op[0], op[1]); 406 } else { 407 assert(op[0]->type->is_scalar()); 408 do_mul_mat_scalar(result, op[1], op[0]); 409 } 410 } 411 break; 412 413 case ir_binop_all_equal: 414 case ir_binop_any_nequal: 415 do_equal_mat_mat(result, op[1], op[0], 416 (orig_expr->operation == ir_binop_all_equal)); 417 break; 418 419 default: 420 printf("FINISHME: Handle matrix operation for %s\n", 421 orig_expr->operator_string()); 422 abort(); 423 } 424 orig_assign->remove(); 425 this->made_progress = true; 426 427 return visit_continue; 428 } 429