1 module vayne.op;
2 
3 
4 import std.conv;
5 import std.format;
6 import std.meta;
7 
8 
9 enum : uint { OpCodesVersion = 1 };
10 
11 
12 enum OpCode : ulong {
13 	Nop,
14 	Halt,
15 	Throw,
16 	Output,
17 
18 	Jump,
19 	JumpIfZero,
20 	JumpIfNotZero,
21 
22 	Move,
23 	PushScope,
24 	PopScope,
25 
26 	Increment,
27 	Decrement,
28 	Minus,
29 	Not,
30 
31 	And,
32 	Or,
33 	Add,
34 	Subtract,
35 	Multiply,
36 	Divide,
37 	Remainder,
38 	Power,
39 	Concat,
40 
41 	Test,
42 	Equal,
43 	NotEqual,
44 	Less,
45 	LessOrEqual,
46 	Greater,
47 	GreaterOrEqual,
48 
49 	Length,
50 	Keys,
51 	TestKey,
52 	Slice,
53 	Dispatch,
54 	Element,
55 	LookUp,
56 
57 	Call,
58 	DispatchCall,
59 }
60 
61 
62 struct Operand {
63 	enum Kind : uint {
64 		Immediate = 0,
65 		Register,
66 		Constant,
67 	}
68 
69 	auto isConst() const {
70 		return kind == Kind.Constant;
71 	}
72 
73 	auto isImmediate() const {
74 		return kind == Kind.Immediate;
75 	}
76 
77 	auto isRegister() const {
78 		return kind == Kind.Register;
79 	}
80 
81 	auto isRegisterOrConst() const {
82 		return (kind == Kind.Register) || (kind == Kind.Constant);
83 	}
84 
85 	uint kind;
86 	uint value;
87 }
88 
89 
90 // code0: [63 arg1 const?][62 arg0 const?][60..33 arg1][33..6 arg0][6..0 op]
91 // code1: [63 arg3 const?][62 arg2 const?][62..54 unused][54..27 arg3][27..0 arg2]
92 
93 enum {
94 	OpBitCount		= 6,
95 	OpMask 			= (1UL << OpBitCount) - 1,
96 
97 	ArgBitCount 	= 28,
98 
99 	Arg0Shift		= OpBitCount,
100 	Arg1Shift		= Arg0Shift + ArgBitCount,
101 	Arg2Shift		= 0,
102 	Arg3Shift		= Arg2Shift + ArgBitCount,
103 
104 	ArgMask 		= (1UL << ArgBitCount) - 1,
105 
106 	Arg0ConstMask	= 1UL << 62,
107 	Arg1ConstMask	= 1UL << 63,
108 	Arg2ConstMask	= 1UL << 62,
109 	Arg3ConstMask	= 1UL << 63,
110 }
111 
112 
113 struct Instr {
114 	this(ulong code0, ulong code1) {
115 		this.code0 = code0;
116 		this.code1 = code1;
117 	}
118 
119 	this(Args...)(OpCode op, Args args) {
120 		assert(op <= OpMask);
121 
122 		auto argCountValid = false;
123 
124 		enum argCount = args.length;
125 
126 		final switch (op) with (OpCode) {
127 		case Nop:
128 		case Halt:
129 			static if (argCount == 0) {
130 				argCountValid = true;
131 			}
132 			break;
133 		case Jump:
134 		case PopScope:
135 			static if (argCount == 1) {
136 				assert(args[0].isImmediate);
137 				argCountValid = true;
138 			}
139 			break;
140 		case Increment:
141 		case Decrement:
142 		case Minus:
143 		case Not:
144 			static if (argCount == 1) {
145 				assert(args[0].isRegister);
146 				argCountValid = true;
147 			}
148 			break;
149 		case Output:
150 		case Throw:
151 		case PushScope:
152 			static if (argCount == 1) {
153 				assert(args[0].isRegisterOrConst);
154 				argCountValid = true;
155 			}
156 			break;
157 		case JumpIfZero:
158 		case JumpIfNotZero:
159 			static if (argCount == 2) {
160 				assert(args[0].isImmediate);
161 				assert(args[1].isRegisterOrConst);
162 				argCountValid = true;
163 			}
164 			break;
165 		case Test:
166 		case Move:
167 		case Length:
168 		case Keys:
169 		case LookUp:
170 			static if (argCount == 2) {
171 				assert(args[0].isRegister);
172 				assert(args[1].isRegisterOrConst);
173 				argCountValid = true;
174 			}
175 			break;
176 		case And:
177 		case Or:
178 		case Add:
179 		case Subtract:
180 		case Multiply:
181 		case Divide:
182 		case Remainder:
183 		case Power:
184 		case Concat:
185 		case Equal:
186 		case NotEqual:
187 		case Less:
188 		case LessOrEqual:
189 		case Greater:
190 		case GreaterOrEqual:
191 		case TestKey:
192 		case Dispatch:
193 		case Element:
194 			static if (argCount == 3) {
195 				assert(args[0].isRegister);
196 				assert(args[1].isRegisterOrConst);
197 				assert(args[2].isRegisterOrConst);
198 				argCountValid = true;
199 			}
200 			break;
201 		case Slice:
202 			static if (argCount == 4) {
203 				assert(args[0].isRegister);
204 				assert(args[1].isRegisterOrConst);
205 				assert(args[2].isRegisterOrConst);
206 				assert(args[3].isRegisterOrConst);
207 				argCountValid = true;
208 			}
209 			break;
210 		case Call:
211 		case DispatchCall:
212 			static if (argCount == 4) {
213 				assert(args[0].isRegister);
214 				assert(args[1].isRegisterOrConst);
215 				assert(args[2].isRegister);
216 				assert(args[3].isImmediate);
217 				argCountValid = true;
218 			}
219 		}
220 
221 		if (!argCountValid)
222 			assert(false, "wrong number of arguments to encode for opcode " ~ op.to!string);
223 
224 		code0 = op;
225 
226 		static if (argCount > 0) {
227 			assert(args[0].value <= ArgMask);
228 			code0 |= (cast(ulong)args[0].value << Arg0Shift) | (args[0].isConst ? Arg0ConstMask : 0);
229 		}
230 		static if (argCount > 1) {
231 			assert(args[1].value <= ArgMask);
232 			code0 |= (cast(ulong)args[1].value << Arg1Shift) | (args[1].isConst ? Arg1ConstMask : 0);
233 		}
234 		static if (argCount > 2) {
235 			assert(args[2].value <= ArgMask);
236 			code1 |= (cast(ulong)args[2].value << Arg2Shift) | (args[2].isConst ? Arg2ConstMask : 0);
237 		}
238 		static if (argCount > 3) {
239 			assert(args[3].value <= ArgMask);
240 			code1 |= (cast(ulong)args[3].value << Arg3Shift) | (args[3].isConst ? Arg3ConstMask : 0);
241 		}
242 	}
243 
244 	ulong code0;
245 	ulong code1;
246 
247 	@property OpCode op() const {
248 		return cast(OpCode)(code0 & OpMask);
249 	}
250 
251 	size_t arg(size_t Arg)() const {
252 		static if (Arg == 0) {
253 			return (code0 >> Arg0Shift) & ArgMask;
254 		} else static if (Arg == 1) {
255 			return (code0 >> Arg1Shift) & ArgMask;
256 		} else static if (Arg == 2) {
257 			return (code1 >> Arg2Shift) & ArgMask;
258 		} else static if (Arg == 3) {
259 			return (code1 >> Arg3Shift) & ArgMask;
260 		}
261 	}
262 
263 	bool argConst(size_t Arg)() const {
264 		static if (Arg == 0) {
265 			return (code0 & Arg0ConstMask) != 0;
266 		} else static if (Arg == 1) {
267 			return (code0 & Arg1ConstMask) != 0;
268 		} else static if (Arg == 2) {
269 			return (code1 & Arg2ConstMask) != 0;
270 		} else static if (Arg == 3) {
271 			return (code1 & Arg3ConstMask) != 0;
272 		}
273 	}
274 
275 	string argName(size_t Arg)() const {
276 		return format("%s%s", (argConst!Arg ? "c" : "r"), arg!Arg);
277 	}
278 
279 	string toStringFull() const {
280 		return format("%016x%016x %s", code1, code0, toString);
281 	}
282 
283 	bool isBoolean() const {
284 		final switch (op) with (OpCode) {
285 		case Nop:
286 		case Halt:
287 		case Jump:
288 		case Increment:
289 		case Decrement:
290 		case Minus:
291 		case Output:
292 		case Throw:
293 		case JumpIfZero:
294 		case JumpIfNotZero:
295 		case Move:
296 		case Length:
297 		case Keys:
298 		case And:
299 		case Or:
300 		case Add:
301 		case Subtract:
302 		case Multiply:
303 		case Divide:
304 		case Remainder:
305 		case Power:
306 		case Concat:
307 		case Slice:
308 		case Dispatch:
309 		case Element:
310 		case LookUp:
311 		case Call:
312 		case DispatchCall:
313 		case PushScope:
314 		case PopScope:
315 			return false;
316 		case Not:
317 		case Test:
318 		case Equal:
319 		case NotEqual:
320 		case Less:
321 		case LessOrEqual:
322 		case Greater:
323 		case GreaterOrEqual:
324 		case TestKey:
325 			return true;
326 		}
327 	}
328 
329 	string toString() const {
330 		auto name = op.to!string;
331 
332 		final switch (op) with (OpCode) {
333 		case Nop:
334 		case Halt:
335 			return name;
336 		case Jump:
337 		case PopScope:
338 			return format("%s %s", name, arg!0);
339 		case Increment:
340 		case Decrement:
341 		case Minus:
342 		case Not:
343 		case Output:
344 		case Throw:
345 		case PushScope:
346 			return format("%s %s", name, argName!0);
347 		case JumpIfZero:
348 		case JumpIfNotZero:
349 			return format("%s %s %s", name, arg!0, argName!1);
350 		case Test:
351 		case Move:
352 		case Length:
353 		case Keys:
354 		case LookUp:
355 			return format("%s %s %s", name, argName!0, argName!1);
356 		case And:
357 		case Or:
358 		case Add:
359 		case Subtract:
360 		case Multiply:
361 		case Divide:
362 		case Remainder:
363 		case Power:
364 		case Concat:
365 		case Equal:
366 		case NotEqual:
367 		case Less:
368 		case LessOrEqual:
369 		case Greater:
370 		case GreaterOrEqual:
371 		case TestKey:
372 		case Dispatch:
373 		case Element:
374 			return format("%s %s %s %s", name, argName!0, argName!1, argName!2);
375 		case Slice:
376 			return format("%s %s %s %s %s", name, argName!0, argName!1, argName!2, argName!3);
377 		case DispatchCall:
378 		case Call:
379 			return format("%s %s %s %s %s", name, argName!0, argName!1, argName!2, arg!3);
380 		}
381 	}
382 }