Commit 8e2b44f7 authored by Bevin Hansson's avatar Bevin Hansson Committed by Mikael Holmen
Browse files

[Intrinsic] Add fixed point division intrinsics.

Summary:
This patch adds intrinsics and ISelDAG nodes for
signed and unsigned fixed-point division:

  llvm.sdiv.fix.*
  llvm.udiv.fix.*

These intrinsics perform scaled division on two
integers or vectors of integers. They are required
for the implementation of the Embedded-C fixed-point
arithmetic in Clang.

Patch by: ebevhan

Reviewers: bjope, leonardchan, efriedma, craig.topper

Reviewed By: craig.topper

Subscribers: Ka-Ka, ilya, hiraditya, jdoerfert, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D70007
parent b2c2fe72
Loading
Loading
Loading
Loading
+141 −4
Original line number Diff line number Diff line
@@ -13675,16 +13675,17 @@ Fixed Point Arithmetic Intrinsics
A fixed point number represents a real data type for a number that has a fixed
number of digits after a radix point (equivalent to the decimal point '.').
The number of digits after the radix point is referred as the ``scale``. These
The number of digits after the radix point is referred as the `scale`. These
are useful for representing fractional values to a specific precision. The
following intrinsics perform fixed point arithmetic operations on 2 operands
of the same scale, specified as the third argument.
The `llvm.*mul.fix` family of intrinsic functions represents a multiplication
The ``llvm.*mul.fix`` family of intrinsic functions represents a multiplication
of fixed point numbers through scaled integers. Therefore, fixed point
multplication can be represented as
multiplication can be represented as
.. code-block:: llvm
::
        %result = call i4 @llvm.smul.fix.i4(i4 %a, i4 %b, i32 %scale)
        ; Expands to
@@ -13695,6 +13696,22 @@ multplication can be represented as
        %r = ashr i8 %mul, i8 %scale2  ; this is for a target rounding down towards negative infinity
        %result = trunc i8 %r to i4
The ``llvm.*div.fix`` family of intrinsic functions represents a division of
fixed point numbers through scaled integers. Fixed point division can be
represented as:
.. code-block:: llvm
        %result call i4 @llvm.sdiv.fix.i4(i4 %a, i4 %b, i32 %scale)
        ; Expands to
        %a2 = sext i4 %a to i8
        %b2 = sext i4 %b to i8
        %scale2 = trunc i32 %scale to i8
        %a3 = shl i8 %a2, %scale2
        %r = sdiv i8 %a3, %b2 ; this is for a target rounding towards zero
        %result = trunc i8 %r to i4
For each of these functions, if the result cannot be represented exactly with
the provided scale, the result is rounded. Rounding is unspecified since
preferred rounding may vary for different targets. Rounding is specified
@@ -13963,6 +13980,126 @@ Examples
      %res = call i4 @llvm.umul.fix.sat.i4(i4 2, i4 4, i32 1)  ; %res = 4 (1 x 2 = 2)
'``llvm.sdiv.fix.*``' Intrinsics
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Syntax
"""""""
This is an overloaded intrinsic. You can use ``llvm.sdiv.fix``
on any integer bit width or vectors of integers.
::
      declare i16 @llvm.sdiv.fix.i16(i16 %a, i16 %b, i32 %scale)
      declare i32 @llvm.sdiv.fix.i32(i32 %a, i32 %b, i32 %scale)
      declare i64 @llvm.sdiv.fix.i64(i64 %a, i64 %b, i32 %scale)
      declare <4 x i32> @llvm.sdiv.fix.v4i32(<4 x i32> %a, <4 x i32> %b, i32 %scale)
