Skip to content

Commit

Permalink
[Intrinsic] Signed Fixed Point Saturation Multiplication Intrinsic
Browse files Browse the repository at this point in the history
Add an intrinsic that takes 2 signed integers with the scale of them provided
as the third argument and performs fixed point multiplication on them. The
result is saturated and clamped between the largest and smallest representable
values of the first 2 operands.

This is a part of implementing fixed point arithmetic in clang where some of
the more complex operations will be implemented as intrinsics.

Differential Revision: https://reviews.llvm.org/D55720

llvm-svn: 361289
  • Loading branch information
PiJoules committed May 21, 2019
1 parent ed6df47 commit 0bada7c
Show file tree
Hide file tree
Showing 16 changed files with 1,150 additions and 25 deletions.
95 changes: 95 additions & 0 deletions llvm/docs/LangRef.rst
Expand Up @@ -13278,6 +13278,31 @@ 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
of fixed point numbers through scaled integers. Therefore, fixed point
multplication can be represented as

::
%result = call i4 @llvm.smul.fix.i4(i4 %a, i4 %b, i32 %scale)
=>
%a2 = sext i4 %a to i8
%b2 = sext i4 %b to i8
%mul = mul nsw nuw i8 %a, %b
%scale2 = trunc i32 %scale to i8
%r = ashr i8 %mul, i8 %scale2 ; this is for a target rounding down towards negative infinity
%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
through a target hook. Different pipelines should legalize or optimize this
using the rounding specified by this hook if it is provided. Operations like
constant folding, instruction combining, KnownBits, and ValueTracking should
also use this hook, if provided, and not assume the direction of rounding. A
rounded result must always be within one unit of precision from the true
result. That is, the error between the returned result and the true result must
be less than 1/2^(scale).


'``llvm.smul.fix.*``' Intrinsics
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down Expand Up @@ -13398,6 +13423,76 @@ Examples
%res = call i4 @llvm.umul.fix.i4(i4 15, i4 1, i32 1) ; %res = 7 (or 8) (7.5 x 0.5 = 3.75)


'``llvm.smul.fix.sat.*``' Intrinsics
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Syntax
"""""""

This is an overloaded intrinsic. You can use ``llvm.smul.fix.sat``
on any integer bit width or vectors of integers.

::

declare i16 @llvm.smul.fix.sat.i16(i16 %a, i16 %b, i32 %scale)
declare i32 @llvm.smul.fix.sat.i32(i32 %a, i32 %b, i32 %scale)
declare i64 @llvm.smul.fix.sat.i64(i64 %a, i64 %b, i32 %scale)
declare <4 x i32> @llvm.smul.fix.sat.v4i32(<4 x i32> %a, <4 x i32> %b, i32 %scale)

Overview
"""""""""

The '``llvm.smul.fix.sat``' family of intrinsic functions perform signed
fixed point saturation multiplication 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. ``%a`` and ``%b`` are the two
values that will undergo signed fixed point multiplication. The argument
``%scale`` represents the scale of both operands, and must be a constant
integer.

Semantics:
""""""""""

This operation performs fixed point multiplication 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.

The maximum value this operation can clamp to is the largest signed value
representable by the bit width of the first 2 arguments. The minimum value is the
smallest signed value representable by this bit width.


