GlobOptSimd128.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. // SIMD_JS
  6. // GlobOpt bits related to SIMD.js
  7. #include "Backend.h"
  8. /*
  9. Handles all Simd128 type-spec of an instr, if possible.
  10. */
  11. bool
  12. GlobOpt::TypeSpecializeSimd128(
  13. IR::Instr *instr,
  14. Value **pSrc1Val,
  15. Value **pSrc2Val,
  16. Value **pDstVal
  17. )
  18. {
  19. if (this->GetIsAsmJSFunc() || SIMD128_TYPE_SPEC_FLAG == false)
  20. {
  21. // no type-spec for ASMJS code or flag is off.
  22. return false;
  23. }
  24. switch (instr->m_opcode)
  25. {
  26. case Js::OpCode::ArgOut_A_InlineBuiltIn:
  27. if (instr->GetSrc1()->IsRegOpnd())
  28. {
  29. StackSym *sym = instr->GetSrc1()->AsRegOpnd()->m_sym;
  30. if (IsSimd128TypeSpecialized(sym, this->currentBlock))
  31. {
  32. ValueType valueType = (*pSrc1Val)->GetValueInfo()->Type();
  33. Assert(valueType.IsSimd128());
  34. ToTypeSpecUse(instr, instr->GetSrc1(), this->currentBlock, *pSrc1Val, nullptr, GetIRTypeFromValueType(valueType), GetBailOutKindFromValueType(valueType));
  35. return true;
  36. }
  37. }
  38. return false;
  39. case Js::OpCode::Ld_A:
  40. if (instr->GetSrc1()->IsRegOpnd())
  41. {
  42. StackSym *sym = instr->GetSrc1()->AsRegOpnd()->m_sym;
  43. IRType type = TyIllegal;
  44. if (IsSimd128F4TypeSpecialized(sym, this->currentBlock))
  45. {
  46. type = TySimd128F4;
  47. }
  48. else if (IsSimd128I4TypeSpecialized(sym, this->currentBlock))
  49. {
  50. type = TySimd128I4;
  51. }
  52. else
  53. {
  54. return false;
  55. }
  56. ToTypeSpecUse(instr, instr->GetSrc1(), this->currentBlock, *pSrc1Val, nullptr, type, IR::BailOutSimd128F4Only /*not used for Ld_A*/);
  57. TypeSpecializeSimd128Dst(type, instr, *pSrc1Val, *pSrc1Val, pDstVal);
  58. return true;
  59. }
  60. return false;
  61. case Js::OpCode::ExtendArg_A:
  62. if (Simd128DoTypeSpec(instr, *pSrc1Val, *pSrc2Val, *pDstVal))
  63. {
  64. Assert(instr->m_opcode == Js::OpCode::ExtendArg_A);
  65. Assert(instr->GetDst()->GetType() == TyVar);
  66. ValueType valueType = instr->GetDst()->GetValueType();
  67. // Type-spec src1 only based on dst type. Dst type is set by the inliner based on func signature.
  68. ToTypeSpecUse(instr, instr->GetSrc1(), this->currentBlock, *pSrc1Val, nullptr, GetIRTypeFromValueType(valueType), GetBailOutKindFromValueType(valueType), true /*lossy*/);
  69. ToVarRegOpnd(instr->GetDst()->AsRegOpnd(), this->currentBlock);
  70. return true;
  71. }
  72. return false;
  73. }
  74. if (!Js::IsSimd128Opcode(instr->m_opcode))
  75. {
  76. return false;
  77. }
  78. // Simd instr
  79. if (Simd128DoTypeSpec(instr, *pSrc1Val, *pSrc2Val, *pDstVal))
  80. {
  81. ThreadContext::SimdFuncSignature simdFuncSignature;
  82. instr->m_func->GetScriptContext()->GetThreadContext()->GetSimdFuncSignatureFromOpcode(instr->m_opcode, simdFuncSignature);
  83. // type-spec logic
  84. // special handling for load/store
  85. // OptArraySrc will type-spec the array and the index. We type-spec the value here.
  86. if (Js::IsSimd128Load(instr->m_opcode))
  87. {
  88. TypeSpecializeSimd128Dst(GetIRTypeFromValueType(simdFuncSignature.returnType), instr, nullptr, *pSrc1Val, pDstVal);
  89. Simd128SetIndirOpndType(instr->GetSrc1()->AsIndirOpnd(), instr->m_opcode);
  90. return true;
  91. }
  92. if (Js::IsSimd128Store(instr->m_opcode))
  93. {
  94. ToTypeSpecUse(instr, instr->GetSrc1(), this->currentBlock, *pSrc1Val, nullptr, GetIRTypeFromValueType(simdFuncSignature.args[2]), GetBailOutKindFromValueType(simdFuncSignature.args[2]));
  95. Simd128SetIndirOpndType(instr->GetDst()->AsIndirOpnd(), instr->m_opcode);
  96. return true;
  97. }
  98. // For op with ExtendArg. All sources are already type-specialized, just type-specialize dst
  99. if (simdFuncSignature.argCount <= 2)
  100. {
  101. Assert(instr->GetSrc1());
  102. ToTypeSpecUse(instr, instr->GetSrc1(), this->currentBlock, *pSrc1Val, nullptr, GetIRTypeFromValueType(simdFuncSignature.args[0]), GetBailOutKindFromValueType(simdFuncSignature.args[0]));
  103. if (instr->GetSrc2())
  104. {
  105. ToTypeSpecUse(instr, instr->GetSrc2(), this->currentBlock, *pSrc2Val, nullptr, GetIRTypeFromValueType(simdFuncSignature.args[1]), GetBailOutKindFromValueType(simdFuncSignature.args[1]));
  106. }
  107. }
  108. if (instr->GetDst())
  109. {
  110. TypeSpecializeSimd128Dst(GetIRTypeFromValueType(simdFuncSignature.returnType), instr, nullptr, *pSrc1Val, pDstVal);
  111. }
  112. return true;
  113. }
  114. else
  115. {
  116. // We didn't type-spec
  117. if (!IsLoopPrePass())
  118. {
  119. // Emit bailout if not loop prepass.
  120. // The inliner inserts bytecodeUses of original args after the instruction. Bailout is safe.
  121. IR::Instr * bailoutInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNoSimdTypeSpec, IR::BailOutNoSimdTypeSpec, instr, instr->m_func);
  122. bailoutInstr->SetByteCodeOffset(instr);
  123. instr->InsertAfter(bailoutInstr);
  124. instr->m_opcode = Js::OpCode::Nop;
  125. if (instr->GetSrc1())
  126. {
  127. instr->FreeSrc1();
  128. if (instr->GetSrc2())
  129. {
  130. instr->FreeSrc2();
  131. }
  132. }
  133. if (instr->GetDst())
  134. {
  135. instr->FreeDst();
  136. }
  137. if (this->byteCodeUses)
  138. {
  139. // All inlined SIMD ops have jitOptimizedReg srcs
  140. Assert(this->byteCodeUses->IsEmpty());
  141. JitAdelete(this->alloc, this->byteCodeUses);
  142. this->byteCodeUses = nullptr;
  143. }
  144. RemoveCodeAfterNoFallthroughInstr(bailoutInstr);
  145. return true;
  146. }
  147. }
  148. return false;
  149. }
  150. bool
  151. GlobOpt::Simd128DoTypeSpec(IR::Instr *instr, const Value *src1Val, const Value *src2Val, const Value *dstVal)
  152. {
  153. bool doTypeSpec = true;
  154. // TODO: Some operations require additional opnd constraints (e.g. shuffle/swizzle).
  155. if (Js::IsSimd128Opcode(instr->m_opcode))
  156. {
  157. ThreadContext::SimdFuncSignature simdFuncSignature;
  158. instr->m_func->GetScriptContext()->GetThreadContext()->GetSimdFuncSignatureFromOpcode(instr->m_opcode, simdFuncSignature);
  159. if (!simdFuncSignature.valid)
  160. {
  161. // not implemented yet.
  162. return false;
  163. }
  164. // special handling for Load/Store
  165. if (Js::IsSimd128Load(instr->m_opcode) || Js::IsSimd128Store(instr->m_opcode))
  166. {
  167. return Simd128DoTypeSpecLoadStore(instr, src1Val, src2Val, dstVal, &simdFuncSignature);
  168. }
  169. const uint argCount = simdFuncSignature.argCount;
  170. switch (argCount)
  171. {
  172. case 2:
  173. Assert(src2Val);
  174. doTypeSpec = doTypeSpec && Simd128CanTypeSpecOpnd(src2Val->GetValueInfo()->Type(), simdFuncSignature.args[1]) && Simd128ValidateIfLaneIndex(instr, instr->GetSrc2(), 1);
  175. // fall-through
  176. case 1:
  177. Assert(src1Val);
  178. doTypeSpec = doTypeSpec && Simd128CanTypeSpecOpnd(src1Val->GetValueInfo()->Type(), simdFuncSignature.args[0]) && Simd128ValidateIfLaneIndex(instr, instr->GetSrc1(), 0);
  179. break;
  180. default:
  181. {
  182. // extended args
  183. Assert(argCount > 2);
  184. // Check if all args have been type specialized.
  185. int arg = argCount - 1;
  186. IR::Instr * eaInstr = GetExtendedArg(instr);
  187. while (arg>=0)
  188. {
  189. Assert(eaInstr);
  190. Assert(eaInstr->m_opcode == Js::OpCode::ExtendArg_A);
  191. ValueType expectedType = simdFuncSignature.args[arg];
  192. IR::Opnd * opnd = eaInstr->GetSrc1();
  193. StackSym * sym = opnd->GetStackSym();
  194. // In Forward Prepass: Check liveness through liveness bits, not IR type, since in prepass no actual type-spec happens.
  195. // In the Forward Pass: Check IRType since Sym can be null, because of const prop.
  196. if (expectedType.IsSimd128Float32x4())
  197. {
  198. if (sym && !IsSimd128F4TypeSpecialized(sym, &currentBlock->globOptData) ||
  199. !sym && opnd->GetType() != TySimd128F4)
  200. {
  201. return false;
  202. }
  203. }
  204. else if (expectedType.IsSimd128Int32x4())
  205. {
  206. if (sym && !IsSimd128I4TypeSpecialized(sym, &currentBlock->globOptData) ||
  207. !sym && opnd->GetType() != TySimd128I4)
  208. {
  209. return false;
  210. }
  211. }
  212. else if (expectedType.IsFloat())
  213. {
  214. if (sym && !IsFloat64TypeSpecialized(sym, &currentBlock->globOptData) ||
  215. !sym&& opnd->GetType() != TyFloat64)
  216. {
  217. return false;
  218. }
  219. }
  220. else if (expectedType.IsInt())
  221. {
  222. if ((sym && !IsInt32TypeSpecialized(sym, &currentBlock->globOptData) && !currentBlock->globOptData.liveLossyInt32Syms->Test(sym->m_id)) ||
  223. !sym && opnd->GetType() != TyInt32)
  224. {
  225. return false;
  226. }
  227. // Extra check if arg is a lane index
  228. if (!Simd128ValidateIfLaneIndex(instr, opnd, arg))
  229. {
  230. return false;
  231. }
  232. }
  233. else
  234. {
  235. Assert(UNREACHED);
  236. }
  237. eaInstr = GetExtendedArg(eaInstr);
  238. arg--;
  239. }
  240. // all args are type-spec'd
  241. doTypeSpec = true;
  242. }
  243. }
  244. }
  245. else
  246. {
  247. Assert(instr->m_opcode == Js::OpCode::ExtendArg_A);
  248. // For ExtendArg, the expected type is encoded in the dst(link) operand.
  249. doTypeSpec = doTypeSpec && Simd128CanTypeSpecOpnd(src1Val->GetValueInfo()->Type(), instr->GetDst()->GetValueType());
  250. }
  251. return doTypeSpec;
  252. }
  253. bool
  254. GlobOpt::Simd128DoTypeSpecLoadStore(IR::Instr *instr, const Value *src1Val, const Value *src2Val, const Value *dstVal, const ThreadContext::SimdFuncSignature *simdFuncSignature)
  255. {
  256. IR::Opnd *baseOpnd = nullptr, *indexOpnd = nullptr, *valueOpnd = nullptr;
  257. IR::Opnd *src, *dst;
  258. bool doTypeSpec = true;
  259. // value = Ld [arr + index]
  260. // [arr + index] = St value
  261. src = instr->GetSrc1();
  262. dst = instr->GetDst();
  263. Assert(dst && src && !instr->GetSrc2());
  264. if (Js::IsSimd128Load(instr->m_opcode))
  265. {
  266. Assert(src->IsIndirOpnd());
  267. baseOpnd = instr->GetSrc1()->AsIndirOpnd()->GetBaseOpnd();
  268. indexOpnd = instr->GetSrc1()->AsIndirOpnd()->GetIndexOpnd();
  269. valueOpnd = instr->GetDst();
  270. }
  271. else if (Js::IsSimd128Store(instr->m_opcode))
  272. {
  273. Assert(dst->IsIndirOpnd());
  274. baseOpnd = instr->GetDst()->AsIndirOpnd()->GetBaseOpnd();
  275. indexOpnd = instr->GetDst()->AsIndirOpnd()->GetIndexOpnd();
  276. valueOpnd = instr->GetSrc1();
  277. // St(arr, index, value). Make sure value can be Simd128 type-spec'd
  278. doTypeSpec = doTypeSpec && Simd128CanTypeSpecOpnd(FindValue(valueOpnd->AsRegOpnd()->m_sym)->GetValueInfo()->Type(), simdFuncSignature->args[2]);
  279. }
  280. else
  281. {
  282. Assert(UNREACHED);
  283. }
  284. // array and index operands should have been type-specialized in OptArraySrc: ValueTypes should be definite at this point. If not, don't type-spec.
  285. // We can be in a loop prepass, where opnd ValueInfo is not set yet. Get the ValueInfo from the Value Table instead.
  286. ValueType baseOpndType = FindValue(baseOpnd->AsRegOpnd()->m_sym)->GetValueInfo()->Type();
  287. if (IsLoopPrePass())
  288. {
  289. doTypeSpec = doTypeSpec && (baseOpndType.IsObject() && baseOpndType.IsTypedArray());
  290. // indexOpnd might be missing if loading from [0]
  291. if (indexOpnd != nullptr)
  292. {
  293. ValueType indexOpndType = FindValue(indexOpnd->AsRegOpnd()->m_sym)->GetValueInfo()->Type();
  294. doTypeSpec = doTypeSpec && indexOpndType.IsLikelyInt();
  295. }
  296. }
  297. else
  298. {
  299. doTypeSpec = doTypeSpec && (baseOpndType.IsObject() && baseOpndType.IsTypedArray());
  300. if (indexOpnd != nullptr)
  301. {
  302. ValueType indexOpndType = FindValue(indexOpnd->AsRegOpnd()->m_sym)->GetValueInfo()->Type();
  303. doTypeSpec = doTypeSpec && indexOpndType.IsInt();
  304. }
  305. }
  306. return doTypeSpec;
  307. }
  308. // We can type spec an opnd if:
  309. // Both profiled/propagated and expected types are not Simd128. e.g. expected type is f64/f32/i32 where there is a conversion logic from the incoming type.
  310. // Opnd type is (Likely) SIMD128 and matches expected type.
  311. // Opnd type is Object. e.g. possibly result of merging different SIMD types. We specialize because we don't know which pass is dynamically taken.
  312. bool GlobOpt::Simd128CanTypeSpecOpnd(const ValueType opndType, ValueType expectedType)
  313. {
  314. if (!opndType.IsSimd128() && !expectedType.IsSimd128())
  315. {
  316. // Non-Simd types can be coerced or we bailout by a FromVar.
  317. return true;
  318. }
  319. // Simd type
  320. if (opndType.HasBeenNull() || opndType.HasBeenUndefined())
  321. {
  322. return false;
  323. }
  324. if (
  325. (opndType.IsLikelyObject() && opndType.ToDefiniteObject() == expectedType) ||
  326. (opndType.IsLikelyObject() && opndType.GetObjectType() == ObjectType::Object)
  327. )
  328. {
  329. return true;
  330. }
  331. return false;
  332. }
  333. /*
  334. Given an instr, opnd and the opnd position. Return true if opnd is a lane index and valid, or not a lane index all-together..
  335. */
  336. bool GlobOpt::Simd128ValidateIfLaneIndex(const IR::Instr * instr, IR::Opnd * opnd, uint argPos)
  337. {
  338. Assert(instr);
  339. Assert(opnd);
  340. uint laneIndex;
  341. uint argPosLo, argPosHi;
  342. uint laneIndexLo, laneIndexHi;
  343. // operation takes a lane index ?
  344. switch (instr->m_opcode)
  345. {
  346. case Js::OpCode::Simd128_Swizzle_F4:
  347. case Js::OpCode::Simd128_Swizzle_I4:
  348. argPosLo = 1; argPosHi = 4;
  349. laneIndexLo = 0; laneIndexHi = 3;
  350. break;
  351. case Js::OpCode::Simd128_Shuffle_F4:
  352. case Js::OpCode::Simd128_Shuffle_I4:
  353. argPosLo = 2; argPosHi = 5;
  354. laneIndexLo = 0; laneIndexHi = 7;
  355. break;
  356. case Js::OpCode::Simd128_ReplaceLane_F4:
  357. case Js::OpCode::Simd128_ReplaceLane_I4:
  358. case Js::OpCode::Simd128_ExtractLane_F4:
  359. case Js::OpCode::Simd128_ExtractLane_I4:
  360. argPosLo = argPosHi = 1;
  361. laneIndexLo = 0; laneIndexHi = 3;
  362. break;
  363. default:
  364. return true; // not a lane index
  365. }
  366. // arg in lane index pos of operation ?
  367. if (argPos < argPosLo || argPos > argPosHi)
  368. {
  369. return true; // not a lane index
  370. }
  371. // It is a lane index ...
  372. // Arg is Int constant (literal or const prop'd) ?
  373. if (!opnd->IsIntConstOpnd())
  374. {
  375. return false;
  376. }
  377. laneIndex = (uint) opnd->AsIntConstOpnd()->GetValue();
  378. // In range ?
  379. if (laneIndex < laneIndexLo|| laneIndex > laneIndexHi)
  380. {
  381. return false;
  382. }
  383. return true;
  384. }
  385. IR::Instr * GlobOpt::GetExtendedArg(IR::Instr *instr)
  386. {
  387. IR::Opnd *src1, *src2;
  388. src1 = instr->GetSrc1();
  389. src2 = instr->GetSrc2();
  390. if (instr->m_opcode == Js::OpCode::ExtendArg_A)
  391. {
  392. if (src2)
  393. {
  394. // mid chain
  395. Assert(src2->GetStackSym()->IsSingleDef());
  396. return src2->GetStackSym()->GetInstrDef();
  397. }
  398. else
  399. {
  400. // end of chain
  401. return nullptr;
  402. }
  403. }
  404. else
  405. {
  406. // start of chain
  407. Assert(Js::IsSimd128Opcode(instr->m_opcode));
  408. Assert(src1);
  409. Assert(src1->GetStackSym()->IsSingleDef());
  410. return src1->GetStackSym()->GetInstrDef();
  411. }
  412. }
  413. IRType GlobOpt::GetIRTypeFromValueType(const ValueType &valueType)
  414. {
  415. if (valueType.IsFloat())
  416. {
  417. return TyFloat64;
  418. }
  419. else if (valueType.IsInt())
  420. {
  421. return TyInt32;
  422. }
  423. else if (valueType.IsSimd128Float32x4())
  424. {
  425. return TySimd128F4;
  426. }
  427. else
  428. {
  429. Assert(valueType.IsSimd128Int32x4());
  430. return TySimd128I4;
  431. }
  432. }
  433. ValueType GlobOpt::GetValueTypeFromIRType(const IRType &type)
  434. {
  435. switch (type)
  436. {
  437. case TyInt32:
  438. return ValueType::GetInt(false);
  439. case TyFloat64:
  440. return ValueType::Float;
  441. case TySimd128F4:
  442. return ValueType::GetSimd128(ObjectType::Simd128Float32x4);
  443. case TySimd128I4:
  444. return ValueType::GetSimd128(ObjectType::Simd128Int32x4);
  445. default:
  446. Assert(UNREACHED);
  447. }
  448. return ValueType::UninitializedObject;
  449. }
  450. IR::BailOutKind GlobOpt::GetBailOutKindFromValueType(const ValueType &valueType)
  451. {
  452. if (valueType.IsFloat())
  453. {
  454. // if required valueType is Float, then allow coercion from any primitive except String.
  455. return IR::BailOutPrimitiveButString;
  456. }
  457. else if (valueType.IsInt())
  458. {
  459. return IR::BailOutIntOnly;
  460. }
  461. else if (valueType.IsSimd128Float32x4())
  462. {
  463. return IR::BailOutSimd128F4Only;
  464. }
  465. else
  466. {
  467. Assert(valueType.IsSimd128Int32x4());
  468. return IR::BailOutSimd128I4Only;
  469. }
  470. }
  471. void
  472. GlobOpt::UpdateBoundCheckHoistInfoForSimd(ArrayUpperBoundCheckHoistInfo &upperHoistInfo, ValueType arrValueType, const IR::Instr *instr)
  473. {
  474. if (!upperHoistInfo.HasAnyInfo())
  475. {
  476. return;
  477. }
  478. int newOffset = GetBoundCheckOffsetForSimd(arrValueType, instr, upperHoistInfo.Offset());
  479. upperHoistInfo.UpdateOffset(newOffset);
  480. }
  481. int
  482. GlobOpt::GetBoundCheckOffsetForSimd(ValueType arrValueType, const IR::Instr *instr, const int oldOffset /* = -1 */)
  483. {
  484. if (!(Js::IsSimd128LoadStore(instr->m_opcode)))
  485. {
  486. return oldOffset;
  487. }
  488. if (!arrValueType.IsTypedArray())
  489. {
  490. // no need to adjust for other array types, we will not type-spec (see Simd128DoTypeSpecLoadStore)
  491. return oldOffset;
  492. }
  493. Assert(instr->dataWidth == 4 || instr->dataWidth == 8 || instr->dataWidth == 12 || instr->dataWidth == 16);
  494. int numOfElems = Lowerer::SimdGetElementCountFromBytes(arrValueType, instr->dataWidth);
  495. // we want to make bound checks more conservative. We compute how many extra elements we need to add to the bound check
  496. // e.g. if original bound check is value <= Length + offset, and dataWidth is 16 bytes on Float32 array, then we need room for 4 elements. The bound check guarantees room for 1 element.
  497. // Hence, we need to ensure 3 more: value <= Length + offset - 3
  498. // We round up since dataWidth may span a partial lane (e.g. dataWidth = 12, bpe = 8 bytes)
  499. int offsetBias = -(numOfElems - 1);
  500. // we should always make an existing bound-check more conservative.
  501. Assert(offsetBias <= 0);
  502. return oldOffset + offsetBias;
  503. }
  504. void
  505. GlobOpt::Simd128SetIndirOpndType(IR::IndirOpnd *indirOpnd, Js::OpCode opcode)
  506. {
  507. switch (opcode)
  508. {
  509. case Js::OpCode::Simd128_LdArr_F4:
  510. case Js::OpCode::Simd128_StArr_F4:
  511. indirOpnd->SetType(TySimd128F4);
  512. indirOpnd->SetValueType(ValueType::GetSimd128(ObjectType::Simd128Float32x4));
  513. break;
  514. case Js::OpCode::Simd128_LdArr_I4:
  515. case Js::OpCode::Simd128_StArr_I4:
  516. indirOpnd->SetType(TySimd128I4);
  517. indirOpnd->SetValueType(ValueType::GetSimd128(ObjectType::Simd128Int32x4));
  518. break;
  519. default:
  520. Assert(UNREACHED);
  521. }
  522. }