mutable
A Database System for Research and Fast Prototyping
Loading...
Searching...
No Matches
StackMachine.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cstdint>
6
7
8namespace m {
9
10namespace ast {
11
12struct Expr;
13
14}
15
16namespace cnf {
17
18 struct CNF;
19
20}
21
22struct StackMachineBuilder;
23
26{
27 friend struct Interpreter;
28 friend struct StackMachineBuilder;
29
30 static constexpr std::size_t SIZE_OF_MEMORY = 4 * 1024; // 4 KiB
31
32 enum class Opcode : uint8_t
33 {
34#define M_OPCODE(CODE, ...) CODE,
35#include "tables/Opcodes.tbl"
36#undef M_OPCODE
37 Last
38 };
39 static_assert(uint64_t(Opcode::Last) < (1UL << (sizeof(Opcode) * 8)), "too many opcodes");
40
41 using index_t = std::size_t;
42
43 private:
44 static constexpr const char *OPCODE_TO_STR[] = {
45#define M_OPCODE(CODE, ...) #CODE,
46#include "tables/Opcodes.tbl"
47#undef M_OPCODE
48 };
49 static const std::unordered_map<std::string, Opcode> STR_TO_OPCODE;
50
51 public:
52 static Opcode str_to_opcode(const std::string &str) { return STR_TO_OPCODE.at(str); }
53
54 private:
55 /*----- The schema of incoming and outgoing tuples. --------------------------------------------------------------*/
57 std::vector<const Type*> out_schema;
58
59 /*----- Fields defining the structure of the machine. ------------------------------------------------------------*/
60 std::vector<Opcode> ops;
61 std::vector<Value> context_;
63 int64_t current_stack_size_ = 0;
64
65 /*----- Fields capturing the internal state during execution. ----------------------------------------------------*/
66 mutable Value *values_ = nullptr;
67 mutable bool *null_bits_ = nullptr;
68 mutable decltype(ops)::const_iterator op_;
69 mutable std::size_t top_ = 0;
70 mutable uint8_t memory_[SIZE_OF_MEMORY];
71
72 public:
75
79
84
89
90 StackMachine(const StackMachine&) = delete;
92
94 delete[] values_;
95 delete[] null_bits_;
96 }
97
99 const Schema & schema_in() const { return in_schema; }
100
102 const std::vector<const Type*> & schema_out() const { return out_schema; }
103
104 std::size_t num_ops() const { return ops.size(); }
105
107 std::size_t required_stack_size() const { return required_stack_size_; }
108
110 void emit(const ast::Expr &expr, std::size_t tuple_id = 0);
111
113 void emit(const ast::Expr &expr, const Schema &schema, std::size_t tuple_id = 0);
114
116 void emit(const ast::Expr &expr,
117 std::vector<Schema> &schemas,
118 std::vector<std::size_t> &tuple_ids);
119
121 void emit(const cnf::CNF &cnf, std::size_t tuple_id = 0);
122
124 void emit(const cnf::CNF &cnf,
125 std::vector<Schema> &schemas,
126 std::vector<std::size_t> &tuple_ids);
127
128 /* The following macros are used to automatically generate methods to emit a particular opcode. For example, for
129 * the opcode `Pop`, we will define a function `emit_Pop()`, that appends the `Pop` opcode to the current opcode
130 * sequence. For opcodes that require an argument, a function with the respective parameter is defined and that
131 * parameter is inserted into the opcode sequence. For example, the opcode `Ld_Ctx` requires a single parameter
132 * with the index of the context value. The macro will expand to the method `emit_Ld_Ctx(uint8_t idx)`, that first
133 * appends the `Ld_Ctx` opcode to the opcode sequence and then appends the `idx` parameter to the opcode sequence.
134 */
135#define SELECT(XXX, _1, _2, _3, FN, ...) FN(__VA_ARGS__)
136#define ARGS_0(XXX, ...)
137#define ARGS_1(I, XXX, ARG0, ...) uint8_t ARG0
138#define ARGS_2(I, II, XXX, ARG0, ARG1, ...) uint8_t ARG0, uint8_t ARG1
139#define ARGS_3(I, II, III, XXX, ARG0, ARG1, ARG2, ...) uint8_t ARG0, uint8_t ARG1, uint8_t ARG2
140#define ARGS(...) SELECT(__VA_ARGS__, ARGS_3, ARGS_2, ARGS_1, ARGS_0, __VA_ARGS__)
141#define PUSH_0(XXX, ...)
142#define PUSH_1(I, XXX, ARG0, ...) \
143 ops.push_back(static_cast<Opcode>((ARG0)));
144#define PUSH_2(I, II, XXX, ARG0, ARG1, ...) \
145 ops.push_back(static_cast<Opcode>((ARG0))); \
146 ops.push_back(static_cast<Opcode>((ARG1)));
147#define PUSH_3(I, II, III, XXX, ARG0, ARG1, ARG2, ...) \
148 ops.push_back(static_cast<Opcode>((ARG0))); \
149 ops.push_back(static_cast<Opcode>((ARG1))); \
150 ops.push_back(static_cast<Opcode>((ARG2)));
151#define PUSH(...) SELECT(__VA_ARGS__, PUSH_3, PUSH_2, PUSH_1, PUSH_0, __VA_ARGS__)
152
153#define M_OPCODE(CODE, DELTA, ...) \
154 void emit_ ## CODE ( ARGS(XXX, ##__VA_ARGS__) ) { \
155 ops.push_back(StackMachine::Opcode:: CODE ); \
156 current_stack_size_ += DELTA; \
157 M_insist(current_stack_size_ >= 0); \
158 required_stack_size_ = std::max(required_stack_size_, current_stack_size_); \
159 PUSH(XXX, ##__VA_ARGS__) \
160 }
161
162#include "tables/Opcodes.tbl"
163
164#undef M_OPCODE
165#undef SELECT
166#undef ARGS_0
167#undef ARGS_1
168#undef ARGS_2
169#undef ARGS
170#undef PUSH_0
171#undef PUSH_1
172#undef PUSH_2
173#undef PUSH
174
176 void emit(Opcode opc) { ops.push_back(opc); }
177
179 void emit_Ld(const Type *ty);
180
182 void emit_St(const Type *ty);
183
185 void emit_St_Tup(std::size_t tuple_id, std::size_t index, const Type *ty);
186
188 void emit_Print(std::size_t ostream_index, const Type *ty);
189
191 void emit_Cast(const Type *to_ty, const Type *from_ty);
192
194 std::size_t add(Value val) {
195 auto idx = context_.size();
196 context_.push_back(val);
197 return idx;
198 }
199
201 void set(std::size_t idx, Value val) {
202 M_insist(idx < context_.size(), "index out of bounds");
203 context_[idx] = val;
204 }
205
208 std::size_t add_and_emit_load(Value val) {
209 auto idx = add(val);
210 emit_Ld_Ctx(idx);
211 return idx;
212 }
213
218 void operator()(Tuple **tuples) const;
219
220 void dump(std::ostream &out) const;
221 void dump() const;
222};
223
224}
#define M_insist(...)
Definition: macro.hpp:129
const Expr
Definition: AST.hpp:437
‍mutable namespace
Definition: Backend.hpp:10
Evaluates SQL operator trees on the database.
A Schema represents a sequence of identifiers, optionally with a prefix, and their associated types.
Definition: Schema.hpp:39
A stack machine that evaluates an expression.
void set(std::size_t idx, Value val)
Sets the Value in the context at index idx to val.
Schema in_schema
schema of the input tuple
void emit_Print(std::size_t ostream_index, const Type *ty)
Emit a Print_X instruction based on Type ty, e.g. Print_i for integral Types.
std::size_t index_t
std::size_t add_and_emit_load(Value val)
Adds the Value val to the context and emits a load instruction to load this value to the top of the s...
int64_t required_stack_size_
the required size of the stack
static Opcode str_to_opcode(const std::string &str)
std::vector< Value > context_
the context of the stack machine, e.g. constants or global variables
bool * null_bits_
array of NULL bits used as a stack
static const std::unordered_map< std::string, Opcode > STR_TO_OPCODE
void dump() const
StackMachine(Schema in_schema)
Create a StackMachine with the given input Schema in_schema.
static constexpr const char * OPCODE_TO_STR[]
StackMachine()
Create a StackMachine that does not accept input.
int64_t current_stack_size_
the "current" stack size; i.e. after the last operation is executed
void emit_Ld(const Type *ty)
Emit a Ld_X instruction based on Type ty, e.g. Ld_i32 for 4 byte integral types.
std::size_t num_ops() const
Value * values_
array of values used as a stack
const Schema & schema_in() const
Returns the Schema of input Tuples.
std::size_t top_
the top of the stack
StackMachine(StackMachine &&)=default
void emit_St(const Type *ty)
Emit a St_X instruction based on Type ty, e.g. St_i32 for 4 byte integral types.
std::vector< Opcode > ops
the sequence of operations to perform
decltype(ops) ::const_iterator op_
the next operation to execute
std::vector< const Type * > out_schema
schema of the output tuple
void operator()(Tuple **tuples) const
Evaluate this StackMachine given the Tuples referenced by tuples.
std::size_t add(Value val)
Appends the Value val to the context and returns its assigned index.
void emit_St_Tup(std::size_t tuple_id, std::size_t index, const Type *ty)
Emit a St_Tup_X instruction based on Type ty, e.g. St_Tup_i for integral Types.
void emit(Opcode opc)
Append the given opcode to the opcode sequence.
uint8_t memory_[SIZE_OF_MEMORY]
memory usable by the stack machine, e.g. to work on BLOBs
static constexpr std::size_t SIZE_OF_MEMORY
void emit(const ast::Expr &expr, std::size_t tuple_id=0)
Emit operations evaluating the Expr expr.
StackMachine(const StackMachine &)=delete
const std::vector< const Type * > & schema_out() const
Returns a sequence of Types defining the schema of output Tuples.
std::size_t required_stack_size() const
Returns the required size of the stack to evaluate the opcode sequence.
void emit_Cast(const Type *to_ty, const Type *from_ty)
Emit opcodes to convert a value of Type from_ty to Type to_ty.
This class represents types in the SQL type system.
Definition: Type.hpp:46
This class holds a SQL attribute value.
Definition: Tuple.hpp:19
An expression.
Definition: AST.hpp:39
A CNF represents a conjunction of cnf::Clauses.
Definition: CNF.hpp:134