Completely rewrite stack management.
Now we always use the stack instead of keeping a TOS register. This is very inefficient, but I'll worry about register allocation later. The new block model is inspired by x86's `enter` and `leave` instructions. I intend to support nested procedures at some point in the future.master
parent
4e06f8d00f
commit
57aa667000
15
src/asm.h
15
src/asm.h
|
@ -6,7 +6,7 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef size_t ip;
|
||||
typedef uint32_t ip;
|
||||
extern ip here;
|
||||
|
||||
/// Jump to a known address.
|
||||
|
@ -15,17 +15,4 @@ void inst_jump(ip there);
|
|||
/// Jump to an unresolved address.
|
||||
ip inst_jump_unresolved(void);
|
||||
void inst_jump_resolve(ip disp, ip there);
|
||||
|
||||
void inst_mov_imm(reg reg, uint64_t imm);
|
||||
void inst_mov_imm_i64(reg reg, int64_t imm);
|
||||
void inst_syscall(void);
|
||||
|
||||
void inst_push(reg reg);
|
||||
void inst_pop(reg reg);
|
||||
|
||||
void inst_mov(reg dest, reg src);
|
||||
void inst_mov_from(reg dest, reg base);
|
||||
void inst_mov_from_disp(reg dest, reg base, int32_t disp);
|
||||
void inst_mov_to(reg base, reg src);
|
||||
void inst_mov_to_disp(reg base, reg src, int32_t disp);
|
||||
#endif
|
||||
|
|
2
src/io.c
2
src/io.c
|
@ -22,7 +22,7 @@ static const char* outfile_name;
|
|||
FILE* infile;
|
||||
FILE* outfile;
|
||||
// HACK: "here" tracking should be handled by the assembler, not IO.
|
||||
size_t here = 0;
|
||||
uint32_t here = 0;
|
||||
|
||||
#ifdef __linux__
|
||||
void open_files(const char* infile_name, const char* outfile_name_) {
|
||||
|
|
2
src/io.h
2
src/io.h
|
@ -6,7 +6,7 @@
|
|||
|
||||
extern FILE* outfile;
|
||||
extern FILE* infile;
|
||||
extern size_t here;
|
||||
extern uint32_t here;
|
||||
|
||||
void open_files(const char* infile_name, const char* outfile_name);
|
||||
void close_files(void);
|
||||
|
|
277
src/ir.c
277
src/ir.c
|
@ -12,174 +12,80 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct fixups {
|
||||
struct fixups* next;
|
||||
ip disp;
|
||||
#define MAX_STACK_FRAMES 32
|
||||
#define MAX_LABELS 256
|
||||
#define MAX_FIXUPS 256
|
||||
|
||||
struct stack_frame {
|
||||
uint32_t depth;
|
||||
uint32_t label_depth;
|
||||
};
|
||||
|
||||
struct label_info {
|
||||
const struct frame* frame;
|
||||
size_t argc;
|
||||
struct label {
|
||||
uint32_t frame;
|
||||
uint32_t argc;
|
||||
ip definition;
|
||||
struct fixups* fixups;
|
||||
uint32_t fixupc;
|
||||
ip fixups[MAX_FIXUPS];
|
||||
};
|
||||
|
||||
struct labels {
|
||||
struct labels* prev;
|
||||
struct label_info info;
|
||||
};
|
||||
static uint32_t stack_depth = 0;
|
||||
static uint32_t stack_frame = 0;
|
||||
static struct stack_frame stack_frames[MAX_STACK_FRAMES];
|
||||
static uint32_t label_depth;
|
||||
static struct label labels[MAX_LABELS];
|
||||
|
||||
struct frame {
|
||||
struct frame* prev;
|
||||
size_t depth;
|
||||
struct labels* labels;
|
||||
};
|
||||
static struct frame* current_frame;
|
||||
|
||||
struct storage {
|
||||
enum { STORE_REG, STORE_STACK } type;
|
||||
union { reg reg; size_t off; };
|
||||
};
|
||||
|
||||
static size_t top_of_stack;
|
||||
|
||||
// Enter a new stack frame.
|
||||
static void enter(void) {
|
||||
struct frame* next = malloc(sizeof(struct frame));
|
||||
next->prev = current_frame;
|
||||
next->depth = top_of_stack;
|
||||
current_frame = next;
|
||||
void init(var* argc, var* argv, var* env) {
|
||||
assert(stack_depth == 0);
|
||||
x86_inst_mov_r64_r64(BP, SP);
|
||||
// TODO: replace with add, once I implement add
|
||||
x86_inst_add_r64_imm8(BP, 8 * 3);
|
||||
*env = stack_depth++;
|
||||
*argv = stack_depth++;
|
||||
*argc = stack_depth++;
|
||||
}
|
||||
|
||||
// Leave the current stack frame.
|
||||
static void leave(void) {
|
||||
struct frame* next = current_frame;
|
||||
current_frame = next->prev;
|
||||
top_of_stack = next->depth;
|
||||
struct labels* labels = next->labels;
|
||||
free(next);
|
||||
while (labels != NULL) {
|
||||
struct labels* next_label = labels->prev;
|
||||
free(labels);
|
||||
labels = next_label;
|
||||
}
|
||||
label enter(uint32_t retc) {
|
||||
assert(stack_frame < MAX_STACK_FRAMES);
|
||||
struct stack_frame frame = { stack_depth, label_depth };
|
||||
stack_frames[stack_frame] = frame;
|
||||
stack_frame++;
|
||||
return declare(retc);
|
||||
}
|
||||
|
||||
// Allocate registers or stack space for the arguments.
|
||||
static void reserve(size_t argc) {
|
||||
top_of_stack += argc;
|
||||
void leave(var* args) {
|
||||
assert(stack_frame > 0);
|
||||
struct stack_frame frame = stack_frames[stack_frame];
|
||||
define(frame.label_depth, args);
|
||||
stack_depth = frame.depth;
|
||||
label_depth = frame.label_depth;
|
||||
}
|
||||
|
||||
static var new_var(void) {
|
||||
var var = top_of_stack;
|
||||
top_of_stack++;
|
||||
x86_inst_push_r64(RA);
|
||||
return var;
|
||||
label declare(uint32_t argc) {
|
||||
assert(label_depth < MAX_LABELS);
|
||||
struct label label = { stack_frame, argc, (ip) -1, 0, 0 };
|
||||
labels[label_depth] = label;
|
||||
return label_depth++;
|
||||
}
|
||||
|
||||
static struct storage storage(var var) {
|
||||
struct storage storage;
|
||||
if (var == top_of_stack - 1) {
|
||||
storage.type = STORE_REG;
|
||||
storage.reg = RA;
|
||||
} else {
|
||||
storage.type = STORE_STACK;
|
||||
storage.off = -var * 8 - 16;
|
||||
}
|
||||
return storage;
|
||||
}
|
||||
|
||||
static void move(var dest, var src) {
|
||||
if (dest == src) return;
|
||||
struct storage ds = storage(dest);
|
||||
struct storage ss = storage(src);
|
||||
if (ds.type == STORE_REG && ss.type == STORE_REG) {
|
||||
x86_inst_mov_r64_r64(ds.reg, ss.reg);
|
||||
} else if (ds.type == STORE_STACK && ss.type == STORE_REG) {
|
||||
x86_inst_mov_m64_r64_disp(RB, ss.reg, ds.off);
|
||||
} else if (ds.type == STORE_REG && ds.type == STORE_STACK) {
|
||||
x86_inst_mov_r64_m64_disp(ds.reg, RB, ss.off);
|
||||
} else {
|
||||
// FIXME: DI is scratch register?
|
||||
x86_inst_mov_r64_m64_disp(DI, RB, ss.off);
|
||||
x86_inst_mov_m64_r64_disp(RB, DI, ds.off);
|
||||
}
|
||||
}
|
||||
|
||||
static void exchange(var x, var y) {
|
||||
if (x == y) return;
|
||||
assert(0); // UNIMPLEMENTED
|
||||
}
|
||||
|
||||
// Restore the stack and registers to a previous frame,
|
||||
// in preparation for a jump out of the current frame.
|
||||
//
|
||||
// This involves loading spilled variables into registers,
|
||||
// restoring the stack pointer,
|
||||
// spilling variables onto the stack to make space for arguments,
|
||||
// and relocating arguments to the correct registers.
|
||||
static void restore(const struct frame* frame, size_t argc, var* args) {
|
||||
for (size_t i = 0; i < argc; i++) {
|
||||
var arg = args[i];
|
||||
size_t depth = frame->depth + i;
|
||||
if (arg == depth) continue;
|
||||
size_t conflict = (size_t) -1;
|
||||
for (size_t j = i + 1; j < argc; j++) {
|
||||
if (depth == args[j]) {
|
||||
conflict = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (conflict == (size_t) -1) {
|
||||
move(depth, args[i]);
|
||||
} else {
|
||||
// TODO: an algorithm which produces fewer exchanges
|
||||
exchange(args[conflict], args[i]);
|
||||
args[conflict] = args[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
label declare_label(size_t argc) {
|
||||
struct labels* labels = malloc(sizeof(struct labels));
|
||||
labels->prev = current_frame->labels;
|
||||
labels->info.frame = current_frame;
|
||||
labels->info.argc = argc;
|
||||
labels->info.definition = (ip) -1;
|
||||
current_frame->labels = labels;
|
||||
return &labels->info;
|
||||
}
|
||||
|
||||
void define_label(label label, var* args) {
|
||||
assert(label->frame == current_frame);
|
||||
void define(label l, var* args) {
|
||||
struct label* label = &labels[l];
|
||||
label->definition = here;
|
||||
struct fixups* fixups = label->fixups;
|
||||
while (fixups != NULL) {
|
||||
struct fixups* fixup = fixups;
|
||||
inst_jump_resolve(fixup->disp, here);
|
||||
fixups = fixup->next;
|
||||
free(fixup);
|
||||
while (label->fixupc > 0) {
|
||||
label->fixupc--;
|
||||
inst_jump_resolve(label->fixups[label->fixupc], here);
|
||||
}
|
||||
enter();
|
||||
reserve(label->argc);
|
||||
}
|
||||
|
||||
void queue_fixup(label label, ip disp) {
|
||||
struct fixups* fixup = malloc(sizeof(struct fixups));
|
||||
fixup->next = label->fixups;
|
||||
fixup->disp = disp;
|
||||
label->fixups = fixup;
|
||||
}
|
||||
|
||||
void jump(label label, var* args) {
|
||||
restore(label->frame, label->argc, args);
|
||||
if (label->definition != (ip) -1) {
|
||||
inst_jump(label->definition);
|
||||
void jump(label l, var* args) {
|
||||
struct label* label = &labels[l];
|
||||
if (label->definition == (ip) -1) {
|
||||
assert(label->fixupc < MAX_FIXUPS);
|
||||
label->fixups[label->fixupc] = inst_jump_unresolved();
|
||||
label->fixupc++;
|
||||
} else {
|
||||
ip disp = inst_jump_unresolved();
|
||||
queue_fixup(label, disp);
|
||||
inst_jump(label->definition);
|
||||
}
|
||||
leave();
|
||||
}
|
||||
|
||||
void jump_table(size_t branches, label* labels, var index, var* args) {
|
||||
|
@ -190,34 +96,59 @@ void jump_if(label label, var cond, var* args) {
|
|||
assert(0); // UNIMPLEMENTED
|
||||
}
|
||||
|
||||
|
||||
static void save(var* vars) {
|
||||
|
||||
void load_var(reg reg, var var) {
|
||||
// the stack grows downward, so the bottom of the stack, BP, points to nothing;
|
||||
// subtracting 8 causes it to point to the first variable, 0.
|
||||
// (each variable is 8 bytes.)
|
||||
x86_inst_mov_r64_m64_disp(reg, BP, -(var * 8) - 8);
|
||||
}
|
||||
|
||||
var lit(uint64_t x) {
|
||||
var var = new_var();
|
||||
struct storage stg = storage(var);
|
||||
assert(stg.type == STORE_REG);
|
||||
x86_inst_mov_r64_imm(stg.reg, x);
|
||||
return var;
|
||||
var push_var(reg reg) {
|
||||
x86_inst_push_r64(reg);
|
||||
return stack_depth++;
|
||||
}
|
||||
|
||||
void syscall(size_t argc, var* args) {
|
||||
assert(argc > 0);
|
||||
// rax already populated by top of stack
|
||||
// FIXME: this won't work forever
|
||||
// FIXME: save args in case we don't want to sysexit
|
||||
// FIXME: save other registers
|
||||
if (argc > 1)
|
||||
x86_inst_mov_r64_m64_disp(DI, RB, -args[1] * 8 - 16);
|
||||
if (argc > 2)
|
||||
x86_inst_mov_r64_m64_disp(SI, RB, -args[2] * 8 - 16);
|
||||
if (argc > 3)
|
||||
x86_inst_mov_r64_m64_disp(RD, RB, -args[3] * 8 - 16);
|
||||
var lit(uint64_t lit) {
|
||||
x86_inst_mov_r64_imm(AX, lit);
|
||||
x86_inst_push_r64(AX);
|
||||
return stack_depth++;
|
||||
}
|
||||
|
||||
var sub(var subtrahend, var minuend) {
|
||||
// TODO: use modr/m
|
||||
load_var(AX, subtrahend);
|
||||
load_var(BX, minuend);
|
||||
x86_inst_sub_r64_r64(AX, BX);
|
||||
return push_var(AX);
|
||||
}
|
||||
|
||||
// Linux system call: https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/
|
||||
var syscall(size_t argc, var* args) {
|
||||
assert(argc > 0 && argc <= 7);
|
||||
switch(argc) {
|
||||
case 7:
|
||||
load_var(R9, args[6]);
|
||||
__attribute__((fallthrough));
|
||||
case 6:
|
||||
load_var(R8, args[5]);
|
||||
__attribute__ ((fallthrough));
|
||||
case 5:
|
||||
load_var(R10, args[4]);
|
||||
__attribute__ ((fallthrough));
|
||||
case 4:
|
||||
load_var(DX, args[3]);
|
||||
__attribute__ ((fallthrough));
|
||||
case 3:
|
||||
load_var(SI, args[2]);
|
||||
__attribute__ ((fallthrough));
|
||||
case 2:
|
||||
load_var(DI, args[1]);
|
||||
__attribute__ ((fallthrough));
|
||||
case 1:
|
||||
// the system call number, not an argument
|
||||
load_var(AX, args[0]);
|
||||
}
|
||||
// NOTE: syscall clobbers rcx and r11.
|
||||
x86_inst_syscall();
|
||||
}
|
||||
|
||||
void init(void) {
|
||||
x86_inst_mov_r64_r64(RB, SP);
|
||||
return push_var(AX);
|
||||
}
|
||||
|
|
74
src/ir.h
74
src/ir.h
|
@ -1,49 +1,71 @@
|
|||
#ifndef _ASSEMBLER_H
|
||||
#define _ASSEMBLER_H
|
||||
#ifndef _IR_H
|
||||
#define _IR_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef size_t var;
|
||||
typedef struct label_info* label;
|
||||
typedef uint32_t var;
|
||||
typedef uint32_t label;
|
||||
|
||||
/// Declare a new label.
|
||||
///
|
||||
/// A label is the destination of a jump,
|
||||
/// located with a fixed stack context and fixed argument types.
|
||||
label declare_label(size_t args);
|
||||
/// Call this at the beginning of execution.
|
||||
/// It performs initialization and stuff.
|
||||
void init(var* argc, var* argv, var* env);
|
||||
|
||||
/// Define a previously-declared label.
|
||||
/// Enter a new block.
|
||||
///
|
||||
/// The label must be defined in the same stack context
|
||||
/// which it was declared in.
|
||||
/// All labels defined in this block will have access to all variables
|
||||
/// which are in scope as of calling `enter`. You will be able to jump
|
||||
/// to any label which is defined in this block from here
|
||||
/// to the symmetric `leave`.
|
||||
///
|
||||
/// The definition of the label is... here, which is to say
|
||||
/// whatever code you proceed to generate after this.
|
||||
///
|
||||
/// A label is implicitly terminated by an unconditional jump or exit.
|
||||
/// However, it may exit at multiple locations via unconditional jumps.
|
||||
void define_label(label label, var* args);
|
||||
/// This also generates a new label corresponding with the end of the block,
|
||||
/// which will be automatically defined when you call `leave`.
|
||||
label enter(uint32_t retc);
|
||||
|
||||
/// Jump to label, unconditionally. Terminates a block.
|
||||
/// Leave a block.
|
||||
///
|
||||
/// It is only possible to jump to a label in a parent or adjacent stack frame
|
||||
/// (you can't jump *deeper* into the stack).
|
||||
/// This will restore the context to how it was when `enter` was called,
|
||||
/// plus the return values declared by the call to `enter`.
|
||||
void leave(var* args);
|
||||
|
||||
|
||||
/// Declare a new label in the innermost block.
|
||||
///
|
||||
/// This label can only be called from the block or nested blocks.
|
||||
/// This label must be called with the given number of arguments.
|
||||
label declare(uint32_t argc);
|
||||
|
||||
/// Define a label in the innermost block, automatically terminating
|
||||
/// any previous labels.
|
||||
///
|
||||
/// All variables defined prior to the beginning of this block will be in scope.
|
||||
/// The arguments associated with the label will be in scope.
|
||||
/// Variables defined *after* the beginning of the block but *prior* to this label
|
||||
/// will *not* be in scope.
|
||||
///
|
||||
/// From this label you can jump to any label in the enclosing block
|
||||
/// or any parent block.
|
||||
void define(label label, var* args);
|
||||
|
||||
/// Jump to label, unconditionally; never returns.
|
||||
void jump(label label, var* args);
|
||||
|
||||
/// Jump to label in table; never returns. Terminates a block.
|
||||
/// Jump to `index`th label in table; never returns.
|
||||
///
|
||||
/// All labels must be at the same depth and accept the same arguments.
|
||||
/// `index` must not be out of bounds.
|
||||
void jump_table(size_t branches, label* labels, var index, var* args);
|
||||
|
||||
/// Jump to label if `cond` is not zero. Does not terminate a block.
|
||||
/// Jump to label if `cond` is not zero.
|
||||
void jump_if(label label, var cond, var* args);
|
||||
|
||||
var lit(uint64_t x);
|
||||
/// Integer literal.
|
||||
var lit(uint64_t lit);
|
||||
|
||||
void syscall(size_t argc, var* args);
|
||||
/// Subtraction.
|
||||
var sub(var subtrahend, var minuend);
|
||||
|
||||
void init(void);
|
||||
/// Perform a system call.
|
||||
var syscall(size_t argc, var* args);
|
||||
|
||||
#endif
|
||||
|
|
11
src/main.c
11
src/main.c
|
@ -14,10 +14,13 @@
|
|||
#define ELF_HEADER_SIZE 0xb0
|
||||
|
||||
size_t compile(void) {
|
||||
init();
|
||||
var code = lit(42);
|
||||
var call = lit(60);
|
||||
var args[2] = { call, code };
|
||||
var argc, argv, env;
|
||||
init(&argc, &argv, &env);
|
||||
var a = lit(52);
|
||||
var b = lit(10);
|
||||
var exit_code = sub(a, b);
|
||||
var sys_exit = lit(60);
|
||||
var args[2] = { sys_exit, exit_code };
|
||||
syscall(2, args);
|
||||
return ELF_HEADER_SIZE;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,12 @@ static void x86_rexwr(reg reg) {
|
|||
emit_u8(rex);
|
||||
}
|
||||
|
||||
static void x86_rexwb(reg b) {
|
||||
uint8_t rex = REX | REX_W;
|
||||
if (b >= R8) rex |= REX_B;
|
||||
emit_u8(rex);
|
||||
}
|
||||
|
||||
static void x86_rexwrb(reg r, reg b) {
|
||||
uint8_t rex = REX | REX_W;
|
||||
if (r >= R8) rex |= REX_R;
|
||||
|
@ -59,6 +65,10 @@ static void x86_modrm32(reg r, reg b) {
|
|||
emit_u8(MODRM_RM32 | (REG(r) << 3) | REG(b));
|
||||
}
|
||||
|
||||
static void x86_modxm(uint8_t ext, reg b) {
|
||||
emit_u8(MODRM_RR | (ext << 3) | REG(b));
|
||||
}
|
||||
|
||||
static void x86_enc_opr(uint8_t op, reg reg) {
|
||||
x86_opt_rexr(reg);
|
||||
emit_u8(op + REG(reg));
|
||||
|
@ -123,6 +133,28 @@ static void x86_enc_rexw_modrmd(uint8_t op, reg r, reg b, int32_t disp) {
|
|||
}
|
||||
}
|
||||
|
||||
static void x86_enc_rexw_modxm_imm8(uint8_t op, uint8_t ext, reg m, uint8_t imm) {
|
||||
x86_rexwb(m);
|
||||
emit_u8(op);
|
||||
x86_modxm(ext, m);
|
||||
emit_u8(imm);
|
||||
}
|
||||
|
||||
static void x86_enc_rexw_modxm_imm32(uint8_t op, uint8_t ext, reg m, uint32_t imm) {
|
||||
x86_rexwb(m);
|
||||
emit_u8(op);
|
||||
x86_modxm(ext, m);
|
||||
emit_u32(imm);
|
||||
}
|
||||
|
||||
static void x86_enc_rexw_modxm_imm(uint8_t op, uint8_t ext, reg m, uint32_t imm) {
|
||||
if (imm <= UINT8_MAX) {
|
||||
x86_enc_rexw_modxm_imm8(op, ext, m, (uint8_t) imm);
|
||||
} else {
|
||||
x86_enc_rexw_modxm_imm32(op, ext, m, imm);
|
||||
}
|
||||
}
|
||||
|
||||
static void x86_enc_disp8(uint8_t op, int8_t disp) {
|
||||
uint8_t buf[2] = { op, (uint8_t) disp };
|
||||
emit(buf, 2);
|
||||
|
@ -142,7 +174,7 @@ void x86_inst_mov_r64_imm32(reg dest, uint32_t imm) {
|
|||
}
|
||||
|
||||
void x86_inst_mov_r64_imm(reg dest, uint64_t imm) {
|
||||
// TODO: xor if 0, use inc and dec, 16-bit and 8-bit immediates
|
||||
// TODO: xor if 0, use inc and dec, 16-bit and 8-bit immediates, sign extension
|
||||
if (imm <= UINT32_MAX) {
|
||||
x86_inst_mov_r64_imm32(dest, (uint32_t) imm);
|
||||
} else {
|
||||
|
@ -150,15 +182,6 @@ void x86_inst_mov_r64_imm(reg dest, uint64_t imm) {
|
|||
}
|
||||
}
|
||||
|
||||
void x86_inst_mov_r64_imms(reg dest, int64_t imm) {
|
||||
// TODO: sign-extend
|
||||
if (imm >= 0) {
|
||||
x86_inst_mov_r64_imm(dest, imm);
|
||||
} else {
|
||||
x86_inst_mov_r64_imm64(dest, (int64_t) imm);
|
||||
}
|
||||
}
|
||||
|
||||
void x86_inst_mov_r64_r64(reg dest, reg src) {
|
||||
x86_enc_rexw_modrr(0x8b, dest, src);
|
||||
}
|
||||
|
@ -220,6 +243,28 @@ void x86_inst_jmp_disp(int32_t disp) {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: special instructions for AX
|
||||
void x86_inst_sub_r64_imm8(reg dest, int8_t imm) {
|
||||
x86_enc_rexw_modxm_imm8(0x83, 5, dest, (uint8_t) imm);
|
||||
}
|
||||
|
||||
void x86_inst_sub_r64_imm32(reg dest, int32_t imm) {
|
||||
x86_enc_rexw_modxm_imm32(0x81, 5, dest, (uint32_t) imm);
|
||||
}
|
||||
|
||||
void x86_inst_sub_r64_imm(reg dest, int32_t imm) {
|
||||
x86_enc_rexw_modxm_imm(0x81, 5, dest, (uint32_t) imm);
|
||||
}
|
||||
|
||||
void x86_inst_sub_r64_r64(reg dest, reg src) {
|
||||
x86_enc_rexw_modrr(0x2B, dest, src);
|
||||
}
|
||||
|
||||
// TODO: in case of -128, choice of add vs. sub matters!
|
||||
void x86_inst_add_r64_imm8(reg dest, int8_t imm) {
|
||||
x86_enc_rexw_modxm_imm8(0x83, 0, dest, (uint8_t) imm);
|
||||
}
|
||||
|
||||
void x86_inst_syscall(void) {
|
||||
const uint8_t buf[2] = { 0x0f, 0x05 };
|
||||
emit(buf, 2);
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
// The specific register size (e.g. al/ax/eax/rax) depends on the instruction.
|
||||
// All registers are valid for all instructions; we will perform exchanges if necessary.
|
||||
typedef enum reg {
|
||||
RA = 0, // rax, eax, ax, al
|
||||
RC = 1, // rcx, ecx, cx, cl
|
||||
RD = 2, // rdx, edx, dx, dl
|
||||
RB = 3, // rbx, ebx, bx, bl
|
||||
AX = 0, // rax, eax, ax, al
|
||||
CX = 1, // rcx, ecx, cx, cl
|
||||
DX = 2, // rdx, edx, dx, dl
|
||||
BX = 3, // rbx, ebx, bx, bl
|
||||
SP = 4, // rsp, esp, sp, spl (we do not use ah)
|
||||
BP = 5, // rbp, ebp, bp, bpl (we do not use ch)
|
||||
SI = 6, // rsi, esi, si, sil (we do not use dh)
|
||||
|
@ -29,7 +29,6 @@ typedef enum reg {
|
|||
void x86_inst_mov_r64_imm64(reg dest, uint64_t imm);
|
||||
void x86_inst_mov_r64_imm32(reg dest, uint32_t imm);
|
||||
void x86_inst_mov_r64_imm(reg dest, uint64_t imm);
|
||||
void x86_inst_mov_r64_imms(reg dest, int64_t imm);
|
||||
void x86_inst_mov_r64_r64(reg dest, reg src);
|
||||
void x86_inst_mov_r64_m64(reg dest, reg src);
|
||||
void x86_inst_mov_r64_m64_disp8(reg dest, reg src, int8_t disp);
|
||||
|
@ -39,13 +38,23 @@ void x86_inst_mov_m64_r64(reg dest, reg src);
|
|||
void x86_inst_mov_m64_r64_disp8(reg dest, reg src, int8_t disp);
|
||||
void x86_inst_mov_m64_r64_disp32(reg dest, reg src, int32_t disp);
|
||||
void x86_inst_mov_m64_r64_disp(reg dest, reg src, int32_t disp);
|
||||
|
||||
void x86_inst_push_r64(reg reg);
|
||||
void x86_inst_pop_r64(reg reg);
|
||||
|
||||
#define X86_JMP_DISP8_SIZE 2
|
||||
void x86_inst_jmp_disp8(int8_t disp);
|
||||
#define X86_JMP_DISP32_SIZE 5
|
||||
void x86_inst_jmp_disp32(int32_t disp);
|
||||
void x86_inst_jmp_disp(int32_t disp);
|
||||
|
||||
void x86_inst_sub_r64_imm8(reg dest, int8_t imm);
|
||||
void x86_inst_sub_r64_imm32(reg dest, int32_t imm);
|
||||
void x86_inst_sub_r64_imm(reg dest, int32_t imm);
|
||||
void x86_inst_sub_r64_r64(reg dest, reg src);
|
||||
|
||||
void x86_inst_add_r64_imm8(reg dest, int8_t imm);
|
||||
|
||||
void x86_inst_syscall(void);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue