blob: 06ff8f1f05e8a623bdc9936714a304f74a2d3cff [file] [log] [blame]
Alan Baker33376ea2018-08-30 12:02:31 -04001// Copyright 2018 The Clspv Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include <vector>
16
17#include "llvm/ADT/SmallVector.h"
18#include "llvm/IR/BasicBlock.h"
19#include "llvm/IR/Instructions.h"
20#include "llvm/IR/Module.h"
21#include "llvm/Pass.h"
22#include "llvm/Support/raw_ostream.h"
23
24#include "clspv/Option.h"
25
26using namespace llvm;
27
28namespace {
29class ScalarizePass : public ModulePass {
30public:
31 static char ID;
32 ScalarizePass() : ModulePass(ID) {}
33
34 bool runOnModule(Module &M) override;
35
36private:
37 // Breaks a struct type phi down into its constituent elements. It does this
38 // recursively in the event the subtypes are also structs. Returns the
39 // replacement value. Returns the replacment value for the phi.
40 Value *ScalarizePhi(PHINode *phi);
41
42 // Phi nodes that need to be deleted.
43 std::vector<PHINode*> to_delete_;
44};
45} // namespace
46
47namespace clspv {
48ModulePass *createScalarizePass() { return new ScalarizePass(); }
49} // namespace clspv
50
51char ScalarizePass::ID = 0;
52static RegisterPass<ScalarizePass>
53 X("Scalarize", "Scalarizes some instructions with composite returns");
54
55bool ScalarizePass::runOnModule(Module &M) {
56 bool Changed = false;
57 for (auto &F : M) {
58 for (auto &BB : F) {
59 for (auto &I : BB) {
60 if (auto *phi = dyn_cast<PHINode>(&I)) {
61 if (clspv::Option::HackPhis() && phi->getType()->isStructTy()) {
62 ScalarizePhi(phi);
63 Changed = true;
64 }
65 }
66 }
67 }
68 }
69
70 for (auto *phi : to_delete_)
71 phi->eraseFromParent();
72
73 return Changed;
74}
75
76Value *ScalarizePass::ScalarizePhi(PHINode *phi) {
77 // Break down all the struct elements of the phi into individual elements and
78 // create new phis for them. Recombine the new phis into a single struct
79 // after the phi instructions.
80 if (!phi->getType()->isStructTy())
81 return phi;
82
83 // Cache the insertion location now. If we break down subtypes, we don't want
84 // to insert uses before definitions.
85 Instruction *where = phi->getParent()->getFirstNonPHI();
86 const auto num_incoming_values = phi->getNumIncomingValues();
87 unsigned type_index = 0;
88 SmallVector<Value *, 16> replacements;
89 for (auto *subtype : phi->getType()->subtypes()) {
90 PHINode *replacement =
91 PHINode::Create(subtype, num_incoming_values, "", phi);
92 for (unsigned i = 0; i != num_incoming_values; ++i) {
93 auto *incoming_block = phi->getIncomingBlock(i);
94 auto *incoming_value = phi->getIncomingValue(i);
95
96 Value *extracted_value = nullptr;
97 if (auto *constant = dyn_cast<Constant>(incoming_value)) {
98 extracted_value = constant->getAggregateElement(type_index);
99 } else {
100 // Extract the value just before the incoming block's branch.
101 extracted_value = ExtractValueInst::Create(
102 incoming_value, {type_index}, "", incoming_block->getTerminator());
103 }
104 replacement->addIncoming(extracted_value, incoming_block);
105 }
106 // Recursively break down subtype structs.
107 replacements.push_back(ScalarizePhi(replacement));
108 ++type_index;
109 }
110
111 // Regenerate the struct just after the phi instructions. Update the
112 // insertion location to aid RewriteInsertsPass.
113 Value *prev = Constant::getNullValue(phi->getType());
114 for (unsigned i = 0; i != replacements.size(); ++i) {
115 where = InsertValueInst::Create(prev, replacements[i], {i}, "", where);
116 prev = where;
117 }
118
119 // Replace the struct phi
120 phi->replaceAllUsesWith(prev);
121 to_delete_.push_back(phi);
122
123 return prev;
124}