nctref/src/x86/arch.h

336 lines
9.3 KiB
C

#pragma once
#include<string.h>
#include"ntc.h"
#include<assert.h>
#include<stdbool.h>
#include<stdlib.h>
#include"ast/ast.h"
#define HWR_AL 1
#define HWR_AH 2
#define HWR_BL 4
#define HWR_BH 8
#define HWR_CL 16
#define HWR_CH 32
#define HWR_DL 64
#define HWR_DH 128
#define HWR_DI 256
#define HWR_SI 512
#define HWR_BP 1024
#define HWR_DS 2048
#define HWR_ES 4096
#define HWR_FS 8192
#define HWR_GS 16384
#define HWR_AX (HWR_AL | HWR_AH)
#define HWR_BX (HWR_BL | HWR_BH)
#define HWR_CX (HWR_CL | HWR_CH)
#define HWR_DX (HWR_DL | HWR_DH)
#define HWR_EAX HWR_AX
#define HWR_EBX HWR_BX
#define HWR_ECX HWR_CX
#define HWR_EDX HWR_DX
#define HWR_EDI HWR_DI
#define HWR_ESI HWR_SI
#define HWR_EBP HWR_BP
#define HWR_GPR (HWR_EAX | HWR_EBX | HWR_ECX | HWR_EDX | HWR_EDI | HWR_ESI | HWR_EBP)
#define HWR_IND (HWR_EDI | HWR_ESI)
#define HWR_ALL (HWR_GPR | HWR_IND)
#define HWR_SEGREGS (HWR_DS | HWR_ES | HWR_FS | HWR_GS)
#define MAX_REGS_PER_CLASS 16
typedef struct RegisterClass {
uint32_t rMask;
uint16_t w;
uint16_t p;
uint32_t rs[MAX_REGS_PER_CLASS];
const char *rsN[MAX_REGS_PER_CLASS];
uint8_t rsS[MAX_REGS_PER_CLASS];
} RegisterClass;
#define REG_CLASS_8 0
#define REG_CLASS_NOT_8 1
#define REG_CLASS_16_32 2
#define REG_CLASS_IND 3
#define REG_CLASS_IA16_PTRS 4
#define REG_CLASS_IA16_USEABLE 5
#define REG_CLASS_DATASEGS 6
extern RegisterClass REG_CLASSES[];
typedef enum {
I086 = 0,
I186 = 1,
I286 = 2,
I386 = 3,
IUNKNOWN86 = 255,
} X86Target;
static inline X86Target x86_target() {
const char *str = ntc_get_arg("target");
if(!str) return I386;
if(!strcmp(str, "086")) {
return I086;
}
if(!strcmp(str, "186")) {
return I186;
}
if(!strcmp(str, "286")) {
return I286;
}
if(!strcmp(str, "386")) {
return I386;
}
return IUNKNOWN86;
}
static inline bool x86_ia16() {
return x86_target() < I386;
}
static inline size_t x86_max_gpr_size() {
switch(x86_target()) {
case I086:
case I186:
case I286:
return 2;
case I386:
return 4;
default: abort();
}
}
static inline bool x86_imul_supported() {
return x86_target() >= I186;
}
static inline bool x86_is_marked_ptr(ScopeItem *si) {
return si->data.var.preclassed && si->data.var.registerClass == REG_CLASS_IA16_PTRS;
}
// Can expression be expressed as a single x86 operand?
#define XOP_NOT_XOP 0
#define XOP_NOT_MEM 1
#define XOP_MEM 2
static inline int is_xop(AST *e) {
if(e->nodeKind == AST_EXPR_CAST && e->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && e->exprCast.to->type == TYPE_TYPE_POINTER) {
e = e->exprCast.what;
}
if(e->nodeKind == AST_EXPR_PRIMITIVE) {
return XOP_NOT_MEM;
} else if(e->nodeKind == AST_EXPR_VAR) {
return e->exprVar.thing->kind == SCOPEITEM_VAR ? XOP_NOT_MEM : XOP_MEM;
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF && e->exprUnOp.operand->nodeKind == AST_EXPR_BINARY_OP && e->exprUnOp.operand->exprBinOp.operator == BINOP_ADD && is_xop(e->exprUnOp.operand->exprBinOp.operands[0]) == XOP_NOT_MEM && is_xop(e->exprUnOp.operand->exprBinOp.operands[1]) == XOP_NOT_MEM) {
return XOP_MEM;
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_REF && e->exprUnOp.operand->nodeKind == AST_EXPR_VAR && e->exprUnOp.operand->exprVar.thing->kind == SCOPEITEM_SYMBOL) {
return XOP_NOT_MEM;
} else if(e->nodeKind == AST_EXPR_UNARY_OP && e->exprUnOp.operator == UNOP_DEREF) {
AST *c = e->exprUnOp.operand;
if(c->nodeKind == AST_EXPR_CAST && c->exprCast.what->expression.type->type == TYPE_TYPE_POINTER && c->exprCast.to->type == TYPE_TYPE_POINTER) {
c = c->exprCast.what;
}
if(c->nodeKind == AST_EXPR_VAR && c->exprVar.thing->kind == SCOPEITEM_VAR) {
if(x86_ia16() && !x86_is_marked_ptr(c->exprVar.thing)) {
// In IA-16, pointers MUST be preclassed to REG_CLASS_IA16_PTRS
return XOP_NOT_XOP;
}
return XOP_MEM;
} else if(
(c->nodeKind == AST_EXPR_BINARY_OP && c->exprBinOp.operator == BINOP_ADD && c->exprBinOp.operands[0]->nodeKind == AST_EXPR_UNARY_OP && c->exprBinOp.operands[0]->exprUnOp.operator == UNOP_REF && c->exprBinOp.operands[0]->exprUnOp.operand->nodeKind == AST_EXPR_VAR) ||
(c->nodeKind == AST_EXPR_BINARY_OP && c->exprBinOp.operator == BINOP_ADD && c->exprBinOp.operands[0]->nodeKind == AST_EXPR_VAR)) {
if(c->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) {
return XOP_MEM;
} else if(c->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR) {
if(x86_ia16() && !x86_is_marked_ptr(c->exprBinOp.operands[1]->exprVar.thing)) {
// In IA-16, pointers MUST be preclassed to REG_CLASS_IA16_PTRS
return XOP_NOT_XOP;
}
return XOP_MEM;
} else if(c->exprBinOp.operands[1]->nodeKind == AST_EXPR_BINARY_OP && c->exprBinOp.operands[1]->exprBinOp.operator == BINOP_MUL && c->exprBinOp.operands[1]->exprBinOp.operands[0]->nodeKind == AST_EXPR_PRIMITIVE && c->exprBinOp.operands[1]->exprBinOp.operands[1]->nodeKind == AST_EXPR_VAR) {
if(x86_ia16() && !x86_is_marked_ptr(c->exprBinOp.operands[1]->exprBinOp.operands[1]->exprVar.thing)) {
// In IA-16, pointers MUST be preclassed to REG_CLASS_IA16_PTRS
return XOP_NOT_XOP;
}
int scale = c->exprBinOp.operands[1]->exprBinOp.operands[0]->exprPrim.val;
if(scale == 1 || scale == 2 || scale == 4 || scale == 8) {
return XOP_MEM;
}
}
}
} else if(e->nodeKind == AST_EXPR_STACK_POINTER) {
return XOP_NOT_MEM;
}
return XOP_NOT_XOP;
}
typedef enum {
NOT_AT_ALL_IT,
DEST_IS_BAD_XOP,
SRC0_IS_BAD_XOP,
SRC1_IS_BAD_XOP,
DEST_NOT_PRECOLORED,
SRC0_NOT_PRECOLORED,
SRC1_NOT_PRECOLORED,
DEST_NOT_SRC0,
MISSING_MULHI_FOR_MUL,
GUCCI,
} WhysItBad_Huh;
static inline WhysItBad_Huh x86_test_mul_matching_mulhi(AST *mulhi, AST *mul) {
assert(mulhi->statement.next == mul);
if(mulhi->stmtAssign.to->nodeKind != AST_EXPR_BINARY_OP) {
return NOT_AT_ALL_IT;
}
if(mulhi->stmtAssign.to->exprBinOp.operator != BINOP_MULHI) {
return NOT_AT_ALL_IT;
}
if(!ast_expression_equal(mulhi->stmtAssign.to->exprBinOp.operands[0], mul->stmtAssign.to->exprBinOp.operands[0])) {
return NOT_AT_ALL_IT;
}
if(!ast_expression_equal(mulhi->stmtAssign.to->exprBinOp.operands[1], mul->stmtAssign.to->exprBinOp.operands[1])) {
return NOT_AT_ALL_IT;
}
if(is_xop(mulhi->stmtAssign.what) != XOP_NOT_MEM) {
return DEST_IS_BAD_XOP;
}
return GUCCI;
}
static inline WhysItBad_Huh x86_test_mul(AST *stmtPrev, AST *stmt) {
if(stmt->nodeKind != AST_STMT_ASSIGN) {
return NOT_AT_ALL_IT;
}
if(stmt->stmtAssign.to->nodeKind != AST_EXPR_BINARY_OP) {
return NOT_AT_ALL_IT;
}
if(stmt->stmtAssign.to->exprBinOp.operator != BINOP_MUL) {
return NOT_AT_ALL_IT;
}
if(stmt->stmtAssign.what->nodeKind != AST_EXPR_VAR || stmt->stmtAssign.what->exprVar.thing->kind != SCOPEITEM_VAR) {
return DEST_IS_BAD_XOP;
}
if(x86_imul_supported()) {
return GUCCI;
} else if(stmt->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE) {
return SRC1_IS_BAD_XOP;
}
if(is_xop(stmt->stmtAssign.to->exprBinOp.operands[1]) == XOP_NOT_XOP) {
return SRC1_IS_BAD_XOP;
}
/*if(stmt->stmtAssign.to->exprBinOp.operands[1]->nodeKind == AST_EXPR_PRIMITIVE && x86_get_target() < I186) {
return SRC1_IS_BAD_XOP;
}*/
if(!ast_expression_equal(stmt->stmtAssign.what, stmt->stmtAssign.to->exprBinOp.operands[0])) {
return DEST_NOT_SRC0;
}
if(!stmt->stmtAssign.what->exprVar.thing->data.var.precolored) {
return DEST_NOT_PRECOLORED;
}
if(x86_test_mul_matching_mulhi(stmtPrev, stmt) != GUCCI) {
return MISSING_MULHI_FOR_MUL;
}
return GUCCI;
}
static inline void arch_add_hidden_variables(Scope *scope) {
/*if(!scope_find(scope, "@seg_ds")) {
ScopeItem *si = calloc(1, sizeof(*si));
si->type = primitive_parse("u16");
si->kind = SCOPEITEM_VAR;
si->data.var.preclassed = true;
si->data.var.registerClass = REG_CLASS_DATASEGS;
si->data.var.precolored = true;
si->data.var.color = 0;
scope_set(scope, "@seg_ds", si);
}*/
}
static inline bool x86_is_lea(AST *s) {
return !x86_ia16() && s->nodeKind == AST_STMT_ASSIGN && s->stmtAssign.to->nodeKind == AST_EXPR_BINARY_OP && s->stmtAssign.to->exprBinOp.operator == BINOP_ADD && is_xop(s->stmtAssign.what) == XOP_NOT_MEM && is_xop(s->stmtAssign.to->exprBinOp.operands[0]) == XOP_NOT_MEM && is_xop(s->stmtAssign.to->exprBinOp.operands[1]) == XOP_NOT_MEM;
}
static inline int arch_sp_size() {
return x86_max_gpr_size();
}
static inline int arch_gpr_size() {
return x86_max_gpr_size();
}
// lol
static inline bool is_reg_b(int cls, int res) {
const char *name = REG_CLASSES[cls].rsN[res];
return !strcmp(name, "bl") || !strcmp(name, "bh") || !!strstr(name, "bx");
}
static inline bool is_reg_di(int cls, int res) {
const char *name = REG_CLASSES[cls].rsN[res];
return !!strstr(name, "di");
}
static inline bool is_reg_si(int cls, int res) {
const char *name = REG_CLASSES[cls].rsN[res];
return !!strstr(name, "si");
}
static inline void reg_cast_to_gpr(int *cls, int *res) {
const char *name = REG_CLASSES[*cls].rsN[*res];
if(strstr(name, "a")) {
*cls = REG_CLASS_16_32;
*res = 1;
} else if(strstr(name, "b")) {
*cls = REG_CLASS_16_32;
*res = 3;
} else if(strstr(name, "c")) {
*cls = REG_CLASS_16_32;
*res = 5;
} else if(strstr(name, "dl") || strstr(name, "dh") || strstr(name, "dx")) {
*cls = REG_CLASS_16_32;
*res = 7;
} else if(strstr(name, "di")) {
*cls = REG_CLASS_16_32;
*res = 9;
} else if(strstr(name, "si")) {
*cls = REG_CLASS_16_32;
*res = 11;
}
if(x86_ia16()) {
(*res)--;
}
}
void arch_init();