From 3ccb2a978d1aa6a91016def89a6f5dd4c33336bb Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Wed, 13 Jan 2016 01:38:56 -0600 Subject: [PATCH] Propagate vector constants. --- .../passes/constant_propagation_pass.cc | 136 +++++- src/xenia/cpu/hir/value.cc | 412 ++++++++++++++++-- src/xenia/cpu/hir/value.h | 23 +- 3 files changed, 526 insertions(+), 45 deletions(-) diff --git a/src/xenia/cpu/compiler/passes/constant_propagation_pass.cc b/src/xenia/cpu/compiler/passes/constant_propagation_pass.cc index c8a5ef632..6f3bc987b 100644 --- a/src/xenia/cpu/compiler/passes/constant_propagation_pass.cc +++ b/src/xenia/cpu/compiler/passes/constant_propagation_pass.cc @@ -161,6 +161,13 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) { i->Remove(); } break; + case OPCODE_ROUND: + if (i->src1.value->IsConstant()) { + v->set_from(i->src1.value); + v->Round(RoundMode(i->flags)); + i->Remove(); + } + break; case OPCODE_ZERO_EXTEND: if (i->src1.value->IsConstant()) { TypeName target_type = v->type; @@ -188,6 +195,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) { case OPCODE_LOAD: if (i->src1.value->IsConstant()) { + assert_false(i->flags & LOAD_STORE_BYTE_SWAP); auto memory = processor_->memory(); auto address = i->src1.value->constant.i32; auto mmio_range = @@ -253,12 +261,23 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) { case OPCODE_SELECT: if (i->src1.value->IsConstant()) { - if (i->src1.value->IsConstantTrue()) { - v->set_from(i->src2.value); + if (i->src1.value->type != VEC128_TYPE) { + if (i->src1.value->IsConstantTrue()) { + v->set_from(i->src2.value); + i->Remove(); + } else if (i->src1.value->IsConstantFalse()) { + v->set_from(i->src3.value); + i->Remove(); + } else if (i->src2.value->IsConstant() && + i->src3.value->IsConstant()) { + // TODO + // v->set_from(i->src2.value); + // v->Select(i->src3.value, i->src1.value); + // i->Remove(); + } } else { - v->set_from(i->src3.value); + // TODO } - i->Remove(); } break; case OPCODE_IS_TRUE: @@ -355,7 +374,7 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) { break; case OPCODE_DID_SATURATE: - assert_true(!i->src1.value->IsConstant()); + // assert_true(!i->src1.value->IsConstant()); break; case OPCODE_ADD: @@ -413,8 +432,33 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) { i->Remove(); } break; - // case OPCODE_MUL_ADD: - // case OPCODE_MUL_SUB + case OPCODE_MUL_ADD: + if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) { + // Multiply part is constant. + if (i->src3.value->IsConstant()) { + v->set_from(i->src1.value); + Value::MulAdd(v, i->src1.value, i->src2.value, i->src3.value); + i->Remove(); + } + } + break; + case OPCODE_MUL_SUB: + if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) { + // Multiply part is constant. + if (i->src3.value->IsConstant()) { + v->set_from(i->src1.value); + Value::MulSub(v, i->src1.value, i->src2.value, i->src3.value); + i->Remove(); + } + } + 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(); + } + break; case OPCODE_NEG: if (i->src1.value->IsConstant()) { v->set_from(i->src1.value); @@ -484,7 +528,6 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) { i->Remove(); } break; - // TODO(benvanik): VECTOR_SHL case OPCODE_SHR: if (i->src1.value->IsConstant() && i->src2.value->IsConstant()) { v->set_from(i->src1.value); @@ -515,13 +558,80 @@ bool ConstantPropagationPass::Run(HIRBuilder* builder) { } break; // TODO(benvanik): INSERT/EXTRACT - // TODO(benvanik): SPLAT/PERMUTE/SWIZZLE - case OPCODE_SPLAT: - if (i->src1.value->IsConstant()) { - // Quite a few of these, from building vec128s. + // TODO(benvanik): PERMUTE/SWIZZLE + 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(); + } + break; + case OPCODE_SPLAT: + if (i->src1.value->IsConstant()) { + v->set_zero(v->type); + v->Splat(i->src1.value); + i->Remove(); + } + 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(); + } + 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(); + } + break; + case OPCODE_VECTOR_CONVERT_F2I: + if (i->src1.value->IsConstant()) { + v->set_zero(VEC128_TYPE); + v->VectorConvertF2I(i->src1.value); + i->Remove(); + } + break; + case OPCODE_VECTOR_CONVERT_I2F: + if (i->src1.value->IsConstant()) { + v->set_zero(VEC128_TYPE); + v->VectorConvertI2F(i->src1.value); + i->Remove(); + } + 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(); + } + 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(); + } + 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(); + } + 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(); } break; - default: // Ignored. break; diff --git a/src/xenia/cpu/hir/value.cc b/src/xenia/cpu/hir/value.cc index 03ef79a2a..4d30de853 100644 --- a/src/xenia/cpu/hir/value.cc +++ b/src/xenia/cpu/hir/value.cc @@ -46,13 +46,13 @@ uint32_t Value::AsUint32() { assert_true(IsConstant()); switch (type) { case INT8_TYPE: - return constant.i8; + return constant.u8; case INT16_TYPE: - return constant.i16; + return constant.u16; case INT32_TYPE: - return constant.i32; + return constant.u32; case INT64_TYPE: - return (uint32_t)constant.i64; + return (uint32_t)constant.u64; default: assert_unhandled_case(type); return 0; @@ -63,13 +63,13 @@ uint64_t Value::AsUint64() { assert_true(IsConstant()); switch (type) { case INT8_TYPE: - return constant.i8; + return constant.u8; case INT16_TYPE: - return constant.i16; + return constant.u16; case INT32_TYPE: - return constant.i32; + return constant.u32; case INT64_TYPE: - return constant.i64; + return constant.u64; default: assert_unhandled_case(type); return 0; @@ -85,15 +85,15 @@ void Value::ZeroExtend(TypeName target_type) { switch (type) { case INT8_TYPE: type = target_type; - constant.i64 = constant.i64 & 0xFF; + constant.u64 = constant.u8; return; case INT16_TYPE: type = target_type; - constant.i64 = constant.i64 & 0xFFFF; + constant.u64 = constant.u16; return; case INT32_TYPE: type = target_type; - constant.i64 = constant.i64 & 0xFFFFFFFF; + constant.u64 = constant.u32; return; default: assert_unhandled_case(type); @@ -210,12 +210,30 @@ void Value::Convert(TypeName target_type, RoundMode round_mode) { assert_unhandled_case(target_type); return; } + case INT64_TYPE: + switch (target_type) { + case FLOAT64_TYPE: + type = target_type; + constant.f64 = (double)constant.i64; + return; + default: + assert_unhandled_case(target_type); + return; + } case FLOAT64_TYPE: switch (target_type) { case FLOAT32_TYPE: type = target_type; constant.f32 = (float)constant.f64; return; + case INT32_TYPE: + type = target_type; + constant.i32 = (int32_t)constant.f64; + return; + case INT64_TYPE: + type = target_type; + constant.i64 = (int64_t)constant.f64; + return; default: assert_unhandled_case(target_type); return; @@ -227,8 +245,28 @@ void Value::Convert(TypeName target_type, RoundMode round_mode) { } void Value::Round(RoundMode round_mode) { - // TODO(benvanik): big matrix. - assert_always(); + switch (type) { + case FLOAT32_TYPE: + switch (round_mode) { + case ROUND_TO_NEAREST: + constant.f32 = std::round(constant.f32); + return; + } + return; + case FLOAT64_TYPE: + return; + case VEC128_TYPE: + for (int i = 0; i < 4; i++) { + switch (round_mode) { + case ROUND_TO_NEAREST: + constant.v128.f32[i] = std::round(constant.v128.f32[i]); + return; + } + } + return; + default: + assert_unhandled_case(type); + } } bool Value::Add(Value* other) { @@ -325,6 +363,11 @@ void Value::Mul(Value* other) { case FLOAT64_TYPE: constant.f64 *= other->constant.f64; break; + case VEC128_TYPE: + for (int i = 0; i < 4; i++) { + constant.v128.f32[i] *= other->constant.v128.f32[i]; + } + break; default: assert_unhandled_case(type); break; @@ -406,6 +449,32 @@ void Value::Div(Value* other, bool is_unsigned) { case FLOAT64_TYPE: constant.f64 /= other->constant.f64; break; + case VEC128_TYPE: + for (int i = 0; i < 4; i++) { + constant.v128.f32[i] /= other->constant.v128.f32[i]; + } + break; + default: + assert_unhandled_case(type); + break; + } +} + +void Value::Max(Value* other) { + assert_true(type == other->type); + switch (type) { + case FLOAT32_TYPE: + constant.f32 = std::max(constant.f32, other->constant.f32); + break; + case FLOAT64_TYPE: + constant.f64 = std::max(constant.f64, other->constant.f64); + break; + case VEC128_TYPE: + for (int i = 0; i < 4; i++) { + constant.v128.f32[i] = + std::max(constant.v128.f32[i], other->constant.v128.f32[i]); + } + break; default: assert_unhandled_case(type); break; @@ -413,13 +482,49 @@ void Value::Div(Value* other, bool is_unsigned) { } void Value::MulAdd(Value* dest, Value* value1, Value* value2, Value* value3) { - // TODO(benvanik): big matrix. - assert_always(); + switch (dest->type) { + case VEC128_TYPE: + for (int i = 0; i < 4; i++) { + dest->constant.v128.f32[i] = + (value1->constant.v128.f32[i] * value2->constant.v128.f32[i]) + + value3->constant.v128.f32[i]; + } + break; + case FLOAT32_TYPE: + dest->constant.f32 = + (value1->constant.f32 * value2->constant.f32) + value3->constant.f32; + break; + case FLOAT64_TYPE: + dest->constant.f64 = + (value1->constant.f64 * value2->constant.f64) + value3->constant.f64; + break; + default: + assert_unhandled_case(dest->type); + break; + } } void Value::MulSub(Value* dest, Value* value1, Value* value2, Value* value3) { - // TODO(benvanik): big matrix. - assert_always(); + switch (dest->type) { + case VEC128_TYPE: + for (int i = 0; i < 4; i++) { + dest->constant.v128.f32[i] = + (value1->constant.v128.f32[i] * value2->constant.v128.f32[i]) - + value3->constant.v128.f32[i]; + } + break; + case FLOAT32_TYPE: + dest->constant.f32 = + (value1->constant.f32 * value2->constant.f32) - value3->constant.f32; + break; + case FLOAT64_TYPE: + dest->constant.f64 = + (value1->constant.f64 * value2->constant.f64) - value3->constant.f64; + break; + default: + assert_unhandled_case(dest->type); + break; + } } void Value::Neg() { @@ -527,6 +632,9 @@ void Value::And(Value* other) { case INT64_TYPE: constant.i64 &= other->constant.i64; break; + case VEC128_TYPE: + constant.v128 &= other->constant.v128; + break; default: assert_unhandled_case(type); break; @@ -548,6 +656,9 @@ void Value::Or(Value* other) { case INT64_TYPE: constant.i64 |= other->constant.i64; break; + case VEC128_TYPE: + constant.v128 |= other->constant.v128; + break; default: assert_unhandled_case(type); break; @@ -569,6 +680,9 @@ void Value::Xor(Value* other) { case INT64_TYPE: constant.i64 ^= other->constant.i64; break; + case VEC128_TYPE: + constant.v128 ^= other->constant.v128; + break; default: assert_unhandled_case(type); break; @@ -603,16 +717,16 @@ void Value::Shl(Value* other) { assert_true(other->type == INT8_TYPE); switch (type) { case INT8_TYPE: - constant.i8 <<= other->constant.i8; + constant.u8 <<= other->constant.u8; break; case INT16_TYPE: - constant.i16 <<= other->constant.i8; + constant.u16 <<= other->constant.u8; break; case INT32_TYPE: - constant.i32 <<= other->constant.i8; + constant.u32 <<= other->constant.u8; break; case INT64_TYPE: - constant.i64 <<= other->constant.i8; + constant.u64 <<= other->constant.u8; break; default: assert_unhandled_case(type); @@ -624,16 +738,16 @@ void Value::Shr(Value* other) { assert_true(other->type == INT8_TYPE); switch (type) { case INT8_TYPE: - constant.i8 = (uint8_t)constant.i8 >> other->constant.i8; + constant.u8 = constant.u8 >> other->constant.u8; break; case INT16_TYPE: - constant.i16 = (uint16_t)constant.i16 >> other->constant.i8; + constant.u16 = constant.u16 >> other->constant.u8; break; case INT32_TYPE: - constant.i32 = (uint32_t)constant.i32 >> other->constant.i8; + constant.u32 = constant.u32 >> other->constant.u8; break; case INT64_TYPE: - constant.i64 = (uint64_t)constant.i64 >> other->constant.i8; + constant.u64 = constant.u64 >> other->constant.u8; break; default: assert_unhandled_case(type); @@ -645,16 +759,16 @@ void Value::Sha(Value* other) { assert_true(other->type == INT8_TYPE); switch (type) { case INT8_TYPE: - constant.i8 = constant.i8 >> other->constant.i8; + constant.i8 = constant.i8 >> other->constant.u8; break; case INT16_TYPE: - constant.i16 = constant.i16 >> other->constant.i8; + constant.i16 = constant.i16 >> other->constant.u8; break; case INT32_TYPE: - constant.i32 = constant.i32 >> other->constant.i8; + constant.i32 = constant.i32 >> other->constant.u8; break; case INT64_TYPE: - constant.i64 = constant.i64 >> other->constant.i8; + constant.i64 = constant.i64 >> other->constant.u8; break; default: assert_unhandled_case(type); @@ -662,6 +776,246 @@ void Value::Sha(Value* other) { } } +void Value::Extract(Value* vec, Value* index) { + assert_true(vec->type == VEC128_TYPE); + switch (type) { + case INT8_TYPE: + constant.u8 = vec->constant.v128.u8[index->constant.u8]; + break; + case INT16_TYPE: + constant.u16 = vec->constant.v128.u16[index->constant.u16]; + break; + case INT32_TYPE: + constant.u32 = vec->constant.v128.u32[index->constant.u32]; + break; + case INT64_TYPE: + constant.u64 = vec->constant.v128.u64[index->constant.u64]; + break; + } +} + +void Value::Select(Value* other, Value* ctrl) { + // TODO + assert_always(); +} + +void Value::Splat(Value* other) { + assert_true(type == VEC128_TYPE); + switch (other->type) { + case INT8_TYPE: + for (int i = 0; i < 16; i++) { + constant.v128.i8[i] = other->constant.i8; + } + break; + case INT16_TYPE: + for (int i = 0; i < 8; i++) { + constant.v128.i16[i] = other->constant.i16; + } + break; + case INT32_TYPE: + case FLOAT32_TYPE: + for (int i = 0; i < 4; i++) { + constant.v128.i32[i] = other->constant.i32; + } + break; + case INT64_TYPE: + case FLOAT64_TYPE: + for (int i = 0; i < 2; i++) { + constant.v128.i64[i] = other->constant.i64; + } + break; + default: + assert_unhandled_case(other->type); + break; + } +} + +void Value::VectorCompareEQ(Value* other, TypeName type) { + assert_true(this->type == VEC128_TYPE && other->type == VEC128_TYPE); + switch (type) { + case INT8_TYPE: + for (int i = 0; i < 16; i++) { + constant.v128.u8[i] = + constant.v128.u8[i] == other->constant.v128.u8[i] ? -1 : 0; + } + break; + case INT16_TYPE: + for (int i = 0; i < 8; i++) { + constant.v128.u16[i] = + constant.v128.u16[i] == other->constant.v128.u16[i] ? -1 : 0; + } + break; + case INT32_TYPE: + case FLOAT32_TYPE: + for (int i = 0; i < 4; i++) { + constant.v128.u32[i] = + constant.v128.u32[i] == other->constant.v128.u32[i] ? -1 : 0; + } + break; + case INT64_TYPE: + case FLOAT64_TYPE: + for (int i = 0; i < 2; i++) { + constant.v128.u64[i] = + constant.v128.u64[i] == other->constant.v128.u64[i] ? -1 : 0; + } + break; + default: + assert_unhandled_case(type); + break; + } +} + +void Value::VectorCompareSGT(Value* other, TypeName type) { + assert_true(this->type == VEC128_TYPE && other->type == VEC128_TYPE); + switch (type) { + case INT8_TYPE: + for (int i = 0; i < 16; i++) { + constant.v128.u8[i] = + constant.v128.i8[i] > other->constant.v128.i8[i] ? -1 : 0; + } + break; + case INT16_TYPE: + for (int i = 0; i < 8; i++) { + constant.v128.u16[i] = + constant.v128.i16[i] > other->constant.v128.i16[i] ? -1 : 0; + } + break; + case INT32_TYPE: + for (int i = 0; i < 4; i++) { + constant.v128.u32[i] = + constant.v128.i32[i] > other->constant.v128.i32[i] ? -1 : 0; + } + break; + case FLOAT32_TYPE: + for (int i = 0; i < 4; i++) { + constant.v128.u32[i] = + constant.v128.f32[i] > other->constant.v128.f32[i] ? -1 : 0; + } + break; + case INT64_TYPE: + for (int i = 0; i < 2; i++) { + constant.v128.u64[i] = + constant.v128.i64[i] > other->constant.v128.i64[i] ? -1 : 0; + } + break; + default: + assert_unhandled_case(type); + break; + } +} + +void Value::VectorConvertI2F(Value* other) { + assert_true(type == VEC128_TYPE); + for (int i = 0; i < 4; i++) { + constant.v128.f32[i] = (float)other->constant.v128.i32[i]; + } +} + +void Value::VectorConvertF2I(Value* other) { + assert_true(type == VEC128_TYPE); + for (int i = 0; i < 4; i++) { + constant.v128.i32[i] = (int32_t)other->constant.v128.f32[i]; + } +} + +void Value::VectorShl(Value* other, TypeName type) { + assert_true(this->type == VEC128_TYPE && other->type == VEC128_TYPE); + switch (type) { + case INT8_TYPE: + for (int i = 0; i < 16; i++) { + constant.v128.u8[i] <<= other->constant.v128.u8[i] & 0x7; + } + break; + case INT16_TYPE: + for (int i = 0; i < 8; i++) { + constant.v128.u16[i] <<= other->constant.v128.u16[i] & 0xF; + } + break; + case INT32_TYPE: + for (int i = 0; i < 4; i++) { + constant.v128.u32[i] <<= other->constant.v128.u32[i] & 0x1F; + } + break; + default: + assert_unhandled_case(type); + break; + } +} + +void Value::VectorShr(Value* other, TypeName type) { + assert_true(this->type == VEC128_TYPE && other->type == VEC128_TYPE); + switch (type) { + case INT8_TYPE: + for (int i = 0; i < 16; i++) { + constant.v128.u8[i] >>= other->constant.v128.u8[i] & 0x7; + } + break; + case INT16_TYPE: + for (int i = 0; i < 8; i++) { + constant.v128.u16[i] >>= other->constant.v128.u16[i] & 0xF; + } + break; + case INT32_TYPE: + for (int i = 0; i < 4; i++) { + constant.v128.u32[i] >>= other->constant.v128.u32[i] & 0x1F; + } + break; + default: + assert_unhandled_case(type); + break; + } +} + +void Value::VectorRol(Value* other, TypeName type) { + assert_true(this->type == VEC128_TYPE && other->type == VEC128_TYPE); + switch (type) { + case INT8_TYPE: + for (int i = 0; i < 16; i++) { + constant.v128.u8[i] = xe::rotate_left(constant.v128.u8[i], + other->constant.v128.i8[i] & 0x7); + } + break; + case INT16_TYPE: + for (int i = 0; i < 8; i++) { + constant.v128.u16[i] = xe::rotate_left( + constant.v128.u16[i], other->constant.v128.u16[i] & 0xF); + } + break; + case INT32_TYPE: + for (int i = 0; i < 4; i++) { + constant.v128.u32[i] = xe::rotate_left( + constant.v128.u32[i], other->constant.v128.u32[i] & 0x1F); + } + break; + default: + assert_unhandled_case(type); + break; + } +} + +void Value::VectorSub(Value* other, TypeName type, bool is_unsigned, + bool saturate) { + assert_true(this->type == VEC128_TYPE && other->type == VEC128_TYPE); + switch (type) { + case INT32_TYPE: + for (int i = 0; i < 4; i++) { + if (is_unsigned) { + if (saturate) { + assert_always(); + } else { + constant.v128.u32[i] -= other->constant.v128.u32[i]; + } + } else { + if (saturate) { + assert_always(); + } else { + constant.v128.i32[i] -= other->constant.v128.i32[i]; + } + } + } + } +} + void Value::ByteSwap() { switch (type) { case INT8_TYPE: diff --git a/src/xenia/cpu/hir/value.h b/src/xenia/cpu/hir/value.h index c078983bb..d797f27d7 100644 --- a/src/xenia/cpu/hir/value.h +++ b/src/xenia/cpu/hir/value.h @@ -77,9 +77,13 @@ class Value { } Use; typedef union { int8_t i8; + uint8_t u8; int16_t i16; + uint16_t u16; int32_t i32; + uint32_t u32; int64_t i64; + uint64_t u64; float f32; double f64; vec128_t v128; @@ -190,6 +194,8 @@ class Value { return !!constant.f32; case FLOAT64_TYPE: return !!constant.f64; + case VEC128_TYPE: + return constant.v128.low || constant.v128.high; default: assert_unhandled_case(type); return false; @@ -199,9 +205,6 @@ class Value { } } bool IsConstantFalse() const { - if (type == VEC128_TYPE) { - assert_always(); - } if (flags & VALUE_IS_CONSTANT) { switch (type) { case INT8_TYPE: @@ -216,6 +219,8 @@ class Value { return !constant.f32; case FLOAT64_TYPE: return !constant.f64; + case VEC128_TYPE: + return !(constant.v128.low || constant.v128.high); default: assert_unhandled_case(type); return false; @@ -475,6 +480,7 @@ class Value { void Mul(Value* other); void MulHi(Value* other, bool is_unsigned); void Div(Value* other, bool is_unsigned); + void Max(Value* other); static void MulAdd(Value* dest, Value* value1, Value* value2, Value* value3); static void MulSub(Value* dest, Value* value1, Value* value2, Value* value3); void Neg(); @@ -488,6 +494,17 @@ class Value { void Shl(Value* other); void Shr(Value* other); void Sha(Value* other); + void Extract(Value* vec, Value* index); + void Select(Value* other, Value* ctrl); + void Splat(Value* other); + void VectorCompareEQ(Value* other, TypeName type); + void VectorCompareSGT(Value* other, TypeName type); + void VectorConvertI2F(Value* other); + void VectorConvertF2I(Value* other); + void VectorShl(Value* other, TypeName type); + void VectorShr(Value* other, TypeName type); + void VectorRol(Value* other, TypeName type); + void VectorSub(Value* other, TypeName type, bool is_unsigned, bool saturate); void ByteSwap(); void CountLeadingZeros(const Value* other); bool Compare(Opcode opcode, Value* other);