[clang] [llvm] Support TR Hardware Instruction for SystemZ (PR #216899)
Patrick Apgar via llvm-commits
llvm-commits at lists.llvm.org
Tue Sep 22 00:06:13 PDT 2026
https://github.com/patricklapgar updated https://github.com/llvm/llvm-project/pull/216899
>From 72ecc7caedba6ab9e30c3d0639519506349ec69b Mon Sep 17 00:00:00 2001
From: patricklapgar <patricklapgardeveloper at gmail.com>
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 <patricklapgardeveloper at gmail.com>
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 <patricklapgardeveloper at gmail.com>
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 <patricklapgardeveloper at gmail.com>
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 <patricklapgardeveloper at gmail.com>
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 at 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 at 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 <patricklapgardeveloper at gmail.com>
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 at 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 at 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
+}
More information about the llvm-commits
mailing list