blob: 9cba94d3eb55fabcdc869d4a34df63f4424624f4 [file] [log] [blame]
// 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.
#include "syzygy/reorder/transforms/basic_block_layout_transform.h"
#include "base/strings/stringprintf.h"
namespace reorder {
namespace transforms {
namespace {
using block_graph::BlockGraph;
using block_graph::BlockVector;
typedef BasicBlockLayoutTransform::BlockInfo BlockInfo;
typedef BasicBlockLayoutTransform::BlockInfos BlockInfos;
typedef BasicBlockLayoutTransform::Order Order;
typedef BasicBlockSubGraphLayoutTransform::BlockPositionPair BlockPositionPair;
typedef BasicBlockSubGraphLayoutTransform::BasicBlockMap BasicBlockMap;
// Build the basic-block map for the given collection of block specifications.
// Returns true if the specifications are consistent, false otherwise.
bool BuildBasicBlockMap(BlockInfos::const_iterator begin,
BlockInfos::const_iterator end,
BasicBlockMap* basic_block_map,
size_t* block_count) {
DCHECK(basic_block_map != NULL);
DCHECK(block_count != NULL);
basic_block_map->clear();
*block_count = 0;
bool empty_bloc_spec_seen = false;
// Iterate over the block specifications.
BlockInfos::const_iterator it = begin;
for (; it != end; ++it, ++(*block_count)) {
if (it->block_spec->basic_block_offsets.empty())
empty_bloc_spec_seen = true;
// For each one, iterate over the collection of basic blocks and update
// the basic block map. While doing this we make sure that each basic
// block is only specified once.
const Order::OffsetVector& offsets(it->block_spec->basic_block_offsets);
for (size_t i = 0; i < offsets.size(); ++i) {
BlockPositionPair block_position(*block_count, i);
bool inserted = basic_block_map->insert(
std::make_pair(offsets[i], block_position)).second;
if (!inserted) {
LOG(ERROR) << "Basic block at offset " << offsets[i] << " of block \""
<< it->block_spec->block->name() << "\" with ID "
<< it->block_spec->block->id() << " is specified multiple "
<< "times.";
return false;
}
}
}
// This must have been called with non-empty input.
DCHECK_LT(0u, *block_count);
// An empty block specification means that ALL of the basic-blocks belong
// to that block. This is only valid if there is a single block specification.
if (*block_count != 1 && empty_bloc_spec_seen) {
LOG(ERROR) << "Have an empty block specification amongst multiple block "
<< "specifications.";
return false;
}
return true;
}
// Compares BlockInfos based on the original block pointer from the block_spec.
// Allows comparing BlockInfos to each other, and Block pointers to BlockInfos.
struct BlockInfoComparator {
bool operator()(const BlockInfo& bi1,
const BlockInfo& bi2) const {
return bi1.original_block < bi2.original_block;
}
bool operator()(const BlockGraph::Block* block,
const BlockInfo& bi) const {
return block < bi.original_block;
}
bool operator()(const BlockInfo& bi,
const BlockGraph::Block* block) const {
return bi.original_block < block;
}
};
} // namespace
const char BasicBlockLayoutTransform::kTransformName[] =
"BasicBlockLayoutTransform";
BasicBlockLayoutTransform::BasicBlockLayoutTransform(Order* order)
: order_(order) {
DCHECK(order != NULL);
}
BasicBlockLayoutTransform::~BasicBlockLayoutTransform() {
}
bool BasicBlockLayoutTransform::PreBlockGraphIteration(
const TransformPolicyInterface* policy,
BlockGraph* block_graph,
BlockGraph::Block* header_block) {
DCHECK(policy != NULL);
DCHECK(block_graph != NULL);
DCHECK(header_block != NULL);
if (!FindOrCreateSections(block_graph))
return false;
BuildBlockInfos();
return true;
}
bool BasicBlockLayoutTransform::OnBlock(
const TransformPolicyInterface* policy,
block_graph::BlockGraph* block_graph,
block_graph::BlockGraph::Block* block) {
DCHECK(policy != NULL);
DCHECK(block_graph != NULL);
DCHECK(block != NULL);
// Get the range of block specifications that are to be applied to this
// source block.
BlockInfos::iterator it_begin = std::lower_bound(block_infos_.begin(),
block_infos_.end(),
block,
BlockInfoComparator());
BlockInfos::iterator it_end = std::upper_bound(block_infos_.begin(),
block_infos_.end(),
block,
BlockInfoComparator());
// This block is not specified in the ordering. It will be left to fall to
// the tail of its original section by the orderer.
if (it_begin == it_end)
return true;
// Build the basic block map. This maps from BB offsets to
// (block index, bb index) pairs.
BasicBlockMap bb_map;
size_t block_count = 0;
if (!BuildBasicBlockMap(it_begin, it_end, &bb_map, &block_count))
return false;
BlockVector new_blocks;
#ifndef NDEBUG
// We expect the block_spec not to have been updated in place yet. If it
// has it's because this block has already been seen by the BB transform,
// which should never happen.
for (BlockInfos::iterator it = it_begin; it != it_end; ++it) {
DCHECK_EQ(it->original_block, it->block_spec->block);
}
#endif // NDEBUG
// Special case: a single block with no BB layout specification. Simply
// ensure the block is in the appropriate section, add an entry to the
// block specification map and move on.
if (block_count == 1 && bb_map.empty()) {
new_blocks.push_back(block);
} else {
// If we get here it's because we have an explicitly specified BB layout.
DCHECK(!bb_map.empty());
// Layout the basic-blocks using a basic block subgraph transform.
BasicBlockSubGraphLayoutTransform bb_layout_tx(bb_map);
if (!ApplyBasicBlockSubGraphTransform(
&bb_layout_tx,
policy,
block_graph,
block,
&new_blocks)) {
return false;
}
}
// We expect there to be as many blocks created as there are block
// specifications.
DCHECK_EQ(block_count, new_blocks.size());
// The transform returns the newly created blocks in the same order as they
// were specified by the BasicBlockMap, thus we can simply iterate through the
// blocks to assign them to the appropriate sections and update the block
// infos.
BlockInfos::iterator it = it_begin;
for (size_t i = 0; i < new_blocks.size(); ++i, ++it) {
new_blocks[i]->set_section(it->section_spec->id);
it->block_spec->block = new_blocks[i];
it->block_spec->basic_block_offsets.clear();
}
return true;
}
bool BasicBlockLayoutTransform::FindOrCreateSections(BlockGraph* block_graph) {
DCHECK(block_graph != NULL);
DCHECK(order_ != NULL);
Order::SectionSpecVector::iterator section_it = order_->sections.begin();
for (; section_it != order_->sections.end(); ++section_it) {
if (!FindOrCreateSection(block_graph, &(*section_it)))
return false;
}
return true;
}
bool BasicBlockLayoutTransform::FindOrCreateSection(
BlockGraph* block_graph,
Order::SectionSpec* section_spec) {
DCHECK(block_graph != NULL);
DCHECK(section_spec != NULL);
DCHECK(!section_spec->name.empty());
BlockGraph::Section* section = NULL;
// Explicit section ID? Ensure it exists and validate it.
if (section_spec->id != Order::SectionSpec::kNewSectionId) {
section = block_graph->GetSectionById(section_spec->id);
if (section == NULL) {
LOG(ERROR) << "Order specifies an invalid section ID.";
return false;
}
// Rename the section if we've been asked to do so.
if (section_spec->name != section->name()) {
LOG(INFO) << "Renaming section \"" << section->name() << "\" to \""
<< section_spec->name << "\".";
section->set_name(section_spec->name);
}
// Set the section characteristics if need be.
if (section_spec->characteristics != section->characteristics()) {
LOG(INFO) << "Changing characteristics from "
<< base::StringPrintf("0x%08X", section->characteristics())
<< " to "
<< base::StringPrintf("0x%08X", section_spec->characteristics)
<< ".";
section->set_characteristics(section_spec->characteristics);
}
} else {
// If an ID wasn't provided then this section better contain at least one
// block.
if (section_spec->blocks.empty()) {
LOG(ERROR) << "Invalid section specification.";
return false;
}
// Otherwise, create a new section.
section = block_graph->AddSection(
section_spec->name, section_spec->characteristics);
if (section == NULL) {
LOG(ERROR) << "Failed to add section \"" << section_spec->name
<< "\".";
return false;
}
// Save the ID of this newly created section.
section_spec->id = section->id();
}
return true;
}
void BasicBlockLayoutTransform::BuildBlockInfos() {
Order::SectionSpecVector::iterator section_spec_it =
order_->sections.begin();
for (; section_spec_it != order_->sections.end(); ++section_spec_it) {
Order::SectionSpec* section_spec = &(*section_spec_it);
Order::BlockSpecVector::iterator block_spec_it =
section_spec_it->blocks.begin();
for (; block_spec_it != section_spec_it->blocks.end(); ++block_spec_it) {
Order::BlockSpec* block_spec = &(*block_spec_it);
BlockInfo block_info = { block_spec->block, section_spec, block_spec };
block_infos_.push_back(block_info);
}
}
std::sort(block_infos_.begin(), block_infos_.end(), BlockInfoComparator());
}
const char BasicBlockSubGraphLayoutTransform::kTransformName[] =
"BasicBlockSubGraphLayoutTransform";
bool BasicBlockSubGraphLayoutTransform::TransformBasicBlockSubGraph(
const TransformPolicyInterface* policy,
BlockGraph* bg,
BasicBlockSubGraph* bbsg) {
DCHECK(policy != NULL);
DCHECK(bg != NULL);
DCHECK(bbsg != NULL);
DCHECK_EQ(1u, bbsg->block_descriptions().size());
typedef BasicBlockSubGraph::BasicBlock BasicBlock;
typedef BasicBlockSubGraph::BBCollection::iterator BBIterator;
typedef std::map<BlockPositionPair, BasicBlock*> ReverseMap;
// Iterate through the basic blocks in the original block. While we're at it
// we invert the basic block map so that iterating through it the blocks will
// be in the necessary order.
size_t block_count = 0;
ReverseMap reverse_map;
BBIterator bb_it = bbsg->basic_blocks().begin();
for (; bb_it != bbsg->basic_blocks().end(); ++bb_it) {
// Find the entry for this basic block in the basic block map. If there
// isn't one the basic block is being deleted. If the BB is required for
// layouting (is referenced by another BB) this will cause an error, but
// we'll find that out when we build the block(s).
BasicBlock* bb = *bb_it;
BasicBlockMap::const_iterator bb_map_it = bb_map_.find(bb->offset());
if (bb_map_it == bb_map_.end())
continue;
block_count = std::max(block_count, bb_map_it->second.first);
CHECK(reverse_map.insert(std::make_pair(bb_map_it->second, bb)).second);
}
++block_count;
// Create the necessary new block descriptions.
BlockDescriptions block_descs;
if (!CreateBlockDescriptions(block_count, bbsg, &block_descs))
return false;
DCHECK_EQ(block_count, block_descs.size());
// The reverse map will be conveniently in sorted order for us. So we simply
// need to append blocks to the block descriptions in the appropriate order.
ReverseMap::iterator rev_map_it = reverse_map.begin();
size_t prev_block_index = SIZE_MAX;
size_t prev_bb_index = SIZE_MAX;
for (; rev_map_it != reverse_map.end(); ++rev_map_it) {
size_t block_index = rev_map_it->first.first;
size_t bb_index = rev_map_it->first.second;
BasicBlock* bb = rev_map_it->second;
// We validate the basic block map by ensuring that it specifies each
// block using values [0, block_count) and that the basic blocks are also
// specified contiguously from 0.
if (block_index != prev_block_index) {
if (block_index != prev_block_index + 1 || block_index >= block_count) {
LOG(ERROR) << "Invalid block index in BasicBlockMap.";
return false;
}
prev_bb_index = SIZE_MAX;
prev_block_index = block_index;
}
if (bb_index != prev_bb_index + 1) {
LOG(ERROR) << "Invalid basic block index in BasicBlockMap.";
return false;
}
prev_bb_index = bb_index;
// Append this basic block to the appropriate block description.
block_descs[block_index]->basic_block_order.push_back(bb);
}
// All blocks should have been written to.
if (prev_block_index + 1 != block_count) {
LOG(ERROR) << "Not all blocks were written to.";
return false;
}
return true;
}
bool BasicBlockSubGraphLayoutTransform::CreateBlockDescriptions(
size_t block_count,
BasicBlockSubGraph* bbsg,
BlockDescriptions* block_descs) {
DCHECK(bbsg != NULL);
DCHECK(block_descs != NULL);
DCHECK(block_descs->empty());
// Get the original block description and empty its list of basic blocks.
BasicBlockSubGraph::BlockDescriptionList::iterator orig_block_desc =
bbsg->block_descriptions().begin();
orig_block_desc->basic_block_order.clear();
block_descs->reserve(block_count);
block_descs->push_back(&(*orig_block_desc));
DCHECK_EQ(1u, bbsg->block_descriptions().size());
if (block_count == 1)
return true;
// TODO(chrisha): We could be more specific in setting CODE or DATA block
// type by analyzing basic-block types. If any CODE basic blocks exist,
// the block type should be code. Otherwise, it should be data.
// If we're outputting to multiple blocks, then create the appropriate
// number of block descriptions. The block descriptions are identical to the
// input block description.
for (size_t i = 1; i < block_count; ++i) {
std::string name = base::StringPrintf(
"%s[%d]", orig_block_desc->name.c_str(), i);
BasicBlockSubGraph::BlockDescription* block_desc =
bbsg->AddBlockDescription(name,
orig_block_desc->compiland_name,
orig_block_desc->type,
orig_block_desc->section,
orig_block_desc->alignment,
orig_block_desc->attributes);
if (block_desc == NULL) {
LOG(ERROR) << "Failed to create new block description.";
return false;
}
block_descs->push_back(block_desc);
}
DCHECK_EQ(block_count, block_descs->size());
return true;
}
} // namespace transforms
} // namespace reorder