mutable
A Database System for Research and Fast Prototyping
Loading...
Searching...
No Matches
V8Engine.cpp
Go to the documentation of this file.
2
7#include "storage/Store.hpp"
8#include <chrono>
9#include <cstdint>
10#include <cstdlib>
11#include <cstring>
12#include <ctime>
13#include <fstream>
14#include <fstream>
15#include <libplatform/libplatform.h>
18#include <mutable/IR/Tuple.hpp>
19#include <mutable/Options.hpp>
26#include <sstream>
27#include <stdexcept>
28#include <string_view>
29#include <unordered_set>
30
31// must be included after Binaryen due to conflicts, e.g. with `::wasm::Throw`
32#include "backend/WasmMacro.hpp"
33
34
35using namespace m;
36using namespace m::storage;
37using namespace m::wasm;
38using namespace m::wasm::detail;
39using args_t = v8::Local<v8::Value>[];
40
41
42namespace {
43
44namespace options {
45
47int wasm_optimization_level = 0;
49bool wasm_adaptive = false;
51bool wasm_compilation_cache = true;
53bool wasm_dump = false;
55bool asm_dump = false;
57uint16_t cdt_port = 0;
58
59}
60
61
62/*======================================================================================================================
63 * V8Engine
64 *====================================================================================================================*/
65
68struct V8Engine : m::WasmEngine
69{
70 friend void create_V8Engine();
71 friend void destroy_V8Engine();
72 friend void register_WasmV8();
73
74 private:
75 static inline v8::Platform *PLATFORM_ = nullptr;
76 v8::ArrayBuffer::Allocator *allocator_ = nullptr;
77 v8::Isolate *isolate_ = nullptr;
78
79 /*----- Objects for remote debugging via CDT. --------------------------------------------------------------------*/
80 std::unique_ptr<V8InspectorClientImpl> inspector_;
81
82 public:
83 V8Engine();
84 V8Engine(const V8Engine&) = delete;
85 V8Engine(V8Engine&&) = default;
86
87 ~V8Engine();
88
89 static v8::Platform * platform() {
90 M_insist(bool(PLATFORM_));
91 return PLATFORM_;
92 }
93
94 void initialize();
95 void compile(const m::MatchBase &plan) const override;
96 void execute(const m::MatchBase &plan) override;
97};
98
99
100/*======================================================================================================================
101 * Implementation of V8Inspector and helper classes / methods
102 *====================================================================================================================*/
103
104inline v8::Local<v8::String> to_v8_string(v8::Isolate *isolate, std::string_view sv) {
105 M_insist(isolate);
106 return v8::String::NewFromUtf8(isolate, sv.data(), v8::NewStringType::kNormal, sv.length()).ToLocalChecked();
107}
108
109inline std::string to_std_string(v8::Isolate *isolate, v8::Local<v8::Value> val) {
110 v8::String::Utf8Value utf8(isolate, val);
111 return *utf8;
112}
113
114inline v8::Local<v8::Object> parse_json(v8::Isolate *isolate, std::string_view json) {
115 M_insist(isolate);
116 auto Ctx = isolate->GetCurrentContext();
117 auto value = v8::JSON::Parse(Ctx, to_v8_string(isolate, json)).ToLocalChecked();
118 if (value.IsEmpty())
119 return v8::Local<v8::Object>();
120 return value->ToObject(Ctx).ToLocalChecked();
121}
122
123inline v8_inspector::StringView make_string_view(const std::string &str) {
124 return v8_inspector::StringView(reinterpret_cast<const uint8_t*>(str.data()), str.length());
125}
126
127inline std::string to_std_string(v8::Isolate *isolate, const v8_inspector::StringView sv) {
128 int length = static_cast<int>(sv.length());
129 v8::Local<v8::String> message = (
130 sv.is8Bit()
131 ? v8::String::NewFromOneByte(isolate, reinterpret_cast<const uint8_t*>(sv.characters8()), v8::NewStringType::kNormal, length)
132 : v8::String::NewFromTwoByte(isolate, reinterpret_cast<const uint16_t*>(sv.characters16()), v8::NewStringType::kNormal, length)
133 ).ToLocalChecked();
134 v8::String::Utf8Value result(isolate, message);
135 return std::string(*result, result.length());
136}
137
138}
139
140void WebSocketChannel::sendResponse(int, std::unique_ptr<v8_inspector::StringBuffer> message)
141{
142 v8::HandleScope handle_scope(isolate_);
143 auto str = to_std_string(isolate_, message->string());
144 conn_.send(str);
145}
146
147void WebSocketChannel::sendNotification(std::unique_ptr<v8_inspector::StringBuffer> message)
148{
149 v8::HandleScope handle_scope(isolate_);
150 auto str = to_std_string(isolate_, message->string());
151 conn_.send(str);
152}
153
154V8InspectorClientImpl::V8InspectorClientImpl(int16_t port, v8::Isolate *isolate)
155 : isolate_(M_notnull(isolate))
156 , server_(port, std::bind(&V8InspectorClientImpl::on_message, this, std::placeholders::_1))
157{
158 std::cout << "Initiating the V8 inspector server. To attach to the inspector, open Chrome/Chromium and "
159 "visit\n\n\t"
160 "devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:"
161 << port << '\n' << std::endl;
162
163 inspector_ = v8_inspector::V8Inspector::create(isolate, this);
164 conn_ = std::make_unique<WebSocketServer::Connection>(server_.await());
165 channel_ = std::make_unique<WebSocketChannel>(isolate_, *conn_);
166
167 /* Create a debugging session by connecting the V8Inspector instance to the channel. */
168 std::string state("mutable");
169 session_ = inspector_->connect(
170 /* contextGroupId= */ 1,
171 /* channel= */ channel_.get(),
172 /* state= */ make_string_view(state),
173 /* trustLevel= */ v8_inspector::V8Inspector::kFullyTrusted,
174 /* pauseState= */ v8_inspector::V8Inspector::kWaitingForDebugger
175 );
176}
177
178void V8InspectorClientImpl::register_context(v8::Local<v8::Context> context)
179{
180 std::string ctx_name("query");
181 inspector_->contextCreated(v8_inspector::V8ContextInfo( context, 1, make_string_view(ctx_name)));
182}
183
184void V8InspectorClientImpl::deregister_context(v8::Local<v8::Context> context)
185{
186 inspector_->contextDestroyed(context);
187}
188
189void V8InspectorClientImpl::on_message(std::string_view sv)
190{
191 v8_inspector::StringView msg(reinterpret_cast<const uint8_t*>(sv.data()), sv.length());
192
193 auto Ctx = isolate_->GetCurrentContext();
194 v8::HandleScope handle_scope(isolate_);
195 auto obj = parse_json(isolate_, sv);
196
197 session_->dispatchProtocolMessage(msg);
198
199 if (not obj.IsEmpty()) {
200 auto method = obj->Get(Ctx, to_v8_string(isolate_, "method")).ToLocalChecked();
201 auto method_name = to_std_string(isolate_, method);
202
203 if (method_name == "Runtime.runIfWaitingForDebugger") {
204 std::string reason("CDT");
205 session_->schedulePauseOnNextStatement(make_string_view(reason),
206 make_string_view(reason));
208 code_(); // execute the code to debug
209 }
210 }
211}
212
214{
215 static bool is_nested = false;
216 if (is_nested) return;
217
218 is_terminated_ = false;
219 is_nested = true;
220 while (not is_terminated_ and conn_->wait_on_message())
221 while (v8::platform::PumpMessageLoop(V8Engine::platform(), isolate_)) { }
222 is_terminated_ = true;
223 is_nested = false;
224}
225
226
227/*======================================================================================================================
228 * V8 Callback Functions
229 *
230 * Functions to be called from the WebAssembly module to give control flow and pass data to the host.
231 *====================================================================================================================*/
232
233void m::wasm::detail::insist(const v8::FunctionCallbackInfo<v8::Value> &info)
234{
235 M_insist(info.Length() == 1);
236 auto idx = info[0].As<v8::BigInt>()->Uint64Value();
237 auto [filename, line, msg] = Module::Get().get_message(idx);
238
239 std::cout.flush();
240 std::cerr << filename << ':' << line << ": Wasm_insist failed.";
241 if (msg)
242 std::cerr << " " << msg << '.';
243 std::cerr << std::endl;
244
245 abort();
246}
247
248void m::wasm::detail::_throw(const v8::FunctionCallbackInfo<v8::Value> &info)
249{
250 M_insist(info.Length() == 2);
251 auto type = static_cast<m::wasm::exception::exception_t>(info[0].As<v8::BigInt>()->Uint64Value());
252 auto idx = info[1].As<v8::BigInt>()->Uint64Value();
253 auto [filename, line, msg] = Module::Get().get_message(idx);
254
255 std::ostringstream oss;
256 oss << filename << ':' << line << ": Exception `" << m::wasm::exception::names_[type] << "` thrown.";
257 if (*msg)
258 oss << " " << msg << '.';
259 oss << std::endl;
260
261 throw m::wasm::exception(type, oss.str());
262}
263
264void m::wasm::detail::print(const v8::FunctionCallbackInfo<v8::Value> &info)
265{
266#ifndef NDEBUG
267 std::cout << "v8 function callback: ";
268#endif
269 for (int i = 0; i != info.Length(); ++i) {
270 v8::HandleScope handle_scope(info.GetIsolate());
271 if (i != 0) std::cout << ',';
272 v8::Local<v8::Value> v = info[i];
273 if (v->IsInt32())
274 std::cout << "0x" << std::hex << uint32_t(v.As<v8::Int32>()->Value()) << std::dec;
275 else
276 std::cout << *v8::String::Utf8Value(info.GetIsolate(), v);
277 }
278 std::cout << std::endl;
279}
280
281void m::wasm::detail::print_memory_consumption(const v8::FunctionCallbackInfo<v8::Value> &info)
282{
283 M_insist(Options::Get().statistics);
284
285 auto alloc_total_mem = info[0].As<v8::Uint32>()->Value();
286 auto alloc_peak_mem = info[1].As<v8::Uint32>()->Value();
287
288 std::cout << "Allocated memory overall consumption: " << alloc_total_mem / (1024.0 * 1024.0) << " MiB"<< std::endl;
289 std::cout << "Allocated memory peak consumption: " << alloc_peak_mem / (1024.0 * 1024.0) << " MiB"<< std::endl;
290}
291
292void m::wasm::detail::set_wasm_instance_raw_memory(const v8::FunctionCallbackInfo<v8::Value> &info)
293{
294 v8::Local<v8::WasmModuleObject> wasm_instance = info[0].As<v8::WasmModuleObject>();
295 v8::Local<v8::Int32> wasm_context_id = info[1].As<v8::Int32>();
296
297 auto &wasm_context = WasmEngine::Get_Wasm_Context_By_ID(wasm_context_id->Value());
298#ifndef NDEBUG
299 std::cerr << "Setting Wasm instance raw memory of the given instance to the VM of Wasm context "
300 << wasm_context_id->Value() << " at " << wasm_context.vm.addr() << " of " << wasm_context.vm.size()
301 << " bytes" << std::endl;
302#endif
303 v8::SetWasmInstanceRawMemory(wasm_instance, wasm_context.vm.as<uint8_t*>(), wasm_context.vm.size());
304}
305
306void m::wasm::detail::read_result_set(const v8::FunctionCallbackInfo<v8::Value> &info)
307{
309
310 auto &root_op = context.plan.get_matched_root();
311 auto &schema = root_op.schema();
312 auto deduplicated_schema = schema.deduplicate();
313 auto deduplicated_schema_without_constants = deduplicated_schema.drop_constants();
314
315 /* Get number of result tuples. */
316 auto num_tuples = info[1].As<v8::Uint32>()->Value();
317 if (num_tuples == 0)
318 return;
319
320 /* Compute address of result set. */
321 M_insist(info.Length() == 2);
322 auto result_set_offset = info[0].As<v8::Uint32>()->Value();
323 M_insist((result_set_offset == 0) == (deduplicated_schema_without_constants.num_entries() == 0),
324 "result set offset equals 0 (i.e. nullptr) iff schema contains only constants");
325 auto result_set = context.vm.as<uint8_t*>() + result_set_offset;
326
327 /* Find the projection nearest to the plan's root since it will determine the constants omitted in the result set. */
328 auto find_projection = [](const Operator &op) -> const ProjectionOperator * {
329 auto find_projection_impl = [](const Operator &op, auto &find_projection_ref) -> const ProjectionOperator * {
330 if (auto projection_op = cast<const ProjectionOperator>(&op)) {
331 return projection_op;
332 } else if (auto c = cast<const Consumer>(&op)) {
333 M_insist(c->children().size() == 1,
334 "at least one projection without siblings in the operator tree must be contained");
335 M_insist(c->schema().num_entries() == c->child(0)->schema().num_entries(),
336 "at least one projection with the same schema as the plan's root must be contained");
337#ifndef NDEBUG
338 for (std::size_t i = 0; i < c->schema().num_entries(); ++i)
339 M_insist(c->schema()[i].id == c->child(0)->schema()[i].id,
340 "at least one projection with the same schema as the plan's root must be contained");
341#endif
342 return find_projection_ref(*c->child(0), find_projection_ref);
343 } else {
344 return nullptr; // no projection found
345 }
346 };
347 return find_projection_impl(op, find_projection_impl);
348 };
349 auto projection = find_projection(root_op);
350
352 auto print_constant = [](std::ostringstream &out, const ast::Constant &c, const Type *type){
353 if (type->is_none()) {
354 out << "NULL";
355 return;
356 }
357
358 /* Interpret constant. */
359 auto value = Interpreter::eval(c);
360
362 [&](const Boolean&) { out << (value.as_b() ? "TRUE" : "FALSE"); },
363 [&](const Numeric &n) {
364 switch (n.kind) {
365 case Numeric::N_Int:
366 case Numeric::N_Decimal:
367 out << value.as_i();
368 break;
369 case Numeric::N_Float:
370 if (n.size() <= 32) {
371 const auto old_precision = out.precision(std::numeric_limits<float>::max_digits10 - 1);
372 out << value.as_f();
373 out.precision(old_precision);
374 } else {
375 const auto old_precision = out.precision(std::numeric_limits<double>::max_digits10 - 1);
376 out << value.as_d();
377 out.precision(old_precision);
378 }
379 }
380 },
381 [&](const CharacterSequence&) { out << '"' << reinterpret_cast<char*>(value.as_p()) << '"'; },
382 [&](const Date&) {
383 const int32_t date = value.as_i(); // signed because year is signed
384 const auto oldfill = out.fill('0');
385 const auto oldfmt = out.flags();
386 out << std::internal
387 << std::setw(date >> 9 > 0 ? 4 : 5) << (date >> 9) << '-'
388 << std::setw(2) << ((date >> 5) & 0xF) << '-'
389 << std::setw(2) << (date & 0x1F);
390 out.fill(oldfill);
391 out.flags(oldfmt);
392 },
393 [&](const DateTime&) {
394 const time_t time = value.as_i();
395 std::tm tm;
396 gmtime_r(&time, &tm);
397 out << put_tm(tm);
398 },
399 [](const NoneType&) { M_unreachable("should've been handled earlier"); },
400 [](auto&&) { M_unreachable("invalid type"); },
401 }, *type);
402 };
403
404 if (deduplicated_schema_without_constants.num_entries() == 0) {
405 /* Schema contains only constants. Create simple loop to generate `num_tuples` constant result tuples. */
406 M_insist(bool(projection), "projection must be found");
407 auto &projections = projection->projections();
408 if (auto callback_op = cast<const CallbackOperator>(&root_op)) {
409 Tuple tup(schema); // tuple entries which are not set are implicitly NULL
410 for (std::size_t i = 0; i < schema.num_entries(); ++i) {
411 auto &e = schema[i];
412 if (e.type->is_none()) continue; // NULL constant
413 M_insist(e.id.is_constant());
414 tup.set(i, Interpreter::eval(as<const ast::Constant>(projections[i].first)));
415 }
416 for (std::size_t i = 0; i < num_tuples; ++i)
417 callback_op->callback()(schema, tup);
418 } else if (auto print_op = cast<const PrintOperator>(&root_op)) {
419 std::ostringstream tup;
420 for (std::size_t i = 0; i < schema.num_entries(); ++i) {
421 auto &e = schema[i];
422 if (i)
423 tup << ',';
424 M_insist(e.id.is_constant());
425 print_constant(tup, as<const ast::Constant>(projections[i].first), e.type);
426 }
427 for (std::size_t i = 0; i < num_tuples; ++i)
428 print_op->out << tup.str() << '\n';
429 }
430 return;
431 }
432
433 /* Create data layout (without constants and duplicates). */
434 M_insist(bool(context.result_set_factory), "result set factory must be set");
435 auto layout = context.result_set_factory->make(deduplicated_schema_without_constants);
436
437 /* Extract results. */
438 if (auto callback_op = cast<const CallbackOperator>(&root_op)) {
439 auto loader = Interpreter::compile_load(deduplicated_schema_without_constants, result_set, layout,
440 deduplicated_schema_without_constants);
441 if (schema.num_entries() == deduplicated_schema.num_entries()) {
442 /* No deduplication was performed. Compute `Tuple` with constants. */
443 M_insist(schema == deduplicated_schema);
444 Tuple tup(schema); // tuple entries which are not set are implicitly NULL
445 for (std::size_t i = 0; i < schema.num_entries(); ++i) {
446 auto &e = schema[i];
447 if (e.type->is_none()) continue; // NULL constant
448 if (e.id.is_constant()) { // other constant
449 M_insist(bool(projection), "projection must be found");
450 tup.set(i, Interpreter::eval(as<const ast::Constant>(projection->projections()[i].first)));
451 }
452 }
453 Tuple *args[] = { &tup };
454 for (std::size_t i = 0; i != num_tuples; ++i) {
455 loader(args);
456 callback_op->callback()(schema, tup);
457 tup.clear();
458 }
459 } else {
460 /* Deduplication was performed. Compute a `Tuple` with duplicates and constants. */
461 Tuple tup_dedupl(deduplicated_schema_without_constants);
462 Tuple tup_dupl(schema); // tuple entries which are not set are implicitly NULL
463 for (std::size_t i = 0; i < schema.num_entries(); ++i) {
464 auto &e = schema[i];
465 if (e.type->is_none()) continue; // NULL constant
466 if (e.id.is_constant()) { // other constant
467 M_insist(bool(projection), "projection must be found");
468 tup_dupl.set(i, Interpreter::eval(as<const ast::Constant>(projection->projections()[i].first)));
469 }
470 }
471 Tuple *args[] = { &tup_dedupl, &tup_dupl };
472 for (std::size_t i = 0; i != deduplicated_schema_without_constants.num_entries(); ++i) {
473 auto &entry = deduplicated_schema_without_constants[i];
474 if (not entry.type->is_none())
475 loader.emit_Ld_Tup(0, i);
476 for (std::size_t j = 0; j != schema.num_entries(); ++j) {
477 auto &e = schema[j];
478 if (e.id == entry.id) {
479 M_insist(e.type == entry.type);
480 loader.emit_St_Tup(1, j, e.type);
481 }
482 }
483 if (not entry.type->is_none())
484 loader.emit_Pop();
485 }
486 for (std::size_t i = 0; i != num_tuples; ++i) {
487 loader(args);
488 callback_op->callback()(schema, tup_dupl);
489 }
490 }
491 } else if (auto print_op = cast<const PrintOperator>(&root_op)) {
492 /* Compute a `Tuple` with duplicates and constants. */
493 Tuple tup(deduplicated_schema_without_constants);
494 Tuple *args[] = { &tup };
495 auto printer = Interpreter::compile_load(deduplicated_schema_without_constants, result_set, layout,
496 deduplicated_schema_without_constants);
497 auto ostream_index = printer.add(&print_op->out);
498 bool constant_emitted = false;
499 std::size_t old_idx = -1UL;
500 for (std::size_t i = 0; i != schema.num_entries(); ++i) {
501 if (i != 0)
502 printer.emit_Putc(ostream_index, ',');
503 auto &e = schema[i];
504 if (not e.type->is_none()) {
505 if (e.id.is_constant()) { // constant except NULL
506 M_insist(bool(projection), "projection must be found");
507 printer.add_and_emit_load(Interpreter::eval(as<const ast::Constant>(projection->projections()[i].first)));
508 constant_emitted = true;
509 } else { // actual value
510 auto idx = deduplicated_schema_without_constants[e.id].first;
511 if (idx != old_idx) {
512 if (old_idx != -1UL)
513 printer.emit_Pop(); // to remove last loaded value
514 printer.emit_Ld_Tup(0, idx);
515 old_idx = idx;
516 }
517 }
518 }
519 printer.emit_Print(ostream_index, e.type);
520 if (e.type->is_none() or constant_emitted) {
521 printer.emit_Pop(); // to remove NULL pushed by `emit_Print()` or other constant pushed above
522 constant_emitted = false;
523 }
524 }
525 if (old_idx != -1UL)
526 printer.emit_Pop(); // to remove last loaded value
527 for (std::size_t i = 0; i != num_tuples; ++i) {
528 printer(args);
529 print_op->out << '\n';
530 }
531 }
532}
533
534template<typename Index, typename V8ValueT, bool IsLower>
535void m::wasm::detail::index_seek(const v8::FunctionCallbackInfo<v8::Value> &info)
536{
537 using key_type = Index::key_type;
538
539 /*----- Unpack function parameters -----*/
540 auto index_id = info[0].As<v8::BigInt>()->Uint64Value();
541 key_type key;
542 if constexpr (std::same_as<V8ValueT, v8::BigInt>)
543 key = info[1].As<V8ValueT>()->Int64Value();
544 else if constexpr (std::same_as<V8ValueT, v8::String>) {
545 auto offset = info[1].As<v8::Uint32>()->Value();
547 key = reinterpret_cast<const char*>(context.vm.as<uint8_t*>() + offset);
548 } else
549 key = info[1].As<V8ValueT>()->Value();
550
551 /*----- Obtain index and cast to correct type. -----*/
553 auto &index = as<const Index>(context.indexes[index_id]);
554
555 /*----- Seek index and return offset. -----*/
556 std::size_t offset = std::distance(
557 index.begin(),
558 M_CONSTEXPR_COND(IsLower, index.lower_bound(key), index.upper_bound(key))
559 );
560 M_insist(std::in_range<uint32_t>(offset), "should fit in uint32_t");
561 info.GetReturnValue().Set(uint32_t(offset));
562}
563
564template<typename Index>
565void m::wasm::detail::index_sequential_scan(const v8::FunctionCallbackInfo<v8::Value> &info)
566{
567 /*----- Unpack function parameters -----*/
568 auto index_id = info[0].As<v8::BigInt>()->Uint64Value();
569 auto entry_offset = info[1].As<v8::Uint32>()->Value();
570 auto address_offset = info[2].As<v8::Uint32>()->Value();
571 auto batch_size = info[3].As<v8::Uint32>()->Value();
572
573 /*----- Compute adress to write results to. -----*/
575 auto buffer_address = reinterpret_cast<uint32_t*>(context.vm.as<uint8_t*>() + address_offset);
576
577 /*----- Obtain index and cast to correct type. -----*/
578 auto &index = as<const Index>(context.indexes[index_id]);
579
580 /*----- Scan index and write result tuple ids to buffer -----*/
581 auto it = index.begin() + entry_offset;
582 for (uint32_t i = 0; i < batch_size; ++i, ++it)
583 buffer_address[i] = it->second;
584}
585
586
587/*======================================================================================================================
588 * V8Engine helper classes
589 *====================================================================================================================*/
590
591namespace {
592
593struct CollectStringLiterals : ConstOperatorVisitor, ast::ConstASTExprVisitor
594{
595 private:
596 std::unordered_set<const char*> literals_;
597
598 public:
599 static std::vector<const char*> Collect(const Operator &plan) {
600 CollectStringLiterals CSL;
601 CSL(plan);
602 return { CSL.literals_.begin(), CSL.literals_.end() };
603 }
604
605 private:
606 CollectStringLiterals() = default;
607
608 using ConstOperatorVisitor::operator();
609 using ConstASTExprVisitor::operator();
610
611 void recurse(const Consumer &C) {
612 for (auto &c: C.children())
613 (*this)(*c);
614 }
615
616 /*----- Operator -------------------------------------------------------------------------------------------------*/
617 void operator()(const ScanOperator&) override { /* nothing to be done */ }
618 void operator()(const CallbackOperator &op) override { recurse(op); }
619 void operator()(const PrintOperator &op) override { recurse(op); }
620 void operator()(const NoOpOperator &op) override { recurse(op); }
621 void operator()(const FilterOperator &op) override {
622 (*this)(op.filter());
623 recurse(op);
624 }
625 void operator()(const DisjunctiveFilterOperator &op) override {
626 (*this)(op.filter());
627 recurse(op);
628 }
629 void operator()(const JoinOperator &op) override {
630 (*this)(op.predicate());
631 recurse(op);
632 }
633 void operator()(const ProjectionOperator &op) override {
634 for (auto &p : op.projections())
635 (*this)(p.first.get());
636 recurse(op);
637 }
638 void operator()(const LimitOperator &op) override { recurse(op); }
639 void operator()(const GroupingOperator &op) override {
640 for (auto &[grp, alias] : op.group_by())
641 (*this)(grp.get());
642 recurse(op);
643 }
644 void operator()(const AggregationOperator &op) override { recurse(op); }
645 void operator()(const SortingOperator &op) override { recurse(op); }
646
647 /*----- CNF ------------------------------------------------------------------------------------------------------*/
648 void operator()(const cnf::CNF &cnf) {
649 for (auto &clause: cnf) {
650 for (auto &pred: clause)
651 (*this)(*pred);
652 }
653 }
654
655 /*----- Expr -----------------------------------------------------------------------------------------------------*/
656 void operator()(const ast::ErrorExpr&) override { M_unreachable("no errors at this stage"); }
657 void operator()(const ast::Designator&) override { /* nothing to be done */ }
658 void operator()(const ast::Constant &e) override {
659 if (e.is_string()) {
660 auto s = Interpreter::eval(e);
661 literals_.emplace(s.as<const char*>());
662 }
663 }
664 void operator()(const ast::FnApplicationExpr&) override { /* nothing to be done */ } // XXX can string literals be arguments?
665 void operator()(const ast::UnaryExpr &e) override { (*this)(*e.expr); }
666 void operator()(const ast::BinaryExpr &e) override { (*this)(*e.lhs); (*this)(*e.rhs); }
667 void operator()(const ast::QueryExpr&) override { /* nothing to be done */ }
668};
669
670struct CollectTables : ConstOperatorVisitor
671{
672 private:
673 struct hash
674 {
675 uint64_t operator()(const std::reference_wrapper<const Table> &r) const {
676 StrHash h;
677 return h((const char*)(r.get().name()));
678 }
679 };
680
681 struct equal
682 {
683 bool operator()(const std::reference_wrapper<const Table> &first,
684 const std::reference_wrapper<const Table> &second) const
685 {
686 return first.get().name() == second.get().name();
687 }
688 };
689
690
691 std::unordered_set<std::reference_wrapper<const Table>, hash, equal> tables_;
692
693 public:
694 static std::vector<std::reference_wrapper<const Table>> Collect(const Operator &plan) {
695 CollectTables CT;
696 CT(plan);
697 return { CT.tables_.begin(), CT.tables_.end() };
698 }
699
700 private:
701 CollectTables() = default;
702
703 using ConstOperatorVisitor::operator();
704
705 void recurse(const Consumer &C) {
706 for (auto &c: C.children())
707 (*this)(*c);
708 }
709
710 void operator()(const ScanOperator &op) override { tables_.emplace(op.store().table()); }
711 void operator()(const CallbackOperator &op) override { recurse(op); }
712 void operator()(const PrintOperator &op) override { recurse(op); }
713 void operator()(const NoOpOperator &op) override { recurse(op); }
714 void operator()(const FilterOperator &op) override { recurse(op); }
715 void operator()(const DisjunctiveFilterOperator &op) override { recurse(op); }
716 void operator()(const JoinOperator &op) override { recurse(op); }
717 void operator()(const ProjectionOperator &op) override { recurse(op); }
718 void operator()(const LimitOperator &op) override { recurse(op); }
719 void operator()(const GroupingOperator &op) override { recurse(op); }
720 void operator()(const AggregationOperator &op) override { recurse(op); }
721 void operator()(const SortingOperator &op) override { recurse(op); }
722};
723
724
725/*======================================================================================================================
726 * V8Engine implementation
727 *====================================================================================================================*/
728
729V8Engine::V8Engine() { initialize(); }
730
731V8Engine::~V8Engine()
732{
733 inspector_.reset();
734 if (isolate_) {
735 M_insist(allocator_);
736 isolate_->Dispose();
737 delete allocator_;
738 }
739}
740
741void V8Engine::initialize()
742{
743 M_insist(not allocator_);
744 M_insist(not isolate_);
745
746 /*----- Set V8 flags. --------------------------------------------------------------------------------------------*/
747 /* A documentation of these flags can be found at
748 * https://chromium.googlesource.com/v8/v8/+/2c22fd50128ad130e9dba77fce828e5661559121/src/flags/flag-definitions.h.*/
749 std::ostringstream flags;
750 flags << "--stack_size 1000000 ";
751 if (options::wasm_adaptive) {
752 flags << "--opt "
753 << "--liftoff "
754 << "--wasm-tier-up "
755 << "--wasm-dynamic-tiering "
756 << "--wasm-lazy-compilation "; // compile code lazily at runtime if needed
757 } else {
758 flags << "--no-liftoff "
759 << "--no-wasm-lazy-compilation "; // compile code before starting execution
760 }
761 if (not options::wasm_compilation_cache) {
762 flags << "--no-compilation-cache "
763 << "--no-wasm-native-module-cache-enabled ";
764 }
765 if (options::asm_dump) {
766 flags << "--code-comments " // include code comments
767 << "--print-code ";
768 }
769 if (options::cdt_port >= 1024) {
770 flags << "--wasm-bounds-checks "
771 << "--wasm-stack-checks "
772 << "--log "
773 << "--log-all "
774 << "--expose-wasm "
775 << "--trace-wasm "
776 << "--trace-wasm-instances "
777 << "--prof ";
778 } else {
779 flags << "--no-wasm-bounds-checks "
780 << "--no-wasm-stack-checks "
781 << "--wasm-simd-ssse3-codegen ";
782 }
783 v8::V8::SetFlagsFromString(flags.str().c_str());
784
785 v8::Isolate::CreateParams create_params;
786 create_params.array_buffer_allocator = allocator_ = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
787 isolate_ = v8::Isolate::New(create_params);
788}
789
790void V8Engine::compile(const m::MatchBase &plan) const
791{
792#if 1
793 /*----- Add print function. --------------------------------------------------------------------------------------*/
794 Module::Get().emit_function_import<void(uint32_t)>("print");
795 Module::Get().emit_function_import<void(uint32_t, uint32_t)>("print_memory_consumption");
796#endif
797
798 /*----- Emit code for run function which computes the last pipeline and calls other pipeline functions. ----------*/
799 FUNCTION(run, void(void))
800 {
801 auto S = CodeGenContext::Get().scoped_environment(); // create scoped environment for this function
803 }
804
805 /*----- Create function `main` which executes the given query. ---------------------------------------------------*/
806 m::wasm::Function<uint32_t(uint32_t)> main("main");
807 BLOCK_OPEN(main.body())
808 {
809 auto S = CodeGenContext::Get().scoped_environment(); // create scoped environment for this function
810 run(); // call run function
811 if (Options::Get().statistics) {
812 std::cout << "Pre-allocated memory overall consumption: "
813 << Module::Allocator().pre_allocated_memory_consumption() / (1024.0 * 1024.0)
814 << " MiB" << std::endl;
815 Module::Get().emit_call<void>("print_memory_consumption",
816 Module::Allocator().allocated_memory_consumption(),
817 Module::Allocator().allocated_memory_peak());
818 }
819 main.emit_return(CodeGenContext::Get().num_tuples()); // return size of result set
820 }
821
822 /*----- Export main. ---------------------------------------------------------------------------------------------*/
824
825 /*----- Dump the generated WebAssembly code ----------------------------------------------------------------------*/
826 if (options::wasm_dump)
827 Module::Get().dump_all(std::cout);
828
829#ifndef NDEBUG
830 /*----- Validate module before optimization. ---------------------------------------------------------------------*/
831 if (not Module::Validate()) {
833 throw std::logic_error("invalid module");
834 }
835#endif
836
837 /*----- Optimize module. -----------------------------------------------------------------------------------------*/
838#ifndef NDEBUG
839 std::ostringstream dump_before_opt;
840 Module::Get().dump(dump_before_opt);
841#endif
842 if (options::wasm_optimization_level)
843 Module::Optimize(options::wasm_optimization_level);
844
845#ifndef NDEBUG
846 /*----- Validate module after optimization. ----------------------------------------------------------------------*/
847 if (options::wasm_optimization_level and not Module::Validate()) {
848 std::cerr << "Module invalid after optimization!" << std::endl;
849 std::cerr << "WebAssembly before optimization:\n" << dump_before_opt.str() << std::endl;
850 std::cerr << "WebAssembly after optimization:\n";
851 Module::Get().dump(std::cerr);
852 throw std::logic_error("invalid module");
853 }
854#endif
855}
856
857void V8Engine::execute(const m::MatchBase &plan)
858{
859 Catalog &C = Catalog::Get();
860
861 Module::Init();
862 CodeGenContext::Init(); // fresh context
863
864 M_insist(bool(isolate_), "must have an isolate");
865 v8::Locker locker(isolate_);
866 isolate_->Enter();
867
868 {
869 /* Create required V8 scopes. */
870 v8::Isolate::Scope isolate_scope(isolate_);
871 v8::HandleScope handle_scope(isolate_); // tracks and disposes of all object handles
872
873 /* Create global template and context. */
874 v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate_);
875 global->Set(isolate_, "set_wasm_instance_raw_memory", v8::FunctionTemplate::New(isolate_, set_wasm_instance_raw_memory));
876 global->Set(isolate_, "read_result_set", v8::FunctionTemplate::New(isolate_, read_result_set));
877
878#define CREATE_TEMPLATES(IDXTYPE, KEYTYPE, V8TYPE, IDXNAME, SUFFIX) \
879 global->Set(isolate_, M_STR(idx_lower_bound_##IDXNAME##_##SUFFIX), v8::FunctionTemplate::New(isolate_, index_seek<IDXTYPE<KEYTYPE>, V8TYPE, true>)); \
880 global->Set(isolate_, M_STR(idx_upper_bound_##IDXNAME##_##SUFFIX), v8::FunctionTemplate::New(isolate_, index_seek<IDXTYPE<KEYTYPE>, V8TYPE, false>)); \
881 global->Set(isolate_, M_STR(idx_scan_##IDXNAME##_##SUFFIX), v8::FunctionTemplate::New(isolate_, index_sequential_scan<IDXTYPE<KEYTYPE>>))
882
883 CREATE_TEMPLATES(idx::ArrayIndex, bool, v8::Boolean, array, b);
884 CREATE_TEMPLATES(idx::ArrayIndex, int8_t, v8::Int32, array, i1);
885 CREATE_TEMPLATES(idx::ArrayIndex, int16_t, v8::Int32, array, i2);
886 CREATE_TEMPLATES(idx::ArrayIndex, int32_t, v8::Int32, array, i4);
887 CREATE_TEMPLATES(idx::ArrayIndex, int64_t, v8::BigInt, array, i8);
888 CREATE_TEMPLATES(idx::ArrayIndex, float, v8::Number, array, f);
889 CREATE_TEMPLATES(idx::ArrayIndex, double, v8::Number, array, d);
890 CREATE_TEMPLATES(idx::ArrayIndex, const char*, v8::String, array, p);
891 CREATE_TEMPLATES(idx::RecursiveModelIndex, int8_t, v8::Int32, rmi, i1);
892 CREATE_TEMPLATES(idx::RecursiveModelIndex, int16_t, v8::Int32, rmi, i2);
893 CREATE_TEMPLATES(idx::RecursiveModelIndex, int32_t, v8::Int32, rmi, i4);
894 CREATE_TEMPLATES(idx::RecursiveModelIndex, int64_t, v8::BigInt, rmi, i8);
895 CREATE_TEMPLATES(idx::RecursiveModelIndex, float, v8::Number, rmi, f);
896 CREATE_TEMPLATES(idx::RecursiveModelIndex, double, v8::Number, rmi, d);
897#undef CREATE_TEMPLATES
898
899 v8::Local<v8::Context> context = v8::Context::New(isolate_, /* extensions= */ nullptr, global);
900 v8::Context::Scope context_scope(context);
901
902 /* Create the import object for instantiating the WebAssembly module. */
903 WasmContext::config_t wasm_config{0};
904 if (options::cdt_port < 1024)
905 wasm_config |= WasmContext::TRAP_GUARD_PAGES;
906 auto &wasm_context = Create_Wasm_Context_For_ID(Module::ID(), plan, wasm_config);
907
908 auto imports = v8::Object::New(isolate_);
909 auto env = create_env(*isolate_, plan);
910
911 /* Map the remaining address space to the output buffer. */
912 M_insist(Is_Page_Aligned(wasm_context.heap));
913 const auto bytes_remaining = wasm_context.vm.size() - wasm_context.heap;
914 memory::Memory mem = Catalog::Get().allocator().allocate(bytes_remaining);
915 mem.map(bytes_remaining, 0, wasm_context.vm, wasm_context.heap);
916
917 auto compile_time = C.timer().create_timing("Compile SQL to machine code");
918 /* Compile the plan and thereby build the Wasm module. */
919 M_TIME_EXPR(compile(plan), "|- Compile SQL to WebAssembly", C.timer());
920 /* Perform memory pre-allocations and add allocation address initialization to env. */
921 Module::Get().emit_import<uint32_t>("alloc_addr_init");
922 M_DISCARD env->Set(isolate_->GetCurrentContext(), to_v8_string(isolate_, "alloc_addr_init"),
923 v8::Uint32::New(isolate_, Module::Allocator().perform_pre_allocations()));
924 M_DISCARD imports->Set(context, mkstr(*isolate_, "imports"), env);
925 /* Create a WebAssembly instance object. */
926 auto instance = M_TIME_EXPR(instantiate(*isolate_, imports), " ` Compile WebAssembly to machine code", C.timer());
927 compile_time.stop();
928
929 /* Set the underlying memory for the instance. */
930 v8::SetWasmInstanceRawMemory(instance, wasm_context.vm.as<uint8_t*>(), wasm_context.vm.size());
931
932 /* Get the exports of the created WebAssembly instance. */
933 auto exports = instance->Get(context, mkstr(*isolate_, "exports")).ToLocalChecked().As<v8::Object>();
934 auto main = exports->Get(context, mkstr(*isolate_, "main")).ToLocalChecked().As<v8::Function>();
935
936 /* If a debugging port is specified, set up the inspector and start it. */
937 if (options::cdt_port >= 1024 and not inspector_)
938 inspector_ = std::make_unique<V8InspectorClientImpl>(options::cdt_port, isolate_);
939 if (bool(inspector_)) {
940 run_inspector(*inspector_, *isolate_, env);
941 return;
942 }
943
944 /* Invoke the exported function `main` of the module. */
945 args_t args { v8::Int32::New(isolate_, wasm_context.id), };
946 const uint32_t num_rows =
947 M_TIME_EXPR(main->Call(context, context->Global(), 1, args).ToLocalChecked().As<v8::Uint32>()->Value(),
948 "Execute machine code", C.timer());
949
950 /* Print total number of result tuples. */
951 auto &root_op = plan.get_matched_root();
952 if (auto print_op = cast<const PrintOperator>(&root_op)) {
953 if (not Options::Get().quiet)
954 print_op->out << num_rows << " rows\n";
955 } else if (auto noop_op = cast<const NoOpOperator>(&root_op)) {
956 if (not Options::Get().quiet)
957 noop_op->out << num_rows << " rows\n";
958 }
959 Dispose_Wasm_Context(wasm_context);
960 }
961
962 isolate_->Exit();
965}
966
967__attribute__((constructor(101)))
968static void create_V8Engine()
969{
970 V8Engine::PLATFORM_ = v8::platform::NewDefaultPlatform().release();
971 v8::V8::InitializePlatform(V8Engine::PLATFORM_);
972 v8::V8::SetFlagsFromString("--no-freeze-flags-after-init"); // allow changing flags after initialization
973 v8::V8::Initialize();
974}
975
976__attribute__((destructor(101)))
977static void destroy_V8Engine()
978{
979 v8::V8::Dispose();
980 v8::V8::DisposePlatform();
981}
982
983__attribute__((constructor(202)))
984static void register_WasmV8()
985{
986 Catalog &C = Catalog::Get();
987 C.register_wasm_backend<V8Engine>(C.pool("WasmV8"), "WebAssembly backend using Google's V8 engine");
988
989 /*----- Command-line arguments -----------------------------------------------------------------------------------*/
990 C.arg_parser().add<int>(
991 /* group= */ "Wasm",
992 /* short= */ nullptr,
993 /* long= */ "--wasm-opt",
994 /* description= */ "set the optimization level for Wasm modules (0, 1, or 2)",
995 [] (int i) { options::wasm_optimization_level = i; }
996 );
997 C.arg_parser().add<bool>(
998 /* group= */ "WasmV8",
999 /* short= */ nullptr,
1000 /* long= */ "--wasm-adaptive",
1001 /* description= */ "enable adaptive execution of Wasm with Liftoff and dynamic tier-up",
1002 [] (bool b) { options::wasm_adaptive = b; }
1003 );
1004 C.arg_parser().add<bool>(
1005 /* group= */ "WasmV8",
1006 /* short= */ nullptr,
1007 /* long= */ "--no-wasm-compilation-cache",
1008 /* description= */ "disable V8's compilation cache",
1009 [] (bool) { options::wasm_compilation_cache = false; }
1010 );
1011 C.arg_parser().add<bool>(
1012 /* group= */ "Wasm",
1013 /* short= */ nullptr,
1014 /* long= */ "--wasm-dump",
1015 /* description= */ "dump the generated WebAssembly code to stdout",
1016 [] (bool b) { options::wasm_dump = b; }
1017 );
1018 C.arg_parser().add<bool>(
1019 /* group= */ "WasmV8",
1020 /* short= */ nullptr,
1021 /* long= */ "--asm-dump",
1022 /* description= */ "dump the generated assembly code to stdout",
1023 [] (bool b) { options::asm_dump = b; }
1024 );
1025 C.arg_parser().add<int>(
1026 /* group= */ "WasmV8",
1027 /* short= */ nullptr,
1028 /* long= */ "--CDT",
1029 /* description= */ "specify the port for debugging via ChromeDevTools",
1030 [] (int i) { options::cdt_port = i; }
1031 );
1032}
1033
1034}
1035
1036
1037/*======================================================================================================================
1038 * Implementation of helper methods
1039 *====================================================================================================================*/
1040
1041v8::Local<v8::String> m::wasm::detail::mkstr(v8::Isolate &isolate, const std::string &str)
1042{
1043 return to_v8_string(&isolate, str);
1044}
1045
1046v8::Local<v8::WasmModuleObject> m::wasm::detail::instantiate(v8::Isolate &isolate, v8::Local<v8::Object> imports)
1047{
1048 auto Ctx = isolate.GetCurrentContext();
1049 auto [binary_addr, binary_size] = Module::Get().binary();
1050 auto bs = v8::ArrayBuffer::NewBackingStore(
1051 /* data = */ binary_addr,
1052 /* byte_length= */ binary_size,
1053 /* deleter= */ v8::BackingStore::EmptyDeleter,
1054 /* deleter_data= */ nullptr
1055 );
1056 auto buffer = v8::ArrayBuffer::New(&isolate, std::move(bs));
1057
1058 if (Options::Get().statistics)
1059 std::cout << "Wasm code size: " << binary_size << std::endl;
1060
1061 args_t module_args { buffer };
1062 auto wasm = Ctx->Global()->Get(Ctx, mkstr(isolate, "WebAssembly")).ToLocalChecked().As<v8::Object>(); // WebAssembly class
1063 auto wasm_module = wasm->Get(Ctx, mkstr(isolate, "Module")).ToLocalChecked().As<v8::Object>()
1064 ->CallAsConstructor(Ctx, 1, module_args).ToLocalChecked().As<v8::WasmModuleObject>();
1065 free(binary_addr);
1066
1067 if (Options::Get().statistics)
1068 std::cout << "Machine code size: " << wasm_module->GetCompiledModule().Serialize().size << std::endl;
1069
1070 args_t instance_args { wasm_module, imports };
1071 return wasm->Get(Ctx, mkstr(isolate, "Instance")).ToLocalChecked().As<v8::Object>()
1072 ->CallAsConstructor(Ctx, 2, instance_args).ToLocalChecked().As<v8::WasmModuleObject>();
1073}
1074
1075v8::Local<v8::Object> m::wasm::detail::create_env(v8::Isolate &isolate, const m::MatchBase &plan)
1076{
1078 auto Ctx = isolate.GetCurrentContext();
1079 auto env = v8::Object::New(&isolate);
1080
1081 /* Map accessed tables into the Wasm module. */
1082 auto tables = CollectTables::Collect(plan.get_matched_root());
1083 for (auto &table : tables) {
1084 auto off = context.map_table(table.get());
1085
1086 /* Add memory address to env. */
1087 std::ostringstream oss;
1088 oss << table.get().name() << "_mem";
1089 M_DISCARD env->Set(Ctx, to_v8_string(&isolate, oss.str()), v8::Int32::New(&isolate, off));
1090 Module::Get().emit_import<void*>(oss.str().c_str());
1091
1092 /* Add table size (num_rows) to env. */
1093 oss.str("");
1094 oss << table.get().name() << "_num_rows";
1095 M_DISCARD env->Set(Ctx, to_v8_string(&isolate, oss.str()), v8::Int32::New(&isolate, table.get().store().num_rows()));
1096 Module::Get().emit_import<uint32_t>(oss.str().c_str());
1097 }
1098
1099 /* Map all string literals into the Wasm module. */
1100 M_insist(Is_Page_Aligned(context.heap));
1101 auto literals = CollectStringLiterals::Collect(plan.get_matched_root());
1102 std::size_t bytes = 0;
1103 for (auto literal : literals)
1104 bytes += strlen(literal) + 1;
1105 auto aligned_bytes = Ceil_To_Next_Page(bytes);
1106 if (aligned_bytes) {
1107 auto base_addr = context.vm.as<uint8_t*>() + context.heap;
1108 M_DISCARD mmap(base_addr, aligned_bytes, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANON|MAP_PRIVATE, -1, 0);
1109 char *start_addr = reinterpret_cast<char*>(base_addr);
1110 char *dst = start_addr;
1111 for (auto literal : literals) {
1112 CodeGenContext::Get().add_literal(literal, context.heap + (dst - start_addr)); // add literal
1113 dst = stpcpy(dst, literal) + 1; // copy into sequential memory
1114 }
1115 context.heap += aligned_bytes;
1116 context.install_guard_page();
1117 }
1118 M_insist(Is_Page_Aligned(context.heap));
1119
1120 /* Add functions to environment. */
1121 Module::Get().emit_function_import<void(void*,uint32_t)>("read_result_set");
1122
1123#define EMIT_FUNC_IMPORTS(KEYTYPE, IDXNAME, SUFFIX) \
1124 Module::Get().emit_function_import<uint32_t(std::size_t,KEYTYPE)>(M_STR(idx_lower_bound_##IDXNAME##_##SUFFIX)); \
1125 Module::Get().emit_function_import<uint32_t(std::size_t,KEYTYPE)>(M_STR(idx_upper_bound_##IDXNAME##_##SUFFIX)); \
1126 Module::Get().emit_function_import<void(std::size_t,uint32_t,void*,uint32_t)>(M_STR(idx_scan_##IDXNAME##_##SUFFIX))
1127
1128 EMIT_FUNC_IMPORTS(bool, array, b);
1129 EMIT_FUNC_IMPORTS(int8_t, array, i1);
1130 EMIT_FUNC_IMPORTS(int16_t, array, i2);
1131 EMIT_FUNC_IMPORTS(int32_t, array, i4);
1132 EMIT_FUNC_IMPORTS(int64_t, array, i8);
1133 EMIT_FUNC_IMPORTS(float, array, f);
1134 EMIT_FUNC_IMPORTS(double, array, d);
1135 EMIT_FUNC_IMPORTS(const char*, array, p);
1136 EMIT_FUNC_IMPORTS(int8_t, rmi, i1);
1137 EMIT_FUNC_IMPORTS(int16_t, rmi, i2);
1138 EMIT_FUNC_IMPORTS(int32_t, rmi, i4);
1139 EMIT_FUNC_IMPORTS(int64_t, rmi, i8);
1140 EMIT_FUNC_IMPORTS(float, rmi, f);
1141 EMIT_FUNC_IMPORTS(double, rmi, d);
1142#undef EMIT_FUNC_IMPORTS
1143
1144#define ADD_FUNC(FUNC, NAME) { \
1145 auto func = v8::Function::New(Ctx, (FUNC)).ToLocalChecked(); \
1146 env->Set(Ctx, mkstr(isolate, NAME), func).Check(); \
1147}
1148#define ADD_FUNC_(FUNC) ADD_FUNC(FUNC, #FUNC)
1153 ADD_FUNC(_throw, "throw")
1154
1155#define ADD_FUNCS(IDXTYPE, KEYTYPE, V8TYPE, IDXNAME, SUFFIX) \
1156 ADD_FUNC(index_seek<M_COMMA(IDXTYPE<KEYTYPE>) M_COMMA(V8TYPE) true>, M_STR(idx_lower_bound_##IDXNAME##_##SUFFIX)) \
1157 ADD_FUNC(index_seek<M_COMMA(IDXTYPE<KEYTYPE>) M_COMMA(V8TYPE) false>, M_STR(idx_upper_bound_##IDXNAME##_##SUFFIX)) \
1158 ADD_FUNC(index_sequential_scan<IDXTYPE<KEYTYPE>>, M_STR(idx_scan_##IDXNAME##_##SUFFIX))
1159
1160 ADD_FUNCS(idx::ArrayIndex, bool, v8::Boolean, array, b);
1161 ADD_FUNCS(idx::ArrayIndex, int8_t, v8::Int32, array, i1);
1162 ADD_FUNCS(idx::ArrayIndex, int16_t, v8::Int32, array, i2);
1163 ADD_FUNCS(idx::ArrayIndex, int32_t, v8::Int32, array, i4);
1164 ADD_FUNCS(idx::ArrayIndex, int64_t, v8::BigInt, array, i8);
1165 ADD_FUNCS(idx::ArrayIndex, float, v8::Number, array, f);
1166 ADD_FUNCS(idx::ArrayIndex, double, v8::Number, array, d);
1167 ADD_FUNCS(idx::ArrayIndex, const char*, v8::String, array, p);
1168 ADD_FUNCS(idx::RecursiveModelIndex, int8_t, v8::Int32, rmi, i1);
1169 ADD_FUNCS(idx::RecursiveModelIndex, int16_t, v8::Int32, rmi, i2);
1170 ADD_FUNCS(idx::RecursiveModelIndex, int32_t, v8::Int32, rmi, i4);
1171 ADD_FUNCS(idx::RecursiveModelIndex, int64_t, v8::BigInt, rmi, i8);
1172 ADD_FUNCS(idx::RecursiveModelIndex, float, v8::Number, rmi, f);
1173 ADD_FUNCS(idx::RecursiveModelIndex, double, v8::Number, rmi, d);
1174#undef ADD_FUNCS
1175#undef ADD_FUNC_
1176#undef ADD_FUNC
1177
1178 return env;
1179}
1180
1181v8::Local<v8::String> m::wasm::detail::to_json(v8::Isolate &isolate, v8::Local<v8::Value> val)
1182{
1183 M_insist(&isolate);
1184 auto Ctx = isolate.GetCurrentContext();
1185 return v8::JSON::Stringify(Ctx, val).ToLocalChecked();
1186}
1187
1188std::string m::wasm::detail::create_js_debug_script(v8::Isolate &isolate, v8::Local<v8::Object> env,
1189 const WasmEngine::WasmContext &wasm_context)
1190{
1191 std::ostringstream oss;
1192
1193 auto [binary_addr, binary_size] = Module::Get().binary();
1194
1195 auto json = to_json(isolate, env);
1196 std::string env_str = *v8::String::Utf8Value(&isolate, json);
1197 if (env_str != "{}") env_str.insert(env_str.length() - 1, ",");
1198 env_str.insert(env_str.length() - 1, "\"insist\": function (arg) { assert(arg); },");
1199 env_str.insert(env_str.length() - 1, "\"print\": function (arg) { console.log(arg); },");
1200 env_str.insert(env_str.length() - 1, "\"throw\": function (ex) { console.error(ex); },");
1201 env_str.insert(env_str.length() - 1, "\"read_result_set\": read_result_set,");
1202
1203 /* Construct import object. */
1204 oss << "\
1205let importObject = { \"imports\": " << env_str << " };\n\
1206const bytes = Uint8Array.from([";
1207 for (auto p = binary_addr, end = p + binary_size; p != end; ++p) {
1208 if (p != binary_addr) oss << ", ";
1209 oss << unsigned(*p);
1210 }
1211 free(binary_addr);
1212 /* Emit code to instantiate module and invoke exported `run()` function. */
1213 oss << "]);\n\
1214WebAssembly.compile(bytes).then(\n\
1215 (module) => WebAssembly.instantiate(module, importObject),\n\
1216 (error) => console.error(`An error occurred during module compilation: ${error}`)\n\
1217).then(\n\
1218 function(instance) {\n\
1219 set_wasm_instance_raw_memory(instance, " << wasm_context.id << ");\n\
1220 const num_tuples = instance.exports.main();\n\
1221 console.log('The result set contains %i tuples.', num_tuples);\n\
1222 debugger;\n\
1223 },\n\
1224 (error) => console.error(`An error occurred during module instantiation: ${error}`)\n\
1225);\n\
1226debugger;";
1227
1228 /* Create a new temporary file. */
1229 const char *name = "query.js";
1230 std::ofstream file(name, std::ios_base::out);
1231 if (not file)
1232 throw runtime_error("I/O error");
1233 std::cerr << "Creating debug JS script " << name << std::endl;
1234
1235 /* Write the JS code to instantiate the module and invoke `run()` to the temporary file. */
1236 file << oss.str();
1237 if (not file)
1238 throw runtime_error("I/O error");
1239 file.flush();
1240 if (not file)
1241 throw runtime_error("I/O error");
1242
1243 /* Return the name of the temporary file. */
1244 return std::string(name);
1245}
1246
1247void m::wasm::detail::run_inspector(V8InspectorClientImpl &inspector, v8::Isolate &isolate, v8::Local<v8::Object> env)
1248{
1249 auto Ctx = isolate.GetCurrentContext();
1250 auto &wasm_context = WasmEngine::Get_Wasm_Context_By_ID(Module::ID());
1251
1252 inspector.register_context(Ctx);
1253 inspector.start([&]() {
1254 /* Create JS script file that instantiates the Wasm module and invokes `main()`. */
1255 auto filename = create_js_debug_script(isolate, env, wasm_context);
1256 /* Create a `v8::Script` for that JS file. */
1257 std::ifstream js_in(filename);
1258 std::string js(std::istreambuf_iterator<char>(js_in), std::istreambuf_iterator<char>{});
1259 v8::Local<v8::String> js_src = mkstr(isolate, js);
1260 std::string path = std::string("file://./") + filename;
1261 v8::ScriptOrigin js_origin = v8::ScriptOrigin(&isolate, mkstr(isolate, path));
1262 auto script = v8::Script::Compile(Ctx, js_src, &js_origin);
1263 if (script.IsEmpty())
1264 throw std::runtime_error("failed to compile script");
1265 /* Execute the `v8::Script`. */
1266 auto result = script.ToLocalChecked()->Run(Ctx);
1267 if (result.IsEmpty())
1268 throw std::runtime_error("execution failed");
1269 });
1270 inspector.deregister_context(Ctx);
1271}
__attribute__((constructor(202))) static void register_interpreter()
#define M_TIME_EXPR(EXPR, DESCR, TIMER)
Definition: Timer.hpp:177
#define EMIT_FUNC_IMPORTS(KEYTYPE, IDXNAME, SUFFIX)
#define ADD_FUNCS(IDXTYPE, KEYTYPE, V8TYPE, IDXNAME, SUFFIX)
#define CREATE_TEMPLATES(IDXTYPE, KEYTYPE, V8TYPE, IDXNAME, SUFFIX)
#define ADD_FUNC(FUNC, NAME)
#define ADD_FUNC_(FUNC)
#define FUNCTION(NAME, TYPE)
Definition: WasmMacro.hpp:17
#define BLOCK_OPEN(BLK)
Definition: WasmMacro.hpp:8
int main(void)
struct @5 args
void add(const char *group_name, const char *short_name, const char *long_name, const char *description, Callback &&callback)
Adds a new group option to the ArgParser.
Definition: ArgParser.hpp:84
#define M_unreachable(MSG)
Definition: macro.hpp:146
#define M_CONSTEXPR_COND(COND, IF_TRUE, IF_FALSE)
Definition: macro.hpp:54
#define M_notnull(ARG)
Definition: macro.hpp:182
#define M_DISCARD
Definition: macro.hpp:213
#define M_insist(...)
Definition: macro.hpp:129
void insist(const v8::FunctionCallbackInfo< v8::Value > &info)
Definition: V8Engine.cpp:233
void _throw(const v8::FunctionCallbackInfo< v8::Value > &info)
Definition: V8Engine.cpp:248
v8::Local< v8::Object > create_env(v8::Isolate &isolate, const m::MatchBase &plan)
Definition: V8Engine.cpp:1075
v8::Local< v8::WasmModuleObject > instantiate(v8::Isolate &isolate, v8::Local< v8::Object > imports)
Definition: V8Engine.cpp:1046
std::string create_js_debug_script(v8::Isolate &isolate, v8::Local< v8::Object > env, const WasmEngine::WasmContext &wasm_context)
Definition: V8Engine.cpp:1188
void print_memory_consumption(const v8::FunctionCallbackInfo< v8::Value > &info)
Definition: V8Engine.cpp:281
void set_wasm_instance_raw_memory(const v8::FunctionCallbackInfo< v8::Value > &info)
Definition: V8Engine.cpp:292
v8::Local< v8::String > mkstr(v8::Isolate &isolate, const std::string &str)
Definition: V8Engine.cpp:1041
void index_sequential_scan(const v8::FunctionCallbackInfo< v8::Value > &info)
Definition: V8Engine.cpp:565
void run_inspector(V8InspectorClientImpl &inspector, v8::Isolate &isolate, v8::Local< v8::Object > env)
Definition: V8Engine.cpp:1247
void print(const v8::FunctionCallbackInfo< v8::Value > &info)
Definition: V8Engine.cpp:264
void index_seek(const v8::FunctionCallbackInfo< v8::Value > &info)
Definition: V8Engine.cpp:535
v8::Local< v8::String > to_json(v8::Isolate &isolate, v8::Local< v8::Value > val)
Definition: V8Engine.cpp:1181
void read_result_set(const v8::FunctionCallbackInfo< v8::Value > &info)
Definition: V8Engine.cpp:306
and
Constructs a new PrimitiveExpr from a constant value.
Definition: WasmDSL.hpp:1519
Bool< L > value
Definition: WasmUtil.hpp:1317
Bool< L > uint8_t n
Definition: WasmUtil.hpp:1318
auto op
Definition: WasmDSL.hpp:2384
std::size_t bool
Definition: WasmDSL.hpp:528
PrimitiveExpr< uint64_t, L > hash() and(L
‍mutable namespace
Definition: Backend.hpp:10
bool M_EXPORT equal(const T &first, const U &second)
Checks whether first and second are equal considering permutations.
Definition: fn.hpp:391
std::size_t Is_Page_Aligned(std::size_t n)
Returns true iff n is a integral multiple of the page size (in bytes).
Definition: fn.hpp:700
std::function< void(void)> pipeline_t
std::size_t Ceil_To_Next_Page(std::size_t n)
Returns the smallest integral multiple of the page size (in bytes) greater than or equals to n.
Definition: fn.hpp:703
auto visit(Callable &&callable, Base &obj, m::tag< Callable > &&=m::tag< Callable >())
Generic implementation to visit a class hierarchy, with similar syntax as std::visit.
Definition: Visitor.hpp:138
‍command-line options for the HeuristicSearchPlanEnumerator
Definition: V8Engine.cpp:44
STL namespace.
The boolean type.
Definition: Type.hpp:230
The catalog contains all Databases and keeps track of all meta information of the database system.
Definition: Catalog.hpp:215
void register_wasm_backend(ThreadSafePooledString name, const char *description=nullptr)
Registers a new WasmBackend using the given WasmEngine with the given name.
Definition: Catalog.hpp:465
memory::Allocator & allocator()
Returns a reference to the memory::Allocator.
Definition: Catalog.hpp:269
ThreadSafePooledString pool(const char *str) const
Creates an internalized copy of the string str by adding it to the internal StringPool.
Definition: Catalog.hpp:274
static Catalog & Get()
Return a reference to the single Catalog instance.
Timer & timer()
Returns the global Timer instance.
Definition: Catalog.hpp:264
m::ArgParser & arg_parser()
Definition: Catalog.hpp:253
The type of character strings, both fixed length and varying length.
Definition: Type.hpp:290
A Consumer is an Operator that can be evaluated on a sequence of tuples.
Definition: Operator.hpp:133
const std::vector< Producer * > & children() const
Returns a reference to the children of this Consumer.
Definition: Operator.hpp:173
The date type.
Definition: Type.hpp:364
The date type.
Definition: Type.hpp:335
static StackMachine compile_load(const Schema &tuple_schema, void *address, const storage::DataLayout &layout, const Schema &layout_schema, std::size_t row_id=0, std::size_t tuple_id=0)
Compile a StackMachine to load a tuple of Schema tuple_schema using a given memory address and a give...
virtual const Operator & get_matched_root() const =0
Returns the matched logical root operator for physical operators.
virtual void execute(setup_t setup, pipeline_t pipeline, teardown_t teardown) const =0
Executes this physical operator match.
Drops the produced results and outputs only the number of result tuples produced.
Definition: Operator.hpp:238
A Type that represents the absence of any other type.
Definition: Type.hpp:204
The numeric type represents integer and floating-point types of different precision and scale.
Definition: Type.hpp:393
An Operator represents an operation in a query plan.
Definition: Operator.hpp:45
static Options & Get()
Return a reference to the single Options instance.
Definition: Options.cpp:9
Prints the produced Tuples to a std::ostream instance.
Definition: Operator.hpp:223
Computes the FNV-1a 64-bit hash of a cstring.
Definition: fn.hpp:71
TimingProcess create_timing(std::string name)
Creates a new TimingProcess with the given name.
Definition: Timer.hpp:152
void clear()
Sets all Values of this Tuple to NULL.
Definition: Tuple.hpp:231
void set(std::size_t idx, Value val)
Assigns the Value val to this Tuple at index idx and clears the respective NULL bit.
Definition: Tuple.hpp:240
This class represents types in the SQL type system.
Definition: Type.hpp:46
This class holds a SQL attribute value.
Definition: Tuple.hpp:19
A WasmContext holds associated information of a WebAssembly module instance.
Definition: WebAssembly.hpp:27
unsigned id
a unique ID
Definition: WebAssembly.hpp:37
A WasmEngine provides an environment to compile and execute WebAssembly modules.
Definition: WebAssembly.hpp:17
virtual void execute(const MatchBase &plan)=0
Executes the already computed physical covering represented by plan using this WasmEngine.
static WasmContext & Get_Wasm_Context_By_ID(unsigned id)
Returns a reference to the WasmContext with ID id.
virtual void compile(const MatchBase &plan) const =0
Compiles the already computed physical covering represented by plan using this WasmEngine.
void send(std::string_view msg)
A binary expression.
Definition: AST.hpp:348
std::unique_ptr< Expr > lhs
Definition: AST.hpp:349
std::unique_ptr< Expr > rhs
Definition: AST.hpp:350
A constant: a string literal or a numeric constant.
Definition: AST.hpp:213
bool is_string() const
Definition: AST.hpp:233
A designator.
Definition: AST.hpp:134
The error expression.
Definition: AST.hpp:116
A function application.
Definition: AST.hpp:246
A query expression for nested queries.
Definition: AST.hpp:389
A unary expression: "+e", "-e", "~e", "NOT e".
Definition: AST.hpp:324
std::unique_ptr< Expr > expr
Definition: AST.hpp:325
A CNF represents a conjunction of cnf::Clauses.
Definition: CNF.hpp:134
A simple index based on a sorted array that maps keys to their tuple_id.
Definition: Index.hpp:53
A recursive model index with two layers consiting only of linear monels that maps keys to their tuple...
Definition: Index.hpp:137
virtual Memory allocate(std::size_t size)=0
Creates a new memory object with size bytes of freshly allocated memory.
Represents a mapping created by a memory::Allocator.
Definition: memory.hpp:78
void map(std::size_t size, std::size_t offset_src, const AddressSpace &vm, std::size_t offset_dst) const
Map size bytes starting at offset_src into the address space of vm at offset offset_dst.
Definition: memory.cpp:85
Signals a runtime error that mu*t*able is not responsible for and that mu*t*able was not able to reco...
Definition: exception.hpp:49
static setup_t Make_Without_Parent(base_t &&callback=base_t())
static teardown_t Make_Without_Parent(base_t &&callback=base_t())
U32x1 num_tuples() const
Returns the number of result tuples produced.
Definition: WasmUtil.hpp:910
static void Dispose()
Definition: WasmUtil.hpp:885
void add_literal(const char *literal, uint32_t ptr)
Adds the string literal literal located at pointer offset ptr.
Definition: WasmUtil.hpp:917
static void Init()
Definition: WasmUtil.hpp:881
static CodeGenContext & Get()
Definition: WasmUtil.hpp:889
Scope scoped_environment()
Creates a new, scoped Environment.
Definition: WasmUtil.hpp:897
Represents a Wasm function.
Definition: WasmDSL.hpp:1139
void dump_all(std::ostream &out)
Definition: WasmDSL.hpp:993
static void Dispose()
Definition: WasmDSL.hpp:710
void emit_import(const char *extern_name, const char *intern_name=nullptr)
Definition: WasmDSL.hpp:865
static Module & Get()
Definition: WasmDSL.hpp:714
static void Init()
Definition: WasmDSL.hpp:706
void emit_function_import(const char *name)
Definition: WasmDSL.hpp:879
static bool Validate(bool verbose=true, bool global=true)
Validates that the module is well-formed.
Definition: WasmDSL.cpp:367
void dump(std::ostream &out) const
Definition: WasmDSL.hpp:990
friend struct Allocator
Definition: WasmDSL.hpp:652
static unsigned ID()
Returns the ID of the current module.
Definition: WasmDSL.hpp:721
void emit_function_export(const char *name)
Add function name as export.
Definition: WasmDSL.hpp:886
static void Optimize(int optimization_level)
Optimizes the module with the optimization level set to level.
Definition: WasmDSL.cpp:375
void emit_call(const char *fn, PrimitiveExpr< ParamTypes, ParamLs >... args)
Definition: WasmDSL.hpp:6716
const std::tuple< const char *, unsigned, const char * > & get_message(std::size_t idx) const
Definition: WasmDSL.hpp:907
std::pair< uint8_t *, std::size_t > binary()
Returns the binary representation of module_ in a freshly allocated memory.
Definition: WasmDSL.cpp:385
std::unique_ptr< WebSocketServer::Connection > conn_
Definition: V8Engine.hpp:40
std::function< void(void)> code_
the code to execute in the debugger
Definition: V8Engine.hpp:44
void runMessageLoopOnPause(int) override
Synchronously consume all front end (CDT) debugging messages.
Definition: V8Engine.cpp:213
void register_context(v8::Local< v8::Context > context)
Register the context object in the V8Inspector instance.
Definition: V8Engine.cpp:178
std::unique_ptr< WebSocketChannel > channel_
Definition: V8Engine.hpp:41
std::unique_ptr< v8_inspector::V8InspectorSession > session_
Definition: V8Engine.hpp:43
V8InspectorClientImpl(int16_t port, v8::Isolate *isolate)
Definition: V8Engine.cpp:154
void on_message(std::string_view sv)
Definition: V8Engine.cpp:189
void start(std::function< void(void)> code)
Definition: V8Engine.hpp:58
void deregister_context(v8::Local< v8::Context > context)
Deregister the context object in the V8Inspector instance.
Definition: V8Engine.cpp:184
std::unique_ptr< v8_inspector::V8Inspector > inspector_
Definition: V8Engine.hpp:42
void sendResponse(int, std::unique_ptr< v8_inspector::StringBuffer > message) override
Definition: V8Engine.cpp:140
void sendNotification(std::unique_ptr< v8_inspector::StringBuffer > message) override
Definition: V8Engine.cpp:147
WebSocketServer::Connection & conn_
Definition: V8Engine.hpp:19
static constexpr const char *const names_[]
Definition: WasmDSL.hpp:562