GlobOptSimd128.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  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 (func->m_workItem->GetFunctionBody()->GetIsAsmjsMode() || 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. sym->GetSimd128F4EquivSym(func);
  48. }
  49. else if (IsSimd128I4TypeSpecialized(sym, this->currentBlock))
  50. {
  51. type = TySimd128I4;
  52. sym->GetSimd128I4EquivSym(func);
  53. }
  54. else
  55. {
  56. return false;
  57. }
  58. ToTypeSpecUse(instr, instr->GetSrc1(), this->currentBlock, *pSrc1Val, nullptr, type, IR::BailOutSimd128F4Only /*not used for Ld_A*/);
  59. TypeSpecializeSimd128Dst(type, instr, *pSrc1Val, *pSrc1Val, pDstVal);
  60. return true;
  61. }
  62. return false;
  63. case Js::OpCode::ExtendArg_A:
  64. if (Simd128DoTypeSpec(instr, *pSrc1Val, *pSrc2Val, *pDstVal))
  65. {
  66. Assert(instr->m_opcode == Js::OpCode::ExtendArg_A);
  67. Assert(instr->GetDst()->GetType() == TyVar);
  68. ValueType valueType = instr->GetDst()->GetValueType();
  69. // Type-spec src1 only based on dst type. Dst type is set by the inliner based on func signature.
  70. ToTypeSpecUse(instr, instr->GetSrc1(), this->currentBlock, *pSrc1Val, nullptr, GetIRTypeFromValueType(valueType), GetBailOutKindFromValueType(valueType), true /*lossy*/);
  71. ToVarRegOpnd(instr->GetDst()->AsRegOpnd(), this->currentBlock);
  72. return true;
  73. }
  74. return false;
  75. }
  76. if (!Js::IsSimd128Opcode(instr->m_opcode))
  77. {
  78. return false;
  79. }
  80. // Simd instr
  81. if (Simd128DoTypeSpec(instr, *pSrc1Val, *pSrc2Val, *pDstVal))
  82. {
  83. ThreadContext::SimdFuncSignature simdFuncSignature;
  84. instr->m_func->GetScriptContext()->GetThreadContext()->GetSimdFuncSignatureFromOpcode(instr->m_opcode, simdFuncSignature);
  85. // type-spec logic
  86. // For op with ExtendArg. All sources are already type-specialized, just type-specialize dst
  87. if (simdFuncSignature.argCount <= 2)
  88. {
  89. Assert(instr->GetSrc1());
  90. ToTypeSpecUse(instr, instr->GetSrc1(), this->currentBlock, *pSrc1Val, nullptr, GetIRTypeFromValueType(simdFuncSignature.args[0]), GetBailOutKindFromValueType(simdFuncSignature.args[0]));
  91. if (instr->GetSrc2())
  92. {
  93. ToTypeSpecUse(instr, instr->GetSrc2(), this->currentBlock, *pSrc2Val, nullptr, GetIRTypeFromValueType(simdFuncSignature.args[1]), GetBailOutKindFromValueType(simdFuncSignature.args[1]));
  94. }
  95. }
  96. if (instr->GetDst())
  97. {
  98. TypeSpecializeSimd128Dst(GetIRTypeFromValueType(simdFuncSignature.returnType), instr, nullptr, *pSrc1Val, pDstVal);
  99. }
  100. return true;
  101. }
  102. else
  103. {
  104. // We didn't type-spec
  105. if (!IsLoopPrePass())
  106. {
  107. // Emit bailout if not loop prepass.
  108. // The inliner inserts bytecodeUses of original args after the instruction. Bailout is safe.
  109. IR::Instr * bailoutInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNoSimdTypeSpec, IR::BailOutNoSimdTypeSpec, instr, this->func);
  110. bailoutInstr->SetByteCodeOffset(instr);
  111. instr->InsertAfter(bailoutInstr);
  112. instr->m_opcode = Js::OpCode::Nop;
  113. if (instr->GetSrc1())
  114. {
  115. instr->FreeSrc1();
  116. if (instr->GetSrc2())
  117. {
  118. instr->FreeSrc2();
  119. }
  120. }
  121. if (instr->GetDst())
  122. {
  123. instr->FreeDst();
  124. }
  125. if (this->byteCodeUses)
  126. {
  127. // All inlined SIMD ops have jitOptimizedReg srcs
  128. Assert(this->byteCodeUses->IsEmpty());
  129. JitAdelete(this->alloc, this->byteCodeUses);
  130. this->byteCodeUses = nullptr;
  131. }
  132. RemoveCodeAfterNoFallthroughInstr(bailoutInstr);
  133. return true;
  134. }
  135. }
  136. return false;
  137. }
  138. bool
  139. GlobOpt::Simd128DoTypeSpec(IR::Instr *instr, const Value *src1Val, const Value *src2Val, const Value *dstVal)
  140. {
  141. bool doTypeSpec = true;
  142. // TODO: Some operations require additional opnd constraints (e.g. shuffle/swizzle).
  143. if (Js::IsSimd128Opcode(instr->m_opcode))
  144. {
  145. ThreadContext::SimdFuncSignature simdFuncSignature;
  146. instr->m_func->GetScriptContext()->GetThreadContext()->GetSimdFuncSignatureFromOpcode(instr->m_opcode, simdFuncSignature);
  147. if (!simdFuncSignature.valid)
  148. {
  149. // not implemented yet.
  150. return false;
  151. }
  152. const uint argCount = simdFuncSignature.argCount;
  153. switch (argCount)
  154. {
  155. case 2:
  156. Assert(src2Val);
  157. doTypeSpec = doTypeSpec && Simd128CanTypeSpecOpnd(src2Val->GetValueInfo()->Type(), simdFuncSignature.args[1]);
  158. // fall-through
  159. case 1:
  160. Assert(src1Val);
  161. doTypeSpec = doTypeSpec && Simd128CanTypeSpecOpnd(src1Val->GetValueInfo()->Type(), simdFuncSignature.args[0]);
  162. break;
  163. default:
  164. {
  165. // extended args
  166. Assert(argCount > 2);
  167. // Check if all args have been type specialized.
  168. int arg = argCount - 1;
  169. IR::Instr * eaInstr = GetExtendedArg(instr);
  170. while (arg>=0)
  171. {
  172. Assert(eaInstr);
  173. Assert(eaInstr->m_opcode == Js::OpCode::ExtendArg_A);
  174. ValueType expectedType = simdFuncSignature.args[arg];
  175. IR::Opnd * opnd = eaInstr->GetSrc1();
  176. StackSym * sym = opnd->GetStackSym();
  177. // In Forward Prepass: Check liveness through liveness bits, not IR type, since in prepass no actual type-spec happens.
  178. // In the Forward Pass: Check IRType since Sym can be null, because of const prop.
  179. if (expectedType.IsSimd128Float32x4())
  180. {
  181. if (sym && !IsSimd128F4TypeSpecialized(sym, &currentBlock->globOptData) ||
  182. !sym && opnd->GetType() != TySimd128F4)
  183. {
  184. return false;
  185. }
  186. }
  187. else if (expectedType.IsSimd128Int32x4())
  188. {
  189. if (sym && !IsSimd128I4TypeSpecialized(sym, &currentBlock->globOptData) ||
  190. !sym && opnd->GetType() != TySimd128I4)
  191. {
  192. return false;
  193. }
  194. }
  195. else if (expectedType.IsFloat())
  196. {
  197. if (sym && !IsFloat64TypeSpecialized(sym, &currentBlock->globOptData) ||
  198. !sym&& opnd->GetType() != TyFloat64)
  199. {
  200. return false;
  201. }
  202. }
  203. else if (expectedType.IsInt())
  204. {
  205. if ((sym && !IsInt32TypeSpecialized(sym, &currentBlock->globOptData) && !currentBlock->globOptData.liveLossyInt32Syms->Test(sym->m_id)) ||
  206. !sym && opnd->GetType() != TyInt32)
  207. {
  208. return false;
  209. }
  210. }
  211. else
  212. {
  213. Assert(UNREACHED);
  214. }
  215. eaInstr = GetExtendedArg(instr);
  216. arg--;
  217. }
  218. // all args are type-spec'd
  219. doTypeSpec = true;
  220. }
  221. }
  222. }
  223. else
  224. {
  225. Assert(instr->m_opcode == Js::OpCode::ExtendArg_A);
  226. // For ExtendArg, the expected type is encoded in the dst(link) operand.
  227. doTypeSpec = doTypeSpec && Simd128CanTypeSpecOpnd(src1Val->GetValueInfo()->Type(), instr->GetDst()->GetValueType());
  228. }
  229. return doTypeSpec;
  230. }
  231. // We can type spec an opnd if:
  232. // 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.
  233. // Opnd type is (Likely) SIMD128 and matches expected type.
  234. // Opnd type is Object. e.g. possibly result of merging different SIMD types.
  235. // Simd128 values merged with Undefined/Null are still specialized.
  236. // Opnd type is LikelyUndefined: we don't have profile info for the operands.
  237. bool GlobOpt::Simd128CanTypeSpecOpnd(const ValueType opndType, ValueType expectedType)
  238. {
  239. if (!opndType.IsSimd128() && !expectedType.IsSimd128())
  240. {
  241. // Non-Simd types can be coerced or we bailout by a FromVar.
  242. return true;
  243. }
  244. // Simd type
  245. if (opndType.HasBeenNull() || opndType.HasBeenUndefined())
  246. {
  247. return false;
  248. }
  249. if (
  250. (opndType.IsLikelyObject() && opndType.ToDefiniteObject() == expectedType) ||
  251. (opndType.IsLikelyObject() && opndType.GetObjectType() == ObjectType::Object)
  252. )
  253. {
  254. return true;
  255. }
  256. return false;
  257. }
  258. IR::Instr * GlobOpt::GetExtendedArg(IR::Instr *instr)
  259. {
  260. IR::Opnd *src1, *src2;
  261. src1 = instr->GetSrc1();
  262. src2 = instr->GetSrc2();
  263. if (instr->m_opcode == Js::OpCode::ExtendArg_A)
  264. {
  265. if (src2)
  266. {
  267. // mid chain
  268. Assert(src2->GetStackSym()->IsSingleDef());
  269. return src2->GetStackSym()->GetInstrDef();
  270. }
  271. else
  272. {
  273. // end of chain
  274. return nullptr;
  275. }
  276. }
  277. else
  278. {
  279. // start of chain
  280. Assert(Js::IsSimd128Opcode(instr->m_opcode));
  281. Assert(src1);
  282. Assert(src1->GetStackSym()->IsSingleDef());
  283. return src1->GetStackSym()->GetInstrDef();
  284. }
  285. }
  286. IRType GlobOpt::GetIRTypeFromValueType(const ValueType &valueType)
  287. {
  288. if (valueType.IsFloat())
  289. {
  290. return TyFloat64;
  291. }
  292. else if (valueType.IsInt())
  293. {
  294. return TyInt32;
  295. }
  296. else if (valueType.IsSimd128Float32x4())
  297. {
  298. return TySimd128F4;
  299. }
  300. else
  301. {
  302. Assert(valueType.IsSimd128Int32x4());
  303. return TySimd128I4;
  304. }
  305. }
  306. ValueType GlobOpt::GetValueTypeFromIRType(const IRType &type)
  307. {
  308. switch (type)
  309. {
  310. case TyInt32:
  311. return ValueType::GetInt(false);
  312. case TyFloat64:
  313. return ValueType::Float;
  314. case TySimd128F4:
  315. return ValueType::GetSimd128(ObjectType::Simd128Float32x4);
  316. case TySimd128I4:
  317. return ValueType::GetSimd128(ObjectType::Simd128Int32x4);
  318. default:
  319. Assert(UNREACHED);
  320. }
  321. return ValueType::UninitializedObject;
  322. }
  323. IR::BailOutKind GlobOpt::GetBailOutKindFromValueType(const ValueType &valueType)
  324. {
  325. if (valueType.IsFloat())
  326. {
  327. // if required valueType is Float, then allow coercion from any primitive except String.
  328. return IR::BailOutPrimitiveButString;
  329. }
  330. else if (valueType.IsInt())
  331. {
  332. return IR::BailOutIntOnly;
  333. }
  334. else if (valueType.IsSimd128Float32x4())
  335. {
  336. return IR::BailOutSimd128F4Only;
  337. }
  338. else
  339. {
  340. Assert(valueType.IsSimd128Int32x4());
  341. return IR::BailOutSimd128I4Only;
  342. }
  343. }