https://github.com/patricklapgar updated 
https://github.com/llvm/llvm-project/pull/216899

>From 72ecc7caedba6ab9e30c3d0639519506349ec69b Mon Sep 17 00:00:00 2001
From: patricklapgar <[email protected]>
Date: Mon, 17 Aug 2026 18:54:00 -0700
Subject: [PATCH 1/6] Add tr instruction intrinsic def

---
 llvm/include/llvm/IR/IntrinsicsSystemZ.td | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/llvm/include/llvm/IR/IntrinsicsSystemZ.td 
b/llvm/include/llvm/IR/IntrinsicsSystemZ.td
index 38b7463c7b078..8e08a105c2d44 100644
--- a/llvm/include/llvm/IR/IntrinsicsSystemZ.td
+++ b/llvm/include/llvm/IR/IntrinsicsSystemZ.td
@@ -503,3 +503,10 @@ let TargetPrefix = "s390" in {
                        Intrinsic<[llvm_i64_ty], [llvm_i64_ty, llvm_i64_ty],
                                  [IntrNoMem]>;
 }
+
+let TargetPrefix = "s390" in {
+  def int_s390_translate : Intrinsic<[],
+                                [llvm_ptr_ty, llvm_ptr_ty, llvm_i32_ty],
+                                [NoCapture<ArgIndex<0>>, 
NoCapture<ArgIndex<1>>]>,
+                                ClangBuiltin<"__builtin_s390_tr">;
+}
\ No newline at end of file

>From 7f7fdf953b62c47a7365b0338ed8067aa16d8e43 Mon Sep 17 00:00:00 2001
From: patricklapgar <[email protected]>
Date: Mon, 17 Aug 2026 18:54:55 -0700
Subject: [PATCH 2/6] Support tr intrinsic handling pre-lowering

---
 .../Target/SystemZ/SystemZISelLowering.cpp    | 33 +++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp 
