[wasm-simd] Implement v128.const on x64, Arm64

- Add wasm opcode, decode and compiler code for v128.const
 - Add codegen implementations for v128.const on x64/Arm64
 - Reuse/Rename some shuffle specific methods to handle generic
 128-bit immediates
 - Tests

Bug: v8:8460
Change-Id: Idc365c8f6402c13259400eac92e0b75bd0f991a1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2276176
Commit-Queue: Deepti Gandluri (OOO Till November) <gdeepti@chromium.org>
Reviewed-by: Sigurd Schneider <sigurds@chromium.org>
Reviewed-by: Zhi An Ng <zhin@chromium.org>
Reviewed-by: Bill Budge <bbudge@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68643}
This commit is contained in:
Deepti Gandluri 2020-07-01 17:19:09 -07:00 committed by Commit Bot
parent 5902846347
commit 871183ea12
24 changed files with 237 additions and 56 deletions

View File

@ -2445,8 +2445,17 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
__ Mov(dst.W(), tmp.V8H(), 0);
break;
}
case kArm64S128Const: {
uint64_t imm1 =
i.InputInt32(0) | (static_cast<uint64_t>(i.InputInt32(1)) << 32);
uint64_t imm2 =
i.InputInt32(2) | (static_cast<uint64_t>(i.InputInt32(3)) << 32);
__ Movi(i.OutputSimd128Register().V16B(), imm2, imm1);
break;
}
case kArm64S128Zero: {
__ Movi(i.OutputSimd128Register().V16B(), 0);
VRegister dst = i.OutputSimd128Register().V16B();
__ Eor(dst, dst, dst);
break;
}
SIMD_BINOP_CASE(kArm64S128And, And, 16B);

View File

