// Try to load the source expression into the destination FP register. This assumes that two // general purpose (integer) registers are available: the ScratchRegister and the // ReturnValueRegister. It returns a Jump if no conversion can be performed. Assembler::Jump Assembler::genTryDoubleConversion(IR::Expr *src, Assembler::FPRegisterID dest) { switch (src->type) { case IR::DoubleType: moveDouble(toDoubleRegister(src, dest), dest); return Assembler::Jump(); case IR::SInt32Type: convertInt32ToDouble(toInt32Register(src, Assembler::ScratchRegister), dest); return Assembler::Jump(); case IR::UInt32Type: convertUInt32ToDouble(toUInt32Register(src, Assembler::ScratchRegister), dest, Assembler::ReturnValueRegister); return Assembler::Jump(); case IR::NullType: case IR::UndefinedType: case IR::BoolType: // TODO? case IR::StringType: return jump(); default: break; } Q_ASSERT(src->asTemp() || src->asArgLocal()); // It's not a number type, so it cannot be in a register. Q_ASSERT(src->asArgLocal() || src->asTemp()->kind != IR::Temp::PhysicalRegister || src->type == IR::BoolType); Assembler::Pointer tagAddr = loadAddress(Assembler::ScratchRegister, src); tagAddr.offset += 4; load32(tagAddr, Assembler::ScratchRegister); // check if it's an int32: Assembler::Jump isNoInt = branch32(Assembler::NotEqual, Assembler::ScratchRegister, Assembler::TrustedImm32(Value::_Integer_Type)); convertInt32ToDouble(toInt32Register(src, Assembler::ScratchRegister), dest); Assembler::Jump intDone = jump(); // not an int, check if it's a double: isNoInt.link(this); #if QT_POINTER_SIZE == 8 and32(Assembler::TrustedImm32(Value::IsDouble_Mask), Assembler::ScratchRegister); Assembler::Jump isNoDbl = branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(0)); #else and32(Assembler::TrustedImm32(Value::NotDouble_Mask), Assembler::ScratchRegister); Assembler::Jump isNoDbl = branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(Value::NotDouble_Mask)); #endif toDoubleRegister(src, dest); intDone.link(this); return isNoDbl; }
void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Temp *target) { if (op != IR::OpMod && lhs->type == IR::DoubleType && rhs->type == IR::DoubleType && isPregOrConst(lhs) && isPregOrConst(rhs)) { doubleBinop(lhs, rhs, target); return; } if (lhs->type == IR::SInt32Type && rhs->type == IR::SInt32Type) { if (int32Binop(lhs, rhs, target)) return; } Assembler::Jump done; if (lhs->type != IR::StringType && rhs->type != IR::StringType) done = genInlineBinop(lhs, rhs, target); // TODO: inline var===null and var!==null Binop::OpInfo info = Binop::operation(op); if (op == IR::OpAdd && (lhs->type == IR::StringType || rhs->type == IR::StringType)) { const Binop::OpInfo stringAdd = OPCONTEXT(Runtime::addString); info = stringAdd; } if (info.fallbackImplementation) { as->generateFunctionCallImp(target, info.name, info.fallbackImplementation, Assembler::PointerToValue(lhs), Assembler::PointerToValue(rhs)); } else if (info.contextImplementation) { as->generateFunctionCallImp(target, info.name, info.contextImplementation, Assembler::ContextRegister, Assembler::PointerToValue(lhs), Assembler::PointerToValue(rhs)); } else { Q_ASSERT(!"unreachable"); } if (done.isSet()) done.link(as); }
Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Temp *target) { Assembler::Jump done; // Try preventing a call for a few common binary operations. This is used in two cases: // - no register allocation was performed (not available for the platform, or the IR was // not transformed into SSA) // - type inference found that either or both operands can be of non-number type, and the // register allocator will have prepared for a call (meaning: all registers that do not // hold operands are spilled to the stack, which makes them available here) // Note: FPGPr0 can still not be used, because uint32->double conversion uses it as a scratch // register. switch (op) { case IR::OpAdd: { Assembler::FPRegisterID lReg = getFreeFPReg(rightSource, 2); Assembler::FPRegisterID rReg = getFreeFPReg(leftSource, 4); Assembler::Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); Assembler::Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); as->addDouble(rReg, lReg); as->storeDouble(lReg, target); done = as->jump(); if (leftIsNoDbl.isSet()) leftIsNoDbl.link(as); if (rightIsNoDbl.isSet()) rightIsNoDbl.link(as); } break; case IR::OpMul: { Assembler::FPRegisterID lReg = getFreeFPReg(rightSource, 2); Assembler::FPRegisterID rReg = getFreeFPReg(leftSource, 4); Assembler::Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); Assembler::Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); as->mulDouble(rReg, lReg); as->storeDouble(lReg, target); done = as->jump(); if (leftIsNoDbl.isSet()) leftIsNoDbl.link(as); if (rightIsNoDbl.isSet()) rightIsNoDbl.link(as); } break; case IR::OpSub: { Assembler::FPRegisterID lReg = getFreeFPReg(rightSource, 2); Assembler::FPRegisterID rReg = getFreeFPReg(leftSource, 4); Assembler::Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); Assembler::Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); as->subDouble(rReg, lReg); as->storeDouble(lReg, target); done = as->jump(); if (leftIsNoDbl.isSet()) leftIsNoDbl.link(as); if (rightIsNoDbl.isSet()) rightIsNoDbl.link(as); } break; case IR::OpDiv: { Assembler::FPRegisterID lReg = getFreeFPReg(rightSource, 2); Assembler::FPRegisterID rReg = getFreeFPReg(leftSource, 4); Assembler::Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); Assembler::Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); as->divDouble(rReg, lReg); as->storeDouble(lReg, target); done = as->jump(); if (leftIsNoDbl.isSet()) leftIsNoDbl.link(as); if (rightIsNoDbl.isSet()) rightIsNoDbl.link(as); } break; default: break; } return done; }
void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Temp *target) { Q_ASSERT(lhs->asConst() == 0 || rhs->asConst() == 0); Q_ASSERT(isPregOrConst(lhs)); Q_ASSERT(isPregOrConst(rhs)); Assembler::FPRegisterID targetReg; if (target->kind == IR::Temp::PhysicalRegister) targetReg = (Assembler::FPRegisterID) target->index; else targetReg = Assembler::FPGpr0; switch (op) { case IR::OpAdd: as->addDouble(as->toDoubleRegister(lhs), as->toDoubleRegister(rhs), 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); as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); as->subDouble(Assembler::FPGpr0, 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->subDouble(reg, targetReg); break; } #endif as->subDouble(as->toDoubleRegister(lhs), as->toDoubleRegister(rhs), 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); as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); as->divDouble(Assembler::FPGpr0, 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); break; default: { Q_ASSERT(target->type == IR::BoolType); Assembler::Jump trueCase = as->branchDouble(false, op, lhs, rhs); as->storeBool(false, target); Assembler::Jump done = as->jump(); trueCase.link(as); as->storeBool(true, target); done.link(as); } return; } if (target->kind != IR::Temp::PhysicalRegister) as->storeDouble(Assembler::FPGpr0, target); }
void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) { IR::Temp *targetTemp = target->asTemp(); Assembler::FPRegisterID targetReg; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) targetReg = (Assembler::FPRegisterID) targetTemp->index; else targetReg = Assembler::FPGpr0; switch (op) { case IR::OpAdd: 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: 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->mulDouble(as->loadTempAddress(t), targetReg); break; } } #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::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, Assembler::FPGpr0), as->toDoubleRegister(rhs, Assembler::FPGpr1), targetReg); break; case IR::OpDiv: #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(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->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); as->storeBool(false, target); Assembler::Jump done = as->jump(); trueCase.link(as); as->storeBool(true, target); done.link(as); } return; } if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) as->storeDouble(targetReg, target); }