summaryrefslogtreecommitdiff
path: root/tiger-compiler/src/llvmtranslate/escapes-collector.cc
blob: 4984805f62eb81ab9f3ef35119f8421546a768ab (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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#include <ast/all.hh>
#include <ast/default-visitor.hh>
#include <ast/non-assert-visitor.hh>
#include <ast/non-object-visitor.hh>
#include <llvmtranslate/escapes-collector.hh>

namespace llvmtranslate
{
  /// LLVM IR doesn't support static link and nested functions.
  /// In order to translate those functions to LLVM IR, we use a technique
  /// called Lambda Lifting, which consists in passing a pointer to
  /// the escaped variables to the nested function using that variable.
  ///
  /// In order to do that, we need a visitor to collect these kind of
  /// variables and associate them to each function.

  class EscapesCollector
    : public ast::DefaultConstVisitor
    , public ast::NonObjectConstVisitor
    , public ast::NonAssertConstVisitor
  {
  public:
    /// Super class.
    using super_type = ast::DefaultConstVisitor;
    /// Import overloaded operator() methods.
    using super_type::operator();

    EscapesCollector()
      : did_modify_{false}
      , escaped_{}
    {}

    escaped_map_type& escaped_get() { return escaped_; }

    void operator()(const ast::FunctionChunk& e) override
    {
      const bool saved_did_modify = did_modify_;

      // Iterate on the chunk in order to iteratively collect all the callee
      // functions' escaped variables.
      did_modify_ = !e.empty();
      while (did_modify_)
        {
          did_modify_ = false;
          super_type::operator()(e);
        }

      did_modify_ = saved_did_modify;
    }

    void operator()(const ast::FunctionDec& e) override
    {
      // Keep track of the current function
      // FIXME DONE: Some code was deleted here.
      const auto function_type = \
        dynamic_cast<const type::Function*>(&e.type_get()->actual());

      precondition(function_type != nullptr);

      // Last visited function
      const type::Function* previous_scope = current_;

      if (previous_scope == nullptr)
        {
          // Then we are at the first depth of the program, hence _main
          main_function_ = function_type;
        }

      current_ = function_type;

      if (!escaped_.contains(current_))
      {
        escaped_[current_] = misc::set<const ast::VarDec*>{};
      }

      super_type::operator()(e);

      // Guess we only really need to go back one step before
      current_ = previous_scope;
    }

    void operator()(const ast::CallExp& e) override
    {
      super_type::operator()(e);

      // FIXME DONE: Some code was deleted here.
      const auto escaped_reference = escaped_.find(current_);
      precondition(escaped_reference != escaped_.end());
      const auto escaped_state = escaped_reference->second;

      const size_t before_size = escaped_state.size();

      super_type::operator()(e.def_get());

      // Check whether there are any newly collected escaped variables.
      // If there are, mark the iteration as modified.
      // FIXME DONE: Some code was deleted here.
      const size_t after_size = escaped_state.size();

      did_modify_ = before_size != after_size;
    }

    void operator()(const ast::SimpleVar& e) override
    {
      // Associate escaped variables declared in parent frames with their
      // functions
      // FIXME DONE: Some code was deleted here.
      if (current_ == main_function_ || !e.def_get()->escape_get())
      {
          super_type::operator()(e);
          return;
      }

      // Checking that the variable is escaped because not all variables are
      auto attached_to = escaped_.find(current_);

      // We have a function to attach this to
      if (attached_to != escaped_.end()
          && !attached_to->second.contains(e.def_get()))
      {
          attached_to->second.insert(e.def_get());
          did_modify_ = true;
      }

      super_type::operator()(e);
    }

  private:
    /// Whether any modification was done during the iteration.
    bool did_modify_ = false;

    /// Associate a set of variables with their function.
    escaped_map_type escaped_;

    /// Current visiting function.
    // FIXME DONE: Some code was deleted here.
    const type::Function* current_ = nullptr;

    /// The reference to the main function
    const type::Function* main_function_ = nullptr;
  };

  escaped_map_type collect_escapes(const ast::Ast& ast)
  {
    EscapesCollector collect;
    collect(ast);

    return std::move(collect.escaped_get());
  }

} // namespace llvmtranslate