Files
Xenia-Canary/src/xenia/cpu/compiler/passes/constant_propagation_pass.cc
chss95cs@gmail.com 20638c2e61 use Sleep(0) instead of SwitchToThread, should waste less power and help the os with scheduling.
PM4 buffer handling made a virtual member of commandprocessor, place the implementation/declaration into reusable macro files. this is probably the biggest boost here.
Optimized SET_CONSTANT/ LOAD_CONSTANT pm4 ops based on the register range they start writing at, this was also a nice boost

Expose X64 extension flags to code outside of x64 backend, so we can detect and use things like avx512, xop, avx2, etc in normal code
Add freelists for HIR structures to try to reduce the number of last level cache misses during optimization (currently disabled... fixme later)

Analyzed PGO feedback and reordered branches, uninlined functions, moved code out into different functions based on info from it in the PM4 functions, this gave like a 2% boost at best.

Added support for the db16cyc opcode, which is used often in xb360 spinlocks. before it was just being translated to nop, now on x64 we translate it to _mm_pause but may change that in the future to reduce cpu time wasted

texture util - all our divisors were powers of 2, instead we look up a shift. this made texture scaling slightly faster, more so on intel processors which seem to be worse at int divs. GetGuestTextureLayout is now a little faster, although it is still one of the heaviest functions in the emulator when scaling is on.

