Beispiel #1
0
        /// <summary>
        /// Realizes an arithmetic operation.
        /// </summary>
        /// <param name="block">The current basic block.</param>
        /// <param name="builder">The current builder.</param>
        /// <param name="kind">The kind of the arithmetic operation.</param>
        /// <param name="instruction">The current IL instruction.</param>
        private static void MakeArithmetic(
            Block block,
            IRBuilder builder,
            BinaryArithmeticKind kind,
            ILInstruction instruction)
        {
            var arithmeticFlags = ArithmeticFlags.None;
            var convertFlags    = ConvertFlags.None;

            if (instruction.HasFlags(ILInstructionFlags.Overflow))
            {
                arithmeticFlags |= ArithmeticFlags.Overflow;
            }
            if (instruction.HasFlags(ILInstructionFlags.Unsigned))
            {
                convertFlags    |= ConvertFlags.TargetUnsigned;
                arithmeticFlags |= ArithmeticFlags.Unsigned;
            }
            block.PopArithmeticArgs(convertFlags, out Value left, out Value right);
            switch (kind)
            {
            case BinaryArithmeticKind.Shl:
            case BinaryArithmeticKind.Shr:
                // Convert right operand to 32bits
                right = CreateConversion(
                    builder,
                    right,
                    builder.GetPrimitiveType(BasicValueType.Int32),
                    convertFlags);
                break;
            }
            var arithmetic = builder.CreateArithmetic(left, right, kind, arithmeticFlags);

            block.Push(arithmetic);
        }
Beispiel #2
0
 /// <summary>
 /// Creates a binary arithmetic operation.
 /// </summary>
 /// <param name="location">The current location.</param>
 /// <param name="left">The left operand.</param>
 /// <param name="right">The right operand.</param>
 /// <param name="kind">The operation kind.</param>
 /// <returns>A node that represents the arithmetic operation.</returns>
 public ValueReference CreateArithmetic(
     Location location,
     Value left,
     Value right,
     BinaryArithmeticKind kind) =>
 CreateArithmetic(
     location,
     left,
     right,
     kind,
     ArithmeticFlags.None);
Beispiel #3
0
 /// <summary>
 /// Resolves a binary arithmetic operation.
 /// </summary>
 /// <param name="kind">The arithmetic kind.</param>
 /// <param name="isFloat">True, if this is a floating-point operation.</param>
 /// <param name="isFunction">True, if the resolved operation is a function call.</param>
 /// <returns>The resolved arithmetic operation.</returns>
 public static string GetArithmeticOperation(
     BinaryArithmeticKind kind,
     bool isFloat,
     out bool isFunction)
 {
     if (!BinaryArithmeticOperations.TryGetValue((kind, isFloat), out var operation))
     {
         throw new NotSupportedIntrinsicException(kind.ToString());
     }
     isFunction = operation.Item2;
     return(operation.Item1);
 }
Beispiel #4
0
        public static string GetArithmeticOperation(
            BinaryArithmeticKind kind,
            ArithmeticBasicValueType type,
            bool fastMath)
        {
            var key = (kind, type);

            if (fastMath &&
                BinaryArithmeticOperationsFastMath.TryGetValue(key, out string operation))
            {
                return(operation);
            }
            return(BinaryArithmeticOperations[key]);
        }
Beispiel #5
0
        /// <summary>
        /// Resolves a binary arithmetic operation.
        /// </summary>
        /// <param name="kind">The arithmetic kind.</param>
        /// <param name="type">The operation type.</param>
        /// <param name="fastMath">True, to use a fast-math operation.</param>
        /// <returns>The resolved arithmetic operation.</returns>
        public static string GetArithmeticOperation(
            BinaryArithmeticKind kind,
            ArithmeticBasicValueType type,
            bool fastMath)
        {
            var key = (kind, type);

            if (fastMath &&
                BinaryArithmeticOperationsFastMath.TryGetValue(key, out string operation) ||
                BinaryArithmeticOperations.TryGetValue(key, out operation))
            {
                return(operation);
            }
            throw new NotSupportedIntrinsicException(kind.ToString());
        }
