aboutsummaryrefslogtreecommitdiff
path: root/include/bytecode/bytecode.h
blob: 64b54b403f02c70333d5a18b0c51566de4bf5889 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#ifndef AMALGAM_BYTECODE_H
#define AMALGAM_BYTECODE_H

#include "../std/defs.h"
#include "../std/misc.h"
#include "../std/buffer.h"
#include "../defs.h"

#include <setjmp.h>

/*doc(Opcode)
    Variable length opcodes. Sizes range from 1 to 5 bytes.
    # Instruction formats
    Instructions can be in 7 different formats:
    1. 1 byte: Opcode(u8)
    2. 2 bytes: Opcode(u8) + register(i8)
    3. 3 bytes: Opcode(u8) + register(i8) + register(i8)
    4. 3 bytes:\
    4.1 Opcode(u8) + intermediate(u16)\
    4.2 Opcode(u8) + data(u16)\
    4.3 Opcode(u8) + offset(i16)\
    4.4 Opcode(u8) + register(i8) + num_args(u8)
    5. 4 bytes: Opcode(u8) + register(i8) + register(i8) + register(i8)
    6. 4 bytes:\
    6.1 Opcode(u8) + register(i8) + offset(i16)\
    6.2 Opcode(u8) + register(i8) + intermediate(u16)\
    6.3 Opcode(u8) + register(i8) + data(u16)\
    6.4 Opcode(u8) + flags(u8) + num_local_var_reg(u16)
    7. 5 bytes: Opcode(u8) + index(u16) + num_args(u8) + register(i8)
    # Registers
    Registers have a range of 128. Local variables start from register 0 and increment while parameters start from -1
    and decrement.
*/
typedef enum {
    AMAL_OP_NOP,         /* No operation (do nothing). This can be used for patching code */
    AMAL_OP_SETZ,        /* setz reg                             - Set register value to 0 */
    AMAL_OP_MOV,         /* mov dst, src                         - Move src register to dst register */
    AMAL_OP_MOVI,        /* movi dst, src                        - Move src intermediate to dst register */
    AMAL_OP_MOVD,        /* movd dst, src                        - Move src data to dst register */
    AMAL_OP_ADD,         /* add dst, reg1, reg2                  - Add reg1 and reg2 and put the result in dst */
    AMAL_OP_SUB,         /* sub dst, reg1, reg2                  - Substract reg2 from reg1 and put the result in dst */
    AMAL_OP_IMUL,        /* imul dst, reg1, reg2                 - Signed multiplication */
    AMAL_OP_MUL,         /* mul dst, reg1, reg2                  - Unsigned multiplication */
    AMAL_OP_IDIV,        /* idiv dst, reg1, reg2                 - Signed division */
    AMAL_OP_DIV,         /* div dst, reg1, reg2                  - Unsigned division */
    AMAL_OP_PUSH,        /* push reg                             - Push register onto stack */
    AMAL_OP_PUSHI,       /* pushi int                            - Push intermediate onto stack */
    AMAL_OP_PUSHD,       /* pushd data                           - Push data onto stack */
    AMAL_OP_CALL,        /* call fi, num_args, dst               - Call a function using function index (fi) and num_args arguments. The result is stored in register dst. fi is u16 and num_args is u8 */
    AMAL_OP_CALLR,       /* callr reg, num_args                  - Call a function using a register. Used for function pointers. num_args is u8 */
    AMAL_OP_CALLE,       /* calle efi, num_args, dst             - Call an extern function using extern function index (efi) and num_args arguments. The result is stored in register dst. efi is u16 and num_args is u8 */
    AMAL_OP_CMP,         /* cmp dst, reg1, reg2                  - Set dst to 1 if reg1 equals reg2, otherwise set it to 0 */
    AMAL_OP_JZ,          /* jz reg, offset                       - Jump to offset if reg is zero. offset is i16 */
    AMAL_OP_JMP,         /* jmp offset                           - Unconditional jump to offset. offset is i16 */
    AMAL_OP_RET,         /* ret reg                              - Return from the function with reg result */
    AMAL_OP_FUNC_START,  /* func_start flags, num_local_var_reg  - Start of a function which has @num_local_var_reg local variable registers allocated and has the flag @flag. @flag is u8 and @num_local_var_reg is u16 */
    AMAL_OP_FUNC_END     /* func_end                             - End of a function. Implementation should do a ret here */
} AmalOpcode;

#define AMAL_BYTECODE_MAGIC_NUMBER (u32)0xdec05eba
#define AMAL_BYTECODE_MAJOR_VERSION 1
#define AMAL_BYTECODE_MINOR_VERSION 0
#define AMAL_BYTECODE_PATCH_VERSION 0

#define AMAL_BYTECODE_NUM_REGISTERS 256

typedef enum {
    FUNC_FLAG_NONE       = 0,
    FUNC_FLAG_EXPORTED   = (1 << 1)
} amal_func_flag;

typedef u8 AmalOpcodeType;

typedef struct {
    Buffer/*<headers + instruction data>*/ data;
} Bytecode;

typedef struct {
    jmp_buf env;
    Bytecode bytecode;
    Parser *parser; /* borrowed */
} BytecodeCompilerContext;

CHECK_RESULT int bytecode_init(Bytecode *self, ArenaAllocator *allocator);

CHECK_RESULT int buffer_append_header(Buffer *program_data);

/* longjump to self->env on failure */
void generate_bytecode_from_ssa(BytecodeCompilerContext *self);

#endif