xe_unlikely_mutex was not a good choice for the guest clock lock, (running theory) on intel processors another thread may take a significant time to update the clock? maybe because of the uint64 division? really not sure, but switched it to xe_mutex. This fixed audio stutter that i had introduced to 1 or 2 games, fixed performance on that n64 rare game with the monkeys.
Took another crack at DMA implementation, another failure.
Instead of passing as a parameter, keep the ringbuffer reader as the first member of commandprocessor so it can be accessed through this
Added macro for noalias
Applied noalias to Memory::LookupHeap. This reduced the size of the executable by 7 kb.
Reworked kernel shim template, this shaved like 100kb off the exe and eliminated the indirect calls from the shim to the actual implementation. We still unconditionally generate string representations of kernel calls though :(, unless it is kHighFrequency

Add nvapi extensions support, currently unused. Will use CPUVISIBLE memory at some point
Inserted prefetches in a few places based on feedback from vtune.
Add native implementation of SHA int8 if all elements are the same

Vectorized comparisons for SetViewport, SetScissorRect
Vectorized ranged comparisons for WriteRegister
Add XE_MSVC_ASSUME
Move FormatInfo::name out of the structure, instead look up the name in a different table. Debug related data and critical runtime data are best kept apart
Templated UpdateSystemConstantValues based on ROV/RTV and primitive_polygonal
Add ArchFloatMask functions, these are for storing the results of floating point comparisons without doing costly float->int pipeline transfers (vucomiss/setb)
Use floatmasks in UpdateSystemConstantValues for checking if dirty, only transfer to int at end of function.
Instead of dirty |= (x == y) in UpdateSystemConstantValues, now we do dirty_u32 |= (x^y). if any of them are not equal, dirty_u32 will be nz, else if theyre all equal it will be zero. This is more friendly to register renaming and the lack of dependencies on EFLAGS lets the compiler reorder better
Add PrefetchSamplerParameters to D3D12TextureCache
use PrefetchSamplerParameters in UpdateBindings to eliminate cache misses that vtune detected

Add PrefetchTextureBinding to D3D12TextureCache
Prefetch texture bindings to get rid of more misses vtune detected (more accesses out of order with random strides)
Rewrote DMAC, still terrible though and have disabled it for now.
Replace tiny memcmp of 6 U64 in render_target_cache with inline loop, msvc fails to make it a loop and instead does a thunk to their memcmp function, which is optimized for larger sizes

PrefetchTextureBinding in AreActiveTextureSRVKeysUpToDate
Replace memcmp calls for pipelinedescription with handwritten cmp
Directly write some registers that dont have special handling in PM4 functions
Changed EstimateMaxY to try to eliminate mispredictions that vtune was reporting, msvc ended up turning the changed code into a series of blends

in ExecutePacketType3_EVENT_WRITE_EXT, instead of writing extents to an array on the stack and then doing xe_copy_and_swap_16 of the data to its dest, pre-swap each constant and then store those. msvc manages to unroll that into wider stores
stop logging XE_SWAP every time we receive XE_SWAP, stop logging the start and end of each viz query

Prefetch watch nodes in FireWatches based on feedback from vtune
Removed dead code from texture_info.cc
NOINLINE on GpuSwap, PGO builds did it so we should too.
2022-09-11 14:14:48 -07:00

950 lines
33 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->UnlinkAndNOP();
}
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->UnlinkAndNOP();
}
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->UnlinkAndNOP();
}
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->UnlinkAndNOP();
}
result = true;
} else if (i->src2.value->IsConstant()) { // chrispy: fix h3 bug from
// const indirect call true
auto function = processor_->LookupFunction(
uint32_t(i->src2.value->constant.i32));
if (!function) {
break;
}
// i->Replace(&OPCODE_CALL_TRUE_info, i->flags);
i->opcode = &OPCODE_CALL_TRUE_info;
i->set_src2(nullptr);
i->src2.symbol = function;
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->UnlinkAndNOP();
}
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->UnlinkAndNOP();
}
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->UnlinkAndNOP();
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->UnlinkAndNOP();
result = true;
}
break;
case OPCODE_ROUND:
if (i->src1.value->IsConstant()) {
v->set_from(i->src1.value);
v->Round(RoundMode(i->flags));
i->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
result = true;
}
break;
case OPCODE_LVR:
if (i->src1.value->IsConstant()) {
if (!(i->src1.value->AsUint32() & 0xF)) {
v->set_zero(VEC128_TYPE);
i->UnlinkAndNOP();
result = true;
break;
}
}
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->UnlinkAndNOP();
result = true;
break;
case INT16_TYPE:
v->set_constant(xe::load<uint16_t>(host_addr));
i->UnlinkAndNOP();
result = true;
break;
case INT32_TYPE:
v->set_constant(xe::load<uint32_t>(host_addr));
i->UnlinkAndNOP();
result = true;
break;
case INT64_TYPE:
v->set_constant(xe::load<uint64_t>(host_addr));
i->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
} 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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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()) {
if (should_skip_because_of_float) {
break;
}
v->set_from(i->src1.value);
v->Max(i->src2.value);
i->UnlinkAndNOP();
result = true;
}
break;
case OPCODE_NEG:
if (i->src1.value->IsConstant()) {
v->set_from(i->src1.value);
v->Neg();
i->UnlinkAndNOP();
result = true;
}
break;
case OPCODE_ABS:
if (i->src1.value->IsConstant()) {
v->set_from(i->src1.value);
v->Abs();
i->UnlinkAndNOP();
result = true;
}
break;
case OPCODE_SQRT:
if (i->src1.value->IsConstant()) {
v->set_from(i->src1.value);
v->Sqrt();
i->UnlinkAndNOP();
result = true;
}
break;
case OPCODE_RSQRT:
if (i->src1.value->IsConstant()) {
v->set_from(i->src1.value);
v->RSqrt();
i->UnlinkAndNOP();
result = true;
}
break;
case OPCODE_RECIP:
if (i->src1.value->IsConstant()) {
v->set_from(i->src1.value);
v->Recip();
i->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
result = true;
} else if (!i->src1.value->IsConstant() &&
!i->src2.value->IsConstant() &&
i->src1.value == i->src2.value) {
v->set_zero(v->type);
i->UnlinkAndNOP();
result = true;
}
break;
case OPCODE_NOT:
if (i->src1.value->IsConstant()) {
v->set_from(i->src1.value);
v->Not();
i->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
result = true;
}
break;
case OPCODE_BYTE_SWAP:
if (i->src1.value->IsConstant()) {
v->set_from(i->src1.value);
v->ByteSwap();
i->UnlinkAndNOP();
result = true;
}
break;
case OPCODE_CNTLZ:
if (i->src1.value->IsConstant()) {
v->set_zero(v->type);
v->CountLeadingZeros(i->src1.value);
i->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
result = true;
}
break;
case OPCODE_SPLAT:
if (i->src1.value->IsConstant()) {
v->set_zero(v->type);
v->Splat(i->src1.value);
i->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
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->UnlinkAndNOP();
result = true;
}
break;
default:
// Ignored.
break;
}
}
block = block->next;
}
return true;
}
} // namespace passes
} // namespace compiler
} // namespace cpu
} // namespace xe