"turns out theres a lot of quirks with the div instructions we havent been covering if the denom is 0, we jump to the end and mov eax/rax to dst, which is correct because ppc raises no exceptions for divide by 0 unlike x86 except we don't initialize eax before that jump, so whatever garbage from the previous sequence that has been left in eax/rax is what the result of the instruction will be and then in our constant folding, we don't do the same zero check in Value::Div, so if we constant folded the denom to 0 we will host crash the ppc manual says the result for a division by 0 is undefined, but in reality it seems it is always 0 there are a few posts i saw from googling about it, and tests on my rgh gave me 0, but then another issue came up and that is that we dont check for signed overflow in our division, so we raise an exception if guest code ever does (1<<signbit_pos) / -1 signed overflow in division also produces 0 on ppc the last thing is that if src2 is constant we skip the 0 check for division without checking if its nonzero all weird, likely very rare edge cases, except for maybe the signed overflow division chrispy — Today at 9:51 AM oh yeah, and because the int members of constantvalue are all signed ints, we were actually doing signed division always with constant folding" fixed an earlier mistake by me with the precision of fresx made some optimization disableable implemented vkpkx fixed possible bugs with vsr/vsl constant folding disabled the nice imul code for now, there was a bug with int64 version and i dont have time to check started on multiplication/addition/subtraction/division identities Removed optimized VSL implementation, it's going to have to be rewritten anyway Added ppc_ctx_t to xboxkrnl shim for direct context access started working on KeSaveFloatingPointState, re'ed most of it Exposed some more state/functionality to the kernel for implementing lower level routines like the save/restore ones Add cvar to re-enable incorrect mxcsr behavior if a user doesnt care and wants better cpu performance Stubbed out more impossible sequences, replace mul_hi_i32 with a 64 bit multiply
946 lines
32 KiB
C++
946 lines
32 KiB
C++
/**
|
|
******************************************************************************
|
|
* Xenia : Xbox 360 Emulator Research Project *
|
|
******************************************************************************
|
|
* Copyright 2013 Ben Vanik. All rights reserved. *
|
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
|
******************************************************************************
|
|
*/
|
|
|
|
#include "xenia/cpu/compiler/passes/constant_propagation_pass.h"
|
|
|
|
#include <cmath>
|
|
|
|
#include "xenia/base/assert.h"
|
|
#include "xenia/base/cvar.h"
|
|
#include "xenia/base/profiling.h"
|
|
#include "xenia/cpu/function.h"
|
|
#include "xenia/cpu/processor.h"
|
|
|
|
DEFINE_bool(inline_mmio_access, true, "Inline constant MMIO loads and stores.",
|
|
"CPU");
|
|
|
|
DEFINE_bool(permit_float_constant_evaluation, false,
|
|
"Allow float constant evaluation, may produce incorrect results "
|
|
"and break games math",
|
|
"CPU");
|
|
|
|
namespace xe {
|
|
namespace cpu {
|
|
namespace compiler {
|
|
namespace passes {
|
|
|
|
// TODO(benvanik): remove when enums redefined.
|
|
using namespace xe::cpu::hir;
|
|
|
|
using xe::cpu::hir::HIRBuilder;
|
|
using xe::cpu::hir::TypeName;
|
|
using xe::cpu::hir::Value;
|
|
|
|
ConstantPropagationPass::ConstantPropagationPass()
|
|
: ConditionalGroupSubpass() {}
|
|
|
|
ConstantPropagationPass::~ConstantPropagationPass() {}
|
|
|
|
bool ConstantPropagationPass::Run(HIRBuilder* builder, bool& result) {
|
|
// Once ContextPromotion has run there will likely be a whole slew of
|
|
// constants that can be pushed through the function.
|
|
// Example:
|
|
// store_context +100, 1000
|
|
// v0 = load_context +100
|
|
// v1 = add v0, v0
|
|
// store_context +200, v1
|
|
// after PromoteContext:
|
|
// store_context +100, 1000
|
|
// v0 = 1000
|
|
// v1 = add v0, v0
|
|
// store_context +200, v1
|
|
// after PropagateConstants:
|
|
// store_context +100, 1000
|
|
// v0 = 1000
|
|
// v1 = add 1000, 1000
|
|
// store_context +200, 2000
|
|
// A DCE run after this should clean up any of the values no longer needed.
|
|
//
|
|
// Special care needs to be taken with paired instructions. For example,
|
|
// DID_CARRY needs to be set as a constant:
|
|
// v1 = sub.2 20, 1
|
|
// v2 = did_carry v1
|
|
// should become:
|
|
// v1 = 19
|
|
// v2 = 0
|
|
|
|
result = false;
|
|
auto block = builder->first_block();
|
|
while (block) {
|
|
for (auto i = block->instr_head; i; i = i->next) {
|
|
if (((i->opcode->flags & OPCODE_FLAG_DISALLOW_CONSTANT_FOLDING) != 0) &&
|
|
!cvars::permit_float_constant_evaluation) {
|
|
continue;
|
|
}
|
|
bool might_be_floatop = false;
|
|
|
|
i->VisitValueOperands(
|
|
[&might_be_floatop](Value* current_opnd, uint32_t opnd_index) {
|
|
might_be_floatop |= current_opnd->MaybeFloaty();
|
|
});
|
|
if (i->dest) {
|
|
might_be_floatop |= i->dest->MaybeFloaty();
|
|
}
|
|
|
|
bool should_skip_because_of_float =
|
|
might_be_floatop && !cvars::permit_float_constant_evaluation;
|
|
|
|
auto v = i->dest;
|
|
switch (i->opcode->num) {
|
|
case OPCODE_DEBUG_BREAK_TRUE:
|
|
if (i->src1.value->IsConstant()) {
|
|
if (i->src1.value->IsConstantTrue()) {
|
|
i->Replace(&OPCODE_DEBUG_BREAK_info, i->flags);
|
|
} else {
|
|
i->Remove();
|
|
}
|
|
result = true;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_TRAP_TRUE:
|
|
if (i->src1.value->IsConstant()) {
|
|
if (i->src1.value->IsConstantTrue()) {
|
|
i->Replace(&OPCODE_TRAP_info, i->flags);
|
|
} else {
|
|
i->Remove();
|
|
}
|
|
result = true;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_CALL_TRUE:
|
|
if (i->src1.value->IsConstant()) {
|
|
if (i->src1.value->IsConstantTrue()) {
|
|
auto symbol = i->src2.symbol;
|
|
i->Replace(&OPCODE_CALL_info, i->flags);
|
|
i->src1.symbol = symbol;
|
|
} else {
|
|
i->Remove();
|
|
}
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_CALL_INDIRECT:
|
|
if (i->src1.value->IsConstant()) {
|
|
auto function = processor_->LookupFunction(
|
|
uint32_t(i->src1.value->constant.i32));
|
|
if (!function) {
|
|
break;
|
|
}
|
|
i->Replace(&OPCODE_CALL_info, i->flags);
|
|
i->src1.symbol = function;
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_CALL_INDIRECT_TRUE:
|
|
if (i->src1.value->IsConstant()) {
|
|
if (i->src1.value->IsConstantTrue()) {
|
|
auto value = i->src2.value;
|
|
i->Replace(&OPCODE_CALL_INDIRECT_info, i->flags);
|
|
i->set_src1(value);
|
|
} else {
|
|
i->Remove();
|
|
}
|
|
result = true;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_BRANCH_TRUE:
|
|
if (i->src1.value->IsConstant()) {
|
|
if (i->src1.value->IsConstantTrue()) {
|
|
auto label = i->src2.label;
|
|
i->Replace(&OPCODE_BRANCH_info, i->flags);
|
|
i->src1.label = label;
|
|
} else {
|
|
i->Remove();
|
|
}
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_BRANCH_FALSE:
|
|
if (i->src1.value->IsConstant()) {
|
|
if (i->src1.value->IsConstantFalse()) {
|
|
auto label = i->src2.label;
|
|
i->Replace(&OPCODE_BRANCH_info, i->flags);
|
|
i->src1.label = label;
|
|
} else {
|
|
i->Remove();
|
|
}
|
|
result = true;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_CAST:
|
|
if (i->src1.value->IsConstant()) {
|
|
TypeName target_type = v->type;
|
|
v->set_from(i->src1.value);
|
|
v->Cast(target_type);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_CONVERT:
|
|
if (i->src1.value->IsConstant()) {
|
|
TypeName target_type = v->type;
|
|
v->set_from(i->src1.value);
|
|
v->Convert(target_type, RoundMode(i->flags));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_ROUND:
|
|
if (i->src1.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->Round(RoundMode(i->flags));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_ZERO_EXTEND:
|
|
if (i->src1.value->IsConstant()) {
|
|
TypeName target_type = v->type;
|
|
v->set_from(i->src1.value);
|
|
v->ZeroExtend(target_type);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_SIGN_EXTEND:
|
|
if (i->src1.value->IsConstant()) {
|
|
TypeName target_type = v->type;
|
|
v->set_from(i->src1.value);
|
|
v->SignExtend(target_type);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_TRUNCATE:
|
|
if (i->src1.value->IsConstant()) {
|
|
TypeName target_type = v->type;
|
|
v->set_from(i->src1.value);
|
|
v->Truncate(target_type);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_LOAD:
|
|
case OPCODE_LOAD_OFFSET:
|
|
if (i->src1.value->IsConstant()) {
|
|
assert_false(i->flags & LOAD_STORE_BYTE_SWAP);
|
|
auto memory = processor_->memory();
|
|
auto address = i->src1.value->constant.i32;
|
|
if (i->opcode->num == OPCODE_LOAD_OFFSET) {
|
|
address += i->src2.value->constant.i32;
|
|
}
|
|
|
|
auto mmio_range =
|
|
processor_->memory()->LookupVirtualMappedRange(address);
|
|
if (cvars::inline_mmio_access && mmio_range) {
|
|
i->Replace(&OPCODE_LOAD_MMIO_info, 0);
|
|
i->src1.offset = reinterpret_cast<uint64_t>(mmio_range);
|
|
i->src2.offset = address;
|
|
result = true;
|
|
} else {
|
|
auto heap = memory->LookupHeap(address);
|
|
uint32_t protect;
|
|
if (heap && heap->QueryProtect(address, &protect) &&
|
|
!(protect & kMemoryProtectWrite) &&
|
|
(protect & kMemoryProtectRead)) {
|
|
// Memory is readonly - can just return the value.
|
|
auto host_addr = memory->TranslateVirtual(address);
|
|
switch (v->type) {
|
|
case INT8_TYPE:
|
|
v->set_constant(xe::load<uint8_t>(host_addr));
|
|
i->Remove();
|
|
result = true;
|
|
break;
|
|
case INT16_TYPE:
|
|
v->set_constant(xe::load<uint16_t>(host_addr));
|
|
i->Remove();
|
|
result = true;
|
|
break;
|
|
case INT32_TYPE:
|
|
v->set_constant(xe::load<uint32_t>(host_addr));
|
|
i->Remove();
|
|
result = true;
|
|
break;
|
|
case INT64_TYPE:
|
|
v->set_constant(xe::load<uint64_t>(host_addr));
|
|
i->Remove();
|
|
result = true;
|
|
break;
|
|
case VEC128_TYPE:
|
|
vec128_t val;
|
|
val.low = xe::load<uint64_t>(host_addr);
|
|
val.high = xe::load<uint64_t>(host_addr + 8);
|
|
v->set_constant(val);
|
|
i->Remove();
|
|
result = true;
|
|
break;
|
|
default:
|
|
assert_unhandled_case(v->type);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case OPCODE_STORE:
|
|
case OPCODE_STORE_OFFSET:
|
|
if (cvars::inline_mmio_access && i->src1.value->IsConstant()) {
|
|
auto address = i->src1.value->constant.i32;
|
|
if (i->opcode->num == OPCODE_STORE_OFFSET) {
|
|
address += i->src2.value->constant.i32;
|
|
}
|
|
|
|
auto mmio_range =
|
|
processor_->memory()->LookupVirtualMappedRange(address);
|
|
if (mmio_range) {
|
|
auto value = i->src2.value;
|
|
if (i->opcode->num == OPCODE_STORE_OFFSET) {
|
|
value = i->src3.value;
|
|
}
|
|
|
|
i->Replace(&OPCODE_STORE_MMIO_info, 0);
|
|
i->src1.offset = reinterpret_cast<uint64_t>(mmio_range);
|
|
i->src2.offset = address;
|
|
i->set_src3(value);
|
|
result = true;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case OPCODE_SELECT:
|
|
if (i->src1.value->IsConstant()) {
|
|
if (i->src1.value->type != VEC128_TYPE) {
|
|
if (i->src1.value->IsConstantTrue()) {
|
|
auto src2 = i->src2.value;
|
|
i->Replace(&OPCODE_ASSIGN_info, 0);
|
|
i->set_src1(src2);
|
|
result = true;
|
|
} else if (i->src1.value->IsConstantFalse()) {
|
|
auto src3 = i->src3.value;
|
|
i->Replace(&OPCODE_ASSIGN_info, 0);
|
|
i->set_src1(src3);
|
|
result = true;
|
|
} else if (i->src2.value->IsConstant() &&
|
|
i->src3.value->IsConstant()) {
|
|
v->set_from(i->src2.value);
|
|
v->Select(i->src3.value, i->src1.value);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
} else {
|
|
if (i->src2.value->IsConstant() && i->src3.value->IsConstant()) {
|
|
v->set_from(i->src2.value);
|
|
v->Select(i->src3.value, i->src1.value);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case OPCODE_IS_TRUE:
|
|
if (i->src1.value->IsConstant()) {
|
|
if (i->src1.value->IsConstantTrue()) {
|
|
v->set_constant(uint8_t(1));
|
|
} else {
|
|
v->set_constant(uint8_t(0));
|
|
}
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_IS_FALSE:
|
|
if (i->src1.value->IsConstant()) {
|
|
if (i->src1.value->IsConstantFalse()) {
|
|
v->set_constant(uint8_t(1));
|
|
} else {
|
|
v->set_constant(uint8_t(0));
|
|
}
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_IS_NAN:
|
|
if (i->src1.value->IsConstant()) {
|
|
if (i->src1.value->type == FLOAT32_TYPE &&
|
|
std::isnan(i->src1.value->constant.f32)) {
|
|
v->set_constant(uint8_t(1));
|
|
} else if (i->src1.value->type == FLOAT64_TYPE &&
|
|
std::isnan(i->src1.value->constant.f64)) {
|
|
v->set_constant(uint8_t(1));
|
|
} else {
|
|
v->set_constant(uint8_t(0));
|
|
}
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
|
|
// TODO(benvanik): compares
|
|
case OPCODE_COMPARE_EQ:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
bool value = i->src1.value->IsConstantEQ(i->src2.value);
|
|
i->dest->set_constant(uint8_t(value));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_COMPARE_NE:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
bool value = i->src1.value->IsConstantNE(i->src2.value);
|
|
i->dest->set_constant(uint8_t(value));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_COMPARE_SLT:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
bool value = i->src1.value->IsConstantSLT(i->src2.value);
|
|
i->dest->set_constant(uint8_t(value));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_COMPARE_SLE:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
bool value = i->src1.value->IsConstantSLE(i->src2.value);
|
|
i->dest->set_constant(uint8_t(value));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_COMPARE_SGT:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
bool value = i->src1.value->IsConstantSGT(i->src2.value);
|
|
i->dest->set_constant(uint8_t(value));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_COMPARE_SGE:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
bool value = i->src1.value->IsConstantSGE(i->src2.value);
|
|
i->dest->set_constant(uint8_t(value));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_COMPARE_ULT:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
bool value = i->src1.value->IsConstantULT(i->src2.value);
|
|
i->dest->set_constant(uint8_t(value));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_COMPARE_ULE:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
bool value = i->src1.value->IsConstantULE(i->src2.value);
|
|
i->dest->set_constant(uint8_t(value));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_COMPARE_UGT:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
bool value = i->src1.value->IsConstantUGT(i->src2.value);
|
|
i->dest->set_constant(uint8_t(value));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_COMPARE_UGE:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
bool value = i->src1.value->IsConstantUGE(i->src2.value);
|
|
i->dest->set_constant(uint8_t(value));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_DID_SATURATE:
|
|
// assert_true(!i->src1.value->IsConstant());
|
|
break;
|
|
|
|
case OPCODE_ADD:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant() &&
|
|
!should_skip_because_of_float) {
|
|
v->set_from(i->src1.value);
|
|
v->Add(i->src2.value);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_ADD_CARRY:
|
|
if (i->src1.value->IsConstantZero() &&
|
|
i->src2.value->IsConstantZero()) {
|
|
Value* ca = i->src3.value;
|
|
if (ca->IsConstant()) {
|
|
TypeName target_type = v->type;
|
|
v->set_from(ca);
|
|
v->ZeroExtend(target_type);
|
|
i->Remove();
|
|
} else {
|
|
if (i->dest->type == ca->type) {
|
|
i->Replace(&OPCODE_ASSIGN_info, 0);
|
|
i->set_src1(ca);
|
|
} else {
|
|
i->Replace(&OPCODE_ZERO_EXTEND_info, 0);
|
|
i->set_src1(ca);
|
|
}
|
|
}
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_SUB:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant() &&
|
|
!should_skip_because_of_float) {
|
|
v->set_from(i->src1.value);
|
|
v->Sub(i->src2.value);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_MUL:
|
|
if (!should_skip_because_of_float) {
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->Mul(i->src2.value);
|
|
i->Remove();
|
|
result = true;
|
|
} else if (i->src1.value->IsConstant() ||
|
|
i->src2.value->IsConstant()) {
|
|
// Reorder the sources to make things simpler.
|
|
// s1 = non-const, s2 = const
|
|
auto s1 =
|
|
i->src1.value->IsConstant() ? i->src2.value : i->src1.value;
|
|
auto s2 =
|
|
i->src1.value->IsConstant() ? i->src1.value : i->src2.value;
|
|
|
|
// Multiplication by one = no-op
|
|
if (s2->type != VEC128_TYPE && s2->IsConstantOne()) {
|
|
i->Replace(&OPCODE_ASSIGN_info, 0);
|
|
i->set_src1(s1);
|
|
result = true;
|
|
} else if (s2->type == VEC128_TYPE) {
|
|
auto& c = s2->constant;
|
|
if (c.v128.f32[0] == 1.f && c.v128.f32[1] == 1.f &&
|
|
c.v128.f32[2] == 1.f && c.v128.f32[3] == 1.f) {
|
|
i->Replace(&OPCODE_ASSIGN_info, 0);
|
|
i->set_src1(s1);
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case OPCODE_MUL_HI:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->MulHi(i->src2.value, (i->flags & ARITHMETIC_UNSIGNED) != 0);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_DIV:
|
|
if (!should_skip_because_of_float) {
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->Div(i->src2.value, (i->flags & ARITHMETIC_UNSIGNED) != 0);
|
|
i->Remove();
|
|
result = true;
|
|
} else if (!i->src2.value->MaybeFloaty() &&
|
|
i->src2.value->IsConstantZero()) {
|
|
// division by 0 == 0 every time,
|
|
v->set_zero(i->src2.value->type);
|
|
i->Remove();
|
|
result = true;
|
|
} else if (i->src2.value->IsConstant()) {
|
|
// Division by one = no-op.
|
|
Value* src1 = i->src1.value;
|
|
if (i->src2.value->type != VEC128_TYPE &&
|
|
i->src2.value->IsConstantOne()) {
|
|
i->Replace(&OPCODE_ASSIGN_info, 0);
|
|
i->set_src1(src1);
|
|
result = true;
|
|
} else if (i->src2.value->type == VEC128_TYPE) {
|
|
auto& c = i->src2.value->constant;
|
|
if (c.v128.f32[0] == 1.f && c.v128.f32[1] == 1.f &&
|
|
c.v128.f32[2] == 1.f && c.v128.f32[3] == 1.f) {
|
|
i->Replace(&OPCODE_ASSIGN_info, 0);
|
|
i->set_src1(src1);
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case OPCODE_MAX:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->Max(i->src2.value);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_NEG:
|
|
if (i->src1.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->Neg();
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_ABS:
|
|
if (i->src1.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->Abs();
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_SQRT:
|
|
if (i->src1.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->Sqrt();
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_RSQRT:
|
|
if (i->src1.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->RSqrt();
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_RECIP:
|
|
if (i->src1.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->Recip();
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_AND:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->And(i->src2.value);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_AND_NOT:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->AndNot(i->src2.value);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_OR:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->Or(i->src2.value);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_XOR:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->Xor(i->src2.value);
|
|
i->Remove();
|
|
result = true;
|
|
} else if (!i->src1.value->IsConstant() &&
|
|
!i->src2.value->IsConstant() &&
|
|
i->src1.value == i->src2.value) {
|
|
v->set_zero(v->type);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_NOT:
|
|
if (i->src1.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->Not();
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_SHL:
|
|
if (i->dest->type != VEC128_TYPE) {
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->Shl(i->src2.value);
|
|
i->Remove();
|
|
result = true;
|
|
} else if (i->src2.value->IsConstantZero()) {
|
|
auto src1 = i->src1.value;
|
|
i->Replace(&OPCODE_ASSIGN_info, 0);
|
|
i->set_src1(src1);
|
|
result = true;
|
|
}
|
|
}
|
|
break;
|
|
case OPCODE_SHR:
|
|
if (i->dest->type != VEC128_TYPE) {
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->Shr(i->src2.value);
|
|
i->Remove();
|
|
result = true;
|
|
} else if (i->src2.value->IsConstantZero()) {
|
|
auto src1 = i->src1.value;
|
|
i->Replace(&OPCODE_ASSIGN_info, 0);
|
|
i->set_src1(src1);
|
|
result = true;
|
|
}
|
|
}
|
|
break;
|
|
case OPCODE_SHA:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->Sha(i->src2.value);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_ROTATE_LEFT:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->RotateLeft(i->src2.value);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_BYTE_SWAP:
|
|
if (i->src1.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->ByteSwap();
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_CNTLZ:
|
|
if (i->src1.value->IsConstant()) {
|
|
v->set_zero(v->type);
|
|
v->CountLeadingZeros(i->src1.value);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
#if 1
|
|
case OPCODE_PERMUTE: {
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant() &&
|
|
i->src3.value->IsConstant() &&
|
|
(i->flags == INT8_TYPE || i->flags == INT16_TYPE)) {
|
|
v->set_from(i->src1.value);
|
|
v->Permute(i->src2.value, i->src3.value, (TypeName)i->flags);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
|
|
else if (i->src2.value->IsConstantZero() &&
|
|
i->src3.value->IsConstantZero() &&
|
|
i->flags == INT8_TYPE /*probably safe for int16 too*/) {
|
|
/*
|
|
chrispy: hoisted this check here from x64_seq_vector where if
|
|
src1 is not constant, but src2 and src3 are zero, then we know
|
|
the result will always be zero
|
|
*/
|
|
|
|
v->set_zero(VEC128_TYPE);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
#endif
|
|
case OPCODE_INSERT:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant() &&
|
|
i->src3.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->Insert(i->src2.value, i->src3.value, (TypeName)i->flags);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_SWIZZLE:
|
|
if (i->src1.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->Swizzle((uint32_t)i->src2.offset, (TypeName)i->flags);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_EXTRACT:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_zero(v->type);
|
|
v->Extract(i->src1.value, i->src2.value);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_SPLAT:
|
|
if (i->src1.value->IsConstant()) {
|
|
v->set_zero(v->type);
|
|
v->Splat(i->src1.value);
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_VECTOR_COMPARE_EQ:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->VectorCompareEQ(i->src2.value, hir::TypeName(i->flags));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_VECTOR_COMPARE_SGT:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->VectorCompareSGT(i->src2.value, hir::TypeName(i->flags));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_VECTOR_COMPARE_SGE:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->VectorCompareSGE(i->src2.value, hir::TypeName(i->flags));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_VECTOR_COMPARE_UGT:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->VectorCompareUGT(i->src2.value, hir::TypeName(i->flags));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_VECTOR_COMPARE_UGE:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->VectorCompareUGE(i->src2.value, hir::TypeName(i->flags));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_VECTOR_CONVERT_F2I:
|
|
if (i->src1.value->IsConstant()) {
|
|
v->set_zero(VEC128_TYPE);
|
|
v->VectorConvertF2I(i->src1.value,
|
|
!!(i->flags & ARITHMETIC_UNSIGNED));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_VECTOR_CONVERT_I2F:
|
|
if (i->src1.value->IsConstant()) {
|
|
v->set_zero(VEC128_TYPE);
|
|
v->VectorConvertI2F(i->src1.value,
|
|
!!(i->flags & ARITHMETIC_UNSIGNED));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_VECTOR_SHL:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->VectorShl(i->src2.value, hir::TypeName(i->flags));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_VECTOR_SHR:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->VectorShr(i->src2.value, hir::TypeName(i->flags));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_VECTOR_ROTATE_LEFT:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->VectorRol(i->src2.value, hir::TypeName(i->flags));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_VECTOR_ADD:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
uint32_t arith_flags = i->flags >> 8;
|
|
v->VectorAdd(i->src2.value, hir::TypeName(i->flags & 0xFF),
|
|
!!(arith_flags & ARITHMETIC_UNSIGNED),
|
|
!!(arith_flags & ARITHMETIC_SATURATE));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_VECTOR_SUB:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
uint32_t arith_flags = i->flags >> 8;
|
|
v->VectorSub(i->src2.value, hir::TypeName(i->flags & 0xFF),
|
|
!!(arith_flags & ARITHMETIC_UNSIGNED),
|
|
!!(arith_flags & ARITHMETIC_SATURATE));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
|
|
case OPCODE_VECTOR_AVERAGE:
|
|
if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
uint32_t arith_flags = i->flags >> 8;
|
|
v->VectorAverage(i->src2.value, hir::TypeName(i->flags & 0xFF),
|
|
!!(arith_flags & ARITHMETIC_UNSIGNED),
|
|
!!(arith_flags & ARITHMETIC_SATURATE));
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
case OPCODE_VECTOR_DENORMFLUSH: // this one is okay to constant
|
|
// evaluate, since it is just bit math
|
|
if (i->src1.value->IsConstant()) {
|
|
v->set_from(i->src1.value);
|
|
v->DenormalFlush();
|
|
i->Remove();
|
|
result = true;
|
|
}
|
|
break;
|
|
default:
|
|
// Ignored.
|
|
break;
|
|
}
|
|
}
|
|
|
|
block = block->next;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace passes
|
|
} // namespace compiler
|
|
} // namespace cpu
|
|
} // namespace xe
|