Overview
"""""""""
The '``llvm.sdiv.fix``' family of intrinsic functions perform signed
fixed point division on 2 arguments of the same scale.
Arguments
""""""""""
The arguments (%a and %b) and the result may be of integer types of any bit
width, but they must have the same bit width. The arguments may also work with
int vectors of the same length and int size. ``%a`` and ``%b`` are the two
values that will undergo signed fixed point division. The argument
``%scale`` represents the scale of both operands, and must be a constant
integer.
Semantics:
""""""""""
This operation performs fixed point division on the 2 arguments of a
specified scale. The result will also be returned in the same scale specified
in the third argument.
If the result value cannot be precisely represented in the given scale, the
value is rounded up or down to the closest representable value. The rounding
direction is unspecified.
It is undefined behavior if the result value does not fit within the range of
the fixed point type, or if the second argument is zero.
Examples
"""""""""
.. code-block:: llvm
      %res = call i4 @llvm.sdiv.fix.i4(i4 6, i4 2, i32 0)  ; %res = 3 (6 / 2 = 3)
      %res = call i4 @llvm.sdiv.fix.i4(i4 6, i4 4, i32 1)  ; %res = 3 (3 / 2 = 1.5)
      %res = call i4 @llvm.sdiv.fix.i4(i4 3, i4 -2, i32 1) ; %res = -3 (1.5 / -1 = -1.5)
      ; The result in the following could be rounded up to 1 or down to 0.5
      %res = call i4 @llvm.sdiv.fix.i4(i4 3, i4 4, i32 1)  ; %res = 2 (or 1) (1.5 / 2 = 0.75)
'``llvm.udiv.fix.*``' Intrinsics
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Syntax
"""""""
This is an overloaded intrinsic. You can use ``llvm.udiv.fix``
on any integer bit width or vectors of integers.
::
      declare i16 @llvm.udiv.fix.i16(i16 %a, i16 %b, i32 %scale)
      declare i32 @llvm.udiv.fix.i32(i32 %a, i32 %b, i32 %scale)
      declare i64 @llvm.udiv.fix.i64(i64 %a, i64 %b, i32 %scale)
      declare <4 x i32> @llvm.udiv.fix.v4i32(<4 x i32> %a, <4 x i32> %b, i32 %scale)
Overview
"""""""""
The '``llvm.udiv.fix``' family of intrinsic functions perform unsigned
fixed point division on 2 arguments of the same scale.
Arguments
""""""""""
The arguments (%a and %b) and the result may be of integer types of any bit
width, but they must have the same bit width. The arguments may also work with
int vectors of the same length and int size. ``%a`` and ``%b`` are the two
values that will undergo unsigned fixed point division. The argument
``%scale`` represents the scale of both operands, and must be a constant
integer.
Semantics:
""""""""""
This operation performs fixed point division on the 2 arguments of a
specified scale. The result will also be returned in the same scale specified
in the third argument.
If the result value cannot be precisely represented in the given scale, the
value is rounded up or down to the closest representable value. The rounding
direction is unspecified.
It is undefined behavior if the result value does not fit within the range of
the fixed point type, or if the second argument is zero.
Examples
"""""""""
.. code-block:: llvm
      %res = call i4 @llvm.udiv.fix.i4(i4 6, i4 2, i32 0)  ; %res = 3 (6 / 2 = 3)
      %res = call i4 @llvm.udiv.fix.i4(i4 6, i4 4, i32 1)  ; %res = 3 (3 / 2 = 1.5)
      %res = call i4 @llvm.udiv.fix.i4(i4 1, i4 -8, i32 4) ; %res = 2 (0.0625 / 0.5 = 0.125)
      ; The result in the following could be rounded up to 1 or down to 0.5
      %res = call i4 @llvm.udiv.fix.i4(i4 3, i4 4, i32 1)  ; %res = 2 (or 1) (1.5 / 2 = 0.75)
Specialised Arithmetic Intrinsics
---------------------------------
+6 −0
Original line number Diff line number Diff line
@@ -285,6 +285,12 @@ namespace ISD {
    /// bits of the first 2 operands.
    SMULFIXSAT, UMULFIXSAT,

    /// RESULT = [US]DIVFIX(LHS, RHS, SCALE) - Perform fixed point division on
    /// 2 integers with the same width and scale. SCALE represents the scale
    /// of both operands as fixed point numbers. This SCALE parameter must be a
    /// constant integer.
    SDIVFIX, UDIVFIX,

    /// Simple binary floating point operators.
    FADD, FSUB, FMUL, FDIV, FREM,

+10 −0
Original line number Diff line number Diff line
@@ -935,6 +935,8 @@ public:
    case ISD::SMULFIXSAT:
    case ISD::UMULFIX:
    case ISD::UMULFIXSAT:
    case ISD::SDIVFIX:
    case ISD::UDIVFIX:
      Supported = isSupportedFixedPointOperation(Op, VT, Scale);
      break;
    }
