336 lines
9.3 KiB
C
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(); |