[llvm] [llvm] Add a new `Triple::str(N)` method (PR #145150)

via llvm-commits llvm-commits at lists.llvm.org
Fri Jun 20 22:42:25 PDT 2025


https://github.com/royitaqi created https://github.com/llvm/llvm-project/pull/145150

# Change
Adds a new `Triple::str(N)` method, which returns the triple string but only keep the first N components. See method doc for details.

Note that the return type is `StringRef`. This is different from the existing methods, where `std::string` is returned by `Triple::str()` and `Triple::getTriple()`. The reason for `StringRef` is performance - it's unnecessary to create a new `std::string` object, which copies the content of the triple string.

# Usage
This method can be used to generate a triple which has less trailing components (e.g. to remove the "object format" component). This can be for edit or display purposes.

# Alternatives

**Alternative 1**: Add the same method but return `std::string` for consistency with the existing methods. However, we probably don't want to make unnecessary copies just to make the API look more consistent.

**Alternative 2**: Add a new method called `Triple::clone(N = 5)`, which will return a new triple with the triple string that would be returned by `Triple::str(N)`. However, the user might not want to construct a new triple object, or maybe they want to normalize it before creating such object. `Triple::str(N)` is more flexible.

# Tests

Added `TripleTest::StrFirstN`.

>From 7cf98d07d2cfd3642744ec8d7b691a57357f87a7 Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Fri, 20 Jun 2025 22:04:06 -0700
Subject: [PATCH] Add a new Triple::clone() method

---
 llvm/include/llvm/TargetParser/Triple.h    | 21 +++++
 llvm/lib/TargetParser/Triple.cpp           | 24 ++++++
 llvm/unittests/TargetParser/TripleTest.cpp | 93 ++++++++++++++++++++++
 3 files changed, 138 insertions(+)

diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h
index 7fd5278f1ed53..6b9e80dc8743a 100644
--- a/llvm/include/llvm/TargetParser/Triple.h
+++ b/llvm/include/llvm/TargetParser/Triple.h
@@ -463,6 +463,27 @@ class Triple {
 
   const std::string &str() const { return Data; }
 
+  /// Return the triple string but only keep the first \p N components.
+  ///
+  /// The returned string will preserve the first \p N components exactly the
+  /// same as the original (including the leading "-" and the value, empty or
+  /// not).
+  ///
+  /// E.g. Triple("arm64-apple-ios").str(5) == "arm64-apple-ios"
+  /// E.g. Triple("arm64-apple-ios--").str(5) == "arm64-apple-ios--"
+  /// E.g. Triple("arm64-apple-ios--").str(4) == "arm64-apple-ios-"
+  /// E.g. Triple("arm64-apple-ios--").str(3) == "arm64-apple-ios"
+  /// E.g. Triple("arm64-apple-ios--").str(2) == "arm64-apple"
+  /// E.g. Triple("arm64-apple-ios--").str(1) == "arm64"
+  /// E.g. Triple("arm64-apple-ios--").str(0) == ""
+  ///
+  /// This method does not normalize any triple strings. Clients that need to
+  /// handle the non-canonical triples that users often specify should use the
+  /// normalize method.
+  ///
+  /// \returns the (shorterned) triple string.
+  StringRef str(size_t N) const;
+
   const std::string &getTriple() const { return Data; }
 
   /// Whether the triple is empty / default constructed.
diff --git a/llvm/lib/TargetParser/Triple.cpp b/llvm/lib/TargetParser/Triple.cpp
index 6a559ff023caa..0fdb62c473e73 100644
--- a/llvm/lib/TargetParser/Triple.cpp
+++ b/llvm/lib/TargetParser/Triple.cpp
@@ -2107,6 +2107,30 @@ std::string Triple::merge(const Triple &Other) const {
   return Other.str();
 }
 
+StringRef Triple::str(size_t N) const {
+  // If empty, return empty
+  if (N == 0 || Data == "")
+    return "";
+
+  // If keeping all components, return a full clone
+  if (N >= 5)
+    return Data;
+
+  // Find the N-th separator (which is after the N'th component)
+  size_t p = StringRef::npos;
+  for (uint32_t i = 0; i < N; ++i) {
+    p = Data.find('-', p + 1);
+    if (p == StringRef::npos)
+      break;
+  }
+
+  // Create a triple
+  if (p == StringRef::npos)
+    return Data;
+  else
+    return StringRef(Data).substr(0, p);
+}
+
 bool Triple::isMacOSXVersionLT(unsigned Major, unsigned Minor,
                                unsigned Micro) const {
   assert(isMacOSX() && "Not an OS X triple!");
diff --git a/llvm/unittests/TargetParser/TripleTest.cpp b/llvm/unittests/TargetParser/TripleTest.cpp
index bbd12e6d0e412..ba91e91d08a21 100644
--- a/llvm/unittests/TargetParser/TripleTest.cpp
+++ b/llvm/unittests/TargetParser/TripleTest.cpp
@@ -2926,4 +2926,97 @@ TEST(TripleTest, isCompatibleWith) {
     EXPECT_TRUE(DoTest(C.B, C.A, C.Result));
   }
 }
+
+TEST(TripleTest, StrFirstN) {
+  // Empty triple
+  {
+    llvm::Triple triple;
+    ASSERT_EQ(triple.str(), "");
+    ASSERT_EQ(triple.str(5), "");
+  }
+
+  // Normal triple with 3 components
+  {
+    llvm::Triple triple("arm64-apple-ios");
+    ASSERT_EQ(triple.str(), "arm64-apple-ios");
+    ASSERT_EQ(triple.str(5), "arm64-apple-ios");
+    ASSERT_EQ(triple.str(4), "arm64-apple-ios");
+    ASSERT_EQ(triple.str(3), "arm64-apple-ios");
+    ASSERT_EQ(triple.str(2), "arm64-apple");
+    ASSERT_EQ(triple.str(1), "arm64");
+    ASSERT_EQ(triple.str(0), "");
+  }
+
+  // Normal triple with 4 components
+  {
+    llvm::Triple triple("arm64-apple-ios-simulator");
+    ASSERT_EQ(triple.str(), "arm64-apple-ios-simulator");
+    ASSERT_EQ(triple.str(5), "arm64-apple-ios-simulator");
+    ASSERT_EQ(triple.str(4), "arm64-apple-ios-simulator");
+    ASSERT_EQ(triple.str(3), "arm64-apple-ios");
+    ASSERT_EQ(triple.str(2), "arm64-apple");
+    ASSERT_EQ(triple.str(1), "arm64");
+    ASSERT_EQ(triple.str(0), "");
+  }
+
+  // Normal triple with 5 components
+  {
+    llvm::Triple triple("arm64-apple-ios-simulator-macho");
+    ASSERT_EQ(triple.str(), "arm64-apple-ios-simulator-macho");
+    ASSERT_EQ(triple.str(5), "arm64-apple-ios-simulator-macho");
+    ASSERT_EQ(triple.str(4), "arm64-apple-ios-simulator");
+    ASSERT_EQ(triple.str(3), "arm64-apple-ios");
+    ASSERT_EQ(triple.str(2), "arm64-apple");
+    ASSERT_EQ(triple.str(1), "arm64");
+    ASSERT_EQ(triple.str(0), "");
+  }
+
+  // Empty vendor and os
+  {
+    llvm::Triple triple("arm64---simulator-macho");
+    ASSERT_EQ(triple.str(), "arm64---simulator-macho");
+    ASSERT_EQ(triple.str(5), "arm64---simulator-macho");
+    ASSERT_EQ(triple.str(4), "arm64---simulator");
+    ASSERT_EQ(triple.str(3), "arm64--");
+    ASSERT_EQ(triple.str(2), "arm64-");
+    ASSERT_EQ(triple.str(1), "arm64");
+    ASSERT_EQ(triple.str(0), "");
+  }
+
+  // Empty environment
+  {
+    llvm::Triple triple("arm64-apple-ios-");
+    ASSERT_EQ(triple.str(), "arm64-apple-ios-");
+    ASSERT_EQ(triple.str(5), "arm64-apple-ios-");
+    ASSERT_EQ(triple.str(4), "arm64-apple-ios-");
+    ASSERT_EQ(triple.str(3), "arm64-apple-ios");
+    ASSERT_EQ(triple.str(2), "arm64-apple");
+    ASSERT_EQ(triple.str(1), "arm64");
+    ASSERT_EQ(triple.str(0), "");
+  }
+
+  // Empty object format
+  {
+    llvm::Triple triple("arm64-apple-ios-simulator-");
+    ASSERT_EQ(triple.str(), "arm64-apple-ios-simulator-");
+    ASSERT_EQ(triple.str(5), "arm64-apple-ios-simulator-");
+    ASSERT_EQ(triple.str(4), "arm64-apple-ios-simulator");
+    ASSERT_EQ(triple.str(3), "arm64-apple-ios");
+    ASSERT_EQ(triple.str(2), "arm64-apple");
+    ASSERT_EQ(triple.str(1), "arm64");
+    ASSERT_EQ(triple.str(0), "");
+  }
+
+  // Empty environment, but has object format
+  {
+    llvm::Triple triple("arm64----macho");
+    ASSERT_EQ(triple.str(), "arm64----macho");
+    ASSERT_EQ(triple.str(5), "arm64----macho");
+    ASSERT_EQ(triple.str(4), "arm64---");
+    ASSERT_EQ(triple.str(3), "arm64--");
+    ASSERT_EQ(triple.str(2), "arm64-");
+    ASSERT_EQ(triple.str(1), "arm64");
+    ASSERT_EQ(triple.str(0), "");
+  }
+}
 } // end anonymous namespace



More information about the llvm-commits mailing list