Value CppEmitterContext::BinaryOperationImpl()

in libraries/value/src/CppEmitterContext.cpp [771:880]


    Value CppEmitterContext::BinaryOperationImpl(ValueBinaryOperation op, Value destination, Value source)
    {
        if (destination.IsConstant() && source.IsConstant())
        {
            return _computeContext.BinaryOperation(op, destination, source);
        }

        std::string opStr;
        bool canSelfAssign;
        std::tie(opStr, canSelfAssign) = [op]() -> std::pair<std::string, bool> {
            switch (op)
            {
            case ValueBinaryOperation::add:
                return { " += ", true };
            case ValueBinaryOperation::divide:
                return { " /= ", true };
            case ValueBinaryOperation::modulus:
                return { " %= ", true };
            case ValueBinaryOperation::multiply:
                return { " *= ", true };
            case ValueBinaryOperation::subtract:
                return { " -= ", true };
            case ValueBinaryOperation::logicalAnd:
                return { " && ", false };
            case ValueBinaryOperation::logicalOr:
                return { " || ", false };
            default:
                throw LogicException(LogicExceptionErrors::illegalState);
            }
        }();

        if (destination.IsDefined())
        {
            if (destination.GetLayout() != source.GetLayout())
            {
                throw LogicException(LogicExceptionErrors::illegalState);
            }
            if (destination.GetBaseType() != source.GetBaseType())
            {
                throw LogicException(LogicExceptionErrors::illegalState);
            }
        }
        else
        {
            if (!source.IsConstrained())
            {
                throw LogicException(LogicExceptionErrors::illegalState);
            }
            return source;
        }

        const auto& layout = destination.GetLayout();
        auto destStr = GetNameImpl(destination);
        auto srcStr = GetNameImpl(source);
        if (layout == ScalarLayout)
        {
            auto sourceString = ScalarToString(source);
            switch (op)
            {
            case ValueBinaryOperation::add:
                [[fallthrough]];
            case ValueBinaryOperation::subtract:
                if (sourceString == "0") // destination = destination { +, - } 0
                {
                    return destination;
                }
                break;
            case ValueBinaryOperation::divide:
                [[fallthrough]];
            case ValueBinaryOperation::multiply:
                if (sourceString == "1") // destination = destination { /, * } 1
                {
                    return destination;
                }
                break;
            default:
                break;
            }
            if (canSelfAssign)
            {
                Out() << destStr << "[0]" << opStr << sourceString << ";\n";
            }
            else
            {
                Out() << destStr << "[0] = " << destStr << "[0]" << opStr << sourceString << ";\n";
            }
        }
        else
        {
            auto emittableSource = EnsureEmittable(source);
            srcStr = emittableSource.GetName();

            auto iterationVariable = UniqueName("index");
            Out() << "for (size_t " << iterationVariable << " = 0; " << iterationVariable << " < " << layout.GetMemorySize() << "; " << iterationVariable << " += " << layout.GetCumulativeIncrement(layout.NumDimensions() - 1) << ") {\n";

            Indented([&] {
                if (canSelfAssign)
                {
                    Out() << destStr << "[" << iterationVariable << "]" << opStr << srcStr << "[" << iterationVariable << "];\n";
                }
                else
                {
                    Out() << destStr << "[" << iterationVariable << "] = " << destStr << "[" << iterationVariable << "]" << opStr << srcStr << "[" << iterationVariable << "];\n";
                }
            });

            Out() << "}\n\n";
        }
        return destination;
    }