// StackSym::GetIntConstValue int32 StackSym::GetIntConstValue() const { Assert(this->IsIntConst()); IR::Instr *defInstr = this->m_instrDef; IR::Opnd *src1 = defInstr->GetSrc1(); if (src1->IsIntConstOpnd()) { Assert(defInstr->m_opcode == Js::OpCode::Ld_I4 || defInstr->m_opcode == Js::OpCode::LdC_A_I4 || defInstr->m_opcode == Js::OpCode::ArgOut_A_InlineBuiltIn || LowererMD::IsAssign(defInstr)); return src1->AsIntConstOpnd()->AsInt32(); } if (src1->IsAddrOpnd()) { Assert(defInstr->m_opcode == Js::OpCode::Ld_A || LowererMD::IsAssign(defInstr) || defInstr->m_opcode == Js::OpCode::ArgOut_A_InlineBuiltIn); IR::AddrOpnd *addr = src1->AsAddrOpnd(); Assert(addr->IsVar()); Js::Var var = src1->AsAddrOpnd()->m_address; if (Js::TaggedInt::Is(var)) { return Js::TaggedInt::ToInt32(var); } int32 value; const bool isInt32 = Js::JavascriptNumber::TryGetInt32Value(Js::JavascriptNumber::GetValue(var), &value); Assert(isInt32); return value; } Assert(src1->IsRegOpnd()); return src1->AsRegOpnd()->m_sym->GetIntConstValue(); }
BailoutConstantValue StackSym::GetConstValueForBailout() const { Assert(this->IsConst()); IR::Instr *defInstr = this->m_instrDef; IR::Opnd *src1 = defInstr->GetSrc1(); return src1->GetConstValue(); }
IR::Opnd * StackSym::GetConstOpnd() const { Assert(IsConst()); IR::Instr *defInstr = this->m_instrDef; IR::Opnd *src1 = defInstr->GetSrc1(); if (!src1) { #if defined(_M_IX86) || defined(_M_X64) Assert(defInstr->m_opcode == Js::OpCode::MOVSD_ZERO); #else Assert(UNREACHED); #endif } else if (src1->IsIntConstOpnd()) { Assert(this->IsIntConst()); if (defInstr->m_opcode == Js::OpCode::LdC_A_I4) { src1 = IR::AddrOpnd::NewFromNumber(src1->AsIntConstOpnd()->GetValue(), defInstr->m_func); defInstr->ReplaceSrc1(src1); defInstr->m_opcode = Js::OpCode::Ld_A; } else { Assert(defInstr->m_opcode == Js::OpCode::Ld_I4 || LowererMD::IsAssign(defInstr) || defInstr->m_opcode == Js::OpCode::ArgOut_A_InlineBuiltIn); } } else if (src1->IsFloatConstOpnd()) { Assert(this->IsFloatConst()); Assert(defInstr->m_opcode == Js::OpCode::LdC_A_R8); src1 = src1->AsFloatConstOpnd()->GetAddrOpnd(defInstr->m_func); defInstr->ReplaceSrc1(src1); defInstr->m_opcode = Js::OpCode::Ld_A; } else if (src1->IsAddrOpnd()) { Assert(defInstr->m_opcode == Js::OpCode::Ld_A || LowererMD::IsAssign(defInstr) || defInstr->m_opcode == Js::OpCode::ArgOut_A_InlineBuiltIn); } else if (src1->IsMemRefOpnd()) { Assert(this->IsFloatConst() || this->IsSimd128Const()); } else { AssertMsg(UNREACHED, "Invalid const value"); } return src1; }
Js::Var StackSym::GetFloatConstValueAsVar_PostGlobOpt() const { Assert(this->IsConst()); IR::Instr *defInstr = this->m_instrDef; IR::Opnd *src1 = defInstr->GetSrc1(); StackSym * stackSym = nullptr; if (src1->IsRegOpnd()) { stackSym = src1->AsRegOpnd()->m_sym; Assert(!stackSym->m_isEncodedConstant); if (stackSym->m_isSingleDef) { //In ARM constant load is always legalized. Try to get the constant from src def defInstr = stackSym->m_instrDef; src1 = defInstr->GetSrc1(); } } Assert(this->IsFloatConst() || (stackSym && stackSym->IsFloatConst())); IR::AddrOpnd *addrOpnd; if (src1->IsAddrOpnd()) { Assert(defInstr->m_opcode == Js::OpCode::Ld_A); addrOpnd = src1->AsAddrOpnd(); } else { Assert(src1->IsFloatConstOpnd()); Assert(defInstr->m_opcode == Js::OpCode::LdC_A_R8); addrOpnd = src1->AsFloatConstOpnd()->GetAddrOpnd(defInstr->m_func); // This is just to prevent creating multiple numbers when the sym is used multiple times. We can only do this // post-GlobOpt, as otherwise it violates some invariants assumed in GlobOpt. defInstr->ReplaceSrc1(addrOpnd); defInstr->m_opcode = Js::OpCode::Ld_A; } const Js::Var address = addrOpnd->m_address; Assert(Js::JavascriptNumber::Is(address)); return address; }
void SwitchIRBuilder::FixUpMultiBrJumpTable(IR::MultiBranchInstr * multiBranchInstr, uint32 targetOffset) { multiBranchInstr->FixMultiBrDefaultTarget(targetOffset); uint32 offset = multiBranchInstr->GetByteCodeOffset(); IR::Instr * subInstr = multiBranchInstr->GetPrevRealInstr(); IR::Instr * upperBoundCheckInstr = subInstr->GetPrevRealInstr(); IR::Instr * lowerBoundCheckInstr = upperBoundCheckInstr->GetPrevRealInstr(); AssertMsg(subInstr->m_opcode == m_subOp, "Missing Offset Calculation instruction"); AssertMsg(upperBoundCheckInstr->IsBranchInstr() && lowerBoundCheckInstr->IsBranchInstr(), "Invalid boundary check instructions"); AssertMsg(upperBoundCheckInstr->m_opcode == m_gtOp && lowerBoundCheckInstr->m_opcode == m_ltOp, "Invalid boundary check instructions"); m_adapter->CreateRelocRecord(upperBoundCheckInstr->AsBranchInstr(), offset, targetOffset, true); m_adapter->CreateRelocRecord(lowerBoundCheckInstr->AsBranchInstr(), offset, targetOffset, true); }
void *StackSym::GetConstAddress() const { Assert(this->IsConst()); IR::Instr *defInstr = this->m_instrDef; IR::Opnd *src1 = defInstr->GetSrc1(); StackSym * stackSym = nullptr; if (src1->IsRegOpnd()) { stackSym = src1->AsRegOpnd()->m_sym; Assert(!stackSym->m_isEncodedConstant); if (stackSym->m_isSingleDef) { //In ARM constant load is always legalized. Try to get the constant from src def defInstr = stackSym->m_instrDef; src1 = defInstr->GetSrc1(); } } Assert(src1->IsAddrOpnd()); Assert(defInstr->m_opcode == Js::OpCode::Ld_A || defInstr->m_opcode == Js::OpCode::LdStr || defInstr->m_opcode == Js::OpCode::ArgOut_A_InlineBuiltIn || LowererMD::IsAssign(defInstr)); return src1->AsAddrOpnd()->m_address; }
bool GlobOpt::Simd128DoTypeSpec(IR::Instr *instr, const Value *src1Val, const Value *src2Val, const Value *dstVal) { bool doTypeSpec = true; // TODO: Some operations require additional opnd constraints (e.g. shuffle/swizzle). if (Js::IsSimd128Opcode(instr->m_opcode)) { ThreadContext::SimdFuncSignature simdFuncSignature; instr->m_func->GetScriptContext()->GetThreadContext()->GetSimdFuncSignatureFromOpcode(instr->m_opcode, simdFuncSignature); if (!simdFuncSignature.valid) { // not implemented yet. return false; } // special handling for Load/Store if (Js::IsSimd128Load(instr->m_opcode) || Js::IsSimd128Store(instr->m_opcode)) { return Simd128DoTypeSpecLoadStore(instr, src1Val, src2Val, dstVal, &simdFuncSignature); } const uint argCount = simdFuncSignature.argCount; switch (argCount) { case 2: Assert(src2Val); doTypeSpec = doTypeSpec && Simd128CanTypeSpecOpnd(src2Val->GetValueInfo()->Type(), simdFuncSignature.args[1]) && Simd128ValidateIfLaneIndex(instr, instr->GetSrc2(), 1); // fall-through case 1: Assert(src1Val); doTypeSpec = doTypeSpec && Simd128CanTypeSpecOpnd(src1Val->GetValueInfo()->Type(), simdFuncSignature.args[0]) && Simd128ValidateIfLaneIndex(instr, instr->GetSrc1(), 0); break; default: { // extended args Assert(argCount > 2); // Check if all args have been type specialized. int arg = argCount - 1; IR::Instr * eaInstr = GetExtendedArg(instr); while (arg>=0) { Assert(eaInstr); Assert(eaInstr->m_opcode == Js::OpCode::ExtendArg_A); ValueType expectedType = simdFuncSignature.args[arg]; IR::Opnd * opnd = eaInstr->GetSrc1(); StackSym * sym = opnd->GetStackSym(); // In Forward Prepass: Check liveness through liveness bits, not IR type, since in prepass no actual type-spec happens. // In the Forward Pass: Check IRType since Sym can be null, because of const prop. if (expectedType.IsSimd128Float32x4()) { if (sym && !IsSimd128F4TypeSpecialized(sym, ¤tBlock->globOptData) || !sym && opnd->GetType() != TySimd128F4) { return false; } } else if (expectedType.IsSimd128Int32x4()) { if (sym && !IsSimd128I4TypeSpecialized(sym, ¤tBlock->globOptData) || !sym && opnd->GetType() != TySimd128I4) { return false; } } else if (expectedType.IsFloat()) { if (sym && !IsFloat64TypeSpecialized(sym, ¤tBlock->globOptData) || !sym&& opnd->GetType() != TyFloat64) { return false; } } else if (expectedType.IsInt()) { if ((sym && !IsInt32TypeSpecialized(sym, ¤tBlock->globOptData) && !currentBlock->globOptData.liveLossyInt32Syms->Test(sym->m_id)) || !sym && opnd->GetType() != TyInt32) { return false; } // Extra check if arg is a lane index if (!Simd128ValidateIfLaneIndex(instr, opnd, arg)) { return false; } } else { Assert(UNREACHED); } eaInstr = GetExtendedArg(eaInstr); arg--; } // all args are type-spec'd doTypeSpec = true; } } } else { Assert(instr->m_opcode == Js::OpCode::ExtendArg_A); // For ExtendArg, the expected type is encoded in the dst(link) operand. doTypeSpec = doTypeSpec && Simd128CanTypeSpecOpnd(src1Val->GetValueInfo()->Type(), instr->GetDst()->GetValueType()); } return doTypeSpec; }
/* Handles all Simd128 type-spec of an instr, if possible. */ bool GlobOpt::TypeSpecializeSimd128( IR::Instr *instr, Value **pSrc1Val, Value **pSrc2Val, Value **pDstVal ) { if (this->GetIsAsmJSFunc() || SIMD128_TYPE_SPEC_FLAG == false) { // no type-spec for ASMJS code or flag is off. return false; } switch (instr->m_opcode) { case Js::OpCode::ArgOut_A_InlineBuiltIn: if (instr->GetSrc1()->IsRegOpnd()) { StackSym *sym = instr->GetSrc1()->AsRegOpnd()->m_sym; if (IsSimd128TypeSpecialized(sym, this->currentBlock)) { ValueType valueType = (*pSrc1Val)->GetValueInfo()->Type(); Assert(valueType.IsSimd128()); ToTypeSpecUse(instr, instr->GetSrc1(), this->currentBlock, *pSrc1Val, nullptr, GetIRTypeFromValueType(valueType), GetBailOutKindFromValueType(valueType)); return true; } } return false; case Js::OpCode::Ld_A: if (instr->GetSrc1()->IsRegOpnd()) { StackSym *sym = instr->GetSrc1()->AsRegOpnd()->m_sym; IRType type = TyIllegal; if (IsSimd128F4TypeSpecialized(sym, this->currentBlock)) { type = TySimd128F4; } else if (IsSimd128I4TypeSpecialized(sym, this->currentBlock)) { type = TySimd128I4; } else { return false; } ToTypeSpecUse(instr, instr->GetSrc1(), this->currentBlock, *pSrc1Val, nullptr, type, IR::BailOutSimd128F4Only /*not used for Ld_A*/); TypeSpecializeSimd128Dst(type, instr, *pSrc1Val, *pSrc1Val, pDstVal); return true; } return false; case Js::OpCode::ExtendArg_A: if (Simd128DoTypeSpec(instr, *pSrc1Val, *pSrc2Val, *pDstVal)) { Assert(instr->m_opcode == Js::OpCode::ExtendArg_A); Assert(instr->GetDst()->GetType() == TyVar); ValueType valueType = instr->GetDst()->GetValueType(); // Type-spec src1 only based on dst type. Dst type is set by the inliner based on func signature. ToTypeSpecUse(instr, instr->GetSrc1(), this->currentBlock, *pSrc1Val, nullptr, GetIRTypeFromValueType(valueType), GetBailOutKindFromValueType(valueType), true /*lossy*/); ToVarRegOpnd(instr->GetDst()->AsRegOpnd(), this->currentBlock); return true; } return false; } if (!Js::IsSimd128Opcode(instr->m_opcode)) { return false; } // Simd instr if (Simd128DoTypeSpec(instr, *pSrc1Val, *pSrc2Val, *pDstVal)) { ThreadContext::SimdFuncSignature simdFuncSignature; instr->m_func->GetScriptContext()->GetThreadContext()->GetSimdFuncSignatureFromOpcode(instr->m_opcode, simdFuncSignature); // type-spec logic // special handling for load/store // OptArraySrc will type-spec the array and the index. We type-spec the value here. if (Js::IsSimd128Load(instr->m_opcode)) { TypeSpecializeSimd128Dst(GetIRTypeFromValueType(simdFuncSignature.returnType), instr, nullptr, *pSrc1Val, pDstVal); Simd128SetIndirOpndType(instr->GetSrc1()->AsIndirOpnd(), instr->m_opcode); return true; } if (Js::IsSimd128Store(instr->m_opcode)) { ToTypeSpecUse(instr, instr->GetSrc1(), this->currentBlock, *pSrc1Val, nullptr, GetIRTypeFromValueType(simdFuncSignature.args[2]), GetBailOutKindFromValueType(simdFuncSignature.args[2])); Simd128SetIndirOpndType(instr->GetDst()->AsIndirOpnd(), instr->m_opcode); return true; } // For op with ExtendArg. All sources are already type-specialized, just type-specialize dst if (simdFuncSignature.argCount <= 2) { Assert(instr->GetSrc1()); ToTypeSpecUse(instr, instr->GetSrc1(), this->currentBlock, *pSrc1Val, nullptr, GetIRTypeFromValueType(simdFuncSignature.args[0]), GetBailOutKindFromValueType(simdFuncSignature.args[0])); if (instr->GetSrc2()) { ToTypeSpecUse(instr, instr->GetSrc2(), this->currentBlock, *pSrc2Val, nullptr, GetIRTypeFromValueType(simdFuncSignature.args[1]), GetBailOutKindFromValueType(simdFuncSignature.args[1])); } } if (instr->GetDst()) { TypeSpecializeSimd128Dst(GetIRTypeFromValueType(simdFuncSignature.returnType), instr, nullptr, *pSrc1Val, pDstVal); } return true; } else { // We didn't type-spec if (!IsLoopPrePass()) { // Emit bailout if not loop prepass. // The inliner inserts bytecodeUses of original args after the instruction. Bailout is safe. IR::Instr * bailoutInstr = IR::BailOutInstr::New(Js::OpCode::BailOnNoSimdTypeSpec, IR::BailOutNoSimdTypeSpec, instr, instr->m_func); bailoutInstr->SetByteCodeOffset(instr); instr->InsertAfter(bailoutInstr); instr->m_opcode = Js::OpCode::Nop; if (instr->GetSrc1()) { instr->FreeSrc1(); if (instr->GetSrc2()) { instr->FreeSrc2(); } } if (instr->GetDst()) { instr->FreeDst(); } if (this->byteCodeUses) { // All inlined SIMD ops have jitOptimizedReg srcs Assert(this->byteCodeUses->IsEmpty()); JitAdelete(this->alloc, this->byteCodeUses); this->byteCodeUses = nullptr; } RemoveCodeAfterNoFallthroughInstr(bailoutInstr); return true; } } return false; }
intptr_t StackSym::GetLiteralConstValue_PostGlobOpt() const { Assert(this->IsConst()); IR::Instr *defInstr = this->m_instrDef; IR::Opnd *src1 = defInstr->GetSrc1(); StackSym * stackSym = nullptr; if (src1->IsRegOpnd()) { stackSym = src1->AsRegOpnd()->m_sym; if (stackSym->m_isEncodedConstant) { Assert(!stackSym->m_isSingleDef); Assert(LowererMD::IsAssign(defInstr) || defInstr->m_opcode == Js::OpCode::ArgOut_A_InlineBuiltIn); return stackSym->constantValue; } if (stackSym->m_isSingleDef) { //In ARM constant load is always legalized. Try to get the constant from src def defInstr = stackSym->m_instrDef; src1 = defInstr->GetSrc1(); } } if (src1->IsAddrOpnd()) { Assert(defInstr->m_opcode == Js::OpCode::Ld_A || defInstr->m_opcode == Js::OpCode::LdStr || defInstr->m_opcode == Js::OpCode::ArgOut_A_InlineBuiltIn || LowererMD::IsAssign(defInstr)); return reinterpret_cast<intptr_t>(src1->AsAddrOpnd()->m_address); } if (src1->IsIntConstOpnd()) { Assert(this->IsIntConst() || (stackSym && stackSym->IsIntConst())); if (defInstr->m_opcode == Js::OpCode::LdC_A_I4) { IR::AddrOpnd *const addrOpnd = IR::AddrOpnd::NewFromNumber(src1->AsIntConstOpnd()->GetValue(), defInstr->m_func); const Js::Var address = addrOpnd->m_address; // This is just to prevent creating multiple numbers when the sym is used multiple times. We can only do this // post-GlobOpt, as otherwise it violates some invariants assumed in GlobOpt. defInstr->ReplaceSrc1(addrOpnd); defInstr->m_opcode = Js::OpCode::Ld_A; return reinterpret_cast<intptr_t>(address); } Assert(defInstr->m_opcode == Js::OpCode::Ld_I4 || LowererMD::IsAssign(defInstr) || defInstr->m_opcode == Js::OpCode::ArgOut_A_InlineBuiltIn); return src1->AsIntConstOpnd()->GetValue(); } if (src1->IsFloatConstOpnd()) { Assert(this->IsFloatConst() || (stackSym && stackSym->IsFloatConst())); Assert(defInstr->m_opcode == Js::OpCode::LdC_A_R8); IR::AddrOpnd *const addrOpnd = src1->AsFloatConstOpnd()->GetAddrOpnd(defInstr->m_func); const Js::Var address = addrOpnd->m_address; // This is just to prevent creating multiple numbers when the sym is used multiple times. We can only do this // post-GlobOpt, as otherwise it violates some invariants assumed in GlobOpt. defInstr->ReplaceSrc1(addrOpnd); defInstr->m_opcode = Js::OpCode::Ld_A; return reinterpret_cast<intptr_t>(address); } AssertMsg(UNREACHED, "Unknown const value"); return 0; }