Jlm
Store.cpp
Go to the documentation of this file.
1 /*
2  * Copyright 2017 Nico Reißmann <nico.reissmann@gmail.com>
3  * See COPYING for terms of redistribution.
4  */
5 
10 #include <jlm/llvm/ir/Trace.hpp>
11 #include <jlm/util/HashSet.hpp>
12 
13 namespace jlm::llvm
14 {
15 
17 
18 bool
19 StoreNonVolatileOperation::operator==(const Operation & other) const noexcept
20 {
21  auto operation = dynamic_cast<const StoreNonVolatileOperation *>(&other);
22  return operation && operation->narguments() == narguments()
23  && operation->GetStoredType() == GetStoredType()
24  && operation->GetAlignment() == GetAlignment();
25 }
26 
27 std::string
29 {
30  return "Store";
31 }
32 
33 std::unique_ptr<rvsdg::Operation>
35 {
36  return std::make_unique<StoreNonVolatileOperation>(*this);
37 }
38 
39 static bool
40 is_store_mux_reducible(const std::vector<jlm::rvsdg::Output *> & operands)
41 {
42  JLM_ASSERT(operands.size() > 2);
43 
44  const auto memStateMergeNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*operands[2]);
45  if (!is<MemoryStateMergeOperation>(memStateMergeNode))
46  return false;
47 
48  for (size_t n = 2; n < operands.size(); n++)
49  {
50  if (rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*operands[n]) != memStateMergeNode)
51  return false;
52  }
53 
54  return true;
55 }
56 
57 static bool
59  const StoreNonVolatileOperation & op,
60  const std::vector<jlm::rvsdg::Output *> & operands)
61 {
62  JLM_ASSERT(operands.size() > 2);
63 
64  const auto storeNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*operands[2]);
65  if (!is<StoreNonVolatileOperation>(storeNode))
66  return false;
67 
68  if (op.NumMemoryStates() != storeNode->noutputs())
69  return false;
70 
71  /* check for same address */
72  if (operands[0] != storeNode->input(0)->origin())
73  return false;
74 
75  for (size_t n = 2; n < operands.size(); n++)
76  {
77  if (rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*operands[n]) != storeNode
78  || operands[n]->nusers() != 1)
79  return false;
80  }
81 
82  auto other = static_cast<const StoreNonVolatileOperation *>(&storeNode->GetOperation());
83  JLM_ASSERT(op.GetAlignment() == other->GetAlignment());
84  return true;
85 }
86 
87 static bool
88 is_store_alloca_reducible(const std::vector<jlm::rvsdg::Output *> & operands)
89 {
90  if (operands.size() == 3)
91  return false;
92 
93  const auto allocaNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*operands[0]);
94  if (!is<AllocaOperation>(allocaNode))
95  return false;
96 
97  std::unordered_set states(std::next(std::next(operands.begin())), operands.end());
98  if (states.find(allocaNode->output(1)) == states.end())
99  return false;
100 
101  if (allocaNode->output(1)->nusers() != 1)
102  return false;
103 
104  return true;
105 }
106 
107 static bool
108 is_multiple_origin_reducible(const std::vector<jlm::rvsdg::Output *> & operands)
109 {
110  const util::HashSet<rvsdg::Output *> states(std::next(operands.begin(), 2), operands.end());
111  return states.Size() != operands.size() - 2;
112 }
113 
114 static std::vector<jlm::rvsdg::Output *>
116  const StoreNonVolatileOperation & op,
117  const std::vector<jlm::rvsdg::Output *> & operands)
118 {
119  const auto memStateMergeNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*operands[2]);
120  auto memStateMergeOperands = jlm::rvsdg::operands(memStateMergeNode);
121 
122  auto states = StoreNonVolatileOperation::Create(
123  operands[0],
124  operands[1],
125  memStateMergeOperands,
126  op.GetAlignment());
127  return { MemoryStateMergeOperation::Create(states) };
128 }
129 
130 static std::vector<jlm::rvsdg::Output *>
132  const StoreNonVolatileOperation & op,
133  const std::vector<jlm::rvsdg::Output *> & operands)
134 {
136  const auto storeNode = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*operands[2]);
137 
138  auto storeops = jlm::rvsdg::operands(storeNode);
139  std::vector<jlm::rvsdg::Output *> states(std::next(std::next(storeops.begin())), storeops.end());
141 }
142 
143 static std::vector<jlm::rvsdg::Output *>
145  const StoreNonVolatileOperation & op,
146  const std::vector<jlm::rvsdg::Output *> & operands)
147 {
148  auto value = operands[1];
149  auto address = operands[0];
150  auto alloca_state = rvsdg::TryGetOwnerNode<rvsdg::SimpleNode>(*address)->output(1);
151  std::unordered_set<jlm::rvsdg::Output *> states(
152  std::next(std::next(operands.begin())),
153  operands.end());
154 
155  auto outputs =
156  StoreNonVolatileOperation::Create(address, value, { alloca_state }, op.GetAlignment());
157  states.erase(alloca_state);
158  states.insert(outputs[0]);
159  return { states.begin(), states.end() };
160 }
161 
162 static std::vector<jlm::rvsdg::Output *>
164  const StoreNonVolatileOperation & operation,
165  const std::vector<jlm::rvsdg::Output *> & operands)
166 {
167  // FIXME: Unify with the duplicate state removal reduction of the LoadNonVolatile operation
168 
169  JLM_ASSERT(operands.size() > 2);
170  const auto address = operands[0];
171  const auto value = operands[1];
172 
173  std::vector<rvsdg::Output *> newInputStates;
174  std::unordered_map<rvsdg::Output *, size_t> stateIndexMap;
175  for (size_t n = 2; n < operands.size(); n++)
176  {
177  auto state = operands[n];
178  if (stateIndexMap.find(state) == stateIndexMap.end())
179  {
180  const size_t resultIndex = newInputStates.size();
181  newInputStates.push_back(state);
182  stateIndexMap[state] = resultIndex;
183  }
184  }
185 
186  const auto storeResults =
187  StoreNonVolatileOperation::Create(address, value, newInputStates, operation.GetAlignment());
188 
189  std::vector<rvsdg::Output *> results(operation.nresults(), nullptr);
190  for (size_t n = 2; n < operands.size(); n++)
191  {
192  auto state = operands[n];
193  JLM_ASSERT(stateIndexMap.find(state) != stateIndexMap.end());
194  results[n - 2] = storeResults[stateIndexMap[state]];
195  }
196 
197  return results;
198 }
199 
200 std::optional<std::vector<rvsdg::Output *>>
202  const StoreNonVolatileOperation & operation,
203  const std::vector<rvsdg::Output *> & operands)
204 {
206  return perform_store_mux_reduction(operation, operands);
207 
208  return std::nullopt;
209 }
210 
211 std::optional<std::vector<rvsdg::Output *>>
213  const StoreNonVolatileOperation & operation,
214  const std::vector<rvsdg::Output *> & operands)
215 {
216  if (is_store_store_reducible(operation, operands))
217  return perform_store_store_reduction(operation, operands);
218 
219  return std::nullopt;
220 }
221 
222 std::optional<std::vector<rvsdg::Output *>>
224  const StoreNonVolatileOperation & operation,
225  const std::vector<rvsdg::Output *> & operands)
226 {
228  return perform_store_alloca_reduction(operation, operands);
229 
230  return std::nullopt;
231 }
232 
233 std::optional<std::vector<rvsdg::Output *>>
235  const StoreNonVolatileOperation & operation,
236  const std::vector<rvsdg::Output *> & operands)
237 {
239  return perform_multiple_origin_reduction(operation, operands);
240 
241  return std::nullopt;
242 }
243 
244 std::optional<std::vector<rvsdg::Output *>>
246  const StoreNonVolatileOperation & operation,
247  const std::vector<rvsdg::Output *> & operands)
248 {
249  JLM_ASSERT(operands.size() >= 2);
250  const auto address = operands[0];
251  const auto value = operands[1];
252 
253  auto [ioBarrierNode, ioBarrierOperation] =
254  rvsdg::TryGetSimpleNodeAndOptionalOp<IOBarrierOperation>(*address);
255  if (!ioBarrierOperation)
256  return std::nullopt;
257 
258  auto & barredAddress = *IOBarrierOperation::BarredInput(*ioBarrierNode).origin();
259  const auto & tracedAddress = llvm::traceOutput(barredAddress);
260  if (!rvsdg::IsOwnerNodeOperation<AllocaOperation>(tracedAddress))
261  return std::nullopt;
262 
263  auto & storeNode = CreateNode(
264  barredAddress,
265  *value,
266  { std::next(operands.begin(), 2), operands.end() },
267  operation.GetAlignment());
268 
269  return { outputs(&storeNode) };
270 }
271 
272 std::optional<std::vector<rvsdg::Output *>>
274  const StoreNonVolatileOperation & operation,
275  const std::vector<rvsdg::Output *> & operands)
276 {
277  JLM_ASSERT(operands.size() >= 2);
278  const auto & address = *operands[0];
279 
280  // We cannot(!) use the traced address in this normalization as it might result in the wrong
281  // number of users. The address can be routed through a structural node where it has multiple
282  // users, but the traced address would still just have a single user.
283  if (!rvsdg::IsOwnerNodeOperation<AllocaOperation>(address))
284  return std::nullopt;
285 
286  if (address.nusers() != 1)
287  return std::nullopt;
288 
289  std::vector newMemoryStateResults(operands.begin() + 2, operands.end());
290  JLM_ASSERT(newMemoryStateResults.size() == operation.NumMemoryStates());
291 
292  return newMemoryStateResults;
293 }
294 
296 
297 bool
298 StoreVolatileOperation::operator==(const Operation & other) const noexcept
299 {
300  auto operation = dynamic_cast<const StoreVolatileOperation *>(&other);
301  return operation && operation->NumMemoryStates() == NumMemoryStates()
302  && operation->GetStoredType() == GetStoredType()
303  && operation->GetAlignment() == GetAlignment();
304 }
305 
306 std::string
308 {
309  return "StoreVolatile";
310 }
311 
312 std::unique_ptr<rvsdg::Operation>
314 {
315  return std::make_unique<StoreVolatileOperation>(*this);
316 }
317 
318 }
static rvsdg::Input & BarredInput(const rvsdg::SimpleNode &node) noexcept
Definition: IOBarrier.hpp:70
static rvsdg::Output * Create(const std::vector< rvsdg::Output * > &operands)
static std::unique_ptr< llvm::ThreeAddressCode > Create(const Variable *address, const Variable *value, const Variable *state, size_t alignment)
Definition: Store.hpp:281
static rvsdg::SimpleNode & CreateNode(rvsdg::Output &address, rvsdg::Output &value, const std::vector< rvsdg::Output * > &memoryStates, size_t alignment)
Definition: Store.hpp:300
static std::optional< std::vector< rvsdg::Output * > > NormalizeIOBarrierAllocaAddress(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Redirect the address operand of the StoreNonVolatileOperation from an IOBarrierOperation when it can ...
Definition: Store.cpp:245
std::string debug_string() const override
Definition: Store.cpp:28
static std::optional< std::vector< rvsdg::Output * > > NormalizeStoreMux(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Swaps a memory state merge operation and a store operation.
Definition: Store.cpp:201
~StoreNonVolatileOperation() noexcept override
static std::optional< std::vector< rvsdg::Output * > > normalizeStoreAllocaSingleUser(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Definition: Store.cpp:273
std::unique_ptr< Operation > copy() const override
Definition: Store.cpp:34
static std::optional< std::vector< rvsdg::Output * > > NormalizeStoreAlloca(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Removes unnecessary state from a store node when its address originates directly from an alloca node.
Definition: Store.cpp:223
static std::optional< std::vector< rvsdg::Output * > > NormalizeStoreStore(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Removes a duplicated store to the same address.
Definition: Store.cpp:212
static std::optional< std::vector< rvsdg::Output * > > NormalizeDuplicateStates(const StoreNonVolatileOperation &operation, const std::vector< rvsdg::Output * > &operands)
Remove duplicated state operands.
Definition: Store.cpp:234
size_t GetAlignment() const noexcept
Definition: Store.hpp:57
size_t NumMemoryStates() const noexcept
Definition: Store.hpp:69
std::string debug_string() const override
Definition: Store.cpp:307
~StoreVolatileOperation() noexcept override
std::unique_ptr< Operation > copy() const override
Definition: Store.cpp:313
Output * origin() const noexcept
Definition: node.hpp:58
size_t nresults() const noexcept
Definition: operation.cpp:30
size_t narguments() const noexcept
Definition: operation.cpp:17
std::size_t Size() const noexcept
Definition: HashSet.hpp:187
#define JLM_ASSERT(x)
Definition: common.hpp:16
Global memory state passed between functions.
static std::vector< jlm::rvsdg::Output * > perform_store_mux_reduction(const StoreNonVolatileOperation &op, const std::vector< jlm::rvsdg::Output * > &operands)
Definition: Store.cpp:115
rvsdg::Output & traceOutput(rvsdg::Output &output)
Definition: Trace.cpp:39
static bool is_store_store_reducible(const StoreNonVolatileOperation &op, const std::vector< jlm::rvsdg::Output * > &operands)
Definition: Store.cpp:58
static std::vector< jlm::rvsdg::Output * > perform_store_alloca_reduction(const StoreNonVolatileOperation &op, const std::vector< jlm::rvsdg::Output * > &operands)
Definition: Store.cpp:144
static bool is_store_mux_reducible(const std::vector< jlm::rvsdg::Output * > &operands)
Definition: Store.cpp:40
static std::vector< jlm::rvsdg::Output * > perform_store_store_reduction(const StoreNonVolatileOperation &op, const std::vector< jlm::rvsdg::Output * > &operands)
Definition: Store.cpp:131
static std::vector< rvsdg::Output * > perform_multiple_origin_reduction(const LoadNonVolatileOperation &op, const std::vector< rvsdg::Output * > &operands)
Definition: Load.cpp:205
static bool is_multiple_origin_reducible(const std::vector< rvsdg::Output * > &operands)
Definition: Load.cpp:133
static bool is_store_alloca_reducible(const std::vector< jlm::rvsdg::Output * > &operands)
Definition: Store.cpp:88
static std::vector< jlm::rvsdg::Output * > operands(const Node *node)
Definition: node.hpp:1049
static std::vector< jlm::rvsdg::Output * > outputs(const Node *node)
Definition: node.hpp:1058