Beispiel #6
0
        /// <summary>
        /// Constructs a new binary arithmetic value.
        /// </summary>
        /// <param name="basicBlock">The parent basic block.</param>
        /// <param name="left">The left operand.</param>
        /// <param name="right">The right operand.</param>
        /// <param name="kind">The operation kind.</param>
        /// <param name="flags">The operation flags.</param>
        internal BinaryArithmeticValue(
            BasicBlock basicBlock,
            ValueReference left,
            ValueReference right,
            BinaryArithmeticKind kind,
            ArithmeticFlags flags)
            : base(
                basicBlock,
                ImmutableArray.Create(left, right),
                flags,
                ComputeType(left))
        {
            Debug.Assert(
                left.Type == right.Type ||
                (kind == BinaryArithmeticKind.Shl || kind == BinaryArithmeticKind.Shr) &&
                right.BasicValueType == BasicValueType.Int32, "Invalid types");

            Kind = kind;
        }
Beispiel #7
0
        /// <summary>
        /// Realizes an arithmetic operation.
        /// </summary>
        /// <param name="kind">The kind of the arithmetic operation.</param>
        /// <param name="instruction">The current IL instruction.</param>
        private void MakeArithmetic(
            BinaryArithmeticKind kind,
            ILInstruction instruction)
        {
            var arithmeticFlags = ArithmeticFlags.None;
            var convertFlags    = ConvertFlags.None;

            if (instruction.HasFlags(ILInstructionFlags.Overflow))
            {
                arithmeticFlags |= ArithmeticFlags.Overflow;
            }
            if (instruction.HasFlags(ILInstructionFlags.Unsigned))
            {
                convertFlags    |= ConvertFlags.TargetUnsigned;
                arithmeticFlags |= ArithmeticFlags.Unsigned;
            }

            ValueReference result;

            if (Block.PopArithmeticArgs(
                    Location,
                    convertFlags,
                    out var left,
                    out var right) == Block.ArithmeticOperandKind.Pointer)
            {
                // This is a pointer access
                bool isLeftPointer = left.Type.IsPointerType;
                if (!isLeftPointer)
                {
                    Utilities.Swap(ref left, ref right);
                }

                if (kind != BinaryArithmeticKind.Add || right.Type.IsPointerType)
                {
                    throw Location.GetNotSupportedException(
                              ErrorMessages.NotSupportedArithmeticArgumentType,
                              kind);
                }
                result = Builder.CreateLoadElementAddress(
                    Location,
                    left,
                    right);
            }
Beispiel #8
0
        /// <summary>
        /// Creates a binary arithmetic operation.
        /// </summary>
        /// <param name="location">The current location.</param>
        /// <param name="left">The left operand.</param>
        /// <param name="right">The right operand.</param>
        /// <param name="kind">The operation kind.</param>
        /// <param name="flags">Operation flags.</param>
        /// <returns>A node that represents the arithmetic operation.</returns>
        public ValueReference CreateArithmetic(
            Location location,
            Value left,
            Value right,
            BinaryArithmeticKind kind,
            ArithmeticFlags flags)
        {
            VerifyBinaryArithmeticOperands(location, left, right, kind);

            Value simplified;

            if (right is PrimitiveValue rightValue)
            {
                // Check for constants
                if (left is PrimitiveValue leftPrimitive)
                {
                    return(BinaryArithmeticFoldConstants(
                               location,
                               leftPrimitive,
                               rightValue,
                               kind,
                               flags));
                }

                // Check for simplifications of the RHS
                if ((simplified = BinaryArithmeticSimplify_RHS(
                         location,
                         left,
                         rightValue,
                         kind,
                         flags)) != null)
                {
                    return(simplified);
                }

                if (left is BinaryArithmeticValue leftBinary &&
                    leftBinary.Kind == kind &&
                    leftBinary.Right.Resolve() is PrimitiveValue nestedRightValue &&
                    (simplified = BinaryArithmeticSimplify_RHS(
                         location,
                         leftBinary,
                         nestedRightValue,
                         rightValue,
                         kind,
                         flags)) != null)
                {
                    return(simplified);
                }
            }

            if (left is PrimitiveValue leftValue)
            {
                // Move constants to the right
                if (kind.IsCommutative())
                {
                    return(CreateArithmetic(
                               location,
                               right,
                               left,
                               kind,
                               flags));
                }

                // Check for simplifications of the LHS
                if ((simplified = BinaryArithmeticSimplify_LHS(
                         location,
                         leftValue,
                         right,
                         kind,
                         flags)) != null)
                {
                    return(simplified);
                }

                if (right is BinaryArithmeticValue rightBinary &&
                    rightBinary.Kind == kind &&
                    rightBinary.Left.Resolve() is PrimitiveValue nestedLeftValue &&
                    (simplified = BinaryArithmeticSimplify_LHS(
                         location,
                         rightBinary,
                         nestedLeftValue,
                         leftValue,
                         kind,
                         flags)) != null)
                {
                    return(simplified);
                }
            }

            return(Append(new BinaryArithmeticValue(
                              GetInitializer(location),
                              left,
                              right,
                              kind,
                              flags)));
        }
Beispiel #9
0
        /// <summary>
        /// Creates a binary arithmetic operation.
        /// </summary>
        /// <param name="left">The left operand.</param>
        /// <param name="right">The right operand.</param>
        /// <param name="kind">The operation kind.</param>
        /// <param name="flags">Operation flags.</param>
        /// <returns>A node that represents the arithmetic operation.</returns>
        public ValueReference CreateArithmetic(
            Value left,
            Value right,
            BinaryArithmeticKind kind,
            ArithmeticFlags flags)
        {
            Debug.Assert(left != null, "Invalid left node");
            Debug.Assert(right != null, "Invalid right node");

            if (UseConstantPropagation && left is PrimitiveValue leftValue)
            {
                // Check for constants
                if (right is PrimitiveValue rightValue)
                {
                    return(BinaryArithmeticFoldConstants(
                               leftValue, rightValue, kind, flags));
                }

                if (kind == BinaryArithmeticKind.Div)
                {
                    switch (left.BasicValueType)
                    {
                    case BasicValueType.Float32:
                        if (leftValue.Float32Value == 1.0f)
                        {
                            return(CreateArithmetic(right, UnaryArithmeticKind.RcpF));
                        }
                        break;

                    case BasicValueType.Float64:
                        if (leftValue.Float64Value == 1.0)
                        {
                            return(CreateArithmetic(right, UnaryArithmeticKind.RcpF));
                        }
                        break;

                    default:
                        break;
                    }
                }
            }

            switch (kind)
            {
            case BinaryArithmeticKind.And:
            case BinaryArithmeticKind.Or:
            case BinaryArithmeticKind.Xor:
                if (left.BasicValueType.IsFloat())
                {
                    throw new NotSupportedException(string.Format(
                                                        ErrorMessages.NotSupportedArithmeticArgumentType,
                                                        left.BasicValueType));
                }
                break;

            case BinaryArithmeticKind.Atan2F:
            case BinaryArithmeticKind.PowF:
                if (!left.BasicValueType.IsFloat())
                {
                    throw new NotSupportedException(string.Format(
                                                        ErrorMessages.NotSupportedArithmeticArgumentType,
                                                        left.BasicValueType));
                }
                break;
            }

            return(Append(new BinaryArithmeticValue(
                              BasicBlock,
                              left,
                              right,
                              kind,
                              flags)));
        }
Beispiel #10
0
        /// <summary>
        /// Realizes an arithmetic operation.
        /// </summary>
        /// <param name="kind">The kind of the arithmetic operation.</param>
        /// <param name="instruction">The current IL instruction.</param>
        private void MakeArithmetic(
            BinaryArithmeticKind kind,
            ILInstruction instruction)
        {
            var arithmeticFlags = ArithmeticFlags.None;
            var convertFlags    = ConvertFlags.None;

            if (instruction.HasFlags(ILInstructionFlags.Overflow))
            {
                arithmeticFlags |= ArithmeticFlags.Overflow;
            }
            if (instruction.HasFlags(ILInstructionFlags.Unsigned))
            {
                convertFlags    |= ConvertFlags.TargetUnsigned;
                arithmeticFlags |= ArithmeticFlags.Unsigned;
            }

            ValueReference result = default;

            if (Block.PopArithmeticArgs(
                    Location,
                    convertFlags,
                    out var left,
                    out var right) == Block.ArithmeticOperandKind.Pointer)
            {
                // This is a pointer access
                bool isLeftPointer = left.Type.IsPointerType;
                if (!isLeftPointer)
                {
                    Utilities.Swap(ref left, ref right);
                }

                // Check for raw combinations of two pointer values
                if (
                    !right.Type.IsPointerType &&
                    // Check whether this can be safely converted into a LEA value
                    kind == BinaryArithmeticKind.Add)
                {
                    result = Builder.CreateLoadElementAddress(
                        Location,
                        left,
                        right);
                }
                // Check whether this operation on pointer values can be converted
                // into a LEA instruction
                // FIXME: remove this code once we add additional LEA nodes
                else if (
                    kind == BinaryArithmeticKind.Add &&
                    right is BinaryArithmeticValue baseAddress &&
                    TryConvertIntoLoadElementAddress(left, baseAddress, out var lea))
                {
                    result = lea;
                }
            }

            if (!result.IsValid)
            {
                switch (kind)
                {
                case BinaryArithmeticKind.Shl:
                case BinaryArithmeticKind.Shr:
                    // Convert right operand to 32bits
                    right = CreateConversion(
                        right,
                        Builder.GetPrimitiveType(BasicValueType.Int32),
                        convertFlags);
                    break;
                }
                result = Builder.CreateArithmetic(
                    Location,
                    left,
                    right,
                    kind,
                    arithmeticFlags);
            }
            Block.Push(result);
        }
Beispiel #11
0
        /// <summary>
        /// Creates a binary arithmetic operation.
        /// </summary>
        /// <param name="location">The current location.</param>
        /// <param name="left">The left operand.</param>
        /// <param name="right">The right operand.</param>
        /// <param name="kind">The operation kind.</param>
        /// <param name="flags">Operation flags.</param>
        /// <returns>A node that represents the arithmetic operation.</returns>
        public ValueReference CreateArithmetic(
            Location location,
            Value left,
            Value right,
            BinaryArithmeticKind kind,
            ArithmeticFlags flags)
        {
            // TODO: add additional partial arithmetic simplifications in a generic way
            if (UseConstantPropagation && left is PrimitiveValue leftValue)
            {
                // Check for constants
                if (right is PrimitiveValue rightConstant)
                {
                    return(BinaryArithmeticFoldConstants(
                               location,
                               leftValue,
                               rightConstant,
                               kind,
                               flags));
                }

                if (kind == BinaryArithmeticKind.Div)
                {
                    switch (left.BasicValueType)
                    {
                    case BasicValueType.Float32:
                        if (leftValue.Float32Value == 1.0f)
                        {
                            return(CreateArithmetic(
                                       location,
                                       right,
                                       UnaryArithmeticKind.RcpF));
                        }
                        break;

                    case BasicValueType.Float64:
                        if (leftValue.Float64Value == 1.0)
                        {
                            return(CreateArithmetic(
                                       location,
                                       right,
                                       UnaryArithmeticKind.RcpF));
                        }
                        break;

                    default:
                        break;
                    }
                }
            }

            // TODO: remove the following hard-coded rules
            if (right is PrimitiveValue rightValue &&
                left.BasicValueType.IsInt() &&
                Utilities.IsPowerOf2(rightValue.RawValue))
            {
                if (kind == BinaryArithmeticKind.Div ||
                    kind == BinaryArithmeticKind.Mul)
                {
                    var shiftAmount = CreatePrimitiveValue(
                        location,
                        (int)Math.Log(
                            Math.Abs((double)rightValue.RawValue),
                            2.0));
                    var leftKind = Utilities.Select(
                        kind == BinaryArithmeticKind.Div,
                        BinaryArithmeticKind.Shr,
                        BinaryArithmeticKind.Shl);
                    var rightKind = Utilities.Select(
                        leftKind == BinaryArithmeticKind.Shr,
                        BinaryArithmeticKind.Shl,
                        BinaryArithmeticKind.Shr);
                    return(CreateArithmetic(
                               location,
                               left,
                               shiftAmount,
                               Utilities.Select(
                                   rightValue.RawValue > 0,
                                   leftKind,
                                   rightKind)));
                }
            }

            switch (kind)
            {
            case BinaryArithmeticKind.And:
            case BinaryArithmeticKind.Or:
            case BinaryArithmeticKind.Xor:
                if (left.BasicValueType.IsFloat())
                {
                    throw location.GetNotSupportedException(
                              ErrorMessages.NotSupportedArithmeticArgumentType,
                              left.BasicValueType);
                }

                break;

            case BinaryArithmeticKind.Atan2F:
            case BinaryArithmeticKind.PowF:
                if (!left.BasicValueType.IsFloat())
                {
                    throw location.GetNotSupportedException(
                              ErrorMessages.NotSupportedArithmeticArgumentType,
                              left.BasicValueType);
                }

                break;
            }

            return(Append(new BinaryArithmeticValue(
                              GetInitializer(location),
                              left,
                              right,
                              kind,
                              flags)));
        }
Beispiel #12
0
        /// <summary>
        /// Realizes an arithmetic operation.
        /// </summary>
        /// <param name="kind">The kind of the arithmetic operation.</param>
        /// <param name="instruction">The current IL instruction.</param>
        private void MakeArithmetic(
            BinaryArithmeticKind kind,
            ILInstruction instruction)
        {
            var arithmeticFlags = ArithmeticFlags.None;
            var convertFlags    = ConvertFlags.None;

            if (instruction.HasFlags(ILInstructionFlags.Overflow))
            {
                arithmeticFlags |= ArithmeticFlags.Overflow;
            }
            if (instruction.HasFlags(ILInstructionFlags.Unsigned))
            {
                convertFlags    |= ConvertFlags.TargetUnsigned;
                arithmeticFlags |= ArithmeticFlags.Unsigned;
            }

            ValueReference result = default;

            if (Block.PopArithmeticArgs(
                    Location,
                    convertFlags,
                    out var left,
                    out var right) == Block.ArithmeticOperandKind.Pointer)
            {
                // This is a pointer access
                bool isLeftPointer = left.Type.IsPointerType;
                if (!isLeftPointer)
                {
                    Utilities.Swap(ref left, ref right);
                }

                // Check for raw combinations of two pointer values
                if (
                    !right.Type.IsPointerType &&
                    // Check whether this can be safely converted into a LEA value
                    kind == BinaryArithmeticKind.Add)
                {
                    result = Builder.CreateLoadElementAddress(
                        Location,
                        left,
                        right);
                }
                // Check whether this operation on pointer values can be converted
                // into a LEA instruction
                // FIXME: remove this code once we add additional LEA nodes
                else if (
                    kind == BinaryArithmeticKind.Add &&
                    right is BinaryArithmeticValue baseAddress &&
                    baseAddress.Kind == BinaryArithmeticKind.Mul &&
                    // Extract the element stride from the multiplication pattern
                    // (must be the right operand since we have moved all pointer
                    // values the left hand side by definition)
                    TryGetBasicValueSize(baseAddress.Right, out var strideType))
                {
                    // Cast raw pointer into an appropriate target type
                    var targetElementType = Builder.GetPrimitiveType(strideType);
                    left = Builder.CreatePointerCast(
                        Location,
                        left,
                        targetElementType);
                    result = Builder.CreateLoadElementAddress(
                        Location,
                        left,
                        baseAddress.Left);
                }
            }

            if (!result.IsValid)
            {
                switch (kind)
                {
                case BinaryArithmeticKind.Shl:
                case BinaryArithmeticKind.Shr:
                    // Convert right operand to 32bits
                    right = CreateConversion(
                        right,
                        Builder.GetPrimitiveType(BasicValueType.Int32),
                        convertFlags);
                    break;
                }
                result = Builder.CreateArithmetic(
                    Location,
                    left,
                    right,
                    kind,
                    arithmeticFlags);
            }
            Block.Push(result);
        }