| 4447 | } |
| 4448 | |
| 4449 | void OpDispatchBuilder::ALUOp(OpcodeArgs, FEXCore::IR::IROps ALUIROp, FEXCore::IR::IROps AtomicFetchOp, unsigned SrcIdx) { |
| 4450 | // On x86, the canonical way to zero a register is XOR with itself. Detect and |
| 4451 | // emit optimal arm64 assembly. |
| 4452 | if (!DestIsLockedMem(Op) && ALUIROp == FEXCore::IR::IROps::OP_XOR && Op->Dest.IsGPR() && Op->Src[SrcIdx].IsGPR() && |
| 4453 | Op->Dest.Data.GPR == Op->Src[SrcIdx].Data.GPR) { |
| 4454 | |
| 4455 | // Set flags for zero result with inverted carry. We subtract an arbitrary |
| 4456 | // register from itself to get the zero, since `subs wzr, #0` is not |
| 4457 | // encodable. This is optimal and works regardless of the opsize. |
| 4458 | auto Zero = LoadGPR(Op->Dest.Data.GPR.GPR); |
| 4459 | HandleNZ00Write(); |
| 4460 | InvalidateAF(); |
| 4461 | CalculatePF(SubWithFlags(OpSize::i32Bit, Zero, Zero)); |
| 4462 | CFInverted = true; |
| 4463 | FlushRegisterCache(); |
| 4464 | |
| 4465 | // Move 0 into the register |
| 4466 | StoreResult(GPRClass, Op, Constant(0), OpSize::iInvalid); |
| 4467 | return; |
| 4468 | } |
| 4469 | |
| 4470 | auto Size = OpSizeFromDst(Op); |
| 4471 | auto ResultSize = Size; |
| 4472 | |
| 4473 | auto RoundedSize = Size; |
| 4474 | if (ALUIROp != FEXCore::IR::IROps::OP_ANDWITHFLAGS) { |
| 4475 | RoundedSize = std::max(OpSize::i32Bit, RoundedSize); |
| 4476 | } |
| 4477 | |
| 4478 | // X86 basic ALU ops just do the operation between the destination and a single source |
| 4479 | Ref Src = LoadSource(GPRClass, Op, Op->Src[SrcIdx], Op->Flags, {.AllowUpperGarbage = true}); |
| 4480 | |
| 4481 | // Try to eliminate the masking after 8/16-bit operations with constants, by |
| 4482 | // promoting to a full size operation that preserves the upper bits. |
| 4483 | uint64_t Const; |
| 4484 | bool IsConst = IsValueConstant(WrapNode(Src), &Const); |
| 4485 | if (Size < OpSize::i32Bit && !DestIsLockedMem(Op) && Op->Dest.IsGPR() && !Op->Dest.Data.GPR.HighBits && IsConst && |
| 4486 | (ALUIROp == IR::IROps::OP_XOR || ALUIROp == IR::IROps::OP_OR || ALUIROp == IR::IROps::OP_ANDWITHFLAGS)) { |
| 4487 | |
| 4488 | RoundedSize = ResultSize = GetGPROpSize(); |
| 4489 | LOGMAN_THROW_A_FMT(Const < (1ull << IR::OpSizeAsBits(Size)), "does not clobber"); |
| 4490 | |
| 4491 | // For AND, we can play the same trick but we instead need the upper bits of |
| 4492 | // the constant to be all-1s instead of all-0s to preserve. We also can't |
| 4493 | // use andwithflags in this case, since we've promoted to 64-bit so the |
| 4494 | // negate flag would be wrong, but using the regular logical operation path |
| 4495 | // instead still ends up a net win for uops. |
| 4496 | // |
| 4497 | // In the common case where the constant is of the form (1 << x) - 1, the |
| 4498 | // adjusted constant here will inline into the arm64 and instruction, so if |
| 4499 | // flags are not needed, we save an instruction overall. |
| 4500 | if (ALUIROp == IR::IROps::OP_ANDWITHFLAGS) { |
| 4501 | Src = Constant(Const | ~((1ull << IR::OpSizeAsBits(Size)) - 1)); |
| 4502 | ALUIROp = IR::IROps::OP_AND; |
| 4503 | } |
| 4504 | } |
| 4505 | |
| 4506 | Ref Result {}; |
nothing calls this directly
no test coverage detected