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