| 2387 | } |
| 2388 | |
| 2389 | void OpDispatchBuilder::BTOp(OpcodeArgs, uint32_t SrcIndex, BTAction Action) { |
| 2390 | Ref Value; |
| 2391 | ArithRef Src; |
| 2392 | bool IsNonconstant = Op->Src[SrcIndex].IsGPR(); |
| 2393 | |
| 2394 | const uint32_t Size = GetDstBitSize(Op); |
| 2395 | const uint32_t Mask = Size - 1; |
| 2396 | |
| 2397 | if (IsNonconstant) { |
| 2398 | // Because we mask explicitly with And/Bfe/Sbfe after, we can allow garbage here. |
| 2399 | Src = ARef(LoadSource(GPRClass, Op, Op->Src[SrcIndex], Op->Flags, {.AllowUpperGarbage = true})); |
| 2400 | } else { |
| 2401 | // Can only be an immediate |
| 2402 | // Masked by operand size |
| 2403 | Src = ARef(Op->Src[SrcIndex].Data.Literal.Value & Mask); |
| 2404 | } |
| 2405 | |
| 2406 | if (Op->Dest.IsGPR()) { |
| 2407 | // When the destination is a GPR, we don't care about garbage in the upper bits. |
| 2408 | // Load the full register. |
| 2409 | auto Dest = LoadSource_WithOpSize(GPRClass, Op, Op->Dest, GetGPROpSize(), Op->Flags); |
| 2410 | Value = Dest; |
| 2411 | |
| 2412 | // Get the bit selection from the src. We need to mask for 8/16-bit, but |
| 2413 | // rely on the implicit masking of Lshr for native sizes. |
| 2414 | unsigned LshrSize = std::max<uint8_t>(IR::OpSizeToSize(OpSize::i32Bit), Size / 8); |
| 2415 | auto BitSelect = (Size == (LshrSize * 8)) ? Src : Src.And(Mask); |
| 2416 | auto LshrOpSize = IR::SizeToOpSize(LshrSize); |
| 2417 | |
| 2418 | // OF/SF/AF/PF undefined. ZF must be preserved. We choose to preserve OF/SF |
| 2419 | // too since we just use an rmif to insert into CF directly. We could |
| 2420 | // optimize perhaps. |
| 2421 | // |
| 2422 | // Set CF before the action to save a move, except for complements where we |
| 2423 | // can reuse the invert. |
| 2424 | if (Action != BTAction::BTComplement) { |
| 2425 | if (IsNonconstant) { |
| 2426 | Value = _Lshr(IR::SizeToOpSize(LshrSize), Value, BitSelect.Ref()); |
| 2427 | } |
| 2428 | |
| 2429 | SetRFLAG(Value, X86State::RFLAG_CF_RAW_LOC, Src.IsConstant ? Src.C : 0, true); |
| 2430 | CFInverted = false; |
| 2431 | } |
| 2432 | |
| 2433 | switch (Action) { |
| 2434 | case BTAction::BTNone: { |
| 2435 | /* Nothing to do */ |
| 2436 | break; |
| 2437 | } |
| 2438 | |
| 2439 | case BTAction::BTClear: { |
| 2440 | Dest = _Andn(LshrOpSize, Dest, BitSelect.MaskBit(LshrOpSize).Ref()); |
| 2441 | StoreResult(GPRClass, Op, Dest, OpSize::iInvalid); |
| 2442 | break; |
| 2443 | } |
| 2444 | |
| 2445 | case BTAction::BTSet: { |
| 2446 | Dest = _Or(LshrOpSize, Dest, BitSelect.MaskBit(LshrOpSize).Ref()); |
nothing calls this directly
no test coverage detected