Examples
"""""""""

.. code-block:: llvm

%res = call i4 @llvm.smul.fix.sat.i4(i4 3, i4 2, i32 0) ; %res = 6 (2 x 3 = 6)
%res = call i4 @llvm.smul.fix.sat.i4(i4 3, i4 2, i32 1) ; %res = 3 (1.5 x 1 = 1.5)
%res = call i4 @llvm.smul.fix.sat.i4(i4 3, i4 -2, i32 1) ; %res = -3 (1.5 x -1 = -1.5)

; The result in the following could be rounded up to -2 or down to -2.5
%res = call i4 @llvm.smul.fix.sat.i4(i4 3, i4 -3, i32 1) ; %res = -5 (or -4) (1.5 x -1.5 = -2.25)

; Saturation
%res = call i4 @llvm.smul.fix.sat.i4(i4 7, i4 2, i32 0) ; %res = 7
%res = call i4 @llvm.smul.fix.sat.i4(i4 7, i4 2, i32 2) ; %res = 7
%res = call i4 @llvm.smul.fix.sat.i4(i4 -8, i4 2, i32 2) ; %res = -8
%res = call i4 @llvm.smul.fix.sat.i4(i4 -8, i4 -2, i32 2) ; %res = 7

; Scale can affect the saturation result
%res = call i4 @llvm.smul.fix.sat.i4(i4 2, i4 4, i32 0) ; %res = 7 (2 x 4 -> clamped to 7)
%res = call i4 @llvm.smul.fix.sat.i4(i4 2, i4 4, i32 1) ; %res = 4 (1 x 2 = 2)


Specialised Arithmetic Intrinsics
---------------------------------

Expand Down
5 changes: 5 additions & 0 deletions llvm/include/llvm/CodeGen/ISDOpcodes.h
Expand Up @@ -278,6 +278,11 @@ namespace ISD {
/// multiplication on 2 integers.
SMULFIX, UMULFIX,

/// Same as the corresponding unsaturated fixed point instructions, but the
/// result is clamped between the min and max values representable by the
/// bits of the first 2 operands.
SMULFIXSAT,

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

Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/CodeGen/TargetLowering.h
Expand Up @@ -855,6 +855,7 @@ class TargetLoweringBase {
default:
llvm_unreachable("Unexpected fixed point operation.");
case ISD::SMULFIX:
case ISD::SMULFIXSAT:
case ISD::UMULFIX:
Supported = isSupportedFixedPointOperation(Op, VT, Scale);
break;
Expand Down
6 changes: 6 additions & 0 deletions llvm/include/llvm/IR/Intrinsics.td
Expand Up @@ -874,6 +874,12 @@ def int_umul_fix : Intrinsic<[llvm_anyint_ty],
[LLVMMatchType<0>, LLVMMatchType<0>, llvm_i32_ty],
[IntrNoMem, IntrSpeculatable, Commutative, ImmArg<2>]>;

//===------------------- Fixed Point Saturation Arithmetic Intrinsics ----------------===//
//
def int_smul_fix_sat : Intrinsic<[llvm_anyint_ty],
[LLVMMatchType<0>, LLVMMatchType<0>, llvm_i32_ty],
[IntrNoMem, IntrSpeculatable, Commutative, ImmArg<2>]>;

//===------------------------- Memory Use Markers -------------------------===//
//
def int_lifetime_start : Intrinsic<[],
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/Target/TargetSelectionDAG.td
Expand Up @@ -391,6 +391,7 @@ def ssubsat : SDNode<"ISD::SSUBSAT" , SDTIntBinOp>;
def usubsat : SDNode<"ISD::USUBSAT" , SDTIntBinOp>;

def smulfix : SDNode<"ISD::SMULFIX" , SDTIntScaledBinOp, [SDNPCommutative]>;
def smulfixsat : SDNode<"ISD::SMULFIXSAT", SDTIntScaledBinOp, [SDNPCommutative]>;
def umulfix : SDNode<"ISD::UMULFIX" , SDTIntScaledBinOp, [SDNPCommutative]>;

def sext_inreg : SDNode<"ISD::SIGN_EXTEND_INREG", SDTExtInreg>;
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp
Expand Up @@ -1140,6 +1140,7 @@ void SelectionDAGLegalize::LegalizeOp(SDNode *Node) {
break;
}
case ISD::SMULFIX:
case ISD::SMULFIXSAT:
case ISD::UMULFIX: {
unsigned Scale = Node->getConstantOperandVal(2);
Action = TLI.getFixedPointOperationAction(Node->getOpcode(),
Expand Down Expand Up @@ -3334,6 +3335,7 @@ bool SelectionDAGLegalize::ExpandNode(SDNode *Node) {
Results.push_back(TLI.expandAddSubSat(Node, DAG));
break;
case ISD::SMULFIX:
case ISD::SMULFIXSAT:
case ISD::UMULFIX:
Results.push_back(TLI.expandFixedPointMul(Node, DAG));
break;
Expand Down

0 comments on commit 0bada7c

Please sign in to comment.