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 }