Skip to content

[GlobalISel] Add Saturated Truncate Instructions #147526

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions llvm/docs/GlobalISel/GenericOpcode.rst
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,33 @@ G_EXTRACT for scalar types, but acts elementwise on vectors.

%1:_(s16) = G_TRUNC %0:_(s32)

G_TRUNC_SSAT_S
^^^^^^^^^^^^^^

Truncate a signed input to a signed result with saturation.

.. code-block:: none

%1:_(s16) = G_TRUNC_SSAT_S %0:_(s32)

G_TRUNC_SSAT_U
^^^^^^^^^^^^^^

Truncate a signed input to an unsigned result with saturation.

.. code-block:: none

%1:_(s16) = G_TRUNC_SSAT_U %0:_(s32)

G_TRUNC_USAT_U
^^^^^^^^^^^^^^

Truncate a unsigned input to an unsigned result with saturation.

.. code-block:: none

%1:_(s16) = G_TRUNC_USAT_U %0:_(s32)

Type Conversions
----------------

Expand Down
50 changes: 46 additions & 4 deletions llvm/include/llvm/CodeGen/GlobalISel/MachineIRBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -763,7 +763,7 @@ class LLVM_ABI MachineIRBuilder {

/// Build and insert \p Res = G_SEXT \p Op, \p Res = G_TRUNC \p Op, or
/// \p Res = COPY \p Op depending on the differing sizes of \p Res and \p Op.
/// ///
///
/// \pre setBasicBlock or setMI must have been called.
/// \pre \p Res must be a generic virtual register with scalar or vector type.
/// \pre \p Op must be a generic virtual register with scalar or vector type.
Expand All @@ -773,7 +773,7 @@ class LLVM_ABI MachineIRBuilder {

/// Build and insert \p Res = G_ZEXT \p Op, \p Res = G_TRUNC \p Op, or
/// \p Res = COPY \p Op depending on the differing sizes of \p Res and \p Op.
/// ///
///
/// \pre setBasicBlock or setMI must have been called.
/// \pre \p Res must be a generic virtual register with scalar or vector type.
/// \pre \p Op must be a generic virtual register with scalar or vector type.
Expand All @@ -783,7 +783,7 @@ class LLVM_ABI MachineIRBuilder {

// Build and insert \p Res = G_ANYEXT \p Op, \p Res = G_TRUNC \p Op, or
/// \p Res = COPY \p Op depending on the differing sizes of \p Res and \p Op.
/// ///
///
/// \pre setBasicBlock or setMI must have been called.
/// \pre \p Res must be a generic virtual register with scalar or vector type.
/// \pre \p Op must be a generic virtual register with scalar or vector type.
Expand All @@ -794,7 +794,7 @@ class LLVM_ABI MachineIRBuilder {
/// Build and insert \p Res = \p ExtOpc, \p Res = G_TRUNC \p
/// Op, or \p Res = COPY \p Op depending on the differing sizes of \p Res and
/// \p Op.
/// ///
///
/// \pre setBasicBlock or setMI must have been called.
/// \pre \p Res must be a generic virtual register with scalar or vector type.
/// \pre \p Op must be a generic virtual register with scalar or vector type.
Expand All @@ -809,6 +809,48 @@ class LLVM_ABI MachineIRBuilder {
MachineInstrBuilder buildZExtInReg(const DstOp &Res, const SrcOp &Op,
int64_t ImmOp);

/// Build and insert \p Res = \p G_TRUNC_SSAT_S \p Op
///
/// G_TRUNC_SSAT_S truncates the signed input, \p Op, to a signed result with
/// saturation.
///
/// \pre setBasicBlock or setMI must have been called.
/// \pre \p Res must be a generic virtual register with scalar or vector type.
/// \pre \p Op must be a generic virtual register with scalar or vector type.
///
/// \return The newly created instruction.
MachineInstrBuilder buildTruncSSatS(const DstOp &Res, const SrcOp &Op) {
return buildInstr(TargetOpcode::G_TRUNC_SSAT_S, {Res}, {Op});
}

/// Build and insert \p Res = \p G_TRUNC_SSAT_U \p Op
///
/// G_TRUNC_SSAT_U truncates the signed input, \p Op, to an unsigned result
/// with saturation.
///
/// \pre setBasicBlock or setMI must have been called.
/// \pre \p Res must be a generic virtual register with scalar or vector type.
/// \pre \p Op must be a generic virtual register with scalar or vector type.
///
/// \return The newly created instruction.
MachineInstrBuilder buildTruncSSatU(const DstOp &Res, const SrcOp &Op) {
return buildInstr(TargetOpcode::G_TRUNC_SSAT_U, {Res}, {Op});
}

/// Build and insert \p Res = \p G_TRUNC_USAT_U \p Op
///
/// G_TRUNC_USAT_U truncates the unsigned input, \p Op, to an unsigned result
/// with saturation.
///
/// \pre setBasicBlock or setMI must have been called.
/// \pre \p Res must be a generic virtual register with scalar or vector type.
/// \pre \p Op must be a generic virtual register with scalar or vector type.
///
/// \return The newly created instruction.
MachineInstrBuilder buildTruncUSatU(const DstOp &Res, const SrcOp &Op) {
return buildInstr(TargetOpcode::G_TRUNC_USAT_U, {Res}, {Op});
}

/// Build and insert an appropriate cast between two registers of equal size.
MachineInstrBuilder buildCast(const DstOp &Dst, const SrcOp &Src);

Expand Down
9 changes: 9 additions & 0 deletions llvm/include/llvm/Support/TargetOpcodes.def
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,15 @@ HANDLE_TARGET_OPCODE(G_ANYEXT)
/// elements of the vector.
HANDLE_TARGET_OPCODE(G_TRUNC)

/// Generic instruction to truncate a signed operand to a signed result with saturation.
HANDLE_TARGET_OPCODE(G_TRUNC_SSAT_S)

/// Generic instruction to truncate a signed operand to an unsigned result with saturation.
HANDLE_TARGET_OPCODE(G_TRUNC_SSAT_U)

/// Generic instruction to truncate a unsigned operand to an unsigned result with saturation.
HANDLE_TARGET_OPCODE(G_TRUNC_USAT_U)

/// Generic integer constant.
HANDLE_TARGET_OPCODE(G_CONSTANT)

Expand Down
21 changes: 21 additions & 0 deletions llvm/include/llvm/Target/GenericOpcodes.td
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,27 @@ def G_TRUNC : GenericInstruction {
let hasSideEffects = false;
}

// Truncate the signed operand to a signed result with saturation.
def G_TRUNC_SSAT_S : GenericInstruction {
let OutOperandList = (outs type0:$dst);
let InOperandList = (ins type1:$src);
let hasSideEffects = false;
}

// Truncate the signed operand to an unsigned result with saturation.
def G_TRUNC_SSAT_U : GenericInstruction {
let OutOperandList = (outs type0:$dst);
let InOperandList = (ins type1:$src);
let hasSideEffects = false;
}

// Truncate the unsigned operand to an unsigned result with saturation.
def G_TRUNC_USAT_U : GenericInstruction {
let OutOperandList = (outs type0:$dst);
let InOperandList = (ins type1:$src);
let hasSideEffects = false;
}

def G_IMPLICIT_DEF : GenericInstruction {
let OutOperandList = (outs type0:$dst);
let InOperandList = (ins);
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/CodeGen/MachineVerifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1424,6 +1424,9 @@ void MachineVerifier::verifyPreISelGenericInstruction(const MachineInstr *MI) {
case TargetOpcode::G_ZEXT:
case TargetOpcode::G_ANYEXT:
case TargetOpcode::G_TRUNC:
case TargetOpcode::G_TRUNC_SSAT_S:
case TargetOpcode::G_TRUNC_SSAT_U:
case TargetOpcode::G_TRUNC_USAT_U:
case TargetOpcode::G_FPEXT:
case TargetOpcode::G_FPTRUNC: {
// Number of operands and presense of types is already checked (and
Expand All @@ -1450,6 +1453,9 @@ void MachineVerifier::verifyPreISelGenericInstruction(const MachineInstr *MI) {
report("Generic extend has destination type no larger than source", MI);
break;
case TargetOpcode::G_TRUNC:
case TargetOpcode::G_TRUNC_SSAT_S:
case TargetOpcode::G_TRUNC_SSAT_U:
case TargetOpcode::G_TRUNC_USAT_U:
case TargetOpcode::G_FPTRUNC:
if (DstSize >= SrcSize)
report("Generic truncate has destination type no smaller than source",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,15 @@
# DEBUG-NEXT: G_TRUNC (opcode {{[0-9]+}}): 2 type indices, 0 imm indices
# DEBUG-NEXT: .. type index coverage check SKIPPED: user-defined predicate detected
# DEBUG-NEXT: .. imm index coverage check SKIPPED: user-defined predicate detected
# DEBUG-NEXT: G_TRUNC_SSAT_S (opcode {{[0-9]+}}): 2 type indices, 0 imm indices
# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
# DEBUG-NEXT: G_TRUNC_SSAT_U (opcode {{[0-9]+}}): 2 type indices, 0 imm indices
# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
# DEBUG-NEXT: G_TRUNC_USAT_U (opcode {{[0-9]+}}): 2 type indices, 0 imm indices
# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
# DEBUG-NEXT: G_CONSTANT (opcode {{[0-9]+}}): 1 type index, 0 imm indices
# DEBUG-NEXT: .. the first uncovered type index: 1, OK
# DEBUG-NEXT: .. the first uncovered imm index: 0, OK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,15 @@
# DEBUG-NEXT: G_TRUNC (opcode {{[0-9]+}}): 2 type indices, 0 imm indices
# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
# DEBUG-NEXT: G_TRUNC_SSAT_S (opcode {{[0-9]+}}): 2 type indices, 0 imm indices
# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
# DEBUG-NEXT: G_TRUNC_SSAT_U (opcode {{[0-9]+}}): 2 type indices, 0 imm indices
# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
# DEBUG-NEXT: G_TRUNC_USAT_U (opcode {{[0-9]+}}): 2 type indices, 0 imm indices
# DEBUG-NEXT: .. type index coverage check SKIPPED: no rules defined
# DEBUG-NEXT: .. imm index coverage check SKIPPED: no rules defined
# DEBUG-NEXT: G_CONSTANT (opcode {{[0-9]+}}): 1 type index, 0 imm indices
# DEBUG-NEXT: .. the first uncovered type index: 1, OK
# DEBUG-NEXT: .. the first uncovered imm index: 0, OK
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/MC/ELF/mc-dump.s
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# CHECK-NEXT:0 Data Size:0 []
# CHECK-NEXT: Symbol @0 _start
# CHECK-NEXT:0 Org Offset:3 Value:0
# CHECK-NEXT:3 Relaxable Size:2 <MCInst #1996 <MCOperand Expr:.Ltmp0>>
# CHECK-NEXT:3 Relaxable Size:2 <MCInst #1999 <MCOperand Expr:.Ltmp0>>
# CHECK-NEXT: Fixup @1 Value:.Ltmp0 Kind:4001
# CHECK-NEXT:5 Data Size:16 [48,8b,04,25,00,00,00,00,48,8b,04,25,00,00,00,00]
# CHECK-NEXT: Fixup @4 Value:f0@<variant 11> Kind:4017
Expand Down
Loading