Repository: llvm/llvm-project
Pull request #136548: [RISCV] Add ISel patterns for Xqcia instructions
Author: login: hchandel
Labels: name: backend:RISC-V
Created: 2025-04-21T08:54:14Z | Merged: 2025-04-22T10:10:04Z

This patch adds instruction selection patterns for generating the integer
arithmetic instructions.


Diff:
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 98fba9e86e88a..850ea647ea049 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -422,6 +422,15 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
   else if (!Subtarget.hasVendorXTHeadCondMov())
     setOperationAction(ISD::SELECT, XLenVT, Custom);
 
+  if (Subtarget.hasVendorXqcia() && !Subtarget.is64Bit()) {
+    setOperationAction(ISD::UADDSAT, MVT::i32, Legal);
+    setOperationAction(ISD::SADDSAT, MVT::i32, Legal);
+    setOperationAction(ISD::USUBSAT, MVT::i32, Legal);
+    setOperationAction(ISD::SSUBSAT, MVT::i32, Legal);
+    setOperationAction(ISD::SSHLSAT, MVT::i32, Legal);
+    setOperationAction(ISD::USHLSAT, MVT::i32, Legal);
+  }
+
   static const unsigned FPLegalNodeTypes[] = {
       ISD::FMINNUM,       ISD::FMAXNUM,        ISD::FMINIMUMNUM,
       ISD::FMAXIMUMNUM,   ISD::LRINT,          ISD::LLRINT,
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
index bb0818b001d38..741c4ce8f5d1e 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
@@ -1247,6 +1247,10 @@ class PatGprNoX0Simm32NoSimm26<SDPatternOperator OpNode, RVInst48 Inst>
     : Pat<(i32 (OpNode (i32 GPRNoX0:$rs1), simm32_nosimm26:$imm)),
           (Inst GPRNoX0:$rs1, simm32_nosimm26:$imm)>;
 
+class PatGprNoX0GprNoX0<SDPatternOperator OpNode, RVInstR Inst>
+    : Pat<(i32 (OpNode (i32 GPRNoX0:$rs1), (i32 GPRNoX0:$rs2))),
+          (Inst GPRNoX0:$rs1, GPRNoX0:$rs2)>;
+
 class QC48LdPat<PatFrag LoadOp, RVInst48 Inst>
     : Pat<(i32 (LoadOp (AddLike (i32 GPR:$rs1), simm26_nosimm12:$imm26))),
           (Inst GPR:$rs1, simm26_nosimm12:$imm26)>;
@@ -1308,5 +1312,14 @@ let Predicates = [HasVendorXqcisls, IsRV32], AddedComplexity = 1 in {
   def : QCScaledStPat<store, QC_SRW>;
 } // Predicates = [HasVendorXqcisls, IsRV32], AddedComplexity = 1
 
+let Predicates = [HasVendorXqcia, IsRV32] in {
+def : PatGprNoX0GprNoX0<saddsat, QC_ADDSAT>;
+def : PatGprNoX0GprNoX0<uaddsat, QC_ADDUSAT>;
+def : PatGprNoX0GprNoX0<ssubsat, QC_SUBSAT>;
+def : PatGprNoX0GprNoX0<usubsat, QC_SUBUSAT>;
+def : PatGprNoX0GprNoX0<ushlsat, QC_SHLUSAT>;
+def : PatGprNoX0GprNoX0<sshlsat, QC_SHLSAT>;
+} // Predicates = [HasVendorXqcia, IsRV32]
+
 let Predicates = [HasVendorXqciint, IsRV32] in
 def : Pat<(riscv_mileaveret_glue), (QC_C_MILEAVERET)>;
diff --git a/llvm/test/CodeGen/RISCV/xqcia.ll b/llvm/test/CodeGen/RISCV/xqcia.ll
new file mode 100644
index 0000000000000..c75bb9daefcf2
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/xqcia.ll
@@ -0,0 +1,128 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; Test that we are able to generate the Xqcia instructions
+; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
+; RUN:   | FileCheck %s --check-prefixes=RV32I
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-xqcia -verify-machineinstrs < %s \
+; RUN:   | FileCheck %s --check-prefixes=RV32IXQCIA
+
+define i32 @addsat(i32 %a, i32 %b) {
+; RV32I-LABEL: addsat:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    mv a2, a0
+; RV32I-NEXT:    add a0, a0, a1
+; RV32I-NEXT:    slt a2, a0, a2
+; RV32I-NEXT:    slti a1, a1, 0
+; RV32I-NEXT:    beq a1, a2, .LBB0_2
+; RV32I-NEXT:  # %bb.1:
+; RV32I-NEXT:    srai a0, a0, 31
+; RV32I-NEXT:    lui a1, 524288
+; RV32I-NEXT:    xor a0, a0, a1
+; RV32I-NEXT:  .LBB0_2:
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIA-LABEL: addsat:
+; RV32IXQCIA:       # %bb.0:
+; RV32IXQCIA-NEXT:    qc.addsat a0, a0, a1
+; RV32IXQCIA-NEXT:    ret
+ %saddsat = tail call i32 @llvm.sadd.sat.i32(i32 %a,i32 %b)
+ ret i32 %saddsat
+}
+
+define i32 @addusat(i32 %a, i32 %b) {
+; RV32I-LABEL: addusat:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    add a1, a0, a1
+; RV32I-NEXT:    sltu a0, a1, a0
+; RV32I-NEXT:    neg a0, a0
+; RV32I-NEXT:    or a0, a0, a1
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIA-LABEL: addusat:
+; RV32IXQCIA:       # %bb.0:
+; RV32IXQCIA-NEXT:    qc.addusat a0, a0, a1
+; RV32IXQCIA-NEXT:    ret
+ %uaddsat = tail call i32 @llvm.uadd.sat.i32(i32 %a,i32 %b)
+ ret i32 %uaddsat
+}
+
+define i32 @subsat(i32 %a, i32 %b) {
+; RV32I-LABEL: subsat:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    mv a2, a0
+; RV32I-NEXT:    sgtz a3, a1
+; RV32I-NEXT:    sub a0, a0, a1
+; RV32I-NEXT:    slt a1, a0, a2
+; RV32I-NEXT:    beq a3, a1, .LBB2_2
+; RV32I-NEXT:  # %bb.1:
+; RV32I-NEXT:    srai a0, a0, 31
+; RV32I-NEXT:    lui a1, 524288
+; RV32I-NEXT:    xor a0, a0, a1
+; RV32I-NEXT:  .LBB2_2:
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIA-LABEL: subsat:
+; RV32IXQCIA:       # %bb.0:
+; RV32IXQCIA-NEXT:    qc.subsat a0, a0, a1
+; RV32IXQCIA-NEXT:    ret
+ %ssubsat = tail call i32 @llvm.ssub.sat.i32(i32 %a,i32 %b)
+ ret i32 %ssubsat
+}
+
+define i32 @subusat(i32 %a, i32 %b) {
+; RV32I-LABEL: subusat:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    sub a1, a0, a1
+; RV32I-NEXT:    sltu a0, a0, a1
+; RV32I-NEXT:    addi a0, a0, -1
+; RV32I-NEXT:    and a0, a0, a1
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIA-LABEL: subusat:
+; RV32IXQCIA:       # %bb.0:
+; RV32IXQCIA-NEXT:    qc.subusat a0, a0, a1
+; RV32IXQCIA-NEXT:    ret
+ %usubsat = tail call i32 @llvm.usub.sat.i32(i32 %a,i32 %b)
+ ret i32 %usubsat
+}
+
+define i32 @shlusat(i32 %a, i32 %b) {
+; RV32I-LABEL: shlusat:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    sll a2, a0, a1
+; RV32I-NEXT:    srl a1, a2, a1
+; RV32I-NEXT:    xor a0, a0, a1
+; RV32I-NEXT:    seqz a0, a0
+; RV32I-NEXT:    addi a0, a0, -1
+; RV32I-NEXT:    or a0, a0, a2
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIA-LABEL: shlusat:
+; RV32IXQCIA:       # %bb.0:
+; RV32IXQCIA-NEXT:    qc.shlusat a0, a0, a1
+; RV32IXQCIA-NEXT:    ret
+ %ushlsat = tail call i32 @llvm.ushl.sat.i32(i32 %a,i32 %b)
+ ret i32 %ushlsat
+}
+
+define i32 @shlsat(i32 %a, i32 %b) {
+; RV32I-LABEL: shlsat:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    mv a2, a0
+; RV32I-NEXT:    sll a0, a0, a1
+; RV32I-NEXT:    sra a1, a0, a1
+; RV32I-NEXT:    beq a2, a1, .LBB5_2
+; RV32I-NEXT:  # %bb.1:
+; RV32I-NEXT:    srai a2, a2, 31
+; RV32I-NEXT:    lui a0, 524288
+; RV32I-NEXT:    addi a0, a0, -1
+; RV32I-NEXT:    xor a0, a2, a0
+; RV32I-NEXT:  .LBB5_2:
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIA-LABEL: shlsat:
+; RV32IXQCIA:       # %bb.0:
+; RV32IXQCIA-NEXT:    qc.shlsat a0, a0, a1
+; RV32IXQCIA-NEXT:    ret
+ %sshlsat = tail call i32 @llvm.sshl.sat.i32(i32 %a,i32 %b)
+ ret i32 %sshlsat
+}


Reviews:
user: login: lenary
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-21T09:08:44Z
user: login: topperc
body: LGTM
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-04-21T17:27:15Z
user: login: topperc
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-04-21T17:30:17Z
user: login: hchandel
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T06:29:32Z
user: login: svs-quic
body: LGTM!
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-22T06:35:16Z

Review comments:
diff_hunk: @@ -422,6 +422,15 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
   else if (!Subtarget.hasVendorXTHeadCondMov())
     setOperationAction(ISD::SELECT, XLenVT, Custom);
 
+  if (Subtarget.hasVendorXqcia()) {
path: llvm/lib/Target/RISCV/RISCVISelLowering.cpp
user: login: topperc
body: Can we add `&& !Subtarget.is64Bit()` here to be consistent with the isel patterns which include `IsRV32`?
created_at: 2025-04-21T17:30:17Z
author_association: COLLABORATOR
diff_hunk: @@ -422,6 +422,15 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
   else if (!Subtarget.hasVendorXTHeadCondMov())
     setOperationAction(ISD::SELECT, XLenVT, Custom);
 
+  if (Subtarget.hasVendorXqcia()) {
path: llvm/lib/Target/RISCV/RISCVISelLowering.cpp
user: login: hchandel
body: Added this check
created_at: 2025-04-22T06:29:32Z
author_association: CONTRIBUTOR

PR comments:
user: login: llvmbot
created_at: 2025-04-21T08:54:49Z
body: 
<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-risc-v

Author: quic_hchandel (hchandel)

<details>
<summary>Changes</summary>

This patch adds instruction selection patterns for generating the integer
arithmetic instructions.

---
Full diff: https://github.com/llvm/llvm-project/pull/136548.diff


3 Files Affected:

- (modified) llvm/lib/Target/RISCV/RISCVISelLowering.cpp (+9) 
- (modified) llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td (+13) 
- (added) llvm/test/CodeGen/RISCV/xqcia.ll (+128) 


``````````diff
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 98fba9e86e88a..d4ca9f48b7aef 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -422,6 +422,15 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
   else if (!Subtarget.hasVendorXTHeadCondMov())
     setOperationAction(ISD::SELECT, XLenVT, Custom);
 
+  if (Subtarget.hasVendorXqcia()){
+    setOperationAction(ISD::UADDSAT, MVT::i32, Legal);
+    setOperationAction(ISD::SADDSAT, MVT::i32, Legal);
+    setOperationAction(ISD::USUBSAT, MVT::i32, Legal);
+    setOperationAction(ISD::SSUBSAT, MVT::i32, Legal);
+    setOperationAction(ISD::SSHLSAT, MVT::i32, Legal);
+    setOperationAction(ISD::USHLSAT, MVT::i32, Legal);
+  }
+
   static const unsigned FPLegalNodeTypes[] = {
       ISD::FMINNUM,       ISD::FMAXNUM,        ISD::FMINIMUMNUM,
       ISD::FMAXIMUMNUM,   ISD::LRINT,          ISD::LLRINT,
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
index bb0818b001d38..741c4ce8f5d1e 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoXqci.td
@@ -1247,6 +1247,10 @@ class PatGprNoX0Simm32NoSimm26<SDPatternOperator OpNode, RVInst48 Inst>
     : Pat<(i32 (OpNode (i32 GPRNoX0:$rs1), simm32_nosimm26:$imm)),
           (Inst GPRNoX0:$rs1, simm32_nosimm26:$imm)>;
 
+class PatGprNoX0GprNoX0<SDPatternOperator OpNode, RVInstR Inst>
+    : Pat<(i32 (OpNode (i32 GPRNoX0:$rs1), (i32 GPRNoX0:$rs2))),
+          (Inst GPRNoX0:$rs1, GPRNoX0:$rs2)>;
+
 class QC48LdPat<PatFrag LoadOp, RVInst48 Inst>
     : Pat<(i32 (LoadOp (AddLike (i32 GPR:$rs1), simm26_nosimm12:$imm26))),
           (Inst GPR:$rs1, simm26_nosimm12:$imm26)>;
@@ -1308,5 +1312,14 @@ let Predicates = [HasVendorXqcisls, IsRV32], AddedComplexity = 1 in {
   def : QCScaledStPat<store, QC_SRW>;
 } // Predicates = [HasVendorXqcisls, IsRV32], AddedComplexity = 1
 
+let Predicates = [HasVendorXqcia, IsRV32] in {
+def : PatGprNoX0GprNoX0<saddsat, QC_ADDSAT>;
+def : PatGprNoX0GprNoX0<uaddsat, QC_ADDUSAT>;
+def : PatGprNoX0GprNoX0<ssubsat, QC_SUBSAT>;
+def : PatGprNoX0GprNoX0<usubsat, QC_SUBUSAT>;
+def : PatGprNoX0GprNoX0<ushlsat, QC_SHLUSAT>;
+def : PatGprNoX0GprNoX0<sshlsat, QC_SHLSAT>;
+} // Predicates = [HasVendorXqcia, IsRV32]
+
 let Predicates = [HasVendorXqciint, IsRV32] in
 def : Pat<(riscv_mileaveret_glue), (QC_C_MILEAVERET)>;
diff --git a/llvm/test/CodeGen/RISCV/xqcia.ll b/llvm/test/CodeGen/RISCV/xqcia.ll
new file mode 100644
index 0000000000000..c75bb9daefcf2
--- /dev/null
+++ b/llvm/test/CodeGen/RISCV/xqcia.ll
@@ -0,0 +1,128 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; Test that we are able to generate the Xqcia instructions
+; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
+; RUN:   | FileCheck %s --check-prefixes=RV32I
+; RUN: llc -mtriple=riscv32 -mattr=+experimental-xqcia -verify-machineinstrs < %s \
+; RUN:   | FileCheck %s --check-prefixes=RV32IXQCIA
+
+define i32 @addsat(i32 %a, i32 %b) {
+; RV32I-LABEL: addsat:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    mv a2, a0
+; RV32I-NEXT:    add a0, a0, a1
+; RV32I-NEXT:    slt a2, a0, a2
+; RV32I-NEXT:    slti a1, a1, 0
+; RV32I-NEXT:    beq a1, a2, .LBB0_2
+; RV32I-NEXT:  # %bb.1:
+; RV32I-NEXT:    srai a0, a0, 31
+; RV32I-NEXT:    lui a1, 524288
+; RV32I-NEXT:    xor a0, a0, a1
+; RV32I-NEXT:  .LBB0_2:
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIA-LABEL: addsat:
+; RV32IXQCIA:       # %bb.0:
+; RV32IXQCIA-NEXT:    qc.addsat a0, a0, a1
+; RV32IXQCIA-NEXT:    ret
+ %saddsat = tail call i32 @llvm.sadd.sat.i32(i32 %a,i32 %b)
+ ret i32 %saddsat
+}
+
+define i32 @addusat(i32 %a, i32 %b) {
+; RV32I-LABEL: addusat:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    add a1, a0, a1
+; RV32I-NEXT:    sltu a0, a1, a0
+; RV32I-NEXT:    neg a0, a0
+; RV32I-NEXT:    or a0, a0, a1
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIA-LABEL: addusat:
+; RV32IXQCIA:       # %bb.0:
+; RV32IXQCIA-NEXT:    qc.addusat a0, a0, a1
+; RV32IXQCIA-NEXT:    ret
+ %uaddsat = tail call i32 @llvm.uadd.sat.i32(i32 %a,i32 %b)
+ ret i32 %uaddsat
+}
+
+define i32 @subsat(i32 %a, i32 %b) {
+; RV32I-LABEL: subsat:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    mv a2, a0
+; RV32I-NEXT:    sgtz a3, a1
+; RV32I-NEXT:    sub a0, a0, a1
+; RV32I-NEXT:    slt a1, a0, a2
+; RV32I-NEXT:    beq a3, a1, .LBB2_2
+; RV32I-NEXT:  # %bb.1:
+; RV32I-NEXT:    srai a0, a0, 31
+; RV32I-NEXT:    lui a1, 524288
+; RV32I-NEXT:    xor a0, a0, a1
+; RV32I-NEXT:  .LBB2_2:
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIA-LABEL: subsat:
+; RV32IXQCIA:       # %bb.0:
+; RV32IXQCIA-NEXT:    qc.subsat a0, a0, a1
+; RV32IXQCIA-NEXT:    ret
+ %ssubsat = tail call i32 @llvm.ssub.sat.i32(i32 %a,i32 %b)
+ ret i32 %ssubsat
+}
+
+define i32 @subusat(i32 %a, i32 %b) {
+; RV32I-LABEL: subusat:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    sub a1, a0, a1
+; RV32I-NEXT:    sltu a0, a0, a1
+; RV32I-NEXT:    addi a0, a0, -1
+; RV32I-NEXT:    and a0, a0, a1
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIA-LABEL: subusat:
+; RV32IXQCIA:       # %bb.0:
+; RV32IXQCIA-NEXT:    qc.subusat a0, a0, a1
+; RV32IXQCIA-NEXT:    ret
+ %usubsat = tail call i32 @llvm.usub.sat.i32(i32 %a,i32 %b)
+ ret i32 %usubsat
+}
+
+define i32 @shlusat(i32 %a, i32 %b) {
+; RV32I-LABEL: shlusat:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    sll a2, a0, a1
+; RV32I-NEXT:    srl a1, a2, a1
+; RV32I-NEXT:    xor a0, a0, a1
+; RV32I-NEXT:    seqz a0, a0
+; RV32I-NEXT:    addi a0, a0, -1
+; RV32I-NEXT:    or a0, a0, a2
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIA-LABEL: shlusat:
+; RV32IXQCIA:       # %bb.0:
+; RV32IXQCIA-NEXT:    qc.shlusat a0, a0, a1
+; RV32IXQCIA-NEXT:    ret
+ %ushlsat = tail call i32 @llvm.ushl.sat.i32(i32 %a,i32 %b)
+ ret i32 %ushlsat
+}
+
+define i32 @shlsat(i32 %a, i32 %b) {
+; RV32I-LABEL: shlsat:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    mv a2, a0
+; RV32I-NEXT:    sll a0, a0, a1
+; RV32I-NEXT:    sra a1, a0, a1
+; RV32I-NEXT:    beq a2, a1, .LBB5_2
+; RV32I-NEXT:  # %bb.1:
+; RV32I-NEXT:    srai a2, a2, 31
+; RV32I-NEXT:    lui a0, 524288
+; RV32I-NEXT:    addi a0, a0, -1
+; RV32I-NEXT:    xor a0, a2, a0
+; RV32I-NEXT:  .LBB5_2:
+; RV32I-NEXT:    ret
+;
+; RV32IXQCIA-LABEL: shlsat:
+; RV32IXQCIA:       # %bb.0:
+; RV32IXQCIA-NEXT:    qc.shlsat a0, a0, a1
+; RV32IXQCIA-NEXT:    ret
+ %sshlsat = tail call i32 @llvm.sshl.sat.i32(i32 %a,i32 %b)
+ ret i32 %sshlsat
+}

``````````

</details>

author_association: MEMBER
user: login: github-actions[bot]
created_at: 2025-04-21T08:56:38Z
body: <!--LLVM CODE FORMAT COMMENT: {clang-format}-->

:white_check_mark: With the latest revision this PR passed the C/C++ code formatter.
author_association: NONE
user: login: lenary
created_at: 2025-04-21T09:09:04Z
body: LGTM!
author_association: MEMBER
user: login: hchandel
created_at: 2025-04-22T10:09:42Z
body: All Checks have passed. Merging now.
author_association: CONTRIBUTOR