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