WasmSignature.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft Corporation and contributors. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "WasmReaderPch.h"
  6. #ifdef ENABLE_WASM
  7. namespace Wasm
  8. {
  9. WasmSignature::WasmSignature() :
  10. m_id(Js::Constants::UninitializedValue),
  11. m_paramSize(Js::Constants::InvalidArgSlot),
  12. m_params(nullptr),
  13. m_paramsCount(0),
  14. m_shortSig(Js::Constants::InvalidSignature)
  15. {
  16. }
  17. void WasmSignature::AllocateParams(Js::ArgSlot count, Recycler * recycler)
  18. {
  19. if (GetParamCount() != 0)
  20. {
  21. throw WasmCompilationException(_u("Invalid call to WasmSignature::AllocateParams: Params are already allocated"));
  22. }
  23. if (count > 0)
  24. {
  25. m_params = RecyclerNewArrayLeafZ(recycler, Local, count);
  26. }
  27. m_paramsCount = count;
  28. }
  29. void WasmSignature::SetParam(WasmTypes::WasmType type, Js::ArgSlot index)
  30. {
  31. if (index >= GetParamCount())
  32. {
  33. throw WasmCompilationException(_u("Parameter %u out of range (max %u)"), index, GetParamCount());
  34. }
  35. m_params[index] = Local(type);
  36. }
  37. void WasmSignature::AllocateResults(uint32 count, Recycler * recycler)
  38. {
  39. if (GetResultCount() != 0)
  40. {
  41. throw WasmCompilationException(_u("Invalid call to WasmSignature::AllocateResults: Results are already allocated"));
  42. }
  43. if (count > 0)
  44. {
  45. m_results = RecyclerNewArrayLeafZ(recycler, WasmTypes::WasmType, count);
  46. }
  47. m_resultsCount = count;
  48. }
  49. void WasmSignature::SetResult(Local type, uint32 index)
  50. {
  51. if (index >= GetResultCount())
  52. {
  53. throw WasmCompilationException(_u("Result %u out of range (max %u)"), index, GetResultCount());
  54. }
  55. m_results[index] = type;
  56. }
  57. Wasm::Local WasmSignature::GetResult(uint32 index) const
  58. {
  59. if (index >= GetResultCount())
  60. {
  61. throw WasmCompilationException(_u("Result %u out of range (max %u)"), index, GetResultCount());
  62. }
  63. return m_results[index];
  64. }
  65. void WasmSignature::SetSignatureId(uint32 id)
  66. {
  67. Assert(m_id == Js::Constants::UninitializedValue);
  68. m_id = id;
  69. }
  70. Local WasmSignature::GetParam(Js::ArgSlot index) const
  71. {
  72. if (index >= GetParamCount())
  73. {
  74. throw WasmCompilationException(_u("Parameter %u out of range (max %u)"), index, GetParamCount());
  75. }
  76. return m_params[index];
  77. }
  78. Js::ArgSlot WasmSignature::GetParamCount() const
  79. {
  80. return m_paramsCount;
  81. }
  82. uint32 WasmSignature::GetSignatureId() const
  83. {
  84. return m_id;
  85. }
  86. size_t WasmSignature::GetShortSig() const
  87. {
  88. return m_shortSig;
  89. }
  90. template<bool useShortSig>
  91. bool WasmSignature::IsEquivalent(const WasmSignature* sig) const
  92. {
  93. if (useShortSig && m_shortSig != Js::Constants::InvalidSignature)
  94. {
  95. bool isEquivalent = sig->GetShortSig() == m_shortSig;
  96. Assert(this->IsEquivalent<false>(sig) == isEquivalent);
  97. return isEquivalent;
  98. }
  99. if (GetResultCount() == sig->GetResultCount() &&
  100. GetParamCount() == sig->GetParamCount() &&
  101. GetParamsSize() == sig->GetParamsSize())
  102. {
  103. return (
  104. (GetParamCount() == 0 || memcmp(m_params, sig->m_params, GetParamCount() * sizeof(Local)) == 0) &&
  105. (GetResultCount() == 0 || memcmp(m_results, sig->m_results, GetResultCount() * sizeof(Local)) == 0)
  106. );
  107. }
  108. return false;
  109. }
  110. template bool WasmSignature::IsEquivalent<true>(const WasmSignature*) const;
  111. template bool WasmSignature::IsEquivalent<false>(const WasmSignature*) const;
  112. Js::ArgSlot WasmSignature::GetParamSize(Js::ArgSlot index) const
  113. {
  114. switch (GetParam(index))
  115. {
  116. case WasmTypes::F32:
  117. case WasmTypes::I32:
  118. CompileAssert(sizeof(float) == sizeof(int32));
  119. #ifdef _M_X64
  120. // on x64, we always alloc (at least) 8 bytes per arguments
  121. return sizeof(void*);
  122. #elif _M_IX86
  123. return sizeof(int32);
  124. #else
  125. Assert(UNREACHED);
  126. #endif
  127. break;
  128. case WasmTypes::F64:
  129. case WasmTypes::I64:
  130. CompileAssert(sizeof(double) == sizeof(int64));
  131. return sizeof(int64);
  132. break;
  133. #ifdef ENABLE_WASM_SIMD
  134. case WasmTypes::V128:
  135. Wasm::Simd::EnsureSimdIsEnabled();
  136. CompileAssert(sizeof(Simd::simdvec) == 16);
  137. return sizeof(Simd::simdvec);
  138. break;
  139. #endif
  140. default:
  141. WasmTypes::CompileAssertCasesNoFailFast<WasmTypes::I32, WasmTypes::I64, WasmTypes::F32, WasmTypes::F64, WASM_V128_CHECK_TYPE>();
  142. throw WasmCompilationException(_u("Invalid param type"));
  143. }
  144. }
  145. void WasmSignature::FinalizeSignature()
  146. {
  147. if (GetResultCount() > 1)
  148. {
  149. // Do not support short signature with multiple returns at this time
  150. return;
  151. }
  152. Local resultType = GetResultCount() == 1 ? GetResult(0) : WasmTypes::Void;
  153. Assert(m_paramSize == Js::Constants::InvalidArgSlot);
  154. Assert(m_shortSig == Js::Constants::InvalidSignature);
  155. const Js::ArgSlot paramCount = GetParamCount();
  156. m_paramSize = 0;
  157. for (Js::ArgSlot i = 0; i < paramCount; ++i)
  158. {
  159. if (ArgSlotMath::Add(m_paramSize, GetParamSize(i), &m_paramSize))
  160. {
  161. throw WasmCompilationException(_u("Argument size too big"));
  162. }
  163. }
  164. // 3 bits for result type, 3 for each arg
  165. const uint32 nBitsForResult = 3;
  166. #ifdef ENABLE_WASM_SIMD
  167. const uint32 nBitsForArgs = 3;
  168. #else
  169. // We can drop 1 bit by excluding void
  170. const uint32 nBitsForArgs = 2;
  171. #endif
  172. CompileAssert(Local::Void == 0);
  173. // Make sure we can encode all types (including void) with the number of bits reserved
  174. CompileAssert(Local::Limit <= (1 << nBitsForResult));
  175. // Make sure we can encode all types (excluding void) with the number of bits reserved
  176. CompileAssert(Local::Limit - 1 <= (1 << nBitsForArgs));
  177. ::Math::RecordOverflowPolicy sigOverflow;
  178. const uint32 bitsRequiredForSig = UInt32Math::MulAdd<nBitsForArgs, nBitsForResult>((uint32)paramCount, sigOverflow);
  179. if (sigOverflow.HasOverflowed())
  180. {
  181. return;
  182. }
  183. // we don't need to reserve a sentinel bit because there is no result type with value of 7
  184. CompileAssert(Local::Limit <= 0b111);
  185. const uint32 nAvailableBits = sizeof(m_shortSig) * 8;
  186. if (bitsRequiredForSig <= nAvailableBits)
  187. {
  188. // Append the result type to the signature
  189. m_shortSig = (m_shortSig << nBitsForResult) | resultType;
  190. for (Js::ArgSlot i = 0; i < paramCount; ++i)
  191. {
  192. // Append the param type to the signature, -1 to exclude Void
  193. m_shortSig = (m_shortSig << nBitsForArgs) | (m_params[i] - 1);
  194. }
  195. }
  196. }
  197. Js::ArgSlot WasmSignature::GetParamsSize() const
  198. {
  199. return m_paramSize;
  200. }
  201. WasmSignature* WasmSignature::FromIDL(WasmSignatureIDL* sig)
  202. {
  203. // must update WasmSignatureIDL when changing WasmSignature
  204. CompileAssert(sizeof(Wasm::WasmSignature) == sizeof(WasmSignatureIDL));
  205. CompileAssert(offsetof(Wasm::WasmSignature, m_id) == offsetof(WasmSignatureIDL, id));
  206. CompileAssert(offsetof(Wasm::WasmSignature, m_resultsCount) == offsetof(WasmSignatureIDL, resultsCount));
  207. CompileAssert(offsetof(Wasm::WasmSignature, m_paramSize) == offsetof(WasmSignatureIDL, paramSize));
  208. CompileAssert(offsetof(Wasm::WasmSignature, m_paramsCount) == offsetof(WasmSignatureIDL, paramsCount));
  209. CompileAssert(offsetof(Wasm::WasmSignature, m_shortSig) == offsetof(WasmSignatureIDL, shortSig));
  210. CompileAssert(offsetof(Wasm::WasmSignature, m_params) == offsetof(WasmSignatureIDL, params));
  211. CompileAssert(offsetof(Wasm::WasmSignature, m_results) == offsetof(WasmSignatureIDL, results));
  212. CompileAssert(sizeof(Local) == sizeof(int));
  213. return reinterpret_cast<WasmSignature*>(sig);
  214. }
  215. uint32 WasmSignature::WriteSignatureToString(_Out_writes_(maxlen) char16* out, uint32 maxlen)
  216. {
  217. bool hasMultiResults = GetResultCount() > 1;
  218. const uint32 minEnd = GetResultCount() > 1 ? 20 : 12;
  219. AssertOrFailFast(maxlen > minEnd);
  220. AssertOrFailFast(out != nullptr);
  221. uint32 numwritten = 0;
  222. #define WRITE_BUF(msg, ...) numwritten += _snwprintf_s(out+numwritten, maxlen-numwritten, _TRUNCATE, msg, ##__VA_ARGS__);
  223. WRITE_BUF(_u("("));
  224. for (Js::ArgSlot i = 0; i < GetParamCount(); i++)
  225. {
  226. if (i != 0)
  227. {
  228. WRITE_BUF(_u(", "));
  229. }
  230. WRITE_BUF(_u("%ls"), WasmTypes::GetTypeName(this->GetParam(i)));
  231. }
  232. if (numwritten >= maxlen - minEnd) {
  233. // null out the last 12 characters so we can properly end it
  234. for (uint32 i = 1; i <= minEnd; i++)
  235. {
  236. *(out + maxlen - i) = 0;
  237. }
  238. if (numwritten < minEnd)
  239. {
  240. numwritten = 0;
  241. }
  242. else
  243. {
  244. numwritten -= minEnd;
  245. }
  246. WRITE_BUF(_u("..."));
  247. if (hasMultiResults)
  248. {
  249. // If the signature has multiple results, just print the first one then terminate.
  250. WRITE_BUF(_u(")->(%s, ...)"), WasmTypes::GetTypeName(this->GetResult(0)));
  251. return numwritten;
  252. }
  253. }
  254. if (GetResultCount() == 0)
  255. {
  256. WRITE_BUF(_u(")->void"));
  257. }
  258. else if (GetResultCount() == 1)
  259. {
  260. WRITE_BUF(_u(")->%ls"), WasmTypes::GetTypeName(this->GetResult(0)));
  261. }
  262. else
  263. {
  264. WRITE_BUF(_u(")->("));
  265. for (uint32 i = 0; i < GetResultCount(); i++)
  266. {
  267. if (i != 0)
  268. {
  269. WRITE_BUF(_u(", "));
  270. }
  271. WRITE_BUF(_u("%ls"), WasmTypes::GetTypeName(this->GetResult(i)));
  272. }
  273. WRITE_BUF(_u(")"));
  274. }
  275. #undef WRITE_BUF
  276. return numwritten;
  277. }
  278. void WasmSignature::Dump(uint32 maxlen)
  279. {
  280. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  281. char16 buf[512] = { 0 };
  282. this->WriteSignatureToString(buf, min(maxlen, 512u));
  283. Output::Print(buf);
  284. #endif
  285. }
  286. } // namespace Wasm
  287. #endif // ENABLE_WASM