diff --git a/rpcs3/Emu/CPU/CPUTranslator.cpp b/rpcs3/Emu/CPU/CPUTranslator.cpp index 56eece2b36..c70cab655f 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.cpp +++ b/rpcs3/Emu/CPU/CPUTranslator.cpp @@ -610,4 +610,138 @@ void cpu_translator::erase_stores(llvm::ArrayRef args) } } +llvm::KnownBits cpu_translator::get_known_bits_fallback(llvm::Value* value) +{ + // TODO: Improve it - add support for integer addition/subtraction and more stuff + + const auto type = value->getType(); + + if (!type->isVectorTy()) + { + if (const auto it = llvm::dyn_cast(type)) + { + if (auto bin_inst = llvm::dyn_cast(value)) + { + llvm::Value* lhs = ensure(bin_inst->getOperand(0)); + llvm::Value* rhs = ensure(bin_inst->getOperand(1)); + + llvm::ConstantInt* constant_value = llvm::dyn_cast(rhs) ? llvm::dyn_cast(rhs) : llvm::dyn_cast(lhs); + + if (!constant_value) + { + return llvm::KnownBits(type->getScalarSizeInBits()); + } + + if (bin_inst->getOpcode() == llvm::Instruction::Or) + { + llvm::KnownBits ret(type->getScalarSizeInBits()); + ret.One = constant_value->getValue(); + return ret; + } + + if (bin_inst->getOpcode() == llvm::Instruction::And) + { + llvm::KnownBits ret(type->getScalarSizeInBits()); + ret.Zero = constant_value->getValue(); + ret.Zero.flipAllBits(); + return ret; + } + + return llvm::KnownBits(type->getScalarSizeInBits()); + } + } + + fmt::throw_exception("Bad KnownBits type: i%ux", type->getScalarSizeInBits()); + } + + if (auto v = llvm::cast(type); v->getScalarSizeInBits() * v->getNumElements() != 128) + { + // Unsupported + return llvm::KnownBits(type->getScalarSizeInBits()); + } + + const auto cv = llvm::dyn_cast(value); + + if (!cv) + { + if (llvm::isa(value)) + { + llvm::KnownBits ret(type->getScalarSizeInBits()); + ret.Zero.setAllBits(); + return ret; + } + } + + const auto original_value = peek_through_bitcasts(value); + const auto original_type = original_value->getType(); + + auto bin_inst = llvm::dyn_cast(original_value); + + if (!bin_inst) + { + return llvm::KnownBits(type->getScalarSizeInBits()); + } + + llvm::Value* lhs = ensure(bin_inst->getOperand(0)); + llvm::Value* rhs = ensure(bin_inst->getOperand(1)); + + llvm::Value* constant_value = llvm::dyn_cast(rhs) ? llvm::dyn_cast(rhs) : llvm::dyn_cast(lhs); + + if (!constant_value) + { + return llvm::KnownBits(value->getType()->getScalarSizeInBits()); + } + + const auto [ok, v128_const] = get_const_vector(constant_value, -1); + + ensure(ok); + + llvm::APInt dest{}; + + auto combine_bits = [&](const auto& array, u32 size) + { + auto first = +array[0]; + + for (u32 i = 0; i < size; i++) + { + first &= +array[i]; + } + + return first; + }; + + if (type->getScalarType()->isIntegerTy(8)) + { + dest = llvm::APInt(8, combine_bits(v128_const._u8, 16)); + } + else if (type->getScalarType()->isIntegerTy(16)) + { + dest = llvm::APInt(16, combine_bits(v128_const._u16, 8)); + } + else if (type->getScalarType()->isIntegerTy(32)) + { + dest = llvm::APInt(32, combine_bits(v128_const._u32, 4)); + } + else // if (type->getScalarType()->isIntegerTy(64)) + { + return llvm::KnownBits(type->getScalarSizeInBits()); + } + + if (bin_inst->getOpcode() == llvm::Instruction::Or) + { + llvm::KnownBits ret(type->getScalarSizeInBits()); + ret.One = dest; + return ret; + } + + if (bin_inst->getOpcode() == llvm::Instruction::And) + { + llvm::KnownBits ret(type->getScalarSizeInBits()); + ret.Zero = dest; + ret.Zero.flipAllBits(); + return ret; + } + + return llvm::KnownBits(type->getScalarSizeInBits()); +} #endif diff --git a/rpcs3/Emu/CPU/CPUTranslator.h b/rpcs3/Emu/CPU/CPUTranslator.h index 93a0733bac..de2a44b5af 100644 --- a/rpcs3/Emu/CPU/CPUTranslator.h +++ b/rpcs3/Emu/CPU/CPUTranslator.h @@ -4307,6 +4307,8 @@ template return true; } + llvm::KnownBits get_known_bits_fallback(llvm::Value* value); + template llvm::KnownBits get_known_bits(T a) { @@ -4314,7 +4316,7 @@ template if (!is_known_bits_safe(value)) { - return llvm::KnownBits(value->getType()->getScalarSizeInBits()); + return get_known_bits_fallback(value); } return llvm::computeKnownBits(value, m_module->getDataLayout());