1 // Copyright 2017-2019 VMware, Inc.
2 // SPDX-License-Identifier: BSD-2-Clause
3 //
4 // The BSD-2 license (the License) set forth below applies to all parts of the
5 // Cascade project.  You may not use this file except in compliance with the
6 // License.
7 //
8 // BSD-2 License
9 //
10 // Redistribution and use in source and binary forms, with or without
11 // modification, are permitted provided that the following conditions are met:
12 //
13 // 1. Redistributions of source code must retain the above copyright notice, this
14 // list of conditions and the following disclaimer.
15 //
16 // 2. Redistributions in binary form must reproduce the above copyright notice,
17 // this list of conditions and the following disclaimer in the documentation
18 // and/or other materials provided with the distribution.
19 //
20 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND
21 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 #include "runtime/module.h"
32 
33 #include <cassert>
34 #include <iostream>
35 #include <mutex>
36 #include <sstream>
37 #include <unordered_map>
38 #include <unordered_set>
39 #include "runtime/data_plane.h"
40 #include "runtime/isolate.h"
41 #include "runtime/runtime.h"
42 #include "target/compiler.h"
43 #include "target/engine.h"
44 #include "target/state.h"
45 #include "verilog/analyze/module_info.h"
46 #include "verilog/analyze/resolve.h"
47 #include "verilog/ast/ast.h"
48 #include "verilog/print/print.h"
49 #include "verilog/program/elaborate.h"
50 #include "verilog/program/inline.h"
51 #include "verilog/transform/assign_unpack.h"
52 #include "verilog/transform/block_flatten.h"
53 #include "verilog/transform/constant_prop.h"
54 #include "verilog/transform/control_merge.h"
55 #include "verilog/transform/de_alias.h"
56 #include "verilog/transform/delete_initial.h"
57 #include "verilog/transform/dead_code_eliminate.h"
58 #include "verilog/transform/event_expand.h"
59 #include "verilog/transform/index_normalize.h"
60 #include "verilog/transform/loop_unroll.h"
61 
62 using namespace std;
63 
64 namespace {
65 
66 // TODO(eschkufz) This is a bit overkill and not exactly the right way to do
67 // what this is being used for. This mutex sequentializes the execution of the
68 // alternate interrupts scheduled with schedule_interrupt(). This prevents multiple
69 // threads from interleaving the execution of ~Engine();
70 
71 mutex alt_lock_;
72 
73 } // namespace
74 
75 namespace cascade {
76 
operator *() const77 Module* Module::iterator::operator*() const {
78   return path_.front();
79 }
80 
operator ++()81 Module::iterator& Module::iterator::operator++() {
82   if (path_.front() == nullptr) {
83     return *this;
84   }
85   const auto* ptr = path_.front();
86   path_.pop_front();
87 
88   // Sort children lexicographically to enforce deterministic iteration
89   // orderings.
90   for (auto i = ptr->children_.rbegin(), ie = ptr->children_.rend(); i != ie; ++i) {
91     path_.push_front(*i);
92   }
93   return *this;
94 }
95 
operator ==(const Module::iterator & rhs) const96 bool Module::iterator::operator==(const Module::iterator& rhs) const {
97   assert(!path_.empty());
98   assert(!rhs.path_.empty());
99   return path_.front() == rhs.path_.front();
100 }
101 
operator !=(const Module::iterator & rhs) const102 bool Module::iterator::operator!=(const Module::iterator& rhs) const {
103   assert(!path_.empty());
104   assert(!rhs.path_.empty());
105   return path_.front() != rhs.path_.front();
106 }
107 
iterator()108 Module::iterator::iterator() {
109   path_.push_front(nullptr);
110 }
111 
iterator(Module * m)112 Module::iterator::iterator(Module* m) {
113   path_.push_front(nullptr);
114   path_.push_front(m);
115 }
116 
Module(const ModuleDeclaration * psrc,Runtime * rt,Module * parent)117 Module::Module(const ModuleDeclaration* psrc, Runtime* rt, Module* parent) {
118   rt_ = rt;
119 
120   psrc_ = psrc;
121   parent_ = parent;
122 
123   engine_ = rt_->get_compiler()->compile_stub(rt_->get_next_id(), psrc);
124   version_ = 0;
125 }
126 
~Module()127 Module::~Module() {
128   for (auto* c : children_) {
129     delete c;
130   }
131   delete engine_;
132 }
133 
begin()134 Module::iterator Module::begin() {
135   return iterator(this);
136 }
137 
end()138 Module::iterator Module::end() {
139   return iterator();
140 }
141 
engine()142 Engine* Module::engine() {
143   return engine_;
144 }
145 
size() const146 size_t Module::size() const {
147   size_t res = 0;
148   for (auto i = iterator(const_cast<Module*>(this)), ie = const_cast<Module*>(this)->end(); i != ie; ++i) {
149     ++res;
150   }
151   return res;
152 }
153 
synchronize(size_t n)154 void Module::synchronize(size_t n) {
155   // Examine new code and instantiate new modules below the root
156   Instantiator inst(this);
157   const auto idx = psrc_->size_items() - n;
158   for (auto i = psrc_->begin_items()+idx, ie = psrc_->end_items(); i != ie; ++i) {
159     (*i)->accept(&inst);
160   }
161   // Recompile everything
162   for (auto i = iterator(this), ie = end(); i != ie; ++i) {
163     const auto ignore = (*i == this) ? (psrc_->size_items() - n) : 0;
164     (*i)->compile_and_replace(ignore);
165   }
166   // Synchronize subscriptions with the dataplane. Note that we do this *after*
167   // recompilation.  This guarantees that the variable names used by
168   // Isolate::isolate() are deterministic.
169   for (auto i = iterator(this), ie = end(); i != ie; ++i) {
170     for (auto* r : ModuleInfo((*i)->psrc_).reads()) {
171       const auto gid = rt_->get_isolate()->isolate(r);
172       rt_->get_data_plane()->register_id(gid);
173       rt_->get_data_plane()->register_writer((*i)->engine_, gid);
174     }
175     for (auto* w : ModuleInfo((*i)->psrc_).writes()) {
176       const auto gid = rt_->get_isolate()->isolate(w);
177       rt_->get_data_plane()->register_id(gid);
178       rt_->get_data_plane()->register_reader((*i)->engine_, gid);
179     }
180   }
181 }
182 
rebuild()183 void Module::rebuild() {
184   // This method should only be called in a state where all modules are in sync
185   // with the user's program. However(!) we do still need to regenerate source.
186   // Recall that compilation takes over ownership of a module's source code.
187   for (auto i = iterator(this), ie = end(); i != ie; ++i) {
188     const auto ignore = (*i)->psrc_->size_items();
189     (*i)->compile_and_replace(ignore);
190   }
191 }
192 
save(ostream & os)193 void Module::save(ostream& os) {
194   os << size() << endl;
195 
196   for (auto i = iterator(this), ie = end(); i != ie; ++i) {
197     const auto* p = (*i)->psrc_->get_parent();
198     assert(p != nullptr);
199     assert(p->is(Node::Tag::module_instantiation));
200 
201     const auto fid = Resolve().get_readable_full_id(static_cast<const ModuleInstantiation*>(p)->get_iid());
202     ostream(rt_->rdbuf(Runtime::stdinfo_)) << "<save> " << fid << endl;
203 
204     os << "MODULE:" << endl;
205     os << rt_->get_isolate()->isolate(static_cast<const ModuleInstantiation*>(p)) << endl;
206 
207     os << "INPUT:" << endl;
208     auto* input = (*i)->engine_->get_input();
209     input->write(os, 16);
210     delete input;
211 
212     os << "STATE:" << endl;
213     auto* state = (*i)->engine_->get_state();
214     state->write(os, 16);
215     delete state;
216   }
217 }
218 
restart(std::istream & is)219 void Module::restart(std::istream& is) {
220   // Read save file
221   size_t n = 0;
222   is >> n;
223   unordered_map<MId, pair<Input*, State*>> save;
224   for (size_t i = 0; i < n; ++i) {
225     string ignore;
226     MId id;
227     auto input = new Input();
228     auto state = new State();
229 
230     is >> ignore >> id;
231     is >> ignore;
232     input->read(is, 16);
233     is >> ignore;
234     state->read(is, 16);
235 
236     save[id] = make_pair(input, state);
237   }
238 
239   // Update module hierarchy
240   for (auto i = iterator(this), ie = end(); i != ie; ++i) {
241     const auto* p = (*i)->psrc_->get_parent();
242     assert(p != nullptr);
243     assert(p->is(Node::Tag::module_instantiation));
244 
245     const auto fid = Resolve().get_readable_full_id(static_cast<const ModuleInstantiation*>(p)->get_iid());
246     ostream(rt_->rdbuf(Runtime::stdinfo_)) << "<restart> " << fid << endl;
247 
248     const auto id = rt_->get_isolate()->isolate(static_cast<const ModuleInstantiation*>(p));
249     const auto itr = save.find(id);
250     if (itr == save.end()) {
251       continue;
252     }
253 
254     (*i)->engine_->set_input(itr->second.first);
255     (*i)->engine_->set_state(itr->second.second);
256   }
257 
258   // Delete contents of save file
259   for (auto& s : save) {
260     delete s.second.first;
261     delete s.second.second;
262   }
263 }
264 
Instantiator(Module * ptr)265 Module::Instantiator::Instantiator(Module* ptr) {
266   ptr_ = ptr;
267   instances_.push_back(ptr_);
268 }
269 
visit(const CaseGenerateConstruct * cgc)270 void Module::Instantiator::visit(const CaseGenerateConstruct* cgc) {
271   if (Elaborate().is_elaborated(cgc)) {
272     Elaborate().get_elaboration(cgc)->accept(this);
273   }
274 }
275 
visit(const IfGenerateConstruct * igc)276 void Module::Instantiator::visit(const IfGenerateConstruct* igc) {
277   if (Elaborate().is_elaborated(igc)) {
278     Elaborate().get_elaboration(igc)->accept(this);
279   }
280 }
281 
visit(const LoopGenerateConstruct * lgc)282 void Module::Instantiator::visit(const LoopGenerateConstruct* lgc) {
283   if (Elaborate().is_elaborated(lgc)) {
284     for (auto* b : Elaborate().get_elaboration(lgc)) {
285       b->accept(this);
286     }
287   }
288 }
289 
visit(const ModuleInstantiation * mi)290 void Module::Instantiator::visit(const ModuleInstantiation* mi) {
291   // Inline Case: Descend past here
292   if (Inline().is_inlined(mi)) {
293     return Inline().get_source(mi)->accept(this);
294   }
295 
296   // Look up the checker associated with this instantiation
297   auto itr = ModuleInfo(ptr_->psrc_).children().find(mi->get_iid());
298   assert(itr != ModuleInfo(ptr_->psrc_).children().end());
299 
300   // Create a new node
301   auto* child = new Module(itr->second, ptr_->rt_, ptr_);
302   ptr_->children_.push_back(child);
303 
304   // Continue down through this module
305   ptr_ = child;
306   instances_.push_back(ptr_);
307   ptr_->psrc_->accept(this);
308   ptr_ = child->parent_;
309 }
310 
311 
312 
regenerate_ir_source(size_t ignore)313 ModuleDeclaration* Module::regenerate_ir_source(size_t ignore) {
314   auto* md = rt_->get_isolate()->isolate(psrc_, ignore);
315   const auto* std = md->get_attrs()->get<String>("__std");
316   const auto is_logic = (std != nullptr) && (std->get_readable_val() == "logic");
317   if (is_logic) {
318     ModuleInfo(md).invalidate();
319     AssignUnpack().run(md);
320     IndexNormalize().run(md);
321     LoopUnroll().run(md);
322     DeAlias().run(md);
323     ConstantProp().run(md);
324     EventExpand().run(md);
325     ControlMerge().run(md);
326     DeadCodeEliminate().run(md);
327     BlockFlatten().run(md);
328   }
329   return md;
330 }
331 
compile_and_replace(size_t ignore)332 void Module::compile_and_replace(size_t ignore) {
333   // Generate new code and bump the sequence number for this module
334   auto* md = regenerate_ir_source(ignore);
335   const auto this_version = ++version_;
336 
337   // Record human readable name for this module
338   const auto* iid = static_cast<const ModuleInstantiation*>(psrc_->get_parent())->get_iid();
339   const auto fid = Resolve().get_readable_full_id(iid);
340 
341   // Invoke compilations until all jit passes are scheduled
342   compile_and_replace(md, this_version, fid, 1);
343 }
344 
compile_and_replace(ModuleDeclaration * md,size_t version,const string & id,size_t pass)345 void Module::compile_and_replace(ModuleDeclaration* md, size_t version, const string& id, size_t pass) {
346   // Lookup annotations
347   const auto* std = md->get_attrs()->get<String>("__std");
348   const auto* t = md->get_attrs()->get<String>("__target");
349   const auto* l = md->get_attrs()->get<String>("__loc");
350 
351   // Check: Is jit compilation required?
352   const auto tsep = t->get_readable_val().find_first_of(';');
353   const auto lsep = l->get_readable_val().find_first_of(';');
354   const auto jit = std->eq("logic") && ((tsep != string::npos) || (lsep != string::npos));
355 
356   // If we're jit compiling, we'll need a second copy of the source.
357   ModuleDeclaration* md2 = nullptr;
358   if (jit) {
359     md2 = md->clone();
360     if (tsep != string::npos) {
361       md2->get_attrs()->set_or_replace("__target", new String(t->get_readable_val().substr(tsep+1)));
362       md->get_attrs()->set_or_replace("__target", new String(t->get_readable_val().substr(0, tsep)));
363     }
364     if (lsep != string::npos) {
365       md2->get_attrs()->set_or_replace("__loc", new String(l->get_readable_val().substr(lsep+1)));
366       md->get_attrs()->set_or_replace("__loc", new String(l->get_readable_val().substr(0, lsep)));
367     }
368     md->get_attrs()->erase("__delay");
369     md->get_attrs()->erase("__state_safe_int");
370   } else {
371     md2 = new ModuleDeclaration(new Attributes(), new Identifier("null"));
372   }
373   // Invariant: Initial blocks are removed from pass n compilations
374   if (pass > 1) {
375     DeleteInitial().run(md);
376   }
377   // Invariant: First pass for logic must be sw
378   if (std->eq("logic") && (pass == 1) && !md->get_attrs()->get<String>("__target")->eq("sw")) {
379     rt_->get_compiler()->fatal("Pass 1 compilation for logic must target software!");
380     delete md;
381     delete md2;
382     return;
383   }
384 
385   // Compile code
386   stringstream ss;
387   ss << "pass " << pass << " compilation of " << id << " with attributes " << md->get_attrs();
388   const auto info = ss.str();
389   auto* e = rt_->get_compiler()->compile(engine_->get_id(), md);
390 
391   // Special handling for pass 1 compilation, which isn't run asynchronously
392   // and has strict reqiurements on successful completion.
393   if (pass == 1) {
394     if (e == nullptr) {
395       rt_->get_compiler()->fatal("Unable to complete pass 1 compilation!");
396     } else {
397       engine_->replace_with(e);
398       if (engine_->is_stub()) {
399         ostream(rt_->rdbuf(Runtime::stdinfo_)) << "Deferring " << info << endl;
400       } else {
401         ostream(rt_->rdbuf(Runtime::stdinfo_)) << "Finished " << info << endl;
402       }
403     }
404     rt_->reset_open_loop_itrs();
405   }
406   // Pass n compilation takes place asynchronously
407   else {
408     rt_->schedule_interrupt([this, version, e, info]{
409       if ((version < version_) || (e == nullptr)) {
410         ostream(rt_->rdbuf(Runtime::stdinfo_)) << "Aborted " << info << endl;
411       } else {
412         engine_->replace_with(e);
413         ostream(rt_->rdbuf(Runtime::stdinfo_)) << "Finished " << info << endl;
414       }
415       rt_->reset_open_loop_itrs();
416     },
417     [e] {
418       lock_guard<mutex> lg(alt_lock_);
419       if (e != nullptr) {
420         delete e;
421       }
422     });
423   }
424 
425   // Run jit compilation asynchronously
426   if (jit && !engine_->is_stub() && (e != nullptr)) {
427     rt_->schedule_asynchronous(Runtime::Asynchronous([this, md2, version, id, pass, info]{
428       compile_and_replace(md2, version, id, pass+1);
429     }));
430   } else {
431     delete md2;
432   }
433 }
434 
435 } // namespace cascade
436