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_variable *var, int col); 49 ir_rvalue *get_element(ir_variable *var, int col, int row); 50 51 void do_mul_mat_mat(ir_variable *result_var, 52 ir_variable *a_var, ir_variable *b_var); 53 void do_mul_mat_vec(ir_variable *result_var, 54 ir_variable *a_var, ir_variable *b_var); 55 void do_mul_vec_mat(ir_variable *result_var, 56 ir_variable *a_var, ir_variable *b_var); 57 void do_mul_mat_scalar(ir_variable *result_var, 58 ir_variable *a_var, ir_variable *b_var); 59 void do_equal_mat_mat(ir_variable *result_var, ir_variable *a_var, 60 ir_variable *b_var, 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_variable *var, int col, int row) 101 { 102 ir_dereference *deref; 103 104 deref = new(mem_ctx) ir_dereference_variable(var); 105 106 if (var->type->is_matrix()) { 107 deref = new(mem_ctx) ir_dereference_array(var, 108 new(mem_ctx) ir_constant(col)); 109 } else { 110 assert(col == 0); 111 } 112 113 return new(mem_ctx) ir_swizzle(deref, row, 0, 0, 0, 1); 114 } 115 116 ir_dereference * 117 ir_mat_op_to_vec_visitor::get_column(ir_variable *var, int row) 118 { 119 ir_dereference *deref; 120 121 if (!var->type->is_matrix()) { 122 deref = new(mem_ctx) ir_dereference_variable(var); 123 } else { 124 deref = new(mem_ctx) ir_dereference_variable(var); 125 deref = new(mem_ctx) ir_dereference_array(deref, 126 new(mem_ctx) ir_constant(row)); 127 } 128 129 return deref; 130 } 131 132 void 133 ir_mat_op_to_vec_visitor::do_mul_mat_mat(ir_variable *result_var, 134 ir_variable *a_var, 135 ir_variable *b_var) 136 { 137 int b_col, i; 138 ir_assignment *assign; 139 ir_expression *expr; 140 141 for (b_col = 0; b_col < b_var->type->matrix_columns; b_col++) { 142 ir_rvalue *a = get_column(a_var, 0); 143 ir_rvalue *b = get_element(b_var, b_col, 0); 144 145 /* first column */ 146 expr = new(mem_ctx) ir_expression(ir_binop_mul, 147 a->type, 148 a, 149 b); 150 151 /* following columns */ 152 for (i = 1; i < a_var->type->matrix_columns; i++) { 153 ir_expression *mul_expr; 154 155 a = get_column(a_var, i); 156 b = get_element(b_var, b_col, i); 157 158 mul_expr = new(mem_ctx) ir_expression(ir_binop_mul, 159 a->type, 160 a, 161 b); 162 expr = new(mem_ctx) ir_expression(ir_binop_add, 163 a->type, 164 expr, 165 mul_expr); 166 } 167 168 ir_rvalue *result = get_column(result_var, b_col); 169 assign = new(mem_ctx) ir_assignment(result, 170 expr, 171 NULL); 172 base_ir->insert_before(assign); 173 } 174 } 175 176 void 177 ir_mat_op_to_vec_visitor::do_mul_mat_vec(ir_variable *result_var, 178 ir_variable *a_var, 179 ir_variable *b_var) 180 { 181 int i; 182 ir_rvalue *a = get_column(a_var, 0); 183 ir_rvalue *b = get_element(b_var, 0, 0); 184 ir_assignment *assign; 185 ir_expression *expr; 186 187 /* first column */ 188 expr = new(mem_ctx) ir_expression(ir_binop_mul, 189 result_var->type, 190 a, 191 b); 192 193 /* following columns */ 194 for (i = 1; i < a_var->type->matrix_columns; i++) { 195 ir_expression *mul_expr; 196 197 a = get_column(a_var, i); 198 b = get_element(b_var, 0, i); 199 200 mul_expr = new(mem_ctx) ir_expression(ir_binop_mul, 201 result_var->type, 202 a, 203 b); 204 expr = new(mem_ctx) ir_expression(ir_binop_add, 205 result_var->type, 206 expr, 207 mul_expr); 208 } 209 210 ir_rvalue *result = new(mem_ctx) ir_dereference_variable(result_var); 211 assign = new(mem_ctx) ir_assignment(result, 212 expr, 213 NULL); 214 base_ir->insert_before(assign); 215 } 216 217 void 218 ir_mat_op_to_vec_visitor::do_mul_vec_mat(ir_variable *result_var, 219 ir_variable *a_var, 220 ir_variable *b_var) 221 { 222 int i; 223 224 for (i = 0; i < b_var->type->matrix_columns; i++) { 225 ir_rvalue *a = new(mem_ctx) ir_dereference_variable(a_var); 226 ir_rvalue *b = get_column(b_var, i); 227 ir_rvalue *result; 228 ir_expression *column_expr; 229 ir_assignment *column_assign; 230 231 result = new(mem_ctx) ir_dereference_variable(result_var); 232 result = new(mem_ctx) ir_swizzle(result, i, 0, 0, 0, 1); 233 234 column_expr = new(mem_ctx) ir_expression(ir_binop_dot, 235 result->type, 236 a, 237 b); 238 239 column_assign = new(mem_ctx) ir_assignment(result, 240 column_expr, 241 NULL); 242 base_ir->insert_before(column_assign); 243 } 244 } 245 246 void 247 ir_mat_op_to_vec_visitor::do_mul_mat_scalar(ir_variable *result_var, 248 ir_variable *a_var, 249 ir_variable *b_var) 250 { 251 int i; 252 253 for (i = 0; i < a_var->type->matrix_columns; i++) { 254 ir_rvalue *a = get_column(a_var, i); 255 ir_rvalue *b = new(mem_ctx) ir_dereference_variable(b_var); 256 ir_rvalue *result = get_column(result_var, i); 257 ir_expression *column_expr; 258 ir_assignment *column_assign; 259 260 column_expr = new(mem_ctx) ir_expression(ir_binop_mul, 261 result->type, 262 a, 263 b); 264 265 column_assign = new(mem_ctx) ir_assignment(result, 266 column_expr, 267 NULL); 268 base_ir->insert_before(column_assign); 269 } 270 } 271 272 void 273 ir_mat_op_to_vec_visitor::do_equal_mat_mat(ir_variable *result_var, 274 ir_variable *a_var, 275 ir_variable *b_var, 276 bool test_equal) 277 { 278 /* This essentially implements the following GLSL: 279 * 280 * bool equal(mat4 a, mat4 b) 281 * { 282 * return !any(bvec4(a[0] != b[0], 283 * a[1] != b[1], 284 * a[2] != b[2], 285 * a[3] != b[3]); 286 * } 287 * 288 * bool nequal(mat4 a, mat4 b) 289 * { 290 * return any(bvec4(a[0] != b[0], 291 * a[1] != b[1], 292 * a[2] != b[2], 293 * a[3] != b[3]); 294 * } 295 */ 296 const unsigned columns = a_var->type->matrix_columns; 297 const glsl_type *const bvec_type = 298 glsl_type::get_instance(GLSL_TYPE_BOOL, columns, 1); 299 300 ir_variable *const tmp_bvec = 301 new(this->mem_ctx) ir_variable(bvec_type, "mat_cmp_bvec", 302 ir_var_temporary); 303 this->base_ir->insert_before(tmp_bvec); 304 305 for (unsigned i = 0; i < columns; i++) { 306 ir_dereference *const op0 = get_column(a_var, i); 307 ir_dereference *const op1 = get_column(b_var, i); 308 309 ir_expression *const cmp = 310 new(this->mem_ctx) ir_expression(ir_binop_any_nequal, 311 glsl_type::bool_type, op0, op1); 312 313 ir_dereference *const lhs = 314 new(this->mem_ctx) ir_dereference_variable(tmp_bvec); 315 316 ir_assignment *const assign = 317 new(this->mem_ctx) ir_assignment(lhs, cmp, NULL, (1U << i)); 318 319 this->base_ir->insert_before(assign); 320 } 321 322 ir_rvalue *const val = 323 new(this->mem_ctx) ir_dereference_variable(tmp_bvec); 324 325 ir_expression *any = 326 new(this->mem_ctx) ir_expression(ir_unop_any, glsl_type::bool_type, 327 val, NULL); 328 329 if (test_equal) 330 any = new(this->mem_ctx) ir_expression(ir_unop_logic_not, 331 glsl_type::bool_type, 332 any, NULL); 333 334 ir_rvalue *const result = 335 new(this->mem_ctx) ir_dereference_variable(result_var); 336 337 ir_assignment *const assign = 338 new(mem_ctx) ir_assignment(result, any, NULL); 339 base_ir->insert_before(assign); 340 } 341 342 static bool 343 has_matrix_operand(const ir_expression *expr, unsigned &columns) 344 { 345 for (unsigned i = 0; i < expr->get_num_operands(); i++) { 346 if (expr->operands[i]->type->is_matrix()) { 347 columns = expr->operands[i]->type->matrix_columns; 348 return true; 349 } 350 } 351 352 return false; 353 } 354 355 356 ir_visitor_status 357 ir_mat_op_to_vec_visitor::visit_leave(ir_assignment *orig_assign) 358 { 359 ir_expression *orig_expr = orig_assign->rhs->as_expression(); 360 unsigned int i, matrix_columns = 1; 361 ir_variable *op_var[2]; 362 363 if (!orig_expr) 364 return visit_continue; 365 366 if (!has_matrix_operand(orig_expr, matrix_columns)) 367 return visit_continue; 368 369 assert(orig_expr->get_num_operands() <= 2); 370 371 mem_ctx = hieralloc_parent(orig_assign); 372 373 ir_dereference_variable *lhs_deref = 374 orig_assign->lhs->as_dereference_variable(); 375 assert(lhs_deref); 376 377 ir_variable *result_var = lhs_deref->var; 378 379 /* Store the expression operands in temps so we can use them 380 * multiple times. 381 */ 382 for (i = 0; i < orig_expr->get_num_operands(); i++) { 383 ir_assignment *assign; 384 385 op_var[i] = new(mem_ctx) ir_variable(orig_expr->operands[i]->type, 386 "mat_op_to_vec", 387 ir_var_temporary); 388 base_ir->insert_before(op_var[i]); 389 390 lhs_deref = new(mem_ctx) ir_dereference_variable(op_var[i]); 391 assign = new(mem_ctx) ir_assignment(lhs_deref, 392 orig_expr->operands[i], 393 NULL); 394 base_ir->insert_before(assign); 395 } 396 397 /* OK, time to break down this matrix operation. */ 398 switch (orig_expr->operation) { 399 case ir_unop_neg: { 400 const unsigned mask = (1U << result_var->type->vector_elements) - 1; 401 402 /* Apply the operation to each column.*/ 403 for (i = 0; i < matrix_columns; i++) { 404 ir_rvalue *op0 = get_column(op_var[0], i); 405 ir_dereference *result = get_column(result_var, i); 406 ir_expression *column_expr; 407 ir_assignment *column_assign; 408 409 column_expr = new(mem_ctx) ir_expression(orig_expr->operation, 410 result->type, 411 op0, 412 NULL); 413 414 column_assign = new(mem_ctx) ir_assignment(result, 415 column_expr, 416 NULL, 417 mask); 418 assert(column_assign->write_mask != 0); 419 base_ir->insert_before(column_assign); 420 } 421 break; 422 } 423 case ir_binop_add: 424 case ir_binop_sub: 425 case ir_binop_div: 426 case ir_binop_mod: { 427 const unsigned mask = (1U << result_var->type->vector_elements) - 1; 428 429 /* For most operations, the matrix version is just going 430 * column-wise through and applying the operation to each column 431 * if available. 432 */ 433 for (i = 0; i < matrix_columns; i++) { 434 ir_rvalue *op0 = get_column(op_var[0], i); 435 ir_rvalue *op1 = get_column(op_var[1], i); 436 ir_dereference *result = get_column(result_var, i); 437 ir_expression *column_expr; 438 ir_assignment *column_assign; 439 440 column_expr = new(mem_ctx) ir_expression(orig_expr->operation, 441 result->type, 442 op0, 443 op1); 444 445 column_assign = new(mem_ctx) ir_assignment(result, 446 column_expr, 447 NULL, 448 mask); 449 assert(column_assign->write_mask != 0); 450 base_ir->insert_before(column_assign); 451 } 452 break; 453 } 454 case ir_binop_mul: 455 if (op_var[0]->type->is_matrix()) { 456 if (op_var[1]->type->is_matrix()) { 457 do_mul_mat_mat(result_var, op_var[0], op_var[1]); 458 } else if (op_var[1]->type->is_vector()) { 459 do_mul_mat_vec(result_var, op_var[0], op_var[1]); 460 } else { 461 assert(op_var[1]->type->is_scalar()); 462 do_mul_mat_scalar(result_var, op_var[0], op_var[1]); 463 } 464 } else { 465 assert(op_var[1]->type->is_matrix()); 466 if (op_var[0]->type->is_vector()) { 467 do_mul_vec_mat(result_var, op_var[0], op_var[1]); 468 } else { 469 assert(op_var[0]->type->is_scalar()); 470 do_mul_mat_scalar(result_var, op_var[1], op_var[0]); 471 } 472 } 473 break; 474 475 case ir_binop_all_equal: 476 case ir_binop_any_nequal: 477 do_equal_mat_mat(result_var, op_var[1], op_var[0], 478 (orig_expr->operation == ir_binop_all_equal)); 479 break; 480 481 default: 482 printf("FINISHME: Handle matrix operation for %s\n", 483 orig_expr->operator_string()); 484 abort(); 485 } 486 orig_assign->remove(); 487 this->made_progress = true; 488 489 return visit_continue; 490 } 491