#pragma once #include #include"ntc.h" #include #include #include #include"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_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_GPR (HWR_EAX | HWR_EBX | HWR_ECX | HWR_EDX) #define HWR_IND (HWR_EDI | HWR_ESI) #define HWR_ALL (HWR_GPR | HWR_IND) #define MAX_REGS_PER_CLASS 12 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 extern RegisterClass REG_CLASSES[4]; // 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_up(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; } } // 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) { 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) { 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) { 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; 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 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 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; }