github.com/goccy/go-jit@v0.0.0-20200514131505-ff78d45cf6af/internal/ccall/jit-live.c (about) 1 /* 2 * jit-live.c - Liveness analysis for function bodies. 3 * 4 * Copyright (C) 2004 Southern Storm Software, Pty Ltd. 5 * 6 * This file is part of the libjit library. 7 * 8 * The libjit library is free software: you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public License 10 * as published by the Free Software Foundation, either version 2.1 of 11 * the License, or (at your option) any later version. 12 * 13 * The libjit library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with the libjit library. If not, see 20 * <http://www.gnu.org/licenses/>. 21 */ 22 23 #include "jit-internal.h" 24 #include <jit/jit-dump.h> 25 26 #define USE_FORWARD_PROPAGATION 1 27 #define USE_BACKWARD_PROPAGATION 1 28 29 /* 30 * Compute liveness information for a basic block. 31 */ 32 static void 33 compute_liveness_for_block(jit_block_t block) 34 { 35 jit_insn_iter_t iter; 36 jit_insn_t insn; 37 jit_value_t dest; 38 jit_value_t value1; 39 jit_value_t value2; 40 int flags; 41 42 /* Scan backwards to compute the liveness flags */ 43 jit_insn_iter_init_last(&iter, block); 44 while((insn = jit_insn_iter_previous(&iter)) != 0) 45 { 46 /* Skip NOP instructions, which may have arguments left 47 over from when the instruction was replaced, but which 48 are not relevant to our liveness analysis */ 49 if(insn->opcode == JIT_OP_NOP) 50 { 51 continue; 52 } 53 54 /* Fetch the value parameters to this instruction */ 55 flags = insn->flags; 56 if((flags & JIT_INSN_DEST_OTHER_FLAGS) == 0) 57 { 58 dest = insn->dest; 59 if(dest && dest->is_constant) 60 { 61 dest = 0; 62 } 63 } 64 else 65 { 66 dest = 0; 67 } 68 if((flags & JIT_INSN_VALUE1_OTHER_FLAGS) == 0) 69 { 70 value1 = insn->value1; 71 if(value1 && value1->is_constant) 72 { 73 value1 = 0; 74 } 75 } 76 else 77 { 78 value1 = 0; 79 } 80 if((flags & JIT_INSN_VALUE2_OTHER_FLAGS) == 0) 81 { 82 value2 = insn->value2; 83 if(value2 && value2->is_constant) 84 { 85 value2 = 0; 86 } 87 } 88 else 89 { 90 value2 = 0; 91 } 92 93 /* Record the liveness information in the instruction flags */ 94 flags &= ~JIT_INSN_LIVENESS_FLAGS; 95 if(dest) 96 { 97 if(dest->live) 98 { 99 flags |= JIT_INSN_DEST_LIVE; 100 } 101 if(dest->next_use) 102 { 103 flags |= JIT_INSN_DEST_NEXT_USE; 104 } 105 } 106 if(value1) 107 { 108 if(value1->live) 109 { 110 flags |= JIT_INSN_VALUE1_LIVE; 111 } 112 if(value1->next_use) 113 { 114 flags |= JIT_INSN_VALUE1_NEXT_USE; 115 } 116 } 117 if(value2) 118 { 119 if(value2->live) 120 { 121 flags |= JIT_INSN_VALUE2_LIVE; 122 } 123 if(value2->next_use) 124 { 125 flags |= JIT_INSN_VALUE2_NEXT_USE; 126 } 127 } 128 insn->flags = (short)flags; 129 130 /* Set the destination to "not live, no next use" */ 131 if(dest) 132 { 133 if((flags & JIT_INSN_DEST_IS_VALUE) == 0) 134 { 135 if(!(dest->next_use) && !(dest->live)) 136 { 137 /* There is no next use of this value and it is not 138 live on exit from the block. So we can discard 139 the entire instruction as it will have no effect */ 140 #ifdef _JIT_COMPILE_DEBUG 141 printf("liveness analysis: optimize away instruction '"); 142 jit_dump_insn(stdout, block->func, insn); 143 printf("'\n"); 144 #endif 145 insn->opcode = (short)JIT_OP_NOP; 146 continue; 147 } 148 dest->live = 0; 149 dest->next_use = 0; 150 } 151 else 152 { 153 /* The destination is actually a source value for this 154 instruction (e.g. JIT_OP_STORE_RELATIVE_*) */ 155 dest->live = 1; 156 dest->next_use = 1; 157 } 158 } 159 160 /* Set value1 and value2 to "live, next use" */ 161 if(value1) 162 { 163 value1->live = 1; 164 value1->next_use = 1; 165 } 166 if(value2) 167 { 168 value2->live = 1; 169 value2->next_use = 1; 170 } 171 } 172 } 173 174 #if defined(USE_FORWARD_PROPAGATION) || defined(USE_BACKWARD_PROPAGATION) 175 /* 176 * Check if the instruction is eligible for copy propagation. 177 */ 178 static int 179 is_copy_insn(jit_insn_t insn) 180 { 181 jit_type_t dtype; 182 jit_type_t vtype; 183 184 if (!insn || !insn->dest || !insn->value1) 185 { 186 return 0; 187 } 188 189 switch(insn->opcode) 190 { 191 case JIT_OP_COPY_INT: 192 /* Currently JIT_INSN_COPY_INT is used not only for int-to-int 193 copying but for byte-to-int and short-to-int copying too 194 (see jit_insn_convert). Propagation of byte and short values 195 to instructions that expect ints might confuse them. */ 196 dtype = jit_type_normalize(insn->dest->type); 197 vtype = jit_type_normalize(insn->value1->type); 198 if(dtype != vtype) 199 { 200 /* signed/unsigned int conversion should be safe */ 201 if((dtype->kind == JIT_TYPE_INT || dtype->kind == JIT_TYPE_UINT) 202 && (vtype->kind == JIT_TYPE_INT || vtype->kind == JIT_TYPE_UINT)) 203 { 204 return 1; 205 } 206 return 0; 207 } 208 return 1; 209 210 case JIT_OP_COPY_LOAD_SBYTE: 211 case JIT_OP_COPY_LOAD_UBYTE: 212 case JIT_OP_COPY_LOAD_SHORT: 213 case JIT_OP_COPY_LOAD_USHORT: 214 case JIT_OP_COPY_LONG: 215 case JIT_OP_COPY_FLOAT32: 216 case JIT_OP_COPY_FLOAT64: 217 case JIT_OP_COPY_NFLOAT: 218 case JIT_OP_COPY_STRUCT: 219 case JIT_OP_COPY_STORE_BYTE: 220 case JIT_OP_COPY_STORE_SHORT: 221 return 1; 222 } 223 224 return 0; 225 } 226 #endif 227 228 #if USE_FORWARD_PROPAGATION 229 /* 230 * Perform simple copy propagation within basic block. Replaces instructions 231 * that look like this: 232 * 233 * i) t = x 234 * ... 235 * j) y = op(t) 236 * 237 * with the folowing: 238 * 239 * i) t = x 240 * ... 241 * j) y = op(x) 242 * 243 * If "t" is not used after the instruction "j" then further liveness analysis 244 * may replace the instruction "i" with a noop: 245 * 246 * i) noop 247 * ... 248 * j) y = op(x) 249 * 250 * The propagation stops as soon as either "t" or "x" are changed (used as a 251 * dest in a different instruction). 252 */ 253 static int 254 forward_propagation(jit_block_t block) 255 { 256 int optimized; 257 jit_insn_iter_t iter, iter2; 258 jit_insn_t insn, insn2; 259 jit_value_t dest, value; 260 int flags2; 261 262 optimized = 0; 263 264 jit_insn_iter_init(&iter, block); 265 while((insn = jit_insn_iter_next(&iter)) != 0) 266 { 267 if(!is_copy_insn(insn)) 268 { 269 continue; 270 } 271 272 dest = insn->dest; 273 value = insn->value1; 274 275 /* Discard copy to itself */ 276 if(dest == value) 277 { 278 #ifdef _JIT_COMPILE_DEBUG 279 printf("forward copy propagation: optimize away copy to itself in '"); 280 jit_dump_insn(stdout, block->func, insn); 281 printf("'\n"); 282 #endif 283 insn->opcode = (short)JIT_OP_NOP; 284 optimized = 1; 285 continue; 286 } 287 288 /* Not smart enough to tell when it is safe to optimize copying 289 to a value that is used in other basic blocks or may be 290 aliased. */ 291 if(!dest->is_temporary) 292 { 293 continue; 294 } 295 if(dest->is_addressable || dest->is_volatile) 296 { 297 continue; 298 } 299 if(value->is_addressable || value->is_volatile) 300 { 301 continue; 302 } 303 304 iter2 = iter; 305 while((insn2 = jit_insn_iter_next(&iter2)) != 0) 306 { 307 /* Skip NOP instructions, which may have arguments left 308 over from when the instruction was replaced, but which 309 are not relevant to our analysis */ 310 if(insn->opcode == JIT_OP_NOP) 311 { 312 continue; 313 } 314 315 flags2 = insn2->flags; 316 if((flags2 & JIT_INSN_DEST_OTHER_FLAGS) == 0) 317 { 318 if((flags2 & JIT_INSN_DEST_IS_VALUE) == 0) 319 { 320 if(insn2->dest == dest || insn2->dest == value) 321 { 322 break; 323 } 324 } 325 else if(insn2->dest == dest) 326 { 327 #ifdef _JIT_COMPILE_DEBUG 328 printf("forward copy propagation: in '"); 329 jit_dump_insn(stdout, block->func, insn2); 330 printf("' replace "); 331 jit_dump_value(stdout, block->func, insn2->dest, 0); 332 printf(" with "); 333 jit_dump_value(stdout, block->func, value, 0); 334 printf("'\n"); 335 #endif 336 insn2->dest = value; 337 optimized = 1; 338 } 339 } 340 if((flags2 & JIT_INSN_VALUE1_OTHER_FLAGS) == 0) 341 { 342 if(insn2->value1 == dest) 343 { 344 #ifdef _JIT_COMPILE_DEBUG 345 printf("forward copy propagation: in '"); 346 jit_dump_insn(stdout, block->func, insn2); 347 printf("' replace "); 348 jit_dump_value(stdout, block->func, insn2->value1, 0); 349 printf(" with "); 350 jit_dump_value(stdout, block->func, value, 0); 351 printf("'\n"); 352 #endif 353 insn2->value1 = value; 354 optimized = 1; 355 } 356 } 357 if((flags2 & JIT_INSN_VALUE2_OTHER_FLAGS) == 0) 358 { 359 if(insn2->value2 == dest) 360 { 361 #ifdef _JIT_COMPILE_DEBUG 362 printf("forward copy propagation: in '"); 363 jit_dump_insn(stdout, block->func, insn2); 364 printf("' replace "); 365 jit_dump_value(stdout, block->func, insn2->value2, 0); 366 printf(" with "); 367 jit_dump_value(stdout, block->func, value, 0); 368 printf("'\n"); 369 #endif 370 insn2->value2 = value; 371 optimized = 1; 372 } 373 } 374 } 375 } 376 377 return optimized; 378 } 379 #endif 380 381 #ifdef USE_BACKWARD_PROPAGATION 382 /* 383 * Perform simple copy propagation within basic block for the case when a 384 * temporary value is stored to another value. This replaces instructions 385 * that look like this: 386 * 387 * i) t = op(x) 388 * ... 389 * j) y = t 390 * 391 * with the following 392 * 393 * i) y = op(x) 394 * ... 395 * j) noop 396 * 397 * This is only allowed if "t" is used only in the instructions "i" and "j" 398 * and "y" is not used between "i" and "j" (but can be used after "j"). 399 */ 400 static int 401 backward_propagation(jit_block_t block) 402 { 403 int optimized; 404 jit_insn_iter_t iter, iter2; 405 jit_insn_t insn, insn2; 406 jit_value_t dest, value; 407 int flags2; 408 409 optimized = 0; 410 411 jit_insn_iter_init_last(&iter, block); 412 while((insn = jit_insn_iter_previous(&iter)) != 0) 413 { 414 if(!is_copy_insn(insn)) 415 { 416 continue; 417 } 418 419 dest = insn->dest; 420 value = insn->value1; 421 422 /* Discard copy to itself */ 423 if(dest == value) 424 { 425 #ifdef _JIT_COMPILE_DEBUG 426 printf("backward copy propagation: optimize away copy to itself in '"); 427 jit_dump_insn(stdout, block->func, insn); 428 printf("'\n"); 429 #endif 430 insn->opcode = (short)JIT_OP_NOP; 431 optimized = 1; 432 continue; 433 } 434 435 /* "value" is used afterwards so we cannot eliminate it here */ 436 if((insn->flags & (JIT_INSN_VALUE1_LIVE | JIT_INSN_VALUE1_NEXT_USE)) != 0) 437 { 438 continue; 439 } 440 441 if(dest->is_addressable || dest->is_volatile) 442 { 443 continue; 444 } 445 if(value->is_addressable || value->is_volatile) 446 { 447 continue; 448 } 449 450 iter2 = iter; 451 while((insn2 = jit_insn_iter_previous(&iter2)) != 0) 452 { 453 /* Skip NOP instructions, which may have arguments left 454 over from when the instruction was replaced, but which 455 are not relevant to our analysis */ 456 if(insn->opcode == JIT_OP_NOP) 457 { 458 continue; 459 } 460 461 flags2 = insn2->flags; 462 if((flags2 & JIT_INSN_DEST_OTHER_FLAGS) == 0) 463 { 464 if(insn2->dest == dest) 465 { 466 break; 467 } 468 if(insn2->dest == value) 469 { 470 if((flags2 & JIT_INSN_DEST_IS_VALUE) == 0) 471 { 472 #ifdef _JIT_COMPILE_DEBUG 473 printf("backward copy propagation: in '"); 474 jit_dump_insn(stdout, block->func, insn2); 475 printf("' replace "); 476 jit_dump_value(stdout, block->func, insn2->dest, 0); 477 printf(" with "); 478 jit_dump_value(stdout, block->func, dest, 0); 479 printf(" and optimize away '"); 480 jit_dump_insn(stdout, block->func, insn); 481 printf("'\n"); 482 #endif 483 insn->opcode = (short)JIT_OP_NOP; 484 insn2->dest = dest; 485 optimized = 1; 486 } 487 break; 488 } 489 } 490 if((flags2 & JIT_INSN_VALUE1_OTHER_FLAGS) == 0) 491 { 492 if(insn2->value1 == dest || insn2->value1 == value) 493 { 494 break; 495 } 496 } 497 if((flags2 & JIT_INSN_VALUE2_OTHER_FLAGS) == 0) 498 { 499 if(insn2->value2 == dest || insn2->value1 == value) 500 { 501 break; 502 } 503 } 504 } 505 } 506 507 return optimized; 508 } 509 #endif 510 511 /* Reset value liveness flags. */ 512 static void 513 reset_value_liveness(jit_value_t value) 514 { 515 if(value) 516 { 517 if (!value->is_constant && !value->is_temporary) 518 { 519 value->live = 1; 520 } 521 else 522 { 523 value->live = 0; 524 } 525 value->next_use = 0; 526 } 527 } 528 529 /* 530 * Re-scan the block to reset the liveness flags on all non-temporaries 531 * because we need them in the original state for the next block. 532 */ 533 static void 534 reset_liveness_flags(jit_block_t block, int reset_all) 535 { 536 jit_insn_iter_t iter; 537 jit_insn_t insn; 538 int flags; 539 540 jit_insn_iter_init(&iter, block); 541 while((insn = jit_insn_iter_next(&iter)) != 0) 542 { 543 flags = insn->flags; 544 if((flags & JIT_INSN_DEST_OTHER_FLAGS) == 0) 545 { 546 reset_value_liveness(insn->dest); 547 } 548 if((flags & JIT_INSN_VALUE1_OTHER_FLAGS) == 0) 549 { 550 reset_value_liveness(insn->value1); 551 } 552 if((flags & JIT_INSN_VALUE2_OTHER_FLAGS) == 0) 553 { 554 reset_value_liveness(insn->value2); 555 } 556 if(reset_all) 557 { 558 flags &= ~(JIT_INSN_DEST_LIVE | JIT_INSN_DEST_NEXT_USE 559 |JIT_INSN_VALUE1_LIVE | JIT_INSN_VALUE1_NEXT_USE 560 |JIT_INSN_VALUE2_LIVE | JIT_INSN_VALUE2_NEXT_USE); 561 } 562 } 563 } 564 565 void _jit_function_compute_liveness(jit_function_t func) 566 { 567 jit_block_t block = func->builder->entry_block; 568 while(block != 0) 569 { 570 #ifdef USE_FORWARD_PROPAGATION 571 /* Perform forward copy propagation for the block */ 572 forward_propagation(block); 573 #endif 574 575 /* Reset the liveness flags for the next block */ 576 reset_liveness_flags(block, 0); 577 578 /* Compute the liveness flags for the block */ 579 compute_liveness_for_block(block); 580 581 #ifdef USE_BACKWARD_PROPAGATION 582 /* Perform backward copy propagation for the block */ 583 if(backward_propagation(block)) 584 { 585 /* Reset the liveness flags and compute them again */ 586 reset_liveness_flags(block, 1); 587 compute_liveness_for_block(block); 588 } 589 #endif 590 591 /* Move on to the next block in the function */ 592 block = block->next; 593 } 594 }