예제 #1
0
// 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);
}
예제 #5
0
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);
}