mc2lib
compiler.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014-2016, Marco Elver
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in the
14  * documentation and/or other materials provided with the
15  * distribution.
16  *
17  * * Neither the name of the software nor the names of its contributors
18  * may be used to endorse or promote products derived from this
19  * software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 
34 #ifndef MC2LIB_CODEGEN_COMPILER_HPP_
35 #define MC2LIB_CODEGEN_COMPILER_HPP_
36 
37 #include <array>
38 #include <cassert>
39 #include <cstddef>
40 #include <functional>
41 #include <map>
42 #include <memory>
43 #include <stdexcept>
44 #include <unordered_map>
45 #include <unordered_set>
46 #include <utility>
47 #include <vector>
48 
49 #include "../memconsistency/eventsets.hpp"
50 #include "../types.hpp"
51 
52 namespace mc2lib {
53 
58 namespace codegen {
59 
60 namespace mc = memconsistency;
61 
62 template <std::size_t max_size_bytes>
63 using EventPtrs =
64  std::array<const mc::Event *, max_size_bytes / sizeof(types::WriteID)>;
65 
66 template <class... Ts>
67 inline auto MakeEventPtrs(const mc::Event *e1, Ts... en)
68  -> EventPtrs<(1 + sizeof...(Ts)) * sizeof(types::WriteID)> {
69  EventPtrs<(1 + sizeof...(Ts)) * sizeof(types::WriteID)> es{{e1, en...}};
70  return es;
71 }
72 
76 template <class Backend, class EvtStateT>
77 class Op {
78  public:
79  typedef EvtStateT EvtState;
80 
81  // Types
82  typedef std::shared_ptr<Op> Ptr;
83  typedef std::vector<Ptr> Thread;
84  typedef typename Thread::const_iterator ThreadIt;
85  typedef std::unordered_map<types::Pid, Thread> Threads;
86  typedef std::vector<std::pair<ThreadIt, ThreadIt>> ThreadItStack;
87 
88  // Read-only thread types: new Ops can access previous Ops.
89  typedef std::vector<const Op *> ThreadConst;
90  typedef typename ThreadConst::const_iterator ThreadConstIt;
91 
92  // Callback type: optionally, previous Ops get called back with new Ops.
93  // E.g. for lazily constructing control flow graph with random branches.
94  typedef std::function<std::size_t(Op *, types::InstPtr, Backend *, EvtState *,
95  void *, std::size_t)>
97  typedef std::vector<Callback> CallbackStack;
98 
99  template <class T>
100  friend Threads ExtractThreads(T *container) {
101  Threads result;
102  std::unordered_set<Op *> used;
103 
104  for (auto &op : (*container)) {
105  assert(op != nullptr);
106 
107  if (used.insert(op.get()).second) {
108  // Using same instance of Op multiple times is not permitted.
109  op = op->Clone();
110  }
111 
112  result[op->pid()].emplace_back(op);
113  }
114 
115  return result;
116  }
117 
118  friend std::size_t threads_size(const Threads &threads) {
119  std::size_t result = 0;
120 
121  for (const auto &thread : threads) {
122  result += thread.second.size();
123  }
124 
125  return result;
126  }
127 
128  public:
129  explicit Op(types::Pid pid) : pid_(pid) {}
130 
131  virtual ~Op() {}
132 
133  virtual void AdvanceThread(ThreadItStack *it_stack) const {
134  ++(it_stack->back().first);
135  }
136 
140  virtual Ptr Clone() const = 0;
141 
146  virtual void Reset() = 0;
147 
155  virtual bool EnableEmit(EvtState *evts) = 0;
156 
164  virtual void InsertPo(ThreadConstIt before, EvtState *evts) = 0;
165 
172  virtual void RegisterCallback(CallbackStack *callback_stack) {}
173 
185  virtual std::size_t Emit(types::InstPtr start, Backend *backend,
186  EvtState *evts, void *code, std::size_t len) = 0;
187 
199  virtual const mc::Event *LastEvent(const mc::Event *next_event,
200  EvtState *evts) const = 0;
201 
212  virtual const mc::Event *FirstEvent(const mc::Event *prev_event,
213  EvtState *evts) const = 0;
214 
232  virtual bool UpdateObs(types::InstPtr ip, int part, types::Addr addr,
233  const types::WriteID *from_id, std::size_t size,
234  EvtState *evts) = 0;
235 
236  types::Pid pid() const { return pid_; }
237 
239 
240  private:
242 };
243 
244 template <class Backend, class EvtState>
245 class MemOp : public Op<Backend, EvtState> {
246  public:
247  explicit MemOp(types::Pid pid) : Op<Backend, EvtState>(pid) {}
248 
249  virtual types::Addr addr() const = 0;
250 };
251 
252 template <class Backend, class EvtState>
253 class NullOp : public Op<Backend, EvtState> {
254  public:
255  explicit NullOp(types::Pid pid) : Op<Backend, EvtState>(pid) {}
256 
257  bool EnableEmit(EvtState *evts) override { return false; }
258 
260  EvtState *evts) override {
261  throw std::logic_error("Not supported");
262  }
263 
264  std::size_t Emit(types::InstPtr start, Backend *backend, EvtState *evts,
265  void *code, std::size_t len) override {
266  throw std::logic_error("Not supported");
267  return 0;
268  }
269 
270  bool UpdateObs(types::InstPtr ip, int part, types::Addr addr,
271  const types::WriteID *from_id, std::size_t size,
272  EvtState *evts) override {
273  throw std::logic_error("Not supported");
274  return false;
275  }
276 
277  const mc::Event *LastEvent(const mc::Event *next_event,
278  EvtState *evts) const override {
279  throw std::logic_error("Not supported");
280  return nullptr;
281  }
282 
283  const mc::Event *FirstEvent(const mc::Event *prev_event,
284  EvtState *evts) const override {
285  throw std::logic_error("Not supported");
286  return nullptr;
287  }
288 };
289 
293 template <class Operation, class Backend>
294 class Compiler {
295  public:
296  typedef typename Operation::EvtState EvtState;
297  typedef typename Operation::Threads Threads;
298  typedef typename Operation::ThreadIt ThreadIt;
299  typedef typename Operation::ThreadItStack ThreadItStack;
300  typedef typename Operation::ThreadConst ThreadConst;
301  typedef typename Operation::Callback Callback;
302  typedef typename Operation::CallbackStack CallbackStack;
303 
312  explicit Compiler(std::unique_ptr<EvtState> evts) : evts_(std::move(evts)) {
313  Reset();
314  }
315 
326  explicit Compiler(std::unique_ptr<EvtState> evts, Threads &&threads)
327  : evts_(std::move(evts)) {
328  Reset(std::move(threads));
329  }
330 
331  void Reset() {
332  evts_->Reset();
333  backend_.Reset();
334  ip_to_op_.clear();
335  }
336 
337  void Reset(Threads &&threads) {
338  threads_ = std::move(threads);
339 
340  // Must ensure all Operation instances have been reset.
341  for (const auto &thread : threads_) {
342  for (const auto &op : thread.second) {
343  op->Reset();
344  }
345  }
346 
347  Reset();
348  }
349 
350  const Threads &threads() { return threads_; }
351 
352  const EvtState *evts() const { return evts_.get(); }
353 
354  EvtState *evts() { return evts_.get(); }
355 
356  std::size_t Emit(types::InstPtr base, Operation *op, void *code,
357  std::size_t len, ThreadConst *thread_const_ops,
358  CallbackStack *callback_stack) {
359  // Prepare op for emit.
360  if (!op->EnableEmit(evts_.get())) {
361  return 0;
362  }
363 
364  // Generate program-order.
365  if (thread_const_ops != nullptr) {
366  assert(!thread_const_ops->empty());
367  op->InsertPo(--thread_const_ops->end(), evts_.get());
368  thread_const_ops->push_back(op);
369  } else {
370  ThreadConst invalid{nullptr};
371  op->InsertPo(invalid.begin(), evts_.get());
372  }
373 
374  std::size_t ctrl_len = 0;
375 
376  if (callback_stack != nullptr) {
377  // Call all registered callbacks
378  for (auto &callback : (*callback_stack)) {
379  // Pass in current base, e.g. to allow late resolving of branch
380  // targets; allows inserting code for flow control.
381  const std::size_t s =
382  callback(op, base, &backend_, evts_.get(), code, len);
383 
384  assert(s < len);
385  base += s;
386  code = static_cast<char *>(code) + s;
387  len -= s;
388  ctrl_len += s;
389  }
390 
391  // Register callback
392  op->RegisterCallback(callback_stack);
393  }
394 
395  // Must be called *after* InsertPo and callback!
396  const std::size_t op_len =
397  op->Emit(base, &backend_, evts_.get(), code, len);
398  assert(op_len != 0);
399 
400  // Base IP must be unique!
401  assert(IpToOp(base) == nullptr);
402  // Insert IP to Operation mapping.
403  ip_to_op_[base] = std::make_pair(base + op_len, op);
404 
405  return op_len + ctrl_len;
406  }
407 
408  std::size_t Emit(types::Pid pid, types::InstPtr base, void *code,
409  std::size_t len) {
410  auto thread = threads_.find(pid);
411 
412  if (thread == threads_.end()) {
413  return 0;
414  }
415 
416  std::size_t emit_len = 0;
417 
418  // Maintain const sequence of *emitted* ops; nullptr denotes beginning
419  // of sequence (in absence of thread_const_ops.begin()).
420  //
421  // This will be a flattened sequence of ops (evaluated recursive ops).
422  ThreadConst thread_const_ops{nullptr};
423  thread_const_ops.reserve(thread->second.size() + 1);
424 
425  // Callback function list
426  CallbackStack callback_stack;
427 
428  // Enable recursive, nested sequences.
429  ThreadItStack it_stack;
430  it_stack.emplace_back(thread->second.begin(), thread->second.end());
431 
432  while (!it_stack.empty()) {
433  auto &it = it_stack.back().first;
434  auto &end = it_stack.back().second;
435 
436  if (it == end) {
437  it_stack.pop_back();
438  continue;
439  }
440 
441  const auto &op = *it;
442 
443  // Generate code and architecture-specific ordering relations.
444  const std::size_t op_len =
445  Emit(base + emit_len, op.get(), code, len - emit_len,
446  &thread_const_ops, &callback_stack);
447 
448  emit_len += op_len;
449  assert(emit_len <= len);
450  code = static_cast<char *>(code) + op_len;
451 
452  op->AdvanceThread(&it_stack);
453  }
454 
455  // Notify ops of completion
456  for (auto &callback : callback_stack) {
457  const std::size_t s = callback(nullptr, base + emit_len, &backend_,
458  evts_.get(), code, len - emit_len);
459 
460  emit_len += s;
461  assert(emit_len <= len);
462  code = static_cast<char *>(code) + s;
463  }
464 
465  return emit_len;
466  }
467 
468  bool UpdateObs(types::InstPtr ip, int part, types::Addr addr,
469  const types::WriteID *from_id, std::size_t size) {
470  auto op = IpToOp(ip);
471 
472  if (op == nullptr) {
473  return false;
474  }
475 
476  return op->UpdateObs(ip, part, addr, from_id, size, evts_.get());
477  }
478 
480  if (ip_to_op_.empty()) {
481  // Can be legally empty if no code has yet been emitted, i.e. right
482  // after host system startup. By not faulting here, the host can
483  // still use ip_to_op to check if an instruction needs to be
484  // treated specially: before any code has been emitted, no
485  // instructions will be treated specially.
486  return nullptr;
487  }
488 
489  auto e = ip_to_op_.upper_bound(ip);
490  if (e != ip_to_op_.begin()) {
491  e--;
492  }
493 
494  if (!(e->first <= ip && ip < e->second.first)) {
495  return nullptr;
496  }
497 
498  return e->second.second;
499  }
500 
501  private:
502  typedef std::map<types::InstPtr, std::pair<types::InstPtr, Operation *>>
504 
505  std::unique_ptr<EvtState> evts_;
506  Backend backend_;
507  Threads threads_;
508 
509  // Each processor executes unique code, hence IP must be unique. Only stores
510  // the start IP of Op's code.
512 };
513 
514 } // namespace codegen
515 } // namespace mc2lib
516 
517 #endif /* MC2LIB_CODEGEN_COMPILER_HPP_ */
518 
519 /* vim: set ts=2 sts=2 sw=2 et : */
virtual bool EnableEmit(EvtState *evts)=0
Operation::ThreadItStack ThreadItStack
Definition: compiler.hpp:299
Operation::Callback Callback
Definition: compiler.hpp:301
EvtState * evts()
Definition: compiler.hpp:354
auto MakeEventPtrs(const mc::Event *e1, Ts... en) -> EventPtrs<(1+sizeof...(Ts)) *sizeof(types::WriteID)>
Definition: compiler.hpp:67
std::size_t Emit(types::InstPtr base, Operation *op, void *code, std::size_t len, ThreadConst *thread_const_ops, CallbackStack *callback_stack)
Definition: compiler.hpp:356
std::vector< const Op * > ThreadConst
Definition: compiler.hpp:89
std::size_t Emit(types::InstPtr start, Backend *backend, EvtState *evts, void *code, std::size_t len) override
Definition: compiler.hpp:264
bool UpdateObs(types::InstPtr ip, int part, types::Addr addr, const types::WriteID *from_id, std::size_t size)
Definition: compiler.hpp:468
Definition: cats.hpp:47
Operation::ThreadConst ThreadConst
Definition: compiler.hpp:300
EvtStateT EvtState
Definition: compiler.hpp:79
Thread::const_iterator ThreadIt
Definition: compiler.hpp:84
std::unordered_map< types::Pid, Thread > Threads
Definition: compiler.hpp:85
void Reset()
Definition: compiler.hpp:331
Operation::ThreadIt ThreadIt
Definition: compiler.hpp:298
void Reset(Threads &&threads)
Definition: compiler.hpp:337
std::map< types::InstPtr, std::pair< types::InstPtr, Operation * > > InstPtr_Op
Definition: compiler.hpp:503
Definition: compiler.hpp:77
std::vector< Callback > CallbackStack
Definition: compiler.hpp:97
const mc::Event * FirstEvent(const mc::Event *prev_event, EvtState *evts) const override
Definition: compiler.hpp:283
void InsertPo(typename Op< Backend, EvtState >::ThreadConstIt before, EvtState *evts) override
Definition: compiler.hpp:259
Threads threads_
Definition: compiler.hpp:507
std::function< std::size_t(Op *, types::InstPtr, Backend *, EvtState *, void *, std::size_t)> Callback
Definition: compiler.hpp:96
friend Threads ExtractThreads(T *container)
Definition: compiler.hpp:100
Operation::EvtState EvtState
Definition: compiler.hpp:296
virtual bool UpdateObs(types::InstPtr ip, int part, types::Addr addr, const types::WriteID *from_id, std::size_t size, EvtState *evts)=0
Types< true >::InstPtr InstPtr
Instruction pointer type.
Definition: types.hpp:81
virtual Ptr Clone() const =0
virtual ~Op()
Definition: compiler.hpp:131
virtual void Reset()=0
Top level class used to manage code generation (compiler).
Definition: compiler.hpp:294
Definition: eventsets.hpp:103
virtual const mc::Event * LastEvent(const mc::Event *next_event, EvtState *evts) const =0
ThreadConst::const_iterator ThreadConstIt
Definition: compiler.hpp:90
std::vector< Ptr > Thread
Definition: compiler.hpp:83
std::unique_ptr< EvtState > evts_
Definition: compiler.hpp:505
std::vector< std::pair< ThreadIt, ThreadIt > > ThreadItStack
Definition: compiler.hpp:86
NullOp(types::Pid pid)
Definition: compiler.hpp:255
virtual void InsertPo(ThreadConstIt before, EvtState *evts)=0
Definition: compiler.hpp:245
Op< Backend, EvtStateCats > Operation
Definition: armv7.hpp:211
Compiler(std::unique_ptr< EvtState > evts)
Definition: compiler.hpp:312
Operation::Threads Threads
Definition: compiler.hpp:297
Definition: compiler.hpp:253
bool UpdateObs(types::InstPtr ip, int part, types::Addr addr, const types::WriteID *from_id, std::size_t size, EvtState *evts) override
Definition: compiler.hpp:270
std::array< const mc::Event *, max_size_bytes/sizeof(types::WriteID)> EventPtrs
Definition: compiler.hpp:64
const mc::Event * LastEvent(const mc::Event *next_event, EvtState *evts) const override
Definition: compiler.hpp:277
const EvtState * evts() const
Definition: compiler.hpp:352
virtual void AdvanceThread(ThreadItStack *it_stack) const
Definition: compiler.hpp:133
bool EnableEmit(EvtState *evts) override
Definition: compiler.hpp:257
Types< true >::WriteID WriteID
Write ID type.
Definition: types.hpp:86
Op(types::Pid pid)
Definition: compiler.hpp:129
Types< true >::Addr Addr
Address type.
Definition: types.hpp:66
virtual void RegisterCallback(CallbackStack *callback_stack)
Definition: compiler.hpp:172
const Threads & threads()
Definition: compiler.hpp:350
std::shared_ptr< Op > Ptr
Definition: compiler.hpp:82
Compiler(std::unique_ptr< EvtState > evts, Threads &&threads)
Definition: compiler.hpp:326
Types< true >::Pid Pid
Processor/thread ID type.
Definition: types.hpp:71
virtual const mc::Event * FirstEvent(const mc::Event *prev_event, EvtState *evts) const =0
types::Pid pid_
Definition: compiler.hpp:241
Operation::CallbackStack CallbackStack
Definition: compiler.hpp:302
Backend backend_
Definition: compiler.hpp:506
friend std::size_t threads_size(const Threads &threads)
Definition: compiler.hpp:118
types::Pid pid() const
Definition: compiler.hpp:236
InstPtr_Op ip_to_op_
Definition: compiler.hpp:511
void set_pid(types::Pid pid)
Definition: compiler.hpp:238
Operation * IpToOp(types::InstPtr ip) const
Definition: compiler.hpp:479
virtual std::size_t Emit(types::InstPtr start, Backend *backend, EvtState *evts, void *code, std::size_t len)=0
std::size_t Emit(types::Pid pid, types::InstPtr base, void *code, std::size_t len)
Definition: compiler.hpp:408
MemOp(types::Pid pid)
Definition: compiler.hpp:247