| //------------------------------------------------------------------------------------------------------- |
| // Copyright (C) Microsoft. All rights reserved. |
| // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. |
| //------------------------------------------------------------------------------------------------------- |
| #include "Backend.h" |
| #include "ARMEncode.h" |
| #include "Language/JavascriptFunctionArgIndex.h" |
| |
| const FormTable * InstrEncode[]={ |
| #define MACRO(name, jnLayout, attrib, byte2, form, opbyte, ...) opbyte, |
| #include "MdOpCodes.h" |
| #undef ASMDAT |
| }; |
| |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// EncoderMD::Init |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| void |
| EncoderMD::Init(Encoder *encoder) |
| { |
| m_encoder = encoder; |
| m_relocList = nullptr; |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// EncoderMD::GetRegEncode |
| /// |
| /// Get the encoding of a given register. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| const BYTE |
| EncoderMD::GetRegEncode(IR::RegOpnd *regOpnd) |
| { |
| return GetRegEncode(regOpnd->GetReg()); |
| } |
| |
| const BYTE |
| EncoderMD::GetRegEncode(RegNum reg) |
| { |
| return RegEncode[reg]; |
| } |
| |
| const BYTE |
| EncoderMD::GetFloatRegEncode(IR::RegOpnd *regOpnd) |
| { |
| //Each double register holds two single precision registers. |
| BYTE regEncode = GetRegEncode(regOpnd->GetReg()) * 2; |
| AssertMsg(regEncode <= LAST_FLOAT_REG_NUM, "Impossible to allocate higher registers on VFP"); |
| return regEncode; |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// EncoderMD::GetOpdope |
| /// |
| /// Get the dope vector of a particular instr. The dope vector describes |
| /// certain properties of an instr. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| uint32 |
| EncoderMD::GetOpdope(IR::Instr *instr) |
| { |
| return GetOpdope(instr->m_opcode); |
| } |
| |
| uint32 |
| EncoderMD::GetOpdope(Js::OpCode op) |
| { |
| return Opdope[op - (Js::OpCode::MDStart+1)]; |
| } |
| |
| // |
| // EncoderMD::CanonicalizeInstr : |
| // Put the instruction in its final form for encoding. This may involve |
| // expanding a pseudo-op such as LEA or changing an opcode to indicate the |
| // op bits the encoder should use. |
| // |
| // Return the size of the final instruction's encoding. |
| // |
| |
| InstructionType EncoderMD::CanonicalizeInstr(IR::Instr* instr) |
| { |
| if (!instr->IsLowered()) |
| { |
| return InstructionType::None; |
| } |
| |
| switch (instr->m_opcode) |
| { |
| |
| CASE_OPCODES_ALWAYS_THUMB2 |
| return InstructionType::Thumb2; |
| |
| CASE_OPCODES_NEVER_THUMB2 |
| return InstructionType::Thumb; |
| |
| case Js::OpCode::MOV: |
| return this->CanonicalizeMov(instr); |
| |
| case Js::OpCode::B: |
| return InstructionType::Thumb2; // For now only T2 branches are encoded |
| |
| case Js::OpCode::BL: |
| return InstructionType::Thumb2; |
| |
| case Js::OpCode::BNE: |
| case Js::OpCode::BEQ: |
| case Js::OpCode::BLT: |
| case Js::OpCode::BLE: |
| case Js::OpCode::BGE: |
| case Js::OpCode::BGT: |
| case Js::OpCode::BCS: |
| case Js::OpCode::BCC: |
| case Js::OpCode::BHI: |
| case Js::OpCode::BLS: |
| case Js::OpCode::BMI: |
| case Js::OpCode::BPL: |
| case Js::OpCode::BVS: |
| case Js::OpCode::BVC: |
| return InstructionType::Thumb2; // For now only T2 branches are encoded |
| |
| case Js::OpCode::CMP: |
| return this->CmpEncodeType(instr); |
| |
| case Js::OpCode::CMN: |
| return this->CmnEncodeType(instr); |
| |
| case Js::OpCode::CMP_ASR31: |
| return InstructionType::Thumb2; |
| |
| case Js::OpCode::POP: |
| return this->PushPopEncodeType(instr->GetSrc1()->AsIndirOpnd(), instr->GetDst()->AsRegBVOpnd()); |
| |
| case Js::OpCode::PUSH: |
| return this->PushPopEncodeType(instr->GetDst()->AsIndirOpnd(), instr->GetSrc1()->AsRegBVOpnd()); |
| |
| case Js::OpCode::LDR: |
| return this->CanonicalizeLoad(instr); |
| |
| case Js::OpCode::STR: |
| return this->CanonicalizeStore(instr); |
| |
| case Js::OpCode::LEA: |
| return this->CanonicalizeLea(instr); |
| |
| case Js::OpCode::ADD: |
| case Js::OpCode::ADDS: |
| return this->CanonicalizeAdd(instr); |
| |
| case Js::OpCode::SUB: |
| case Js::OpCode::SUBS: |
| return this->CanonicalizeSub(instr); |
| |
| case Js::OpCode::AND: |
| case Js::OpCode::EOR: |
| case Js::OpCode::MUL: |
| case Js::OpCode::ORR: |
| case Js::OpCode::RSB: |
| case Js::OpCode::RSBS: |
| case Js::OpCode::BIC: |
| return this->Alu3EncodeType(instr); |
| |
| case Js::OpCode::EOR_ASR31: |
| return InstructionType::Thumb2; |
| |
| case Js::OpCode::SMULL: |
| case Js::OpCode::SMLAL: |
| return InstructionType::Thumb2; |
| |
| case Js::OpCode::MVN: |
| return this->Alu2EncodeType(instr->GetDst(), instr->GetSrc1()); |
| |
| case Js::OpCode::TST: |
| return this->Alu2EncodeType(instr->GetSrc1(), instr->GetSrc2()); |
| |
| case Js::OpCode::ASR: |
| case Js::OpCode::ASRS: |
| case Js::OpCode::LSL: |
| case Js::OpCode::LSR: |
| return this->ShiftEncodeType(instr); |
| |
| case Js::OpCode::VSTR: |
| case Js::OpCode::VSTR32: |
| case Js::OpCode::VLDR: |
| case Js::OpCode::VLDR32: |
| case Js::OpCode::VABS: |
| case Js::OpCode::VSQRT: |
| case Js::OpCode::VMOV: |
| case Js::OpCode::VMOVARMVFP: |
| case Js::OpCode::VCVTF64F32: |
| case Js::OpCode::VCVTF32F64: |
| case Js::OpCode::VCVTF64S32: |
| case Js::OpCode::VCVTF64U32: |
| case Js::OpCode::VCVTS32F64: |
| case Js::OpCode::VCVTRS32F64: |
| case Js::OpCode::VPUSH: |
| case Js::OpCode::VPOP: |
| case Js::OpCode::VADDF64: |
| case Js::OpCode::VSUBF64: |
| case Js::OpCode::VMULF64: |
| case Js::OpCode::VDIVF64: |
| case Js::OpCode::VNEGF64: |
| case Js::OpCode::VCMPF64: |
| case Js::OpCode::VMRS: |
| case Js::OpCode::VMRSR: |
| case Js::OpCode::VMSR: |
| return InstructionType::Vfp; |
| |
| default: |
| AssertMsg(UNREACHED, "Unexpected opcode in IsInstrThumb2"); |
| return InstructionType::None; |
| } |
| } |
| |
| // CanonicalizeMov: Determine the size of the encoding and change the opcode |
| // if necessary to indicate a wide instruction. (We do this for MOV, LDR, and STR |
| // to cut down on the time it takes to search all the possible forms.) |
| InstructionType EncoderMD::CanonicalizeMov(IR::Instr * instr) |
| { |
| // 3 possibilities: |
| // 1. MOV (T1): |
| // - uint8 to low reg |
| // - any reg to reg |
| // 2. MOVW (T2): |
| // - uint16 to reg |
| // 3. MOV_W (T2): |
| // - mod const imm to reg |
| |
| IR::RegOpnd *dstOpnd = instr->GetDst()->AsRegOpnd(); |
| IR::Opnd *srcOpnd = instr->GetSrc1(); |
| |
| if (srcOpnd->IsRegOpnd()) |
| { |
| // All reg to reg copies are 2 bytes. |
| return InstructionType::Thumb; |
| } |
| |
| int32 immed = srcOpnd->GetImmediateValueAsInt32(instr->m_func); |
| if (IS_LOWREG(dstOpnd->GetReg()) && |
| IS_CONST_UINT8(immed)) |
| { |
| // uint8 -> low reg |
| return InstructionType::Thumb; |
| } |
| |
| // Wide MOV instruction. Choose the opcode based on the constant. |
| if (IS_CONST_UINT16(immed)) |
| { |
| instr->m_opcode = Js::OpCode::MOVW; |
| } |
| else |
| { |
| Assert(CanEncodeModConst12(immed)); |
| instr->m_opcode = Js::OpCode::MOV_W; |
| } |
| |
| return InstructionType::Thumb2; |
| } |
| |
| // CanonicalizeLoad: Determine the size of the encoding and change the opcode |
| // if necessary to indicate a wide instruction. (We do this for MOV, LDR, and STR |
| // to cut down on the time it takes to search all the possible forms.) |
| InstructionType EncoderMD::CanonicalizeLoad(IR::Instr * instr) |
| { |
| IR::Opnd *memOpnd = instr->GetSrc1(); |
| // Note: sign-extension of less-than-4-byte loads requires a wide instruction. |
| if (memOpnd->GetSize() == 4 || memOpnd->IsUnsigned()) |
| { |
| if (!this->IsWideMemInstr(instr->GetSrc1(), instr->GetDst()->AsRegOpnd())) |
| { |
| return InstructionType::Thumb; |
| } |
| } |
| instr->m_opcode = Js::OpCode::LDR_W; |
| return InstructionType::Thumb2; |
| } |
| |
| // CanonicalizeStore: Determine the size of the encoding and change the opcode |
| // if necessary to indicate a wide instruction. (We do this for MOV, LDR, and STR |
| // to cut down on the time it takes to search all the possible forms.) |
| InstructionType EncoderMD::CanonicalizeStore(IR::Instr * instr) |
| { |
| if (this->IsWideMemInstr(instr->GetDst(), instr->GetSrc1()->AsRegOpnd())) |
| { |
| instr->m_opcode = Js::OpCode::STR_W; |
| return InstructionType::Thumb2; |
| } |
| return InstructionType::Thumb; |
| } |
| |
| // IsWideMemInstr: Shared by LDR and STR. |
| // Determine the width of the encoding based on the operand properties. |
| bool EncoderMD::IsWideMemInstr(IR::Opnd *memOpnd, IR::RegOpnd *regOpnd) |
| { |
| // LDR/STR rn, [rbase + rindex], or |
| // LDR/STR rn, [rbase + offset] |
| |
| // If rn is not low reg, instr is wide. |
| if (!IS_LOWREG(regOpnd->GetReg())) |
| { |
| return true; |
| } |
| |
| // Pull the base and index/offset from the indirection. |
| RegNum baseReg; |
| IR::RegOpnd *indexOpnd; |
| int32 offset; |
| if (memOpnd->IsSymOpnd()) |
| { |
| indexOpnd = nullptr; |
| this->BaseAndOffsetFromSym(memOpnd->AsSymOpnd(), &baseReg, &offset, this->m_func); |
| } |
| else |
| { |
| IR::IndirOpnd *indirOpnd = memOpnd->AsIndirOpnd(); |
| // Scaled index operands require wide instruction. |
| if (indirOpnd->GetScale() > 0) |
| { |
| return true; |
| } |
| baseReg = indirOpnd->GetBaseOpnd()->GetReg(); |
| indexOpnd = indirOpnd->GetIndexOpnd(); |
| offset = indirOpnd->GetOffset(); |
| } |
| |
| Assert(offset == 0 || indexOpnd == nullptr); |
| |
| if (indexOpnd) |
| { |
| // Both base and index must be low regs. |
| return !IS_LOWREG(baseReg) || !IS_LOWREG(indexOpnd->GetReg()); |
| } |
| else |
| { |
| size_t size = memOpnd->GetSize(); |
| if (!IS_LOWREG(baseReg) && (baseReg != RegSP || size != 4)) |
| { |
| // Base reg must be low or SP (and we only have 4-byte SP-relative ops). |
| return true; |
| } |
| // Short encodings shift the offset based on the size of the load/store. |
| // (E.g., 4-byte load shifts the offset by 2.) |
| if (offset & (size - 1)) |
| { |
| // Can't use a short encoding if we lose bits by shifting the offset. |
| return true; |
| } |
| uint32 shiftBits = Math::Log2(size); |
| if (baseReg == RegSP) |
| { |
| // LDR/STR rn, [SP + uint8:00] |
| return !IS_CONST_UINT8(offset >> shiftBits); |
| } |
| else |
| { |
| // LDR/STR rn, [base + uint5:size] |
| return !IS_CONST_UINT5(offset >> shiftBits); |
| } |
| } |
| } |
| |
| InstructionType EncoderMD::CanonicalizeAdd(IR::Instr * instr) |
| { |
| IR::Opnd *src2 = instr->GetSrc2(); |
| int32 immed = 0; |
| |
| // Check cases that apply to ADD but not SUB. |
| if (src2->IsRegOpnd()) |
| { |
| // Check for rm = ADD rm, rn |
| if (instr->m_opcode != Js::OpCode::ADDS && |
| instr->GetDst()->AsRegOpnd()->IsSameReg(instr->GetSrc1())) |
| { |
| return InstructionType::Thumb; |
| } |
| } |
| else |
| { |
| immed = src2->GetImmediateValueAsInt32(instr->m_func); |
| |
| // Check for rm = ADD SP, uint8:00 |
| if (IS_LOWREG(instr->GetDst()->AsRegOpnd()->GetReg())) |
| { |
| if (instr->GetSrc1()->AsRegOpnd()->GetReg() == RegSP) |
| { |
| if ((immed & 3) == 0 && IS_CONST_UINT8(immed >> 2)) |
| { |
| return InstructionType::Thumb; |
| } |
| } |
| } |
| } |
| |
| // Now check the shared ADD/SUB cases. |
| if (this->IsWideAddSub(instr)) |
| { |
| // The instr is definitely wide. Let the opcode indicate that if we're using the uint12 form. |
| // Note that the uint12 form can't set the status bits. |
| if (!src2->IsRegOpnd() && !this->CanEncodeModConst12(immed)) |
| { |
| Assert(instr->m_opcode != Js::OpCode::ADDS); |
| Assert(IS_CONST_UINT12(immed)); |
| instr->m_opcode = Js::OpCode::ADDW; |
| } |
| |
| return InstructionType::Thumb2; |
| } |
| |
| return InstructionType::Thumb; |
| } |
| |
| InstructionType EncoderMD::CanonicalizeSub(IR::Instr * instr) |
| { |
| if (this->IsWideAddSub(instr)) |
| { |
| IR::Opnd *src2 = instr->GetSrc2(); |
| |
| // The instr is definitely wide. Let the opcode indicate that if we're using the uint12 form. |
| // Note that the uint12 form can't set the status bits. |
| Assert(!IRType_IsInt64(src2->GetType())); |
| if (!src2->IsRegOpnd() && !this->CanEncodeModConst12(src2->GetImmediateValueAsInt32(instr->m_func))) |
| { |
| Assert(instr->m_opcode != Js::OpCode::SUBS); |
| Assert(IS_CONST_UINT12(src2->GetImmediateValueAsInt32(instr->m_func))); |
| instr->m_opcode = Js::OpCode::SUBW; |
| } |
| |
| return InstructionType::Thumb2; |
| } |
| |
| return InstructionType::Thumb; |
| } |
| |
| bool EncoderMD::IsWideAddSub(IR::Instr * instr) |
| { |
| IR::RegOpnd *dst = instr->GetDst()->AsRegOpnd(); |
| IR::RegOpnd *src1 = instr->GetSrc1()->AsRegOpnd(); |
| IR::Opnd *src2 = instr->GetSrc2(); |
| int32 immed; |
| |
| if (dst->GetReg() == RegSP) |
| { |
| // The one short form is SP = op SP, uint7:00 |
| if (src1->GetReg() != RegSP) |
| { |
| return true; |
| } |
| if (src2->IsRegOpnd()) |
| { |
| return true; |
| } |
| immed = src2->GetImmediateValueAsInt32(instr->m_func); |
| return ((immed & 3) != 0) || !IS_CONST_UINT7(immed >> 2); |
| } |
| else |
| { |
| // low1 = op low2, low3 or |
| // low1 = op low2, uint3 or |
| // low1 = op low1, uint8 |
| if (!IS_LOWREG(dst->GetReg()) || !IS_LOWREG(src1->GetReg())) |
| { |
| return true; |
| } |
| if (src2->IsRegOpnd()) |
| { |
| return !IS_LOWREG(src2->AsRegOpnd()->GetReg()); |
| } |
| else |
| { |
| immed = src2->GetImmediateValueAsInt32(instr->m_func); |
| return dst->IsSameReg(src1) ? !IS_CONST_UINT8(immed) : !IS_CONST_UINT3(immed); |
| } |
| } |
| } |
| |
| InstructionType EncoderMD::CanonicalizeLea(IR::Instr * instr) |
| { |
| RegNum baseReg; |
| int32 offset; |
| |
| IR::Opnd* src1 = instr->UnlinkSrc1(); |
| |
| if (src1->IsSymOpnd()) |
| { |
| // We may as well turn this LEA into the equivalent ADD instruction and let the common ADD |
| // logic handle it. |
| IR::SymOpnd *symOpnd = src1->AsSymOpnd(); |
| |
| this->BaseAndOffsetFromSym(symOpnd, &baseReg, &offset, this->m_func); |
| symOpnd->Free(this->m_func); |
| instr->SetSrc1(IR::RegOpnd::New(nullptr, baseReg, TyMachReg, this->m_func)); |
| instr->SetSrc2(IR::IntConstOpnd::New(offset, TyMachReg, this->m_func)); |
| } |
| else |
| { |
| IR::IndirOpnd *indirOpnd = src1->AsIndirOpnd(); |
| IR::RegOpnd *baseOpnd = indirOpnd->GetBaseOpnd(); |
| IR::RegOpnd *indexOpnd = indirOpnd->GetIndexOpnd(); |
| offset = indirOpnd->GetOffset(); |
| |
| Assert(offset == 0 || indexOpnd == nullptr); |
| instr->SetSrc1(baseOpnd); |
| |
| if (indexOpnd) |
| { |
| AssertMsg(indirOpnd->GetScale() == 0, "NYI Needs shifted register support for ADD"); |
| instr->SetSrc2(indexOpnd); |
| } |
| else |
| { |
| instr->SetSrc2(IR::IntConstOpnd::New(offset, TyMachReg, this->m_func)); |
| } |
| indirOpnd->Free(this->m_func); |
| } |
| instr->m_opcode = Js::OpCode::ADD; |
| return this->CanonicalizeAdd(instr); |
| } |
| |
| InstructionType EncoderMD::CmpEncodeType(IR::Instr * instr) |
| { |
| // CMP: |
| // - low reg, uint8 |
| // - any reg, any reg |
| IR::Opnd *src2 = instr->GetSrc2(); |
| if (src2->IsRegOpnd()) |
| { |
| Assert(instr->GetSrc1()->IsRegOpnd()); |
| return InstructionType::Thumb; |
| } |
| |
| if (IS_LOWREG(instr->GetSrc1()->AsRegOpnd()->GetReg()) && |
| IS_CONST_UINT8(src2->GetImmediateValueAsInt32(instr->m_func))) |
| { |
| return InstructionType::Thumb; |
| } |
| |
| return InstructionType::Thumb2; |
| } |
| |
| InstructionType EncoderMD::CmnEncodeType(IR::Instr * instr) |
| { |
| // CMN: |
| // - low reg, low reg |
| // - any reg, uint8 |
| // - any reg, any reg |
| IR::Opnd *src2 = instr->GetSrc2(); |
| |
| if (src2->IsRegOpnd()) |
| { |
| // low reg, low reg |
| if (IS_LOWREG(instr->GetSrc1()->AsRegOpnd()->GetReg()) && IS_LOWREG(instr->GetSrc2()->AsRegOpnd()->GetReg())) |
| { |
| return InstructionType::Thumb; |
| } |
| } |
| |
| // any reg, uint8 |
| // any reg, any reg |
| return InstructionType::Thumb2; |
| } |
| |
| |
| InstructionType EncoderMD::PushPopEncodeType(IR::IndirOpnd *target, IR::RegBVOpnd * opnd) |
| { |
| if(target->GetBaseOpnd()->GetReg() != RegSP) |
| { |
| return InstructionType::Thumb2; |
| } |
| |
| // NOTE: because T1 encoding permits LR here, we could theoretically check for it specially, |
| // but in practice we never push LR without R11, so it would never help. If that changes, we |
| // should make this function smarter. |
| |
| BYTE lastRegEncode = (BYTE)opnd->m_value.GetPrevBit(); |
| Assert(lastRegEncode != BVInvalidIndex); |
| return lastRegEncode > RegEncode[RegR7] ? InstructionType::Thumb2 : InstructionType::Thumb; |
| } |
| |
| InstructionType EncoderMD::Alu2EncodeType(IR::Opnd *opnd1, IR::Opnd *opnd2) |
| { |
| // Shared by TST (checks src1 and src2) and MVN (checks dst and src1), which is why we pass |
| // operands rather than the whole instruction. |
| // Short encoding requires two low regs as operands. |
| if (!opnd1->IsRegOpnd() || !IS_LOWREG(opnd1->AsRegOpnd()->GetReg())) |
| { |
| return InstructionType::Thumb2; |
| } |
| if (!opnd2->IsRegOpnd() || !IS_LOWREG(opnd2->AsRegOpnd()->GetReg())) |
| { |
| return InstructionType::Thumb2; |
| } |
| return InstructionType::Thumb; |
| } |
| |
| InstructionType EncoderMD::Alu3EncodeType(IR::Instr * instr) |
| { |
| // Check for rm = op rm, rn |
| |
| IR::RegOpnd *dst = instr->GetDst()->AsRegOpnd(); |
| if (!IS_LOWREG(dst->GetReg()) || |
| !dst->IsSameReg(instr->GetSrc1())) |
| { |
| return InstructionType::Thumb2; |
| } |
| |
| IR::Opnd *src2 = instr->GetSrc2(); |
| if (!src2->IsRegOpnd() || !IS_LOWREG(src2->AsRegOpnd()->GetReg())) |
| { |
| return InstructionType::Thumb2; |
| } |
| |
| return InstructionType::Thumb; |
| } |
| |
| InstructionType EncoderMD::ShiftEncodeType(IR::Instr * instr) |
| { |
| // 2 short forms: |
| // rm = op rn, uint5 |
| // rm = op rm, rn |
| |
| IR::RegOpnd *dst = instr->GetDst()->AsRegOpnd(); |
| if (!IS_LOWREG(dst->GetReg())) |
| { |
| return InstructionType::Thumb2; |
| } |
| |
| IR::RegOpnd *src1 = instr->GetSrc1()->AsRegOpnd(); |
| IR::Opnd *src2 = instr->GetSrc2(); |
| if (src2->IsRegOpnd()) |
| { |
| return (IS_LOWREG(src2->AsRegOpnd()->GetReg()) && dst->IsSameReg(src1)) ? InstructionType::Thumb : InstructionType::Thumb2; |
| } |
| else |
| { |
| Assert(IS_CONST_UINT5(src2->GetImmediateValueAsInt32(instr->m_func))); |
| return IS_LOWREG(src1->GetReg()) ? InstructionType::Thumb : InstructionType::Thumb2; |
| } |
| } |
| |
| int |
| EncoderMD::IndirForm(int form, int *pOpnnum, RegNum baseReg, IR::Opnd *indexOpnd) |
| { |
| int opnnum = *pOpnnum; |
| |
| form |= FSRC(INDIR, opnnum++); |
| |
| switch (baseReg) |
| { |
| case RegSP: |
| form |= FSRC(SP, opnnum++); |
| break ; |
| case RegPC: |
| form |= FSRC(PC, opnnum++); |
| break; |
| default: |
| form |= FSRC(REG, opnnum++); |
| break; |
| } |
| |
| if (indexOpnd == nullptr) |
| { |
| // UTC does this for OPBASED. Seems to be based on the assumption |
| // that we have either an offset or an index, but not both. |
| form |= FSRC(CONST, opnnum++); // OFFSET |
| } |
| else |
| { |
| form |= FSRC(REG, opnnum++); // INDEX |
| } |
| |
| *pOpnnum = opnnum; |
| return form; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // |
| // CoGenIForms() |
| // |
| // parses the instruction tuple and generates the corresponding 'form' constant |
| // |
| //--------------------------------------------------------------------------- |
| |
| int |
| EncoderMD::GetForm(IR::Instr *instr, int32 size) |
| { |
| int form; |
| int opnnum; //Current looping operand in the instruction |
| int operands; //Represents if the current operand is dst or source |
| RegNum regNum; |
| IR::Opnd* dst; |
| IR::Opnd* opn; |
| IR::IndirOpnd *indirOpnd; |
| bool sameSrcDst = false; |
| bool T2instr = false; |
| |
| form = 0; |
| |
| T2instr = (size == 4); |
| |
| // Set THUMB or THUMB2 instruction, this is to figure out if the form is T2 or T1. |
| if (T2instr) |
| { |
| form |= FTHUMB2; |
| } |
| else |
| { |
| sameSrcDst = true; |
| form |= FTHUMB; |
| } |
| |
| dst = instr->GetDst(); |
| |
| if (dst == nullptr || LowererMD::IsCall(instr)) |
| { |
| opn = instr->GetSrc1(); |
| opnnum = 1; |
| operands = 1; |
| if (instr->IsBranchInstr() && instr->AsBranchInstr()->GetTarget()) |
| { |
| // Treat the label reference as the first source. |
| form |= FSRC(LABEL, opnnum++); |
| } |
| } |
| else |
| { |
| opn = dst; |
| opnnum = 0; |
| operands = 0; |
| } |
| |
| bool done = false; |
| |
| while (opn != nullptr) |
| { |
| switch (opn->GetKind()) |
| { |
| case IR::OpndKindIntConst: |
| case IR::OpndKindFloatConst: |
| case IR::OpndKindAddr: //UTC - CASE_DATAADDRTUPLE |
| { |
| form |= FSRC(CONST, opnnum++); |
| } |
| break; |
| |
| case IR::OpndKindReg: |
| { |
| regNum = opn->AsRegOpnd()->GetReg(); |
| switch (regNum) |
| { |
| case RegSP: |
| case RegPC: |
| if (size != 4 || instr->m_opcode == Js::OpCode::LDRRET) |
| { |
| if (regNum == RegSP) |
| { |
| form |= FSRC(SP, opnnum++); |
| } |
| else |
| { |
| form |= FSRC(PC, opnnum++); |
| } |
| break; |
| } |
| |
| // FALL THROUGH! |
| default: |
| if (regNum >= RegR0 && regNum <= RegPC) |
| { |
| if ((regNum > RegR7) && (!T2instr)) |
| { |
| form |= FSET(REG,28); |
| } |
| |
| if (operands == 0) |
| { // dst operands |
| form |= FSRC(REG,opnnum++); |
| } |
| else |
| { // src operands |
| if (sameSrcDst && dst && opn->AsRegOpnd()->IsSameReg(dst)) |
| { |
| form |= FSRC(REG,0); // same src,dst |
| sameSrcDst = false; |
| } |
| else |
| { |
| form |= FSRC(REG, opnnum++); |
| } |
| } |
| } |
| else if (regNum >= RegR0 && regNum <= LAST_DOUBLE_REG) |
| { |
| form |= FSRC(DREG, opnnum++); |
| } |
| break; |
| } |
| } |
| break; |
| |
| case IR::OpndKindHelperCall: |
| { |
| form |= FSRC(CODE, opnnum++); |
| } |
| break; |
| |
| case IR::OpndKindRegBV: |
| { |
| Assert(instr->m_opcode == Js::OpCode::PUSH || instr->m_opcode == Js::OpCode::POP |
| || instr->m_opcode == Js::OpCode::VPUSH || instr->m_opcode == Js::OpCode::VPOP); |
| BVIndex count = opn->AsRegBVOpnd()->GetValue().Count(); |
| Assert(count > 0); |
| // Note: only the wide encoding distinguishes between single- and multiple-register push/pop. |
| if (count == 1 && T2instr) |
| { |
| form |= FSRC(REG, opnnum++); |
| } |
| break; |
| } |
| |
| case IR::OpndKindIndir: |
| indirOpnd = opn->AsIndirOpnd(); |
| form = this->IndirForm(form, &opnnum, indirOpnd->GetBaseOpnd()->GetReg(), indirOpnd->GetIndexOpnd()); |
| break; |
| |
| case IR::OpndKindSym: |
| { |
| RegNum baseReg; |
| int32 offset; |
| AssertMsg(opn->AsSymOpnd()->m_sym->IsStackSym(), "Should only see stackSym syms in encoder."); |
| form |= FSRC(INDIR, opnnum++); |
| this->BaseAndOffsetFromSym(opn->AsSymOpnd(), &baseReg, &offset, this->m_func); |
| if (baseReg == RegSP) |
| { |
| form |= FSRC(SP, opnnum++); |
| } |
| else |
| { |
| form |= FSRC(REG, opnnum++); |
| form |= FSRC(CONST, opnnum++); |
| } |
| break; |
| } |
| |
| case IR::OpndKindLabel: |
| form |= FSRC(LABEL, opnnum++); |
| break; |
| |
| case IR::OpndKindMemRef: |
| // Deref of literal address |
| AssertMsg(0, "NYI"); |
| return 0; |
| |
| default: |
| AssertMsg(UNREACHED, "Unrecognized kind"); |
| return 0; |
| } |
| |
| if (done) |
| { |
| //If we have traversed all the 3 operands exit. |
| break; |
| } |
| |
| if (LowererMD::IsCall(instr)) |
| { |
| break; |
| } |
| |
| if (opn == dst) |
| { |
| opn = instr->GetSrc1(); |
| if (instr->IsBranchInstr() && instr->AsBranchInstr()->GetTarget()) |
| { |
| // Treat the label reference as the first source. |
| form |= FSRC(LABEL, opnnum++); |
| } |
| } |
| else |
| { |
| opn = instr->GetSrc2(); |
| done = true; |
| } |
| operands = 1; |
| } |
| |
| return (form); |
| } |
| |
| bool EncoderMD::EncodeImmediate16(int32 constant, DWORD * result) |
| { |
| if (constant > 0xFFFF) |
| { |
| return FALSE; |
| } |
| |
| DWORD encode = ((constant & 0xFF) << 16) | |
| ((constant & 0x0700) << 20) | |
| ((constant & 0x0800) >> 1) | |
| ((constant & 0xF000) >> 12); |
| |
| *result |= encode; |
| return TRUE; |
| } |
| |
| ENCODE_32 |
| EncoderMD::EncodeT2Immediate12(ENCODE_32 encode, int32 constant) |
| { |
| Assert((constant & 0xFFFFF000) == 0); |
| |
| ENCODE_32 encoded = (constant & 0x800) >> (11-10); |
| encoded |= (constant & 0x700) << (16+12-8); |
| encoded |= (constant & 0xFF) << 16; |
| encode |= encoded; |
| |
| return encode; |
| } |
| |
| ENCODE_32 |
| EncoderMD::EncodeT2Offset(ENCODE_32 encode, IR::Instr *instr, int offset, int bitOffset) |
| { |
| if (EncoderMD::IsShifterUpdate(instr)) |
| { |
| Assert(IS_CONST_INT8(offset)); |
| encode |= 9 << 24; |
| if (!EncoderMD::IsShifterSub(instr)) |
| { |
| encode |= 1 << 25; |
| } |
| if (!EncoderMD::IsShifterPost(instr)) |
| { |
| encode |= 1 << 26; |
| } |
| } |
| else |
| { |
| if (offset >=0) |
| { |
| Assert(IS_CONST_UINT12(offset)); |
| encode |= 1 << 7; |
| } |
| else |
| { |
| offset = -offset; |
| Assert(IS_CONST_UINT8(offset)); |
| encode |= 0x0C000000; |
| } |
| } |
| encode |= offset << bitOffset; |
| |
| return encode; |
| } |
| |
| //--------------------------------------------------------------------------- |
| // |
| // GenerateEncoding() |
| // |
| // generates the encoding for the specified tuple/form by applying the |
| // associated encoding steps |
| // |
| //--------------------------------------------------------------------------- |
| ENCODE_32 |
| EncoderMD::GenerateEncoding(IR::Instr* instr, IFORM iform, BYTE *pc, int32 size, InstructionType instrType) |
| { |
| ENCODE_32 encode = 0 ; |
| DWORD encoded = 0; |
| |
| IR::Opnd* opn = 0; |
| IR::Opnd* dst = 0; |
| IR::Opnd* reg = 0; //tupReg; |
| IR::IndirOpnd *indirOpnd; |
| |
| Js::OpCode opcode = instr->m_opcode; |
| const AssemblyStep *AsmSteps = nullptr; |
| const FormTable *ftp = nullptr; |
| |
| int bitOffset; |
| int offset; |
| |
| bool fUpdate; |
| bool fSub; |
| bool fPost; |
| |
| int done = false; |
| int32 constant = 0; //UTC IVALTYPE |
| bool constantValid = false; |
| RegNum regNum; |
| unsigned int iType = 0, SFlag = 0; |
| |
| dst = instr->GetDst(); |
| |
| if(opcode == Js::OpCode::MLS) |
| { |
| Assert(instr->m_prev->GetDst()->IsRegOpnd() && (instr->m_prev->GetDst()->AsRegOpnd()->GetReg() == RegR12)); |
| } |
| |
| if (dst == nullptr || LowererMD::IsCall(instr)) |
| { |
| opn = instr->GetSrc1(); |
| reg = opn; |
| } |
| else if (opcode == Js::OpCode::POP || opcode == Js::OpCode::VPOP) |
| { |
| opn = instr->GetSrc1(); |
| reg = dst; |
| } |
| else |
| { |
| opn = dst; |
| reg = opn; |
| } |
| |
| for (ftp = InstrEncode[opcode - (Js::OpCode::MDStart + 1)]; !done && ftp->form != FORM_NOMORE; ftp++) |
| { |
| if (ftp->form != iform) |
| { |
| if (!((iform & (1<<28)) == 0 && THUMB2_THUMB1_FORM(ftp->form, iform))) |
| { |
| continue; |
| } |
| } |
| |
| AsmSteps = ftp->steps; |
| done = false; |
| constantValid=0; |
| |
| while (!done) |
| { |
| switch (*AsmSteps++) |
| { |
| case STEP_NEXTOPN: |
| // Get Next operand |
| if (opn == dst) |
| { |
| opn = instr->GetSrc1(); |
| } |
| else |
| { |
| Assert(opn == instr->GetSrc1()); |
| opn = instr->GetSrc2(); |
| } |
| |
| reg = opn; |
| continue; |
| |
| case STEP_CONSTANT: |
| Assert(opn->IsImmediateOpnd()); |
| constant = opn->GetImmediateValueAsInt32(instr->m_func); |
| constantValid = true; |
| continue; |
| |
| case STEP_CALL: |
| continue; |
| |
| case STEP_T2_BRANCH24: |
| // Constant encoded with 24bits |
| EncodeReloc::New(&m_relocList, RelocTypeBranch24, m_pc, instr->AsBranchInstr()->GetTarget(), m_encoder->m_tempAlloc); |
| continue; |
| |
| case STEP_T2_BRANCH20: |
| // Constant encoded with 20bits. |
| EncodeReloc::New(&m_relocList, RelocTypeBranch20, m_pc, instr->AsBranchInstr()->GetTarget(), m_encoder->m_tempAlloc); |
| continue; |
| |
| case STEP_REG: |
| Assert(reg != nullptr); |
| Assert(reg->IsRegOpnd()); |
| |
| bitOffset = *AsmSteps++; |
| regNum = (RegNum)this->GetRegEncode(reg->AsRegOpnd()); |
| encode |= regNum << bitOffset; |
| continue; |
| |
| case STEP_HREG: |
| Assert(reg != nullptr); |
| Assert(reg->IsRegOpnd()); |
| bitOffset = *AsmSteps++; |
| regNum = (RegNum)this->GetRegEncode(reg->AsRegOpnd()); |
| encode |= (regNum & 0x7) << bitOffset; |
| continue; |
| |
| case STEP_R12: |
| bitOffset = *AsmSteps++; |
| regNum = (RegNum)this->GetRegEncode(RegR12); |
| encode |= regNum << bitOffset; |
| continue; |
| |
| case STEP_HBIT: |
| Assert(reg != nullptr); |
| Assert(reg->IsRegOpnd()); |
| regNum = (RegNum)this->GetRegEncode(reg->AsRegOpnd()); |
| if (regNum >= MAX_INT_REGISTERS_LOW) |
| { |
| bitOffset = *AsmSteps; |
| encode |= 1 << bitOffset; |
| } |
| AsmSteps++; |
| continue; |
| |
| case STEP_OPEQ: |
| Assert(instr->GetDst()->AsRegOpnd()->IsSameReg(instr->GetSrc1())); |
| continue; |
| |
| case STEP_DUMMY_REG: |
| Assert(opn->AsRegOpnd()->GetReg() == RegSP || |
| opn->AsRegOpnd()->GetReg() == RegPC); |
| continue; |
| |
| case STEP_OPCODE: |
| //ASSERTTNR(!(instr & ftp->inst), tupInstr); |
| encode |= ftp->inst; |
| continue; |
| |
| case STEP_FIXUP: |
| /* |
| if (TU_ISINDIR(tupOpn)) { |
| if (fApplyFixup) |
| CoApplyFixup(tupOpn, dataBuf); |
| }*/ |
| continue; |
| |
| case STEP_LDR: |
| Assert(!constantValid); |
| switch (opn->GetType()) |
| { |
| case TyInt8: |
| constant = 0x5600; |
| case TyInt16: |
| constant = 0x5e00; |
| break; |
| case TyInt32: |
| case TyUint32: |
| case TyVar: |
| constant = 0x5800; |
| break; |
| |
| case TyUint8: |
| constant = 0x5c00; |
| break; |
| |
| case TyUint16: |
| constant = 0x5a00; |
| break; |
| } |
| encode |= constant; |
| continue; |
| |
| case STEP_LDRI: |
| Assert(!constantValid); |
| switch (opn->GetType()) |
| { |
| case TyInt8: |
| case TyInt16: |
| constant = 0; |
| break; |
| |
| case TyInt32: |
| case TyUint32: |
| case TyVar: |
| constant = 0x6800; |
| break; |
| |
| case TyUint8: |
| constant = 0x7800; |
| break; |
| |
| case TyUint16: |
| constant = 0x8800; |
| break; |
| } |
| encode |= constant; |
| continue; |
| |
| case STEP_STRI: |
| Assert(!constantValid); |
| switch (opn->GetType()) |
| { |
| case TyInt8: |
| case TyUint8: |
| constant = 0x7000; |
| break; |
| |
| case TyInt16: |
| case TyUint16: |
| constant = 0x8000; |
| break; |
| |
| case TyInt32: |
| case TyUint32: |
| case TyVar: |
| constant = 0x6000; |
| break; |
| } |
| encode |= constant; |
| continue; |
| |
| case STEP_STR: |
| Assert(!constantValid); |
| switch (opn->GetType()) |
| { |
| case TyInt8: |
| case TyUint8: |
| constant = 0x5400; |
| break; |
| |
| case TyInt16: |
| case TyUint16: |
| constant = 0x5200; |
| break; |
| |
| case TyInt32: |
| case TyUint32: |
| case TyVar: |
| constant = 0x5000; |
| break; |
| } |
| encode |= constant; |
| continue; |
| |
| case STEP_IMM: |
| bitOffset = *AsmSteps++; |
| if (opn->IsIndirOpnd()) |
| { |
| offset = opn->AsIndirOpnd()->GetOffset(); |
| } |
| else |
| { |
| this->BaseAndOffsetFromSym(opn->AsSymOpnd(), ®Num, &offset, this->m_func); |
| } |
| |
| switch (opn->GetSize()) |
| { |
| case 1: |
| break; |
| |
| case 2: |
| Assert(!(offset & 0x1)); |
| offset = offset >> 1; |
| break; |
| |
| case 4: |
| Assert(!(offset & 0x3)); //check for word-align |
| offset = offset >> 2; |
| break; |
| |
| default: |
| Assert(UNREACHED); |
| offset = 0; |
| } |
| Assert(IS_CONST_UINT5(offset)); |
| encode |= offset << bitOffset; |
| continue; |
| |
| case STEP_UIMM3: |
| bitOffset = *AsmSteps++; |
| Assert(constantValid); |
| Assert(IS_CONST_UINT3(constant)); |
| encode |= constant << bitOffset; |
| continue; |
| |
| case STEP_IMM_W7: |
| Assert(constantValid); |
| Assert(!(constant & 0x3)); // check for word-alignment |
| constant = constant >> 2; // remove rightmost two zero bits |
| Assert(IS_CONST_UINT7(constant)); |
| encode |= constant; |
| constantValid = false; |
| continue; |
| |
| case STEP_IMM_DPW8: |
| Assert(constantValid); |
| Assert(IS_CONST_UINT8(constant >> 2)); |
| Assert(constant % 4 == 0); |
| encode |= constant >> 2; |
| constantValid = false; |
| continue; |
| |
| case STEP_IMM_W8: |
| if (opn->IsSymOpnd()) |
| { |
| this->BaseAndOffsetFromSym(opn->AsSymOpnd(), ®Num, &offset, this->m_func); |
| } |
| else |
| { |
| offset = opn->AsIndirOpnd()->GetOffset(); |
| } |
| |
| Assert(offset % 4 == 0); |
| Assert(IS_CONST_UINT8(offset >> 2)); |
| encode |= offset >> 2; |
| continue; |
| |
| case STEP_T2_IMM_16: |
| if (!EncodeImmediate16(constant, &encoded)) |
| { |
| AssertMsg(false,"constant > than 16 bits"); |
| } |
| encode |= encoded; |
| continue; |
| |
| case STEP_T2_IMM_12: |
| encode = this->EncodeT2Immediate12(encode, constant); |
| continue; |
| |
| case STEP_OFFSET: |
| { |
| unsigned int R_bit = 0; |
| |
| if (ISSTORE(instr->m_opcode)) |
| { |
| if (TESTREGBIT(constant, RegLR)) |
| { |
| R_bit = 1 << 8; |
| } |
| CLEARREGBIT(constant, RegLR); |
| } |
| else |
| { |
| if (TESTREGBIT(constant, RegPC)) |
| { |
| R_bit = 1 << 8; |
| } |
| CLEARREGBIT(constant, RegPC); |
| } |
| |
| Assert(IS_CONST_UINT8(constant)); |
| |
| encode |= (CO_UIMMED8(constant) | R_bit); |
| constantValid=false; |
| continue; |
| } |
| |
| case STEP_SCALE_CONST: |
| { |
| bitOffset = *AsmSteps++; |
| byte scale = opn->AsIndirOpnd()->GetScale(); |
| Assert(IS_CONST_UINT5(scale)); |
| encode |= scale << bitOffset; |
| continue; |
| } |
| |
| case STEP_SHIFTER_CONST: |
| bitOffset = *AsmSteps++; |
| |
| // TODO: When we have IR that can send Shifts we will |
| // need to translate the following: |
| // As of now instructions do not have a mechanism to |
| // provide the shift offset. |
| |
| //ASSERTNR(IS_CONST_UINT5(TU_SHIFTER_SHIFT(tupOpn))); |
| //instr |= TU_SHIFTER_SHIFT(tupOpn) << to; |
| continue; |
| |
| case STEP_BASEREG: |
| bitOffset = *AsmSteps++; |
| if (opn->IsIndirOpnd()) |
| { |
| regNum = opn->AsIndirOpnd()->GetBaseOpnd()->GetReg(); |
| } |
| else |
| { |
| this->BaseAndOffsetFromSym(opn->AsSymOpnd(), ®Num, &offset, this->m_func); |
| } |
| encode |= RegEncode[regNum] << bitOffset; |
| continue; |
| |
| case STEP_INDEXED: |
| Assert(opn->IsIndirOpnd()); |
| Assert(opn->AsIndirOpnd()->GetIndexOpnd() != nullptr); |
| Assert(opn->AsIndirOpnd()->GetOffset() == 0); |
| continue; |
| |
| case STEP_INDEXREG: |
| bitOffset = *AsmSteps++; |
| reg = opn->AsIndirOpnd()->GetIndexOpnd(); |
| Assert(reg != nullptr); |
| Assert(reg->IsRegOpnd()); |
| regNum = (RegNum)this->GetRegEncode(reg->AsRegOpnd()); |
| encode |= regNum << bitOffset; |
| continue; |
| |
| case STEP_INDIR: |
| Assert(opn->IsIndirOpnd() || |
| (opn->IsSymOpnd() && opn->AsSymOpnd()->m_sym->IsStackSym())); |
| continue; |
| |
| case STEP_BASED: |
| Assert((opn->IsIndirOpnd() && opn->AsIndirOpnd()->GetIndexOpnd() == nullptr) || |
| (opn->IsSymOpnd() && opn->AsSymOpnd()->m_sym->IsStackSym())); |
| continue; |
| |
| case STEP_T2_REGLIST: |
| //ASSERTTNR(constant_valid, tupOpn); |
| encode |= constant << 16; |
| constantValid = false; |
| if (EncoderMD::IsShifterUpdate(instr)) |
| { |
| encode |= 0x20; |
| } |
| continue; |
| |
| case STEP_UIMM5: |
| bitOffset = *AsmSteps++; |
| Assert(constantValid); |
| Assert(IS_CONST_UINT5(constant)); |
| encode |= constant << bitOffset; |
| constantValid = false; |
| continue; |
| |
| case STEP_UIMM8: |
| Assert(constantValid); |
| Assert(IS_CONST_UINT8(constant)); |
| encode |= constant; |
| constantValid = false; |
| continue; |
| |
| case STEP_REGLIST: |
| { |
| indirOpnd = opn->AsIndirOpnd(); |
| Assert(indirOpnd->GetIndexOpnd() == nullptr); |
| constant = indirOpnd->GetOffset(); |
| |
| IR::Opnd *opndRD; |
| if (EncoderMD::IsLoad(instr)) |
| { |
| opndRD = instr->GetDst(); |
| } |
| else |
| { |
| opndRD = instr->GetSrc1(); |
| } |
| |
| if (!constant) |
| { |
| BVUnit32 registers = opndRD->AsRegBVOpnd()->GetValue(); |
| uint32 regenc; |
| BVIndex index = registers.GetNextBit(); |
| |
| // Note: only the wide encoding distinguishes between |
| // single- and multiple-register push/pop. |
| if (registers.Count() > 1 || size == 2) |
| { |
| // Add the physical register number |
| do |
| { |
| regenc = 1 << index; |
| constant |= regenc; |
| }while ((index = registers.GetNextBit(index + 1))!= BVInvalidIndex); |
| |
| } |
| else |
| { |
| bitOffset = *AsmSteps++; |
| Assert(index < RegEncode[RegSP]); |
| encode |= index << bitOffset; |
| continue; |
| } |
| } |
| |
| if (size == 4) |
| { |
| fSub = EncoderMD::IsShifterSub(instr); |
| fUpdate = EncoderMD::IsShifterUpdate(instr); |
| encode |= fSub << 8; |
| encode |= !fSub << 7; |
| encode |= fUpdate << 5; |
| } |
| |
| constantValid=true; |
| } |
| continue; |
| |
| case STEP_T1_SETS_CR0: |
| { |
| //ASSERTTNR(Tuple::FindReg(TU_DST(tupInstr), RG_SYM(CR0)) != nullptr, tupInstr); |
| } |
| continue; |
| |
| case STEP_SBIT: |
| { |
| if (this->SetsSBit(instr)) |
| { |
| bitOffset = *AsmSteps; |
| encode |= 1 << bitOffset; |
| } |
| AsmSteps++; |
| } |
| continue; |
| |
| case STEP_NOSBIT: |
| // just asserts that we're not supposed to set the condition flags |
| // |
| Assert(!this->SetsSBit(instr)); |
| continue; |
| |
| case STEP_MODCONST_12: |
| if (!EncodeModConst12(constant, &encoded)) |
| { |
| Assert(UNREACHED); |
| } |
| encode |= encoded; |
| continue; |
| |
| case STEP_T2_MEMIMM_POS12_NEG8: |
| bitOffset = *AsmSteps++; |
| Assert(opn != nullptr); |
| if (opn->IsIndirOpnd()) |
| { |
| Assert(opn->AsIndirOpnd()->GetIndexOpnd() == nullptr); |
| offset = opn->AsIndirOpnd()->GetOffset(); |
| // TODO: Handle literal pool loads, if necessary |
| // <tfs #775202>: LDR_W could have $Label Fixup for literal-pool |
| //if (TU_FEFIXUPSYM(tupOpn) && SS_ISLABEL(TU_FEFIXUPSYM(tupOpn))) { |
| // offset += (SS_OFFSET(TU_FEFIXUPSYM(tupOpn)) - ((pc & (~3)) + 4)); |
| //} |
| } |
| else if (opn->IsSymOpnd()) |
| { |
| Assert(opn->AsSymOpnd()->m_sym->IsStackSym()); |
| this->BaseAndOffsetFromSym(opn->AsSymOpnd(), ®Num, &offset, this->m_func); |
| } |
| else |
| { |
| Assert(opn->IsImmediateOpnd()); |
| offset = opn->GetImmediateValueAsInt32(instr->m_func); |
| } |
| encode = this->EncodeT2Offset(encode, instr, offset, bitOffset); |
| continue; |
| |
| case STEP_T2_IMMSTACK_POS12_NEG8: |
| bitOffset = *AsmSteps++; |
| Assert(opn != nullptr); |
| Assert(opn->IsSymOpnd() && opn->AsSymOpnd()->m_sym->IsStackSym()); |
| this->BaseAndOffsetFromSym(opn->AsSymOpnd(), ®Num, &offset, this->m_func); |
| |
| encode = this->EncodeT2Offset(encode, instr, offset, bitOffset); |
| encode |= RegEncode[regNum]; |
| continue; |
| |
| case STEP_T2_STACKSYM_IMM_12: |
| // Used by LEA. Encode base reg at the given bit offset and 12-bit constant |
| // as a normal ADDW immediate. |
| bitOffset = *AsmSteps++; |
| Assert(opn != nullptr); |
| Assert(opn->IsSymOpnd() && opn->AsSymOpnd()->m_sym->IsStackSym()); |
| this->BaseAndOffsetFromSym(opn->AsSymOpnd(), ®Num, &offset, this->m_func); |
| |
| encode |= RegEncode[regNum] << bitOffset; |
| encode = this->EncodeT2Immediate12(encode, offset); |
| continue; |
| |
| case STEP_T2_MEM_TYPE: |
| { |
| Assert((ftp->inst & 0xFF00) == 0xF800); |
| switch (opn->GetType()) |
| { |
| case TyInt8: |
| SFlag = 1; |
| iType = 0; |
| break; |
| case TyUint8: |
| SFlag = 0; |
| iType = 0; |
| break; |
| case TyInt16: |
| SFlag = 1; |
| iType = 1; |
| break; |
| case TyUint16: |
| iType = 1; |
| SFlag = 0; |
| break; |
| case TyInt32: |
| case TyUint32: |
| case TyVar: |
| SFlag = 0; |
| iType = 2; |
| break; |
| default: |
| Assert(UNREACHED); |
| } |
| if (!EncoderMD::IsLoad(instr)) |
| { |
| SFlag = 0; |
| } |
| encode |= (SFlag << 8) | (iType << 5); |
| continue; |
| } |
| |
| case STEP_T2_SHIFT_IMM_5: |
| #if DBG |
| if(instr->m_opcode == Js::OpCode::ASR || |
| instr->m_opcode == Js::OpCode::ASRS || |
| instr->m_opcode == Js::OpCode::LSR) |
| { |
| // Encoding zero is interpreted as 32 |
| // for these instructions. |
| Assert(constant != 0); |
| } |
| #endif |
| Assert(IS_CONST_UINT5(constant)); |
| encoded = (constant & 0x03) << (16+6); |
| encoded |= (constant & 0x1c) << (16+12-2); |
| encode |= encoded; |
| continue; |
| |
| case STEP_MOVW_reloc: |
| Assert(opn && opn->IsLabelOpnd()); |
| if (opn->AsLabelOpnd()->GetLabel()->m_isDataLabel) |
| { |
| Assert(!opn->AsLabelOpnd()->GetLabel()->isInlineeEntryInstr); |
| EncodeReloc::New(&m_relocList, RelocTypeDataLabelLow, m_pc, opn->AsLabelOpnd()->GetLabel(), m_encoder->m_tempAlloc); |
| } |
| else |
| { |
| EncodeReloc::New(&m_relocList, RelocTypeLabelLow, m_pc, opn->AsLabelOpnd()->GetLabel(), m_encoder->m_tempAlloc); |
| } |
| continue; |
| |
| case STEP_MOVT_reloc: |
| Assert(opn && opn->IsLabelOpnd()); |
| EncodeReloc::New(&m_relocList, RelocTypeLabelHigh, m_pc, opn->AsLabelOpnd()->GetLabel(), m_encoder->m_tempAlloc); |
| continue; |
| |
| case STEP_DREG: |
| { |
| int bbit = 0; |
| DWORD tmp = 0; |
| |
| Assert(opn != nullptr && opn->IsRegOpnd()); |
| |
| bitOffset = *AsmSteps++; |
| bbit = *AsmSteps++; |
| |
| regNum = (RegNum)GetRegEncode(opn->AsRegOpnd()); |
| //Check to see if register number is valid |
| Assert(regNum >= 0 && regNum <= LAST_DOUBLE_REG_NUM); |
| |
| tmp |= (regNum & 0xf) << bitOffset; |
| tmp |= ((regNum >> 4) & 0x1) << bbit; |
| Assert(0 == (encode & tmp)); |
| |
| encode |= tmp; |
| continue; |
| } |
| |
| case STEP_SREG: |
| { |
| int bbit = 0; |
| DWORD tmp = 0; |
| |
| Assert(opn != nullptr && opn->IsRegOpnd()); |
| |
| bitOffset = *AsmSteps++; |
| bbit = *AsmSteps++; |
| |
| regNum = (RegNum)GetFloatRegEncode(opn->AsRegOpnd()); |
| //Check to see if register number is valid |
| Assert(regNum >= 0 && regNum <= LAST_FLOAT_REG_NUM); |
| |
| tmp |= (regNum & 0x1) << bbit; |
| tmp |= (regNum >> 1) << bitOffset; |
| |
| Assert(0 == (encode & tmp)); |
| |
| encode |= tmp; |
| continue; |
| } |
| |
| case STEP_IMM_S8: |
| { |
| Assert(opn!=nullptr); |
| AssertMsg(instrType == InstructionType::Vfp, "This step is specific to VFP instructions"); |
| if (opn->IsIndirOpnd()) |
| { |
| Assert(opn->AsIndirOpnd()->GetIndexOpnd() == nullptr); |
| offset = opn->AsIndirOpnd()->GetOffset(); |
| // TODO: Handle literal pool loads, if necessary |
| // <tfs #775202>: LDR_W could have $Label Fixup for literal-pool |
| //if (TU_FEFIXUPSYM(tupOpn) && SS_ISLABEL(TU_FEFIXUPSYM(tupOpn))) { |
| // offset += (SS_OFFSET(TU_FEFIXUPSYM(tupOpn)) - ((pc & (~3)) + 4)); |
| //} |
| } |
| else if (opn->IsSymOpnd()) |
| { |
| Assert(opn->AsSymOpnd()->m_sym->IsStackSym()); |
| this->BaseAndOffsetFromSym(opn->AsSymOpnd(), ®Num, &offset, this->m_func); |
| } |
| else |
| { |
| offset = 0; |
| AssertMsg(false, "Why are we here"); |
| } |
| |
| if (offset < 0) |
| { |
| //IsShifterSub(tupOpn) = TRUE; //Doesn't seem necessary for us, why does UTC set this? |
| offset = -offset; |
| encode &= ~(1 << 7); |
| } |
| else |
| { |
| encode |= (1 << 7); |
| } |
| |
| // Set the W (writeback) bit if IsShifterUpdate is |
| // specified. |
| if (EncoderMD::IsShifterUpdate(instr)) |
| { |
| encode |= (1 << 5); |
| |
| // Set the P (pre-indexed) bit to be the complement |
| // of IsShifterPost |
| if (EncoderMD::IsShifterPost(instr)) |
| { |
| encode &= ~(1 << 8); |
| } |
| else |
| { |
| encode |= (1 << 8); |
| } |
| } |
| else |
| { |
| // Clear the W bit and set the P bit (offset |
| // addressing). |
| encode &= ~(1 << 5); |
| encode |= (1 << 8); |
| } |
| |
| Assert(IS_CONST_UINT8(offset >> 2)); |
| encode |= ((offset >> 2) << 16); |
| continue; |
| } |
| |
| case STEP_DREGLIST: |
| { |
| IR::Opnd *opndRD; |
| if (EncoderMD::IsLoad(instr)) |
| { |
| opndRD = instr->GetDst(); |
| } |
| else |
| { |
| opndRD = instr->GetSrc1(); |
| } |
| |
| BVUnit32 registers = opndRD->AsRegBVOpnd()->GetValue(); |
| DWORD first = registers.GetNextBit(); |
| DWORD last = (DWORD)-1; |
| _BitScanReverse((DWORD*)&last, (DWORD)registers.GetWord()); |
| Assert(last >= first && last <= LAST_DOUBLE_CALLEE_SAVED_REG_NUM); |
| |
| encode |= (CO_UIMMED8((last - first + 1) * 2)) << 16; |
| encode |= (first << 28); |
| } |
| continue; |
| |
| |
| case STEP_AM5: |
| Assert(opn->IsIndirOpnd()); |
| Assert(opn->AsIndirOpnd()->GetIndexOpnd() == nullptr); |
| Assert(opn->AsIndirOpnd()->GetOffset() == 0); |
| |
| fPost = EncoderMD::IsShifterPost(instr); |
| fSub = EncoderMD::IsShifterSub(instr); |
| fUpdate = EncoderMD::IsShifterUpdate(instr); |
| // update addressing mode |
| encode |= !fPost << 8; |
| encode |= !fSub << 7; |
| encode |= fUpdate << 5; |
| continue; |
| |
| |
| case STEP_DONE: |
| done = true; |
| break; |
| |
| default: |
| #if DBG |
| instr->Dump(); |
| AssertMsg(UNREACHED, "Unrecognized assembly step"); |
| #endif |
| return 0; |
| } |
| |
| break; |
| } |
| } |
| |
| #if DBG |
| if (!done) |
| { |
| instr->Dump(); |
| Output::Flush(); |
| AssertMsg(UNREACHED, "Unsupported Instruction Form"); |
| } |
| #endif |
| return encode; |
| } |
| |
| #ifdef INSERT_NOPS |
| ptrdiff_t insertNops(BYTE *pc, ENCODE_32 outInstr, uint count, uint size) |
| { |
| //Insert count nops in the beginning |
| for(int i = 0; i < count;i++) |
| { |
| *(ENCODE_32 *)(pc + i * sizeof(ENCODE_32)) = 0x8000F3AF; |
| } |
| |
| if (size == sizeof(ENCODE_16)) |
| { |
| *(ENCODE_16 *)(pc + count * sizeof(ENCODE_32)) = (ENCODE_16)(outInstr & 0x0000ffff); |
| *(ENCODE_16 *)(pc + sizeof(ENCODE_16) + count * sizeof(ENCODE_32)) = (ENCODE_16)(0xBF00); |
| } |
| else |
| { |
| Assert(size == sizeof(ENCODE_32)); |
| *(ENCODE_32 *)(pc + count * sizeof(ENCODE_32)) = outInstr; |
| } |
| |
| //Insert count nops at the end; |
| for(int i = count + 1; i < (2 *count + 1); i++) |
| { |
| *(ENCODE_32 *)(pc + i * sizeof(ENCODE_32)) = 0x8000F3AF; |
| } |
| |
| return MachInt*(2*count + 1); |
| } |
| #endif //INSERT_NOPS |
| |
| #ifdef SOFTWARE_FIXFOR_HARDWARE_BUGWIN8_502326 |
| bool |
| EncoderMD::IsBuggyHardware() |
| { |
| return true; |
| // TODO: Enable this for restricting to Qualcomm Krait cores affected: KR28M2A10, KR28M2A11, KR28M2A12 |
| /* |
| AssertMsg(AutoSystemInfo::Data.wProcessorArchitecture == 5, "This has to be ARM architecture"); |
| if (((AutoSystemInfo::Data.wProcessorLevel & 0xFC0) == 0x40) && ((AutoSystemInfo::Data.wProcessorRevision & 0xF0) == 0)) |
| { |
| #if DBG_DUMP |
| if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::EncoderPhase)) |
| { |
| Output::Print(_u("TRACE: Running in buggy hardware.\n")); |
| } |
| #endif |
| return true; |
| } |
| return false; |
| */ |
| } |
| |
| bool |
| EncoderMD::CheckBranchInstrCriteria(IR::Instr* instr) |
| { |
| if (ISQBUGGYBR(instr->m_opcode)) |
| { |
| return true; |
| } |
| #if DBG |
| switch (instr->m_opcode) |
| { |
| case Js::OpCode::RET: //This is never Thumb2 hence we are safe in this hardware bug |
| return false; |
| |
| case Js::OpCode::BL: |
| AssertMsg(false, "We don't generate these now. Include in the opcode list above for BL T1 encodings"); |
| return false; |
| |
| case Js::OpCode::BLX: |
| AssertMsg(instr->GetSrc1()->IsRegOpnd(),"If we generate label include in the opcode list above"); |
| //Fallthrough |
| default: |
| { |
| //Assert to make sure none of the other instructions have target as PC register. |
| if (instr->GetDst() && instr->GetDst()->IsRegOpnd()) |
| { |
| AssertMsg(instr->GetDst()->AsRegOpnd()->GetReg() != RegPC, "Check for this opcode above"); |
| } |
| } |
| } |
| #endif |
| |
| return false; |
| } |
| |
| #endif //SOFTWARE_FIXFOR_HARDWARE_BUGWIN8_502326 |
| ///---------------------------------------------------------------------------- |
| /// |
| /// EncoderMD::Encode |
| /// |
| /// Emit the ARM encoding for the given instruction in the passed in |
| /// buffer ptr. |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| ptrdiff_t |
| EncoderMD::Encode(IR::Instr *instr, BYTE *pc, BYTE* beginCodeAddress) |
| { |
| m_pc = pc; |
| |
| ENCODE_32 outInstr; |
| IFORM iform; |
| int size = 0; |
| |
| // Instructions must be lowered, we don't handle non-MD opcodes here. |
| Assert(instr != nullptr); |
| |
| if (instr->IsLowered() == false) |
| { |
| if (instr->IsLabelInstr()) |
| { |
| if (instr->isInlineeEntryInstr) |
| { |
| intptr_t inlineeCallInfo = 0; |
| const bool encodeResult = Js::InlineeCallInfo::Encode(inlineeCallInfo, instr->AsLabelInstr()->GetOffset(), m_pc - m_encoder->m_encodeBuffer); |
| Assert(encodeResult); |
| //We are re-using offset to save the inlineeCallInfo which will be patched in ApplyRelocs |
| //This is a cleaner way to patch MOVW\MOVT pair with the right inlineeCallInfo |
| instr->AsLabelInstr()->ResetOffset((uint32)inlineeCallInfo); |
| } |
| else |
| { |
| instr->AsLabelInstr()->SetPC(m_pc); |
| if (instr->AsLabelInstr()->m_id == m_func->m_unwindInfo.GetPrologStartLabel()) |
| { |
| m_func->m_unwindInfo.SetPrologOffset(m_pc - m_encoder->m_encodeBuffer); |
| } |
| else if (instr->AsLabelInstr()->m_id == m_func->m_unwindInfo.GetEpilogEndLabel()) |
| { |
| // This is the last instruction in the epilog. Any instructions that follow |
| // are separated code, so the unwind info will have to represent them as a function |
| // fragment. (If there's no separated code, then this offset will equal the total |
| // code size.) |
| m_func->m_unwindInfo.SetEpilogEndOffset(m_pc - m_encoder->m_encodeBuffer - m_func->m_unwindInfo.GetPrologOffset()); |
| } |
| } |
| } |
| #if DBG_DUMP |
| if (instr->IsEntryInstr() && ( |
| Js::Configuration::Global.flags.DebugBreak.Contains(m_func->GetFunctionNumber()) || |
| PHASE_ON(Js::DebugBreakPhase, m_func) |
| )) |
| { |
| IR::Instr *int3 = IR::Instr::New(Js::OpCode::DEBUGBREAK, m_func); |
| return this->Encode(int3, m_pc); |
| } |
| #endif |
| return 0; |
| } |
| |
| #ifdef SOFTWARE_FIXFOR_HARDWARE_BUGWIN8_502326 |
| if (IsBuggyHardware()) |
| { |
| // Hardware bug is in Qualcomm 8960. 32 bit thumb branch instructions might not jump to the correct address in following |
| // conditions: |
| // a.3 T16 thumb instruction followed by T32 branch instruction (conditional\unconditional & RegPc load). |
| // b.Branch instruction starts at 0x*****FBE |
| |
| // As we don't know the final address, instead of checking for 0x*FBE we just check for offset 0x*E |
| // as the final function start address is always aligned at 16 byte boundary (EMIT_BUFFER_ALIGNMENT) |
| if (consecutiveThumbInstrCount >= 3 && (((uint)(m_pc - beginCodeAddress) & 0xF) == 0xE) && CheckBranchInstrCriteria(instr)) |
| { |
| Assert(beginCodeAddress); |
| IR::Instr *nop = IR::Instr::New(Js::OpCode::VMOV, |
| IR::RegOpnd::New(nullptr, RegD15, TyMachDouble, this->m_func), |
| IR::RegOpnd::New(nullptr, RegD15, TyMachDouble, this->m_func), |
| m_func); |
| size = this->Encode(nop, m_pc); |
| consecutiveThumbInstrCount = 0; |
| size+= this->Encode(instr, m_pc + size); |
| #if DBG_DUMP |
| if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::EncoderPhase)) |
| { |
| Output::Print(_u("TRACE: Avoiding Branch instruction and Dummy nops at 0x*E \n")); |
| } |
| #endif |
| Assert(size == 8); |
| // We are okay with returning size 8 as the previous 3 thumb instructions any way would have saved 6 bytes |
| // and doesn't alter the logic of allocating temp buffer based on MachMaxInstrSize |
| return size; |
| } |
| } |
| #endif |
| |
| InstructionType instrType = this->CanonicalizeInstr(instr); |
| switch(instrType) |
| { |
| case Thumb: |
| size = 2; |
| consecutiveThumbInstrCount++; |
| break; |
| case Thumb2: |
| size = 4; |
| consecutiveThumbInstrCount = 0; |
| break; |
| case Vfp: |
| size = 4; |
| consecutiveThumbInstrCount = 0; |
| break; |
| |
| default: Assert(false); |
| } |
| |
| AssertMsg(size != MachChar, "Thumb2 is never single Byte"); |
| |
| iform = (IFORM)GetForm(instr, size); |
| outInstr = GenerateEncoding(instr, iform, m_pc, size, instrType); |
| |
| if (outInstr == 0) |
| { |
| return 0; |
| } |
| |
| // TODO: Check if VFP/Neon instructions in Thumb-2 mode we need to swap the instruction halfwords |
| if (size == sizeof(ENCODE_16)) |
| { |
| #ifdef INSERT_NOPS |
| return insertNops(m_pc, outInstr, CountNops, sizeof(ENCODE_16)); |
| #else |
| //2 byte Thumb encoding |
| Assert((outInstr & 0xffff0000) == 0); |
| *(ENCODE_16 *)m_pc = (ENCODE_16)(outInstr & 0x0000ffff); |
| return MachShort; |
| #endif |
| } |
| else if (size == sizeof(ENCODE_32)) |
| { |
| #ifdef INSERT_NOPS |
| return insertNops(m_pc, outInstr, CountNops, sizeof(ENCODE_32)); |
| #else |
| //4 byte Thumb2 encoding |
| *(ENCODE_32 *)m_pc = outInstr ; |
| return MachInt; |
| #endif |
| } |
| |
| AssertMsg(UNREACHED, "Unexpected size"); |
| return 0; |
| } |
| |
| bool |
| EncoderMD::CanEncodeModConst12(DWORD constant) |
| { |
| DWORD encode; |
| return EncodeModConst12(constant, &encode); |
| } |
| |
| bool |
| EncoderMD::EncodeModConst12(DWORD constant, DWORD * result) |
| { |
| unsigned int a, b, c, d, rotation, firstbit, lastbit, temp=0; |
| |
| if (constant == 0) |
| { |
| *result = 0; |
| return true; |
| } |
| |
| a = constant & 0xff; |
| b = (constant >> 8) & 0xff; |
| c = (constant >> 16) & 0xff; |
| d = (constant >> 24) & 0xff; |
| |
| _BitScanReverse((DWORD*)&firstbit, constant); |
| _BitScanForward((DWORD*)&lastbit, constant); |
| |
| if (! ((a == 0 && c == 0 && b == d) |
| || (b == 0 && d == 0 && a == c) |
| || (a == b && b == c && c == d) |
| || (firstbit-lastbit < 8) )) |
| { |
| return false; |
| } |
| |
| *result = 0; |
| if (constant <= 0xFF) |
| { |
| *result |= constant << 16; |
| } |
| else if (firstbit-lastbit < 8) |
| { |
| if (firstbit > 7) |
| { |
| temp |= 0x7F & (constant >> (firstbit-7)); |
| rotation = 32-firstbit+7; |
| } |
| else |
| { |
| temp |= 0x7F & (constant << (7-firstbit)); |
| rotation = 7-firstbit; |
| } |
| *result = (temp & 0xFF) << 16; |
| *result |= (0x10 & rotation) << 6; |
| *result |= (0xE & rotation) << 27; |
| *result |= (0x1 & rotation) << 23; |
| } |
| else |
| { |
| if (a==0 && c==0 && b==d) |
| { |
| *result |= 0x20000000; // HW2[12] |
| *result |= (0xFF & b) << 16; |
| } |
| else if (a==c && b==0 && d==0) |
| { |
| *result |= 0x10000000; |
| *result |= (0xFF & a) << 16; |
| } |
| else if (a==b && b==c && c==d) |
| { |
| *result |= 0x30000000; |
| *result |= (0xFF & d) << 16; |
| } |
| else |
| { |
| Assert(UNREACHED); |
| } |
| } |
| return true; |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// EncodeReloc::New |
| /// |
| ///---------------------------------------------------------------------------- |
| |
| void |
| EncodeReloc::New(EncodeReloc **pHead, RelocType relocType, BYTE *offset, IR::Instr *relocInstr, ArenaAllocator *alloc) |
| { |
| EncodeReloc *newReloc = AnewStruct(alloc, EncodeReloc); |
| newReloc->m_relocType = relocType; |
| newReloc->m_consumerOffset = offset; |
| newReloc->m_next = *pHead; |
| newReloc->m_relocInstr = relocInstr; |
| *pHead = newReloc; |
| } |
| |
| |
| ENCODE_32 EncoderMD::CallOffset(int x) |
| { |
| Assert(IS_CONST_INT24(x >> 1)); |
| |
| ENCODE_32 ret; |
| int Sflag = (x & 0x1000000) >> 24; |
| int off23 = (x & 0x800000) >> 23; |
| int off22 = (x & 0x400000) >> 22; |
| |
| ret = (x & 0xFFE) << 15; |
| ret |= (x & 0x3FF000) >> 12; |
| ret |= (((~off23) ^ Sflag) & 0x1) << (16+13); |
| ret |= (((~off22) ^ Sflag) & 0x1) << (16+11); |
| ret |= (Sflag << 10); |
| return ret; |
| } |
| |
| ENCODE_32 EncoderMD::BranchOffset_T2_24(int x) |
| { |
| x -= 4; |
| Assert(IS_CONST_INT24(x >> 1)); |
| |
| int ret; |
| int Sflag = (x & 0x1000000) >> 24; |
| int off23 = (x & 0x800000) >> 23; |
| int off22 = (x & 0x400000) >> 22; |
| |
| ret = (x & 0xFFE) << 15; |
| ret |= (x & 0x3FF000) >> 12; |
| ret |= (((~off23) ^ Sflag) & 0x1) << (16+13); |
| ret |= (((~off22) ^ Sflag) & 0x1) << (16+11); |
| ret |= (Sflag << 10); |
| return INSTR_TYPE(ret); |
| } |
| |
| ENCODE_32 EncoderMD::BranchOffset_T2_20(int x) |
| { |
| x -= 4; |
| Assert(IS_CONST_INT21(x)); |
| |
| uint32 ret; |
| uint32 Sflag = (x & 0x100000) >> 20; |
| uint32 off19 = (x & 0x80000) >> 19; |
| uint32 off18 = (x & 0x40000) >> 18; |
| |
| ret = (x & 0xFFE) << 15; |
| ret |= (x & 0x3F000) >> 12; |
| ret |= off18 << (13+16); |
| ret |= off19 << (11+16); |
| ret |= (Sflag << 10); |
| return ret; |
| } |
| |
| void |
| EncoderMD::BaseAndOffsetFromSym(IR::SymOpnd *symOpnd, RegNum *pBaseReg, int32 *pOffset, Func * func) |
| { |
| StackSym *stackSym = symOpnd->m_sym->AsStackSym(); |
| |
| RegNum baseReg = func->GetLocalsPointer(); |
| int32 offset = stackSym->m_offset + symOpnd->m_offset; |
| if (baseReg == RegSP) |
| { |
| // SP points to the base of the argument area. Non-reg SP points directly to the locals. |
| offset += (func->m_argSlotsForFunctionsCalled * MachRegInt); |
| if (func->GetMaxInlineeArgOutCount()) |
| { |
| Assert(func->HasInlinee()); |
| if ((!stackSym->IsArgSlotSym() || stackSym->m_isOrphanedArg) && !stackSym->IsParamSlotSym()) |
| { |
| offset += func->GetInlineeArgumentStackSize(); |
| } |
| } |
| } |
| |
| if (stackSym->IsParamSlotSym()) |
| { |
| offset += func->m_localStackHeight + func->m_ArgumentsOffset; |
| if (!EncoderMD::CanEncodeLoadStoreOffset(offset)) |
| { |
| // Use the frame pointer. No need to hoist an offset for a param. |
| baseReg = FRAME_REG; |
| offset = stackSym->m_offset + symOpnd->m_offset - (Js::JavascriptFunctionArgIndex_Frame * MachRegInt); |
| Assert(EncoderMD::CanEncodeLoadStoreOffset(offset)); |
| } |
| } |
| #ifdef DBG |
| else |
| { |
| // Locals are offset by the size of the area allocated for stack args. |
| Assert(offset >= 0); |
| Assert(baseReg != RegSP || (uint)offset >= (func->m_argSlotsForFunctionsCalled * MachRegInt)); |
| |
| if (func->GetMaxInlineeArgOutCount()) |
| { |
| Assert(baseReg == RegSP); |
| if (stackSym->IsArgSlotSym() && !stackSym->m_isOrphanedArg) |
| { |
| Assert(stackSym->m_isInlinedArgSlot); |
| Assert((uint)offset <= ((func->m_argSlotsForFunctionsCalled + func->GetMaxInlineeArgOutCount()) * MachRegInt)); |
| } |
| else |
| { |
| AssertMsg(stackSym->IsAllocated(), "StackSym offset should be set"); |
| Assert((uint)offset > ((func->m_argSlotsForFunctionsCalled + func->GetMaxInlineeArgOutCount()) * MachRegInt)); |
| } |
| } |
| // TODO: restore the following assert (very useful) once we have a way to tell whether prolog/epilog |
| // gen is complete. |
| //Assert(offset < func->m_localStackHeight); |
| } |
| #endif |
| *pBaseReg = baseReg; |
| *pOffset = offset; |
| } |
| |
| ///---------------------------------------------------------------------------- |
| /// |
| /// EncoderMD::ApplyRelocs |
| /// We apply relocations to the temporary buffer using the target buffer's address |
| /// before we copy the contents of the temporary buffer to the target buffer. |
| ///---------------------------------------------------------------------------- |
| void |
| EncoderMD::ApplyRelocs(uint32 codeBufferAddress, size_t codeSize, uint* bufferCRC, BOOL isBrShorteningSucceeded, bool isFinalBufferValidation) |
| { |
| for (EncodeReloc *reloc = m_relocList; reloc; reloc = reloc->m_next) |
| { |
| BYTE * relocAddress = reloc->m_consumerOffset; |
| int32 pcrel; |
| ENCODE_32 encode = *(ENCODE_32*)relocAddress; |
| switch (reloc->m_relocType) |
| { |
| case RelocTypeBranch20: |
| { |
| IR::LabelInstr * labelInstr = reloc->m_relocInstr->AsLabelInstr(); |
| Assert(!labelInstr->isInlineeEntryInstr); |
| AssertMsg(labelInstr->GetPC() != nullptr, "Branch to unemitted label?"); |
| pcrel = (uint32)(labelInstr->GetPC() - reloc->m_consumerOffset); |
| encode |= BranchOffset_T2_20(pcrel); |
| *(uint32 *)relocAddress = encode; |
| break; |
| } |
| |
| case RelocTypeBranch24: |
| { |
| IR::LabelInstr * labelInstr = reloc->m_relocInstr->AsLabelInstr(); |
| Assert(!labelInstr->isInlineeEntryInstr); |
| AssertMsg(labelInstr->GetPC() != nullptr, "Branch to unemitted label?"); |
| pcrel = (uint32)(labelInstr->GetPC() - reloc->m_consumerOffset); |
| encode |= BranchOffset_T2_24(pcrel); |
| *(ENCODE_32 *)relocAddress = encode; |
| break; |
| } |
| |
| case RelocTypeDataLabelLow: |
| { |
| IR::LabelInstr * labelInstr = reloc->m_relocInstr->AsLabelInstr(); |
| Assert(!labelInstr->isInlineeEntryInstr && labelInstr->m_isDataLabel); |
| |
| AssertMsg(labelInstr->GetPC() != nullptr, "Branch to unemitted label?"); |
| |
| pcrel = ((labelInstr->GetPC() - m_encoder->m_encodeBuffer + codeBufferAddress) & 0xFFFF); |
| |
| if (!EncodeImmediate16(pcrel, (DWORD*) &encode)) |
| { |
| Assert(UNREACHED); |
| } |
| *(ENCODE_32 *) relocAddress = encode; |
| break; |
| } |
| |
| case RelocTypeLabelLow: |
| { |
| // Absolute (not relative) label address (lower 16 bits) |
| IR::LabelInstr * labelInstr = reloc->m_relocInstr->AsLabelInstr(); |
| if (!labelInstr->isInlineeEntryInstr) |
| { |
| AssertMsg(labelInstr->GetPC() != nullptr, "Branch to unemitted label?"); |
| // Note that the bottom bit must be set, since this is a Thumb code address. |
| pcrel = ((labelInstr->GetPC() - m_encoder->m_encodeBuffer + codeBufferAddress) & 0xFFFF) | 1; |
| } |
| else |
| { |
| //This is a encoded low 16 bits. |
| pcrel = labelInstr->GetOffset() & 0xFFFF; |
| } |
| if (!EncodeImmediate16(pcrel, (DWORD*) &encode)) |
| { |
| Assert(UNREACHED); |
| } |
| *(ENCODE_32 *) relocAddress = encode; |
| break; |
| } |
| |
| case RelocTypeLabelHigh: |
| { |
| // Absolute (not relative) label address (upper 16 bits) |
| IR::LabelInstr * labelInstr = reloc->m_relocInstr->AsLabelInstr(); |
| if (!labelInstr->isInlineeEntryInstr) |
| { |
| AssertMsg(labelInstr->GetPC() != nullptr, "Branch to unemitted label?"); |
| pcrel = (labelInstr->GetPC() - m_encoder->m_encodeBuffer + codeBufferAddress) >> 16; |
| // We only record the relocation on the low byte of the pair |
| } |
| else |
| { |
| //This is a encoded high 16 bits. |
| pcrel = labelInstr->GetOffset() >> 16; |
| } |
| if (!EncodeImmediate16(pcrel, (DWORD*) &encode)) |
| { |
| Assert(UNREACHED); |
| } |
| *(ENCODE_32 *) relocAddress = encode; |
| break; |
| } |
| |
| case RelocTypeLabel: |
| { |
| IR::LabelInstr * labelInstr = reloc->m_relocInstr->AsLabelInstr(); |
| AssertMsg(labelInstr->GetPC() != nullptr, "Branch to unemitted label?"); |
| /* For Thumb instruction set -> OR 1 with the address*/ |
| *(uint32 *)relocAddress = (uint32)(labelInstr->GetPC() - m_encoder->m_encodeBuffer + codeBufferAddress) | 1; |
| break; |
| } |
| default: |
| AssertMsg(UNREACHED, "Unknown reloc type"); |
| } |
| } |
| } |
| |
| void |
| EncoderMD::EncodeInlineeCallInfo(IR::Instr *instr, uint32 codeOffset) |
| { |
| IR::LabelInstr* inlineeStart = instr->AsLabelInstr(); |
| Assert((inlineeStart->GetOffset() & 0x0F) == inlineeStart->GetOffset()); |
| return; |
| } |
| |
| bool EncoderMD::TryConstFold(IR::Instr *instr, IR::RegOpnd *regOpnd) |
| { |
| Assert(regOpnd->m_sym->IsConst()); |
| |
| if (instr->m_opcode == Js::OpCode::MOV) |
| { |
| if (instr->GetSrc1() != regOpnd) |
| { |
| return false; |
| } |
| if (!instr->GetDst()->IsRegOpnd()) |
| { |
| return false; |
| } |
| |
| instr->ReplaceSrc(regOpnd, regOpnd->m_sym->GetConstOpnd()); |
| LegalizeMD::LegalizeInstr(instr, false); |
| |
| return true; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| bool EncoderMD::TryFold(IR::Instr *instr, IR::RegOpnd *regOpnd) |
| { |
| if (LowererMD::IsAssign(instr)) |
| { |
| if (!instr->GetDst()->IsRegOpnd() || regOpnd != instr->GetSrc1()) |
| { |
| return false; |
| } |
| IR::SymOpnd *symOpnd = IR::SymOpnd::New(regOpnd->m_sym, regOpnd->GetType(), instr->m_func); |
| instr->ReplaceSrc(regOpnd, symOpnd); |
| LegalizeMD::LegalizeInstr(instr, false); |
| |
| return true; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| void EncoderMD::AddLabelReloc(BYTE* relocAddress) |
| { |
| Assert(relocAddress != nullptr); |
| EncodeReloc::New(&m_relocList, RelocTypeLabel, relocAddress, *(IR::Instr**)relocAddress, m_encoder->m_tempAlloc); |
| } |
| |