AsmJSEncoder.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  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. #include "RuntimeLanguagePch.h"
  6. #ifndef TEMP_DISABLE_ASMJS
  7. #include "CodeGenAllocators.h"
  8. #ifdef DBG_DUMP
  9. #include "ByteCode\ByteCodeDumper.h"
  10. #include "ByteCode\AsmJSByteCodeDumper.h"
  11. #endif
  12. #include "AsmJSEncoder.inl"
  13. #if DBG_DUMP
  14. #include "ByteCode\OpCodeUtilAsmJs.h"
  15. #endif
  16. namespace Js
  17. {
  18. template<> int AsmJsEncoder::GetOffset<int>() const{return mIntOffset;}
  19. template<> int AsmJsEncoder::GetOffset<Var>() const{return AsmJsJitTemplate::Globals::StackVarCount * sizeof( Var );}
  20. template<> int AsmJsEncoder::GetOffset<double>() const{ return mDoubleOffset; }
  21. template<> int AsmJsEncoder::GetOffset<float>() const{ return mFloatOffset; }
  22. template<> int AsmJsEncoder::GetOffset<AsmJsSIMDValue>() const{ return mSimdOffset; }
  23. template<>
  24. void AsmJsEncoder::ReadOpTemplate<Js::SmallLayout>( OpCodeAsmJs op )
  25. {
  26. switch( op )
  27. {
  28. #define DEF2(x, op, func) PROCESS_ENCODE_##x(op, func)
  29. #define DEF3(x, op, func, y) PROCESS_ENCODE_##x(op, func, y)
  30. #define DEF2_WMS(x, op, func) PROCESS_ENCODE_##x##_COMMON(op, func, _Small)
  31. #define DEF3_WMS(x, op, func, y) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Small)
  32. #define DEF4_WMS(x, op, func, y, t) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Small, t)
  33. #define EXDEF2(x, op, func) PROCESS_ENCODE_##x(op, func)
  34. #define EXDEF3(x, op, func, y) PROCESS_ENCODE_##x(op, func, y)
  35. #define EXDEF2_WMS(x, op, func) PROCESS_ENCODE_##x##_COMMON(op, func, _Small)
  36. #define EXDEF3_WMS(x, op, func, y) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Small)
  37. #define EXDEF4_WMS(x, op, func, y, t) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Small, t)
  38. #include "AsmJSEncoderHandler.inl"
  39. default:
  40. // Help the C++ optimizer by declaring that the cases we
  41. // have above are sufficient
  42. #if DBG_DUMP
  43. Output::Print( L"Dispatch to bad opcode : %s\n", OpCodeUtilAsmJs::GetOpCodeName(op));
  44. Output::Flush();
  45. #endif
  46. Assert( false );
  47. __assume( false );
  48. };
  49. }
  50. template<>
  51. void AsmJsEncoder::ReadOpTemplate<Js::MediumLayout>( OpCodeAsmJs op )
  52. {
  53. switch( op )
  54. {
  55. #define DEF2_WMS(x, op, func) PROCESS_ENCODE_##x##_COMMON(op, func, _Medium)
  56. #define DEF3_WMS(x, op, func, y) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Medium)
  57. #define DEF4_WMS(x, op, func, y, t) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Medium, t)
  58. #define EXDEF2_WMS(x, op, func) PROCESS_ENCODE_##x##_COMMON(op, func, _Medium)
  59. #define EXDEF3_WMS(x, op, func, y) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Medium)
  60. #define EXDEF4_WMS(x, op, func, y, t) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Medium, t)
  61. #include "AsmJSEncoderHandler.inl"
  62. default:
  63. // Help the C++ optimizer by declaring that the cases we
  64. // have above are sufficient
  65. #if DBG_DUMP
  66. Output::Print( L"Dispatch to bad opcode : %s\n", OpCodeUtilAsmJs::GetOpCodeName(op));
  67. Output::Flush();
  68. #endif
  69. Assert( false );
  70. __assume( false );
  71. };
  72. }
  73. template<>
  74. void AsmJsEncoder::ReadOpTemplate<Js::LargeLayout>( OpCodeAsmJs op )
  75. {
  76. switch( op )
  77. {
  78. #define DEF2_WMS(x, op, func) PROCESS_ENCODE_##x##_COMMON(op, func, _Large)
  79. #define DEF3_WMS(x, op, func, y) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Large)
  80. #define DEF4_WMS(x, op, func, y, t) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Large, t)
  81. #define EXDEF2_WMS(x, op, func) PROCESS_ENCODE_##x##_COMMON(op, func, _Large)
  82. #define EXDEF3_WMS(x, op, func, y) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Large)
  83. #define EXDEF4_WMS(x, op, func, y, t) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Large, t)
  84. #include "AsmJSEncoderHandler.inl"
  85. default:
  86. // Help the C++ optimizer by declaring that the cases we
  87. // have above are sufficient
  88. #if DBG_DUMP
  89. Output::Print( L"Dispatch to bad opcode : %s\n", OpCodeUtilAsmJs::GetOpCodeName(op));
  90. Output::Flush();
  91. #endif
  92. Assert( false );
  93. __assume( false );
  94. };
  95. }
  96. bool AsmJsEncoder::ReadOp()
  97. {
  98. #if DBG_DUMP
  99. int bytecodeoffset = mReader.GetCurrentOffset();
  100. #endif
  101. LayoutSize layoutSize;
  102. OpCodeAsmJs op = (OpCodeAsmJs)mReader.ReadOp(layoutSize);
  103. ip = mReader.GetIP();
  104. #if DBG_DUMP
  105. if (PHASE_TRACE(Js::AsmjsEncoderPhase, mFunctionBody))
  106. {
  107. Output::Print(L"%d.%d:Encoding ",
  108. this->mFunctionBody->GetSourceContextId(),
  109. this->mFunctionBody->GetLocalFunctionId());
  110. AsmJsByteCodeDumper::DumpOp( op, layoutSize, mReader, mFunctionBody );
  111. if( ip != mReader.GetIP() )
  112. {
  113. mReader.SetIP( ip );
  114. }
  115. Output::Print(L" at offset 0x%X (buffer size = 0x%X)\n",
  116. bytecodeoffset, (int)(mPc-mEncodeBuffer));
  117. Output::Flush();
  118. }
  119. #endif
  120. if( op == OpCodeAsmJs::EndOfBlock )
  121. {
  122. Assert(mReader.GetCurrentOffset() == mFunctionBody->GetByteCode()->GetLength());
  123. // last bytecode
  124. return false;
  125. }
  126. switch( layoutSize )
  127. {
  128. case Js::SmallLayout:
  129. ReadOpTemplate<Js::SmallLayout>( op );
  130. break;
  131. case Js::MediumLayout:
  132. ReadOpTemplate<Js::MediumLayout>( op );
  133. break;
  134. case Js::LargeLayout:
  135. ReadOpTemplate<Js::LargeLayout>( op );
  136. break;
  137. default:
  138. break;
  139. }
  140. return true;
  141. }
  142. uint32 AsmJsEncoder::GetEncodeBufferSize(FunctionBody* functionBody)
  143. {
  144. // TODO: Make a good heuristic; this is completely arbitrary. As we emit each bytecode we can calculate the max instruction size.
  145. return UInt32Math::Add(
  146. UInt32Math::Mul(functionBody->GetByteCodeCount(), 30),
  147. 49 /*prolog*/ + 11 /*epilog*/
  148. );
  149. }
  150. void* AsmJsEncoder::Encode( FunctionBody* functionBody )
  151. {
  152. Assert( functionBody );
  153. mFunctionBody = functionBody;
  154. #if DBG_DUMP
  155. AsmJsJitTemplate::Globals::CurrentEncodingFunction = mFunctionBody;
  156. #endif
  157. AsmJsFunctionInfo* asmInfo = functionBody->GetAsmJsFunctionInfo();
  158. FunctionEntryPointInfo* entryPointInfo = ((FunctionEntryPointInfo*)(functionBody->GetDefaultEntryPointInfo()));
  159. // number of var on the stack + ebp + eip
  160. mIntOffset = asmInfo->GetIntByteOffset() + GetOffset<Var>();
  161. mDoubleOffset = asmInfo->GetDoubleByteOffset() + GetOffset<Var>();
  162. mFloatOffset = asmInfo->GetFloatByteOffset() + GetOffset<Var>();
  163. mSimdOffset = asmInfo->GetSimdByteOffset() + GetOffset<Var>();
  164. NoRecoverMemoryArenaAllocator localAlloc(L"BE-AsmJsEncoder", GetPageAllocator(), Js::Throw::OutOfMemory);
  165. mLocalAlloc = &localAlloc;
  166. mRelocLabelMap = Anew( mLocalAlloc, RelocLabelMap, mLocalAlloc );
  167. mTemplateData = AsmJsJitTemplate::InitTemplateData();
  168. mEncodeBufferSize = GetEncodeBufferSize(functionBody);
  169. mEncodeBuffer = AnewArray((&localAlloc), BYTE, mEncodeBufferSize);
  170. mPc = mEncodeBuffer;
  171. mReader.Create( functionBody );
  172. ip = mReader.GetIP();
  173. #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
  174. if( PHASE_TRACE( Js::AsmjsEncoderPhase, mFunctionBody ) )
  175. {
  176. Output::Print( L"\n\n" );
  177. functionBody->DumpFullFunctionName();
  178. Output::Print( L"\n StackSize = %d , Offsets: Var = %d, Int = %d, Double = %d\n", mFunctionBody->GetAsmJsFunctionInfo()->GetTotalSizeinBytes(), GetOffset<Var>(), GetOffset<int>(), GetOffset<double>() );
  179. }
  180. #endif
  181. AsmJsRetType retType = asmInfo->GetReturnType();
  182. AsmJsJitTemplate::FunctionEntry::ApplyTemplate( this, mPc );
  183. while( ReadOp() ){}
  184. AsmJsJitTemplate::FunctionExit::ApplyTemplate( this, mPc );
  185. AsmJsJitTemplate::FreeTemplateData( mTemplateData );
  186. #if DBG_DUMP
  187. AsmJsJitTemplate::Globals::CurrentEncodingFunction = nullptr;
  188. #endif
  189. ApplyRelocs();
  190. ptrdiff_t codeSize = mPc - mEncodeBuffer;
  191. if( codeSize > 0 )
  192. {
  193. Assert( ::Math::FitsInDWord( codeSize ) );
  194. BYTE *buffer;
  195. EmitBufferAllocation *allocation = GetCodeGenAllocator()->emitBufferManager.AllocateBuffer( codeSize, &buffer, 0, 0 );
  196. functionBody->GetAsmJsFunctionInfo()->mTJBeginAddress = buffer;
  197. Assert( allocation != nullptr );
  198. if( buffer == nullptr )
  199. Js::Throw::OutOfMemory();
  200. if (!GetCodeGenAllocator()->emitBufferManager.CommitBuffer(allocation, buffer, codeSize, mEncodeBuffer))
  201. {
  202. Js::Throw::OutOfMemory();
  203. }
  204. functionBody->GetScriptContext()->GetThreadContext()->SetValidCallTargetForCFG(buffer);
  205. // TODO: improve this once EntryPoint cleanup work is complete!
  206. #if 0
  207. const wchar_t *const functionName = functionBody->GetDisplayName();
  208. const wchar_t *const suffix = L"TJ";
  209. wchar_t functionNameArray[256];
  210. const size_t functionNameCharLength = functionBody->GetDisplayNameLength();
  211. wcscpy_s(functionNameArray, 256, functionName);
  212. wcscpy_s(&functionNameArray[functionNameCharLength], 256 - functionNameCharLength, suffix);
  213. #endif
  214. JS_ETW(EventWriteMethodLoad(functionBody->GetScriptContext(),
  215. (void *)buffer,
  216. codeSize,
  217. EtwTrace::GetFunctionId(functionBody),
  218. 0 /* methodFlags - for future use*/,
  219. MethodType_Jit,
  220. EtwTrace::GetSourceId(functionBody),
  221. functionBody->GetLineNumber(),
  222. functionBody->GetColumnNumber(),
  223. functionBody->GetDisplayName()));
  224. entryPointInfo->SetTJCodeGenDone(); // set the codegen to done state for TJ
  225. entryPointInfo->SetCodeSize(codeSize);
  226. return buffer;
  227. }
  228. return nullptr;
  229. }
  230. void Js::AsmJsEncoder::AddReloc( const int labelOffset, BYTE* patchAddr )
  231. {
  232. EncoderRelocLabel* label = nullptr;
  233. if( mRelocLabelMap->TryGetReference( labelOffset, &label ) )
  234. {
  235. EncoderReloc::New( label, patchAddr, mPc, mLocalAlloc );
  236. }
  237. else
  238. {
  239. EncoderRelocLabel newLabel;
  240. EncoderReloc::New( &newLabel, patchAddr, mPc, mLocalAlloc );
  241. mRelocLabelMap->AddNew( labelOffset, newLabel );
  242. }
  243. }
  244. void AsmJsEncoder::ApplyRelocs()
  245. {
  246. const int size = mRelocLabelMap->Count();
  247. for (int i = 0; i < size ; i++)
  248. {
  249. EncoderRelocLabel* label = mRelocLabelMap->GetReferenceAt( i );
  250. #if DBG_DUMP
  251. if( !label->labelSeen )
  252. {
  253. Output::Print( L"Label expected at bytecode offset 0x%x\n", mRelocLabelMap->GetKeyAt( i ) );
  254. Output::Flush();
  255. }
  256. #endif
  257. Assert( label->labelSeen );
  258. EncoderReloc* reloc = label->relocList;
  259. ptrdiff_t offset1 = label->pc - mEncodeBuffer;
  260. this->GetAsmJsFunctionInfo()->mbyteCodeTJMap->AddNew(mRelocLabelMap->GetKeyAt(i), offset1);
  261. while( reloc )
  262. {
  263. ptrdiff_t offset = label->pc - reloc->pc;
  264. *(ptrdiff_t*)reloc->patchAddr = offset;
  265. reloc = reloc->next;
  266. }
  267. }
  268. }
  269. void AsmJsEncoder::EncoderReloc::New( EncoderRelocLabel* label, BYTE* _patchAddr, BYTE* _pc, ArenaAllocator* allocator )
  270. {
  271. AsmJsEncoder::EncoderReloc* reloc = AnewStruct( allocator, AsmJsEncoder::EncoderReloc );
  272. reloc->next = label->relocList;
  273. label->relocList = reloc;
  274. reloc->patchAddr = _patchAddr;
  275. reloc->pc = _pc;
  276. }
  277. };
  278. #endif