@@ -4184,6 +4186,14 @@ public:
  /// method accepts integers as its arguments.
  SDValue expandFixedPointMul(SDNode *Node, SelectionDAG &DAG) const;

  /// Method for building the DAG expansion of ISD::[US]DIVFIX. This
  /// method accepts integers as its arguments.
  /// Note: This method may fail if the division could not be performed
  /// within the type. Clients must retry with a wider type if this happens.
  SDValue expandFixedPointDiv(unsigned Opcode, const SDLoc &dl,
                              SDValue LHS, SDValue RHS,
                              unsigned Scale, SelectionDAG &DAG) const;

  /// Method for building the DAG expansion of ISD::U(ADD|SUB)O. Expansion
  /// always suceeds and populates the Result and Overflow arguments.
  void expandUADDSUBO(SDNode *Node, SDValue &Result, SDValue &Overflow,
+8 −0
Original line number Diff line number Diff line
@@ -930,6 +930,14 @@ def int_umul_fix : Intrinsic<[llvm_anyint_ty],
                             [LLVMMatchType<0>, LLVMMatchType<0>, llvm_i32_ty],
                             [IntrNoMem, IntrSpeculatable, IntrWillReturn, Commutative, ImmArg<2>]>;

def int_sdiv_fix : Intrinsic<[llvm_anyint_ty],
                             [LLVMMatchType<0>, LLVMMatchType<0>, llvm_i32_ty],
                             [IntrNoMem, ImmArg<2>]>;

def int_udiv_fix : Intrinsic<[llvm_anyint_ty],
                             [LLVMMatchType<0>, LLVMMatchType<0>, llvm_i32_ty],
                             [IntrNoMem, ImmArg<2>]>;

//===------------------- Fixed Point Saturation Arithmetic Intrinsics ----------------===//
//
def int_smul_fix_sat : Intrinsic<[llvm_anyint_ty],
+3 −1
Original line number Diff line number Diff line
@@ -124,7 +124,7 @@ def SDTIntSatNoShOp : SDTypeProfile<1, 2, [ // ssat with no shift
def SDTIntBinHiLoOp : SDTypeProfile<2, 2, [ // mulhi, mullo, sdivrem, udivrem
  SDTCisSameAs<0, 1>, SDTCisSameAs<0, 2>, SDTCisSameAs<0, 3>,SDTCisInt<0>
]>;
def SDTIntScaledBinOp : SDTypeProfile<1, 3, [  // smulfix, umulfix
def SDTIntScaledBinOp : SDTypeProfile<1, 3, [  // smulfix, sdivfix, etc
  SDTCisSameAs<0, 1>, SDTCisSameAs<0, 2>, SDTCisInt<0>, SDTCisInt<3>
]>;

@@ -400,6 +400,8 @@ def smulfix : SDNode<"ISD::SMULFIX" , SDTIntScaledBinOp, [SDNPCommutative]>
def smulfixsat : SDNode<"ISD::SMULFIXSAT", SDTIntScaledBinOp, [SDNPCommutative]>;
def umulfix    : SDNode<"ISD::UMULFIX"   , SDTIntScaledBinOp, [SDNPCommutative]>;
def umulfixsat : SDNode<"ISD::UMULFIXSAT", SDTIntScaledBinOp, [SDNPCommutative]>;
def sdivfix    : SDNode<"ISD::SDIVFIX"   , SDTIntScaledBinOp>;
def udivfix    : SDNode<"ISD::UDIVFIX"   , SDTIntScaledBinOp>;

def sext_inreg : SDNode<"ISD::SIGN_EXTEND_INREG", SDTExtInreg>;
def sext_invec : SDNode<"ISD::SIGN_EXTEND_VECTOR_INREG", SDTExtInvec>;
Loading