Add 64-bit opcodes.

This commit is contained in:
David Anderson 2014-12-15 00:59:02 -08:00
parent 8c9c1e3a6c
commit 653faba33a
4 changed files with 328 additions and 11 deletions

View File

@ -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);
}

View File

@ -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,

View File

@ -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)
{

View File

@ -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();