// Copyright (c) 2018 Google LLC. // // 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 "source/opt/loop_fission.h" #include <set> #include "source/opt/register_pressure.h" // Implement loop fission with an optional parameter to split only // if the register pressure in a given loop meets a certain criteria. This is // controlled via the constructors of LoopFissionPass. // // 1 - Build a list of loops to be split, these are top level loops (loops // without child loops themselves) which meet the register pressure criteria, as // determined by the ShouldSplitLoop method of LoopFissionPass. // // 2 - For each loop in the list, group each instruction into a set of related // instructions by traversing each instructions users and operands recursively. // We stop if we encounter an instruction we have seen before or an instruction // which we don't consider relevant (i.e OpLoopMerge). We then group these // groups into two different sets, one for the first loop and one for the // second. // // 3 - We then run CanPerformSplit to check that it would be legal to split a // loop using those two sets. We check that we haven't altered the relative // order load/stores appear in the binary and that we aren't breaking any // dependency between load/stores by splitting them into two loops. We also // check that none of the OpBranch instructions are dependent on a load as we // leave control flow structure intact and move only instructions in the body so // we want to avoid any loads with side affects or aliasing. // // 4 - We then split the loop by calling SplitLoop. This function clones the // loop and attaches it to the preheader and connects the new loops merge block // to the current loop header block. We then use the two sets built in step 2 to // remove instructions from each loop. If an instruction appears in the first // set it is removed from the second loop and vice versa. // // 5 - If the multiple split passes flag is set we check if each of the loops // still meet the register pressure criteria. If they do then we add them to the // list of loops to be split (created in step one) to allow for loops to be // split multiple times. // namespace spvtools { namespace opt { class LoopFissionImpl { … }; bool LoopFissionImpl::MovableInstruction(const Instruction& inst) const { … } void LoopFissionImpl::TraverseUseDef(Instruction* inst, std::set<Instruction*>* returned_set, bool ignore_phi_users, bool report_loads) { … } bool LoopFissionImpl::GroupInstructionsByUseDef() { … } bool LoopFissionImpl::CanPerformSplit() { … } Loop* LoopFissionImpl::SplitLoop() { … } LoopFissionPass::LoopFissionPass(const size_t register_threshold_to_split, bool split_multiple_times) : … { … } LoopFissionPass::LoopFissionPass() : … { … } bool LoopFissionPass::ShouldSplitLoop(const Loop& loop, IRContext* c) { … } Pass::Status LoopFissionPass::Process() { … } } // namespace opt } // namespace spvtools