/// <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); }
/// <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);
/// <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); }
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]); }
/// <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()); }
/// <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; }
/// <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); }
/// <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))); }
/// <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))); }
/// <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); }
/// <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))); }
/// <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); }