| 1146 | } |
| 1147 | |
| 1148 | bool Decoder::IsBranchMonoTailcall(uint64_t NumInstructions) const { |
| 1149 | // While the mono call backpatching block can easily be detected due it being the only one to contain SMC-faulting |
| 1150 | // atomics, that can't be said for the tailcall jump backpatcher which has changed several times across versions and |
| 1151 | // can be partially inlined. To work around this, instead detect the tailcall site itself and force full non-signal-based |
| 1152 | // SMC detection for that single block. |
| 1153 | if (!ExecutableRangeWritable) { |
| 1154 | // We only care about jitted code |
| 1155 | return false; |
| 1156 | } |
| 1157 | |
| 1158 | // See mini-{amd64,x86}.c in the mono codebase, specifically where METHOD_JUMP patches are emitted. |
| 1159 | if (GetGPROpSize() == IR::OpSize::i32Bit) { |
| 1160 | // Matches: |
| 1161 | // LEAVE |
| 1162 | // <none> / NOP / MOV EAX, EAX / LEA EBP, [EBP+0] |
| 1163 | // JMP imm32 |
| 1164 | if (DecodeInst->OP != 0xE9 || NumInstructions < 2) { |
| 1165 | return false; |
| 1166 | } |
| 1167 | |
| 1168 | auto PrevInst = std::prev(DecodeInst); |
| 1169 | if (PrevInst->OP == 0xC9) { |
| 1170 | return true; |
| 1171 | } |
| 1172 | |
| 1173 | if (NumInstructions < 3 || std::prev(PrevInst)->OP != 0xC9) { |
| 1174 | return false; |
| 1175 | } |
| 1176 | |
| 1177 | return PrevInst->OP == 0x90 || (PrevInst->OP == 0x8B && PrevInst->ModRM == 0xC0) || |
| 1178 | (PrevInst->OP == 0x8D && PrevInst->ModRM == 0x6D && PrevInst->Src[1].IsLiteral() && PrevInst->Src[1].Literal() == 0); |
| 1179 | } else { |
| 1180 | FEXCore::X86Tables::ModRMDecoded ModRM; |
| 1181 | ModRM.Hex = DecodeInst->ModRM; |
| 1182 | if (DecodeInst->OPRaw == 0xFF && ModRM.reg == 4 && DecodeInst->Src[0].IsGPR()) { |
| 1183 | if (DecodeInst->Src[0].Data.GPR.GPR == FEXCore::X86State::REG_RAX) { |
| 1184 | // Found in versions of mono from 2024 onwards - matches: |
| 1185 | // REX.W JMP rax |
| 1186 | return (DecodeInst->Flags & (DecodeFlags::FLAG_REX_PREFIX | DecodeFlags::FLAG_REX_WIDENING | DecodeFlags::FLAG_REX_XGPR_B | |
| 1187 | DecodeFlags::FLAG_REX_XGPR_X | DecodeFlags::FLAG_REX_XGPR_R)) == |
| 1188 | (DecodeFlags::FLAG_REX_PREFIX | DecodeFlags::FLAG_REX_WIDENING); |
| 1189 | } else if (NumInstructions > 1 && DecodeInst->Src[0].Data.GPR.GPR == FEXCore::X86State::REG_R11) { |
| 1190 | // Found in older versions of mono - match: |
| 1191 | // MOV r11, imm64 |
| 1192 | // JMP r11 |
| 1193 | auto PrevInst = std::prev(DecodeInst); |
| 1194 | return PrevInst->OP == 0xBB && PrevInst->Dest.IsGPR() && PrevInst->Dest.Data.GPR.GPR == FEXCore::X86State::REG_R11; |
| 1195 | } |
| 1196 | } |
| 1197 | } |
| 1198 | |
| 1199 | return false; |
| 1200 | } |
| 1201 | |
| 1202 | bool Decoder::InstCanContinue() const { |
| 1203 | if (DecodeInst->PC + DecodeInst->InstSize == NextBlockStartAddress) { |