2
0
Эх сурвалжийг харах

Since Wasm doesn't have a constant table, the IR for switch cases wouldn't build properly with the added Ld_I4 before every case.
Therefore, I added a bytecode with the case value as an immediate instead of a register.
This also generates a smaller bytecode for switch statements

Michael Ferris 9 жил өмнө
parent
commit
99a68a4219

+ 9 - 9
lib/Backend/CaseNode.cpp

@@ -7,8 +7,8 @@
 int
 DefaultComparer<CaseNode *>::Compare(CaseNode* caseNode1, CaseNode* caseNode2)
 {
-    int caseVal1 = caseNode1->GetSrc2IntConst();
-    int caseVal2 = caseNode2->GetSrc2IntConst();
+    int caseVal1 = caseNode1->GetUpperBoundIntConst();
+    int caseVal2 = caseNode2->GetUpperBoundIntConst();
     uint32 caseOffset1 = caseNode1->GetOffset();
     uint32 caseOffset2 = caseNode2->GetOffset();
 
@@ -24,16 +24,16 @@ DefaultComparer<CaseNode *>::Compare(CaseNode* caseNode1, CaseNode* caseNode2)
 bool
 DefaultComparer<CaseNode *>::Equals(CaseNode * caseNode1, CaseNode* caseNode2)
 {
-    if(caseNode1->IsSrc2IntConst() && caseNode2->IsSrc2IntConst())
+    if(caseNode1->IsUpperBoundIntConst() && caseNode2->IsUpperBoundIntConst())
     {
-        int caseVal1 = caseNode1->GetSrc2IntConst();
-        int caseVal2 = caseNode2->GetSrc2IntConst();
+        int caseVal1 = caseNode1->GetUpperBoundIntConst();
+        int caseVal2 = caseNode2->GetUpperBoundIntConst();
         return caseVal1 == caseVal2;
     }
-    else if(caseNode1->IsSrc2StrConst() && caseNode2->IsSrc2StrConst())
+    else if(caseNode1->IsUpperBoundStrConst() && caseNode2->IsUpperBoundStrConst())
     {
-        Js::JavascriptString * caseVal1 = caseNode1->GetSrc2StringConst();
-        Js::JavascriptString * caseVal2 = caseNode2->GetSrc2StringConst();
+        Js::JavascriptString * caseVal1 = caseNode1->GetUpperBoundStrConst();
+        Js::JavascriptString * caseVal2 = caseNode2->GetUpperBoundStrConst();
         return Js::JavascriptString::Equals(caseVal1, caseVal2);
     }
     else
@@ -46,5 +46,5 @@ DefaultComparer<CaseNode *>::Equals(CaseNode * caseNode1, CaseNode* caseNode2)
 uint
 DefaultComparer<CaseNode *>::GetHashCode(CaseNode* caseNode)
 {
-    return (uint)caseNode->GetSrc2IntConst();
+    return (uint)caseNode->GetUpperBoundIntConst();
 }

+ 55 - 10
lib/Backend/CaseNode.h

@@ -16,6 +16,28 @@ private:
     IR::BranchInstr*    caseInstr;      // caseInstr - stores the case instruction
     IR::Opnd*           lowerBound;     // lowerBound - used for integer cases
 
+    int32 GetIntConst(IR::Opnd* opnd)
+    {
+        Assert(IsIntConst(opnd));
+        return opnd->IsIntConstOpnd() ? opnd->AsIntConstOpnd()->AsInt32() : opnd->GetStackSym()->GetIntConstValue();
+    }
+
+    Js::JavascriptString* GetStringConst(IR::Opnd* opnd)
+    {
+        Assert(IsStrConst(opnd));
+        return Js::JavascriptString::FromVar(opnd->GetStackSym()->GetConstAddress());
+    }
+
+    bool IsIntConst(IR::Opnd* opnd)
+    {
+        return opnd->IsIntConstOpnd() || opnd->GetStackSym()->IsIntConst();
+    }
+
+    bool IsStrConst(IR::Opnd* opnd)
+    {
+        return opnd->GetStackSym()->m_isStrConst;
+    }
+
 public:
     CaseNode(IR::BranchInstr* caseInstr, uint32 offset, uint32 targetOffset, IR::Opnd* lowerBound = nullptr)
         : caseInstr(caseInstr),
@@ -25,27 +47,50 @@ public:
     {
     }
 
-    int32 GetSrc2IntConst()
+    int32 GetUpperBoundIntConst()
     {
-        AssertMsg(caseInstr->GetSrc2()->GetStackSym()->IsIntConst(),"Source2 operand is not an integer constant");
-        return caseInstr->GetSrc2()->GetStackSym()->GetIntConstValue();
+        AssertMsg(IsUpperBoundIntConst(), "Source2 operand is not an integer constant");
+        return GetIntConst(GetUpperBound());
     }
 
-    Js::JavascriptString* GetSrc2StringConst()
+    Js::JavascriptString* GetUpperBoundStrConst()
     {
-        AssertMsg(caseInstr->GetSrc2()->GetStackSym()->m_isStrConst,"Source2 operand is not an integer constant");
-        return Js::JavascriptString::FromVar(caseInstr->GetSrc2()->GetStackSym()->GetConstAddress());
+        AssertMsg(IsUpperBoundStrConst(),"Source2 operand is not a string constant");
+        return GetStringConst(GetUpperBound());
     }
 
-    bool IsSrc2IntConst()
+    bool IsUpperBoundIntConst()
     {
-        return caseInstr->GetSrc2()->GetStackSym()->IsIntConst();
+        return IsIntConst(GetUpperBound());
     }
 
-    bool IsSrc2StrConst()
+    bool IsUpperBoundStrConst()
     {
-        return caseInstr->GetSrc2()->GetStackSym()->m_isStrConst;
+        return IsStrConst(GetUpperBound());
     }
+
+    int32 GetLowerBoundIntConst()
+    {
+        AssertMsg(IsLowerBoundIntConst(), "LowerBound is not an integer constant");
+        return GetIntConst(lowerBound);
+    }
+
+    Js::JavascriptString* GetLowerBoundStrConst()
+    {
+        AssertMsg(IsLowerBoundStrConst(), "LowerBound is not a string constant");
+        return GetStringConst(lowerBound);
+    }
+
+    bool IsLowerBoundIntConst()
+    {
+        return IsIntConst(lowerBound);
+    }
+
+    bool IsLowerBoundStrConst()
+    {
+        return IsStrConst(lowerBound);
+    }
+
     uint32 GetOffset()
     {
         return offset;

+ 30 - 1
lib/Backend/IRBuilderAsmJs.cpp

@@ -3112,6 +3112,15 @@ IRBuilderAsmJs::BuildBrInt2(Js::OpCodeAsmJs newOpcode, uint32 offset)
     BuildBrInt2(newOpcode, offset, layout->RelativeJumpOffset, layout->I1, layout->I2);
 }
 
+template <typename SizePolicy>
+void
+IRBuilderAsmJs::BuildBrInt1Const1(Js::OpCodeAsmJs newOpcode, uint32 offset)
+{
+    Assert(OpCodeAttrAsmJs::HasMultiSizeLayout(newOpcode));
+    auto layout = m_jnReader.GetLayout<Js::OpLayoutT_BrInt1Const1<SizePolicy>>();
+    BuildBrInt1Const1(newOpcode, offset, layout->RelativeJumpOffset, layout->I1, layout->C1);
+}
+
 void
 IRBuilderAsmJs::BuildBrInt2(Js::OpCodeAsmJs newOpcode, uint32 offset, int32 relativeOffset, Js::RegSlot src1, Js::RegSlot src2)
 {
@@ -3124,9 +3133,29 @@ IRBuilderAsmJs::BuildBrInt2(Js::OpCodeAsmJs newOpcode, uint32 offset, int32 rela
     IR::RegOpnd * src2Opnd = BuildSrcOpnd(src2RegSlot, TyInt32);
     src2Opnd->SetValueType(ValueType::GetInt(false));
 
+    BuildBrCmp(newOpcode, offset, relativeOffset, src1Opnd, src2Opnd);
+}
+
+void
+IRBuilderAsmJs::BuildBrInt1Const1(Js::OpCodeAsmJs newOpcode, uint32 offset, int32 relativeOffset, Js::RegSlot src1, int32 src2)
+{
+    Js::RegSlot src1RegSlot = GetRegSlotFromIntReg(src1);
+
+    IR::RegOpnd * src1Opnd = BuildSrcOpnd(src1RegSlot, TyInt32);
+    src1Opnd->SetValueType(ValueType::GetInt(false));
+
+    IR::Opnd * src2Opnd = IR::IntConstOpnd::New(src2, TyInt32, this->m_func);
+    src2Opnd->SetValueType(ValueType::GetInt(false));
+
+    BuildBrCmp(newOpcode, offset, relativeOffset, src1Opnd, src2Opnd);
+}
+
+void
+IRBuilderAsmJs::BuildBrCmp(Js::OpCodeAsmJs newOpcode, uint32 offset, int32 relativeOffset, IR::RegOpnd* src1Opnd, IR::Opnd* src2Opnd)
+{
     uint targetOffset = m_jnReader.GetCurrentOffset() + relativeOffset;
 
-    if (newOpcode == Js::OpCodeAsmJs::Case_Int)
+    if (newOpcode == Js::OpCodeAsmJs::Case_Int || newOpcode == Js::OpCodeAsmJs::Case_IntConst)
     {
         // branches for cases are generated entirely by the switch builder
         m_switchBuilder.OnCase(

+ 2 - 0
lib/Backend/IRBuilderAsmJs.h

@@ -143,6 +143,8 @@ private:
     void                    BuildDouble3(Js::OpCodeAsmJs newOpcode, uint32 offset, Js::RegSlot dst, Js::RegSlot src1, Js::RegSlot src2);
     void                    BuildBrInt1(Js::OpCodeAsmJs newOpcode, uint32 offset, int32 relativeOffset, Js::RegSlot src);
     void                    BuildBrInt2(Js::OpCodeAsmJs newOpcode, uint32 offset, int32 relativeOffset, Js::RegSlot src1, Js::RegSlot src2);
+    void                    BuildBrInt1Const1(Js::OpCodeAsmJs newOpcode, uint32 offset, int32 relativeOffset, Js::RegSlot src1, int32 src2);
+    void                    BuildBrCmp(Js::OpCodeAsmJs newOpcode, uint32 offset, int32 relativeOffset, IR::RegOpnd* src1Opnd, IR::Opnd* src2Opnd);
     void                    GenerateLoopBodySlotAccesses(uint offset);
     void                    GenerateLoopBodyStSlots(SymID loopParamSymId, uint offset);
     IR::Instr*              GenerateStSlotForReturn(IR::RegOpnd* srcOpnd, IRType type);

+ 34 - 27
lib/Backend/SwitchIRBuilder.cpp

@@ -223,17 +223,24 @@ SwitchIRBuilder::SetProfiledInstruction(IR::Instr * instr, Js::ProfileId profile
 ///----------------------------------------------------------------------------
 
 void
-SwitchIRBuilder::OnCase(IR::RegOpnd * src1Opnd, IR::RegOpnd * src2Opnd, uint32 offset, uint32 targetOffset)
+SwitchIRBuilder::OnCase(IR::RegOpnd * src1Opnd, IR::Opnd * src2Opnd, uint32 offset, uint32 targetOffset)
 {
     IR::BranchInstr * branchInstr;
 
-    if (src2Opnd->m_sym->m_isIntConst && m_intConstSwitchCases->TestAndSet(src2Opnd->m_sym->GetIntConstValue()))
+    Assert(src2Opnd->IsIntConstOpnd() || src2Opnd->IsRegOpnd());
+    // Support only int32 const opnd
+    Assert(!src2Opnd->IsIntConstOpnd() || src2Opnd->GetType() == TyInt32);
+    StackSym* sym = src2Opnd->GetStackSym();
+    const bool isIntConst = src2Opnd->IsIntConstOpnd() || sym->IsIntConst();
+    const bool isStrConst = !isIntConst && sym->m_isStrConst;
+
+    if (isIntConst && m_intConstSwitchCases->TestAndSet(sym ? sym->GetIntConstValue() : src2Opnd->AsIntConstOpnd()->AsInt32()))
     {
         // We've already seen a case statement with the same int const value. No need to emit anything for this.
         return;
     }
 
-    if (src2Opnd->m_sym->m_isStrConst && TestAndAddStringCaseConst(Js::JavascriptString::FromVar(src2Opnd->GetStackSym()->GetConstAddress())))
+    if (isStrConst && TestAndAddStringCaseConst(Js::JavascriptString::FromVar(sym->GetConstAddress())))
     {
         // We've already seen a case statement with the same string const value. No need to emit anything for this.
         return;
@@ -255,17 +262,17 @@ SwitchIRBuilder::OnCase(IR::RegOpnd * src1Opnd, IR::RegOpnd * src2Opnd, uint32 o
 
     if (GlobOpt::IsSwitchOptEnabled(m_func->GetTopFunc()))
     {
-        if (m_switchIntDynProfile && src2Opnd->m_sym->IsIntConst())
+        if (m_switchIntDynProfile && isIntConst)
         {
             CaseNode* caseNode = JitAnew(m_tempAlloc, CaseNode, branchInstr, offset, targetOffset, src2Opnd);
             m_caseNodes->Add(caseNode);
             deferred = true;
         }
-        else if (m_switchStrDynProfile && src2Opnd->m_sym->m_isStrConst)
+        else if (m_switchStrDynProfile && isStrConst)
         {
             CaseNode* caseNode = JitAnew(m_tempAlloc, CaseNode, branchInstr, offset, targetOffset, src2Opnd);
             m_caseNodes->Add(caseNode);
-            m_seenOnlySingleCharStrCaseNodes = m_seenOnlySingleCharStrCaseNodes && caseNode->GetSrc2StringConst()->GetLength() == 1;
+            m_seenOnlySingleCharStrCaseNodes = m_seenOnlySingleCharStrCaseNodes && caseNode->GetUpperBoundStrConst()->GetLength() == 1;
             deferred = true;
         }
     }
@@ -332,8 +339,8 @@ SwitchIRBuilder::RefineCaseNodes()
         CaseNode * currCaseNode = m_caseNodes->Item(currCaseIndex);
         uint32 prevCaseTargetOffset = prevCaseNode->GetTargetOffset();
         uint32 currCaseTargetOffset = currCaseNode->GetTargetOffset();
-        int prevCaseConstValue = prevCaseNode->GetSrc2IntConst();
-        int currCaseConstValue = currCaseNode->GetSrc2IntConst();
+        int prevCaseConstValue = prevCaseNode->GetUpperBoundIntConst();
+        int currCaseConstValue = currCaseNode->GetUpperBoundIntConst();
 
         /*To handle empty case statements with/without repetition*/
         if (prevCaseTargetOffset == currCaseTargetOffset &&
@@ -345,7 +352,7 @@ SwitchIRBuilder::RefineCaseNodes()
         {
             if (tmpCaseNodes->Count() != 0)
             {
-                int lastTmpCaseConstValue = tmpCaseNodes->Item(tmpCaseNodes->Count() - 1)->GetSrc2IntConst();
+                int lastTmpCaseConstValue = tmpCaseNodes->Item(tmpCaseNodes->Count() - 1)->GetUpperBoundIntConst();
                 /*To handle duplicate non empty case statements*/
                 if (lastTmpCaseConstValue != prevCaseConstValue)
                 {
@@ -460,17 +467,17 @@ SwitchIRBuilder::BuildLinearTraverseInstr(int start, int end, uint fallThrOffset
 
         bool dontBuildEmptyCases = false;
 
-        if (currCaseNode->IsSrc2IntConst())
+        if (currCaseNode->IsUpperBoundIntConst())
         {
-            int lowerBoundCaseConstValue = currCaseNode->GetLowerBound()->GetStackSym()->GetIntConstValue();
-            int upperBoundCaseConstValue = currCaseNode->GetUpperBound()->GetStackSym()->GetIntConstValue();
+            int lowerBoundCaseConstValue = currCaseNode->GetLowerBoundIntConst();
+            int upperBoundCaseConstValue = currCaseNode->GetUpperBoundIntConst();
 
             if (lowerBoundCaseConstValue == upperBoundCaseConstValue)
             {
                 dontBuildEmptyCases = true;
             }
         }
-        else if (currCaseNode->IsSrc2StrConst())
+        else if (currCaseNode->IsUpperBoundStrConst())
         {
             dontBuildEmptyCases = true;
         }
@@ -577,13 +584,13 @@ SwitchIRBuilder::BuildOptimizedIntegerCaseInstrs(uint32 targetOffset)
     {
         int nextIndex = currentIndex + 1;
         //Check if there is no missing value between subsequent case arms
-        if (m_caseNodes->Item(currentIndex)->GetSrc2IntConst() + 1 != m_caseNodes->Item(nextIndex)->GetSrc2IntConst())
+        if (m_caseNodes->Item(currentIndex)->GetUpperBoundIntConst() + 1 != m_caseNodes->Item(nextIndex)->GetUpperBoundIntConst())
         {
             //value of the case nodes are guaranteed to be 32 bits or less than 32bits at this point(if it is more, the Switch Opt will not kick in)
             Assert(nextIndex == endjmpTableIndex + 1);
-            int64 speculatedEndJmpCaseValue = m_caseNodes->Item(nextIndex)->GetSrc2IntConst();
-            int64 endJmpCaseValue = m_caseNodes->Item(endjmpTableIndex)->GetSrc2IntConst();
-            int64 startJmpCaseValue = m_caseNodes->Item(startjmpTableIndex)->GetSrc2IntConst();
+            int64 speculatedEndJmpCaseValue = m_caseNodes->Item(nextIndex)->GetUpperBoundIntConst();
+            int64 endJmpCaseValue = m_caseNodes->Item(endjmpTableIndex)->GetUpperBoundIntConst();
+            int64 startJmpCaseValue = m_caseNodes->Item(startjmpTableIndex)->GetUpperBoundIntConst();
 
             int64 speculatedJmpTableSize = speculatedEndJmpCaseValue - startJmpCaseValue + 1;
             int64 jmpTableSize = endJmpCaseValue - startJmpCaseValue + 1;
@@ -618,8 +625,8 @@ SwitchIRBuilder::BuildOptimizedIntegerCaseInstrs(uint32 targetOffset)
         }
     }
 
-    int64 endJmpCaseValue = m_caseNodes->Item(endjmpTableIndex)->GetSrc2IntConst();
-    int64 startJmpCaseValue = m_caseNodes->Item(startjmpTableIndex)->GetSrc2IntConst();
+    int64 endJmpCaseValue = m_caseNodes->Item(endjmpTableIndex)->GetUpperBoundIntConst();
+    int64 startJmpCaseValue = m_caseNodes->Item(startjmpTableIndex)->GetUpperBoundIntConst();
     int64 jmpTableSize = endJmpCaseValue - startJmpCaseValue + 1;
 
     if (jmpTableSize < CONFIG_FLAG(MinSwitchJumpTableSize))
@@ -825,7 +832,7 @@ SwitchIRBuilder::BuildMultiBrCaseInstrForStrings(uint32 targetOffset)
         generateDictionary = false;
         for (uint i = 0; i < caseCount; i++)
         {
-            Js::JavascriptString * str = m_caseNodes->Item(i)->GetSrc2StringConst();
+            Js::JavascriptString * str = m_caseNodes->Item(i)->GetUpperBoundStrConst();
             Assert(str->GetLength() == 1);
             char16 currChar = str->GetString()[0];
             minChar = min(minChar, currChar);
@@ -846,7 +853,7 @@ SwitchIRBuilder::BuildMultiBrCaseInstrForStrings(uint32 targetOffset)
         //Adding normal cases to the instruction (except the default case, which we do it later)
         for (uint i = 0; i < caseCount; i++)
         {
-            Js::JavascriptString * str = m_caseNodes->Item(i)->GetSrc2StringConst();
+            Js::JavascriptString * str = m_caseNodes->Item(i)->GetUpperBoundStrConst();
             uint32 caseTargetOffset = m_caseNodes->Item(i)->GetTargetOffset();
             multiBranchInstr->AddtoDictionary(caseTargetOffset, str);
         }
@@ -871,7 +878,7 @@ SwitchIRBuilder::BuildMultiBrCaseInstrForStrings(uint32 targetOffset)
         //Adding normal cases to the instruction (except the default case, which we do it later)
         for (uint i = 0; i < caseCount; i++)
         {
-            Js::JavascriptString * str = m_caseNodes->Item(i)->GetSrc2StringConst();
+            Js::JavascriptString * str = m_caseNodes->Item(i)->GetUpperBoundStrConst();
             Assert(str->GetLength() == 1);
             uint32 caseTargetOffset = m_caseNodes->Item(i)->GetTargetOffset();
             multiBranchInstr->AddtoJumpTable(caseTargetOffset, str->GetString()[0] - minChar);
@@ -907,8 +914,8 @@ SwitchIRBuilder::BuildMultiBrCaseInstrForInts(uint32 start, uint32 end, uint32 t
 
     uint32 lastCaseOffset = m_caseNodes->Item(end)->GetOffset();
 
-    int32 baseCaseValue = m_caseNodes->Item(start)->GetLowerBound()->GetStackSym()->GetIntConstValue();
-    int32 lastCaseValue = m_caseNodes->Item(end)->GetUpperBound()->GetStackSym()->GetIntConstValue();
+    int32 baseCaseValue = m_caseNodes->Item(start)->GetLowerBoundIntConst();
+    int32 lastCaseValue = m_caseNodes->Item(end)->GetUpperBoundIntConst();
 
     multiBranchInstr->m_baseCaseValue = baseCaseValue;
     multiBranchInstr->m_lastCaseValue = lastCaseValue;
@@ -923,10 +930,10 @@ SwitchIRBuilder::BuildMultiBrCaseInstrForInts(uint32 start, uint32 end, uint32 t
 
     for (int jmpIndex = jmpTableSize - 1; jmpIndex >= 0; jmpIndex--)
     {
-        if (caseIndex >= 0 && jmpIndex == m_caseNodes->Item(caseIndex)->GetSrc2IntConst() - baseCaseValue)
+        if (caseIndex >= 0 && jmpIndex == m_caseNodes->Item(caseIndex)->GetUpperBoundIntConst() - baseCaseValue)
         {
-            lowerBoundCaseConstValue = m_caseNodes->Item(caseIndex)->GetLowerBound()->GetStackSym()->GetIntConstValue();
-            upperBoundCaseConstValue = m_caseNodes->Item(caseIndex)->GetUpperBound()->GetStackSym()->GetIntConstValue();
+            lowerBoundCaseConstValue = m_caseNodes->Item(caseIndex)->GetLowerBoundIntConst();
+            upperBoundCaseConstValue = m_caseNodes->Item(caseIndex)->GetUpperBoundIntConst();
             caseTargetOffset = m_caseNodes->Item(caseIndex--)->GetTargetOffset();
             multiBranchInstr->AddtoJumpTable(caseTargetOffset, jmpIndex);
         }

+ 1 - 1
lib/Backend/SwitchIRBuilder.h

@@ -100,7 +100,7 @@ public:
     void                BeginSwitch();
     void                EndSwitch(uint32 offset, uint32 targetOffset);
     void                SetProfiledInstruction(IR::Instr * instr, Js::ProfileId profileId);
-    void                OnCase(IR::RegOpnd * src1Opnd, IR::RegOpnd * src2Opnd, uint32 offset, uint32 targetOffset);
+    void                OnCase(IR::RegOpnd * src1Opnd, IR::Opnd * src2Opnd, uint32 offset, uint32 targetOffset);
     void                FlushCases(uint32 targetOffset);
 
     void                RefineCaseNodes();

+ 8 - 0
lib/Runtime/ByteCode/AsmJsByteCodeDumper.cpp

@@ -936,6 +936,14 @@ namespace Js
         DumpIntReg(data->I2);
     }
 
+    template <class T>
+    void AsmJsByteCodeDumper::DumpBrInt1Const1(OpCodeAsmJs op, const unaligned T * data, FunctionBody * dumpFunction, ByteCodeReader& reader)
+    {
+        DumpOffset(data->RelativeJumpOffset, reader);
+        DumpIntReg(data->I1);
+        DumpI4(data->C1);
+    }
+
     // Float32x4
     template <class T>
     void AsmJsByteCodeDumper::DumpFloat32x4_2(OpCodeAsmJs op, const unaligned T * data, FunctionBody * dumpFunction, ByteCodeReader& reader)

+ 23 - 0
lib/Runtime/ByteCode/AsmJsByteCodeWriter.cpp

@@ -372,6 +372,21 @@ namespace Js
         return false;
     }
 
+    template <typename SizePolicy>
+    bool AsmJsByteCodeWriter::TryWriteAsmBrReg1Const1(OpCodeAsmJs op, ByteCodeLabel labelID, RegSlot R1, int C1)
+    {
+        OpLayoutT_BrInt1Const1<SizePolicy> layout;
+        if (SizePolicy::Assign(layout.I1, R1) && SizePolicy::Assign(layout.C1, C1))
+        {
+            size_t const offsetOfRelativeJumpOffsetFromEnd = sizeof(OpLayoutT_BrInt1Const1<SizePolicy>) - offsetof(OpLayoutT_BrInt1Const1<SizePolicy>, RelativeJumpOffset);
+            layout.RelativeJumpOffset = offsetOfRelativeJumpOffsetFromEnd;
+            m_byteCodeData.EncodeT<SizePolicy::LayoutEnum>(op, &layout, sizeof(layout), this);
+            AddJumpOffset(op, labelID, offsetOfRelativeJumpOffsetFromEnd);
+            return true;
+        }
+        return false;
+    }
+
     template <typename SizePolicy>
     bool AsmJsByteCodeWriter::TryWriteAsmCall(OpCodeAsmJs op, RegSlot returnValueRegister, RegSlot functionRegister, ArgSlot givenArgCount, AsmJsRetType retType)
     {
@@ -545,6 +560,14 @@ namespace Js
         MULTISIZE_LAYOUT_WRITE(AsmBrReg2, op, labelID, R1, R2);
     }
 
+    void AsmJsByteCodeWriter::AsmBrReg1Const1(OpCodeAsmJs op, ByteCodeLabel labelID, RegSlot R1, int C1)
+    {
+        CheckOpen();
+        CheckLabel(labelID);
+
+        MULTISIZE_LAYOUT_WRITE(AsmBrReg1Const1, op, labelID, R1, C1);
+    }
+
     void AsmJsByteCodeWriter::AsmStartCall(OpCodeAsmJs op, ArgSlot ArgCount, bool isPatching)
     {
         CheckOpen();

+ 2 - 0
lib/Runtime/ByteCode/AsmJsByteCodeWriter.h

@@ -39,6 +39,7 @@ namespace Js
         void AsmBr           ( ByteCodeLabel labelID, OpCodeAsmJs op = OpCodeAsmJs::AsmBr );
         void AsmBrReg1       ( OpCodeAsmJs op, ByteCodeLabel labelID, RegSlot R1 );
         void AsmBrReg2       ( OpCodeAsmJs op, ByteCodeLabel labelID, RegSlot R1, RegSlot R2 );
+        void AsmBrReg1Const1 ( OpCodeAsmJs op, ByteCodeLabel labelID, RegSlot R1, int C1 );
         void AsmStartCall    ( OpCodeAsmJs op, ArgSlot ArgCount, bool isPatching = false);
         void AsmCall         ( OpCodeAsmJs op, RegSlot returnValueRegister, RegSlot functionRegister, ArgSlot givenArgCount, AsmJsRetType retType );
         void AsmSlot         ( OpCodeAsmJs op, RegSlot value, RegSlot instance, int32 slotId );
@@ -74,6 +75,7 @@ namespace Js
         template <typename SizePolicy> bool TryWriteDouble1Const1   ( OpCodeAsmJs op, RegSlot R0, double C1 );
         template <typename SizePolicy> bool TryWriteAsmBrReg1       ( OpCodeAsmJs op, ByteCodeLabel labelID, RegSlot R1 );
         template <typename SizePolicy> bool TryWriteAsmBrReg2       ( OpCodeAsmJs op, ByteCodeLabel labelID, RegSlot R1, RegSlot R2 );
+        template <typename SizePolicy> bool TryWriteAsmBrReg1Const1 ( OpCodeAsmJs op, ByteCodeLabel labelID, RegSlot R1, int C1 );
         template <typename SizePolicy> bool TryWriteAsmCall         ( OpCodeAsmJs op, RegSlot returnValueRegister, RegSlot functionRegister, ArgSlot givenArgCount, AsmJsRetType retType );
         template <typename SizePolicy> bool TryWriteAsmSlot         ( OpCodeAsmJs op, RegSlot value, RegSlot instance, int32 slotId );
         template <typename SizePolicy> bool TryWriteAsmTypedArr     ( OpCodeAsmJs op, RegSlot value, uint32 slotIndex, ArrayBufferView::ViewType viewType );

+ 1 - 0
lib/Runtime/ByteCode/ByteCodeSerializer.cpp

@@ -860,6 +860,7 @@ public:
                 DEFAULT_LAYOUT(AsmBr);
                 DEFAULT_LAYOUT_WITH_ONEBYTE(BrInt1);
                 DEFAULT_LAYOUT_WITH_ONEBYTE(BrInt2);
+                DEFAULT_LAYOUT_WITH_ONEBYTE(BrInt1Const1);
                 //Float32x4
                 DEFAULT_LAYOUT_WITH_ONEBYTE(Float32x4_2);
                 DEFAULT_LAYOUT_WITH_ONEBYTE(Float32x4_3);

+ 2 - 1
lib/Runtime/ByteCode/LayoutTypesAsmJs.h

@@ -88,7 +88,8 @@ LAYOUT_TYPE_WMS     ( Float1Int1       ) // 2 double register
 LAYOUT_TYPE_WMS     ( Double3          ) // 3 double register
 LAYOUT_TYPE_WMS     ( BrInt1           ) // Conditional branching with 1 int
 LAYOUT_TYPE_WMS     ( BrInt2           ) // Conditional branching with 2 int
-LAYOUT_TYPE_WMS     ( AsmUnsigned1     ) // Conditional branching with 2 int
+LAYOUT_TYPE_WMS     ( BrInt1Const1     ) // Conditional branching with 1 int and 1 const
+LAYOUT_TYPE_WMS     ( AsmUnsigned1     ) // 1 unsigned int register
 
 // Float32x4
 LAYOUT_TYPE_WMS     ( Float32x4_2                       )

+ 1 - 0
lib/Runtime/ByteCode/OpCodesAsmJs.h

@@ -77,6 +77,7 @@ MACRO_WMS   ( BrEq_Int                  , BrInt2       , None            ) // Ju
 MACRO_WMS   ( BeginSwitch_Int           , Int2         , None            ) // Start of an integer switch statement, same function as Ld_Int
 MACRO       ( EndSwitch_Int             , AsmBr        , OpNoFallThrough ) // End of an integer switch statement, jumps to default case or past end of switch
 MACRO_WMS   ( Case_Int                  , BrInt2       , None            ) // Integer branch, same function as BrInt2
+MACRO_WMS   ( Case_IntConst             , BrInt1Const1 , None            ) // Integer branch with inline const, same function as BrInt2
 
 // Type conversion
 MACRO_WMS   ( Reinterpret_ITF           , Float1Int1   , None            ) // reinterpret bits of int to float

+ 7 - 0
lib/Runtime/ByteCode/OpLayoutsAsmJs.h

@@ -453,6 +453,13 @@ namespace Js
         typename SizePolicy::RegSlotType     I2;
     };
 
+    template <typename SizePolicy>
+    struct OpLayoutT_BrInt1Const1
+    {
+        int32  RelativeJumpOffset;
+        typename SizePolicy::RegSlotType     I1;
+        int32     C1;
+    };
 
     /* Float32x4 layouts */
     //--------------------

+ 1 - 0
lib/Runtime/Language/AsmJsEncoder.h

@@ -70,6 +70,7 @@ namespace Js
         template <class T> void OP_LdUndef( const unaligned T* playout );
         template <class T> void OP_Br( const unaligned T* playout );
         template <class T> void OP_BrEq( const unaligned T* playout );
+        template <class T> void OP_BrEqConst( const unaligned T* playout );
         template <class T> void OP_BrTrue( const unaligned T* playout );
         template <class T> void OP_Empty( const unaligned T* playout );
         template <class T> void Op_LdSlot_Db( const unaligned T* playout );

+ 17 - 0
lib/Runtime/Language/AsmJsEncoder.inl

@@ -232,6 +232,23 @@ namespace Js
         }
     }
 
+    template <class T>
+    void AsmJsEncoder::OP_BrEqConst(const unaligned T* playout)
+    {
+        if (playout->RelativeJumpOffset)
+        {
+            const int labelOffset = mReader.GetCurrentOffset() + playout->RelativeJumpOffset;
+            Assert(playout->RelativeJumpOffset > 0 || mRelocLabelMap->ContainsKey(labelOffset));
+            bool isBackEdge = false;
+            if (playout->RelativeJumpOffset < 0)
+                isBackEdge = true;
+            BYTE* relocAddr = nullptr;
+            AsmJsJitTemplate::BrEq::ApplyTemplate(this, mPc, CalculateOffset<int>(playout->I1), playout->C1, &relocAddr, isBackEdge, true);
+            Assert(relocAddr);
+            AddReloc(labelOffset, relocAddr);
+        }
+    }
+
     template <class T>
     void Js::AsmJsEncoder::Op_LdConst_Int( const unaligned T* playout )
     {

+ 1 - 0
lib/Runtime/Language/AsmJsEncoderHandler.inl

@@ -73,6 +73,7 @@ EXDEF3    ( CUSTOM     , NopEx             , OP_Empty                , Empty
   DEF3_WMS( INT2       , BeginSwitch_Int   , Ld_Int                  , Int2          )
   DEF3    ( CUSTOM     , EndSwitch_Int     , OP_Br                   , AsmBr         )
   DEF3_WMS( CUSTOM     , Case_Int          , OP_BrEq                 , BrInt2        )
+  DEF3_WMS( CUSTOM     , Case_IntConst     , OP_BrEqConst            , BrInt1Const1  )
 
   DEF3_WMS( CUSTOM     , Conv_DTI          , Op_Db_To_Int            , Int1Double1   )
   DEF3_WMS( CUSTOM     , Conv_ITD          , Op_Int_To_Db            , Double1Int1   )

+ 1 - 1
lib/Runtime/Language/AsmJsJitTemplate.h

@@ -51,7 +51,7 @@ namespace Js
         CreateTemplate( FunctionExit );
         CreateTemplate( Br, BYTE** relocAddr, bool isBackEdge);
         CreateTemplate( BrTrue, int offset, BYTE** relocAddr, bool isBackEdge);
-        CreateTemplate( BrEq, int leftOffset, int rightOffset, BYTE** relocAddr, bool isBackEdge);
+        CreateTemplate( BrEq, int leftOffset, int rightOffset, BYTE** relocAddr, bool isBackEdge, bool isSrc2Const = false);
         CreateTemplate( Label );
         CreateTemplate( LdUndef, int targetOffset );
         CreateTemplate( LdSlot, int targetOffset, int arrOffset, int slotIndex );

+ 1 - 0
lib/Runtime/Language/InterpreterHandlerAsmJs.inl

@@ -76,6 +76,7 @@ EXDEF2    (NOPASMJS          , NopEx        , Empty
   DEF2_WMS( I1toI1Mem        , BeginSwitch_Int, (int)                                            )
   DEF2    ( BR_ASM           , EndSwitch_Int, OP_Br                                              )
   DEF2_WMS( BR_ASM_Mem       , Case_Int     , AsmJsMath::CmpEq<int>                              )
+  DEF2_WMS( BR_ASM_Const     , Case_IntConst, AsmJsMath::CmpEq<int>                              )
 
   DEF2_WMS( I1toI1Mem        , Neg_Int      , AsmJsMath::Neg<int>                                ) // int unary '-'
   DEF2_WMS( I1toI1Mem        , Not_Int      , AsmJsMath::Not                                     ) // int unary '~'

+ 12 - 0
lib/Runtime/Language/InterpreterProcessOpCodeAsmJs.h

@@ -409,6 +409,18 @@ if (switchProfileMode) \
     }
 #define PROCESS_BR_ASM_Mem(name, func) PROCESS_BR_ASM_Mem_COMMON(name, func,)
 
+#define PROCESS_BR_ASM_Const_COMMON(name, func,suffix) \
+    case OpCodeAsmJs::name: \
+    { \
+        PROCESS_READ_LAYOUT_ASMJS(name, BrInt1Const1, suffix); \
+        if (func(GetRegRawInt(playout->I1), playout->C1)) \
+        { \
+            ip = m_reader.SetCurrentRelativeOffset(ip, playout->RelativeJumpOffset); \
+        } \
+        break; \
+    }
+#define PROCESS_BR_ASM_Const(name, func) PROCESS_BR_ASM_Const_COMMON(name, func,)
+
 #define PROCESS_BR_ASM_MemStack_COMMON(name, func,suffix) \
     case OpCodeAsmJs::name: \
     { \

+ 20 - 6
lib/Runtime/Language/i386/AsmJsJitTemplate.cpp

@@ -1391,12 +1391,15 @@ namespace Js
             return size;
         }
 
-        int BrEq::ApplyTemplate(TemplateContext context, BYTE*& buffer, int leftOffset, int rightOffset, BYTE** relocAddr, bool isBackEdge)
+        int BrEq::ApplyTemplate(TemplateContext context, BYTE*& buffer, int leftOffset, int rightOffset, BYTE** relocAddr, bool isBackEdge, bool isSrc2Const /*= false*/)
         {
             X86TemplateData* templateData = GetTemplateData( context );
             int size = 0;
             leftOffset -= templateData->GetBaseOffSet();
-            rightOffset -= templateData->GetBaseOffSet();
+            if (!isSrc2Const) 
+            {
+                rightOffset -= templateData->GetBaseOffSet();
+            }
             if (isBackEdge)
             {
                 RegNum regInc = templateData->GetReg<int>(0);
@@ -1404,9 +1407,9 @@ namespace Js
                 size += INC::EncodeInstruction<int>(buffer, InstrParamsAddr(regInc, context->GetFunctionBody()->GetAsmJsTotalLoopCountOffset()));
                 templateData->InvalidateReg(regInc);
             }
-            RegNum reg1, reg2;
+            RegNum reg1, reg2 = RegEAX;
             const int reg1Found = templateData->FindRegWithStackOffset<int>( reg1, leftOffset );
-            const int reg2Found = templateData->FindRegWithStackOffset<int>( reg2, rightOffset );
+            const int reg2Found = isSrc2Const || templateData->FindRegWithStackOffset<int>( reg2, rightOffset );
             switch( reg1Found & (reg2Found<<1) )
             {
             case 0:
@@ -1419,10 +1422,21 @@ namespace Js
                 size += CMP::EncodeInstruction<int32>( buffer, InstrParamsRegAddr( reg1, RegEBP, rightOffset ) );
                 break;
             case 2:
-                size += CMP::EncodeInstruction<int32>( buffer, InstrParamsRegAddr( reg2, RegEBP, leftOffset ) );
+                if (isSrc2Const) 
+                {
+                    size += CMP::EncodeInstruction<int32>(buffer, InstrParamsAddrImm<int32>(RegEBP, leftOffset, rightOffset));
+                }
+                else 
+                {
+                    size += CMP::EncodeInstruction<int32>(buffer, InstrParamsRegAddr(reg2, RegEBP, leftOffset));
+                }
                 break;
             case 3:
-                if( reg1 == reg2 )
+                if (isSrc2Const)
+                {
+                    size += CMP::EncodeInstruction<int32>(buffer, InstrParamsRegImm<int32>(reg1, rightOffset));
+                }
+                else if( reg1 == reg2 )
                 {
                     templateData->InvalidateAllReg();
                     *relocAddr = buffer;

+ 1 - 4
lib/WasmReader/WasmByteCodeGenerator.cpp

@@ -898,11 +898,8 @@ WasmBytecodeGenerator::EmitBrTable()
     for (uint i = 0; i < numTargets; i++)
     {
         uint target = targetTable[i];
-        Js::RegSlot caseLoc = m_i32RegSlots->AcquireTmpRegister();
-        m_writer.AsmInt1Const1(Js::OpCodeAsmJs::Ld_IntConst, caseLoc, i);
         Js::ByteCodeLabel targetLabel = GetLabel(target);
-        m_writer.AsmBrReg2(Js::OpCodeAsmJs::Case_Int, targetLabel, scrutineeInfo.location, caseLoc);
-        m_i32RegSlots->ReleaseTmpRegister(caseLoc);
+        m_writer.AsmBrReg1Const1(Js::OpCodeAsmJs::Case_IntConst, targetLabel, scrutineeInfo.location, i);
     }
     m_i32RegSlots->ReleaseTmpRegister(scrutineeInfo.location);