teemperor created this revision.
teemperor added reviewers: JDevlieghere, davide, sgraenitz.
Herald added subscribers: lldb-commits, abidh, mgorny.
Herald added a project: LLDB.
teemperor marked an inline comment as done.
teemperor added inline comments.


================
Comment at: lldb/source/Commands/OptionsBase.td:11
+  // *.inc file.
+  string Command;
+}
----------------
I'm open to renaming this if anyone has a better name for this.


We currently have man large arrays containing initializers for our command 
options.
These tables are tricky maintain as we don't have any good place to check them 
for consistency and
it's also hard to read (`nullptr, {}, 0` is not very descriptive).

This patch fixes this by letting table gen generate those tables. This way we 
can have a more readable
syntax for this (especially for all the default arguments) and we can let 
TableCheck check them
for consistency (e.g. an option with an optional argument can't have 
`eArgTypeNone`, naming of flags', etc.).

Also refactoring the related data structures can now be done without changing 
the hundred of option initializers.

For example, this line:

  {LLDB_OPT_SET_ALL, false, "hide-aliases",         'a', 
OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Hide aliases in the 
command list."},

becomes this:

  def hide_aliases : Option<"hide-aliases", "a">, Desc<"Hide aliases in the 
command list.">;

For now I just moved a few initializers to the new format to demonstrate the 
change. I'll slowly migrate the other
option initializers tables  in separate patches.


Repository:
  rLLDB LLDB

https://reviews.llvm.org/D64365

Files:
  lldb/CMakeLists.txt
  lldb/cmake/modules/AddLLDB.cmake
  lldb/source/Commands/BreakpointList.td
  lldb/source/Commands/CMakeLists.txt
  lldb/source/Commands/CommandObjectBreakpoint.cpp
  lldb/source/Commands/CommandObjectHelp.cpp
  lldb/source/Commands/CommandObjectSettings.cpp
  lldb/source/Commands/Options.td
  lldb/source/Commands/OptionsBase.td
  lldb/utils/TableGen/CMakeLists.txt
  lldb/utils/TableGen/LLDBOptionDefEmitter.cpp
  lldb/utils/TableGen/LLDBTableGen.cpp
  lldb/utils/TableGen/LLDBTableGenBackends.h

Index: lldb/utils/TableGen/LLDBTableGenBackends.h
===================================================================
--- /dev/null
+++ lldb/utils/TableGen/LLDBTableGenBackends.h
@@ -0,0 +1,35 @@
+//===- LLDBTableGenBackends.h - Declarations for LLDB TableGen Backends ---===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the declarations for all of the LLDB TableGen
+// backends. A "TableGen backend" is just a function. See
+// "$LLVM_ROOT/utils/TableGen/TableGenBackends.h" for more info.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LLDB_UTILS_TABLEGEN_TABLEGENBACKENDS_H
+#define LLVM_LLDB_UTILS_TABLEGEN_TABLEGENBACKENDS_H
+
+#include <string>
+
+namespace llvm {
+class raw_ostream;
+class RecordKeeper;
+} // namespace llvm
+
+using llvm::raw_ostream;
+using llvm::RecordKeeper;
+
+namespace lldb_private {
+
+void EmitOptionDefs(RecordKeeper &RK, raw_ostream &OS);
+
+} // namespace lldb_private
+
+#endif
Index: lldb/utils/TableGen/LLDBTableGen.cpp
===================================================================
--- /dev/null
+++ lldb/utils/TableGen/LLDBTableGen.cpp
@@ -0,0 +1,74 @@
+//===- TableGen.cpp - Top-Level TableGen implementation for Clang ---------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains the main function for Clang's TableGen.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LLDBTableGenBackends.h" // Declares all backends.
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/Signals.h"
+#include "llvm/TableGen/Error.h"
+#include "llvm/TableGen/Main.h"
+#include "llvm/TableGen/Record.h"
+
+using namespace llvm;
+using namespace lldb_private;
+
+enum ActionType {
+  PrintRecords,
+  DumpJSON,
+  GenOptionDefs,
+};
+
+namespace {
+cl::opt<ActionType>
+    Action(cl::desc("Action to perform:"),
+           cl::values(clEnumValN(PrintRecords, "print-records",
+                                 "Print all records to stdout (default)"),
+                      clEnumValN(DumpJSON, "dump-json",
+                                 "Dump all records as machine-readable JSON"),
+                      clEnumValN(GenOptionDefs, "gen-lldb-option-defs",
+                                 "Generate clang attribute clases")));
+
+bool LLDBTableGenMain(raw_ostream &OS, RecordKeeper &Records) {
+  switch (Action) {
+  case PrintRecords:
+    OS << Records; // No argument, dump all contents
+    break;
+  case DumpJSON:
+    EmitJSON(Records, OS);
+    break;
+  case GenOptionDefs:
+    EmitOptionDefs(Records, OS);
+    break;
+  }
+  return false;
+}
+} // namespace
+
+int main(int argc, char **argv) {
+  sys::PrintStackTraceOnErrorSignal(argv[0]);
+  PrettyStackTraceProgram X(argc, argv);
+  cl::ParseCommandLineOptions(argc, argv);
+
+  llvm_shutdown_obj Y;
+
+  return TableGenMain(argv[0], &LLDBTableGenMain);
+}
+
+#ifdef __has_feature
+#if __has_feature(address_sanitizer)
+#include <sanitizer/lsan_interface.h>
+// Disable LeakSanitizer for this binary as it has too many leaks that are not
+// very interesting to fix. See compiler-rt/include/sanitizer/lsan_interface.h .
+int __lsan_is_turned_off() { return 1; }
+#endif // __has_feature(address_sanitizer)
+#endif // defined(__has_feature)
Index: lldb/utils/TableGen/LLDBOptionDefEmitter.cpp
===================================================================
--- /dev/null
+++ lldb/utils/TableGen/LLDBOptionDefEmitter.cpp
@@ -0,0 +1,144 @@
+//===- LLDBOptionDefEmitter.cpp - Generate LLDB command options =-*- C++ -*--=//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// These tablegen backends emits LLDB's OptionDefinition values for different
+// LLDB commands.
+//
+//===----------------------------------------------------------------------===//
+
+#include "LLDBTableGenBackends.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/TableGen/Record.h"
+#include "llvm/TableGen/StringMatcher.h"
+#include "llvm/TableGen/TableGenBackend.h"
+#include <map>
+#include <vector>
+
+using namespace llvm;
+
+/// Map of command names to their associated records.
+typedef std::map<std::string, std::vector<Record *>> RecordsByCommand;
+
+/// Groups all records by their command.
+static RecordsByCommand getCommandList(std::vector<Record *> Options) {
+  RecordsByCommand result;
+  for (Record *Option : Options)
+    result[Option->getValueAsString("Command").str()].push_back(Option);
+  return result;
+}
+
+static void emitOption(Record *Option, raw_ostream &OS) {
+  OS << "{";
+
+  // List of option groups this option is in.
+  std::vector<std::string> GroupsArg;
+
+  if (Option->getValue("Groups")) {
+    // The user specified a list of groups.
+    auto Groups = Option->getValueAsListOfInts("Groups");
+    for (int Group : Groups)
+      GroupsArg.push_back("LLDB_OPT_SET_" + std::to_string(Group));
+    OS << llvm::join(GroupsArg.begin(), GroupsArg.end(), " | ");
+  } else if (Option->getValue("GroupStart")) {
+    // The user specified a range of groups (with potentially only one element).
+    int GroupStart = Option->getValueAsInt("GroupStart");
+    int GroupEnd = Option->getValueAsInt("GroupEnd");
+    for (int i = GroupStart; i <= GroupEnd; ++i)
+      GroupsArg.push_back("LLDB_OPT_SET_" + std::to_string(i));
+  }
+
+  // If we have any grous, we merge them. Otherwise we move this option into
+  // the all group.
+  if (GroupsArg.empty())
+    OS << "LLDB_OPT_SET_ALL";
+  else
+    OS << llvm::join(GroupsArg.begin(), GroupsArg.end(), " | ");
+
+  OS << ", ";
+
+  // Check if this option is required.
+  if (auto R = Option->getValue("Required"))
+    OS << "true";
+  else
+    OS << "false";
+
+  // Add the full and short name for this option.
+  OS << ", \"" << Option->getValueAsString("FullName") << "\", ";
+  OS << '\'' << Option->getValueAsString("ShortName") << "'";
+
+  auto ArgType = Option->getValue("ArgType");
+  bool IsOptionalArg = Option->getValue("OptionalArg") != nullptr;
+
+  // Decide if we have either an option, required or no argument for this
+  // option.
+  OS << ", OptionParser::";
+  if (ArgType) {
+    if (IsOptionalArg)
+      OS << "eOptionalArgument";
+    else
+      OS << "eRequiredArgument";
+  } else
+    OS << "eNoArgument";
+  OS << ", nullptr, {}, ";
+
+  // Read the tab completions we offer for this option (if there are any)
+  if (Option->getValue("Completions")) {
+    auto Completions = Option->getValueAsListOfStrings("Completions");
+    std::vector<std::string> CompletionArgs;
+    for (llvm::StringRef Completion : Completions)
+      CompletionArgs.push_back("CommandCompletions::e" + Completion.str() +
+                               "Completion");
+
+    OS << llvm::join(CompletionArgs.begin(), CompletionArgs.end(), " | ");
+  } else {
+    OS << "CommandCompletions::eNoCompletion";
+  }
+
+  // Add the argument type.
+  OS << ", eArgType";
+  if (ArgType) {
+    OS << ArgType->getValue()->getAsUnquotedString();
+  } else
+    OS << "None";
+  OS << ", ";
+
+  // Add the description if there is any.
+  if (auto D = Option->getValue("Description"))
+    OS << D->getValue()->getAsString();
+  else
+    OS << "\"\"";
+  OS << "},\n";
+}
+
+/// Emits all option initializers to the raw_ostream.
+static void emitOptions(std::string Command, std::vector<Record *> Option,
+                        raw_ostream &OS) {
+  std::string NeededMacro = "LLDB_OPTIONS_" + Command;
+  // All options are in one file, so we need put them behind macros and ask the
+  // user to define the macro for the options that are needed.
+  OS << "#ifdef " << NeededMacro << "\n";
+  for (Record *R : Option)
+    emitOption(R, OS);
+  // We undefine the macro for the user like Clang's include files are doing it.
+  OS << "#undef " << NeededMacro << "\n";
+  OS << "#endif // " << Command << " command\n\n";
+}
+
+void lldb_private::EmitOptionDefs(RecordKeeper &Records, raw_ostream &OS) {
+
+  std::vector<Record *> Options = Records.getAllDerivedDefinitions("Option");
+
+  emitSourceFileHeader("Options for LLDB command line commands", OS);
+
+  RecordsByCommand ByCommand = getCommandList(Options);
+
+  for (auto &CommandRecordPair : ByCommand) {
+    emitOptions(CommandRecordPair.first, CommandRecordPair.second, OS);
+  }
+}
Index: lldb/utils/TableGen/CMakeLists.txt
===================================================================
--- /dev/null
+++ lldb/utils/TableGen/CMakeLists.txt
@@ -0,0 +1,8 @@
+set(LLVM_LINK_COMPONENTS Support)
+
+add_tablegen(lldb-tblgen LLDB
+  LLDBOptionDefEmitter.cpp
+  LLDBTableGen.cpp
+  )
+set_target_properties(lldb-tblgen PROPERTIES FOLDER "LLDB tablegenning")
+
Index: lldb/source/Commands/OptionsBase.td
===================================================================
--- /dev/null
+++ lldb/source/Commands/OptionsBase.td
@@ -0,0 +1,61 @@
+// Base class for all options.
+class Option<string fullname, string shortname> {
+  string FullName = fullname;
+  string ShortName = shortname;
+  // The associated command/subcommand. Needs to be a valid C identifier
+  // because this is used to define the associated macro that needs to be
+  // set to generate the specific option initializers.
+  // Example value: "settings_set", which would mean one needs to set the
+  // LLDB_OPTIONS_settings_set macro before including the generated
+  // *.inc file.
+  string Command;
+}
+
+// Moves the option into a list of option groups.
+class Groups<list<int> groups> {
+  list<int> Groups = groups;
+}
+
+// Moves the option in all option groups in a range.
+// Start and end values are inclusive.
+class GroupRange<int start, int end> {
+  int GroupStart = start;
+  int GroupEnd = end;
+}
+// Moves the option in a single option group.
+class Group<int group> {
+  int GroupStart = group;
+  int GroupEnd = group;
+}
+
+// Sets the description for the option that should be
+// displayed to the user.
+class Desc<string description> {
+  string Description = description;
+}
+
+// Marks the option as required when calling the
+// associated command.
+class Required {
+  bit Required = 1;
+}
+
+// Gives the option an optional argument.
+class OptionalArgument<string type> {
+  string ArgType = type;
+  bit OptionalArg = 1;
+}
+
+// Gives the option an required argument.
+class Argument<string type> {
+  string ArgType = type;
+}
+
+// Sets the available completions for the given option.
+class Completions<list<string> completions> {
+  list<string> Completions = completions;
+}
+// Sets a single completion for the given option.
+class Completion<string completion> {
+  list<string> Completions = [completion];
+}
Index: lldb/source/Commands/Options.td
===================================================================
--- /dev/null
+++ lldb/source/Commands/Options.td
@@ -0,0 +1,43 @@
+include "OptionsBase.td"
+
+let Command = "help" in {
+  def hide_aliases : Option<"hide-aliases", "a">,
+    Desc<"Hide aliases in the command list.">;
+  def hide_user : Option<"hide-user-commands", "u">;
+  def show_hidden : Option<"show-hidden-commands", "h">;
+}
+
+let Command = "settings_set" in {
+  def global : Option<"global", "g">, Argument<"Filename">,
+    Completion<"DiskFile">,
+    Desc<"Apply the new value to the global default value.">;
+  def force : Option<"force", "f">,
+    Desc<"Force an empty value to be accepted as the default.">;
+}
+
+let Command = "settings_write" in {
+  def settings_wf : Option<"file", "f">, Required,
+    Desc<"The file into which to write the settings.">;
+  def append : Option<"append", "a">,
+    Desc<"Append to saved settings file if it exists.">;
+}
+
+let Command = "settings_read" in {
+  def settings_rf : Option<"file", "f">, Required,
+  Desc<"The file from which to read the settings.">;
+}
+
+let Command = "breakpoint_list" in {
+  def internal : Option<"internal", "i">,
+    Desc<"Show debugger internal breakpoints">;
+  def brief : Option<"brief", "b">, Group<1>,
+    Desc<"Give a brief description of the breakpoint (no location info).">;
+  def full : Option<"full", "f">, Group<2>,
+    Desc<"Give a full description of the breakpoint and its locations.">;
+  def verbose : Option<"verbose", "v">, Group<3>,
+    Desc<"Explain everything we know about the breakpoint (for debugging "
+    "debugger bugs).">;
+  def dummy_bp : Option<"dummy-breakpoints", "D">,
+    Desc<"List Dummy breakpoints - i.e. breakpoints set before a file is "
+    "provided, which prime new targets.">;
+}
Index: lldb/source/Commands/CommandObjectSettings.cpp
===================================================================
--- lldb/source/Commands/CommandObjectSettings.cpp
+++ lldb/source/Commands/CommandObjectSettings.cpp
@@ -22,10 +22,8 @@
 // CommandObjectSettingsSet
 
 static constexpr OptionDefinition g_settings_set_options[] = {
-    // clang-format off
-  { LLDB_OPT_SET_2, false, "global", 'g', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Apply the new value to the global default value." },
-  { LLDB_OPT_SET_2, false, "force",  'f', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Force an empty value to be accepted as the default." }
-    // clang-format on
+#define LLDB_OPTIONS_settings_set
+#include "Options.inc"
 };
 
 class CommandObjectSettingsSet : public CommandObjectRaw {
@@ -313,10 +311,8 @@
 // CommandObjectSettingsWrite -- Write settings to file
 
 static constexpr OptionDefinition g_settings_write_options[] = {
-    // clang-format off
-  { LLDB_OPT_SET_ALL, true,  "file",  'f', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eDiskFileCompletion, eArgTypeFilename,    "The file into which to write the settings." },
-  { LLDB_OPT_SET_ALL, false, "append",'a', OptionParser::eNoArgument,       nullptr, {}, 0,                                       eArgTypeNone,        "Append to saved settings file if it exists."},
-    // clang-format on
+#define LLDB_OPTIONS_settings_write
+#include "Options.inc"
 };
 
 class CommandObjectSettingsWrite : public CommandObjectParsed {
@@ -438,9 +434,8 @@
 // CommandObjectSettingsRead -- Read settings from file
 
 static constexpr OptionDefinition g_settings_read_options[] = {
-    // clang-format off
-  {LLDB_OPT_SET_ALL, true, "file",'f', OptionParser::eRequiredArgument, nullptr, {}, CommandCompletions::eDiskFileCompletion, eArgTypeFilename,       "The file from which to read the breakpoints." },
-    // clang-format on
+#define LLDB_OPTIONS_settings_read
+#include "Options.inc"
 };
 
 class CommandObjectSettingsRead : public CommandObjectParsed {
Index: lldb/source/Commands/CommandObjectHelp.cpp
===================================================================
--- lldb/source/Commands/CommandObjectHelp.cpp
+++ lldb/source/Commands/CommandObjectHelp.cpp
@@ -66,11 +66,8 @@
 CommandObjectHelp::~CommandObjectHelp() = default;
 
 static constexpr OptionDefinition g_help_options[] = {
-    // clang-format off
-  {LLDB_OPT_SET_ALL, false, "hide-aliases",         'a', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Hide aliases in the command list."},
-  {LLDB_OPT_SET_ALL, false, "hide-user-commands",   'u', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Hide user-defined commands from the list."},
-  {LLDB_OPT_SET_ALL, false, "show-hidden-commands", 'h', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Include commands prefixed with an underscore."},
-    // clang-format on
+#define LLDB_OPTIONS_help
+#include "Options.inc"
 };
 
 llvm::ArrayRef<OptionDefinition>
Index: lldb/source/Commands/CommandObjectBreakpoint.cpp
===================================================================
--- lldb/source/Commands/CommandObjectBreakpoint.cpp
+++ lldb/source/Commands/CommandObjectBreakpoint.cpp
@@ -1246,15 +1246,8 @@
 
 #pragma mark List::CommandOptions
 static constexpr OptionDefinition g_breakpoint_list_options[] = {
-    // clang-format off
-  { LLDB_OPT_SET_ALL, false, "internal",          'i', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Show debugger internal breakpoints" },
-  { LLDB_OPT_SET_1,   false, "brief",             'b', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Give a brief description of the breakpoint (no location info)." },
-  // FIXME: We need to add an "internal" command, and then add this sort of thing to it.
-  // But I need to see it for now, and don't want to wait.
-  { LLDB_OPT_SET_2,   false, "full",              'f', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Give a full description of the breakpoint and its locations." },
-  { LLDB_OPT_SET_3,   false, "verbose",           'v', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "Explain everything we know about the breakpoint (for debugging debugger bugs)." },
-  { LLDB_OPT_SET_ALL, false, "dummy-breakpoints", 'D', OptionParser::eNoArgument, nullptr, {}, 0, eArgTypeNone, "List Dummy breakpoints - i.e. breakpoints set before a file is provided, which prime new targets." },
-    // clang-format on
+#define LLDB_OPTIONS_breakpoint_list
+#include "Options.inc"
 };
 
 #pragma mark List
Index: lldb/source/Commands/CMakeLists.txt
===================================================================
--- lldb/source/Commands/CMakeLists.txt
+++ lldb/source/Commands/CMakeLists.txt
@@ -1,3 +1,7 @@
+lldb_tablegen(Options.inc -gen-lldb-option-defs
+  SOURCE Options.td
+  TARGET LLDBOptionsGen)
+
 add_lldb_library(lldbCommands
   CommandCompletions.cpp
   CommandObjectApropos.cpp
@@ -45,3 +49,5 @@
   LINK_COMPONENTS
     Support
   )
+
+add_dependencies(lldbCommands LLDBOptionsGen)
Index: lldb/source/Commands/BreakpointList.td
===================================================================
--- /dev/null
+++ lldb/source/Commands/BreakpointList.td
@@ -0,0 +1,4 @@
+include "Options.td"
+
+def hide_aliases : Option<"internal", "i">,
+  Desc<"Show debugger internal breakpoints">;
Index: lldb/cmake/modules/AddLLDB.cmake
===================================================================
--- lldb/cmake/modules/AddLLDB.cmake
+++ lldb/cmake/modules/AddLLDB.cmake
@@ -1,4 +1,37 @@
+function(lldb_tablegen)
+  # Syntax:
+  # lldb_tablegen output-file [tablegen-arg ...] SOURCE source-file
+  # [[TARGET cmake-target-name] [DEPENDS extra-dependency ...]]
+  #
+  # Generates a custom command for invoking tblgen as
+  #
+  # tblgen source-file -o=output-file tablegen-arg ...
+  #
+  # and, if cmake-target-name is provided, creates a custom target for
+  # executing the custom command depending on output-file. It is
+  # possible to list more files to depend after DEPENDS.
+
+  cmake_parse_arguments(LTG "" "SOURCE;TARGET" "" ${ARGN})
+
+  if( NOT LTG_SOURCE )
+    message(FATAL_ERROR "SOURCE source-file required by clang_tablegen")
+  endif()
+
+  set( LLVM_TARGET_DEFINITIONS ${LTG_SOURCE} )
+  tablegen(LLDB ${LTG_UNPARSED_ARGUMENTS})
+
+  if(LTG_TARGET)
+    add_public_tablegen_target(${LTG_TARGET})
+    set_target_properties( ${LTG_TARGET} PROPERTIES FOLDER "LLDB tablegenning")
+    set_property(GLOBAL APPEND PROPERTY LLDB_TABLEGEN_TARGETS ${LTG_TARGET})
+  endif()
+endfunction(lldb_tablegen)
+
 function(add_lldb_library name)
+  include_directories(BEFORE
+    ${CMAKE_CURRENT_BINARY_DIR}
+    ${CMAKE_CURRENT_SOURCE_DIR})
+
   # only supported parameters to this macro are the optional
   # MODULE;SHARED;STATIC library type and source files
   cmake_parse_arguments(PARAM
Index: lldb/CMakeLists.txt
===================================================================
--- lldb/CMakeLists.txt
+++ lldb/CMakeLists.txt
@@ -33,6 +33,8 @@
 if (NOT LLDB_DISABLE_PYTHON)
   add_subdirectory(scripts)
 endif ()
+
+add_subdirectory(utils/TableGen)
 add_subdirectory(source)
 add_subdirectory(tools)
 add_subdirectory(docs)
_______________________________________________
lldb-commits mailing list
lldb-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to