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#ifdef NDEBUG
262 /* Print message of thrown exception in release build as it is not printed by default if the exception is never
263 * catched (which seems to be impossible since the `_throw()` callback itself cannot be enclosed by a
264 * try-catch-block at host side). */
265 std::cerr << oss.str() << std::endl;
266#endif
267
268 throw m::wasm::exception(type, oss.str());
269}
270
271void m::wasm::detail::print(const v8::FunctionCallbackInfo<v8::Value> &info)
272{
273#ifndef NDEBUG
274 std::cout << "v8 function callback: ";
275#endif
276 for (int i = 0; i != info.Length(); ++i) {
277 v8::HandleScope handle_scope(info.GetIsolate());
278 if (i != 0) std::cout << ',';
279 v8::Local<v8::Value> v = info[i];
280 if (v->IsInt32())
281 std::cout << "0x" << std::hex << uint32_t(v.As<v8::Int32>()->Value()) << std::dec;
282 else
283 std::cout << *v8::String::Utf8Value(info.GetIsolate(), v);
284 }
285 std::cout << std::endl;
286}
287
288void m::wasm::detail::print_memory_consumption(const v8::FunctionCallbackInfo<v8::Value> &info)
289{
290 M_insist(Options::Get().statistics);
291
292 auto alloc_total_mem = info[0].As<v8::Uint32>()->Value();
293 auto alloc_peak_mem = info[1].As<v8::Uint32>()->Value();
294
295 std::cout << "Allocated memory overall consumption: " << alloc_total_mem / (1024.0 * 1024.0) << " MiB"<< std::endl;
296 std::cout << "Allocated memory peak consumption: " << alloc_peak_mem / (1024.0 * 1024.0) << " MiB"<< std::endl;
297}
298
299void m::wasm::detail::set_wasm_instance_raw_memory(const v8::FunctionCallbackInfo<v8::Value> &info)
300{
301 v8::Local<v8::WasmModuleObject> wasm_instance = info[0].As<v8::WasmModuleObject>();
302 v8::Local<v8::Int32> wasm_context_id = info[1].As<v8::Int32>();
303
304 auto &wasm_context = WasmEngine::Get_Wasm_Context_By_ID(wasm_context_id->Value());
305#ifndef NDEBUG
306 std::cerr << "Setting Wasm instance raw memory of the given instance to the VM of Wasm context "
307 << wasm_context_id->Value() << " at " << wasm_context.vm.addr() << " of " << wasm_context.vm.size()
308 << " bytes" << std::endl;
309#endif
310 v8::SetWasmInstanceRawMemory(wasm_instance, wasm_context.vm.as<uint8_t*>(), wasm_context.vm.size());
311}
312
313void m::wasm::detail::read_result_set(const v8::FunctionCallbackInfo<v8::Value> &info)
314{
316
317 auto &root_op = context.plan.get_matched_root();
318 auto &schema = root_op.schema();
319 auto deduplicated_schema = schema.deduplicate();
320 auto deduplicated_schema_without_constants = deduplicated_schema.drop_constants();
321
322 /* Get number of result tuples. */
323 auto num_tuples = info[1].As<v8::Uint32>()->Value();
324 if (num_tuples == 0)
325 return;
326
327 /* Compute address of result set. */
328 M_insist(info.Length() == 2);
329 auto result_set_offset = info[0].As<v8::Uint32>()->Value();
330 M_insist((result_set_offset == 0) == (deduplicated_schema_without_constants.num_entries() == 0),
331 "result set offset equals 0 (i.e. nullptr) iff schema contains only constants");
332 auto result_set = context.vm.as<uint8_t*>() + result_set_offset;
333
334 /* Find the projection nearest to the plan's root since it will determine the constants omitted in the result set. */
335 auto find_projection = [](const Operator &op) -> const ProjectionOperator * {
336 auto find_projection_impl = [](const Operator &op, auto &find_projection_ref) -> const ProjectionOperator * {
337 if (auto projection_op = cast<const ProjectionOperator>(&op)) {
338 return projection_op;
339 } else if (auto c = cast<const Consumer>(&op)) {
340 M_insist(c->children().size() == 1,
341 "at least one projection without siblings in the operator tree must be contained");
342 M_insist(c->schema().num_entries() == c->child(0)->schema().num_entries(),
343 "at least one projection with the same schema as the plan's root must be contained");
344#ifndef NDEBUG
345 for (std::size_t i = 0; i < c->schema().num_entries(); ++i)
346 M_insist(c->schema()[i].id == c->child(0)->schema()[i].id,
347 "at least one projection with the same schema as the plan's root must be contained");
348#endif
349 return find_projection_ref(*c->child(0), find_projection_ref);
350 } else {
351 return nullptr; // no projection found
352 }
353 };
354 return find_projection_impl(op, find_projection_impl);
355 };
356 auto projection = find_projection(root_op);
357
359 auto print_constant = [](std::ostringstream &out, const ast::Constant &c, const Type *type){
360 if (type->is_none()) {
361 out << "NULL";
362 return;
363 }
364
365 /* Interpret constant. */
366 auto value = Interpreter::eval(c);
367
369 [&](const Boolean&) { out << (value.as_b() ? "TRUE" : "FALSE"); },
370 [&](const Numeric &n) {
371 switch (n.kind) {
372 case Numeric::N_Int:
373 case Numeric::N_Decimal:
374 out << value.as_i();
375 break;
376 case Numeric::N_Float:
377 if (n.size() <= 32) {
378 const auto old_precision = out.precision(std::numeric_limits<float>::max_digits10 - 1);
379 out << value.as_f();
380 out.precision(old_precision);
381 } else {
382 const auto old_precision = out.precision(std::numeric_limits<double>::max_digits10 - 1);
383 out << value.as_d();
384 out.precision(old_precision);
385 }
386 }
387 },
388 [&](const CharacterSequence&) { out << '"' << reinterpret_cast<char*>(value.as_p()) << '"'; },
389 [&](const Date&) {
390 const int32_t date = value.as_i(); // signed because year is signed
391 const auto oldfill = out.fill('0');
392 const auto oldfmt = out.flags();
393 out << std::internal
394 << std::setw(date >> 9 > 0 ? 4 : 5) << (date >> 9) << '-'
395 << std::setw(2) << ((date >> 5) & 0xF) << '-'
396 << std::setw(2) << (date & 0x1F);
397 out.fill(oldfill);
398 out.flags(oldfmt);
399 },
400 [&](const DateTime&) {
401 const time_t time = value.as_i();
402 std::tm tm;
403 gmtime_r(&time, &tm);
404 out << put_tm(tm);
405 },
406 [](const NoneType&) { M_unreachable("should've been handled earlier"); },
407 [](auto&&) { M_unreachable("invalid type"); },
408 }, *type);
409 };
410
411 if (deduplicated_schema_without_constants.num_entries() == 0) {
412 /* Schema contains only constants. Create simple loop to generate `num_tuples` constant result tuples. */
413 M_insist(bool(projection), "projection must be found");
414 auto &projections = projection->projections();
415 if (auto callback_op = cast<const CallbackOperator>(&root_op)) {
416 Tuple tup(schema); // tuple entries which are not set are implicitly NULL
417 for (std::size_t i = 0; i < schema.num_entries(); ++i) {
418 auto &e = schema[i];
419 if (e.type->is_none()) continue; // NULL constant
420 M_insist(e.id.is_constant());
421 tup.set(i, Interpreter::eval(as<const ast::Constant>(projections[i].first)));
422 }
423 for (std::size_t i = 0; i < num_tuples; ++i)
424 callback_op->callback()(schema, tup);
425 } else if (auto print_op = cast<const PrintOperator>(&root_op)) {
426 std::ostringstream tup;
427 for (std::size_t i = 0; i < schema.num_entries(); ++i) {
428 auto &e = schema[i];
429 if (i)
430 tup << ',';
431 M_insist(e.id.is_constant());
432 print_constant(tup, as<const ast::Constant>(projections[i].first), e.type);
433 }
434 for (std::size_t i = 0; i < num_tuples; ++i)
435 print_op->out << tup.str() << '\n';
436 }
437 return;
438 }
439
440 /* Create data layout (without constants and duplicates). */
441 M_insist(bool(context.result_set_factory), "result set factory must be set");
442 auto layout = context.result_set_factory->make(deduplicated_schema_without_constants);
443
444 /* Extract results. */
445 if (auto callback_op = cast<const CallbackOperator>(&root_op)) {
446 auto loader = Interpreter::compile_load(deduplicated_schema_without_constants, result_set, layout,
447 deduplicated_schema_without_constants);
448 if (schema.num_entries() == deduplicated_schema.num_entries()) {
449 /* No deduplication was performed. Compute `Tuple` with constants. */
450 M_insist(schema == deduplicated_schema);
451 Tuple tup(schema); // tuple entries which are not set are implicitly NULL
452 for (std::size_t i = 0; i < schema.num_entries(); ++i) {
453 auto &e = schema[i];
454 if (e.type->is_none()) continue; // NULL constant
455 if (e.id.is_constant()) { // other constant
456 M_insist(bool(projection), "projection must be found");
457 tup.set(i, Interpreter::eval(as<const ast::Constant>(projection->projections()[i].first)));
458 }
459 }
460 Tuple *args[] = { &tup };
461 for (std::size_t i = 0; i != num_tuples; ++i) {
462 loader(args);
463 callback_op->callback()(schema, tup);
464 tup.clear();
465 }
466 } else {
467 /* Deduplication was performed. Compute a `Tuple` with duplicates and constants. */
468 Tuple tup_dedupl(deduplicated_schema_without_constants);
469 Tuple tup_dupl(schema); // tuple entries which are not set are implicitly NULL
470 for (std::size_t i = 0; i < schema.num_entries(); ++i) {
471 auto &e = schema[i];
472 if (e.type->is_none()) continue; // NULL constant
473 if (e.id.is_constant()) { // other constant
474 M_insist(bool(projection), "projection must be found");
475 tup_dupl.set(i, Interpreter::eval(as<const ast::Constant>(projection->projections()[i].first)));
476 }
477 }
478 Tuple *args[] = { &tup_dedupl, &tup_dupl };
479 for (std::size_t i = 0; i != deduplicated_schema_without_constants.num_entries(); ++i) {
480 auto &entry = deduplicated_schema_without_constants[i];
481 if (not entry.type->is_none())
482 loader.emit_Ld_Tup(0, i);
483 for (std::size_t j = 0; j != schema.num_entries(); ++j) {
484 auto &e = schema[j];
485 if (e.id == entry.id) {
486 M_insist(e.type == entry.type);
487 loader.emit_St_Tup(1, j, e.type);
488 }
489 }
490 if (not entry.type->is_none())
491 loader.emit_Pop();
492 }
493 for (std::size_t i = 0; i != num_tuples; ++i) {
494 loader(args);
495 callback_op->callback()(schema, tup_dupl);
496 }
497 }
498 } else if (auto print_op = cast<const PrintOperator>(&root_op)) {
499 /* Compute a `Tuple` with duplicates and constants. */
500 Tuple tup(deduplicated_schema_without_constants);
501 Tuple *args[] = { &tup };
502 auto printer = Interpreter::compile_load(deduplicated_schema_without_constants, result_set, layout,
503 deduplicated_schema_without_constants);
504 auto ostream_index = printer.add(&print_op->out);
505 bool constant_emitted = false;
506 std::size_t old_idx = -1UL;
507 for (std::size_t i = 0; i != schema.num_entries(); ++i) {
508 if (i != 0)
509 printer.emit_Putc(ostream_index, ',');
510 auto &e = schema[i];
511 if (not e.type->is_none()) {
512 if (e.id.is_constant()) { // constant except NULL
513 M_insist(bool(projection), "projection must be found");
514 printer.add_and_emit_load(Interpreter::eval(as<const ast::Constant>(projection->projections()[i].first)));
515 constant_emitted = true;
516 } else { // actual value
517 auto idx = deduplicated_schema_without_constants[e.id].first;
518 if (idx != old_idx) {
519 if (old_idx != -1UL)
520 printer.emit_Pop(); // to remove last loaded value
521 printer.emit_Ld_Tup(0, idx);
522 old_idx = idx;
523 }
524 }
525 }
526 printer.emit_Print(ostream_index, e.type);
527 if (e.type->is_none() or constant_emitted) {
528 printer.emit_Pop(); // to remove NULL pushed by `emit_Print()` or other constant pushed above
529 constant_emitted = false;
530 }
531 }
532 if (old_idx != -1UL)
533 printer.emit_Pop(); // to remove last loaded value
534 for (std::size_t i = 0; i != num_tuples; ++i) {
535 printer(args);
536 print_op->out << '\n';
537 }
538 }
539}
540
541template<typename Index, typename V8ValueT, bool IsLower>
542void m::wasm::detail::index_seek(const v8::FunctionCallbackInfo<v8::Value> &info)
543{
544 using key_type = Index::key_type;
545
546 /*----- Unpack function parameters -----*/
547 auto index_id = info[0].As<v8::BigInt>()->Uint64Value();
548 key_type key;
549 if constexpr (std::same_as<V8ValueT, v8::BigInt>)
550 key = info[1].As<V8ValueT>()->Int64Value();
551 else if constexpr (std::same_as<V8ValueT, v8::String>) {
552 auto offset = info[1].As<v8::Uint32>()->Value();
554 key = reinterpret_cast<const char*>(context.vm.as<uint8_t*>() + offset);
555 } else
556 key = info[1].As<V8ValueT>()->Value();
557
558 /*----- Obtain index and cast to correct type. -----*/
560 auto &index = as<const Index>(context.indexes[index_id]);
561
562 /*----- Seek index and return offset. -----*/
563 std::size_t offset = std::distance(
564 index.begin(),
565 M_CONSTEXPR_COND(IsLower, index.lower_bound(key), index.upper_bound(key))
566 );
567 M_insist(std::in_range<uint32_t>(offset), "should fit in uint32_t");
568 info.GetReturnValue().Set(uint32_t(offset));
569}
570
571template<typename Index>
572void m::wasm::detail::index_sequential_scan(const v8::FunctionCallbackInfo<v8::Value> &info)
573{
574 /*----- Unpack function parameters -----*/
575 auto index_id = info[0].As<v8::BigInt>()->Uint64Value();
576 auto entry_offset = info[1].As<v8::Uint32>()->Value();
577 auto address_offset = info[2].As<v8::Uint32>()->Value();
578 auto batch_size = info[3].As<v8::Uint32>()->Value();
579
580 /*----- Compute adress to write results to. -----*/
582 auto buffer_address = reinterpret_cast<uint32_t*>(context.vm.as<uint8_t*>() + address_offset);
583
584 /*----- Obtain index and cast to correct type. -----*/
585 auto &index = as<const Index>(context.indexes[index_id]);
586
587 /*----- Scan index and write result tuple ids to buffer -----*/
588 auto it = index.begin() + entry_offset;
589 for (uint32_t i = 0; i < batch_size; ++i, ++it)
590 buffer_address[i] = it->second;
591}
592
593
594/*======================================================================================================================
595 * V8Engine helper classes
596 *====================================================================================================================*/
597
598namespace {
599
600struct CollectStringLiterals : ConstOperatorVisitor, ast::ConstASTExprVisitor
601{
602 private:
603 std::unordered_set<const char*> literals_;
604
605 public:
606 static std::vector<const char*> Collect(const Operator &plan) {
607 CollectStringLiterals CSL;
608 CSL(plan);
609 return { CSL.literals_.begin(), CSL.literals_.end() };
610 }
611
612 private:
613 CollectStringLiterals() = default;
614
615 using ConstOperatorVisitor::operator();
616 using ConstASTExprVisitor::operator();
617
618 void recurse(const Consumer &C) {
619 for (auto &c: C.children())
620 (*this)(*c);
621 }
622
623 /*----- Operator -------------------------------------------------------------------------------------------------*/
624 void operator()(const ScanOperator&) override { /* nothing to be done */ }
625 void operator()(const CallbackOperator &op) override { recurse(op); }
626 void operator()(const PrintOperator &op) override { recurse(op); }
627 void operator()(const NoOpOperator &op) override { recurse(op); }
628 void operator()(const FilterOperator &op) override {
629 (*this)(op.filter());
630 recurse(op);
631 }
632 void operator()(const DisjunctiveFilterOperator &op) override {
633 (*this)(op.filter());
634 recurse(op);
635 }
636 void operator()(const JoinOperator &op) override {
637 (*this)(op.predicate());
638 recurse(op);
639 }
640 void operator()(const ProjectionOperator &op) override {
641 for (auto &p : op.projections())
642 (*this)(p.first.get());
643 recurse(op);
644 }
645 void operator()(const LimitOperator &op) override { recurse(op); }
646 void operator()(const GroupingOperator &op) override {
647 for (auto &[grp, alias] : op.group_by())
648 (*this)(grp.get());
649 recurse(op);
650 }
651 void operator()(const AggregationOperator &op) override { recurse(op); }
652 void operator()(const SortingOperator &op) override { recurse(op); }
653
654 /*----- CNF ------------------------------------------------------------------------------------------------------*/
655 void operator()(const cnf::CNF &cnf) {
656 for (auto &clause: cnf) {
657 for (auto &pred: clause)
658 (*this)(*pred);
659 }
660 }
661
662 /*----- Expr -----------------------------------------------------------------------------------------------------*/
663 void operator()(const ast::ErrorExpr&) override { M_unreachable("no errors at this stage"); }
664 void operator()(const ast::Designator&) override { /* nothing to be done */ }
665 void operator()(const ast::Constant &e) override {
666 if (e.is_string()) {
667 auto s = Interpreter::eval(e);
668 literals_.emplace(s.as<const char*>());
669 }
670 }
671 void operator()(const ast::FnApplicationExpr&) override { /* nothing to be done */ } // XXX can string literals be arguments?
672 void operator()(const ast::UnaryExpr &e) override { (*this)(*e.expr); }
673 void operator()(const ast::BinaryExpr &e) override { (*this)(*e.lhs); (*this)(*e.rhs); }
674 void operator()(const ast::QueryExpr&) override { /* nothing to be done */ }
675};
676
677struct CollectTables : ConstOperatorVisitor
678{
679 private:
680 struct hash
681 {
682 uint64_t operator()(const std::reference_wrapper<const Table> &r) const {
683 StrHash h;
684 return h((const char*)(r.get().name()));
685 }
686 };
687
688 struct equal
689 {
690 bool operator()(const std::reference_wrapper<const Table> &first,
691 const std::reference_wrapper<const Table> &second) const
692 {
693 return first.get().name() == second.get().name();
694 }
695 };
696
697
698 std::unordered_set<std::reference_wrapper<const Table>, hash, equal> tables_;
699
700 public:
701 static std::vector<std::reference_wrapper<const Table>> Collect(const Operator &plan) {
702 CollectTables CT;
703 CT(plan);
704 return { CT.tables_.begin(), CT.tables_.end() };
705 }
706
707 private:
708 CollectTables() = default;
709
710 using ConstOperatorVisitor::operator();
711
712 void recurse(const Consumer &C) {
713 for (auto &c: C.children())
714 (*this)(*c);
715 }
716
717 void operator()(const ScanOperator &op) override { tables_.emplace(op.store().table()); }
718 void operator()(const CallbackOperator &op) override { recurse(op); }
719 void operator()(const PrintOperator &op) override { recurse(op); }
720 void operator()(const NoOpOperator &op) override { recurse(op); }
721 void operator()(const FilterOperator &op) override { recurse(op); }
722 void operator()(const DisjunctiveFilterOperator &op) override { recurse(op); }
723 void operator()(const JoinOperator &op) override { recurse(op); }
724 void operator()(const ProjectionOperator &op) override { recurse(op); }
725 void operator()(const LimitOperator &op) override { recurse(op); }
726 void operator()(const GroupingOperator &op) override { recurse(op); }
727 void operator()(const AggregationOperator &op) override { recurse(op); }
728 void operator()(const SortingOperator &op) override { recurse(op); }
729};
730
731
732/*======================================================================================================================
733 * V8Engine implementation
734 *====================================================================================================================*/
735
736V8Engine::V8Engine() { initialize(); }
737
738V8Engine::~V8Engine()
739{
740 inspector_.reset();
741 if (isolate_) {
742 M_insist(allocator_);
743 isolate_->Dispose();
744 delete allocator_;
745 }
746}
747
748void V8Engine::initialize()
749{
750 M_insist(not allocator_);
751 M_insist(not isolate_);
752
753 /*----- Set V8 flags. --------------------------------------------------------------------------------------------*/
754 /* A documentation of these flags can be found at
755 * https://chromium.googlesource.com/v8/v8/+/2c22fd50128ad130e9dba77fce828e5661559121/src/flags/flag-definitions.h.*/
756 std::ostringstream flags;
757 flags << "--stack_size 1000000 ";
758 if (options::wasm_adaptive) {
759 flags << "--opt "
760 << "--liftoff "
761 << "--wasm-tier-up "
762 << "--wasm-dynamic-tiering "
763 << "--wasm-lazy-compilation "; // compile code lazily at runtime if needed
764 } else {
765 flags << "--no-liftoff "
766 << "--no-wasm-lazy-compilation "; // compile code before starting execution
767 }
768 if (not options::wasm_compilation_cache) {
769 flags << "--no-compilation-cache "
770 << "--no-wasm-native-module-cache-enabled ";
771 }
772 if (options::asm_dump) {
773 flags << "--code-comments " // include code comments
774 << "--print-code ";
775 }
776 if (options::cdt_port >= 1024) {
777 flags << "--wasm-bounds-checks "
778 << "--wasm-stack-checks "
779 << "--log "
780 << "--log-all "
781 << "--expose-wasm "
782 << "--trace-wasm "
783 << "--trace-wasm-instances "
784 << "--prof ";
785 } else {
786 flags << "--no-wasm-bounds-checks "
787 << "--no-wasm-stack-checks "
788 << "--wasm-simd-ssse3-codegen ";
789 }
790 v8::V8::SetFlagsFromString(flags.str().c_str());
791
792 v8::Isolate::CreateParams create_params;
793 create_params.array_buffer_allocator = allocator_ = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
794 isolate_ = v8::Isolate::New(create_params);
795}
796
797void V8Engine::compile(const m::MatchBase &plan) const
798{
799#if 1
800 /*----- Add print function. --------------------------------------------------------------------------------------*/
801 Module::Get().emit_function_import<void(uint32_t)>("print");
802 Module::Get().emit_function_import<void(uint32_t, uint32_t)>("print_memory_consumption");
803#endif
804
805 /*----- Emit code for run function which computes the last pipeline and calls other pipeline functions. ----------*/
806 FUNCTION(run, void(void))
807 {
808 auto S = CodeGenContext::Get().scoped_environment(); // create scoped environment for this function
810 }
811
812 /*----- Create function `main` which executes the given query. ---------------------------------------------------*/
813 m::wasm::Function<uint32_t(uint32_t)> main("main");
814 BLOCK_OPEN(main.body())
815 {
816 auto S = CodeGenContext::Get().scoped_environment(); // create scoped environment for this function
817 run(); // call run function
818 if (Options::Get().statistics) {
819 std::cout << "Pre-allocated memory overall consumption: "
820 << Module::Allocator().pre_allocated_memory_consumption() / (1024.0 * 1024.0)
821 << " MiB" << std::endl;
822 Module::Get().emit_call<void>("print_memory_consumption",
823 Module::Allocator().allocated_memory_consumption(),
824 Module::Allocator().allocated_memory_peak());
825 }
826 main.emit_return(CodeGenContext::Get().num_tuples()); // return size of result set
827 }
828
829 /*----- Export main. ---------------------------------------------------------------------------------------------*/
831
832 /*----- Dump the generated WebAssembly code ----------------------------------------------------------------------*/
833 if (options::wasm_dump)
834 Module::Get().dump_all(std::cout);
835
836#ifndef NDEBUG
837 /*----- Validate module before optimization. ---------------------------------------------------------------------*/
838 if (not Module::Validate()) {
840 throw std::logic_error("invalid module");
841 }
842#endif
843
844 /*----- Optimize module. -----------------------------------------------------------------------------------------*/
845#ifndef NDEBUG
846 std::ostringstream dump_before_opt;
847 Module::Get().dump(dump_before_opt);
848#endif
849 if (options::wasm_optimization_level)
850 Module::Optimize(options::wasm_optimization_level);
851
852#ifndef NDEBUG
853 /*----- Validate module after optimization. ----------------------------------------------------------------------*/
854 if (options::wasm_optimization_level and not Module::Validate()) {
855 std::cerr << "Module invalid after optimization!" << std::endl;
856 std::cerr << "WebAssembly before optimization:\n" << dump_before_opt.str() << std::endl;
857 std::cerr << "WebAssembly after optimization:\n";
858 Module::Get().dump(std::cerr);
859 throw std::logic_error("invalid module");
860 }
861#endif
862}
863
864void V8Engine::execute(const m::MatchBase &plan)
865{
866 Catalog &C = Catalog::Get();
867
868 Module::Init();
869 CodeGenContext::Init(); // fresh context
870
871 M_insist(bool(isolate_), "must have an isolate");
872 v8::Locker locker(isolate_);
873 isolate_->Enter();
874
875 {
876 /* Create required V8 scopes. */
877 v8::Isolate::Scope isolate_scope(isolate_);
878 v8::HandleScope handle_scope(isolate_); // tracks and disposes of all object handles
879
880 /* Create global template and context. */
881 v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate_);
882 global->Set(isolate_, "set_wasm_instance_raw_memory", v8::FunctionTemplate::New(isolate_, set_wasm_instance_raw_memory));
883 global->Set(isolate_, "read_result_set", v8::FunctionTemplate::New(isolate_, read_result_set));
884
885#define CREATE_TEMPLATES(IDXTYPE, KEYTYPE, V8TYPE, IDXNAME, SUFFIX) \
886 global->Set(isolate_, M_STR(idx_lower_bound_##IDXNAME##_##SUFFIX), v8::FunctionTemplate::New(isolate_, index_seek<IDXTYPE<KEYTYPE>, V8TYPE, true>)); \
887 global->Set(isolate_, M_STR(idx_upper_bound_##IDXNAME##_##SUFFIX), v8::FunctionTemplate::New(isolate_, index_seek<IDXTYPE<KEYTYPE>, V8TYPE, false>)); \
888 global->Set(isolate_, M_STR(idx_scan_##IDXNAME##_##SUFFIX), v8::FunctionTemplate::New(isolate_, index_sequential_scan<IDXTYPE<KEYTYPE>>))
889
890 CREATE_TEMPLATES(idx::ArrayIndex, bool, v8::Boolean, array, b);
891 CREATE_TEMPLATES(idx::ArrayIndex, int8_t, v8::Int32, array, i1);
892 CREATE_TEMPLATES(idx::ArrayIndex, int16_t, v8::Int32, array, i2);
893 CREATE_TEMPLATES(idx::ArrayIndex, int32_t, v8::Int32, array, i4);
894 CREATE_TEMPLATES(idx::ArrayIndex, int64_t, v8::BigInt, array, i8);
895 CREATE_TEMPLATES(idx::ArrayIndex, float, v8::Number, array, f);
896 CREATE_TEMPLATES(idx::ArrayIndex, double, v8::Number, array, d);
897 CREATE_TEMPLATES(idx::ArrayIndex, const char*, v8::String, array, p);
898 CREATE_TEMPLATES(idx::RecursiveModelIndex, int8_t, v8::Int32, rmi, i1);
899 CREATE_TEMPLATES(idx::RecursiveModelIndex, int16_t, v8::Int32, rmi, i2);
900 CREATE_TEMPLATES(idx::RecursiveModelIndex, int32_t, v8::Int32, rmi, i4);
901 CREATE_TEMPLATES(idx::RecursiveModelIndex, int64_t, v8::BigInt, rmi, i8);
902 CREATE_TEMPLATES(idx::RecursiveModelIndex, float, v8::Number, rmi, f);
903 CREATE_TEMPLATES(idx::RecursiveModelIndex, double, v8::Number, rmi, d);
904#undef CREATE_TEMPLATES
905
906 v8::Local<v8::Context> context = v8::Context::New(isolate_, /* extensions= */ nullptr, global);
907 v8::Context::Scope context_scope(context);
908
909 /* Create the import object for instantiating the WebAssembly module. */
910 WasmContext::config_t wasm_config{0};
911 if (options::cdt_port < 1024)
912 wasm_config |= WasmContext::TRAP_GUARD_PAGES;
913 auto &wasm_context = Create_Wasm_Context_For_ID(Module::ID(), plan, wasm_config);
914
915 auto imports = v8::Object::New(isolate_);
916 auto env = create_env(*isolate_, plan);
917
918 /* Map the remaining address space to the output buffer. */
919 M_insist(Is_Page_Aligned(wasm_context.heap));
920 const auto bytes_remaining = wasm_context.vm.size() - wasm_context.heap;
921 memory::Memory mem = Catalog::Get().allocator().allocate(bytes_remaining);
922 mem.map(bytes_remaining, 0, wasm_context.vm, wasm_context.heap);
923
924 auto compile_time = C.timer().create_timing("Compile SQL to machine code");
925 /* Compile the plan and thereby build the Wasm module. */
926 M_TIME_EXPR(compile(plan), "|- Compile SQL to WebAssembly", C.timer());
927 /* Perform memory pre-allocations and add allocation address initialization to env. */
928 Module::Get().emit_import<uint32_t>("alloc_addr_init");
929 M_DISCARD env->Set(isolate_->GetCurrentContext(), to_v8_string(isolate_, "alloc_addr_init"),
930 v8::Uint32::New(isolate_, Module::Allocator().perform_pre_allocations()));
931 M_DISCARD imports->Set(context, mkstr(*isolate_, "imports"), env);
932 /* Create a WebAssembly instance object. */
933 auto instance = M_TIME_EXPR(instantiate(*isolate_, imports), " ` Compile WebAssembly to machine code", C.timer());
934 compile_time.stop();
935
936 /* Set the underlying memory for the instance. */
937 v8::SetWasmInstanceRawMemory(instance, wasm_context.vm.as<uint8_t*>(), wasm_context.vm.size());
938
939 /* Get the exports of the created WebAssembly instance. */
940 auto exports = instance->Get(context, mkstr(*isolate_, "exports")).ToLocalChecked().As<v8::Object>();
941 auto main = exports->Get(context, mkstr(*isolate_, "main")).ToLocalChecked().As<v8::Function>();
942
943 /* If a debugging port is specified, set up the inspector and start it. */
944 if (options::cdt_port >= 1024 and not inspector_)
945 inspector_ = std::make_unique<V8InspectorClientImpl>(options::cdt_port, isolate_);
946 if (bool(inspector_)) {
947 run_inspector(*inspector_, *isolate_, env);
948 return;
949 }
950
951 /* Invoke the exported function `main` of the module. */
952 args_t args { v8::Int32::New(isolate_, wasm_context.id), };
953 const uint32_t num_rows =
954 M_TIME_EXPR(main->Call(context, context->Global(), 1, args).ToLocalChecked().As<v8::Uint32>()->Value(),
955 "Execute machine code", C.timer());
956
957 /* Print total number of result tuples. */
958 auto &root_op = plan.get_matched_root();
959 if (auto print_op = cast<const PrintOperator>(&root_op)) {
960 if (not Options::Get().quiet)
961 print_op->out << num_rows << " rows\n";
962 } else if (auto noop_op = cast<const NoOpOperator>(&root_op)) {
963 if (not Options::Get().quiet)
964 noop_op->out << num_rows << " rows\n";
965 }
966 Dispose_Wasm_Context(wasm_context);
967 }
968
969 isolate_->Exit();
972}
973
974__attribute__((constructor(101)))
975static void create_V8Engine()
976{
977 V8Engine::PLATFORM_ = v8::platform::NewDefaultPlatform().release();
978 v8::V8::InitializePlatform(V8Engine::PLATFORM_);
979 v8::V8::SetFlagsFromString("--no-freeze-flags-after-init"); // allow changing flags after initialization
980 v8::V8::Initialize();
981}
982
983__attribute__((destructor(101)))
984static void destroy_V8Engine()
985{
986 v8::V8::Dispose();
987 v8::V8::DisposePlatform();
988}
989
990__attribute__((constructor(202)))
991static void register_WasmV8()
992{
993 Catalog &C = Catalog::Get();
994 C.register_wasm_backend<V8Engine>(C.pool("WasmV8"), "WebAssembly backend using Google's V8 engine");
995
996 /*----- Command-line arguments -----------------------------------------------------------------------------------*/
997 C.arg_parser().add<int>(
998 /* group= */ "Wasm",
999 /* short= */ nullptr,
1000 /* long= */ "--wasm-opt",
1001 /* description= */ "set the optimization level for Wasm modules (0, 1, or 2)",
1002 [] (int i) { options::wasm_optimization_level = i; }
1003 );
1004 C.arg_parser().add<bool>(
1005 /* group= */ "WasmV8",
1006 /* short= */ nullptr,
1007 /* long= */ "--wasm-adaptive",
1008 /* description= */ "enable adaptive execution of Wasm with Liftoff and dynamic tier-up",
1009 [] (bool b) { options::wasm_adaptive = b; }
1010 );
1011 C.arg_parser().add<bool>(
1012 /* group= */ "WasmV8",
1013 /* short= */ nullptr,
1014 /* long= */ "--no-wasm-compilation-cache",
1015 /* description= */ "disable V8's compilation cache",
1016 [] (bool) { options::wasm_compilation_cache = false; }
1017 );
1018 C.arg_parser().add<bool>(
1019 /* group= */ "Wasm",
1020 /* short= */ nullptr,
1021 /* long= */ "--wasm-dump",
1022 /* description= */ "dump the generated WebAssembly code to stdout",
1023 [] (bool b) { options::wasm_dump = b; }
1024 );
1025 C.arg_parser().add<bool>(
1026 /* group= */ "WasmV8",
1027 /* short= */ nullptr,
1028 /* long= */ "--asm-dump",
1029 /* description= */ "dump the generated assembly code to stdout",
1030 [] (bool b) { options::asm_dump = b; }
1031 );
1032 C.arg_parser().add<int>(
1033 /* group= */ "WasmV8",
1034 /* short= */ nullptr,
1035 /* long= */ "--CDT",
1036 /* description= */ "specify the port for debugging via ChromeDevTools",
1037 [] (int i) { options::cdt_port = i; }
1038 );
1039}
1040
1041}
1042
1043
1044/*======================================================================================================================
1045 * Implementation of helper methods
1046 *====================================================================================================================*/
1047
1048v8::Local<v8::String> m::wasm::detail::mkstr(v8::Isolate &isolate, const std::string &str)
1049{
1050 return to_v8_string(&isolate, str);
1051}
1052
1053v8::Local<v8::WasmModuleObject> m::wasm::detail::instantiate(v8::Isolate &isolate, v8::Local<v8::Object> imports)
1054{
1055 auto Ctx = isolate.GetCurrentContext();
1056 auto [binary_addr, binary_size] = Module::Get().binary();
1057 auto bs = v8::ArrayBuffer::NewBackingStore(
1058 /* data = */ binary_addr,
1059 /* byte_length= */ binary_size,
1060 /* deleter= */ v8::BackingStore::EmptyDeleter,
1061 /* deleter_data= */ nullptr
1062 );
1063 auto buffer = v8::ArrayBuffer::New(&isolate, std::move(bs));
1064
1065 if (Options::Get().statistics)
1066 std::cout << "Wasm code size: " << binary_size << std::endl;
1067
1068 args_t module_args { buffer };
1069 auto wasm = Ctx->Global()->Get(Ctx, mkstr(isolate, "WebAssembly")).ToLocalChecked().As<v8::Object>(); // WebAssembly class
1070 auto wasm_module = wasm->Get(Ctx, mkstr(isolate, "Module")).ToLocalChecked().As<v8::Object>()
1071 ->CallAsConstructor(Ctx, 1, module_args).ToLocalChecked().As<v8::WasmModuleObject>();
1072 free(binary_addr);
1073
1074 if (Options::Get().statistics)
1075 std::cout << "Machine code size: " << wasm_module->GetCompiledModule().Serialize().size << std::endl;
1076
1077 args_t instance_args { wasm_module, imports };
1078 return wasm->Get(Ctx, mkstr(isolate, "Instance")).ToLocalChecked().As<v8::Object>()
1079 ->CallAsConstructor(Ctx, 2, instance_args).ToLocalChecked().As<v8::WasmModuleObject>();
1080}
1081
1082v8::Local<v8::Object> m::wasm::detail::create_env(v8::Isolate &isolate, const m::MatchBase &plan)
1083{
1084 auto &DB = Catalog::Get().get_database_in_use();
1086 auto Ctx = isolate.GetCurrentContext();
1087 auto env = v8::Object::New(&isolate);
1088
1089 /* Map accessed tables (and possibly indexes) into the Wasm module. */
1090 auto tables = CollectTables::Collect(plan.get_matched_root());
1091 for (auto &table : tables) {
1092 auto off = context.map_table(table.get());
1093
1094 /* Add memory address to env. */
1095 std::ostringstream oss;
1096 oss << table.get().name() << "_mem";
1097 M_DISCARD env->Set(Ctx, to_v8_string(&isolate, oss.str()), v8::Int32::New(&isolate, off));
1098 Module::Get().emit_import<void*>(oss.str().c_str());
1099
1100 /* Add table size (num_rows) to env. */
1101 oss.str("");
1102 oss << table.get().name() << "_num_rows";
1103 M_DISCARD env->Set(Ctx, to_v8_string(&isolate, oss.str()), v8::Int32::New(&isolate, table.get().store().num_rows()));
1104 Module::Get().emit_import<uint32_t>(oss.str().c_str());
1105
1106 /* Check if array index on any attribute exists. TODO: check whether actual plan applies index scan */
1109 {
1110 for (auto &attr : table.get()) {
1111 if (DB.has_index(table.get().name(), attr.name, idx::IndexMethod::Array)) { // TODO: add other index types
1112 auto &index = DB.get_index(table.get().name(), attr.name, idx::IndexMethod::Array);
1113 auto off = context.map_index(index);
1114
1115 /* Add memory address to env. */
1116 std::ostringstream oss;
1117 oss << "index_" << table.get().name() << '_' << attr.name << "_array" << "_mem";
1118 M_DISCARD env->Set(Ctx, to_v8_string(&isolate, oss.str()), v8::Int32::New(&isolate, off));
1119 Module::Get().emit_import<void*>(oss.str().c_str());
1120
1121 /* Add table size (num_rows) to env. */
1122 oss.str("");
1123 oss << "index_" << table.get().name() << '_' << attr.name << "_array" << "_num_entries";
1124 M_DISCARD env->Set(Ctx, to_v8_string(&isolate, oss.str()), v8::Int32::New(&isolate, index.num_entries()));
1125 Module::Get().emit_import<uint32_t>(oss.str().c_str());
1126 }
1127 }
1128 }
1129 }
1130
1131 /* Map all string literals into the Wasm module. */
1132 M_insist(Is_Page_Aligned(context.heap));
1133 auto literals = CollectStringLiterals::Collect(plan.get_matched_root());
1134 std::size_t bytes = 0;
1135 for (auto literal : literals)
1136 bytes += strlen(literal) + 1;
1137 auto aligned_bytes = Ceil_To_Next_Page(bytes);
1138 if (aligned_bytes) {
1139 auto base_addr = context.vm.as<uint8_t*>() + context.heap;
1140 M_DISCARD mmap(base_addr, aligned_bytes, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANON|MAP_PRIVATE, -1, 0);
1141 char *start_addr = reinterpret_cast<char*>(base_addr);
1142 char *dst = start_addr;
1143 for (auto literal : literals) {
1144 CodeGenContext::Get().add_literal(literal, context.heap + (dst - start_addr)); // add literal
1145 dst = stpcpy(dst, literal) + 1; // copy into sequential memory
1146 }
1147 context.heap += aligned_bytes;
1148 context.install_guard_page();
1149 }
1150 M_insist(Is_Page_Aligned(context.heap));
1151
1152 /* Add functions to environment. */
1153 Module::Get().emit_function_import<void(void*,uint32_t)>("read_result_set");
1154
1155#define EMIT_FUNC_IMPORTS(KEYTYPE, IDXNAME, SUFFIX) \
1156 Module::Get().emit_function_import<uint32_t(std::size_t,KEYTYPE)>(M_STR(idx_lower_bound_##IDXNAME##_##SUFFIX)); \
1157 Module::Get().emit_function_import<uint32_t(std::size_t,KEYTYPE)>(M_STR(idx_upper_bound_##IDXNAME##_##SUFFIX)); \
1158 Module::Get().emit_function_import<void(std::size_t,uint32_t,void*,uint32_t)>(M_STR(idx_scan_##IDXNAME##_##SUFFIX))
1159
1160 EMIT_FUNC_IMPORTS(bool, array, b);
1161 EMIT_FUNC_IMPORTS(int8_t, array, i1);
1162 EMIT_FUNC_IMPORTS(int16_t, array, i2);
1163 EMIT_FUNC_IMPORTS(int32_t, array, i4);
1164 EMIT_FUNC_IMPORTS(int64_t, array, i8);
1165 EMIT_FUNC_IMPORTS(float, array, f);
1166 EMIT_FUNC_IMPORTS(double, array, d);
1167 EMIT_FUNC_IMPORTS(const char*, array, p);
1168 EMIT_FUNC_IMPORTS(int8_t, rmi, i1);
1169 EMIT_FUNC_IMPORTS(int16_t, rmi, i2);
1170 EMIT_FUNC_IMPORTS(int32_t, rmi, i4);
1171 EMIT_FUNC_IMPORTS(int64_t, rmi, i8);
1172 EMIT_FUNC_IMPORTS(float, rmi, f);
1173 EMIT_FUNC_IMPORTS(double, rmi, d);
1174#undef EMIT_FUNC_IMPORTS
1175
1176#define ADD_FUNC(FUNC, NAME) { \
1177 auto func = v8::Function::New(Ctx, (FUNC)).ToLocalChecked(); \
1178 env->Set(Ctx, mkstr(isolate, NAME), func).Check(); \
1179}
1180#define ADD_FUNC_(FUNC) ADD_FUNC(FUNC, #FUNC)
1185 ADD_FUNC(_throw, "throw")
1186
1187#define ADD_FUNCS(IDXTYPE, KEYTYPE, V8TYPE, IDXNAME, SUFFIX) \
1188 ADD_FUNC(index_seek<M_COMMA(IDXTYPE<KEYTYPE>) M_COMMA(V8TYPE) true>, M_STR(idx_lower_bound_##IDXNAME##_##SUFFIX)) \
1189 ADD_FUNC(index_seek<M_COMMA(IDXTYPE<KEYTYPE>) M_COMMA(V8TYPE) false>, M_STR(idx_upper_bound_##IDXNAME##_##SUFFIX)) \
1190 ADD_FUNC(index_sequential_scan<IDXTYPE<KEYTYPE>>, M_STR(idx_scan_##IDXNAME##_##SUFFIX))
1191
1192 ADD_FUNCS(idx::ArrayIndex, bool, v8::Boolean, array, b);
1193 ADD_FUNCS(idx::ArrayIndex, int8_t, v8::Int32, array, i1);
1194 ADD_FUNCS(idx::ArrayIndex, int16_t, v8::Int32, array, i2);
1195 ADD_FUNCS(idx::ArrayIndex, int32_t, v8::Int32, array, i4);
1196 ADD_FUNCS(idx::ArrayIndex, int64_t, v8::BigInt, array, i8);
1197 ADD_FUNCS(idx::ArrayIndex, float, v8::Number, array, f);
1198 ADD_FUNCS(idx::ArrayIndex, double, v8::Number, array, d);
1199 ADD_FUNCS(idx::ArrayIndex, const char*, v8::String, array, p);
1200 ADD_FUNCS(idx::RecursiveModelIndex, int8_t, v8::Int32, rmi, i1);
1201 ADD_FUNCS(idx::RecursiveModelIndex, int16_t, v8::Int32, rmi, i2);
1202 ADD_FUNCS(idx::RecursiveModelIndex, int32_t, v8::Int32, rmi, i4);
1203 ADD_FUNCS(idx::RecursiveModelIndex, int64_t, v8::BigInt, rmi, i8);
1204 ADD_FUNCS(idx::RecursiveModelIndex, float, v8::Number, rmi, f);
1205 ADD_FUNCS(idx::RecursiveModelIndex, double, v8::Number, rmi, d);
1206#undef ADD_FUNCS
1207#undef ADD_FUNC_
1208#undef ADD_FUNC
1209
1210 return env;
1211}
1212
1213v8::Local<v8::String> m::wasm::detail::to_json(v8::Isolate &isolate, v8::Local<v8::Value> val)
1214{
1215 M_insist(&isolate);
1216 auto Ctx = isolate.GetCurrentContext();
1217 return v8::JSON::Stringify(Ctx, val).ToLocalChecked();
1218}
1219
1220std::string m::wasm::detail::create_js_debug_script(v8::Isolate &isolate, v8::Local<v8::Object> env,
1221 const WasmEngine::WasmContext &wasm_context)
1222{
1223 std::ostringstream oss;
1224
1225 auto [binary_addr, binary_size] = Module::Get().binary();
1226
1227 auto json = to_json(isolate, env);
1228 std::string env_str = *v8::String::Utf8Value(&isolate, json);
1229 if (env_str != "{}") env_str.insert(env_str.length() - 1, ",");
1230 env_str.insert(env_str.length() - 1, "\"insist\": function (arg) { assert(arg); },");
1231 env_str.insert(env_str.length() - 1, "\"print\": function (arg) { console.log(arg); },");
1232 env_str.insert(env_str.length() - 1, "\"throw\": function (ex) { console.error(ex); },");
1233 env_str.insert(env_str.length() - 1, "\"read_result_set\": read_result_set,");
1234
1235 /* Construct import object. */
1236 oss << "\
1237let importObject = { \"imports\": " << env_str << " };\n\
1238const bytes = Uint8Array.from([";
1239 for (auto p = binary_addr, end = p + binary_size; p != end; ++p) {
1240 if (p != binary_addr) oss << ", ";
1241 oss << unsigned(*p);
1242 }
1243 free(binary_addr);
1244 /* Emit code to instantiate module and invoke exported `run()` function. */
1245 oss << "]);\n\
1246WebAssembly.compile(bytes).then(\n\
1247 (module) => WebAssembly.instantiate(module, importObject),\n\
1248 (error) => console.error(`An error occurred during module compilation: ${error}`)\n\
1249).then(\n\
1250 function(instance) {\n\
1251 set_wasm_instance_raw_memory(instance, " << wasm_context.id << ");\n\
1252 const num_tuples = instance.exports.main();\n\
1253 console.log('The result set contains %i tuples.', num_tuples);\n\
1254 debugger;\n\
1255 },\n\
1256 (error) => console.error(`An error occurred during module instantiation: ${error}`)\n\
1257);\n\
1258debugger;";
1259
1260 /* Create a new temporary file. */
1261 const char *name = "query.js";
1262 std::ofstream file(name, std::ios_base::out);
1263 if (not file)
1264 throw runtime_error("I/O error");
1265 std::cerr << "Creating debug JS script " << name << std::endl;
1266
1267 /* Write the JS code to instantiate the module and invoke `run()` to the temporary file. */
1268 file << oss.str();
1269 if (not file)
1270 throw runtime_error("I/O error");
1271 file.flush();
1272 if (not file)
1273 throw runtime_error("I/O error");
1274
1275 /* Return the name of the temporary file. */
1276 return std::string(name);
1277}
1278
1279void m::wasm::detail::run_inspector(V8InspectorClientImpl &inspector, v8::Isolate &isolate, v8::Local<v8::Object> env)
1280{
1281 auto Ctx = isolate.GetCurrentContext();
1282 auto &wasm_context = WasmEngine::Get_Wasm_Context_By_ID(Module::ID());
1283
1284 inspector.register_context(Ctx);
1285 inspector.start([&]() {
1286 /* Create JS script file that instantiates the Wasm module and invokes `main()`. */
1287 auto filename = create_js_debug_script(isolate, env, wasm_context);
1288 /* Create a `v8::Script` for that JS file. */
1289 std::ifstream js_in(filename);
1290 std::string js(std::istreambuf_iterator<char>(js_in), std::istreambuf_iterator<char>{});
1291 v8::Local<v8::String> js_src = mkstr(isolate, js);
1292 std::string path = std::string("file://./") + filename;
1293 v8::ScriptOrigin js_origin = v8::ScriptOrigin(&isolate, mkstr(isolate, path));
1294 auto script = v8::Script::Compile(Ctx, js_src, &js_origin);
1295 if (script.IsEmpty())
1296 throw std::runtime_error("failed to compile script");
1297 /* Execute the `v8::Script`. */
1298 auto result = script.ToLocalChecked()->Run(Ctx);
1299 if (result.IsEmpty())
1300 throw std::runtime_error("execution failed");
1301 });
1302 inspector.deregister_context(Ctx);
1303}
__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
option_configs::IndexScanStrategy index_scan_strategy
Which index scan strategy should be used for wasm::IndexScan.
option_configs::IndexScanCompilationStrategy index_scan_compilation_strategy
Which compilation strategy should be used for wasm::IndexScan.
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:1082
v8::Local< v8::WasmModuleObject > instantiate(v8::Isolate &isolate, v8::Local< v8::Object > imports)
Definition: V8Engine.cpp:1053
std::string create_js_debug_script(v8::Isolate &isolate, v8::Local< v8::Object > env, const WasmEngine::WasmContext &wasm_context)
Definition: V8Engine.cpp:1220
void print_memory_consumption(const v8::FunctionCallbackInfo< v8::Value > &info)
Definition: V8Engine.cpp:288
void set_wasm_instance_raw_memory(const v8::FunctionCallbackInfo< v8::Value > &info)
Definition: V8Engine.cpp:299
v8::Local< v8::String > mkstr(v8::Isolate &isolate, const std::string &str)
Definition: V8Engine.cpp:1048
void index_sequential_scan(const v8::FunctionCallbackInfo< v8::Value > &info)
Definition: V8Engine.cpp:572
void run_inspector(V8InspectorClientImpl &inspector, v8::Isolate &isolate, v8::Local< v8::Object > env)
Definition: V8Engine.cpp:1279
void print(const v8::FunctionCallbackInfo< v8::Value > &info)
Definition: V8Engine.cpp:271
void index_seek(const v8::FunctionCallbackInfo< v8::Value > &info)
Definition: V8Engine.cpp:542
v8::Local< v8::String > to_json(v8::Isolate &isolate, v8::Local< v8::Value > val)
Definition: V8Engine.cpp:1213
void read_result_set(const v8::FunctionCallbackInfo< v8::Value > &info)
Definition: V8Engine.cpp:313
and
Constructs a new PrimitiveExpr from a constant value.
Definition: WasmDSL.hpp:1520
Bool< L > value
Definition: WasmUtil.hpp:1317
Bool< L > uint8_t n
Definition: WasmUtil.hpp:1318
auto op
Definition: WasmDSL.hpp:2385
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
Database & get_database_in_use()
Returns a reference to the Database that is currently in use, if any.
Definition: Catalog.hpp:295
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:58
A recursive model index with two layers consiting only of linear monels that maps keys to their tuple...
Definition: Index.hpp:163
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:1140
void dump_all(std::ostream &out)
Definition: WasmDSL.hpp:994
static void Dispose()
Definition: WasmDSL.hpp:711
void emit_import(const char *extern_name, const char *intern_name=nullptr)
Definition: WasmDSL.hpp:866
static Module & Get()
Definition: WasmDSL.hpp:715
static void Init()
Definition: WasmDSL.hpp:707
void emit_function_import(const char *name)
Definition: WasmDSL.hpp:880
static bool Validate(bool verbose=true, bool global=true)
Validates that the module is well-formed.
Definition: WasmDSL.cpp:371
void dump(std::ostream &out) const
Definition: WasmDSL.hpp:991
friend struct Allocator
Definition: WasmDSL.hpp:653
static unsigned ID()
Returns the ID of the current module.
Definition: WasmDSL.hpp:722
void emit_function_export(const char *name)
Add function name as export.
Definition: WasmDSL.hpp:887
static void Optimize(int optimization_level)
Optimizes the module with the optimization level set to level.
Definition: WasmDSL.cpp:379
void emit_call(const char *fn, PrimitiveExpr< ParamTypes, ParamLs >... args)
Definition: WasmDSL.hpp:6717
const std::tuple< const char *, unsigned, const char * > & get_message(std::size_t idx) const
Definition: WasmDSL.hpp:908
std::pair< uint8_t *, std::size_t > binary()
Returns the binary representation of module_ in a freshly allocated memory.
Definition: WasmDSL.cpp:389
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:563