//------------------------------------------------------------------------------------------------------- // 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; } IntConstType immed = srcOpnd->GetImmediateValue(); 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(); IntConstType 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->GetImmediateValue(); // 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. if (!src2->IsRegOpnd() && !this->CanEncodeModConst12(src2->GetImmediateValue())) { Assert(instr->m_opcode != Js::OpCode::SUBS); Assert(IS_CONST_UINT12(src2->GetImmediateValue())); 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(); IntConstType 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->GetImmediateValue(); 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->GetImmediateValue(); 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->GetImmediateValue())) { 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->GetImmediateValue())); 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(long 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, long 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; long 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->GetImmediateValue(); 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(); unsigned long 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 // : 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->GetImmediateValue(); } 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 // : 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(L"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) { Js::Var 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())) { 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(L"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) { 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); }