@ -338,6 +338,7 @@ namespace compiler {
V(Arm64I8x16RoundingAverageU) \
V(Arm64I8x16Abs) \
V(Arm64I8x16BitMask) \
V(Arm64S128Const) \
V(Arm64S128Zero) \
V(Arm64S128Dup) \
V(Arm64S128And) \

View File

@ -308,6 +308,7 @@ int InstructionScheduler::GetTargetInstructionFlags(
case kArm64I8x16RoundingAverageU:
case kArm64I8x16Abs:
case kArm64I8x16BitMask:
case kArm64S128Const:
case kArm64S128Zero:
case kArm64S128Dup:
case kArm64S128And:

View File

@ -3310,6 +3310,24 @@ void InstructionSelector::VisitInt64AbsWithOverflow(Node* node) {
V(S128Xor, kArm64S128Xor) \
V(S128AndNot, kArm64S128AndNot)
void InstructionSelector::VisitS128Const(Node* node) {
Arm64OperandGenerator g(this);
static const int kUint32Immediates = 4;
uint32_t val[kUint32Immediates];
STATIC_ASSERT(sizeof(val) == kSimd128Size);
memcpy(val, S128ImmediateParameterOf(node->op()).data(), kSimd128Size);
// If all bytes are zeros, avoid emitting code for generic constants
bool all_zeros = !(val[0] && val[1] && val[2] && val[3]);
InstructionOperand dst = g.DefineAsRegister(node);
if (all_zeros) {
Emit(kArm64S128Zero, dst);
} else {
Emit(kArm64S128Const, g.DefineAsRegister(node), g.UseImmediate(val[0]),
g.UseImmediate(val[1]), g.UseImmediate(val[2]),
g.UseImmediate(val[3]));
}
}
void InstructionSelector::VisitS128Zero(Node* node) {
Arm64OperandGenerator g(this);
Emit(kArm64S128Zero, g.DefineAsRegister(node));

View File

@ -2189,6 +2189,8 @@ void InstructionSelector::VisitNode(Node* node) {
return MarkAsSimd128(node), VisitI8x16Abs(node);
case IrOpcode::kI8x16BitMask:
return MarkAsWord32(node), VisitI8x16BitMask(node);
case IrOpcode::kS128Const:
return MarkAsSimd128(node), VisitS128Const(node);
case IrOpcode::kS128Zero:
return MarkAsSimd128(node), VisitS128Zero(node);
case IrOpcode::kS128And:
@ -2710,6 +2712,10 @@ void InstructionSelector::VisitI32x4DotI16x8S(Node* node) { UNIMPLEMENTED(); }
#endif // !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_IA32 && !V8_TARGET_ARCH_ARM64
// && !V8_TARGET_ARCH_ARM
#if !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_ARM64
void InstructionSelector::VisitS128Const(Node* node) { UNIMPLEMENTED(); }
#endif // !V8_TARGET_ARCH_X64 && !V8_TARGET_ARCH_ARM64
void InstructionSelector::VisitFinishRegion(Node* node) { EmitIdentity(node); }
void InstructionSelector::VisitParameter(Node* node) {
@ -3198,7 +3204,7 @@ void InstructionSelector::CanonicalizeShuffle(bool inputs_equal,
void InstructionSelector::CanonicalizeShuffle(Node* node, uint8_t* shuffle,
bool* is_swizzle) {
// Get raw shuffle indices.
memcpy(shuffle, S8x16ShuffleParameterOf(node->op()).data(), kSimd128Size);
memcpy(shuffle, S128ImmediateParameterOf(node->op()).data(), kSimd128Size);
bool needs_swap;
bool inputs_equal = GetVirtualRegister(node->InputAt(0)) ==
GetVirtualRegister(node->InputAt(1));

View File

@ -757,13 +757,13 @@ void AdjustStackPointerForTailCall(TurboAssembler* assembler,
}
}
void SetupShuffleMaskInTempRegister(TurboAssembler* assembler, uint32_t* mask,
XMMRegister tmp) {
uint64_t shuffle_mask = (mask[0]) | (uint64_t{mask[1]} << 32);
assembler->Move(tmp, shuffle_mask);
shuffle_mask = (mask[2]) | (uint64_t{mask[3]} << 32);
assembler->movq(kScratchRegister, shuffle_mask);
assembler->Pinsrq(tmp, kScratchRegister, int8_t{1});
void SetupSimdImmediateInRegister(TurboAssembler* assembler, uint32_t* imms,
XMMRegister reg) {
uint64_t value = (imms[0]) | (uint64_t{imms[1]} << 32);
assembler->Move(reg, value);
value = (imms[2]) | (uint64_t{imms[3]} << 32);
assembler->movq(kScratchRegister, value);
assembler->Pinsrq(reg, kScratchRegister, int8_t{1});
}
} // namespace
@ -3191,11 +3191,27 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
__ Pmaddwd(i.OutputSimd128Register(), i.InputSimd128Register(1));
break;
}
case kX64S128Const: {
// Emit code for generic constants as all zeros, or ones cases will be
// handled separately by the selector.
XMMRegister dst = i.OutputSimd128Register();
uint32_t imm[4] = {};
for (int j = 0; j < 4; j++) {
imm[j] = i.InputUint32(j);
}
SetupSimdImmediateInRegister(tasm(), imm, dst);
break;
}
case kX64S128Zero: {
XMMRegister dst = i.OutputSimd128Register();
__ Xorps(dst, dst);
break;
}
case kX64S128AllOnes: {
XMMRegister dst = i.OutputSimd128Register();
__ Pcmpeqd(dst, dst);
break;
}
case kX64I16x8Splat: {
XMMRegister dst = i.OutputSimd128Register();
if (HasRegisterInput(instr, 0)) {
@ -3728,7 +3744,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
mask[j - 1] = i.InputUint32(j);
}
SetupShuffleMaskInTempRegister(tasm(), mask, tmp_simd);
SetupSimdImmediateInRegister(tasm(), mask, tmp_simd);
__ Pshufb(dst, tmp_simd);
} else { // two input operands
DCHECK_EQ(6, instr->InputCount());
@ -3741,7 +3757,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
mask1[j - 2] |= (lane < kSimd128Size ? lane : 0x80) << k;
}
}
SetupShuffleMaskInTempRegister(tasm(), mask1, tmp_simd);
SetupSimdImmediateInRegister(tasm(), mask1, tmp_simd);
__ Pshufb(kScratchDoubleReg, tmp_simd);
uint32_t mask2[4] = {};
if (instr->InputAt(1)->IsSimd128Register()) {
@ -3757,7 +3773,7 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
mask2[j - 2] |= (lane >= kSimd128Size ? (lane & 0x0F) : 0x80) << k;
}
}
SetupShuffleMaskInTempRegister(tasm(), mask2, tmp_simd);
SetupSimdImmediateInRegister(tasm(), mask2, tmp_simd);
__ Pshufb(dst, tmp_simd);
__ Por(dst, kScratchDoubleReg);
}

View File

@ -316,7 +316,9 @@ namespace compiler {
V(X64I8x16RoundingAverageU) \
V(X64I8x16Abs) \
V(X64I8x16BitMask) \
V(X64S128Const) \
V(X64S128Zero) \
V(X64S128AllOnes) \
V(X64S128Not) \
V(X64S128And) \
V(X64S128Or) \

View File

@ -293,7 +293,9 @@ int InstructionScheduler::GetTargetInstructionFlags(
case kX64S128Xor:
case kX64S128Not:
case kX64S128Select:
case kX64S128Const:
case kX64S128Zero:
case kX64S128AllOnes:
case kX64S128AndNot:
case kX64V64x2AnyTrue:
case kX64V64x2AllTrue:

View File

@ -2809,6 +2809,31 @@ VISIT_ATOMIC_BINOP(Xor)
V(V16x8AllTrue) \
V(V8x16AllTrue)
void InstructionSelector::VisitS128Const(Node* node) {
X64OperandGenerator g(this);
static const int kUint32Immediates = 4;
uint32_t val[kUint32Immediates];
STATIC_ASSERT(sizeof(val) == kSimd128Size);
memcpy(val, S128ImmediateParameterOf(node->op()).data(), kSimd128Size);
// If all bytes are zeros or ones, avoid emitting code for generic constants
bool all_zeros = !(val[0] && val[1] && val[2] && val[3]);
bool all_ones = val[0] == UINT32_MAX && val[1] == UINT32_MAX &&
val[2] == UINT32_MAX && val[3] == UINT32_MAX;
InstructionOperand dst = g.DefineAsRegister(node);
if (all_zeros) {
Emit(kX64S128Zero, dst);
} else if (all_ones) {
Emit(kX64S128AllOnes, dst);
} else {
InstructionOperand inputs[kUint32Immediates];
for (int i = 0; i < kUint32Immediates; ++i) {
inputs[i] = g.UseImmediate(val[i]);
}
InstructionOperand temp(g.TempSimd128Register());
Emit(kX64S128Const, 1, &dst, kUint32Immediates, inputs, 1, &temp);
}
}
void InstructionSelector::VisitS128Zero(Node* node) {
X64OperandGenerator g(this);
Emit(kX64S128Zero, g.DefineAsRegister(node));

View File

@ -1499,21 +1499,21 @@ const Operator* MachineOperatorBuilder::I64x2ReplaceLaneI32Pair(
"Replace lane", 3, 0, 0, 1, 0, 0, lane_index);
}
bool operator==(S8x16ShuffleParameter const& lhs,
S8x16ShuffleParameter const& rhs) {
return (lhs.shuffle() == rhs.shuffle());
bool operator==(S128ImmediateParameter const& lhs,
S128ImmediateParameter const& rhs) {
return (lhs.immediate() == rhs.immediate());
}
bool operator!=(S8x16ShuffleParameter const& lhs,
S8x16ShuffleParameter const& rhs) {
bool operator!=(S128ImmediateParameter const& lhs,
S128ImmediateParameter const& rhs) {
return !(lhs == rhs);
}
size_t hash_value(S8x16ShuffleParameter const& p) {
return base::hash_range(p.shuffle().begin(), p.shuffle().end());
size_t hash_value(S128ImmediateParameter const& p) {
return base::hash_range(p.immediate().begin(), p.immediate().end());
}
std::ostream& operator<<(std::ostream& os, S8x16ShuffleParameter const& p) {
std::ostream& operator<<(std::ostream& os, S128ImmediateParameter const& p) {
for (int i = 0; i < 16; i++) {
const char* separator = (i < 15) ? "," : "";
os << static_cast<uint32_t>(p[i]) << separator;
@ -1521,16 +1521,23 @@ std::ostream& operator<<(std::ostream& os, S8x16ShuffleParameter const& p) {
return os;
}
S8x16ShuffleParameter const& S8x16ShuffleParameterOf(Operator const* op) {
DCHECK_EQ(IrOpcode::kS8x16Shuffle, op->opcode());
return OpParameter<S8x16ShuffleParameter>(op);
S128ImmediateParameter const& S128ImmediateParameterOf(Operator const* op) {
DCHECK(IrOpcode::kS8x16Shuffle == op->opcode() ||
IrOpcode::kS128Const == op->opcode());
return OpParameter<S128ImmediateParameter>(op);
}
const Operator* MachineOperatorBuilder::S128Const(const uint8_t value[16]) {
return new (zone_) Operator1<S128ImmediateParameter>(
IrOpcode::kS128Const, Operator::kPure, "Immediate", 0, 0, 0, 1, 0, 0,
S128ImmediateParameter(value));
}
const Operator* MachineOperatorBuilder::S8x16Shuffle(
const uint8_t shuffle[16]) {
return new (zone_) Operator1<S8x16ShuffleParameter>(
return new (zone_) Operator1<S128ImmediateParameter>(
IrOpcode::kS8x16Shuffle, Operator::kPure, "Shuffle", 2, 0, 0, 1, 0, 0,
S8x16ShuffleParameter(shuffle));
S128ImmediateParameter(shuffle));
}
StackCheckKind StackCheckKindOf(Operator const* op) {

View File

@ -152,30 +152,30 @@ MachineRepresentation AtomicStoreRepresentationOf(Operator const* op)
MachineType AtomicOpType(Operator const* op) V8_WARN_UNUSED_RESULT;
class S8x16ShuffleParameter {
class S128ImmediateParameter {
public:
explicit S8x16ShuffleParameter(const uint8_t shuffle[16]) {
std::copy(shuffle, shuffle + 16, shuffle_.begin());
explicit S128ImmediateParameter(const uint8_t immediate[16]) {
std::copy(immediate, immediate + 16, immediate_.begin());
}
const std::array<uint8_t, 16>& shuffle() const { return shuffle_; }
const uint8_t* data() const { return shuffle_.data(); }
uint8_t operator[](int x) const { return shuffle_[x]; }
const std::array<uint8_t, 16>& immediate() const { return immediate_; }
const uint8_t* data() const { return immediate_.data(); }
uint8_t operator[](int x) const { return immediate_[x]; }
private:
std::array<uint8_t, 16> shuffle_;
std::array<uint8_t, 16> immediate_;
};
V8_EXPORT_PRIVATE bool operator==(S8x16ShuffleParameter const& lhs,
S8x16ShuffleParameter const& rhs);
bool operator!=(S8x16ShuffleParameter const& lhs,
S8x16ShuffleParameter const& rhs);
V8_EXPORT_PRIVATE bool operator==(S128ImmediateParameter const& lhs,
S128ImmediateParameter const& rhs);
bool operator!=(S128ImmediateParameter const& lhs,
S128ImmediateParameter const& rhs);
size_t hash_value(S8x16ShuffleParameter const& p);
size_t hash_value(S128ImmediateParameter const& p);
V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream&,
S8x16ShuffleParameter const&);
S128ImmediateParameter const&);
V8_EXPORT_PRIVATE S8x16ShuffleParameter const& S8x16ShuffleParameterOf(
V8_EXPORT_PRIVATE S128ImmediateParameter const& S128ImmediateParameterOf(
Operator const* op) V8_WARN_UNUSED_RESULT;
StackCheckKind StackCheckKindOf(Operator const* op) V8_WARN_UNUSED_RESULT;
@ -737,6 +737,7 @@ class V8_EXPORT_PRIVATE MachineOperatorBuilder final
const Operator* S128Load();
const Operator* S128Store();
const Operator* S128Const(const uint8_t value[16]);
const Operator* S128Zero();
const Operator* S128And();

View File

@ -939,6 +939,7 @@
V(S128Load) \
V(S128Store) \
V(S128Zero) \
V(S128Const) \
V(S128Not) \
V(S128And) \
V(S128Or) \

View File

@ -1657,7 +1657,7 @@ void SimdScalarLowering::LowerNode(Node* node) {
}
case IrOpcode::kS8x16Shuffle: {
DCHECK_EQ(2, node->InputCount());
S8x16ShuffleParameter shuffle = S8x16ShuffleParameterOf(node->op());
S128ImmediateParameter shuffle = S128ImmediateParameterOf(node->op());
Node** rep_left = GetReplacementsWithType(node->InputAt(0), rep_type);
Node** rep_right = GetReplacementsWithType(node->InputAt(1), rep_type);
Node** rep_node = zone()->NewArray<Node*>(16);

View File

@ -976,6 +976,11 @@ Node* WasmGraphBuilder::Float64Constant(double value) {
return mcgraph()->Float64Constant(value);
}
Node* WasmGraphBuilder::Simd128Constant(const uint8_t value[16]) {
has_simd_ = true;
return graph()->NewNode(mcgraph()->machine()->S128Const(value));
}
namespace {
Node* Branch(MachineGraph* mcgraph, Node* cond, Node** true_node,
Node** false_node, Node* control, BranchHint hint) {

View File

@ -199,6 +199,7 @@ class WasmGraphBuilder {
Node* IntPtrConstant(intptr_t value);
Node* Float32Constant(float value);
Node* Float64Constant(double value);
Node* Simd128Constant(const uint8_t value[16]);
Node* Binop(wasm::WasmOpcode opcode, Node* left, Node* right,
wasm::WasmCodePosition position = wasm::kNoCodePosition);
Node* Unop(wasm::WasmOpcode opcode, Node* input,

View File

@ -2947,8 +2947,13 @@ class LiftoffCompiler {
}
}
void S128Const(FullDecoder* decoder, const Simd128Immediate<validate>& imm,
Value* result) {
unsupported(decoder, kSimd, "simd");
}
void Simd8x16ShuffleOp(FullDecoder* decoder,
const Simd8x16ShuffleImmediate<validate>& imm,
const Simd128Immediate<validate>& imm,
const Value& input0, const Value& input1,
Value* result) {
static constexpr RegClass result_rc = reg_class_for(ValueType::kS128);
@ -2956,7 +2961,7 @@ class LiftoffCompiler {
LiftoffRegister lhs = __ PopToRegister(LiftoffRegList::ForRegs(rhs));
LiftoffRegister dst = __ GetUnusedRegister(result_rc, {lhs, rhs}, {});
__ LiftoffAssembler::emit_s8x16_shuffle(dst, lhs, rhs, imm.shuffle);
__ LiftoffAssembler::emit_s8x16_shuffle(dst, lhs, rhs, imm.value);
__ PushRegister(kWasmS128, dst);
}

View File

@ -649,12 +649,12 @@ struct SimdLaneImmediate {
// Immediate for SIMD S8x16 shuffle operations.
template <Decoder::ValidateFlag validate>
struct Simd8x16ShuffleImmediate {
uint8_t shuffle[kSimd128Size] = {0};
struct Simd128Immediate {
uint8_t value[kSimd128Size] = {0};
inline Simd8x16ShuffleImmediate(Decoder* decoder, const byte* pc) {
inline Simd128Immediate(Decoder* decoder, const byte* pc) {
for (uint32_t i = 0; i < kSimd128Size; ++i) {
shuffle[i] = decoder->read_u8<validate>(pc + i, "shuffle");
value[i] = decoder->read_u8<validate>(pc + i, "value");
}
}
};
@ -921,7 +921,8 @@ struct ControlBase {
F(SimdOp, WasmOpcode opcode, Vector<Value> args, Value* result) \
F(SimdLaneOp, WasmOpcode opcode, const SimdLaneImmediate<validate>& imm, \
const Vector<Value> inputs, Value* result) \
F(Simd8x16ShuffleOp, const Simd8x16ShuffleImmediate<validate>& imm, \
F(S128Const, const Simd128Immediate<validate>& imm, Value* result) \
F(Simd8x16ShuffleOp, const Simd128Immediate<validate>& imm, \
const Value& input0, const Value& input1, Value* result) \
F(Throw, const ExceptionIndexImmediate<validate>& imm, \
const Vector<Value>& args) \
@ -1307,11 +1308,10 @@ class WasmDecoder : public Decoder {
}
}
inline bool Validate(const byte* pc,
Simd8x16ShuffleImmediate<validate>& imm) {
inline bool Validate(const byte* pc, Simd128Immediate<validate>& imm) {
uint8_t max_lane = 0;
for (uint32_t i = 0; i < kSimd128Size; ++i) {
max_lane = std::max(max_lane, imm.shuffle[i]);
max_lane = std::max(max_lane, imm.value[i]);
}
// Shuffle indices must be in [0..31] for a 16 lane shuffle.
if (!VALIDATE(max_lane < 2 * kSimd128Size)) {
@ -1632,6 +1632,7 @@ class WasmDecoder : public Decoder {
return 1 + length + imm.length;
}
// Shuffles require a byte per lane, or 16 immediate bytes.
case kExprS128Const:
case kExprS8x16Shuffle:
return 1 + length + kSimd128Size;
default:
@ -1815,6 +1816,8 @@ class WasmDecoder : public Decoder {
FOREACH_SIMD_1_OPERAND_2_PARAM_OPCODE(DECLARE_OPCODE_CASE)
FOREACH_SIMD_MASK_OPERAND_OPCODE(DECLARE_OPCODE_CASE)
return {2, 1};
FOREACH_SIMD_CONST_OPCODE(DECLARE_OPCODE_CASE)
return {0, 1};
default: {
sig = WasmOpcodes::Signature(opcode);
if (sig) {
@ -3102,6 +3105,13 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return this->ok();
}
uint32_t SimdConstOp(uint32_t opcode_length) {
Simd128Immediate<validate> imm(this, this->pc_ + opcode_length);
auto* result = Push(kWasmS128);
CALL_INTERFACE_IF_REACHABLE(S128Const, imm, result);
return opcode_length + kSimd128Size;
}
uint32_t SimdExtractLane(WasmOpcode opcode, ValueType type,
uint32_t opcode_length) {
SimdLaneImmediate<validate> imm(this, this->pc_ + opcode_length);
@ -3130,7 +3140,7 @@ class WasmFullDecoder : public WasmDecoder<validate> {
}
uint32_t Simd8x16ShuffleOp(uint32_t opcode_length) {
Simd8x16ShuffleImmediate<validate> imm(this, this->pc_ + opcode_length);
Simd128Immediate<validate> imm(this, this->pc_ + opcode_length);
if (this->Validate(this->pc_ + opcode_length, imm)) {
Value input1 = Pop(1, kWasmS128);
Value input0 = Pop(0, kWasmS128);
@ -3211,6 +3221,8 @@ class WasmFullDecoder : public WasmDecoder<validate> {
return DecodeLoadTransformMem(LoadType::kI64Load32U,
LoadTransformationKind::kExtend,
opcode_length);
case kExprS128Const:
return SimdConstOp(opcode_length);
default: {
if (!FLAG_wasm_simd_post_mvp &&
WasmOpcodes::IsSimdPostMvpOpcode(opcode)) {

View File

@ -262,6 +262,11 @@ class WasmGraphBuildingInterface {
result->node = builder_->Float64Constant(value);
}
void S128Const(FullDecoder* decoder, const Simd128Immediate<validate>& imm,
Value* result) {
result->node = builder_->Simd128Constant(imm.value);
}
void RefNull(FullDecoder* decoder, Value* result) {
result->node = builder_->RefNull();
}
@ -502,11 +507,11 @@ class WasmGraphBuildingInterface {
}
void Simd8x16ShuffleOp(FullDecoder* decoder,
const Simd8x16ShuffleImmediate<validate>& imm,
const Simd128Immediate<validate>& imm,
const Value& input0, const Value& input1,
Value* result) {
TFNode* input_nodes[] = {input0.node, input1.node};
result->node = BUILD(Simd8x16ShuffleOp, imm.shuffle, input_nodes);
result->node = BUILD(Simd8x16ShuffleOp, imm.value, input_nodes);
}
void Throw(FullDecoder* decoder, const ExceptionIndexImmediate<validate>& imm,

View File

@ -10,6 +10,7 @@
#include "src/utils/vector.h"
#include "src/wasm/leb-helper.h"
#include "src/wasm/local-decl-encoder.h"
#include "src/wasm/value-type.h"
#include "src/wasm/wasm-module.h"
#include "src/wasm/wasm-opcodes.h"
#include "src/wasm/wasm-result.h"
@ -172,6 +173,7 @@ class V8_EXPORT_PRIVATE WasmFunctionBuilder : public ZoneObject {
void EmitI64Const(int64_t val);
void EmitF32Const(float val);
void EmitF64Const(double val);
void EmitS128Const(Simd128 val);
void EmitWithU8(WasmOpcode opcode, const byte immediate);
void EmitWithU8U8(WasmOpcode opcode, const byte imm1, const byte imm2);
void EmitWithI32V(WasmOpcode opcode, int32_t immediate);

View File

@ -172,6 +172,7 @@ constexpr const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
CASE_SIGN_OP(INT, LoadMem16, "load16")
CASE_SIGN_OP(I64, LoadMem32, "load32")
CASE_S128_OP(LoadMem, "load128")
CASE_S128_OP(Const, "const")
CASE_ALL_OP(StoreMem, "store")
CASE_INT_OP(StoreMem8, "store8")
CASE_INT_OP(StoreMem16, "store16")

View File

@ -283,6 +283,8 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, const WasmFeatures&);
V(S64x2LoadSplat, 0xfd0a, s_i) \
V(S128StoreMem, 0xfd0b, v_is)
#define FOREACH_SIMD_CONST_OPCODE(V) V(S128Const, 0xfd0c, _)
#define FOREACH_SIMD_MASK_OPERAND_OPCODE(V) V(S8x16Shuffle, 0xfd0d, s_ss)
#define FOREACH_SIMD_MVP_0_OPERAND_OPCODE(V) \
@ -639,6 +641,7 @@ bool IsJSCompatibleSignature(const FunctionSig* sig, const WasmFeatures&);
FOREACH_SIMD_1_OPERAND_OPCODE(V) \
FOREACH_SIMD_MASK_OPERAND_OPCODE(V) \
FOREACH_SIMD_MEM_OPCODE(V) \
FOREACH_SIMD_CONST_OPCODE(V) \
FOREACH_ATOMIC_OPCODE(V) \
FOREACH_ATOMIC_0_OPERAND_OPCODE(V) \
FOREACH_NUMERIC_OPCODE(V) \

View File

@ -3677,6 +3677,47 @@ WASM_SIMD_TEST(BitSelect) {
DCHECK_EQ(0x01020304, r.Call(0xFFFFFFFF));
}
void RunSimdConstTest(ExecutionTier execution_tier, LowerSimd lower_simd,
const std::array<uint8_t, kSimd128Size>& expected) {
WasmRunner<uint32_t> r(execution_tier, lower_simd);
byte temp1 = r.AllocateLocal(kWasmS128);
uint8_t* src0 = r.builder().AddGlobal<uint8_t>(kWasmS128);
BUILD(r, WASM_SET_GLOBAL(temp1, WASM_SIMD_CONSTANT(expected)), WASM_ONE);
CHECK_EQ(1, r.Call());
for (size_t i = 0; i < expected.size(); i++) {
CHECK_EQ(ReadLittleEndianValue<uint8_t>(&src0[i]), expected[i]);
}
}
#if V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_ARM64
WASM_SIMD_TEST_NO_LOWERING(S128Const) {
std::array<uint8_t, kSimd128Size> expected;
// Test for generic constant
for (int i = 0; i < kSimd128Size; i++) {
expected[i] = i;
}
RunSimdConstTest(execution_tier, lower_simd, expected);
}
WASM_SIMD_TEST_NO_LOWERING(S128ConstAllZero) {
std::array<uint8_t, kSimd128Size> expected;
// Test for generic constant
for (int i = 0; i < kSimd128Size; i++) {
expected[i] = 0;
}
RunSimdConstTest(execution_tier, lower_simd, expected);
}
WASM_SIMD_TEST_NO_LOWERING(S128ConstAllOnes) {
std::array<uint8_t, kSimd128Size> expected;
// Test for generic constant
for (int i = 0; i < kSimd128Size; i++) {
expected[i] = 0xff;
}
RunSimdConstTest(execution_tier, lower_simd, expected);
}
#endif // V8_TARGET_ARCH_X64 || V8_TARGET_ARCH_ARM64
void RunI8x16MixedRelationalOpTest(ExecutionTier execution_tier,
LowerSimd lower_simd, WasmOpcode opcode,
Int8BinOp expected_op) {

View File

@ -2549,6 +2549,17 @@ class WasmInterpreterInternals {
Push(WasmValue(Simd128(res)));
return true;
}
case kExprS128Const: {
Simd128Immediate<Decoder::kNoValidate> imm(decoder,
code->at(pc + *len));
int16 res;
for (size_t i = 0; i < kSimd128Size; ++i) {
res.val[LANE(i, res)] = imm.value[i];
}
Push(WasmValue(Simd128(res)));
*len += 16;
return true;
}
case kExprS8x16Swizzle: {
int16 v2 = Pop().to_s128().to_i8x16();
int16 v1 = Pop().to_s128().to_i8x16();
@ -2562,14 +2573,14 @@ class WasmInterpreterInternals {
return true;
}
case kExprS8x16Shuffle: {
Simd8x16ShuffleImmediate<Decoder::kNoValidate> imm(decoder,
code->at(pc + *len));
Simd128Immediate<Decoder::kNoValidate> imm(decoder,
code->at(pc + *len));
*len += 16;
int16 v2 = Pop().to_s128().to_i8x16();
int16 v1 = Pop().to_s128().to_i8x16();
int16 res;
for (size_t i = 0; i < kSimd128Size; ++i) {
int lane = imm.shuffle[i];
int lane = imm.value[i];
res.val[LANE(i, v1)] = lane < kSimd128Size
? v1.val[LANE(lane, v1)]
: v2.val[LANE(lane - kSimd128Size, v1)];

View File

@ -792,6 +792,12 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_SIMD_CONCAT_OP(op, bytes, x, y) \
x, y, WASM_SIMD_OP(op), TO_BYTE(bytes)
#define WASM_SIMD_SELECT(format, x, y, z) x, y, z, WASM_SIMD_OP(kExprS128Select)
#define WASM_SIMD_CONSTANT(v) \
WASM_SIMD_OP(kExprS128Const), TO_BYTE(v[0]), TO_BYTE(v[1]), TO_BYTE(v[2]), \
TO_BYTE(v[3]), TO_BYTE(v[4]), TO_BYTE(v[5]), TO_BYTE(v[6]), \
TO_BYTE(v[7]), TO_BYTE(v[8]), TO_BYTE(v[9]), TO_BYTE(v[10]), \
TO_BYTE(v[11]), TO_BYTE(v[12]), TO_BYTE(v[13]), TO_BYTE(v[14]), \
TO_BYTE(v[15])
#define WASM_SIMD_F64x2_SPLAT(x) WASM_SIMD_SPLAT(F64x2, x)
#define WASM_SIMD_F64x2_EXTRACT_LANE(lane, x) \