blob: e22b4d675ebedb4d9b4e8aa26ae9e9e026a69496 [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeLanguagePch.h"
#ifdef ASMJS_PLAT
#include "CodeGenAllocators.h"
#ifdef DBG_DUMP
#include "ByteCode/ByteCodeDumper.h"
#include "ByteCode/AsmJsByteCodeDumper.h"
#endif
#include "AsmJsEncoder.inl"
#if DBG_DUMP
#include "ByteCode/OpCodeUtilAsmJs.h"
#endif
namespace Js
{
template<> int AsmJsEncoder::GetOffset<int>() const{return mIntOffset;}
template<> int AsmJsEncoder::GetOffset<Var>() const{return AsmJsJitTemplate::Globals::StackVarCount * sizeof( Var );}
template<> int AsmJsEncoder::GetOffset<double>() const{ return mDoubleOffset; }
template<> int AsmJsEncoder::GetOffset<float>() const{ return mFloatOffset; }
template<> int AsmJsEncoder::GetOffset<AsmJsSIMDValue>() const{ return mSimdOffset; }
template<>
void AsmJsEncoder::ReadOpTemplate<Js::SmallLayout>( OpCodeAsmJs op )
{
switch( op )
{
#define DEF2(x, op, func) PROCESS_ENCODE_##x(op, func)
#define DEF3(x, op, func, y) PROCESS_ENCODE_##x(op, func, y)
#define DEF2_WMS(x, op, func) PROCESS_ENCODE_##x##_COMMON(op, func, _Small)
#define DEF3_WMS(x, op, func, y) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Small)
#define DEF4_WMS(x, op, func, y, t) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Small, t)
#define EXDEF2(x, op, func) PROCESS_ENCODE_##x(op, func)
#define EXDEF3(x, op, func, y) PROCESS_ENCODE_##x(op, func, y)
#define EXDEF2_WMS(x, op, func) PROCESS_ENCODE_##x##_COMMON(op, func, _Small)
#define EXDEF3_WMS(x, op, func, y) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Small)
#define EXDEF4_WMS(x, op, func, y, t) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Small, t)
#include "AsmJsEncoderHandler.inl"
default:
// Help the C++ optimizer by declaring that the cases we
// have above are sufficient
#if DBG_DUMP
Output::Print( _u("Dispatch to bad opcode : %s\n"), OpCodeUtilAsmJs::GetOpCodeName(op));
Output::Flush();
#endif
Assert( false );
__assume( false );
};
}
template<>
void AsmJsEncoder::ReadOpTemplate<Js::MediumLayout>( OpCodeAsmJs op )
{
switch( op )
{
#define DEF2_WMS(x, op, func) PROCESS_ENCODE_##x##_COMMON(op, func, _Medium)
#define DEF3_WMS(x, op, func, y) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Medium)
#define DEF4_WMS(x, op, func, y, t) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Medium, t)
#define EXDEF2_WMS(x, op, func) PROCESS_ENCODE_##x##_COMMON(op, func, _Medium)
#define EXDEF3_WMS(x, op, func, y) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Medium)
#define EXDEF4_WMS(x, op, func, y, t) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Medium, t)
#include "AsmJsEncoderHandler.inl"
default:
// Help the C++ optimizer by declaring that the cases we
// have above are sufficient
#if DBG_DUMP
Output::Print( _u("Dispatch to bad opcode : %s\n"), OpCodeUtilAsmJs::GetOpCodeName(op));
Output::Flush();
#endif
Assert( false );
__assume( false );
};
}
template<>
void AsmJsEncoder::ReadOpTemplate<Js::LargeLayout>( OpCodeAsmJs op )
{
switch( op )
{
#define DEF2_WMS(x, op, func) PROCESS_ENCODE_##x##_COMMON(op, func, _Large)
#define DEF3_WMS(x, op, func, y) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Large)
#define DEF4_WMS(x, op, func, y, t) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Large, t)
#define EXDEF2_WMS(x, op, func) PROCESS_ENCODE_##x##_COMMON(op, func, _Large)
#define EXDEF3_WMS(x, op, func, y) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Large)
#define EXDEF4_WMS(x, op, func, y, t) PROCESS_ENCODE_##x##_COMMON(op, func, y, _Large, t)
#include "AsmJsEncoderHandler.inl"
default:
// Help the C++ optimizer by declaring that the cases we
// have above are sufficient
#if DBG_DUMP
Output::Print( _u("Dispatch to bad opcode : %s\n"), OpCodeUtilAsmJs::GetOpCodeName(op));
Output::Flush();
#endif
Assert( false );
__assume( false );
};
}
bool AsmJsEncoder::ReadOp()
{
#if DBG_DUMP
int bytecodeoffset = mReader.GetCurrentOffset();
#endif
LayoutSize layoutSize;
OpCodeAsmJs op = (OpCodeAsmJs)mReader.ReadOp(layoutSize);
ip = mReader.GetIP();
#if DBG_DUMP
if (PHASE_TRACE(Js::AsmjsEncoderPhase, mFunctionBody))
{
Output::Print(_u("%d.%d:Encoding "),
this->mFunctionBody->GetSourceContextId(),
this->mFunctionBody->GetLocalFunctionId());
AsmJsByteCodeDumper::DumpOp( op, layoutSize, mReader, mFunctionBody );
if( ip != mReader.GetIP() )
{
mReader.SetIP( ip );
}
Output::Print(_u(" at offset 0x%X (buffer size = 0x%X)\n"),
bytecodeoffset, (int)(mPc-mEncodeBuffer));
Output::Flush();
}
#endif
if( op == OpCodeAsmJs::EndOfBlock )
{
Assert(mReader.GetCurrentOffset() == mFunctionBody->GetByteCode()->GetLength());
// last bytecode
return false;
}
switch( layoutSize )
{
case Js::SmallLayout:
ReadOpTemplate<Js::SmallLayout>( op );
break;
case Js::MediumLayout:
ReadOpTemplate<Js::MediumLayout>( op );
break;
case Js::LargeLayout:
ReadOpTemplate<Js::LargeLayout>( op );
break;
default:
break;
}
return true;
}
uint32 AsmJsEncoder::GetEncodeBufferSize(FunctionBody* functionBody)
{
// TODO: Make a good heuristic; this is completely arbitrary. As we emit each bytecode we can calculate the max instruction size.
return UInt32Math::Add(
UInt32Math::Mul(functionBody->GetByteCodeCount(), 30),
49 /*prolog*/ + 11 /*epilog*/
);
}
void* AsmJsEncoder::Encode( FunctionBody* functionBody )
{
Assert( functionBody );
mFunctionBody = functionBody;
#if DBG_DUMP
AsmJsJitTemplate::Globals::CurrentEncodingFunction = mFunctionBody;
#endif
AsmJsFunctionInfo* asmInfo = functionBody->GetAsmJsFunctionInfo();
FunctionEntryPointInfo* entryPointInfo = ((FunctionEntryPointInfo*)(functionBody->GetDefaultEntryPointInfo()));
// number of var on the stack + ebp + eip
mIntOffset = asmInfo->GetIntByteOffset() + GetOffset<Var>();
mDoubleOffset = asmInfo->GetDoubleByteOffset() + GetOffset<Var>();
mFloatOffset = asmInfo->GetFloatByteOffset() + GetOffset<Var>();
mSimdOffset = asmInfo->GetSimdByteOffset() + GetOffset<Var>();
NoRecoverMemoryArenaAllocator localAlloc(_u("BE-AsmJsEncoder"), GetPageAllocator(), Js::Throw::OutOfMemory);
mLocalAlloc = &localAlloc;
mRelocLabelMap = Anew( mLocalAlloc, RelocLabelMap, mLocalAlloc );
mTemplateData = AsmJsJitTemplate::InitTemplateData();
mEncodeBufferSize = GetEncodeBufferSize(functionBody);
mEncodeBuffer = AnewArray((&localAlloc), BYTE, mEncodeBufferSize);
mPc = mEncodeBuffer;
mReader.Create( functionBody );
ip = mReader.GetIP();
#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
if( PHASE_TRACE( Js::AsmjsEncoderPhase, mFunctionBody ) )
{
Output::Print( _u("\n\n") );
functionBody->DumpFullFunctionName();
Output::Print( _u("\n StackSize = %d , Offsets: Var = %d, Int = %d, Double = %d\n"), mFunctionBody->GetAsmJsFunctionInfo()->GetTotalSizeinBytes(), GetOffset<Var>(), GetOffset<int>(), GetOffset<double>() );
}
#endif
AsmJsJitTemplate::FunctionEntry::ApplyTemplate( this, mPc );
while( ReadOp() ){}
AsmJsJitTemplate::FunctionExit::ApplyTemplate( this, mPc );
AsmJsJitTemplate::FreeTemplateData( mTemplateData );
#if DBG_DUMP
AsmJsJitTemplate::Globals::CurrentEncodingFunction = nullptr;
#endif
ApplyRelocs();
ptrdiff_t codeSize = mPc - mEncodeBuffer;
if( codeSize > 0 )
{
Assert( ::Math::FitsInDWord( codeSize ) );
BYTE *buffer;
EmitBufferAllocation<VirtualAllocWrapper, PreReservedVirtualAllocWrapper> *allocation = GetCodeGenAllocator()->emitBufferManager.AllocateBuffer( codeSize, &buffer, 0, 0 );
functionBody->GetAsmJsFunctionInfo()->mTJBeginAddress = buffer;
if (buffer == nullptr)
{
Js::Throw::OutOfMemory();
}
if (!GetCodeGenAllocator()->emitBufferManager.CommitBuffer(allocation, buffer, codeSize, mEncodeBuffer))
{
Js::Throw::OutOfMemory();
}
functionBody->GetScriptContext()->GetThreadContext()->SetValidCallTargetForCFG(buffer);
// TODO: improve this once EntryPoint cleanup work is complete!
#if 0
const char16 *const functionName = functionBody->GetDisplayName();
const char16 *const suffix = _u("TJ");
char16 functionNameArray[256];
const size_t functionNameCharLength = functionBody->GetDisplayNameLength();
wcscpy_s(functionNameArray, 256, functionName);
wcscpy_s(&functionNameArray[functionNameCharLength], 256 - functionNameCharLength, suffix);
#endif
JS_ETW(EventWriteMethodLoad(functionBody->GetScriptContext(),
(void *)buffer,
codeSize,
EtwTrace::GetFunctionId(functionBody),
0 /* methodFlags - for future use*/,
MethodType_Jit,
EtwTrace::GetSourceId(functionBody),
functionBody->GetLineNumber(),
functionBody->GetColumnNumber(),
functionBody->GetDisplayName()));
entryPointInfo->SetTJCodeGenDone(); // set the codegen to done state for TJ
entryPointInfo->SetCodeSize(codeSize);
return buffer;
}
return nullptr;
}
void Js::AsmJsEncoder::AddReloc( const int labelOffset, BYTE* patchAddr )
{
EncoderRelocLabel* label = nullptr;
if( mRelocLabelMap->TryGetReference( labelOffset, &label ) )
{
EncoderReloc::New( label, patchAddr, mPc, mLocalAlloc );
}
else
{
EncoderRelocLabel newLabel;
EncoderReloc::New( &newLabel, patchAddr, mPc, mLocalAlloc );
mRelocLabelMap->AddNew( labelOffset, newLabel );
}
}
void AsmJsEncoder::ApplyRelocs()
{
const int size = mRelocLabelMap->Count();
for (int i = 0; i < size ; i++)
{
EncoderRelocLabel* label = mRelocLabelMap->GetReferenceAt( i );
#if DBG_DUMP
if( !label->labelSeen )
{
Output::Print( _u("Label expected at bytecode offset 0x%x\n"), mRelocLabelMap->GetKeyAt( i ) );
Output::Flush();
}
#endif
Assert( label->labelSeen );
EncoderReloc* reloc = label->relocList;
ptrdiff_t offset1 = label->pc - mEncodeBuffer;
this->GetAsmJsFunctionInfo()->mbyteCodeTJMap->AddNew(mRelocLabelMap->GetKeyAt(i), offset1);
while( reloc )
{
ptrdiff_t offset = label->pc - reloc->pc;
*(ptrdiff_t*)reloc->patchAddr = offset;
reloc = reloc->next;
}
}
}
void AsmJsEncoder::EncoderReloc::New( EncoderRelocLabel* label, BYTE* _patchAddr, BYTE* _pc, ArenaAllocator* allocator )
{
AsmJsEncoder::EncoderReloc* reloc = AnewStruct( allocator, AsmJsEncoder::EncoderReloc );
reloc->next = label->relocList;
label->relocList = reloc;
reloc->patchAddr = _patchAddr;
reloc->pc = _pc;
}
};
#endif