using namespace QV4;
using namespace JIT;
-namespace {
-inline bool isPregOrConst(IR::Expr *e)
-{
- if (IR::Temp *t = e->asTemp())
- return t->kind == IR::Temp::PhysicalRegister;
- return e->asConst() != 0;
-}
-} // anonymous namespace
-
-
#define OP(op) \
{ isel_stringIfy(op), op, 0, 0, 0 }
#define OPCONTEXT(op) \
void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target)
{
if (op != IR::OpMod
- && lhs->type == IR::DoubleType && rhs->type == IR::DoubleType
- && isPregOrConst(lhs) && isPregOrConst(rhs)) {
+ && lhs->type == IR::DoubleType && rhs->type == IR::DoubleType) {
doubleBinop(lhs, rhs, target);
return;
}
void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target)
{
- Q_ASSERT(lhs->asConst() == 0 || rhs->asConst() == 0);
- Q_ASSERT(isPregOrConst(lhs));
- Q_ASSERT(isPregOrConst(rhs));
IR::Temp *targetTemp = target->asTemp();
Assembler::FPRegisterID targetReg;
if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister)
switch (op) {
case IR::OpAdd:
- as->addDouble(as->toDoubleRegister(lhs), as->toDoubleRegister(rhs),
- targetReg);
+ if (lhs->asConst())
+ std::swap(lhs, rhs); // Y = constant + X -> Y = X + constant
+
+#if CPU(X86)
+ if (IR::Const *c = rhs->asConst()) { // Y = X + constant -> Y = X; Y += [constant-address]
+ as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
+ Assembler::Address addr = as->constantTable().loadValueAddress(c, Assembler::ScratchRegister);
+ as->addDouble(addr, targetReg);
+ break;
+ }
+ if (IR::Temp *t = rhs->asTemp()) { // Y = X + [temp-memory-address] -> Y = X; Y += [temp-memory-address]
+ if (t->kind != IR::Temp::PhysicalRegister) {
+ as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
+ as->addDouble(as->loadTempAddress(t), targetReg);
+ break;
+ }
+ }
+#endif
+ as->addDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), as->toDoubleRegister(rhs, Assembler::FPGpr1), targetReg);
break;
+
case IR::OpMul:
- as->mulDouble(as->toDoubleRegister(lhs), as->toDoubleRegister(rhs),
- targetReg);
- break;
- case IR::OpSub:
-#if CPU(X86) || CPU(X86_64)
- if (IR::Temp *rightTemp = rhs->asTemp()) {
- if (rightTemp->kind == IR::Temp::PhysicalRegister && rightTemp->index == targetReg) {
- as->moveDouble(targetReg, Assembler::FPGpr0);
+ if (lhs->asConst())
+ std::swap(lhs, rhs); // Y = constant * X -> Y = X * constant
+
+#if CPU(X86)
+ if (IR::Const *c = rhs->asConst()) { // Y = X * constant -> Y = X; Y *= [constant-address]
+ as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
+ Assembler::Address addr = as->constantTable().loadValueAddress(c, Assembler::ScratchRegister);
+ as->mulDouble(addr, targetReg);
+ break;
+ }
+ if (IR::Temp *t = rhs->asTemp()) { // Y = X * [temp-memory-address] -> Y = X; Y *= [temp-memory-address]
+ if (t->kind != IR::Temp::PhysicalRegister) {
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
- as->subDouble(Assembler::FPGpr0, targetReg);
+ as->mulDouble(as->loadTempAddress(t), targetReg);
break;
}
- } else if (rhs->asConst() && targetReg == Assembler::FPGpr0) {
- Q_ASSERT(lhs->asTemp());
- Q_ASSERT(lhs->asTemp()->kind == IR::Temp::PhysicalRegister);
+ }
+#endif
+ as->mulDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), as->toDoubleRegister(rhs, Assembler::FPGpr1), targetReg);
+ break;
+
+ case IR::OpSub:
+#if CPU(X86)
+ if (IR::Const *c = rhs->asConst()) { // Y = X - constant -> Y = X; Y -= [constant-address]
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
- Assembler::FPRegisterID reg = (Assembler::FPRegisterID) lhs->asTemp()->index;
- as->moveDouble(as->toDoubleRegister(rhs, reg), reg);
- as->subDouble(reg, targetReg);
+ Assembler::Address addr = as->constantTable().loadValueAddress(c, Assembler::ScratchRegister);
+ as->subDouble(addr, targetReg);
break;
}
+ if (IR::Temp *t = rhs->asTemp()) { // Y = X - [temp-memory-address] -> Y = X; Y -= [temp-memory-address]
+ if (t->kind != IR::Temp::PhysicalRegister) {
+ as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
+ as->subDouble(as->loadTempAddress(t), targetReg);
+ break;
+ }
+ }
#endif
+ if (rhs->asTemp() && rhs->asTemp()->kind == IR::Temp::PhysicalRegister
+ && targetTemp
+ && targetTemp->kind == IR::Temp::PhysicalRegister
+ && targetTemp->index == rhs->asTemp()->index) { // Y = X - Y -> Tmp = Y; Y = X - Tmp
+ as->moveDouble(as->toDoubleRegister(rhs, Assembler::FPGpr1), Assembler::FPGpr1);
+ as->subDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), Assembler::FPGpr1, targetReg);
+ break;
+ }
- as->subDouble(as->toDoubleRegister(lhs), as->toDoubleRegister(rhs),
- targetReg);
+ as->subDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), as->toDoubleRegister(rhs, Assembler::FPGpr1), targetReg);
break;
+
case IR::OpDiv:
-#if CPU(X86) || CPU(X86_64)
- if (IR::Temp *rightTemp = rhs->asTemp()) {
- if (rightTemp->kind == IR::Temp::PhysicalRegister && rightTemp->index == targetReg) {
- as->moveDouble(targetReg, Assembler::FPGpr0);
+#if CPU(X86)
+ if (IR::Const *c = rhs->asConst()) { // Y = X / constant -> Y = X; Y /= [constant-address]
+ as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
+ Assembler::Address addr = as->constantTable().loadValueAddress(c, Assembler::ScratchRegister);
+ as->divDouble(addr, targetReg);
+ break;
+ }
+ if (IR::Temp *t = rhs->asTemp()) { // Y = X / [temp-memory-address] -> Y = X; Y /= [temp-memory-address]
+ if (t->kind != IR::Temp::PhysicalRegister) {
as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
- as->divDouble(Assembler::FPGpr0, targetReg);
+ as->divDouble(as->loadTempAddress(t), targetReg);
break;
}
- } else if (rhs->asConst() && targetReg == Assembler::FPGpr0) {
- Q_ASSERT(lhs->asTemp());
- Q_ASSERT(lhs->asTemp()->kind == IR::Temp::PhysicalRegister);
- as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg);
- Assembler::FPRegisterID reg = (Assembler::FPRegisterID) lhs->asTemp()->index;
- as->moveDouble(as->toDoubleRegister(rhs, reg), reg);
- as->divDouble(reg, targetReg);
- break;
}
#endif
- as->divDouble(as->toDoubleRegister(lhs), as->toDoubleRegister(rhs),
- targetReg);
+
+ if (rhs->asTemp() && rhs->asTemp()->kind == IR::Temp::PhysicalRegister
+ && targetTemp
+ && targetTemp->kind == IR::Temp::PhysicalRegister
+ && targetTemp->index == rhs->asTemp()->index) { // Y = X / Y -> Tmp = Y; Y = X / Tmp
+ as->moveDouble(as->toDoubleRegister(rhs, Assembler::FPGpr1), Assembler::FPGpr1);
+ as->divDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), Assembler::FPGpr1, targetReg);
+ break;
+ }
+
+ as->divDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), as->toDoubleRegister(rhs, Assembler::FPGpr1), targetReg);
break;
+
default: {
Q_ASSERT(target->type == IR::BoolType);
Assembler::Jump trueCase = as->branchDouble(false, op, lhs, rhs);
}
if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister)
- as->storeDouble(Assembler::FPGpr0, target);
+ as->storeDouble(targetReg, target);
}
bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target)
{
Q_ASSERT(leftSource->type == IR::SInt32Type);
+ Q_ASSERT(rightSource->type == IR::SInt32Type);
+
+ switch (op) {
+ case IR::OpBitAnd:
+ case IR::OpBitOr:
+ case IR::OpBitXor:
+ case IR::OpAdd:
+ case IR::OpMul:
+ if (leftSource->asConst()) // X = Const op Y -> X = Y op Const
+ std::swap(leftSource, rightSource);
+ else if (IR::Temp *t = leftSource->asTemp()) {
+ if (t->kind != IR::Temp::PhysicalRegister) // X = [address] op Y -> X = Y op [address]
+ std::swap(leftSource, rightSource);
+ }
+ break;
+
+ case IR::OpLShift:
+ case IR::OpRShift:
+ case IR::OpURShift:
+ case IR::OpSub:
+ // handled by this method, but we can't flip operands.
+ break;
+
+ default:
+ return false; // not handled by this method, stop here.
+ }
+
+ bool inplaceOpWithAddress = false;
+
IR::Temp *targetTemp = target->asTemp();
Assembler::RegisterID targetReg = Assembler::ReturnValueRegister;
if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) {
- // We try to load leftSource into the target's register, but we can't do that if
- // the target register is the same as rightSource.
IR::Temp *rhs = rightSource->asTemp();
- if (!rhs || rhs->kind != IR::Temp::PhysicalRegister || rhs->index != targetTemp->index)
+ if (!rhs || rhs->kind != IR::Temp::PhysicalRegister || rhs->index != targetTemp->index) {
+ // We try to load leftSource into the target's register, but we can't do that if
+ // the target register is the same as rightSource.
targetReg = (Assembler::RegisterID) targetTemp->index;
+ } else if (rhs && rhs->kind == IR::Temp::PhysicalRegister && targetTemp->index == rhs->index) {
+ // However, if the target register is the same as the rightSource register, we can flip
+ // the operands for certain operations.
+ switch (op) {
+ case IR::OpBitAnd:
+ case IR::OpBitOr:
+ case IR::OpBitXor:
+ case IR::OpAdd:
+ case IR::OpMul:
+ // X = Y op X -> X = X op Y (or rephrased: X op= Y (so an in-place operation))
+ std::swap(leftSource, rightSource);
+ targetReg = (Assembler::RegisterID) targetTemp->index;
+ break;
+
+ case IR::OpLShift:
+ case IR::OpRShift:
+ case IR::OpURShift:
+ case IR::OpSub:
+ break;
+
+ default:
+ Q_UNREACHABLE();
+ return false;
+ }
+ }
+
+ // determine if we have X op= [address]
+ if (IR::Temp *lhs = leftSource->asTemp()) {
+ if (lhs->kind == IR::Temp::PhysicalRegister && lhs->index == targetTemp->index) {
+ if (IR::Temp *rhs = rightSource->asTemp()) {
+ if (rhs->kind != IR::Temp::PhysicalRegister) {
+ switch (op) {
+ case IR::OpBitAnd:
+ case IR::OpBitOr:
+ case IR::OpBitXor:
+ case IR::OpAdd:
+ case IR::OpMul:
+ break;
+ inplaceOpWithAddress = true;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
}
- switch (op) {
- case IR::OpBitAnd: {
- Q_ASSERT(rightSource->type == IR::SInt32Type);
+ if (op == IR::OpSub) {
if (rightSource->asTemp() && rightSource->asTemp()->kind == IR::Temp::PhysicalRegister
&& targetTemp
&& targetTemp->kind == IR::Temp::PhysicalRegister
&& targetTemp->index == rightSource->asTemp()->index) {
- as->and32(as->toInt32Register(leftSource, Assembler::ScratchRegister),
- (Assembler::RegisterID) targetTemp->index);
- return true;
+ // X = Y - X -> Tmp = X; X = Y; X -= Tmp
+ targetReg = (Assembler::RegisterID) targetTemp->index;
+ as->move(targetReg, Assembler::ScratchRegister);
+ as->move(as->toInt32Register(leftSource, targetReg), targetReg);
+ as->sub32(Assembler::ScratchRegister, targetReg);
+ } else {
+ as->move(as->toInt32Register(leftSource, targetReg), targetReg);
+ as->sub32(as->toInt32Register(rightSource, Assembler::ScratchRegister), targetReg);
}
-
- as->and32(as->toInt32Register(leftSource, targetReg),
- as->toInt32Register(rightSource, Assembler::ScratchRegister),
- targetReg);
as->storeInt32(targetReg, target);
- } return true;
- case IR::OpBitOr: {
- Q_ASSERT(rightSource->type == IR::SInt32Type);
- if (rightSource->asTemp() && rightSource->asTemp()->kind == IR::Temp::PhysicalRegister
- && targetTemp
- && targetTemp->kind == IR::Temp::PhysicalRegister
- && targetTemp->index == rightSource->asTemp()->index) {
- as->or32(as->toInt32Register(leftSource, Assembler::ScratchRegister),
- (Assembler::RegisterID) targetTemp->index);
+ return true;
+ }
+
+ Assembler::RegisterID l = as->toInt32Register(leftSource, targetReg);
+ if (IR::Const *c = rightSource->asConst()) { // All cases of Y = X op Const
+ Assembler::TrustedImm32 r(int(c->value));
+
+ switch (op) {
+ case IR::OpBitAnd: as->and32(r, l, targetReg); break;
+ case IR::OpBitOr: as->or32 (r, l, targetReg); break;
+ case IR::OpBitXor: as->xor32(r, l, targetReg); break;
+ case IR::OpAdd: as->add32(r, l, targetReg); break;
+ case IR::OpMul: as->mul32(r, l, targetReg); break;
+
+ case IR::OpLShift: r.m_value &= 0x1f; as->lshift32(l, r, targetReg); break;
+ case IR::OpRShift: r.m_value &= 0x1f; as->rshift32(l, r, targetReg); break;
+ case IR::OpURShift: r.m_value &= 0x1f; as->urshift32(l, r, targetReg);
+ as->storeUInt32(targetReg, target); // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations!
return true;
+
+ case IR::OpSub: // already handled before
+ default: // not handled by this method:
+ Q_UNREACHABLE();
+ return false;
}
+ } else if (inplaceOpWithAddress) { // All cases of X = X op [address-of-Y]
+ Assembler::Pointer rhsAddr = as->loadAddress(Assembler::ScratchRegister, rightSource);
+ switch (op) {
+ case IR::OpBitAnd: as->and32(rhsAddr, targetReg); break;
+ case IR::OpBitOr: as->or32 (rhsAddr, targetReg); break;
+ case IR::OpBitXor: as->xor32(rhsAddr, targetReg); break;
+ case IR::OpAdd: as->add32(rhsAddr, targetReg); break;
+ case IR::OpMul: as->mul32(rhsAddr, targetReg); break;
+ break;
- as->or32(as->toInt32Register(leftSource, targetReg),
- as->toInt32Register(rightSource, Assembler::ScratchRegister),
- targetReg);
- as->storeInt32(targetReg, target);
- } return true;
- case IR::OpBitXor: {
- Q_ASSERT(rightSource->type == IR::SInt32Type);
- if (rightSource->asTemp() && rightSource->asTemp()->kind == IR::Temp::PhysicalRegister
- && targetTemp
- && targetTemp->kind == IR::Temp::PhysicalRegister
- && targetTemp->index == rightSource->asTemp()->index) {
- as->xor32(as->toInt32Register(leftSource, Assembler::ScratchRegister),
- (Assembler::RegisterID) targetTemp->index);
- return true;
+ default: // not handled by this method:
+ Q_UNREACHABLE();
+ return false;
}
+ } else { // All cases of Z = X op Y
+ Assembler::RegisterID r = as->toInt32Register(rightSource, Assembler::ScratchRegister);
+ switch (op) {
+ case IR::OpBitAnd: as->and32(l, r, targetReg); break;
+ case IR::OpBitOr: as->or32 (l, r, targetReg); break;
+ case IR::OpBitXor: as->xor32(l, r, targetReg); break;
+ case IR::OpAdd: as->add32(l, r, targetReg); break;
+ case IR::OpMul: as->mul32(l, r, targetReg); break;
- as->xor32(as->toInt32Register(leftSource, targetReg),
- as->toInt32Register(rightSource, Assembler::ScratchRegister),
- targetReg);
- as->storeInt32(targetReg, target);
- } return true;
- case IR::OpLShift: {
- Q_ASSERT(rightSource->type == IR::SInt32Type);
-
- if (IR::Const *c = rightSource->asConst()) {
- if (int(c->value) == 0)
- as->move(as->toInt32Register(leftSource, Assembler::ReturnValueRegister), targetReg);
- else
- as->lshift32(as->toInt32Register(leftSource, Assembler::ReturnValueRegister),
- Assembler::TrustedImm32(int(c->value) & 0x1f), targetReg);
- } else {
- as->move(as->toInt32Register(rightSource, Assembler::ScratchRegister),
- Assembler::ScratchRegister);
#if CPU(ARM) || CPU(X86) || CPU(X86_64)
- // The ARM assembler will generate this for us, and Intel will do it on the CPU.
+ // The ARM assembler will generate an and with 0x1f for us, and Intel will do it on the CPU.
+
+ case IR::OpLShift: as->lshift32(l, r, targetReg); break;
+ case IR::OpRShift: as->rshift32(l, r, targetReg); break;
+ case IR::OpURShift: as->urshift32(l, r, targetReg);
+ as->storeUInt32(targetReg, target); // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations!
+ return true;
#else
+ case IR::OpLShift:
+ as->move(r, Assembler::ScratchRegister);
as->and32(Assembler::TrustedImm32(0x1f), Assembler::ScratchRegister);
-#endif
- as->lshift32(as->toInt32Register(leftSource, targetReg), Assembler::ScratchRegister, targetReg);
- }
- as->storeInt32(targetReg, target);
- } return true;
- case IR::OpRShift: {
- Q_ASSERT(rightSource->type == IR::SInt32Type);
-
- if (IR::Const *c = rightSource->asConst()) {
- if (int(c->value) == 0)
- as->move(as->toInt32Register(leftSource, Assembler::ReturnValueRegister), targetReg);
- else
- as->rshift32(as->toInt32Register(leftSource, Assembler::ReturnValueRegister),
- Assembler::TrustedImm32(int(c->value) & 0x1f), targetReg);
- } else {
- as->move(as->toInt32Register(rightSource, Assembler::ScratchRegister),
- Assembler::ScratchRegister);
-#if CPU(ARM) || CPU(X86) || CPU(X86_64)
- // The ARM assembler will generate this for us, and Intel will do it on the CPU.
-#else
+ as->lshift32(l, Assembler::ScratchRegister, targetReg);
+ break;
+
+ case IR::OpLShift:
+ as->move(r, Assembler::ScratchRegister);
as->and32(Assembler::TrustedImm32(0x1f), Assembler::ScratchRegister);
-#endif
- as->rshift32(as->toInt32Register(leftSource, targetReg), Assembler::ScratchRegister, targetReg);
- }
- as->storeInt32(targetReg, target);
- } return true;
- case IR::OpURShift:
- Q_ASSERT(rightSource->type == IR::SInt32Type);
-
- if (IR::Const *c = rightSource->asConst()) {
- if (int(c->value) == 0)
- as->move(as->toInt32Register(leftSource, Assembler::ReturnValueRegister), targetReg);
- else
- as->urshift32(as->toInt32Register(leftSource, Assembler::ReturnValueRegister),
- Assembler::TrustedImm32(int(c->value) & 0x1f), targetReg);
- } else {
- as->move(as->toInt32Register(rightSource, Assembler::ScratchRegister),
- Assembler::ScratchRegister);
-#if CPU(ARM) || CPU(X86) || CPU(X86_64)
- // The ARM assembler will generate this for us, and Intel will do it on the CPU.
-#else
+ as->rshift32(l, Assembler::ScratchRegister, targetReg);
+ break;
+
+ case IR::OpLShift:
+ as->move(r, Assembler::ScratchRegister);
as->and32(Assembler::TrustedImm32(0x1f), Assembler::ScratchRegister);
+ as->storeUInt32(targetReg, target); // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations!
+ return true;
#endif
- as->urshift32(as->toInt32Register(leftSource, targetReg), Assembler::ScratchRegister, targetReg);
- }
- as->storeUInt32(targetReg, target);
- return true;
- case IR::OpAdd: {
- Q_ASSERT(rightSource->type == IR::SInt32Type);
-
- as->add32(as->toInt32Register(leftSource, targetReg),
- as->toInt32Register(rightSource, Assembler::ScratchRegister),
- targetReg);
- as->storeInt32(targetReg, target);
- } return true;
- case IR::OpSub: {
- Q_ASSERT(rightSource->type == IR::SInt32Type);
- if (rightSource->asTemp() && rightSource->asTemp()->kind == IR::Temp::PhysicalRegister
- && targetTemp
- && targetTemp->kind == IR::Temp::PhysicalRegister
- && targetTemp->index == rightSource->asTemp()->index) {
- Assembler::RegisterID targetReg = (Assembler::RegisterID) targetTemp->index;
- as->move(targetReg, Assembler::ScratchRegister);
- as->move(as->toInt32Register(leftSource, targetReg), targetReg);
- as->sub32(Assembler::ScratchRegister, targetReg);
- as->storeInt32(targetReg, target);
- return true;
+ case IR::OpSub: // already handled before
+ default: // not handled by this method:
+ Q_UNREACHABLE();
+ return false;
}
-
- as->move(as->toInt32Register(leftSource, targetReg), targetReg);
- as->sub32(as->toInt32Register(rightSource, Assembler::ScratchRegister), targetReg);
- as->storeInt32(targetReg, target);
- } return true;
- case IR::OpMul: {
- Q_ASSERT(rightSource->type == IR::SInt32Type);
-
- as->mul32(as->toInt32Register(leftSource, targetReg),
- as->toInt32Register(rightSource, Assembler::ScratchRegister),
- targetReg);
- as->storeInt32(targetReg, target);
- } return true;
- default:
- return false;
}
+
+ as->storeInt32(targetReg, target);
+ return true;
}
static inline Assembler::FPRegisterID getFreeFPReg(IR::Expr *shouldNotOverlap, unsigned hint)