| // Copyright 2012 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // |
| // Implementation of the BlockBuilder class. |
| // |
| // TODO(rogerm): Revisit the BasicBlockDecomposer/BlockBuilder interface |
| // via the BasicBlockSubgraph. Consider copying out the block data into |
| // the subgraph instead of having it reference the original block. |
| |
| #include "syzygy/block_graph/block_builder.h" |
| |
| #include <limits> |
| #include <map> |
| #include <utility> |
| #include <vector> |
| |
| #include "syzygy/block_graph/basic_block.h" |
| #include "syzygy/block_graph/basic_block_assembler.h" |
| #include "syzygy/block_graph/basic_block_subgraph.h" |
| #include "syzygy/block_graph/block_graph.h" |
| #include "syzygy/common/align.h" |
| #include "syzygy/core/assembler.h" |
| |
| namespace block_graph { |
| |
| namespace { |
| |
| // A bunch of handy typedefs for some verbose types. |
| typedef BlockGraph::Block Block; |
| typedef BlockGraph::Offset Offset; |
| typedef BlockGraph::Size Size; |
| typedef BasicBlockSubGraph::BlockDescription BlockDescription; |
| typedef BasicBlockSubGraph::BlockDescriptionList BlockDescriptionList; |
| typedef BasicBlockSubGraph::BasicBlockOrdering BasicBlockOrdering; |
| typedef BasicBlockOrdering::const_iterator BasicBlockOrderingConstIter; |
| typedef BlockDescriptionList::const_iterator BlockDescriptionConstIter; |
| typedef BasicBlock::Instructions::const_iterator InstructionConstIter; |
| typedef BasicBlock::Successors::const_iterator SuccessorConstIter; |
| |
| // Definitions of various length NOP codes for 32-bit X86. We use the same |
| // ones that are typically used by MSVC and recommended by Intel. |
| |
| // NOP (XCHG EAX, EAX) |
| const uint8 kNop1[1] = { 0x90 }; |
| // 66 NOP |
| const uint8 kNop2[2] = { 0x66, 0x90 }; |
| // LEA REG, 0 (REG) (8-bit displacement) |
| const uint8 kNop3[3] = { 0x66, 0x66, 0x90 }; |
| // NOP DWORD PTR [EAX + 0] (8-bit displacement) |
| const uint8 kNop4[4] = { 0x0F, 0x1F, 0x40, 0x00 }; |
| // NOP DWORD PTR [EAX + EAX*1 + 0] (8-bit displacement) |
| const uint8 kNop5[5] = { 0x0F, 0x1F, 0x44, 0x00, 0x00 }; |
| // LEA REG, 0 (REG) (32-bit displacement) |
| const uint8 kNop6[6] = { 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00 }; |
| // LEA REG, 0 (REG) (32-bit displacement) |
| const uint8 kNop7[7] = { 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00 }; |
| // NOP DWORD PTR [EAX + EAX*1 + 0] (32-bit displacement) |
| const uint8 kNop8[8] = { 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
| // NOP WORD PTR [EAX + EAX*1 + 0] (32-bit displacement) |
| const uint8 kNop9[9] = { 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 }; |
| |
| // Collect all of the various NOPs in an array indexable by their length. |
| const uint8* kNops[] = { NULL, kNop1, kNop2, kNop3, kNop4, kNop5, kNop6, |
| kNop7, kNop8, kNop9 }; |
| |
| const size_t kInvalidSize = -1; |
| |
| // Updates the tag info map for the given taggable object. |
| void UpdateTagInfoMap(const TagSet& tag_set, |
| TaggedObjectType type, |
| BlockGraph::Block* block, |
| BlockGraph::Offset offset, |
| BlockGraph::Size size, |
| TagInfoMap* tag_info_map) { |
| DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), block); |
| DCHECK_NE(reinterpret_cast<TagInfoMap*>(NULL), tag_info_map); |
| TagInfo tag_info(type, block, offset, size); |
| TagSet::const_iterator tag_it = tag_set.begin(); |
| for (; tag_it != tag_set.end(); ++tag_it) |
| (*tag_info_map)[*tag_it].push_back(tag_info); |
| } |
| |
| // Checks that any BasicEndBlocks are at the end of their associated |
| // basic-block order. |
| bool EndBlocksAreWellPlaced(BasicBlockSubGraph* subgraph) { |
| DCHECK_NE(reinterpret_cast<BasicBlockSubGraph*>(NULL), subgraph); |
| |
| BasicBlockSubGraph::BlockDescriptionList::iterator bd_it = |
| subgraph->block_descriptions().begin(); |
| for (; bd_it != subgraph->block_descriptions().end(); ++bd_it) { |
| BasicBlockSubGraph::BasicBlockOrdering& bbs = bd_it->basic_block_order; |
| BasicBlockSubGraph::BasicBlockOrdering::iterator bb_it = bbs.begin(); |
| BasicBlockSubGraph::BasicBlockOrdering::iterator bb_it_end = bbs.end(); |
| bool end_block_seen = false; |
| for (; bb_it != bb_it_end; ++bb_it) { |
| if ((*bb_it)->type() == BasicBlock::BASIC_END_BLOCK) { |
| end_block_seen = true; |
| } else { |
| // If we've already seen a basic end block and now we've encountered |
| // a basic block of another type then the BB order is invalid. |
| if (end_block_seen) { |
| LOG(ERROR) << "Basic-block order has end-block in invalid location!"; |
| return false; |
| } |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| // Adds a new label to a block, or merges a label with an existing one. This is |
| // used because labels may collide when creating a new block, as BasicEndBlocks |
| // have zero size. |
| void AddOrMergeLabel(BlockGraph::Offset offset, |
| const BlockGraph::Label& label, |
| BlockGraph::Block* block) { |
| DCHECK_NE(reinterpret_cast<BlockGraph::Block*>(NULL), block); |
| |
| BlockGraph::Label previous_label; |
| if (block->GetLabel(offset, &previous_label)) { |
| std::string new_name = previous_label.name() + ", " + label.name(); |
| BlockGraph::LabelAttributes new_attr = previous_label.attributes() | |
| label.attributes(); |
| CHECK(block->RemoveLabel(offset)); |
| CHECK(block->SetLabel(offset, new_name, new_attr)); |
| } else { |
| CHECK(block->SetLabel(offset, label)); |
| } |
| } |
| |
| // A utility class to package up the context in which new blocks are generated. |
| class MergeContext { |
| public: |
| // Initialize a MergeContext with the block graph and original block. |
| MergeContext(BlockGraph* bg, const Block* ob, TagInfoMap* tag_info_map) |
| : sub_graph_(NULL), block_graph_(bg), original_block_(ob), |
| tag_info_map_(tag_info_map) { |
| DCHECK_NE(reinterpret_cast<BlockGraph*>(NULL), bg); |
| DCHECK_NE(reinterpret_cast<TagInfoMap*>(NULL), tag_info_map); |
| } |
| |
| // Accessor. |
| const BlockVector& new_blocks() const { return new_blocks_; } |
| |
| // Generate all of the blocks described in @p subgraph. |
| // @param subgraph Defines the block properties and basic blocks to use |
| // for each of the blocks to be created. |
| bool GenerateBlocks(const BasicBlockSubGraph& subgraph); |
| |
| // Transfers incoming references from the original block to the |
| // newly generated block or blocks. |
| // @param subgraph The subgraph. |
| void TransferReferrers(const BasicBlockSubGraph* subgraph) const; |
| |
| // A clean-up function to remove the original block from which @p subgraph |
| // is derived (if any) from the block graph. This must only be performed |
| // after having updated the block graph with the new blocks and transferred |
| // all references to the new block(s). |
| // @param subgraph The subgraph. |
| void RemoveOriginalBlock(BasicBlockSubGraph* subgraph); |
| |
| private: |
| typedef BlockGraph::Block::SourceRange SourceRange; |
| |
| // Temporary data structures used during layouting. |
| // This can effectively have 3 states: |
| // condition == Successor::kInvalidCondition && successor == NULL. |
| // The successor is unused. |
| // condition == Successor::kInvalidCondition && successor != NULL. |
| // The successor is elided (has no representation in the block). |
| // condition != Successor::kInvalidCondition && successor != NULL. |
| // The successor will have an explicit representation in the block. |
| struct SuccessorLayoutInfo { |
| // The condition flags for this successor. |
| // Set to Successor::kInvalidCondition if unused or elided. |
| Successor::Condition condition; |
| // The reference this condition refers to. |
| BasicBlockReference reference; |
| // The size of this successor's manifestation. |
| Size size; |
| // A pointer to the original successor. This is used for propagating tags. |
| // This is set to NULL if the successor isn't used at all. |
| const Successor* successor; |
| }; |
| struct BasicBlockLayoutInfo { |
| // The basic block this layout info concerns. Useful for debugging. |
| const BasicBlock* basic_block; |
| |
| // Stores the block that basic_block will be manifested in. |
| Block* block; |
| |
| // Current start offset for basic_block in block. |
| Offset start_offset; |
| |
| // Size of basic_block. |
| Size basic_block_size; |
| |
| // The label to assign our successor(s), if any. |
| BlockGraph::Label successor_label; |
| |
| // The source range this successor originally occupied, if any. |
| SourceRange successor_source_range; |
| |
| // Layout info for this block's successors. |
| SuccessorLayoutInfo successors[2]; |
| }; |
| typedef std::map<const BasicBlock*, BasicBlockLayoutInfo> |
| BasicBlockLayoutInfoMap; |
| |
| // Update the new block with the source range for the bytes in the |
| // range [new_offset, new_offset + new_size). |
| // @param source_range The source range (if any) to assign. |
| // @param new_offset The offset in the new block where the original bytes |
| // will now live. |
| // @param new_size The number of bytes the new range occupies. |
| // @param new_block The block to change. |
| void CopySourceRange(const SourceRange& source_range, |
| Offset new_offset, |
| Size new_size, |
| Block* new_block); |
| |
| // Copy @p references to @p new_block, starting at @p offset. |
| // @param references The references to copy. |
| // @param offset The starting offset in @p new_block to copy to. |
| // @param new_block The block to copy the references to. |
| // @pre Layouting has been done for all referred basic blocks. |
| void CopyReferences(const BasicBlock::BasicBlockReferenceMap& references, |
| Offset offset, |
| Block* new_block); |
| |
| // Assemble the instruction(s) to implement the successors in @p info. |
| // @param info The layout info for the basic block in question. |
| bool AssembleSuccessors(const BasicBlockLayoutInfo& info); |
| |
| // Insert NOPs into the given byte range. |
| // @param offset the offset at which to insert NOPs. |
| // @param bytes the number of NOP bytes to insert. |
| // @param new_block the block in which to insert the NOPs. |
| bool InsertNops(Offset offset, Size bytes, Block* new_block); |
| |
| // Copy the given @p instructions to the current working block. |
| // @param instructions The instructions to copy. |
| // @param offset The offset where the @p instructions should be inserted. |
| // @param new_block the block in which to insert the instructions. |
| bool CopyInstructions(const BasicBlock::Instructions& instructions, |
| Offset offset, |
| Block* new_block); |
| |
| // Copy the data (or padding bytes) in @p basic_block into new working block. |
| // @param basic_block The basic_block to copy. |
| // @param offset The offset where the @p basic_block should be inserted. |
| // @param new_block the block in which to insert the data. |
| bool CopyData(const BasicDataBlock* basic_block, |
| Offset offset, |
| Block* new_block); |
| |
| // Initializes layout information for @p order and stores it in layout_info_. |
| // @param order The basic block ordering to process. |
| // @param block The destination block for this ordering. |
| bool InitializeBlockLayout(const BasicBlockOrdering& order, Block* block); |
| |
| // Generates a layout for @p order. This layout will arrange each basic block |
| // in the ordering back-to-back with minimal reach encodings on each |
| // successor, while respecting basic block alignments. |
| // @param order The basic block ordering to process. |
| bool GenerateBlockLayout(const BasicBlockOrdering& order); |
| |
| // Generates a layout for @p subgraph and stores it in layout_info_. |
| // @param subgraph The subgraph to process. |
| bool GenerateLayout(const BasicBlockSubGraph& subgraph); |
| |
| // Populate a new block with data and/or instructions per |
| // its corresponding layout. |
| // @param order their ordering. |
| bool PopulateBlock(const BasicBlockOrdering& order); |
| |
| // Populate all new blocks with data and/or instructions per layout_info_. |
| // @param subgraph The subgraph to process. |
| bool PopulateBlocks(const BasicBlockSubGraph& subgraph); |
| |
| // Transfer all external referrers that refer to @p bb to point to |
| // bb's new location instead of to the original block. |
| // @param bb The basic block. |
| void UpdateReferrers(const BasicBlock* bb) const; |
| |
| // Returns the minimal successor size for @p condition. |
| static Size GetShortSuccessorSize(Successor::Condition condition); |
| // Returns the maximal successor size for @p condition. |
| static Size GetLongSuccessorSize(Successor::Condition condition); |
| |
| // Computes and returns the required successor size for @p successor. |
| // @param info The layout info for the basic block. |
| // @param start_offset Offset from the start of @p info.basic_block to |
| // the first byte of the successor. |
| // @param successor The successor to size. |
| Size ComputeRequiredSuccessorSize(const BasicBlockLayoutInfo& info, |
| Offset start_offset, |
| const SuccessorLayoutInfo& successor); |
| |
| // Finds the layout info for a given basic block. |
| // @param bb The basic block whose layout info is desired. |
| BasicBlockLayoutInfo& FindLayoutInfo(const BasicBlock* bb); |
| const BasicBlockLayoutInfo& FindLayoutInfo(const BasicBlock* bb) const; |
| |
| // Resolves a basic block reference to a block reference. |
| // @param type The desired type of the returned reference. |
| // @param size The desired size of the returned reference. |
| // @param ref The basic block reference to resolve. |
| // @pre GenerateLayout has succeeded. |
| BlockGraph::Reference ResolveReference(BlockGraph::ReferenceType type, |
| Size size, |
| const BasicBlockReference& ref) const; |
| |
| // Resolves a basic block reference to a block reference. |
| // @param ref The basic block reference to resolve. |
| // @pre GenerateLayout has succeeded. |
| BlockGraph::Reference ResolveReference(const BasicBlockReference& ref) const; |
| |
| private: |
| // The subgraph we're layouting. |
| const BasicBlockSubGraph* sub_graph_; |
| |
| // Layout info. |
| BasicBlockLayoutInfoMap layout_info_; |
| |
| // The block graph in which the new blocks are generated. |
| BlockGraph* const block_graph_; |
| |
| // The original block from which the new blocks are derived. |
| const Block* original_block_; |
| |
| // The tag info map tracking all user data in the subgraph. |
| TagInfoMap* tag_info_map_; |
| |
| // The set of blocks generated in this context so far. |
| BlockVector new_blocks_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MergeContext); |
| }; |
| |
| bool MergeContext::GenerateBlocks(const BasicBlockSubGraph& subgraph) { |
| sub_graph_ = &subgraph; |
| |
| if (!GenerateLayout(subgraph) || !PopulateBlocks(subgraph)) { |
| // Remove generated blocks (this is safe as they're all disconnected) |
| // and return false. |
| BlockVector::iterator it = new_blocks_.begin(); |
| for (; it != new_blocks_.end(); ++it) { |
| DCHECK((*it)->referrers().empty()); |
| DCHECK((*it)->references().empty()); |
| block_graph_->RemoveBlock(*it); |
| } |
| new_blocks_.clear(); |
| |
| sub_graph_ = NULL; |
| return false; |
| } |
| |
| sub_graph_ = NULL; |
| return true; |
| } |
| |
| void MergeContext::TransferReferrers(const BasicBlockSubGraph* subgraph) const { |
| // Iterate through the layout info, and update each referenced BB. |
| BasicBlockLayoutInfoMap::const_iterator it = layout_info_.begin(); |
| for (; it != layout_info_.end(); ++it) |
| UpdateReferrers(it->second.basic_block); |
| } |
| |
| void MergeContext::CopySourceRange(const SourceRange& source_range, |
| Offset new_offset, |
| Size new_size, |
| Block* new_block) { |
| DCHECK_LE(0, new_offset); |
| DCHECK_NE(0U, new_size); |
| |
| // If the range is empty, there's nothing to do. |
| if (source_range.size() == 0) |
| return; |
| |
| // Insert the new source range mapping into the new block. |
| bool inserted = new_block->source_ranges().Insert( |
| Block::DataRange(new_offset, new_size), source_range); |
| DCHECK(inserted); |
| } |
| |
| bool MergeContext::AssembleSuccessors(const BasicBlockLayoutInfo& info) { |
| BasicBlock::Instructions instructions; |
| BasicBlockAssembler assm(info.start_offset + info.basic_block_size, |
| instructions.begin(), &instructions); |
| |
| // Copy the successor label, if any, to where it belongs. |
| if (info.successor_label.IsValid()) { |
| AddOrMergeLabel(info.start_offset + info.basic_block_size, |
| info.successor_label, |
| info.block); |
| } |
| |
| Offset successor_start = info.start_offset + info.basic_block_size; |
| for (size_t i = 0; i < arraysize(info.successors); ++i) { |
| const SuccessorLayoutInfo& successor = info.successors[i]; |
| |
| // Exit loop early if appropriate. |
| if (successor.condition == Successor::kInvalidCondition && |
| successor.successor == NULL) { |
| break; |
| } |
| |
| // A pointer to the original successor should always be set for any |
| // manifested or elided successor. |
| DCHECK_NE(reinterpret_cast<Successor*>(NULL), successor.successor); |
| |
| // If this represents an elided successor then simply emit tag information |
| // and continue. |
| if (successor.condition == Successor::kInvalidCondition) { |
| // Update the tag-info map for the successor. |
| UpdateTagInfoMap(successor.successor->tags(), kSuccessorTag, info.block, |
| successor_start, 0, tag_info_map_); |
| UpdateTagInfoMap(successor.successor->reference().tags(), |
| kReferenceTag, info.block, successor_start, 0, |
| tag_info_map_); |
| continue; |
| } |
| |
| // Default to a short reference. |
| ValueSize reference_size = core::kSize8Bit; |
| if (successor.size != GetShortSuccessorSize(successor.condition)) |
| reference_size = core::kSize32Bit; |
| |
| // Construct the reference we're going to deposit into the instruction |
| // list first. |
| UntypedReference untyped_ref(successor.reference); |
| |
| // For debugging, we stomp the correct offset into the re-generated block |
| // for block-internal references. |
| BlockGraph::Reference resolved_ref = ResolveReference(successor.reference); |
| |
| // Default to the offset immediately following the successor, which |
| // will translate to a zero pc-relative offset. |
| Offset ref_offset = successor_start + successor.size; |
| if (resolved_ref.referenced() == info.block) |
| ref_offset = resolved_ref.offset(); |
| Immediate dest(ref_offset, reference_size, untyped_ref); |
| |
| // Assemble the instruction. |
| switch (successor.condition) { |
| case Successor::kConditionAbove: |
| case Successor::kConditionAboveOrEqual: |
| case Successor::kConditionBelow: |
| case Successor::kConditionBelowOrEqual: |
| case Successor::kConditionEqual: |
| case Successor::kConditionGreater: |
| case Successor::kConditionGreaterOrEqual: |
| case Successor::kConditionLess: |
| case Successor::kConditionLessOrEqual: |
| case Successor::kConditionNotEqual: |
| case Successor::kConditionNotOverflow: |
| case Successor::kConditionNotParity: |
| case Successor::kConditionNotSigned: |
| case Successor::kConditionOverflow: |
| case Successor::kConditionParity: |
| case Successor::kConditionSigned: |
| assm.j(static_cast<core::ConditionCode>(successor.condition), dest); |
| break; |
| |
| case Successor::kConditionTrue: |
| assm.jmp(dest); |
| break; |
| |
| default: |
| NOTREACHED(); |
| return false; |
| } |
| |
| // Make sure the assembler produced what we expected. |
| DCHECK_EQ(successor.size, instructions.back().size()); |
| DCHECK_EQ(1u, instructions.back().references().size()); |
| |
| // Copy the tags that are associated with the successor reference to the |
| // appropriately sized instruction reference. CopyInstructions will then |
| // take care of updating the tag info map. |
| instructions.back().references().begin()->second.tags() = |
| successor.reference.tags(); |
| |
| // Update the tag-info map for the successor. |
| UpdateTagInfoMap(successor.successor->tags(), kSuccessorTag, info.block, |
| successor_start, successor.size, tag_info_map_); |
| |
| // Walk our start address forwards. |
| successor_start += successor.size; |
| |
| if (info.successor_source_range.size() != 0) { |
| Instruction& instr = instructions.back(); |
| |
| // Attribute this instruction to the original successor's source range. |
| instr.set_source_range(info.successor_source_range); |
| } |
| } |
| |
| if (!instructions.empty()) { |
| Offset start_offset = info.start_offset + info.basic_block_size; |
| return CopyInstructions(instructions, start_offset, info.block); |
| } |
| |
| return true; |
| } |
| |
| bool MergeContext::InsertNops(Offset offset, Size bytes, Block* new_block) { |
| DCHECK_NE(reinterpret_cast<Block*>(NULL), new_block); |
| |
| uint8* buffer = new_block->GetMutableData(); |
| DCHECK_NE(reinterpret_cast<uint8*>(NULL), buffer); |
| |
| size_t kMaxNopLength = arraysize(kNops) - 1; |
| buffer += offset; |
| while (bytes >= kMaxNopLength) { |
| ::memcpy(buffer, kNops[kMaxNopLength], kMaxNopLength); |
| buffer += kMaxNopLength; |
| bytes -= kMaxNopLength; |
| } |
| |
| if (bytes > 0) { |
| DCHECK_GT(kMaxNopLength, bytes); |
| ::memcpy(buffer, kNops[bytes], bytes); |
| } |
| |
| return true; |
| } |
| |
| bool MergeContext::CopyInstructions( |
| const BasicBlock::Instructions& instructions, |
| Offset offset, Block* new_block) { |
| DCHECK_NE(reinterpret_cast<Block*>(NULL), new_block); |
| DCHECK_EQ(BasicBlock::BASIC_CODE_BLOCK, new_block->type()); |
| // Get the target buffer. |
| uint8* buffer = new_block->GetMutableData(); |
| DCHECK_NE(reinterpret_cast<uint8*>(NULL), buffer); |
| |
| // Copy the instruction data and assign each instruction an offset. |
| InstructionConstIter it = instructions.begin(); |
| for (; it != instructions.end(); ++it) { |
| const Instruction& instruction = *it; |
| |
| // Update the tag-info map for the instruction. |
| UpdateTagInfoMap(instruction.tags(), kInstructionTag, new_block, |
| offset, instruction.size(), tag_info_map_); |
| |
| // Copy the instruction bytes. |
| ::memcpy(buffer + offset, instruction.data(), instruction.size()); |
| |
| // Preserve the label on the instruction, if any. |
| if (instruction.has_label()) |
| AddOrMergeLabel(offset, instruction.label(), new_block); |
| |
| // Record the source range. |
| CopySourceRange(instruction.source_range(), |
| offset, instruction.size(), |
| new_block); |
| |
| // Copy references. |
| CopyReferences(instruction.references(), offset, new_block); |
| |
| // Update the offset/bytes_written. |
| offset += instruction.size(); |
| } |
| |
| return true; |
| } |
| |
| void MergeContext::CopyReferences( |
| const BasicBlock::BasicBlockReferenceMap& references, |
| Offset offset, Block* new_block) { |
| BasicBlock::BasicBlockReferenceMap::const_iterator it = references.begin(); |
| for (; it != references.end(); ++it) { |
| BlockGraph::Reference resolved = ResolveReference(it->second); |
| |
| Offset ref_offset = offset + it->first; |
| CHECK(new_block->SetReference(ref_offset, resolved)); |
| |
| // Update the tag-info map for this reference. |
| UpdateTagInfoMap(it->second.tags(), kReferenceTag, new_block, ref_offset, |
| resolved.size(), tag_info_map_); |
| } |
| } |
| |
| bool MergeContext::CopyData(const BasicDataBlock* data_block, |
| Offset offset, |
| Block* new_block) { |
| DCHECK_NE(reinterpret_cast<BasicDataBlock*>(NULL), data_block); |
| DCHECK_EQ(BasicBlock::BASIC_DATA_BLOCK, data_block->type()); |
| |
| // Get the target buffer. |
| uint8* buffer = new_block->GetMutableData(); |
| DCHECK_NE(reinterpret_cast<uint8*>(NULL), buffer); |
| |
| // Copy the basic-new_block_'s data bytes. |
| ::memcpy(buffer + offset, data_block->data(), data_block->size()); |
| |
| // Record the source range. |
| CopySourceRange(data_block->source_range(), |
| offset, data_block->size(), |
| new_block); |
| |
| CopyReferences(data_block->references(), offset, new_block); |
| return true; |
| } |
| |
| bool MergeContext::InitializeBlockLayout(const BasicBlockOrdering& order, |
| Block* new_block) { |
| // Populate the initial layout info. |
| BasicBlockOrderingConstIter it = order.begin(); |
| for (; it != order.end(); ++it) { |
| const BasicBlock* bb = *it; |
| |
| // Propagate BB alignment to the parent block. |
| if (bb->alignment() > new_block->alignment()) |
| new_block->set_alignment(bb->alignment()); |
| |
| // Create and initialize the layout info for this block. |
| DCHECK(layout_info_.find(bb) == layout_info_.end()); |
| |
| BasicBlockLayoutInfo& info = layout_info_[bb]; |
| info.basic_block = bb; |
| info.block = new_block; |
| info.start_offset = 0; |
| info.basic_block_size = kInvalidSize; |
| |
| const BasicCodeBlock* code_block = BasicCodeBlock::Cast(bb); |
| if (code_block != NULL) |
| info.basic_block_size = code_block->GetInstructionSize(); |
| |
| const BasicDataBlock* data_block = BasicDataBlock::Cast(bb); |
| if (data_block != NULL) |
| info.basic_block_size = data_block->size(); |
| |
| const BasicEndBlock* end_block = BasicEndBlock::Cast(bb); |
| if (end_block != NULL) |
| info.basic_block_size = end_block->size(); |
| |
| // The size must have been set. |
| DCHECK_NE(kInvalidSize, info.basic_block_size); |
| |
| for (size_t i = 0; i < arraysize(info.successors); ++i) { |
| info.successors[i].condition = Successor::kInvalidCondition; |
| info.successors[i].size = 0; |
| info.successors[i].successor = NULL; |
| } |
| |
| // Find the next basic block, if any. |
| BasicBlockOrderingConstIter next_it(it); |
| ++next_it; |
| const BasicBlock* next_bb = NULL; |
| if (next_it != order.end()) |
| next_bb = *(next_it); |
| |
| if (code_block == NULL) |
| continue; |
| |
| // Go through and decide how to manifest the successors for the current |
| // basic block. A basic block has zero, one or two successors, and any |
| // successor that refers to the next basic block in sequence is elided, as |
| // it's most efficient for execution to simply fall through. We do this in |
| // two nearly-identical code blocks, as the handling is only near-identical |
| // for each of two possible successors. |
| DCHECK_GE(2U, code_block->successors().size()); |
| SuccessorConstIter succ_it = code_block->successors().begin(); |
| SuccessorConstIter succ_end = code_block->successors().end(); |
| |
| // Process the first successor, if any. |
| size_t manifested_successors = 0; |
| size_t elided_successors = 0; |
| if (succ_it != succ_end) { |
| const BasicBlock* destination_bb = succ_it->reference().basic_block(); |
| |
| // Record the source range of the original successor. |
| if (succ_it->source_range().size() != 0) { |
| DCHECK_EQ(0U, info.successor_source_range.size()); |
| info.successor_source_range = succ_it->source_range(); |
| } |
| // Record the label of the original successor. |
| if (succ_it->has_label()) |
| info.successor_label = succ_it->label(); |
| |
| // Only manifest this successor if it's not referencing the next block. |
| SuccessorLayoutInfo& successor = |
| info.successors[manifested_successors + elided_successors]; |
| successor.successor = &(*succ_it); |
| if (destination_bb == NULL || destination_bb != next_bb) { |
| ++manifested_successors; |
| successor.condition = succ_it->condition(); |
| successor.reference = succ_it->reference(); |
| } else { |
| ++elided_successors; |
| } |
| |
| // Go to the next successor, if any. |
| ++succ_it; |
| } |
| |
| // Process the second successor, if any. |
| if (succ_it != succ_end) { |
| const BasicBlock* destination_bb = succ_it->reference().basic_block(); |
| |
| // Record the source range of the original successor. |
| if (succ_it->source_range().size() != 0) { |
| DCHECK_EQ(0U, info.successor_source_range.size()); |
| info.successor_source_range = succ_it->source_range(); |
| } |
| // Record the label of the original successor. |
| if (succ_it->has_label()) { |
| DCHECK(!info.successor_label.IsValid()); |
| info.successor_label = succ_it->label(); |
| } |
| |
| // Only manifest this successor if it's not referencing the next block. |
| SuccessorLayoutInfo& successor = |
| info.successors[manifested_successors + elided_successors]; |
| successor.successor = &(*succ_it); |
| if (destination_bb == NULL || destination_bb != next_bb) { |
| Successor::Condition condition = succ_it->condition(); |
| |
| // If we've already manifested a successor above, it'll be for the |
| // complementary condition to ours. While it's correct to manifest it |
| // as a conditional branch, it's more efficient to manifest as an |
| // unconditional jump. |
| if (manifested_successors != 0) { |
| DCHECK_EQ(Successor::InvertCondition(info.successors[0].condition), |
| succ_it->condition()); |
| |
| condition = Successor::kConditionTrue; |
| } |
| |
| ++manifested_successors; |
| |
| successor.condition = condition; |
| successor.reference = succ_it->reference(); |
| } else { |
| ++elided_successors; |
| } |
| } |
| |
| // A basic-block can have at most 2 successors by definition. Since we emit |
| // a successor layout struct for each one (whether or not its elided), we |
| // expect there to have been as many successors emitted as there are |
| // successors in the basic block itself. |
| DCHECK_GE(arraysize(info.successors), |
| manifested_successors + elided_successors); |
| DCHECK_EQ(code_block->successors().size(), |
| manifested_successors + elided_successors); |
| } |
| |
| return true; |
| } |
| |
| bool MergeContext::GenerateBlockLayout(const BasicBlockOrdering& order) { |
| BasicBlockOrderingConstIter it = order.begin(); |
| |
| // Loop over the layout, expanding successors until stable. |
| while (true) { |
| bool expanded_successor = false; |
| |
| // Update the start offset for each of the BBs, respecting the BB alignment |
| // constraints. |
| it = order.begin(); |
| Offset next_block_start = 0; |
| Block* new_block = NULL; |
| for (; it != order.end(); ++it) { |
| BasicBlockLayoutInfo& info = FindLayoutInfo(*it); |
| next_block_start = common::AlignUp(next_block_start, |
| info.basic_block->alignment()); |
| info.start_offset = next_block_start; |
| |
| if (new_block == NULL) |
| new_block = info.block; |
| DCHECK(new_block == info.block); |
| |
| next_block_start += info.basic_block_size + |
| info.successors[0].size + |
| info.successors[1].size; |
| } |
| |
| // See whether there's a need to expand the successor sizes. |
| it = order.begin(); |
| for (; it != order.end(); ++it) { |
| const BasicBlock* bb = *it; |
| BasicBlockLayoutInfo& info = FindLayoutInfo(bb); |
| |
| // Compute the start offset for this block's first successor. |
| Offset start_offset = info.start_offset + info.basic_block_size; |
| for (size_t i = 0; i < arraysize(info.successors); ++i) { |
| SuccessorLayoutInfo& successor = info.successors[i]; |
| |
| // Exit the loop if this (and possibly the subsequent) successor |
| // is un-manifested. |
| if (successor.condition == Successor::kInvalidCondition && |
| successor.successor == NULL) { |
| break; |
| } |
| |
| // Skip over elided successors. |
| DCHECK_NE(reinterpret_cast<Successor*>(NULL), successor.successor); |
| if (successor.condition == Successor::kInvalidCondition) |
| continue; |
| |
| // Compute the new size and update the start offset for the next |
| // successor (if any). |
| Size new_size = |
| ComputeRequiredSuccessorSize(info, start_offset, successor); |
| start_offset += new_size; |
| |
| // Keep the biggest offset used by this jump. A jump may temporarily |
| // appear shorter when the start offset of this basic block has moved |
| // but the offset of the target basic block still needs to be updated |
| // within this iteration. |
| new_size = std::max(successor.size, new_size); |
| |
| // Check whether we're expanding this successor. |
| if (new_size != successor.size) { |
| successor.size = new_size; |
| expanded_successor = true; |
| } |
| } |
| } |
| |
| if (!expanded_successor) { |
| // We've achieved a stable layout and we know that next_block_start |
| // is the size of the new block, so resize it and allocate the data now. |
| new_block->set_size(next_block_start); |
| new_block->AllocateData(next_block_start); |
| |
| return true; |
| } |
| } |
| } |
| |
| bool MergeContext::GenerateLayout(const BasicBlockSubGraph& subgraph) { |
| // Create each new block and initialize a layout for it. |
| BlockDescriptionConstIter it = subgraph.block_descriptions().begin(); |
| for (; it != subgraph.block_descriptions().end(); ++it) { |
| const BlockDescription& description = *it; |
| |
| // Skip the block if it's empty. |
| if (description.basic_block_order.empty()) |
| continue; |
| |
| Block* new_block = block_graph_->AddBlock( |
| description.type, 0, description.name); |
| if (new_block == NULL) { |
| LOG(ERROR) << "Failed to create block '" << description.name << "'."; |
| return false; |
| } |
| |
| // Save this block in the set of newly generated blocks. On failure, this |
| // list will be used by GenerateBlocks() to clean up after itself. |
| new_blocks_.push_back(new_block); |
| |
| // Initialize the new block's properties. |
| new_block->set_alignment(description.alignment); |
| new_block->set_section(description.section); |
| new_block->set_attributes(description.attributes); |
| |
| // Initialize the layout for this block. |
| if (!InitializeBlockLayout(description.basic_block_order, new_block)) { |
| LOG(ERROR) << "Failed to initialize layout for basic block '" << |
| description.name << "'"; |
| return false; |
| } |
| } |
| |
| // Now generate a layout for each ordering. |
| it = subgraph.block_descriptions().begin(); |
| for (; it != subgraph.block_descriptions().end(); ++it) { |
| const BlockDescription& description = *it; |
| |
| // Skip the block if it's empty. |
| if (description.basic_block_order.empty()) |
| continue; |
| |
| // Generate the layout for this block. |
| if (!GenerateBlockLayout(description.basic_block_order)) { |
| LOG(ERROR) << "Failed to generate a layout for basic block '" << |
| description.name << "'"; |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool MergeContext::PopulateBlock(const BasicBlockOrdering& order) { |
| // Populate the new block with basic blocks. |
| BasicBlockOrderingConstIter bb_iter = order.begin(); |
| BasicBlockOrderingConstIter bb_end = order.end(); |
| |
| BlockGraph::Offset prev_offset = 0; |
| |
| for (; bb_iter != bb_end; ++bb_iter) { |
| const BasicBlock* bb = *bb_iter; |
| const BasicBlockLayoutInfo& info = FindLayoutInfo(bb); |
| |
| // Determine the tag type and size associated with this basic block. |
| TaggedObjectType tag_type = kBasicDataBlockTag; |
| size_t tag_size = info.basic_block_size; |
| if (bb->type() == BasicBlock::BASIC_CODE_BLOCK) { |
| // We include the size of the successors if its a basic code block. |
| tag_type = kBasicCodeBlockTag; |
| for (size_t i = 0; i < arraysize(info.successors); ++i) { |
| if (info.successors[i].condition != Successor::kInvalidCondition) |
| tag_size += info.successors[i].size; |
| } |
| } |
| |
| // Update the tag-info map for the basic block. |
| UpdateTagInfoMap(bb->tags(), tag_type, info.block, info.start_offset, |
| tag_size, tag_info_map_); |
| |
| // Handle any padding for alignment. |
| if (info.start_offset > prev_offset) { |
| if (!InsertNops(prev_offset, info.start_offset - prev_offset, |
| info.block)) { |
| LOG(ERROR) << "Failed to insert NOPs for '" << bb->name() << "'."; |
| return false; |
| } |
| } |
| prev_offset = info.start_offset + info.basic_block_size + |
| info.successors[0].size + info.successors[1].size; |
| |
| // Handle data basic blocks. |
| const BasicDataBlock* data_block = BasicDataBlock::Cast(bb); |
| if (data_block != NULL) { |
| // If the basic-block is labeled, copy the label. |
| if (data_block->has_label()) |
| AddOrMergeLabel(info.start_offset, data_block->label(), info.block); |
| |
| // Copy its data. |
| if (!CopyData(data_block, info.start_offset, info.block)) { |
| LOG(ERROR) << "Failed to copy data for '" << bb->name() << "'."; |
| return false; |
| } |
| } |
| |
| // Handle code basic blocks. |
| const BasicCodeBlock* code_block = BasicCodeBlock::Cast(bb); |
| if (code_block != NULL) { |
| // Copy the instructions. |
| if (!CopyInstructions(code_block->instructions(), |
| info.start_offset, |
| info.block)) { |
| LOG(ERROR) << "Failed to copy instructions for '" << bb->name() << "'."; |
| return false; |
| } |
| |
| // Synthesize the successor instructions and assign each to an offset. |
| if (!AssembleSuccessors(info)) { |
| LOG(ERROR) << "Failed to create successors for '" << bb->name() << "'."; |
| return false; |
| } |
| } |
| |
| const BasicEndBlock* end_block = BasicEndBlock::Cast(bb); |
| if (end_block != NULL) { |
| // If the end block is labeled, copy the label. |
| if (end_block->has_label()) |
| AddOrMergeLabel(info.start_offset, end_block->label(), info.block); |
| } |
| |
| // We must have handled the basic block as at least one of the fundamental |
| // types. |
| DCHECK(code_block != NULL || data_block != NULL || end_block != NULL); |
| } |
| |
| return true; |
| } |
| |
| bool MergeContext::PopulateBlocks(const BasicBlockSubGraph& subgraph) { |
| // Create each new block and generate a layout for it. |
| BlockDescriptionConstIter it = subgraph.block_descriptions().begin(); |
| for (; it != subgraph.block_descriptions().end(); ++it) { |
| const BasicBlockOrdering& order = it->basic_block_order; |
| |
| // Skip the block if it's empty. |
| if (order.empty()) |
| continue; |
| |
| if (!PopulateBlock(order)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void MergeContext::UpdateReferrers(const BasicBlock* bb) const { |
| DCHECK_NE(reinterpret_cast<BasicBlock*>(NULL), bb); |
| |
| // Find the current location of this basic block. |
| BasicBlockLayoutInfoMap::const_iterator layout_it = layout_info_.find(bb); |
| DCHECK(layout_it != layout_info_.end()); |
| const BasicBlockLayoutInfo& info = layout_it->second; |
| DCHECK_EQ(bb, info.basic_block); |
| |
| // Update all external referrers to point to the new location. |
| const BasicBlock::BasicBlockReferrerSet& referrers = bb->referrers(); |
| BasicBlock::BasicBlockReferrerSet::const_iterator it = referrers.begin(); |
| for (; it != referrers.end(); ++it) { |
| // Get a non-const pointer to the referring block. Note that we don't |
| // modify the set property on referrers as we update the block's references. |
| const BasicBlockReferrer& referrer = *it; |
| Block* referring_block = const_cast<Block*>(referrer.block()); |
| BlockGraph::Reference old_ref; |
| bool found = referring_block->GetReference(referrer.offset(), &old_ref); |
| DCHECK(found); |
| |
| // The base of the reference is directed to the corresponding BB's |
| // start address in the new block. |
| BlockGraph::Reference new_ref(old_ref.type(), |
| old_ref.size(), |
| info.block, |
| info.start_offset, |
| info.start_offset); |
| |
| bool is_new = referring_block->SetReference(referrer.offset(), new_ref); |
| DCHECK(!is_new); // TODO(rogerm): Is this a valid DCHECK? |
| } |
| } |
| |
| void MergeContext::RemoveOriginalBlock(BasicBlockSubGraph* subgraph) { |
| DCHECK_NE(reinterpret_cast<BasicBlockSubGraph*>(NULL), subgraph); |
| DCHECK_EQ(original_block_, subgraph->original_block()); |
| |
| Block* original_block = const_cast<Block*>(this->original_block_); |
| if (original_block == NULL) |
| return; |
| |
| DCHECK(!original_block->HasExternalReferrers()); |
| |
| bool removed = original_block->RemoveAllReferences(); |
| DCHECK(removed); |
| |
| removed = block_graph_->RemoveBlock(original_block); |
| DCHECK(removed); |
| |
| subgraph->set_original_block(NULL); |
| original_block_ = NULL; |
| } |
| |
| Size MergeContext::GetShortSuccessorSize(Successor::Condition condition) { |
| switch (condition) { |
| case Successor::kConditionAbove: |
| case Successor::kConditionAboveOrEqual: |
| case Successor::kConditionBelow: |
| case Successor::kConditionBelowOrEqual: |
| case Successor::kConditionEqual: |
| case Successor::kConditionGreater: |
| case Successor::kConditionGreaterOrEqual: |
| case Successor::kConditionLess: |
| case Successor::kConditionLessOrEqual: |
| case Successor::kConditionNotEqual: |
| case Successor::kConditionNotOverflow: |
| case Successor::kConditionNotParity: |
| case Successor::kConditionNotSigned: |
| case Successor::kConditionOverflow: |
| case Successor::kConditionParity: |
| case Successor::kConditionSigned: |
| // Translates to a conditional branch. |
| return core::AssemblerImpl::kShortBranchSize; |
| |
| case Successor::kConditionTrue: |
| // Translates to a jump. |
| return core::AssemblerImpl::kShortJumpSize; |
| |
| default: |
| NOTREACHED() << "Unsupported successor type."; |
| return 0; |
| } |
| } |
| |
| Size MergeContext::GetLongSuccessorSize(Successor::Condition condition) { |
| switch (condition) { |
| case Successor::kConditionAbove: |
| case Successor::kConditionAboveOrEqual: |
| case Successor::kConditionBelow: |
| case Successor::kConditionBelowOrEqual: |
| case Successor::kConditionEqual: |
| case Successor::kConditionGreater: |
| case Successor::kConditionGreaterOrEqual: |
| case Successor::kConditionLess: |
| case Successor::kConditionLessOrEqual: |
| case Successor::kConditionNotEqual: |
| case Successor::kConditionNotOverflow: |
| case Successor::kConditionNotParity: |
| case Successor::kConditionNotSigned: |
| case Successor::kConditionOverflow: |
| case Successor::kConditionParity: |
| case Successor::kConditionSigned: |
| // Translates to a conditional branch. |
| return core::AssemblerImpl::kLongBranchSize; |
| |
| case Successor::kConditionTrue: |
| // Translates to a jump. |
| return core::AssemblerImpl::kLongJumpSize; |
| |
| default: |
| NOTREACHED() << "Unsupported successor type."; |
| return 0; |
| } |
| } |
| |
| Size MergeContext::ComputeRequiredSuccessorSize( |
| const BasicBlockLayoutInfo& info, |
| Offset start_offset, |
| const SuccessorLayoutInfo& successor) { |
| switch (successor.reference.referred_type()) { |
| case BasicBlockReference::REFERRED_TYPE_BLOCK: |
| return GetLongSuccessorSize(successor.condition); |
| |
| case BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK: { |
| Size short_size = GetShortSuccessorSize(successor.condition); |
| const BasicBlock* dest_bb = successor.reference.basic_block(); |
| DCHECK_NE(reinterpret_cast<BasicBlock*>(NULL), dest_bb); |
| const BasicBlockLayoutInfo& dest = FindLayoutInfo(dest_bb); |
| |
| // If the destination is within the same destination block, |
| // let's see if we can use a short reach here. |
| if (info.block == dest.block) { |
| Offset destination_offset = |
| dest.start_offset - (start_offset + short_size); |
| |
| // Are we in-bounds for a short reference? |
| if (destination_offset <= std::numeric_limits<int8>::max() && |
| destination_offset >= std::numeric_limits<int8>::min()) { |
| return short_size; |
| } |
| } |
| |
| return GetLongSuccessorSize(successor.condition); |
| } |
| |
| default: |
| NOTREACHED() << "Impossible Successor reference type."; |
| return 0; |
| } |
| } |
| |
| MergeContext::BasicBlockLayoutInfo& MergeContext::FindLayoutInfo( |
| const BasicBlock* bb) { |
| BasicBlockLayoutInfoMap::iterator it = layout_info_.find(bb); |
| DCHECK(it != layout_info_.end()); |
| DCHECK_EQ(bb, it->second.basic_block); |
| |
| return it->second; |
| } |
| |
| const MergeContext::BasicBlockLayoutInfo& MergeContext::FindLayoutInfo( |
| const BasicBlock* bb) const { |
| BasicBlockLayoutInfoMap::const_iterator it = layout_info_.find(bb); |
| DCHECK(it != layout_info_.end()); |
| DCHECK_EQ(bb, it->second.basic_block); |
| |
| return it->second; |
| } |
| |
| BlockGraph::Reference MergeContext::ResolveReference( |
| BlockGraph::ReferenceType type, Size size, |
| const BasicBlockReference& ref) const { |
| if (ref.referred_type() == BasicBlockReference::REFERRED_TYPE_BASIC_BLOCK) { |
| // It's a basic block reference, we need to resolve it to a |
| // block reference. |
| const BasicBlockLayoutInfo& info = FindLayoutInfo(ref.basic_block()); |
| |
| return BlockGraph::Reference(type, |
| size, |
| info.block, |
| info.start_offset, |
| info.start_offset); |
| } else { |
| DCHECK_EQ(BasicBlockReference::REFERRED_TYPE_BLOCK, ref.referred_type()); |
| DCHECK_NE(ref.block(), original_block_); |
| |
| return BlockGraph::Reference(type, |
| size, |
| const_cast<Block*>(ref.block()), |
| ref.offset(), |
| ref.base()); |
| } |
| } |
| |
| BlockGraph::Reference MergeContext::ResolveReference( |
| const BasicBlockReference& ref) const { |
| return ResolveReference(ref.reference_type(), ref.size(), ref); |
| } |
| |
| } // namespace |
| |
| BlockBuilder::BlockBuilder(BlockGraph* bg) : block_graph_(bg) { |
| } |
| |
| bool BlockBuilder::Merge(BasicBlockSubGraph* subgraph) { |
| DCHECK_NE(reinterpret_cast<BasicBlockSubGraph*>(NULL), subgraph); |
| |
| // Before starting the layout ensure any BasicEndBlocks are at the end of/ |
| // their respective basic-block orderings. |
| if (!EndBlocksAreWellPlaced(subgraph)) |
| return false; |
| |
| MergeContext context(block_graph_, |
| subgraph->original_block(), |
| &tag_info_map_); |
| |
| if (!context.GenerateBlocks(*subgraph)) |
| return false; |
| |
| context.TransferReferrers(subgraph); |
| context.RemoveOriginalBlock(subgraph); |
| |
| // Track the newly created blocks. |
| new_blocks_.reserve(new_blocks_.size() + context.new_blocks().size()); |
| new_blocks_.insert(new_blocks_.end(), |
| context.new_blocks().begin(), |
| context.new_blocks().end()); |
| |
| // And we're done. |
| return true; |
| } |
| |
| } // namespace block_graph |