b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
index 710832525426b..1f2db88bfd7a5 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
@@ -5444,6 +5444,39 @@ SDValue SystemZTargetLowering::lowerPREFETCH(SDValue Op,
 SDValue
 SystemZTargetLowering::lowerINTRINSIC_W_CHAIN(SDValue Op,
                                               SelectionDAG &DAG) const {
+  SDLoc DL(op);
+  unsigned IntNo = Op.getConstantOperandVal(1);
+  
+  // Intercept translation intrinsic
+  if (IntNo == Intrinsic::int_s390_translate) {
+    SDValue Chain = Op.getOperand(0);
+    SDValue Src   = Op.getOperand(2);
+    SDValue Tbl   = Op.getOperand(3);
+    SDValue Len   = Op.getOperand(4);
+
+    // If the input length is a static constant,
+    // create a new target node and return it
+    if(auto *ConstLen = dyn_cast<ConstantSDNode>(Len)) {
+      uint64_t Value = ConstLen->getZExtValue();
+
+      if(Value >= 1 && Value <= 256) {
+        uint64_t HardwareLen = Value - 1;
+        SDValue ImmLen = DAG.getTargetConstant(HardwareLen, DL, MVT::i32);
+
+        SmallVector<SDValue, 4> Ops;
+        Ops.push_back(Chain);
+        Ops.push_back(Src);
+        Ops.push_back(ImmLen);
+        Ops.push_back(Tbl);
+
+        return DAG.getNode(SystemZISD::TR, DL, MVT::Other, Ops);
+      }
+    }
+
+    // If the input length is variable, return the SDValue as-is
+    return SDValue();
+  }
+  
   unsigned Opcode, CCValid;
   if (isIntrinsicWithCCAndChain(Op, Opcode, CCValid)) {
     assert(Op->getNumValues() == 2 && "Expected only CC result and chain");

>From 309a4287d678e43e0fde9515dd9f3bb0db6295bd Mon Sep 17 00:00:00 2001
From: patricklapgar <[email protected]>
Date: Mon, 17 Aug 2026 18:56:43 -0700
Subject: [PATCH 3/6] Support tr pseudo function and opcode pattern

---
 llvm/lib/Target/SystemZ/SystemZInstrInfo.td | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td 
b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
index f53716a663ed8..e527916436ce2 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
@@ -1923,6 +1923,25 @@ let Predicates = [FeatureConcurrentFunctions], Uses = 
[R0D], Defs = [CC],
   def PFCR : BinaryRSY<"pfcr", 0xEB16, null_frag, GR64>;
 }
 
+//===----------------------------------------------------------------------===//
+// Translate
+//===----------------------------------------------------------------------===//
+
+// Handle variable length data via pseudo
+def TR_VAR 
+  : Pseudo<(outs), (ins GR64:$src, GR64:$tbl, GR32:$len),
+           [(int_s390_translate GR64:$src, GR64:$tbl, GR32:$len)]> {
+  let hasSideEffects = 1;
+  let mayLoad = 1;
+  let mayStore = 1;
+  let Uses = [CC];
+  let Defs = [CC];
+}
+
+// Handle constant length data by pattern matching directly to TR opcode
+def : Pat<(z_tr GR64:$src, imm:$len, GR64:$tbl),
+          (TR GR64:$src, imm:$len, GR64:$tbl)>;
+
 
//===----------------------------------------------------------------------===//
 // Translate and convert
 
//===----------------------------------------------------------------------===//

>From 3638c14eae5f8ec59370a123588cd11f11ccfff1 Mon Sep 17 00:00:00 2001
From: patricklapgar <[email protected]>
Date: Mon, 17 Aug 2026 18:57:28 -0700
Subject: [PATCH 4/6] Add SystemZ TR opcode def

---
 llvm/lib/Target/SystemZ/SystemZOperators.td | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/llvm/lib/Target/SystemZ/SystemZOperators.td 
b/llvm/lib/Target/SystemZ/SystemZOperators.td
index 005b79638218a..75b708d0b66fb 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperators.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperators.td
@@ -257,6 +257,10 @@ def SDT_ZSetJmp             : SDTypeProfile<1, 1,
                                              SDTCisPtrTy<1>]>;
 def SDT_ZLongJmp            : SDTypeProfile<0, 1, [SDTCisPtrTy<0>]>;
 
+def SDT_ZTranslate          : SDTypeProfile<0, 3, 
+                                            [SDTCisPtrTy<0>,
+                                             SDTCisInt<1>,
+                                             SDTCisPtrTy<2>]>;
 
 
//===----------------------------------------------------------------------===//
 // Node definitions
@@ -420,6 +424,10 @@ def z_storeeswap       : SDNode<"SystemZISD::VSTER", 
SDTStore,
 def z_stckf            : SDNode<"SystemZISD::STCKF", SDT_ZStoreInherent,
                                 [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>;
 
+// Translate string data from one code to another code
+def z_tr               : SDNode<"SystemZISD::TR", SDT_ZTranslate, 
+                                [SDNPHasChain, SDNPSideEffect, SDNPMayLoad, 
SDNPMayStore]>;
+
 // Test Data Class.
 //
 // Operand 0: the value to test

>From 9392cb5f539967cedf139b9b82e9eb070e4cf92a Mon Sep 17 00:00:00 2001
From: patricklapgar <[email protected]>
Date: Mon, 31 Aug 2026 19:14:20 -0700
Subject: [PATCH 5/6] TR instruction source update (WIP)

---
 clang/include/clang/Basic/BuiltinsSystemZ.td  |  5 ++
 .../CodeGen/SystemZ/builtins-systemz-tr.c     | 37 +++++++++
 llvm/include/llvm/IR/IntrinsicsSystemZ.td     |  6 +-
 .../Target/SystemZ/SystemZISelLowering.cpp    | 76 +++++++++++--------
 llvm/lib/Target/SystemZ/SystemZISelLowering.h |  4 +
 llvm/lib/Target/SystemZ/SystemZInstrInfo.td   | 49 +++++++-----
 llvm/lib/Target/SystemZ/SystemZOperators.td   | 14 ++--
 .../CodeGen/SystemZ/translate-instruction.ll  | 45 +++++++++++
 8 files changed, 176 insertions(+), 60 deletions(-)
 create mode 100644 clang/test/CodeGen/SystemZ/builtins-systemz-tr.c
 create mode 100644 llvm/test/CodeGen/SystemZ/translate-instruction.ll

diff --git a/clang/include/clang/Basic/BuiltinsSystemZ.td 
b/clang/include/clang/Basic/BuiltinsSystemZ.td
index 946a03cd66170..978bf4ca39af0 100644
--- a/clang/include/clang/Basic/BuiltinsSystemZ.td
+++ b/clang/include/clang/Basic/BuiltinsSystemZ.td
@@ -375,3 +375,8 @@ let Attributes = [NoThrow, Const], Features = 
"vector-enhancements-3" in {
   def vclzq : SystemZTargetBuiltin<"__uint128_t(__uint128_t)">;
   def vctzq : SystemZTargetBuiltin<"__uint128_t(__uint128_t)">;
 }
+
+// String/General memory intrinsics
+let Attributes = [NoThrow] in {
+  def tr : SystemZTargetBuiltin<"void(void *, int, int, void *, int)">;
+}
\ No newline at end of file
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-tr.c 
b/clang/test/CodeGen/SystemZ/builtins-systemz-tr.c
new file mode 100644
index 0000000000000..18d78c6ca1391
--- /dev/null
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-tr.c
@@ -0,0 +1,37 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py 
UTC_ARGS: --version 6
+// RUN: %clang_cc1 -triple s390x-linux-gnu -target-cpu z13 -emit-llvm %s -o - 
| FileCheck %s
+
+// CHECK-LABEL: define dso_local void @test_tr_static(
+// CHECK-SAME: ptr noundef [[SRC:%.*]], ptr noundef [[TBL:%.*]]) 
#[[ATTR0:[0-9]+]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[SRC_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[TBL_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[SRC]], ptr [[SRC_ADDR]], align 8
+// CHECK-NEXT:    store ptr [[TBL]], ptr [[TBL_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[SRC_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[TBL_ADDR]], align 8
+// CHECK-NEXT:    call void @llvm.s390.translate(ptr [[TMP0]], i32 0, i32 16, 
ptr [[TMP1]], i32 0)
+// CHECK-NEXT:    ret void
+//
+void test_tr_static(char *src, char *tbl) {
+    __builtin_s390_tr(src, 0, 16, tbl, 0);
+}
+
+// CHECK-LABEL: define dso_local void @test_tr_variable(
+// CHECK-SAME: ptr noundef [[SRC:%.*]], ptr noundef [[TBL:%.*]], i32 noundef 
signext [[LEN:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[SRC_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[TBL_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[LEN_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    store ptr [[SRC]], ptr [[SRC_ADDR]], align 8
+// CHECK-NEXT:    store ptr [[TBL]], ptr [[TBL_ADDR]], align 8
+// CHECK-NEXT:    store i32 [[LEN]], ptr [[LEN_ADDR]], align 4
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[SRC_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4
+// CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[TBL_ADDR]], align 8
+// CHECK-NEXT:    call void @llvm.s390.translate(ptr [[TMP0]], i32 0, i32 
[[TMP1]], ptr [[TMP2]], i32 0)
+// CHECK-NEXT:    ret void
+//
+void test_tr_variable(char *src, char *tbl, int len) {
+    __builtin_s390_tr(src, 0, len, tbl, 0);
+}
diff --git a/llvm/include/llvm/IR/IntrinsicsSystemZ.td 
b/llvm/include/llvm/IR/IntrinsicsSystemZ.td
index 8e08a105c2d44..9d87202439053 100644
--- a/llvm/include/llvm/IR/IntrinsicsSystemZ.td
+++ b/llvm/include/llvm/IR/IntrinsicsSystemZ.td
@@ -506,7 +506,9 @@ let TargetPrefix = "s390" in {
 
 let TargetPrefix = "s390" in {
   def int_s390_translate : Intrinsic<[],
-                                [llvm_ptr_ty, llvm_ptr_ty, llvm_i32_ty],
-                                [NoCapture<ArgIndex<0>>, 
NoCapture<ArgIndex<1>>]>,
+                                [llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty, 
+                                 llvm_ptr_ty, llvm_i32_ty],
+                                [IntrArgMemOnly, 
+                                 NoCapture<ArgIndex<0>>, 
NoCapture<ArgIndex<3>>]>,
                                 ClangBuiltin<"__builtin_s390_tr">;
 }
\ No newline at end of file
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp 
b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
index fd7eabb5fc555..5e8884fa52cfc 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
@@ -5444,39 +5444,7 @@ SDValue SystemZTargetLowering::lowerPREFETCH(SDValue Op,
 SDValue
 SystemZTargetLowering::lowerINTRINSIC_W_CHAIN(SDValue Op,
                                               SelectionDAG &DAG) const {
-  SDLoc DL(op);
-  unsigned IntNo = Op.getConstantOperandVal(1);
-  
-  // Intercept translation intrinsic
-  if (IntNo == Intrinsic::int_s390_translate) {
-    SDValue Chain = Op.getOperand(0);
-    SDValue Src   = Op.getOperand(2);
-    SDValue Tbl   = Op.getOperand(3);
-    SDValue Len   = Op.getOperand(4);
-
-    // If the input length is a static constant,
-    // create a new target node and return it
-    if(auto *ConstLen = dyn_cast<ConstantSDNode>(Len)) {
-      uint64_t Value = ConstLen->getZExtValue();
-
-      if(Value >= 1 && Value <= 256) {
-        uint64_t HardwareLen = Value - 1;
-        SDValue ImmLen = DAG.getTargetConstant(HardwareLen, DL, MVT::i32);
-
-        SmallVector<SDValue, 4> Ops;
-        Ops.push_back(Chain);
-        Ops.push_back(Src);
-        Ops.push_back(ImmLen);
-        Ops.push_back(Tbl);
-
-        return DAG.getNode(SystemZISD::TR, DL, MVT::Other, Ops);
-      }
-    }
-
-    // If the input length is variable, return the SDValue as-is
-    return SDValue();
-  }
-  
+  SDLoc DL(Op);
   unsigned Opcode, CCValid;
   if (isIntrinsicWithCCAndChain(Op, Opcode, CCValid)) {
     assert(Op->getNumValues() == 2 && "Expected only CC result and chain");
@@ -10556,6 +10524,45 @@ MachineBasicBlock 
*SystemZTargetLowering::emitExt128(MachineInstr &MI,
   return MBB;
 }
 
+MachineBasicBlock *
+SystemZTargetLowering::emitTRWrapper(MachineInstr &MI,
+                                     MachineBasicBlock *MBB,
+                                     unsigned Opcode) const {
+  MachineFunction &MF = *MBB->getParent();
+  MachineRegisterInfo &MRI = MF.getRegInfo();
+  const SystemZInstrInfo *TII = 
+      static_cast<const SystemZInstrInfo *>(Subtarget.getInstrInfo());
+  DebugLoc DL = MI.getDebugLoc();
+
+  Register SrcReg = MI.getOperand(0).getReg();
+  int64_t D1Imm   = MI.getOperand(1).getImm();
+  Register LenReg = MI.getOperand(2).getReg();
+  Register TblReg = MI.getOperand(3).getReg();
+  int64_t D2Imm   = MI.getOperand(4).getImm();
+
+  MRI.constrainRegClass(SrcReg, &SystemZ::ADDR64BitRegClass);
+  MRI.constrainRegClass(TblReg, &SystemZ::ADDR64BitRegClass);
+
+  Register Len32Reg = MRI.createVirtualRegister(&SystemZ::GR32BitRegClass);
+  BuildMI(*MBB, MI, DL, TII->get(SystemZ::AHI), Len32Reg)
+      .addReg(LenReg)
+      .addImm(-1);
+  
+  // Must constrain 32-bit length register to 64-bit for EXRL execution
+  Register Len64Reg = MRI.createVirtualRegister(&SystemZ::ADDR64BitRegClass);
+  BuildMI(*MBB, MI, DL, TII->get(SystemZ::LLGFR), Len64Reg)
+      .addReg(Len32Reg);
+  
+  BuildMI(*MBB, MI, DL, TII->get(SystemZ::EXRL_Pseudo))
+      .addImm(Opcode)
+      .addReg(Len64Reg)
+      .addReg(SrcReg).addImm(D1Imm)
+      .addReg(TblReg).addImm(D2Imm);
+  
+  MI.eraseFromParent();
+  return MBB;
+}
+
 MachineBasicBlock *
 SystemZTargetLowering::emitMemMemWrapper(MachineInstr &MI,
                                          MachineBasicBlock *MBB,
@@ -11363,6 +11370,9 @@ MachineBasicBlock 
*SystemZTargetLowering::EmitInstrWithCustomInserter(
   case SystemZ::CMP_STACKGUARD_DAG:
     return emitStackGuardPseudo(MI, MBB, SystemZ::CMP_STACKGUARD);
 
+  case SystemZ::TR_Pseudo:
+    return emitTRWrapper(MI, MBB, SystemZ::TR);
+
   default:
     llvm_unreachable("Unexpected instr type to insert");
   }
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.h 
b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
index f01f7dcc6ab1d..b7b27532f7e4d 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.h
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
@@ -494,6 +494,10 @@ class SystemZTargetLowering : public TargetLowering {
   getTargetMMOFlags(const Instruction &I) const override;
   const TargetRegisterClass *getRepRegClassFor(MVT VT) const override;
 
+  MachineBasicBlock *emitTRWrapper(MachineInstr &MI,
+                                   MachineBasicBlock *MBB,
+                                   unsigned Opcode) const;
+
 private:
   bool isInternal(const Function *Fn) const;
   mutable std::map<const Function *, bool> IsInternalCache;
diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td 
b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
index e527916436ce2..2784a58d00952 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
@@ -1923,25 +1923,6 @@ let Predicates = [FeatureConcurrentFunctions], Uses = 
[R0D], Defs = [CC],
   def PFCR : BinaryRSY<"pfcr", 0xEB16, null_frag, GR64>;
 }
 
-//===----------------------------------------------------------------------===//
-// Translate
-//===----------------------------------------------------------------------===//
-
-// Handle variable length data via pseudo
-def TR_VAR 
-  : Pseudo<(outs), (ins GR64:$src, GR64:$tbl, GR32:$len),
-           [(int_s390_translate GR64:$src, GR64:$tbl, GR32:$len)]> {
-  let hasSideEffects = 1;
-  let mayLoad = 1;
-  let mayStore = 1;
-  let Uses = [CC];
-  let Defs = [CC];
-}
-
-// Handle constant length data by pattern matching directly to TR opcode
-def : Pat<(z_tr GR64:$src, imm:$len, GR64:$tbl),
-          (TR GR64:$src, imm:$len, GR64:$tbl)>;
-
 
//===----------------------------------------------------------------------===//
 // Translate and convert
 
//===----------------------------------------------------------------------===//
@@ -2290,6 +2271,36 @@ let hasSideEffects = 1 in {
                                           []>;
 }
 
+// Translate
+let hasSideEffects = 1, usesCustomInserter = 1,
+    hasNoSchedulingInfo = 1, mayLoad = 1, 
+    mayStore = 1, Defs = [CC] in {
+  def TR_Pseudo : Pseudo<(outs), (ins GR64:$src, i32imm:$D1, GR32:$len, 
+                                    GR64:$tbl, i32imm:$D2), []>;
+                                  // [(z_tr GR64:$src, i32imm:$D1, GR32:$len, 
+                                  //   GR64:$tbl, i32imm:$D2)]>;
+}
+
+// Transform constant length N (1..256) into N - 1 for SS-format
+def tr_len : SDNodeXForm<imm, [{
+  return CurDAG->getTargetConstant(N->getZExtValue() - 1, SDLoc(N), MVT::i32);
+}]>;
+
+def tr_len_leaf : ImmLeaf<i32,
+  [{ return N->getZExtValue() >= 1 && N->getZExtValue() <= 256; }],
+  tr_len
+>;
+
+// Match TR invocation w/ a static constant provided as the length
+def : Pat<(int_s390_translate ADDR64:$src, i32:$D1, tr_len_leaf:$len, 
+                                ADDR64:$tbl, i32:$D2),
+          (TR (bdladdr12onlylen8 ADDR64:$src, i32:$D1, tr_len_leaf:$len), 
+               (bdaddr12only ADDR64:$tbl, i32:$D2))>;
+
+// Match TR invocation w/ a variable provided as the length
+def : Pat<(int_s390_translate ADDR64:$src, i32:$D1, GR32:$len, GR64:$tbl, 
i32:$D2),
+          (TR_Pseudo ADDR64:$src, i32:$D1, GR32:$len, ADDR64:$tbl, i32:$D2)>;
+
 
//===----------------------------------------------------------------------===//
 // .insn directive instructions
 
//===----------------------------------------------------------------------===//
diff --git a/llvm/lib/Target/SystemZ/SystemZOperators.td 
b/llvm/lib/Target/SystemZ/SystemZOperators.td
index 75b708d0b66fb..b0a5327d92c04 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperators.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperators.td
@@ -257,10 +257,12 @@ def SDT_ZSetJmp             : SDTypeProfile<1, 1,
                                              SDTCisPtrTy<1>]>;
 def SDT_ZLongJmp            : SDTypeProfile<0, 1, [SDTCisPtrTy<0>]>;
 
-def SDT_ZTranslate          : SDTypeProfile<0, 3, 
-                                            [SDTCisPtrTy<0>,
-                                             SDTCisInt<1>,
-                                             SDTCisPtrTy<2>]>;
+// def SDT_ZTranslate          : SDTypeProfile<0, 5, 
+//                                             [SDTCisPtrTy<0>,
+//                                              SDTCisInt<1>,  
+//                                              SDTCisInt<2>,  
+//                                              SDTCisPtrTy<3>,
+//                                              SDTCisInt<4>]>;
 
 
//===----------------------------------------------------------------------===//
 // Node definitions
@@ -425,8 +427,8 @@ def z_stckf            : SDNode<"SystemZISD::STCKF", 
SDT_ZStoreInherent,
                                 [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>;
 
 // Translate string data from one code to another code
-def z_tr               : SDNode<"SystemZISD::TR", SDT_ZTranslate, 
-                                [SDNPHasChain, SDNPSideEffect, SDNPMayLoad, 
SDNPMayStore]>;
+// def z_tr               : SDNode<"SystemZISD::TR", SDT_ZTranslate, 
+//                                 [SDNPHasChain, SDNPMayLoad, SDNPMayStore]>;
 
 // Test Data Class.
 //
diff --git a/llvm/test/CodeGen/SystemZ/translate-instruction.ll 
b/llvm/test/CodeGen/SystemZ/translate-instruction.ll
new file mode 100644
index 0000000000000..8458875838e91
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/translate-instruction.ll
@@ -0,0 +1,45 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py 
UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z13 -verify-machineinstrs | 
FileCheck %s
+
+declare void @llvm.s390.translate(ptr, i32, i32, ptr, i32)
+
+;Test 1: Static constant length -> Direct TR instruction
+define void @test_tr_static(ptr %src, ptr %tbl) {
+; CHECK-LABEL: test_tr_static:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    stmg %r6, %r15, 48(%r15)
+; CHECK-NEXT:    .cfi_offset %r6, -112
+; CHECK-NEXT:    .cfi_offset %r14, -48
+; CHECK-NEXT:    .cfi_offset %r15, -40
+; CHECK-NEXT:    aghi %r15, -160
+; CHECK-NEXT:    .cfi_def_cfa_offset 320
+; CHECK-NEXT:    lgr %r5, %r3
+; CHECK-NEXT:    lhi %r3, 0
+; CHECK-NEXT:    lhi %r4, 16
+; CHECK-NEXT:    lhi %r6, 0
+; CHECK-NEXT:    brasl %r14, llvm.s390.translate@PLT
+; CHECK-NEXT:    lmg %r6, %r15, 208(%r15)
+; CHECK-NEXT:    br %r14
+  call void @llvm.s390.translate(ptr %src, i32 0, i32 16, ptr %tbl, i32 0)
+  ret void
+}
+
+; Test 2: Variable length -> AHI (decrement by 1) + EXRL execution
+define void @test_tr_variable(ptr %src, ptr %tbl, i32 %len) {
+; CHECK-LABEL: test_tr_variable:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    stmg %r6, %r15, 48(%r15)
+; CHECK-NEXT:    .cfi_offset %r6, -112
+; CHECK-NEXT:    .cfi_offset %r14, -48
+; CHECK-NEXT:    .cfi_offset %r15, -40
+; CHECK-NEXT:    aghi %r15, -160
+; CHECK-NEXT:    .cfi_def_cfa_offset 320
+; CHECK-NEXT:    lgr %r5, %r3
+; CHECK-NEXT:    lhi %r3, 0
+; CHECK-NEXT:    lhi %r6, 0
+; CHECK-NEXT:    brasl %r14, llvm.s390.translate@PLT
+; CHECK-NEXT:    lmg %r6, %r15, 208(%r15)
+; CHECK-NEXT:    br %r14
+  call void @llvm.s390.translate(ptr %src, i32 0, i32 %len, ptr %tbl, i32 0)
+  ret void
+}

>From c36c7ef3ac69bc41aea3d152e1c741734886c575 Mon Sep 17 00:00:00 2001
From: patricklapgar <[email protected]>
Date: Tue, 22 Sep 2026 00:05:55 -0700
Subject: [PATCH 6/6] cics tr support refactor

---
 clang/include/clang/Basic/BuiltinsSystemZ.td  |   2 +-
 .../SystemZ/builtins-systemz-tr-invalid.c     |  13 ++
 .../CodeGen/SystemZ/builtins-systemz-tr.c     |  74 +++++++---
 llvm/include/llvm/IR/IntrinsicsSystemZ.td     |   6 +-
 .../Target/SystemZ/SystemZISelLowering.cpp    | 133 +++++++++++++-----
 llvm/lib/Target/SystemZ/SystemZISelLowering.h |   9 +-
 llvm/lib/Target/SystemZ/SystemZInstrInfo.td   |  39 ++---
 llvm/lib/Target/SystemZ/SystemZOperators.td   |  14 +-
 .../CodeGen/SystemZ/translate-instruction.ll  |  45 ------
 .../test/CodeGen/SystemZ/translate-invalid.ll |  17 +++
 llvm/test/CodeGen/SystemZ/translate.ll        |  41 ++++++
 11 files changed, 253 insertions(+), 140 deletions(-)
 create mode 100644 clang/test/CodeGen/SystemZ/builtins-systemz-tr-invalid.c
 delete mode 100644 llvm/test/CodeGen/SystemZ/translate-instruction.ll
 create mode 100644 llvm/test/CodeGen/SystemZ/translate-invalid.ll
 create mode 100644 llvm/test/CodeGen/SystemZ/translate.ll

diff --git a/clang/include/clang/Basic/BuiltinsSystemZ.td 
b/clang/include/clang/Basic/BuiltinsSystemZ.td
index 978bf4ca39af0..c5b730c7c1b41 100644
--- a/clang/include/clang/Basic/BuiltinsSystemZ.td
+++ b/clang/include/clang/Basic/BuiltinsSystemZ.td
@@ -378,5 +378,5 @@ let Attributes = [NoThrow, Const], Features = 
"vector-enhancements-3" in {
 
 // String/General memory intrinsics
 let Attributes = [NoThrow] in {
-  def tr : SystemZTargetBuiltin<"void(void *, int, int, void *, int)">;
+  def tr : SystemZTargetBuiltin<"void(void *, unsigned long int, void const 
*)">;
 }
\ No newline at end of file
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-tr-invalid.c 
b/clang/test/CodeGen/SystemZ/builtins-systemz-tr-invalid.c
new file mode 100644
index 0000000000000..d650e6d2b74e0
--- /dev/null
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-tr-invalid.c
@@ -0,0 +1,13 @@
+// REQUIRES: systemz-registered-target
+// RUN: not --crash %clang_cc1 -triple s390x-ibm-linux -S -O2 -o /dev/null %s 
2>&1 | FileCheck %s
+
+// CHECK: error: TRANSLATE length must be a compile-time constant between 1 
and 256
+// CHECK: error: TRANSLATE length must be a compile-time constant between 1 
and 256
+
+void tr_invalid_len_zero(char *src, const unsigned char *table) {
+    __builtin_s390_tr(src, 0, table);
+}
+
+void tr_invalid_len_260(char *src, const unsigned char *table) {
+    __builtin_s390_tr(src, 260, table);
+}
diff --git a/clang/test/CodeGen/SystemZ/builtins-systemz-tr.c 
b/clang/test/CodeGen/SystemZ/builtins-systemz-tr.c
index 18d78c6ca1391..25762f1d290df 100644
--- a/clang/test/CodeGen/SystemZ/builtins-systemz-tr.c
+++ b/clang/test/CodeGen/SystemZ/builtins-systemz-tr.c
@@ -1,37 +1,71 @@
 // NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py 
UTC_ARGS: --version 6
-// RUN: %clang_cc1 -triple s390x-linux-gnu -target-cpu z13 -emit-llvm %s -o - 
| FileCheck %s
+// REQUIRES: systemz-registered-target
+// RUN: %clang_cc1 -triple s390x-ibm-linux -emit-llvm %s -o - | FileCheck %s
 
-// CHECK-LABEL: define dso_local void @test_tr_static(
-// CHECK-SAME: ptr noundef [[SRC:%.*]], ptr noundef [[TBL:%.*]]) 
#[[ATTR0:[0-9]+]] {
+// CHECK-LABEL: define dso_local void @tr_len_five(
+// CHECK-SAME: ptr noundef [[SRC:%.*]], ptr noundef [[TABLE:%.*]]) 
#[[ATTR0:[0-9]+]] {
 // CHECK-NEXT:  [[ENTRY:.*:]]
 // CHECK-NEXT:    [[SRC_ADDR:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    [[TBL_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[TABLE_ADDR:%.*]] = alloca ptr, align 8
 // CHECK-NEXT:    store ptr [[SRC]], ptr [[SRC_ADDR]], align 8
-// CHECK-NEXT:    store ptr [[TBL]], ptr [[TBL_ADDR]], align 8
+// CHECK-NEXT:    store ptr [[TABLE]], ptr [[TABLE_ADDR]], align 8
 // CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[SRC_ADDR]], align 8
-// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[TBL_ADDR]], align 8
-// CHECK-NEXT:    call void @llvm.s390.translate(ptr [[TMP0]], i32 0, i32 16, 
ptr [[TMP1]], i32 0)
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[TABLE_ADDR]], align 8
+// CHECK-NEXT:    call void @llvm.s390.translate(ptr [[TMP0]], i64 5, ptr 
[[TMP1]])
 // CHECK-NEXT:    ret void
 //
-void test_tr_static(char *src, char *tbl) {
-    __builtin_s390_tr(src, 0, 16, tbl, 0);
+void tr_len_five(char *src, const unsigned char *table) {
+    __builtin_s390_tr(src, 5, table);
 }
 
-// CHECK-LABEL: define dso_local void @test_tr_variable(
-// CHECK-SAME: ptr noundef [[SRC:%.*]], ptr noundef [[TBL:%.*]], i32 noundef 
signext [[LEN:%.*]]) #[[ATTR0]] {
+// CHECK-LABEL: define dso_local void @tr_len_one(
+// CHECK-SAME: ptr noundef [[SRC:%.*]], ptr noundef [[TABLE:%.*]]) #[[ATTR0]] {
 // CHECK-NEXT:  [[ENTRY:.*:]]
 // CHECK-NEXT:    [[SRC_ADDR:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    [[TBL_ADDR:%.*]] = alloca ptr, align 8
-// CHECK-NEXT:    [[LEN_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT:    [[TABLE_ADDR:%.*]] = alloca ptr, align 8
 // CHECK-NEXT:    store ptr [[SRC]], ptr [[SRC_ADDR]], align 8
-// CHECK-NEXT:    store ptr [[TBL]], ptr [[TBL_ADDR]], align 8
-// CHECK-NEXT:    store i32 [[LEN]], ptr [[LEN_ADDR]], align 4
+// CHECK-NEXT:    store ptr [[TABLE]], ptr [[TABLE_ADDR]], align 8
 // CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[SRC_ADDR]], align 8
-// CHECK-NEXT:    [[TMP1:%.*]] = load i32, ptr [[LEN_ADDR]], align 4
-// CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[TBL_ADDR]], align 8
-// CHECK-NEXT:    call void @llvm.s390.translate(ptr [[TMP0]], i32 0, i32 
[[TMP1]], ptr [[TMP2]], i32 0)
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[TABLE_ADDR]], align 8
+// CHECK-NEXT:    call void @llvm.s390.translate(ptr [[TMP0]], i64 1, ptr 
[[TMP1]])
 // CHECK-NEXT:    ret void
 //
-void test_tr_variable(char *src, char *tbl, int len) {
-    __builtin_s390_tr(src, 0, len, tbl, 0);
+void tr_len_one(char *src, const unsigned char *table) {
+    __builtin_s390_tr(src, 1, table);
+}
+
+// CHECK-LABEL: define dso_local void @tr_len_256(
+// CHECK-SAME: ptr noundef [[SRC:%.*]], ptr noundef [[TABLE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[SRC_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[TABLE_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[SRC]], ptr [[SRC_ADDR]], align 8
+// CHECK-NEXT:    store ptr [[TABLE]], ptr [[TABLE_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[SRC_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load ptr, ptr [[TABLE_ADDR]], align 8
+// CHECK-NEXT:    call void @llvm.s390.translate(ptr [[TMP0]], i64 256, ptr 
[[TMP1]])
+// CHECK-NEXT:    ret void
+//
+void tr_len_256(char *src, const unsigned char *table) {
+    __builtin_s390_tr(src, 256, table);
+}
+
+// CHECK-LABEL: define dso_local void @tr_len_variable(
+// CHECK-SAME: ptr noundef [[SRC:%.*]], i64 noundef [[LENGTH:%.*]], ptr 
noundef [[TABLE:%.*]]) #[[ATTR0]] {
+// CHECK-NEXT:  [[ENTRY:.*:]]
+// CHECK-NEXT:    [[SRC_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    [[LENGTH_ADDR:%.*]] = alloca i64, align 8
+// CHECK-NEXT:    [[TABLE_ADDR:%.*]] = alloca ptr, align 8
+// CHECK-NEXT:    store ptr [[SRC]], ptr [[SRC_ADDR]], align 8
+// CHECK-NEXT:    store i64 [[LENGTH]], ptr [[LENGTH_ADDR]], align 8
+// CHECK-NEXT:    store ptr [[TABLE]], ptr [[TABLE_ADDR]], align 8
+// CHECK-NEXT:    [[TMP0:%.*]] = load ptr, ptr [[SRC_ADDR]], align 8
+// CHECK-NEXT:    [[TMP1:%.*]] = load i64, ptr [[LENGTH_ADDR]], align 8
+// CHECK-NEXT:    [[TMP2:%.*]] = load ptr, ptr [[TABLE_ADDR]], align 8
+// CHECK-NEXT:    call void @llvm.s390.translate(ptr [[TMP0]], i64 [[TMP1]], 
ptr [[TMP2]])
+// CHECK-NEXT:    ret void
+//
+void tr_len_variable(char *src, long length,
+                        const unsigned char *table) {
+    __builtin_s390_tr(src, length, table);
 }
diff --git a/llvm/include/llvm/IR/IntrinsicsSystemZ.td 
b/llvm/include/llvm/IR/IntrinsicsSystemZ.td
index 9d87202439053..d4bcbf29ebb7a 100644
--- a/llvm/include/llvm/IR/IntrinsicsSystemZ.td
+++ b/llvm/include/llvm/IR/IntrinsicsSystemZ.td
@@ -506,9 +506,9 @@ let TargetPrefix = "s390" in {
 
 let TargetPrefix = "s390" in {
   def int_s390_translate : Intrinsic<[],
-                                [llvm_ptr_ty, llvm_i32_ty, llvm_i32_ty, 
-                                 llvm_ptr_ty, llvm_i32_ty],
+                                [llvm_ptr_ty, llvm_i64_ty, llvm_ptr_ty],
                                 [IntrArgMemOnly, 
-                                 NoCapture<ArgIndex<0>>, 
NoCapture<ArgIndex<3>>]>,
+                                 NoCapture<ArgIndex<0>>, 
+                                 NoCapture<ArgIndex<2>>]>,
                                 ClangBuiltin<"__builtin_s390_tr">;
 }
\ No newline at end of file
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp 
b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
index 5e8884fa52cfc..9868bbd67126d 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.cpp
@@ -807,6 +807,7 @@ SystemZTargetLowering::SystemZTargetLowering(const 
TargetMachine &TM,
 
   // Handle intrinsics.
   setOperationAction(ISD::INTRINSIC_W_CHAIN, MVT::Other, Custom);
+  setOperationAction(ISD::INTRINSIC_VOID,    MVT::Other, Custom);
   setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom);
 
   // We're not using SJLJ for exception handling, but they're implemented
@@ -4207,6 +4208,36 @@ SDValue 
SystemZTargetLowering::lowerTLSGetOffset(GlobalAddressSDNode *Node,
   return DAG.getCopyFromReg(Chain, DL, SystemZ::R2D, PtrVT, Glue);
 }
 
+SDValue SystemZTargetLowering::lowerTR(SDValue Op,
+                                        SelectionDAG &DAG) const {
+  SDLoc DL(Op);
+  
+  SDValue Chain = Op.getOperand(0);
+  SDValue Src   = Op.getOperand(2);
+  SDValue Len   = Op.getOperand(3);
+  SDValue Tbl   = Op.getOperand(4);
+
+  // For compile-time constant lengths, validate range [1, 256].
+  // Variable lengths are handled (or rejected) by the instruction at runtime.
+  if(auto *C = dyn_cast<ConstantSDNode>(Len)) {
+    uint64_t LenVal = C->getZExtValue();
+    if(LenVal < 1 || LenVal > 256) {
+      DAG.getContext()->emitError(
+          "TRANSLATE length must be a compile-time constant between 1 and 
256");
+      return DAG.getUNDEF(MVT::Other);
+    }
+  }
+
+ // Adjust the provided length to encode length-1. Both TR and EXRL
+ // instructions must carry the adjusted value.
+ SDValue AdjLen = DAG.getNode(ISD::ADD, DL, MVT::i64,
+                              DAG.getZExtOrTrunc(Len, DL, MVT::i64),
+                              DAG.getSignedConstant(-1, DL, MVT::i64));
+
+  SDValue Ops[] = { Chain, Src, AdjLen, Tbl };
+  return DAG.getNode(SystemZISD::TR, DL, MVT::Other, Ops);
+}
+
 SDValue SystemZTargetLowering::lowerThreadPointer(const SDLoc &DL,
                                                   SelectionDAG &DAG) const {
   SDValue Chain = DAG.getEntryNode();
@@ -5457,6 +5488,20 @@ SystemZTargetLowering::lowerINTRINSIC_W_CHAIN(SDValue Op,
   return SDValue();
 }
 
+SDValue 
+SystemZTargetLowering::lowerINTRINSIC_VOID(SDValue Op,
+                                           SelectionDAG &DAG) const {
+  unsigned Id = Op.getConstantOperandVal(1);
+  switch(Id) {
+    case Intrinsic::s390_translate:
+      return lowerTR(Op, DAG);
+    default:
+      break;
+  }
+
+  return SDValue();
+}
+
 SDValue
 SystemZTargetLowering::lowerINTRINSIC_WO_CHAIN(SDValue Op,
                                                SelectionDAG &DAG) const {
@@ -7299,6 +7344,8 @@ SDValue SystemZTargetLowering::LowerOperation(SDValue Op,
     return lowerPREFETCH(Op, DAG);
   case ISD::INTRINSIC_W_CHAIN:
     return lowerINTRINSIC_W_CHAIN(Op, DAG);
+  case ISD::INTRINSIC_VOID:
+    return lowerINTRINSIC_VOID(Op, DAG);
   case ISD::INTRINSIC_WO_CHAIN:
     return lowerINTRINSIC_WO_CHAIN(Op, DAG);
   case ISD::BUILD_VECTOR:
@@ -10525,39 +10572,59 @@ MachineBasicBlock 
*SystemZTargetLowering::emitExt128(MachineInstr &MI,
 }
 
 MachineBasicBlock *
-SystemZTargetLowering::emitTRWrapper(MachineInstr &MI,
-                                     MachineBasicBlock *MBB,
-                                     unsigned Opcode) const {
-  MachineFunction &MF = *MBB->getParent();
-  MachineRegisterInfo &MRI = MF.getRegInfo();
-  const SystemZInstrInfo *TII = 
-      static_cast<const SystemZInstrInfo *>(Subtarget.getInstrInfo());
+SystemZTargetLowering::emitTRImm(MachineInstr &MI,
+                                 MachineBasicBlock *MBB) const {
+  const SystemZInstrInfo *TII = Subtarget.getInstrInfo();
   DebugLoc DL = MI.getDebugLoc();
 
-  Register SrcReg = MI.getOperand(0).getReg();
-  int64_t D1Imm   = MI.getOperand(1).getImm();
-  Register LenReg = MI.getOperand(2).getReg();
-  Register TblReg = MI.getOperand(3).getReg();
-  int64_t D2Imm   = MI.getOperand(4).getImm();
-
-  MRI.constrainRegClass(SrcReg, &SystemZ::ADDR64BitRegClass);
-  MRI.constrainRegClass(TblReg, &SystemZ::ADDR64BitRegClass);
-
-  Register Len32Reg = MRI.createVirtualRegister(&SystemZ::GR32BitRegClass);
-  BuildMI(*MBB, MI, DL, TII->get(SystemZ::AHI), Len32Reg)
-      .addReg(LenReg)
-      .addImm(-1);
-  
-  // Must constrain 32-bit length register to 64-bit for EXRL execution
-  Register Len64Reg = MRI.createVirtualRegister(&SystemZ::ADDR64BitRegClass);
-  BuildMI(*MBB, MI, DL, TII->get(SystemZ::LLGFR), Len64Reg)
-      .addReg(Len32Reg);
-  
+  MachineOperand SrcBase = earlyUseOperand(MI.getOperand(0));
+  uint64_t SrcDisp       = MI.getOperand(1).getImm();
+  uint64_t SrcLen        = MI.getOperand(2).getImm();
+  MachineOperand TblBase = earlyUseOperand(MI.getOperand(3));
+  uint64_t TblDisp       = MI.getOperand(4).getImm();
+
+  // TR instr expects the following format:
+  // - 1st op (source): (base, disp. length)
+  // - 2nd op (translation table): (base, disp.)
+  // Note: The provided length has alread been adjusted, so
+  //       passing the length op's immediate let TR's encoder
+  //       to leave it as-is and not subtract 1 a second time.
+  BuildMI(*MBB, MI, DL, TII->get(SystemZ::TR))
+   .add(SrcBase).addImm(SrcDisp).addImm(SrcLen) // BDL1
+   .add(TblBase).addImm(TblDisp);               // BDL2
+
+  MI.eraseFromParent();
+  return MBB;
+}
+ 
+MachineBasicBlock *
+SystemZTargetLowering::emitTRReg(MachineInstr &MI,
+                                 MachineBasicBlock *MBB) const {
+  const SystemZInstrInfo *TII = Subtarget.getInstrInfo();
+  DebugLoc DL = MI.getDebugLoc();
+
+  MachineOperand SrcBase = earlyUseOperand(MI.getOperand(0));
+  uint64_t SrcDisp       = MI.getOperand(1).getImm();
+  Register SrcLen        = MI.getOperand(2).getReg();
+  MachineOperand TblBase = earlyUseOperand(MI.getOperand(3));
+  uint64_t TblDisp       = MI.getOperand(4).getImm();
+
+  // To handle variable-length values, the TR pseudo must be
+  // lowered into EXRL_Pseudo. This way, EXRL will execute the TR
+  // instruction along w/ any addtl. instructions needed to compute
+  // the value of the provided length. Both address bases must be
+  // placed into dedicate virtual regs before sendoff to EXRL_Pseudo.
+  Register SrcReg = forceReg(MI, SrcBase, TII);
+  Register TblReg = forceReg(MI, TblBase, TII);
+
+  // Unlike the official TR instruction, EXRL_Pseudo already expects
+  // an adjusted length value to be provided, which at this point has
+  // already been done via lowerTR(), so no further adjustment needed.
   BuildMI(*MBB, MI, DL, TII->get(SystemZ::EXRL_Pseudo))
-      .addImm(Opcode)
-      .addReg(Len64Reg)
-      .addReg(SrcReg).addImm(D1Imm)
-      .addReg(TblReg).addImm(D2Imm);
+   .addImm(SystemZ::TR)
+   .addReg(SrcLen)
+   .addReg(SrcReg).addImm(SrcDisp)  // BDL1
+   .addReg(TblReg).addImm(TblDisp); // BD2
   
   MI.eraseFromParent();
   return MBB;
@@ -11370,8 +11437,10 @@ MachineBasicBlock 
*SystemZTargetLowering::EmitInstrWithCustomInserter(
   case SystemZ::CMP_STACKGUARD_DAG:
     return emitStackGuardPseudo(MI, MBB, SystemZ::CMP_STACKGUARD);
 
-  case SystemZ::TR_Pseudo:
-    return emitTRWrapper(MI, MBB, SystemZ::TR);
+  case SystemZ::TRImm:
+    return emitTRImm(MI, MBB);
+  case SystemZ::TRReg:
+    return emitTRReg(MI, MBB);
 
   default:
     llvm_unreachable("Unexpected instr type to insert");
diff --git a/llvm/lib/Target/SystemZ/SystemZISelLowering.h 
b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
index b7b27532f7e4d..74678814dc198 100644
--- a/llvm/lib/Target/SystemZ/SystemZISelLowering.h
+++ b/llvm/lib/Target/SystemZ/SystemZISelLowering.h
@@ -382,6 +382,7 @@ class SystemZTargetLowering : public TargetLowering {
   SDValue lowerSTACKRESTORE(SDValue Op, SelectionDAG &DAG) const;
   SDValue lowerPREFETCH(SDValue Op, SelectionDAG &DAG) const;
   SDValue lowerINTRINSIC_W_CHAIN(SDValue Op, SelectionDAG &DAG) const;
+  SDValue lowerINTRINSIC_VOID(SDValue Op, SelectionDAG &DAG) const;
   SDValue lowerINTRINSIC_WO_CHAIN(SDValue Op, SelectionDAG &DAG) const;
   bool isVectorElementLoad(SDValue Op) const;
   SDValue buildVector(SelectionDAG &DAG, const SDLoc &DL, EVT VT,
@@ -494,9 +495,11 @@ class SystemZTargetLowering : public TargetLowering {
   getTargetMMOFlags(const Instruction &I) const override;
   const TargetRegisterClass *getRepRegClassFor(MVT VT) const override;
 
-  MachineBasicBlock *emitTRWrapper(MachineInstr &MI,
-                                   MachineBasicBlock *MBB,
-                                   unsigned Opcode) const;
+  SDValue lowerTR(SDValue Op, SelectionDAG &DAG) const;
+  MachineBasicBlock *emitTRImm(MachineInstr &MI,
+                               MachineBasicBlock *MBB) const;
+  MachineBasicBlock *emitTRReg(MachineInstr &MI,
+                               MachineBasicBlock *MBB) const;
 
 private:
   bool isInternal(const Function *Fn) const;
diff --git a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td 
b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
index 2784a58d00952..3965d457a3bad 100644
--- a/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
+++ b/llvm/lib/Target/SystemZ/SystemZInstrInfo.td
@@ -2272,34 +2272,17 @@ let hasSideEffects = 1 in {
 }
 
 // Translate
-let hasSideEffects = 1, usesCustomInserter = 1,
-    hasNoSchedulingInfo = 1, mayLoad = 1, 
-    mayStore = 1, Defs = [CC] in {
-  def TR_Pseudo : Pseudo<(outs), (ins GR64:$src, i32imm:$D1, GR32:$len, 
-                                    GR64:$tbl, i32imm:$D2), []>;
-                                  // [(z_tr GR64:$src, i32imm:$D1, GR32:$len, 
-                                  //   GR64:$tbl, i32imm:$D2)]>;
-}
-
-// Transform constant length N (1..256) into N - 1 for SS-format
-def tr_len : SDNodeXForm<imm, [{
-  return CurDAG->getTargetConstant(N->getZExtValue() - 1, SDLoc(N), MVT::i32);
-}]>;
-
-def tr_len_leaf : ImmLeaf<i32,
-  [{ return N->getZExtValue() >= 1 && N->getZExtValue() <= 256; }],
-  tr_len
->;
-
-// Match TR invocation w/ a static constant provided as the length
-def : Pat<(int_s390_translate ADDR64:$src, i32:$D1, tr_len_leaf:$len, 
-                                ADDR64:$tbl, i32:$D2),
-          (TR (bdladdr12onlylen8 ADDR64:$src, i32:$D1, tr_len_leaf:$len), 
-               (bdaddr12only ADDR64:$tbl, i32:$D2))>;
-
-// Match TR invocation w/ a variable provided as the length
-def : Pat<(int_s390_translate ADDR64:$src, i32:$D1, GR32:$len, GR64:$tbl, 
i32:$D2),
-          (TR_Pseudo ADDR64:$src, i32:$D1, GR32:$len, ADDR64:$tbl, i32:$D2)>;
+let usesCustomInserter = 1, hasNoSchedulingInfo = 1, 
+    mayLoad = 1, mayStore = 1 in {
+
+  // Match TR invocation w/ static constant length
+  def TRImm : Pseudo<(outs), (ins bdaddr12only:$bd1, imm64:$len, 
bdaddr12only:$bd2),
+                             [(z_tr bdaddr12only:$bd1, imm64:$len, 
bdaddr12only:$bd2)]>;
+
+  // Match TR invocation w/ variable length
+  def TRReg : Pseudo<(outs), (ins bdaddr12only:$bd1, ADDR64:$len, 
bdaddr12only:$bd2), 
+                             [(z_tr bdaddr12only:$bd1, ADDR64:$len, 
bdaddr12only:$bd2)]>;
+}
 
 
//===----------------------------------------------------------------------===//
 // .insn directive instructions
diff --git a/llvm/lib/Target/SystemZ/SystemZOperators.td 
b/llvm/lib/Target/SystemZ/SystemZOperators.td
index b0a5327d92c04..cca85649a659d 100644
--- a/llvm/lib/Target/SystemZ/SystemZOperators.td
+++ b/llvm/lib/Target/SystemZ/SystemZOperators.td
@@ -257,12 +257,10 @@ def SDT_ZSetJmp             : SDTypeProfile<1, 1,
                                              SDTCisPtrTy<1>]>;
 def SDT_ZLongJmp            : SDTypeProfile<0, 1, [SDTCisPtrTy<0>]>;
 
-// def SDT_ZTranslate          : SDTypeProfile<0, 5, 
-//                                             [SDTCisPtrTy<0>,
-//                                              SDTCisInt<1>,  
-//                                              SDTCisInt<2>,  
-//                                              SDTCisPtrTy<3>,
-//                                              SDTCisInt<4>]>;
+def SDT_ZTranslate          : SDTypeProfile<0, 3, 
+                                            [SDTCisPtrTy<0>,
+                                             SDTCisVT<1, i64>,
+                                             SDTCisPtrTy<2>]>;
 
 
//===----------------------------------------------------------------------===//
 // Node definitions
@@ -427,8 +425,8 @@ def z_stckf            : SDNode<"SystemZISD::STCKF", 
SDT_ZStoreInherent,
                                 [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>;
 
 // Translate string data from one code to another code
-// def z_tr               : SDNode<"SystemZISD::TR", SDT_ZTranslate, 
-//                                 [SDNPHasChain, SDNPMayLoad, SDNPMayStore]>;
+def z_tr               : SDNode<"SystemZISD::TR", SDT_ZTranslate, 
+                                [SDNPHasChain, SDNPMayLoad, SDNPMayStore]>;
 
 // Test Data Class.
 //
diff --git a/llvm/test/CodeGen/SystemZ/translate-instruction.ll 
b/llvm/test/CodeGen/SystemZ/translate-instruction.ll
deleted file mode 100644
index 8458875838e91..0000000000000
--- a/llvm/test/CodeGen/SystemZ/translate-instruction.ll
+++ /dev/null
@@ -1,45 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py 
UTC_ARGS: --version 6
-; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z13 -verify-machineinstrs | 
FileCheck %s
-
-declare void @llvm.s390.translate(ptr, i32, i32, ptr, i32)
-
-;Test 1: Static constant length -> Direct TR instruction
-define void @test_tr_static(ptr %src, ptr %tbl) {
-; CHECK-LABEL: test_tr_static:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    stmg %r6, %r15, 48(%r15)
-; CHECK-NEXT:    .cfi_offset %r6, -112
-; CHECK-NEXT:    .cfi_offset %r14, -48
-; CHECK-NEXT:    .cfi_offset %r15, -40
-; CHECK-NEXT:    aghi %r15, -160
-; CHECK-NEXT:    .cfi_def_cfa_offset 320
-; CHECK-NEXT:    lgr %r5, %r3
-; CHECK-NEXT:    lhi %r3, 0
-; CHECK-NEXT:    lhi %r4, 16
-; CHECK-NEXT:    lhi %r6, 0
-; CHECK-NEXT:    brasl %r14, llvm.s390.translate@PLT
-; CHECK-NEXT:    lmg %r6, %r15, 208(%r15)
-; CHECK-NEXT:    br %r14
-  call void @llvm.s390.translate(ptr %src, i32 0, i32 16, ptr %tbl, i32 0)
-  ret void
-}
-
-; Test 2: Variable length -> AHI (decrement by 1) + EXRL execution
-define void @test_tr_variable(ptr %src, ptr %tbl, i32 %len) {
-; CHECK-LABEL: test_tr_variable:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    stmg %r6, %r15, 48(%r15)
-; CHECK-NEXT:    .cfi_offset %r6, -112
-; CHECK-NEXT:    .cfi_offset %r14, -48
-; CHECK-NEXT:    .cfi_offset %r15, -40
-; CHECK-NEXT:    aghi %r15, -160
-; CHECK-NEXT:    .cfi_def_cfa_offset 320
-; CHECK-NEXT:    lgr %r5, %r3
-; CHECK-NEXT:    lhi %r3, 0
-; CHECK-NEXT:    lhi %r6, 0
-; CHECK-NEXT:    brasl %r14, llvm.s390.translate@PLT
-; CHECK-NEXT:    lmg %r6, %r15, 208(%r15)
-; CHECK-NEXT:    br %r14
-  call void @llvm.s390.translate(ptr %src, i32 0, i32 %len, ptr %tbl, i32 0)
-  ret void
-}
diff --git a/llvm/test/CodeGen/SystemZ/translate-invalid.ll 
b/llvm/test/CodeGen/SystemZ/translate-invalid.ll
new file mode 100644
index 0000000000000..b7b54841bc080
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/translate-invalid.ll
@@ -0,0 +1,17 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py 
UTC_ARGS: --version 6
+; RUN: not --crash llc < %s -mtriple=s390x-linux-gnu 2>&1 | FileCheck %s
+
+; CHECK: error: TRANSLATE length must be a compile-time constant between 1 and 
256
+; CHECK: error: TRANSLATE length must be a compile-time constant between 1 and 
256
+
+declare void @llvm.s390.translate(ptr, i64, ptr)
+
+define void @tr_invalid_len_zero(ptr %src, ptr %table) {
+  call void @llvm.s390.translate(ptr %src, i64 0, ptr %table)
+  ret void
+}
+
+define void @tr_invalid_len_260(ptr %src, ptr %table) {
+  call void @llvm.s390.translate(ptr %src, i64 260, ptr %table)
+  ret void
+}
diff --git a/llvm/test/CodeGen/SystemZ/translate.ll 
b/llvm/test/CodeGen/SystemZ/translate.ll
new file mode 100644
index 0000000000000..6fb2b07e1627f
--- /dev/null
+++ b/llvm/test/CodeGen/SystemZ/translate.ll
@@ -0,0 +1,41 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py 
UTC_ARGS: --version 6
+; RUN: llc < %s -mtriple=s390x-linux-gnu -verify-machineinstrs | FileCheck %s
+
+declare void @llvm.s390.translate(ptr, i64, ptr)
+
+define void @tr_len_five(ptr %src, ptr %table) {
+; CHECK-LABEL: tr_len_five:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    tr 0(4,%r2), 0(%r3)
+; CHECK-NEXT:    br %r14
+  call void @llvm.s390.translate(ptr %src, i64 5, ptr %table)
+  ret void
+}
+
+define void @tr_len_one(ptr %src, ptr %table) {
+; CHECK-LABEL: tr_len_one:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    tr 0(0,%r2), 0(%r3)
+; CHECK-NEXT:    br %r14
+  call void @llvm.s390.translate(ptr %src, i64 1, ptr %table)
+  ret void
+}
+
+define void @tr_len_256(ptr %src, ptr %table) {
+; CHECK-LABEL: tr_len_256:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    tr 0(255,%r2), 0(%r3)
+; CHECK-NEXT:    br %r14
+  call void @llvm.s390.translate(ptr %src, i64 256, ptr %table)
+  ret void
+}
+
+define void @tr_len_variable(ptr %src, i64 %length, ptr %table) {
+; CHECK-LABEL: tr_len_variable:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    aghi %r3, -1
+; CHECK-NEXT:    exrl %r3, .Ltmp0
+; CHECK-NEXT:    br %r14
+  call void @llvm.s390.translate(ptr %src, i64 %length, ptr %table)
+  ret void
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to