/// Measure each basic block, fill the BlockOffsets, and return the size of /// the function, starting with BB unsigned MSP430BSel::measureFunction(OffsetVector &BlockOffsets, MachineBasicBlock *FromBB) { // Give the blocks of the function a dense, in-order, numbering. MF->RenumberBlocks(FromBB); MachineFunction::iterator Begin; if (FromBB == nullptr) { Begin = MF->begin(); } else { Begin = FromBB->getIterator(); } BlockOffsets.resize(MF->getNumBlockIDs()); unsigned TotalSize = BlockOffsets[Begin->getNumber()]; for (auto &MBB : make_range(Begin, MF->end())) { BlockOffsets[MBB.getNumber()] = TotalSize; for (MachineInstr &MI : MBB) { TotalSize += TII->getInstSizeInBytes(MI); } } return TotalSize; }
bool ModuleGenerator::finishCodegen() { uint32_t offsetInWhole = masm_.size(); uint32_t numFuncExports = metadata_->funcExports.length(); MOZ_ASSERT(numFuncExports == exportedFuncs_.count()); // Generate stubs in a separate MacroAssembler since, otherwise, for modules // larger than the JumpImmediateRange, even local uses of Label will fail // due to the large absolute offsets temporarily stored by Label::bind(). OffsetVector entries; ProfilingOffsetVector interpExits; ProfilingOffsetVector jitExits; EnumeratedArray<JumpTarget, JumpTarget::Limit, Offsets> jumpTargets; Offsets interruptExit; { TempAllocator alloc(&lifo_); MacroAssembler masm(MacroAssembler::AsmJSToken(), alloc); if (!entries.resize(numFuncExports)) return false; for (uint32_t i = 0; i < numFuncExports; i++) entries[i] = GenerateEntry(masm, metadata_->funcExports[i]); if (!interpExits.resize(numFuncImports())) return false; if (!jitExits.resize(numFuncImports())) return false; for (uint32_t i = 0; i < numFuncImports(); i++) { interpExits[i] = GenerateInterpExit(masm, metadata_->funcImports[i], i); jitExits[i] = GenerateJitExit(masm, metadata_->funcImports[i]); } for (JumpTarget target : MakeEnumeratedRange(JumpTarget::Limit)) jumpTargets[target] = GenerateJumpTarget(masm, target); interruptExit = GenerateInterruptStub(masm); if (masm.oom() || !masm_.asmMergeWith(masm)) return false; } // Adjust each of the resulting Offsets (to account for being merged into // masm_) and then create code ranges for all the stubs. for (uint32_t i = 0; i < numFuncExports; i++) { entries[i].offsetBy(offsetInWhole); metadata_->funcExports[i].initEntryOffset(entries[i].begin); if (!metadata_->codeRanges.emplaceBack(CodeRange::Entry, entries[i])) return false; } for (uint32_t i = 0; i < numFuncImports(); i++) { interpExits[i].offsetBy(offsetInWhole); metadata_->funcImports[i].initInterpExitOffset(interpExits[i].begin); if (!metadata_->codeRanges.emplaceBack(CodeRange::ImportInterpExit, interpExits[i])) return false; jitExits[i].offsetBy(offsetInWhole); metadata_->funcImports[i].initJitExitOffset(jitExits[i].begin); if (!metadata_->codeRanges.emplaceBack(CodeRange::ImportJitExit, jitExits[i])) return false; } for (JumpTarget target : MakeEnumeratedRange(JumpTarget::Limit)) { jumpTargets[target].offsetBy(offsetInWhole); if (!metadata_->codeRanges.emplaceBack(CodeRange::Inline, jumpTargets[target])) return false; } interruptExit.offsetBy(offsetInWhole); if (!metadata_->codeRanges.emplaceBack(CodeRange::Inline, interruptExit)) return false; // Fill in LinkData with the offsets of these stubs. linkData_.interruptOffset = interruptExit.begin; linkData_.outOfBoundsOffset = jumpTargets[JumpTarget::OutOfBounds].begin; linkData_.unalignedAccessOffset = jumpTargets[JumpTarget::UnalignedAccess].begin; linkData_.badIndirectCallOffset = jumpTargets[JumpTarget::BadIndirectCall].begin; // Only call convertOutOfRangeBranchesToThunks after all other codegen that may // emit new jumps to JumpTargets has finished. if (!convertOutOfRangeBranchesToThunks()) return false; // Now that all thunks have been generated, patch all the thunks. for (CallThunk& callThunk : metadata_->callThunks) { uint32_t funcIndex = callThunk.u.funcIndex; callThunk.u.codeRangeIndex = funcIndexToCodeRange_[funcIndex]; masm_.patchThunk(callThunk.offset, funcCodeRange(funcIndex).funcNonProfilingEntry()); } for (JumpTarget target : MakeEnumeratedRange(JumpTarget::Limit)) { for (uint32_t thunkOffset : jumpThunks_[target]) masm_.patchThunk(thunkOffset, jumpTargets[target].begin); } // Code-generation is complete! masm_.finish(); return !masm_.oom(); }
/// Do expand branches and split the basic blocks if necessary. /// Returns true if made any change. bool MSP430BSel::expandBranches(OffsetVector &BlockOffsets) { // For each conditional branch, if the offset to its destination is larger // than the offset field allows, transform it into a long branch sequence // like this: // short branch: // bCC MBB // long branch: // b!CC $PC+6 // b MBB // bool MadeChange = false; for (auto MBB = MF->begin(), E = MF->end(); MBB != E; ++MBB) { unsigned MBBStartOffset = 0; for (auto MI = MBB->begin(), EE = MBB->end(); MI != EE; ++MI) { MBBStartOffset += TII->getInstSizeInBytes(*MI); // If this instruction is not a short branch then skip it. if (MI->getOpcode() != MSP430::JCC && MI->getOpcode() != MSP430::JMP) { continue; } MachineBasicBlock *DestBB = MI->getOperand(0).getMBB(); // Determine the distance from the current branch to the destination // block. MBBStartOffset already includes the size of the current branch // instruction. int BlockDistance = BlockOffsets[DestBB->getNumber()] - BlockOffsets[MBB->getNumber()]; int BranchDistance = BlockDistance - MBBStartOffset; // If this branch is in range, ignore it. if (isInRage(BranchDistance)) { continue; } DEBUG(dbgs() << " Found a branch that needs expanding, BB#" << DestBB->getNumber() << ", Distance " << BranchDistance << "\n"); // If JCC is not the last instruction we need to split the MBB. if (MI->getOpcode() == MSP430::JCC && std::next(MI) != EE) { DEBUG(dbgs() << " Found a basic block that needs to be split, BB#" << MBB->getNumber() << "\n"); // Create a new basic block. MachineBasicBlock *NewBB = MF->CreateMachineBasicBlock(MBB->getBasicBlock()); MF->insert(std::next(MBB), NewBB); // Splice the instructions following MI over to the NewBB. NewBB->splice(NewBB->end(), &*MBB, std::next(MI), MBB->end()); // Update the successor lists. for (MachineBasicBlock *Succ : MBB->successors()) { if (Succ == DestBB) { continue; } MBB->replaceSuccessor(Succ, NewBB); NewBB->addSuccessor(Succ); } // We introduced a new MBB so all following blocks should be numbered // and measured again. measureFunction(BlockOffsets, &*MBB); ++NumSplit; // It may be not necessary to start all over at this point, but it's // safer do this anyway. return true; } MachineInstr &OldBranch = *MI; DebugLoc dl = OldBranch.getDebugLoc(); int InstrSizeDiff = -TII->getInstSizeInBytes(OldBranch); if (MI->getOpcode() == MSP430::JCC) { MachineBasicBlock *NextMBB = &*std::next(MBB); assert(MBB->isSuccessor(NextMBB) && "This block must have a layout successor!"); // The BCC operands are: // 0. Target MBB // 1. MSP430 branch predicate SmallVector<MachineOperand, 1> Cond; Cond.push_back(MI->getOperand(1)); // Jump over the long branch on the opposite condition TII->reverseBranchCondition(Cond); MI = BuildMI(*MBB, MI, dl, TII->get(MSP430::JCC)) .addMBB(NextMBB) .add(Cond[0]); InstrSizeDiff += TII->getInstSizeInBytes(*MI); ++MI; } // Unconditional branch to the real destination. MI = BuildMI(*MBB, MI, dl, TII->get(MSP430::Bi)).addMBB(DestBB); InstrSizeDiff += TII->getInstSizeInBytes(*MI); // Remove the old branch from the function. OldBranch.eraseFromParent(); // The size of a new instruction is different from the old one, so we need // to correct all block offsets. for (int i = MBB->getNumber() + 1, e = BlockOffsets.size(); i < e; ++i) { BlockOffsets[i] += InstrSizeDiff; } MBBStartOffset += InstrSizeDiff; ++NumExpanded; MadeChange = true; } } return MadeChange; }
bool ModuleGenerator::finishCodegen() { masm_.haltingAlign(CodeAlignment); uint32_t offsetInWhole = masm_.size(); uint32_t numFuncExports = metadata_->funcExports.length(); MOZ_ASSERT(numFuncExports == exportedFuncs_.count()); // Generate stubs in a separate MacroAssembler since, otherwise, for modules // larger than the JumpImmediateRange, even local uses of Label will fail // due to the large absolute offsets temporarily stored by Label::bind(). OffsetVector entries; ProfilingOffsetVector interpExits; ProfilingOffsetVector jitExits; TrapExitOffsetArray trapExits; Offsets outOfBoundsExit; Offsets unalignedAccessExit; Offsets interruptExit; Offsets throwStub; { TempAllocator alloc(&lifo_); MacroAssembler masm(MacroAssembler::WasmToken(), alloc); Label throwLabel; if (!entries.resize(numFuncExports)) return false; for (uint32_t i = 0; i < numFuncExports; i++) entries[i] = GenerateEntry(masm, metadata_->funcExports[i]); if (!interpExits.resize(numFuncImports())) return false; if (!jitExits.resize(numFuncImports())) return false; for (uint32_t i = 0; i < numFuncImports(); i++) { interpExits[i] = GenerateImportInterpExit(masm, metadata_->funcImports[i], i, &throwLabel); jitExits[i] = GenerateImportJitExit(masm, metadata_->funcImports[i], &throwLabel); } for (Trap trap : MakeEnumeratedRange(Trap::Limit)) trapExits[trap] = GenerateTrapExit(masm, trap, &throwLabel); outOfBoundsExit = GenerateOutOfBoundsExit(masm, &throwLabel); unalignedAccessExit = GenerateUnalignedExit(masm, &throwLabel); interruptExit = GenerateInterruptExit(masm, &throwLabel); throwStub = GenerateThrowStub(masm, &throwLabel); if (masm.oom() || !masm_.asmMergeWith(masm)) return false; } // Adjust each of the resulting Offsets (to account for being merged into // masm_) and then create code ranges for all the stubs. for (uint32_t i = 0; i < numFuncExports; i++) { entries[i].offsetBy(offsetInWhole); metadata_->funcExports[i].initEntryOffset(entries[i].begin); if (!metadata_->codeRanges.emplaceBack(CodeRange::Entry, entries[i])) return false; } for (uint32_t i = 0; i < numFuncImports(); i++) { interpExits[i].offsetBy(offsetInWhole); metadata_->funcImports[i].initInterpExitOffset(interpExits[i].begin); if (!metadata_->codeRanges.emplaceBack(CodeRange::ImportInterpExit, interpExits[i])) return false; jitExits[i].offsetBy(offsetInWhole); metadata_->funcImports[i].initJitExitOffset(jitExits[i].begin); if (!metadata_->codeRanges.emplaceBack(CodeRange::ImportJitExit, jitExits[i])) return false; } for (Trap trap : MakeEnumeratedRange(Trap::Limit)) { trapExits[trap].offsetBy(offsetInWhole); if (!metadata_->codeRanges.emplaceBack(CodeRange::TrapExit, trapExits[trap])) return false; } outOfBoundsExit.offsetBy(offsetInWhole); if (!metadata_->codeRanges.emplaceBack(CodeRange::Inline, outOfBoundsExit)) return false; unalignedAccessExit.offsetBy(offsetInWhole); if (!metadata_->codeRanges.emplaceBack(CodeRange::Inline, unalignedAccessExit)) return false; interruptExit.offsetBy(offsetInWhole); if (!metadata_->codeRanges.emplaceBack(CodeRange::Inline, interruptExit)) return false; throwStub.offsetBy(offsetInWhole); if (!metadata_->codeRanges.emplaceBack(CodeRange::Inline, throwStub)) return false; // Fill in LinkData with the offsets of these stubs. linkData_.outOfBoundsOffset = outOfBoundsExit.begin; linkData_.interruptOffset = interruptExit.begin; // Now that all other code has been emitted, patch all remaining callsites. if (!patchCallSites(&trapExits)) return false; // Now that all code has been generated, patch far jumps to destinations. for (CallThunk& callThunk : metadata_->callThunks) { uint32_t funcIndex = callThunk.u.funcIndex; callThunk.u.codeRangeIndex = funcToCodeRange_[funcIndex]; CodeOffset farJump(callThunk.offset); masm_.patchFarJump(farJump, funcCodeRange(funcIndex).funcNonProfilingEntry()); } for (const TrapFarJump& farJump : masm_.trapFarJumps()) masm_.patchFarJump(farJump.jump, trapExits[farJump.trap].begin); // Code-generation is complete! masm_.finish(); return !masm_.oom(); }