[llvm] [opt] Enable -mcpu=help without an input file (PR #187876)

Tomer Shafir via llvm-commits llvm-commits at lists.llvm.org
Sat Mar 21 11:03:36 PDT 2026


https://github.com/tomershafir updated https://github.com/llvm/llvm-project/pull/187876

>From 1b443e4420d4fb862f290650ea546e0e662e2fd1 Mon Sep 17 00:00:00 2001
From: tomershafir <tomer.shafir8 at gmail.com>
Date: Sat, 21 Mar 2026 19:45:43 +0200
Subject: [PATCH] [opt] Enable -mcpu=help without an input file

This patch enables `-mcpu=help` or `-mattr=help` invocation without an input file, like `llc`. For example, the following command `opt -mtriple=aarch64 -mattr=help` would previously hang but now it succeeds.

The implementation is similar to llc, creating a target machine for help printing side effects and existing early. Note: llc has a bug in the default triple handling that we already fix here upfront. Ill submit a separate fix for llc.
---
 llvm/test/tools/opt/mattr-mcpu-help.ll | 34 ++++++++++++++++++++++++++
 llvm/tools/opt/optdriver.cpp           | 32 +++++++++++++++++++++---
 2 files changed, 63 insertions(+), 3 deletions(-)
 create mode 100644 llvm/test/tools/opt/mattr-mcpu-help.ll

diff --git a/llvm/test/tools/opt/mattr-mcpu-help.ll b/llvm/test/tools/opt/mattr-mcpu-help.ll
new file mode 100644
index 0000000000000..c4de602d00540
--- /dev/null
+++ b/llvm/test/tools/opt/mattr-mcpu-help.ll
@@ -0,0 +1,34 @@
+; REQUIRES: aarch64-registered-target
+; REQUIRES: default_triple
+
+;; Test -mattr=help
+; RUN: opt -mtriple=aarch64 -mattr=help 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-NO-OPTIMIZE
+; RUN: opt -mtriple=aarch64 -mattr=help -o %t.s 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-NO-OPTIMIZE
+; RUN: opt < %s -mtriple=aarch64 -mattr=help 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-NO-OPTIMIZE
+; RUN: opt < %s -mtriple=aarch64 -mattr=help -o %t.s 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-NO-OPTIMIZE
+
+;; -mattr=help doesn't have to be first
+; RUN: opt -mtriple=aarch64 -mattr=+zcm-fpr64 -mattr=help 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-NO-OPTIMIZE
+
+;; Using default target triple for -mattr=help
+; RUN: opt -mattr=help 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-NO-OPTIMIZE
+; RUN: opt < %s -mattr=help 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-NO-OPTIMIZE
+
+;; Test -mcpu=help
+; RUN: opt -mtriple=aarch64 -mcpu=help 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-NO-OPTIMIZE
+; RUN: opt -mtriple=aarch64 -mcpu=help -o %t.s 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-NO-OPTIMIZE
+; RUN: opt < %s -mtriple=aarch64 -mcpu=help 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-NO-OPTIMIZE
+; RUN: opt < %s -mtriple=aarch64 -mcpu=help -o %t.s 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-NO-OPTIMIZE
+
+;; Using default target triple for -mcpu=help
+; RUN: opt -mcpu=help 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-NO-OPTIMIZE
+; RUN: opt < %s -mcpu=help 2>&1 | FileCheck %s --check-prefixes=CHECK,CHECK-NO-OPTIMIZE
+
+; CHECK: Available CPUs for this target:
+; CHECK: Available features for this target:
+
+;; To check we dont compile the file
+; CHECK-NO-OPTIMIZE-NOT: foo
+define i32 @foo() {
+  ret i32 0
+}
diff --git a/llvm/tools/opt/optdriver.cpp b/llvm/tools/opt/optdriver.cpp
index 2d1613edc2c83..7a64f28ed86b2 100644
--- a/llvm/tools/opt/optdriver.cpp
+++ b/llvm/tools/opt/optdriver.cpp
@@ -488,6 +488,33 @@ optMain(int argc, char **argv,
     return 0;
   }
 
+  // If user just wants to list available options, skip module loading.
+  std::string CPUStr = codegen::getCPUStr();
+  auto MAttrs = codegen::getMAttrs();
+  bool SkipModule = CPUStr == "help" || is_contained(MAttrs, "help");
+  if (SkipModule) {
+    Triple TheTriple;
+    if (!TargetTriple.empty())
+      TheTriple = Triple(Triple::normalize(TargetTriple));
+    else
+      TheTriple = Triple(sys::getDefaultTargetTriple());
+
+    // Create the target machine just to print the help info. Use unique_ptr
+    // to avoid a memory leak.
+    Expected<std::unique_ptr<TargetMachine>> ExpectedTM =
+        codegen::createTargetMachineForTriple(TheTriple.str(),
+                                              GetCodeGenOptLevel());
+    if (Error E = ExpectedTM.takeError()) {
+      errs() << argv[0] << ": " << toString(std::move(E)) << "\n";
+      return 1;
+    }
+
+    // If we don't have a module then just exit now. We do this down
+    // here since the CPU/Feature help is underneath the target machine
+    // creation.
+    return 0;
+  }
+
   TimeTracerRAII TimeTracer(argv[0]);
 
   SMDiagnostic Err;
@@ -627,11 +654,8 @@ optMain(int argc, char **argv,
   }
 
   Triple ModuleTriple(M->getTargetTriple());
-  std::string CPUStr, FeaturesStr;
   std::unique_ptr<TargetMachine> TM;
   if (ModuleTriple.getArch()) {
-    CPUStr = codegen::getCPUStr();
-    FeaturesStr = codegen::getFeaturesStr();
     Expected<std::unique_ptr<TargetMachine>> ExpectedTM =
         codegen::createTargetMachineForTriple(ModuleTriple.str(),
                                               GetCodeGenOptLevel());
@@ -655,6 +679,8 @@ optMain(int argc, char **argv,
         codegen::InitTargetOptionsFromCodeGenFlags(ModuleTriple);
   }
 
+  std::string FeaturesStr = codegen::getFeaturesStr();
+
   // Override function attributes based on CPUStr, FeaturesStr, and command line
   // flags.
   codegen::setFunctionAttributes(*M, CPUStr, FeaturesStr);



More information about the llvm-commits mailing list