diff --git a/public/jit/x86/assembler-x86.h b/public/jit/x86/assembler-x86.h index 974851174..628079d24 100644 --- a/public/jit/x86/assembler-x86.h +++ b/public/jit/x86/assembler-x86.h @@ -430,6 +430,16 @@ class AssemblerX86 : public Assembler void shrl(const Operand &dest, uint8_t imm) { shift_imm(dest, 5, imm); } + + void sall_cl(Register dest) { + shift_cl(dest.code, 4); + } + void sall(Register dest, uint8_t imm) { + shift_imm(dest.code, 4, imm); + } + void sall(const Operand &dest, uint8_t imm) { + shift_imm(dest, 4, imm); + } void sarl_cl(Register dest) { shift_cl(dest.code, 7); } @@ -440,6 +450,19 @@ class AssemblerX86 : public Assembler shift_imm(dest, 7, imm); } + void shldl_cl(Register op2, const Operand &op1) { + emit2(0x0f, 0xa5, op2.code, op1); + } + void shldl_cl(Register op2, Register op1) { + emit2(0x0f, 0xa5, op2.code, op1.code); + } + void shrdl_cl(Register op2, const Operand &op1) { + emit2(0x0f, 0xad, op2.code, op1); + } + void shrdl_cl(Register op2, Register op1) { + emit2(0x0f, 0xad, op2.code, op1.code); + } + void cmpl(Register left, int32_t imm) { alu_imm(7, imm, Operand(left)); } @@ -520,6 +543,25 @@ class AssemblerX86 : public Assembler alu_imm(0, imm, dest); } + void adcl(const Operand &dest, Register src) { + emit1(0x11, src.code, dest); + } + void adcl(Register dest, const Operand &src) { + emit1(0x13, dest.code, src); + } + void adcl(Register dest, int32_t imm) { + alu_imm(2, imm, Operand(dest)); + } + void adcl(const Operand &dest, int32_t imm) { + alu_imm(2, imm, dest); + } + void sbbl(const Operand &dest, Register src) { + emit1(0x19, src.code, dest); + } + void sbbl(Register dest, const Operand &src) { + emit1(0x1b, dest.code, src); + } + void imull(Register dest, const Operand &src) { emit2(0x0f, 0xaf, dest.code, src); } @@ -551,6 +593,10 @@ class AssemblerX86 : public Assembler void testl(Register op1, Register op2) { emit1(0x85, op2.code, op1.code); } + void testl(Register op1, int32_t imm) { + emit1(0xf7, 0, op1.code); + writeInt32(imm); + } void set(ConditionCode cc, const Operand &dest) { emit2(0x0f, 0x90 + uint8_t(cc), 0, dest); } diff --git a/sourcepawn/include/smx/smx-v1-opcodes.h b/sourcepawn/include/smx/smx-v1-opcodes.h index c4bff4796..5237e588e 100644 --- a/sourcepawn/include/smx/smx-v1-opcodes.h +++ b/sourcepawn/include/smx/smx-v1-opcodes.h @@ -245,17 +245,20 @@ namespace sp { _(RND_F32_TO_FLOOR, "floor.f32") \ _(RND_F32_TO_CEIL, "ceil.f32") \ _(RND_F32_TO_ZERO, "rndtozero.f32") \ - _(CMP_F32, "cmp.f32") \ - _(GT_F32, "gt.f32") \ - _(GE_F32, "ge.f32") \ - _(LT_F32, "lt.f32") \ - _(LE_F32, "le.f32") \ - _(NE_F32, "ne.f32") \ - _(EQ_F32, "eq.f32") \ - _(NOT_F32, "not.f32") \ - _(AND_C, "and.c") \ - _(ZEX_PRI, "zex.pri") \ - _(ZEX_ALT, "zex.alt") + _(CMP_F32, "cmp.f32") \ + _(GT_F32, "gt.f32") \ + _(GE_F32, "ge.f32") \ + _(LT_F32, "lt.f32") \ + _(LE_F32, "le.f32") \ + _(NE_F32, "ne.f32") \ + _(EQ_F32, "eq.f32") \ + _(NOT_F32, "not.f32") \ + _(AND_C, "and.c") \ + _(ZEX_PRI, "zex.pri") \ + _(ZEX_ALT, "zex.alt") \ + _(PREFIX_I64, "prefix.i64") \ + _(SMODULO, "smod") \ + _(UMODULO, "umod") enum OPCODE { #define _(op, text) OP_##op, diff --git a/sourcepawn/jit/x86/jit_x86.cpp b/sourcepawn/jit/x86/jit_x86.cpp index 9f212f3c0..98a402cac 100644 --- a/sourcepawn/jit/x86/jit_x86.cpp +++ b/sourcepawn/jit/x86/jit_x86.cpp @@ -1463,6 +1463,9 @@ Compiler::emitOp(OPCODE op) break; } + case OP_PREFIX_I64: + return emit64BitOp((OPCODE)readCell()); + case OP_NOP: break; @@ -1474,6 +1477,270 @@ Compiler::emitOp(OPCODE op) return true; } +static void +div_i64_impl(int64_t *left, int64_t *right, int64_t *out) +{ + *out = *left / *right; +} + +#define DEFINE_CXX_I64_IMPL(name, type, op) \ + static void \ + name##_##type##_impl(type *left, type *right, type *out) \ + { \ + *out = *left op *right; \ + } + +DEFINE_CXX_I64_IMPL(div, int64_t, /); +DEFINE_CXX_I64_IMPL(div, uint64_t, /); +DEFINE_CXX_I64_IMPL(mod, int64_t, %); +DEFINE_CXX_I64_IMPL(mod, uint64_t, %); + +#define DEFINE_CXX_B64_IMPL(name, type, op) \ + static int \ + cmp_##type##_##name(type *left, type *right) \ + { \ + return left op right; \ + } + +DEFINE_CXX_B64_IMPL(ge, int64_t, >=); +DEFINE_CXX_B64_IMPL(gt, int64_t, >); +DEFINE_CXX_B64_IMPL(lt, int64_t, <); +DEFINE_CXX_B64_IMPL(le, int64_t, <=); +DEFINE_CXX_B64_IMPL(ge, uint64_t, >=); +DEFINE_CXX_B64_IMPL(gt, uint64_t, >); +DEFINE_CXX_B64_IMPL(lt, uint64_t, <); +DEFINE_CXX_B64_IMPL(le, uint64_t, <=); + +bool +Compiler::emit64BitOp(OPCODE op) +{ + // Binary ops: + // stk+12 = left, high + // stk+8 = left, low + // stk+4 = right, high + // stk+0 = right, low + // + // pri, alt may be used as scratch. + switch (op) { + case OP_ADD: + __ movl(eax, Operand(stk, 0)); + __ movl(edx, Operand(stk, 4)); + __ addl(Operand(stk, 8), eax); + __ adcl(Operand(stk, 12), edx); + __ addl(stk, 8); + break; + + case OP_SUB: + __ movl(eax, Operand(stk, 0)); + __ movl(edx, Operand(stk, 4)); + __ subl(Operand(stk, 8), eax); + __ sbbl(Operand(stk, 12), edx); + __ addl(stk, 8); + break; + + case OP_SMUL: + case OP_UMUL: + { + // Perform the multiply in three steps, then add the results. + const Register right_lo = edx; + const Register right_hi = ebx; + const Register left_lo = eax; + const Register left_hi = ecx; + __ push(ebx); + __ movl(right_lo, Operand(stk, 0)); + __ movl(right_hi, Operand(stk, 4)); + __ movl(left_lo, Operand(stk, 8)); + __ movl(left_hi, Operand(stk, 12)); + __ imull(right_hi, left_lo); // eax*ebx -> ebx + __ imull(left_hi, right_lo); // edx*ecx -> ecx + __ mul(right_lo); // eax*edx -> eax:edx + __ addl(ecx, ebx); // ecx += ebx + __ addl(edx, ecx); // edx += ecx + __ pop(ebx); + __ addl(stk, 8); + break; + } + + case OP_SDIV: + case OP_UDIV: + case OP_UMODULO: + case OP_SMODULO: + __ cmpl(Operand(stk, 0), 0); + __ j(zero, &error_divide_by_zero_); + __ cmpl(Operand(stk, 4), 0); + __ j(zero, &error_divide_by_zero_); + __ lea(eax, Operand(stk, 0)); + __ lea(edx, Operand(stk, 8)); + __ push(edx); + __ push(eax); + __ push(edx); + if (op == OP_SDIV) + __ call(ExternalAddress((void *)div_int64_t_impl)); + else if (op == OP_UDIV) + __ call(ExternalAddress((void *)div_uint64_t_impl)); + else if (op == OP_SMODULO) + __ call(ExternalAddress((void *)mod_int64_t_impl)); + else if (op == OP_UMODULO) + __ call(ExternalAddress((void *)mod_uint64_t_impl)); + __ addl(esp, 12); + __ addl(stk, 8); + break; + + case OP_AND: + __ movl(eax, Operand(stk, 0)); + __ movl(edx, Operand(stk, 4)); + __ andl(Operand(stk, 8), eax); + __ andl(Operand(stk, 12), edx); + __ addl(stk, 8); + break; + + case OP_OR: + __ movl(eax, Operand(stk, 0)); + __ movl(edx, Operand(stk, 4)); + __ orl(Operand(stk, 8), eax); + __ orl(Operand(stk, 12), edx); + __ addl(stk, 8); + break; + + case OP_XOR: + __ movl(eax, Operand(stk, 0)); + __ movl(edx, Operand(stk, 4)); + __ xorl(Operand(stk, 8), eax); + __ xorl(Operand(stk, 12), edx); + __ addl(stk, 8); + break; + + case OP_SGRTR: + case OP_SLESS: + case OP_LESS: + case OP_GRTR: + case OP_LEQ: + case OP_SLEQ: + case OP_GEQ: + case OP_SGEQ: + __ lea(eax, Operand(stk, 0)); + __ lea(edx, Operand(stk, 8)); + __ push(edx); + __ push(eax); + switch (op) { + case OP_SGRTR: + __ call(ExternalAddress((void *)cmp_int64_t_gt)); + break; + case OP_GRTR: + __ call(ExternalAddress((void *)cmp_uint64_t_gt)); + break; + case OP_SLESS: + __ call(ExternalAddress((void *)cmp_int64_t_lt)); + break; + case OP_LESS: + __ call(ExternalAddress((void *)cmp_uint64_t_lt)); + break; + case OP_SLEQ: + __ call(ExternalAddress((void *)cmp_int64_t_le)); + break; + case OP_LEQ: + __ call(ExternalAddress((void *)cmp_uint64_t_le)); + break; + case OP_SGEQ: + __ call(ExternalAddress((void *)cmp_int64_t_ge)); + break; + case OP_GEQ: + __ call(ExternalAddress((void *)cmp_uint64_t_ge)); + break; + } + __ addl(esp, 8); + __ addl(stk, 16); + break; + + case OP_EQ: + case OP_NEQ: + { + Label f, t; + __ movl(eax, Operand(stk, 0)); + __ movl(edx, Operand(stk, 4)); + __ cmpl(Operand(stk, 8), eax); + __ j((op == OP_EQ) ? not_equal : equal, &f); + __ cmpl(Operand(stk, 12), edx); + __ j((op == OP_EQ) ? not_equal : equal, &f); + __ movl(pri, 1); + __ jmp(&t); + __ bind(&f); + __ xorl(pri, pri); + __ bind(&t); + break; + } + + // shift is in pri + case OP_SHL: + { + __ movl(ecx, pri); + __ movl(eax, Operand(stk, 0)); + __ movl(edx, Operand(stk, 4)); + Label done; + __ shldl_cl(edx, eax); // :TODO: right order? + __ sall_cl(eax); + __ testl(eax, 0x20); + __ j(zero, &done); + __ movl(edx, eax); + __ xorl(eax, eax); + __ movl(eax, edx); + __ xorl(edx, edx); + __ bind(&done); + __ movl(Operand(stk, 0), eax); + __ movl(Operand(stk, 4), edx); + break; + } + + case OP_SHR: + case OP_SSHR: + { + __ movl(ecx, pri); + __ movl(eax, Operand(stk, 0)); + __ movl(edx, Operand(stk, 4)); + Label done; + __ shrdl_cl(eax, edx); // :TODO: right order? + if (op == OP_SHR) + __ shrl_cl(edx); + else if (op == OP_SSHR) + __ sarl_cl(edx); + __ testl(eax, 0x20); + __ j(zero, &done); + __ movl(eax, edx); + __ xorl(edx, edx); + __ bind(&done); + __ movl(Operand(stk, 0), eax); + __ movl(Operand(stk, 4), edx); + break; + } + + case OP_NEG: + __ negl(Operand(stk, 0)); + __ adcl(Operand(stk, 4), 0); + __ negl(Operand(stk, 4)); + break; + + case OP_INVERT: + __ notl(Operand(stk, 0)); + __ notl(Operand(stk, 4)); + break; + + // i64 -> bool + case OP_NOT: + __ movl(eax, Operand(stk, 0)); + __ orl(eax, Operand(stk, 4)); + __ testl(eax, eax); + __ movl(eax, 0); + __ set(zero, r8_al); + __ addl(stk, 8); + break; + + default: + error_ = SP_ERROR_INVALID_INSTRUCTION; + return false; + } + return true; +} + Label * Compiler::labelAt(size_t offset) { diff --git a/sourcepawn/jit/x86/jit_x86.h b/sourcepawn/jit/x86/jit_x86.h index daaec0f22..b5cf4ac20 100644 --- a/sourcepawn/jit/x86/jit_x86.h +++ b/sourcepawn/jit/x86/jit_x86.h @@ -106,6 +106,7 @@ class Compiler void emitErrorPath(Label *dest, int code); void emitErrorPaths(); void emitFloatCmp(ConditionCode cc); + bool emit64BitOp(OPCODE op); ExternalAddress cipAddr() { sp_context_t *ctx = rt_->GetBaseContext()->GetCtx();