//===--- BasicBlockData.h - Defines the BasicBlockData utility --*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2021 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file defines the BasicBlockData utility // //===----------------------------------------------------------------------===// #ifndef SWIFT_SIL_BASICBLOCKDATA_H #define SWIFT_SIL_BASICBLOCKDATA_H #include "swift/SIL/SILFunction.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/STLExtras.h" namespace swift { /// Manage additional data for a function's basic blocks. /// /// This can be used by transforms to store temporary data per basic block. /// It is very efficient: only a single memory allocation is needed and no maps /// are used to lookup data. /// /// It is still permitted to use a BasicBlockData after the block list of a /// function has changed. With two exceptions: /// * If new blocks are added during the lifetime of a BasicBlockData, no data /// can be queried for a new block. /// * If a new BasicBlockData is created after the block list changed, then any /// older BasicBlockData (which was created before the change) cannot be /// used anymore. /// /// This means, it's totally fine to store a BasicBlockData in an Analysis, as /// long as the invalidation mechanism ensures that the analysis data is not /// retrieved after the function's block list changed. /// /// The template argument \p N specifies how many Data elements can be stored /// inline in the data vector. The default value of 32 avoids malloc for about /// 90% of all functions. template class BasicBlockData { SILFunction *function; llvm::SmallVector data; /// The data is valid if validForBlockOrder matches the function's /// BlockListChangeIdx. unsigned validForBlockOrder = 0; int getIndex(SILBasicBlock *block) const { assert(block->index >= 0 && "Cannot get data for a new block"); assert(validForBlockOrder == function->BlockListChangeIdx && "BasicBlockData invalid because the function's block list changed"); return block->index; } public: /// For iterating over the function's blocks, yielding the block and its data. template class IteratorTy { using DataTy = typename std::conditional::type; using BasicBlockDataTy = typename std::conditional, BasicBlockData>::type; /// The underlying SILBasicBlock iterator. BlockIterTy blockIter; /// Reference to the constructing BasicBlockData. Used to retrieve the data. BasicBlockDataTy &blockData; friend class BasicBlockData; IteratorTy(BlockIterTy blockIter, BasicBlockDataTy &blockData) : blockIter(blockIter), blockData(blockData) {} public: /// Result of re-referencing the iterator. struct BlockAndData { SILBasicBlock █ DataTy &data; BlockAndData(SILBasicBlock &block, DataTy &data) : block(block), data(data) {} }; IteratorTy & operator++() { // Preincrement blockIter++; return *this; } IteratorTy & operator++(int) { // Postincrement IteratorTy tmp = *this; ++*this; return tmp; } SILBasicBlock &block() const { return *blockIter; } DataTy &data() const { return blockData[&block()]; } BlockAndData operator*() const { return BlockAndData(block(), data()); } bool operator==(const IteratorTy &rhs) const { return blockIter == rhs.blockIter; } bool operator!=(const IteratorTy &rhs) const { return blockIter != rhs.blockIter; } }; using iterator = IteratorTy; using const_iterator = IteratorTy; using reverse_iterator = IteratorTy; using const_reverse_iterator = IteratorTy; static Data constructDefaultData(SILBasicBlock *) { return Data(); } /// Initialize Data for all basic blocks. /// /// The \p init function must return the initial data for a block. If not /// provided, all data is constructed with the Data() default constructor. BasicBlockData(SILFunction *function, llvm::function_ref init = // Would be nice to simply write constructDefaultData in // closure syntax, but I didn't get it compile under Windows. constructDefaultData) : function(function) { // Reserve enough space. Though SILFunction::size() iterates over all // blocks it is still better than to risk multiple mallocs. data.reserve(function->size()); bool blockListChanged = false; for (auto blockAndIdx : llvm::enumerate(*function)) { SILBasicBlock &block = blockAndIdx.value(); int idx = blockAndIdx.index(); if (block.index != idx) { blockListChanged = true; block.index = idx; } data.push_back(init(&block)); } function->incrementRefCount(); // If we assigned new block indices, it invalidates all older BasicBlockData // instances. if (blockListChanged) ++function->BlockListChangeIdx; validForBlockOrder = function->BlockListChangeIdx; } ~BasicBlockData() { function->decrementRefCount(); } // For iterating over the function's basic blocks, yielding the block and its // data. For example: // // BasicBlockData blockData; // for (auto bd : blockData) { // SILBasicBlock &block = bd.block; // Mydata &dataForBlock = bd.data; // } iterator begin() { return iterator(function->begin(), *this); } iterator end() { return iterator(function->end(), *this); } const_iterator begin() const { return const_iterator(function->begin(), *this); } const_iterator end() const { return const_iterator(function->end(), *this); } reverse_iterator rbegin() { return reverse_iterator(function->rbegin(), *this); } reverse_iterator rend() { return reverse_iterator(function->rend(), *this); } const_reverse_iterator rbegin() const { return const_reverse_iterator(function->rbegin(), *this); } const_reverse_iterator rend() const { return const_reverse_iterator(function->rend(), *this); } /// Returns the function's entry block and its data. typename iterator::BlockAndData entry() { return *begin(); } /// Returns the function's entry block and its data (const-version). typename const_iterator::BlockAndData entry() const { return *begin(); } /// Returns the underlying function. SILFunction *getFunction() const { return function; } /// Gets a mutable reference to a block's data. Data &operator[] (SILBasicBlock *block) { return data[getIndex(block)]; } /// Gets a constant reference to a block's data. const Data &operator[] (SILBasicBlock *block) const { return data[getIndex(block)]; } /// If \p block is a new block, i.e. created after this BasicBlockData was /// constructed, creates a new Data with \p init and returns it. Data &get(SILBasicBlock *block, llvm::function_ref init) { if (block->index < 0) { assert(validForBlockOrder == function->BlockListChangeIdx && "BasicBlockData invalid because the function's block list changed"); validForBlockOrder = ++function->BlockListChangeIdx; block->index = data.size(); data.push_back(init()); } return data[getIndex(block)]; } /// Look up the state associated with \p block. Returns nullptr upon failure. NullablePtr get(SILBasicBlock *block) const { if (block->index < 0) return {nullptr}; Data *d = &const_cast(this)->data[getIndex(block)]; return NullablePtr(d); } /// If \p block is a new block, i.e. created after this BasicBlockData was /// constructed, creates a new Data by calling /// Data(std::forward(Args)...). template std::pair try_emplace(SILBasicBlock *block, ArgTypes &&...Args) { if (block->index != 0) { return {&data[getIndex(block)], false}; } assert(validForBlockOrder == function->BlockListChangeIdx && "BasicBlockData invalid because the function's block list changed"); validForBlockOrder = ++function->BlockListChangeIdx; block->index = data.size(); data.emplace_back(std::forward(Args)...); return {&data.back(), true}; } }; } // end swift namespace #endif