/* * Copyright (c) 2024 Ross Cunniff * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ // OADL opcodes enum { // Miscellaneous group OP_NOP = 0x00, OP_POP, // val pop OP_DUP, // val dup => val val OP_EXCH, // v1 v2 exch => v2 v1 OP_UNUSED_0x04, // unused opcode #0x04 OP_INC, // v0 => v0 + 1 (without promotion) OP_DEC, // v0 => v0 - 1 (without promotion) // Unused: 0x07 // Arithmetic OP_ADD = 0x08, // op1 op2 add => (op1 + op2) OP_SUB, // op1 op2 sub => (op1 - op2) OP_MUL, // op1 op2 mul => (op1 * op2) OP_DIV, // op1 op2 div => (op1 / op2) OP_MOD, // op1 op2 mod => (op1 % op2) OP_NEG, // op1 neg => (-op1) OP_POW, // op1 op2 pow => (op1 ** op2) // Unused: 0x0F // Boolean/bit OP_AND = 0x10, // op1 op2 and => (op1 & op2) OP_OR, // op1 op2 or => (op1 | op2) OP_XOR, // op1 op2 xor => (op1 ^ op2) OP_NOT, // op1 not => (~op1) OP_LOGNOT, // op1 lognot => (~op1) OP_LSHIFT, // op1 op2 lshift => (op1 << op2) OP_RSHIFT, // op1 op2 rshift => (op1 >> op2) // Unused: 0x17 // Comparison OP_LT = 0x18, // op1 op2 lt => (op1 < op2) OP_GT, // op1 op2 gt => (op1 > op2) OP_LE, // op1 op2 le => (op1 <= op2) OP_GE, // op1 op2 ge => (op1 >= op2) // Unused: 0x1C - 0x1F // Equivalence OP_EQ = 0x20, // op1 op2 eq => (op1 == op2) OP_NE, // op1 op2 ne => (op1 != op2) OP_ARR_EQ, // op1 op2 arreq => (op1 #= op2) // Unused: 0x21 - 0x27 // String matching OP_MATCH = 0x28, // str patt comp MATCH => new match env OP_ENDMATCH, // ENDMATCH => pop match env OP_NMATCH, // NMATCH => # of matches OP_MATCHN, // N MATCHN => (match #N) // Unused: 0x2C - 0x2F // Execution flow OP_CALL = 0x30, // arg1 arg2 ... argN N addr call OP_RET, // retval ret OP_MCALL, // arg1 arg2 ... argN N obj public mcall OP_JMP, // addr jmp OP_JMPZ, // val addr jmpz OP_LOCALS, // Allocate locals on the stack OP_TRY, // addr try OP_ENDTRY, // endtry OP_THROW, // excepnum throw OP_PROC, // PROC => current executing proc OP_ARR_CALL, // arr addr arrcall OP_ARR_MCALL, // arr obj public arrmcall // Unused: 0x3C - 0x3F // Get/set global, local, arg, index OP_GETG = 0x40, // global getg => (*global) OP_SETG, // global val setg OP_SETG_TC, // type global val setg_tc OP_GETL, // local getl => (*local) OP_SETL, // local val setl OP_SETL_TC, // type local val setl_tc OP_GETA, // arg geta => (*arg) OP_SETA, // arg val seta OP_SETA_TC, // type arg val seta_tc OP_GETP, // prop getp => (self.prop) OP_SETP, // prop val setp OP_SETP_TC, // type prop val setp_tc OP_GETPK, // prop getpk => prop (self.prop) OP_GETI, // arr index geti => (arr[index]) OP_GETI_M, // arr i0 i1 ... n geti_m => arr[i0,i1,...] OP_SETI, // arr index val seti OP_SETI_M, // arr i0 i1 ... n val seti_m OP_GETIK, // arr index getik => arr index arr[index] OP_GETIK_M, // arr i0 i1 ... n getik_m => arr i0 ... a[i] OP_GETPUB, // obj pub getpub => obj.pub OP_SETPUB, // obj pub val setpub OP_GETPUBK, // obj pub getpubk => obj pub obj.pub OP_SELF, // self => self OP_LOOPINIT, // vbase expr num ishash loopinit => stop? OP_LOOPINCR, // vbase num ishash loopincr => stop? OP_GETI_X, // arr index geti_x => arr#[index] OP_GETI_XK, // arr index geti_xk => arr index arr#[index] OP_SETI_X, // arr index val seti_x OP_SETSUBR, // arr s0 e0 s1 e1 ... n val setsubr OP_GETSUB_K, // arr s0 e0 ... n getsub_k => arr s0 .. arr[..] OP_ARGCHECK, // arg type argcheck // Unused: 0x5F - 0x5F // debugging ops OP_BREAKPOINT = 0x60, // Break and exec OP_DEBUG_POP, // Pop debug name stack OP_LINE, // linenum line OP_DEBUG_PRINT, // var DEBUG_PRINT OP_BREAK_HALT, // Break and halt // Unused: 0x65 - 0x6F // Push instructions - 0 extra bytes OP_NIL = 0x70, // Push nil - 0xFFFFFFFF OP_FLT_0, // Push 0.0 OP_INT_0, // Push 0 OP_DBL_0, // Push 0.d (only OADL64) OP_I64_0, // Push 0L (only OADL64) OP_WCH_0, // Push '\x0'L OP_F16_0, // Push 0.0h OP_U16_0, // Push 0us OP_I16_0, // Push 0s OP_U8_0, // Push 0ub OP_I8_0, // Push 0b OP_CHR_0, // Push '\x0' OP_FALSE, // Push false OP_TRUE, // Push true OP_ARG_0, // Push address of arg 0 // Unused: 0x7F // Unused: 0x80 - 0x8F // Push instructions - 1 extra byte OP_INT_8 = 0x90, // Push -128 to 127 OP_I64_8, // Push -128l to 127l (OADL64) OP_WCH_8, // Push '\x0'L to '\xFF'L OP_U16_8, // Push 0us to 255us OP_I16_8, // Push -128s to 127s OP_U8_8, // Push 0ub to 255ub OP_I8_8, // Push -128b to 127b OP_CHR_8, // Push Char OP_TYP_8, // Push 8-bit Type OP_EXCP_8, // Push 8-bit Exception OP_LOCAL_8, // next byte is local var offs OP_ARG_8, // next byte is arg offs OP_BUILTIN, // next byte is builtin number from builtin.h OP_FILE_8, // Push 8-bit File // Unused: 0x9E - 0x9F // Multibyte instructions - 2 extra bytes OP_INT_16 = 0xA0, // Push -32768 to 32767 OP_FLT_16, // next 2 bytes are upper bits of float OP_I64_16, // Push -32768l to 32767l (OADL64) OP_DBL_16, // next 2 bytes are upper bits of dbl (OADL64) OP_WCH_16, // Push '\x0800'L to '\xFFFF'L OP_F16_16, // Push Half OP_U16_16, // Push 0us to 65535us OP_I16_16, // Push -32768s to 32767s OP_LOCAL_16, // next 2 bytes are local var offs OP_ARG_16, // next 2 bytes are arg offs OP_DEBUG_PUSH, // next 2 bytes are unique ID // Unused: 0xAB - 0xAF // Multibyte instructions - various extra bytes OP_FLT_24 = 0xB0, // next 3 bytes are upper 3 of float OP_DBL_24, // next 3 bytes are upper 3 of dbl (OADL64) OP_WCH_24, // Push '\x080000'L to '\xFFFFFF'L OP_ADDRESS, // next 3 bytes are code address offset // Unused: 0xB4 - 0xB7 // next 3 bytes... OP_PARENT = 0xB8, // next 4 bytes are parent object OP_DBL_32, // next 4 bytes are upper 4 of Double (OADL64) OP_I64_32, // next 4 bytes are lower 4 of Long (OADL64) OP_DEBUG_VAR, // next 4 bytes are name for debugging OP_FILE, // next 4 bytes are array ID of filename OP_DEBUG_USE_NS, // next 4 bytes are namespace name ID // Unused: 0xBE // next 4 bytes... OP_VAR_64 = 0xBF, // next 8 bytes are 64-bit Var (OADL64 only) // Multibyte instructions - 4 extra bytes OP_PUSH_32 = 0xC0, // Lower 6 bits of opcode are type. // next 4 bytes are value. }; typedef uint8_t Opcode; namespace oadl { extern const uint8_t opcodeSizes[256]; extern const uint8_t op0types[16]; extern const uint8_t op8types[16]; extern const uint8_t op16types[16]; #if defined(OADL_DEFINE_MACH_TABLE) // [ const uint8_t opcodeSizes[256] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0x90 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xA0 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 9, // 0xB0 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 0xC0 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 0xD0 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 0xE0 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // 0xF0 }; const uint8_t op0types[16] = { OADL_VT_NULL, OADL_VT_FLOAT, OADL_VT_INT, OADL_VT_DOUBLE, OADL_VT_LONG, OADL_VT_WCHAR, OADL_VT_HALF, OADL_VT_USHORT, OADL_VT_SHORT, OADL_VT_UBYTE, OADL_VT_BYTE, OADL_VT_CHAR, OADL_VT_BOOL, OADL_VT_BOOL, OADL_VT_ARG, OADL_VT_NULL, }; const uint8_t op8types[16] = { OADL_VT_INT, OADL_VT_LONG, OADL_VT_WCHAR, OADL_VT_USHORT, OADL_VT_SHORT, OADL_VT_UBYTE, OADL_VT_BYTE, OADL_VT_CHAR, OADL_VT_TYPE, OADL_VT_EXCEPT, OADL_VT_LOCAL, OADL_VT_ARG, OADL_VT_INTR, OADL_VT_FILE, OADL_VT_NULL, OADL_VT_NULL, }; const uint8_t op16types[16] = { OADL_VT_INT, OADL_VT_FLOAT, OADL_VT_LONG, OADL_VT_DOUBLE, OADL_VT_WCHAR, OADL_VT_HALF, OADL_VT_USHORT, OADL_VT_SHORT, OADL_VT_LOCAL, OADL_VT_ARG, OADL_VT_NULL, OADL_VT_NULL, OADL_VT_NULL, OADL_VT_NULL, OADL_VT_NULL, OADL_VT_NULL, }; #endif // ] // Instruction decode methods static inline int instrCount(Opcode op) { return oadl::opcodeSizes[op]; } static inline int encode(Opcode *ip, Opcode op, uint32_t a0, uint32_t a1) { int size = instrCount(op); ip[0] = op; // Note tricky fallthroughs switch (size) { #if defined(OADL64) case 9 : ip[8] = (Opcode) ((a1 >> 24) & 0xFF); ip[7] = (Opcode) ((a1 >> 16) & 0xFF); ip[6] = (Opcode) ((a1 >> 8) & 0xFF); ip[5] = (Opcode) ((a1 ) & 0xFF); // FALLTHROUGH #endif case 5 : ip[4] = (Opcode) ((a0 >> 24) & 0xFF); // FALLTHROUGH case 4 : ip[3] = (Opcode) ((a0 >> 16) & 0xFF); // FALLTHROUGH case 3 : ip[2] = (Opcode) ((a0 >> 8) & 0xFF); // FALLTHROUGH case 2 : ip[1] = (Opcode) ((a0 ) & 0xFF); break; } return size; } static inline int decode(Opcode *ip, Opcode *op, uint32_t *a0, uint32_t *a1) { Opcode b0 = ip[0]; *op = b0; uint32_t va0 = 0, va1 = 0; int size = instrCount(b0); // Note tricky fallthroughs again switch (size) { #if defined(OADL64) case 9 : va1 |= ip[8] << 24; va1 |= ip[7] << 16; va1 |= ip[6] << 8; va1 |= ip[5]; // FALLTHROUGH #endif case 5 : // FALLTHROUGH va0 |= ip[4] << 24; case 4 : // FALLTHROUGH va0 |= ip[3] << 16; case 3 : // FALLTHROUGH va0 |= ip[2] << 8; case 2 : // FALLTHROUGH va0 |= ip[1]; break; } *a0 = va0; *a1 = va1; return size; } }