| 108 | #endif |
| 109 | |
| 110 | bool JitCodeGen::calculateLongestBlock(DecodedOp* op) { |
| 111 | U32 eip = this->startingEip; |
| 112 | DecodedOp* nextOp = op; |
| 113 | U32 furthestJump = 0; |
| 114 | |
| 115 | // find the longest block we can compile |
| 116 | // branches that jump out of the block will be the end of the block |
| 117 | |
| 118 | // 1st pass, find longest block including all direct jumps (conditional jumps, direct jumps, loop, etc) |
| 119 | |
| 120 | // jumpTo will keep track of valid jump targets. We need this if we are going to decode more instructions (cpu->getOp) |
| 121 | // Without this the next byte of instruction may actually be invalid, I have seen skipped bytes in the instructions, |
| 122 | // I assume its for alignment/performance reasons. Firefight installer will trigger this |
| 123 | BHashTable<U32, DecodedOp*> jumpTo; |
| 124 | |
| 125 | // opentdd will trigger this isValid check |
| 126 | while (nextOp && nextOp->isValid()) { |
| 127 | // could be ret, call, int. Basically this is an instruction where we are not guaranteed to see a next instruction |
| 128 | if (nextOp->isBranch() && !nextOp->isDirectJumpBranch()) { |
| 129 | // is this the last return, if so, then don't decode more |
| 130 | if (nextOp->isRet() && furthestJump < eip) { |
| 131 | break; |
| 132 | } |
| 133 | if (nextOp->isIndirectJump()) { |
| 134 | // opentdd needs this when creating a new game, I'm not sure why data.cpu->memory->getDecodedOp(eip + nextOp->len) will find an op but its not correct, might be another bug |
| 135 | break; |
| 136 | } |
| 137 | // These next 4 look aheads, nextOp->next = |
| 138 | // They don't improve performance on Quake 2, but do make a significant improvement for Cinebench, 10-20% |
| 139 | if (!nextOp->next) { |
| 140 | // don't call cpu->getOp since that will decode and we are not sure the next byte is a valid instruction. |
| 141 | // we can call memory->getDecodedOp to see if this instruction has already been decoded, in that case we know its valid. |
| 142 | nextOp->next = this->cpu->memory->getDecodedOp(eip + nextOp->len); |
| 143 | } |
| 144 | if (!nextOp->next && nextOp->isDirectBranchWithNext()) { |
| 145 | nextOp->next = this->cpu->getOp(eip + nextOp->len, 0); |
| 146 | } |
| 147 | if (!nextOp->next && nextOp->isCall() && furthestJump > eip) { |
| 148 | nextOp->next = this->cpu->getOp(eip + nextOp->len, 0); |
| 149 | } |
| 150 | if (!nextOp->next && jumpTo.contains(eip + nextOp->len)) { |
| 151 | // this gives a 30% improvement to cinebench, but makes F-16 unstable. I wonder if something is wrong with this line of code |
| 152 | // or if by creating a larger block it increases the chance that self modifying code will hit it and there is someting wrong |
| 153 | // with how I handle self modifying code |
| 154 | nextOp->next = this->cpu->getOp(eip + nextOp->len, 0); |
| 155 | } |
| 156 | |
| 157 | if (!nextOp->next) { |
| 158 | // since we couldn't figure out if the next byte is part of a valid instruction, we are done looking |
| 159 | break; |
| 160 | } |
| 161 | } |
| 162 | if (nextOp->isDirectJumpBranch() && (eip + nextOp->len + nextOp->imm) < this->startingEip) { |
| 163 | // if we have somewhere to go after this, then continue |
| 164 | |
| 165 | // see if we can restart this JIT with the target of this jump to before the incoming op argument to create a bigger JIT block |
| 166 | DecodedOp* targetOp = this->cpu->memory->getDecodedOp(eip + nextOp->len + nextOp->imm); |
| 167 | if (!targetOp) { |
nothing calls this directly
no test coverage detected