summaryrefslogtreecommitdiff
path: root/compiler/loco/doc/LEP_000_Dialect_Service.md
blob: f6f6dc80922a40e3217a07be3a7f6766a8400d5e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# Dialect Service

This loco enhancement proposal (_LEP_) discusses how to permit a _loco_ graph without canonical dialect.

## Revision

| Date | Status |
| ---  | --- |
| 2019/09/03 | Proposed |

## Motivation

One of key design principles behind _loco_ is to allow users (= NN compiler writers) to easily define their own intermediate representation (IR) on top of shared infrastructure.

Unfortunately, however, there is a gap between dream and reality.
It is currently impossible to create a _loco_ graph only with non-canonical dialects;
there is no way to express the interaction between graph-level output without _canonical.Push_ node.

This proposal aims to remove this restriction in order to bridge the gap between dream and reality.

## Design

Each dialect is now allowed to expose its internal to its client (such as transformations and core algorithms) through a so-called "Service" interface.

Although this proposal focuses on ``output_nodes`` helper in _loco.core_, its coverage is not limited to this helper.
Any pass and algorithm can take an advantage of this generic infrastructure.

Let us dive into some details.

### What is "service"?

A service declares a collection of APIs that each **client** (not dialect) needs.

Let us consider ``output_nodes``. ``output_nodes`` needs to check whether a node is associated with any graph-level output.

Here is one possible service design that satisfies this need.
```cxx
virtual bool associated(const Node *node) const = 0;
virtual GraphOutputIndex index(const Node *node) const = 0;
```

### How to declare a service

All of these service interfaces should inherit ``loco::DialectService`` interface that _loco.core_ defines.
```cxx
struct DialectService
{
  virtual ~DialectService() = default;
};
```

For example, it is possible to declare the service that ``output_nodes`` needs as follows:
```cxx
struct GraphOutputIndexQueryService : public DialectService
{
  virtual ~GraphOutputIndexQueryService() = default;

  virtual bool associated(const Node *node) const = 0;
  virtual GraphOutputIndex index(const Node *node) const = 0;
};
```

### How to access a service

This proposal extends ``Dialect`` class with ``service`` method.

Each dialect SHOULD return a valid pointer on ``service<Service>`` method call if it implements that service. Otherwise, it SHOULD return a null pointer otherwise.

**WARNING** It is impossible to use ``get``. ``get`` is currently reserved for singleton accessor.

Given a ``GraphOutputIndexQueryService``, it is possible to revise ``output_nodes`` as follows:
```cxx
std::vector<loco::Node *> output_nodes(loco::Graph *g)
{
  std::map<GraphOutputIndex, loco::Node *> table;

  for (uint32_t n = 0; n < g->nodes()->size(); ++n)
  {
    auto node = g->nodes()->at(n);

    if (auto service = node->dialect()->service<GraphOutputIndexQueryService>())
    {
      if (service->associated(node))
      {
        auto output_index = service->index(node);
        assert(table.find(output_index) == table.end());
        table[output_index] = node;
      }
    }
  }

  std::vector<loco::Node *> res;

  for (uint32_t n = 0; n < g->outputs()->size(); ++n)
  {
    auto it = table.find(n);
    // NOTE This behavior originates from the current implementation of output_nodes
    res.emplace_back(it == table.end() ? nullptr : it->second);
  }

  return res;
}
```

**PLEASE NOTE THAT** ``output_nodes`` now works with all the dialects that implement ``GraphOutputIndexQueryService``.

### How to register a service

Each dialect should invoke protected ``service`` method during its construction.
```cxx
AwesomeDialect::AwesomeDialect()
{
  std::unique_ptr<Impl> impl = ...;
  service<GraphOutputIndexQueryService>(std::move(impl));
}
```