commit b43ef1072891fa694565d481c467d57769884946
parent 4bb3dc44855c91b7330a50bf855f3bb4930ee309
Author: Nihal Jere <nihal@nihaljere.xyz>
Date: Mon, 10 Jan 2022 20:10:12 -0600
x64: support numbered registers everywhere and remove some repetition
Diffstat:
2 files changed, 106 insertions(+), 140 deletions(-)
diff --git a/test/upper_registers.pass.nooc b/test/upper_registers.pass.nooc
@@ -0,0 +1,17 @@
+let foo proc(i64, i64, i64, i64, i64, i64, i64, i64) (i64) = proc(a i64, b i64, c i64, d i64, e i64, f i64, g i64, h i64) (out i64) {
+ out = + + + + + + + a b c d e f g h
+ return
+}
+
+let main proc() = proc() {
+ let a i64 = 1
+ let b i64 = 2
+ let c i64 = 3
+ let d i64 = 4
+ let e i64 = 5
+ let f i64 = 6
+ let g i64 = 7
+ let h i64 = 8
+ foo(a, b, c, d, e, f, g, h)
+ syscall2(60, 0)
+}+
\ No newline at end of file
diff --git a/x64.c b/x64.c
@@ -2,6 +2,7 @@
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
+#include <strings.h>
#include "nooc.h"
#include "ir.h"
@@ -209,231 +210,179 @@ mov_m32_r32(char *buf, uint64_t addr, enum reg src)
return 9;
}
+#define MOVE_FROMREG 0
+#define MOVE_TOREG 1
+
static size_t
-mov_mr64_r64(char *buf, enum reg dest, enum reg src)
+_move_between_reg_and_memaddr_in_reg(char *buf, enum reg reg, enum reg mem, uint8_t opsize, bool dir)
{
+ uint8_t rex = opsize == 8 ? REX_W : 0;
+ rex |= (reg >= 8 ? REX_R : 0) | (mem >= 8 ? REX_B : 0);
+
if (buf) {
- *(buf++) = REX_W;
- *(buf++) = 0x89;
- *(buf++) = (MOD_INDIRECT << 6) | (src << 3) | dest;
+ if (opsize == 2)
+ *(buf++) = OP_SIZE_OVERRIDE;
+
+ if (rex)
+ *(buf++) = rex;
+
+ *(buf++) = 0x88 + (opsize != 1) + 2*dir;
+
+ *(buf++) = (MOD_INDIRECT << 6) | ((reg & 7) << 3) | (mem & 7);
}
- return 3;
+ // 8 and 2 have a length of 3, but 4 and 1 have a length of 2
+ return !!rex + (opsize == 2) + 2;
}
static size_t
-mov_mr32_r32(char *buf, enum reg dest, enum reg src)
+mov_mr64_r64(char *buf, enum reg dest, enum reg src)
{
- if (buf) {
- *(buf++) = 0x89;
- *(buf++) = (MOD_INDIRECT << 6) | (src << 3) | dest;
- }
+ return _move_between_reg_and_memaddr_in_reg(buf, src, dest, 8, MOVE_FROMREG);
+}
- return 2;
+static size_t
+mov_mr32_r32(char *buf, enum reg dest, enum reg src)
+{
+ return _move_between_reg_and_memaddr_in_reg(buf, src, dest, 4, MOVE_FROMREG);
}
static size_t
mov_mr16_r16(char *buf, enum reg dest, enum reg src)
{
- if (buf) {
- *(buf++) = OP_SIZE_OVERRIDE;
- *(buf++) = 0x89;
- *(buf++) = (MOD_INDIRECT << 6) | (src << 3) | dest;
- }
-
- return 3;
+ return _move_between_reg_and_memaddr_in_reg(buf, src, dest, 2, MOVE_FROMREG);
}
static size_t
mov_mr8_r8(char *buf, enum reg dest, enum reg src)
{
- if (buf) {
- *(buf++) = 0x88;
- *(buf++) = (MOD_INDIRECT << 6) | (src << 3) | dest;
- }
-
- return 2;
+ return _move_between_reg_and_memaddr_in_reg(buf, src, dest, 1, MOVE_FROMREG);
}
static size_t
mov_r64_mr64(char *buf, enum reg dest, enum reg src)
{
- if (buf) {
- *(buf++) = REX_W;
- *(buf++) = 0x8B;
- *(buf++) = (MOD_INDIRECT << 6) | (dest << 3) | src;
- }
-
- return 3;
+ return _move_between_reg_and_memaddr_in_reg(buf, dest, src, 8, MOVE_TOREG);
}
static size_t
mov_r32_mr32(char *buf, enum reg dest, enum reg src)
{
- if (buf) {
- *(buf++) = 0x8B;
- *(buf++) = (MOD_INDIRECT << 6) | (dest << 3) | src;
- }
-
- return 2;
+ return _move_between_reg_and_memaddr_in_reg(buf, dest, src, 4, MOVE_TOREG);
}
static size_t
mov_r16_mr16(char *buf, enum reg dest, enum reg src)
{
- if (buf) {
- *(buf++) = OP_SIZE_OVERRIDE;
- *(buf++) = 0x8B;
- *(buf++) = (MOD_INDIRECT << 6) | (dest << 3) | src;
- }
-
- return 3;
+ return _move_between_reg_and_memaddr_in_reg(buf, dest, src, 2, MOVE_TOREG);
}
static size_t
mov_r8_mr8(char *buf, enum reg dest, enum reg src)
{
+ return _move_between_reg_and_memaddr_in_reg(buf, dest, src, 1, MOVE_TOREG);
+}
+
+static size_t
+_move_between_reg_and_reg(char *buf, enum reg dest, enum reg src, uint8_t opsize)
+{
+ uint8_t rex = (src >= 8 ? REX_R : 0) | (dest >= 8 ? REX_B : 0) | (opsize == 8 ? REX_W : 0);
if (buf) {
- *(buf++) = 0x8A;
- *(buf++) = (MOD_INDIRECT << 6) | (dest << 3) | src;
+ if (opsize == 2)
+ *(buf++) = OP_SIZE_OVERRIDE;
+
+ if (rex)
+ *(buf++) = rex;
+
+ *(buf++) = 0x88 + (opsize != 1);
+ *(buf++) = (MOD_DIRECT << 6) | ((src & 7) << 3) | (dest & 7);
}
- return 2;
+ return 2 + !!rex + (opsize == 2);
}
static size_t
mov_r64_r64(char *buf, enum reg dest, enum reg src)
{
- if (buf) {
- *(buf++) = REX_W | (src >= 8 ? REX_R : 0) | (dest >= 8 ? REX_B : 0);
- *(buf++) = 0x89;
- *(buf++) = (MOD_DIRECT << 6) | (src << 3) | dest;
- }
-
- return 3;
+ return _move_between_reg_and_reg(buf, dest, src, 8);
}
static size_t
mov_r32_r32(char *buf, enum reg dest, enum reg src)
{
- if (buf) {
- if (src >= 8 || dest >= 8) *(buf++) = (src >= 8 ? REX_R : 0) | (dest >= 8 ? REX_B : 0);
- *(buf++) = 0x89;
- *(buf++) = (MOD_DIRECT << 6) | (src << 3) | dest;
- }
-
- return (src >= 8 || dest >= 8) ? 3 : 2;
+ return _move_between_reg_and_reg(buf, dest, src, 4);
}
static size_t
-mov_disp8_m64_r64(char *buf, enum reg dest, int8_t disp, enum reg src)
+_move_between_reg_and_memaddr_in_reg_with_disp(char *buf, enum reg reg, enum reg mem, int8_t disp, uint8_t opsize, bool dir)
{
- assert(src != 4);
+ assert((reg & 7) != 4 && (mem & 7) != 4);
+ uint8_t rex = opsize == 8 ? REX_W : 0;
+ rex |= (reg >= 8 ? REX_R : 0) | (mem >= 8 ? REX_B : 0);
+
if (buf) {
- *(buf++) = REX_W;
- *(buf++) = 0x89;
- *(buf++) = (MOD_DISP8 << 6) | (src << 3) | dest;
+ if (opsize == 2)
+ *(buf++) = OP_SIZE_OVERRIDE;
+
+ if (rex)
+ *(buf++) = rex;
+
+ *(buf++) = 0x88 + (opsize != 1) + 2*dir;
+
+ *(buf++) = (MOD_DISP8 << 6) | ((reg & 7) << 3) | (mem & 7);
*(buf++) = disp;
}
- return 4;
+ // 8 and 2 have a length of 3, but 4 and 1 have a length of 2
+ return !!rex + (opsize == 2) + 3;
}
-// FIXME: we don't handle r8-r15 properly in most of these
static size_t
-mov_disp8_m32_r32(char *buf, enum reg dest, int8_t disp, enum reg src)
+mov_disp8_m64_r64(char *buf, enum reg dest, int8_t disp, enum reg src)
{
- assert(src != 4);
- if (buf) {
- *(buf++) = 0x89;
- *(buf++) = (MOD_DISP8 << 6) | (src << 3) | dest;
- *(buf++) = disp;
- }
+ return _move_between_reg_and_memaddr_in_reg_with_disp(buf, src, dest, disp, 8, MOVE_FROMREG);
+}
- return 3;
+static size_t
+mov_disp8_m32_r32(char *buf, enum reg dest, int8_t disp, enum reg src)
+{
+ return _move_between_reg_and_memaddr_in_reg_with_disp(buf, src, dest, disp, 4, MOVE_FROMREG);
}
-// FIXME: we don't handle r8-r15 properly in most of these
static size_t
mov_disp8_m16_r16(char *buf, enum reg dest, int8_t disp, enum reg src)
{
- assert(src != 4);
- if (buf) {
- *(buf++) = OP_SIZE_OVERRIDE;
- *(buf++) = 0x89;
- *(buf++) = (MOD_DISP8 << 6) | (src << 3) | dest;
- *(buf++) = disp;
- }
-
- return 4;
+ return _move_between_reg_and_memaddr_in_reg_with_disp(buf, src, dest, disp, 2, MOVE_FROMREG);
}
-// FIXME: we don't handle r8-r15 properly in most of these
static size_t
mov_disp8_m8_r8(char *buf, enum reg dest, int8_t disp, enum reg src)
{
- assert(src != 4);
- if (buf) {
- *(buf++) = 0x88;
- *(buf++) = (MOD_DISP8 << 6) | (src << 3) | dest;
- *(buf++) = disp;
- }
-
- return 3;
+ return _move_between_reg_and_memaddr_in_reg_with_disp(buf, src, dest, disp, 1, MOVE_FROMREG);
}
static size_t
mov_disp8_r64_m64(char *buf, enum reg dest, enum reg src, int8_t disp)
{
- assert(src != 4);
- if (buf) {
- *(buf++) = REX_W;
- *(buf++) = 0x8b;
- *(buf++) = (MOD_DISP8 << 6) | (dest << 3) | src;
- *(buf++) = disp;
- }
-
- return 4;
+ return _move_between_reg_and_memaddr_in_reg_with_disp(buf, dest, src, disp, 8, MOVE_TOREG);
}
static size_t
mov_disp8_r32_m32(char *buf, enum reg dest, enum reg src, int8_t disp)
{
- assert(src != 4);
- if (buf) {
- *(buf++) = 0x8b;
- *(buf++) = (MOD_DISP8 << 6) | (dest << 3) | src;
- *(buf++) = disp;
- }
-
- return 3;
+ return _move_between_reg_and_memaddr_in_reg_with_disp(buf, dest, src, disp, 4, MOVE_TOREG);
}
static size_t
mov_disp8_r16_m16(char *buf, enum reg dest, enum reg src, int8_t disp)
{
- assert(src != 4);
- if (buf) {
- *(buf++) = OP_SIZE_OVERRIDE;
- *(buf++) = 0x8b;
- *(buf++) = (MOD_DISP8 << 6) | (dest << 3) | src;
- *(buf++) = disp;
- }
-
- return 4;
+ return _move_between_reg_and_memaddr_in_reg_with_disp(buf, dest, src, disp, 2, MOVE_TOREG);
}
static size_t
mov_disp8_r8_m8(char *buf, enum reg dest, enum reg src, int8_t disp)
{
- assert(src != 4);
- if (buf) {
- *(buf++) = 0x8A;
- *(buf++) = (MOD_DISP8 << 6) | (dest << 3) | src;
- *(buf++) = disp;
- }
-
- return 3;
+ return _move_between_reg_and_memaddr_in_reg_with_disp(buf, dest, src, disp, 1, MOVE_TOREG);
}
static size_t
@@ -594,29 +543,28 @@ ret(char *buf)
}
static size_t
-push_r64(char *buf, enum reg reg)
+_pushpop_r64(char *buf, uint8_t ioff, enum reg reg)
{
if (buf) {
if (reg >= 8)
*(buf++) = REX_B;
- *buf = 0x50 + (reg & 7);
+ *buf = 0x50 + ioff + (reg & 7);
}
return reg >= 8 ? 2 : 1;
}
static size_t
-pop_r64(char *buf, enum reg reg)
+push_r64(char *buf, enum reg reg)
{
- if (buf) {
- if (reg >= 8)
- *(buf++) = REX_B;
-
- *buf = 0x58 + (reg & 7);
- }
+ return _pushpop_r64(buf, 0, reg);
+}
- return reg >= 8 ? 2 : 1;
+static size_t
+pop_r64(char *buf, enum reg reg)
+{
+ return _pushpop_r64(buf, 8, reg);
}
#define NEXT ins++; assert(ins <= end);
@@ -655,7 +603,7 @@ emitsyscall(char *buf, uint8_t paramcount)
}
const struct target x64_target = {
- .reserved = (1 << RSP) | (1 << RBP),
+ .reserved = (1 << RSP) | (1 << RBP) | (1 << R12) | (1 << R13),
.emitsyscall = emitsyscall
};