Repository: ROCm/MITuna (language: python)

Issue:
RabbitMQ connection heartbeat
rabbitMQ fails to close stale connections by default and we run out of sockets (available connection).
Added heartbeat check at 60sec to close unresponsive connections and free up space.


Discussion / hints:

Fix patch:
diff --git a/.flake8 b/.flake8
new file mode 100644
index 00000000..54fac219
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,5 @@
+[flake8]
+# To match the approved yapf.
+indent-size = 2
+# Let doc strings be uncapitalised and not end in periods.
+extend-ignore = D400,D403
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..52675f89
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+*~
+__pycache__
+*.rej
+*.orig
diff --git a/.style.yapf b/.style.yapf
new file mode 100644
index 00000000..4861cafe
--- /dev/null
+++ b/.style.yapf
@@ -0,0 +1,3 @@
+[style]
+based_on_style = google
+indent_width = 2
diff --git a/README.md b/README.md
index 3cfe8681..92634d3b 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-TUNA 
+TUNA
 ====
 
 Tuna is a distributed tuning infrastructure that provides pre-compiled kernels
@@ -13,7 +13,7 @@ Install python3.9
 ```
 apt-get update && apt-get install software-properties-common
 add-apt-repository ppa:deadsnakes/ppa
-apt install python3.9
+apt install python3.9 python3.9-dev python3.9-venv
 ```
 
 Install pip for python3.9
@@ -28,11 +28,23 @@ Install MySQL server
 apt-get install mysql-server
 ```
 
+```
+mysqld --initialize
+grep 'temporary password' /var/log/mysql/error.log
+```
+
 Enable the service
 ```
 systemctl start mysql
 ```
 
+```
+mysql -u root -p
+<use the temporary password>
+mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'root-password';
+mysql> CREATE DATABASE <database_name>;
+```
+
 Install ipmitool
 ```
 apt-get install ipmitool
@@ -50,7 +62,7 @@ passwordless ssh.
 
 Installation
 ------------
-Clone the repo using 
+Clone the repo using
 ```
 git clone <repo url>
 ```
@@ -58,9 +70,9 @@ Enter the Tuna directory
 ```
 cd MITuna
 ```
-Create a virtual envornment, and activate it (by sourcing its `activate` script)
+Create a virtual environment, and activate it (by sourcing its `activate` script)
 ```
-virtualenv -p python3.9 myvenv
+python3.9 -m venv myvenv
 source myvenv/bin/activate
 ```
 Install the required dependencies:
@@ -72,7 +84,7 @@ The above assumes that Tuna lives in the home directory and the virtual environm
 Add the following environment variables to a local file and then source the file:
 ```
 export TUNA_DB_USER_NAME=root
-export TUNA_DB_PASSWORD=<password for root>
+export TUNA_DB_USER_PASSWORD=<password for root>
 export TUNA_DB_HOSTNAME=localhost
 export TUNA_DB_NAME=<database_name>
 export gateway_ip=<gateway_ip>
diff --git a/doc/src/TuningCycle.md b/doc/src/TuningCycle.md
index 1bd84514..62c6cdd2 100644
--- a/doc/src/TuningCycle.md
+++ b/doc/src/TuningCycle.md
@@ -34,14 +34,14 @@ the tuning part.
 Tuning Steps
 ------------
 
-Tuning stores intermittent data in a central mySQL database. Each reference to a table, 
+Tuning stores intermittent data in a central mySQL database. Each reference to a table,
 refers to a table in this database. Each table and its associated schema is found in miopen_tables.py.
 
-Tuning is divided in multiple steps and each step builds on top of the previous ones. 
-To start a tuning session, some prerequisite have to be asserted: setting up configurations, 
-getting the latest solvers and their associated applicability from MIOpen, 
-and adding the jobs that compose the tuning session. 
-Once these prerequisite are established the tuning session can begin. Each step, 
+Tuning is divided in multiple steps and each step builds on top of the previous ones.
+To start a tuning session, some prerequisite have to be asserted: setting up configurations,
+getting the latest solvers and their associated applicability from MIOpen,
+and adding the jobs that compose the tuning session.
+Once these prerequisite are established the tuning session can begin. Each step,
 including the prerequisites are detailed below.
 
 **Add Network Configurations(1)**
@@ -59,8 +59,8 @@ benchmarking of a certain model, post tuning.
 ```
 
 The config table contains network configurations. If provided with a text file of MIOpenDriver
-commands, the import script can translate those commands and populate the config table. 
-Additionally the user may provide a name to tag a configuration for easier recall later. 
+commands, the import script can translate those commands and populate the config table.
+Additionally the user may provide a name to tag a configuration for easier recall later.
 A tag will be required when adding a tuning job. Tags are stored in the config_tags table.
 A model and framework name and version are also required. This enables MITuna to track
 benchmark performance post-tuning.
@@ -68,10 +68,10 @@ benchmark performance post-tuning.
 ```
 ./go_fish.py miopen import_configs --add_model Resnet50 --md_version 1
 ./go_fish.py miopen import_configs --add_framework Pytorch --fw_version 1
-./go_fish.py miopen import_configs -t resnet50 -f ../utils/recurrent_cfgs/resnet50.txt 
+./go_fish.py miopen import_configs -t resnet50 -f ../utils/recurrent_cfgs/resnet50.txt
 --model Resnet50 --md_version 1 --framework Pytorch --fw_version 1</p>
 -t - tag
--f - filepath 
+-f - filepath
 --model - model name
 --md_version - model version
 --framework - framework name
@@ -81,7 +81,7 @@ benchmark performance post-tuning.
 
 **Add Solvers (2)**
 
-The solver table contains MIOpen solvers and solver characteristics. 
+The solver table contains MIOpen solvers and solver characteristics.
 This should be updated when an MIOpen version modifies solvers.
 
 ```
@@ -90,7 +90,7 @@ This should be updated when an MIOpen version modifies solvers.
 
 **Add Tuning Session (3)**
 
-Session will track the architecture and skew, as well as the miopen version and 
+Session will track the architecture and skew, as well as the miopen version and
 rocm version for the tuning session.
 
 This command will need to be run from inside the tuning environment eg MITuna docker
@@ -116,7 +116,7 @@ solver_applicability table with applicable solvers for each configuration for th
 **Load Jobs (5)**
 
 Time to create the jobs for the tuning session. Specify the session id, the configs that
-should be tuned, and the fin_step to be executed. Confings can be added by using the tag from
+should be tuned, and the fin_step to be executed. Configs can be added by using the tag from
 the config_tags table. Jobs should have a compile and an eval fin step pair.
 
 Fin steps include: miopen_perf_compile, miopen_perf_eval, miopen_find_compile, and miopen_find_eval.
@@ -132,13 +132,13 @@ Fin steps include: miopen_perf_compile, miopen_perf_eval, miopen_find_compile, a
 
 **Compile Step (6)**
 
-Once prerequisites are set, tuning can begin. To compile the jobs, 
+Once prerequisites are set, tuning can begin. To compile the jobs,
 supply the session id along with the compile fin_step matching the one in the job table.
 
 [Use backend=HIPNOGPU docker]
 ```
 ./go_fish.py miopen --session_id 1 --fin_steps miopen_perf_compile
---session_id    - tuning session id 
+--session_id    - tuning session id
 --fin_steps     - execute this operation
 ```
 
@@ -169,4 +169,3 @@ eg for MI100, -p will produce a gfx90878.db file, -f will produce gfx90878.HIP.f
 -f           - export find db
 -k           - export kernel db
 ```
-
diff --git a/tuna/example/README.md b/tuna/example/README.md
index a72f06ff..b8912f07 100644
--- a/tuna/example/README.md
+++ b/tuna/example/README.md
@@ -1,4 +1,4 @@
-New library integration 
+New library integration
 =======================
 An example of how to integrate external applications in Tuna to utilize the scaling
 features of Tuna
@@ -11,7 +11,7 @@ The supported tuning steps are:
 ```
 ./go_fish.py example --add_tables
 ./go_fish.py example --init_session -l my_label
-./tuna/example/load_job.py -a gfx908 -n 120 -l my_lable --session_id 1
+./example/load_job.py -a gfx908 -n 120 -l my_label --session_id 1
 ./go_fish.py example --execute --session_id 1
 ```
 
@@ -33,7 +33,7 @@ used to add new jobs and track the tuning data, post execution step.
 
 The third step is:
 ```
-./tuna/example/load_job.py -a gfx908 -n 120 -l my_lable --session_id 1
+./tuna/example/load_job.py -a gfx908 -n 120 -l my_label --session_id 1
 
 ```
 This steps loads jobs in the *job* table. These jobs will be picked up for execution in the
diff --git a/tuna/example/example_worker.py b/tuna/example/example_worker.py
index f451f969..3aa2b915 100644
--- a/tuna/example/example_worker.py
+++ b/tuna/example/example_worker.py
@@ -35,7 +35,7 @@
 
 
 class ExampleWorker(WorkerInterface):
-  """ The Example class implementes the worker class. Its purpose is to run a command. It picks up
+  """ The Example class implements the worker class. Its purpose is to run a command. It picks up
   new jobs and when completed, sets the state to completed. """
 
   def __init__(self, **kwargs: Dict[str, Any]) -> None:
diff --git a/tuna/example/load_job.py b/tuna/example/load_job.py
index 85c77944..a2daa182 100755
--- a/tuna/example/load_job.py
+++ b/tuna/example/load_job.py
@@ -50,7 +50,7 @@ def parse_args():
                       type=str,
                       dest='label',
                       required=True,
-                      help='Label to annontate the jobs.',
+                      help='Label to annotate the jobs.',
                       default='new')
 
   args = parser.parse_args()
diff --git a/tuna/example/session.py b/tuna/example/session.py
index 804849dd..58dd0d89 100644
--- a/tuna/example/session.py
+++ b/tuna/example/session.py
@@ -38,7 +38,7 @@
 
 
 class SessionExample(BASE, SessionMixin):
-  """Session table to keep track of tunning sesions"""
+  """Session table to keep track of tuning sessions"""
   #pylint: disable=attribute-defined-outside-init
 
   __tablename__: str = "session_example"
diff --git a/tuna/lib_utils.py b/tuna/lib_utils.py
index 7a556a29..66d8e21e 100644
--- a/tuna/lib_utils.py
+++ b/tuna/lib_utils.py
@@ -30,15 +30,18 @@
 from tuna.libraries import Library
 from tuna.miopen.miopen_lib import MIOpen
 from tuna.example.example_lib import Example
+from tuna.rocmlir.rocmlir_lib import RocMLIR
 
 
-def get_library(args: Dict[str, Any]) -> Union[Example, MIOpen]:
+def get_library(args: Dict[str, Any]) -> Union[Example, MIOpen, RocMLIR]:
   """Factory method to get lib based on args"""
-  library: Union[Example, MIOpen]
+  library: Union[Example, MIOpen, RocMLIR]
   if 'lib' not in args.keys() or args['lib'].value == Library.MIOPEN.value:
     library = MIOpen()
   elif args['lib'].value == Library.EXAMPLE.value:
     library = Example()
+  elif args['lib'].value == Library.ROCMLIR.value:
+    library = RocMLIR()
   else:
     raise ValueError("Not implemented")
 
diff --git a/tuna/libraries.py b/tuna/libraries.py
index c902e0c7..6d113d28 100644
--- a/tuna/libraries.py
+++ b/tuna/libraries.py
@@ -34,6 +34,7 @@ class Library(Enum):
   # pylint: disable=invalid-name ; uppercasing would require modifying a lot of files
   MIOPEN: str = 'miopen'
   EXAMPLE: str = 'example'
+  ROCMLIR: str = 'rocmlir'
 
   def __str__(self) -> str:
     return self.value
diff --git a/tuna/miopen/db/build_schema.py b/tuna/miopen/db/build_schema.py
index 0af64d85..889b62ba 100755
--- a/tuna/miopen/db/build_schema.py
+++ b/tuna/miopen/db/build_schema.py
@@ -46,7 +46,7 @@ def recreate_triggers(drop_triggers, create_triggers):
       try:
         conn.execute(trg)
       except OperationalError as oerr:
-        LOGGER.warning("Operational Error occured while adding trigger: '%s'",
+        LOGGER.warning("Operational Error occurred while adding trigger: '%s'",
                        trg)
         LOGGER.info('%s \n', oerr)
         continue
diff --git a/tuna/miopen/subcmd/export_db.py b/tuna/miopen/subcmd/export_db.py
index df770faa..564402ef 100755
--- a/tuna/miopen/subcmd/export_db.py
+++ b/tuna/miopen/subcmd/export_db.py
@@ -49,7 +49,14 @@
 
 DIR_NAME = {'F': 'Fwd', 'B': 'BwdData', 'W': 'BwdWeights'}
 
-_, ID_SOLVER_MAP = get_id_solvers()
+ID_SOLVER_MAP = None
+
+
+def require_id_solvers():
+  """Initialise ID_SOLVER_MAP from database"""
+  global ID_SOLVER_MAP  # pylint: disable=global-statement
+  if not ID_SOLVER_MAP:
+    _, ID_SOLVER_MAP = get_id_solvers()
 
 
 def arg_export_db(args: argparse.Namespace, logger: logging.Logger):
@@ -198,6 +205,7 @@ def build_miopen_fdb(query, logger):
       else:
         lst.append(fdb_entry)
 
+  require_id_solvers()
   for _, entries in find_db.items():
     entries.sort(key=lambda x: (float(x.kernel_time), ID_SOLVER_MAP[x.solver]))
     while len(entries) > 4:
@@ -212,6 +220,7 @@ def write_fdb(arch, num_cu, ocl, find_db, filename=None):
   """
   file_name = get_filename(arch, num_cu, filename, ocl, DB_Type.FIND_DB)
 
+  require_id_solvers()
   with open(file_name, 'w') as out:  # pylint: disable=unspecified-encoding
     for key, solvers in sorted(find_db.items(), key=lambda kv: kv[0]):
       solvers.sort(
@@ -389,6 +398,7 @@ def insert_perf_db_sqlite(cnx, perf_db_entry, ins_cfg_id):
   perf_db_dict = {
       k: v for k, v in perf_db_dict.items() if k in SQLITE_PERF_DB_COLS
   }
+  require_id_solvers()
   perf_db_dict['solver'] = ID_SOLVER_MAP[perf_db_dict['solver']]
 
   insert_solver_sqlite(cnx, perf_db_dict)
diff --git a/tuna/rocmlir/__init__.py b/tuna/rocmlir/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/tuna/rocmlir/import_configs.py b/tuna/rocmlir/import_configs.py
new file mode 100755
index 00000000..fe489b62
--- /dev/null
+++ b/tuna/rocmlir/import_configs.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python3
+###############################################################################
+#
+# MIT License
+#
+# Copyright (c) 2022 Advanced Micro Devices, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+###############################################################################
+""" Module for tagging and importing configs """
+import os
+import logging
+import argparse
+import itertools
+
+from sqlalchemy.exc import IntegrityError
+from tuna.dbBase.sql_alchemy import DbSession
+from tuna.utils.db_utility import connect_db
+from tuna.utils.logger import setup_logger
+from tuna.rocmlir.rocmlir_tables import RocMLIRDBTables
+
+## Adapted from perfRunner.getConvConfigurations.
+
+#DIRECTIONS = ['-F 1', '-F 2', '-F 4']
+# temporarily disable backward-data until rocmlir-tuning-driver can handle multi-kernel code
+DIRECTIONS = ['-F 1', '-F 4']
+DATA_TYPES = ['-t f32', '-t f16', '-t i8']
+LAYOUTS = ['NHWC', 'NCHW']
+
+
+def get_conv_configurations(filename):
+  """Read conv-configs from filename and expand into all combinations of
+     direction, type, and layout.
+  """
+  configs = []
+  with open(filename, 'r', encoding='utf8') as config_file:
+    lines = config_file.readlines()
+
+    # All combinations of conv direction, type and layouts
+    for direction, datatype, layout, line in \
+            itertools.product(DIRECTIONS, DATA_TYPES, LAYOUTS, lines):
+      line = line.strip()
+
+      # Skip empty lines
+      if len(line) == 0 or line[0] == '#':
+        continue
+      # Skip int8 non-fwd convolutions
+      if datatype == '-t i8' and direction != '-F 1':
+        continue
+
+      # Skip datatype if already in
+      datatype = f"{datatype} "
+      # check for the presense of a positional arg
+      if line[0][0] != "-":
+        datatype = ""
+
+      # Skip direction if already in
+      direction = f"{direction} "
+      if "-F" in line:
+        direction = ""
+
+      # Skip filter layout if already in
+      filter_layout = f"-f {layout} "
+      if "-f" in line:
+        filter_layout = ""
+
+      # Skip input layout if already in
+      input_layout = f"-I {layout} "
+      if "-I" in line:
+        input_layout = ""
+
+      # Skip output layout if already in
+      output_layout = f"-O {layout} "
+      if "-O" in line:
+        output_layout = ""
+
+      one_config = f"{datatype}{direction}{filter_layout}{input_layout}{output_layout}{line}"
+      if one_config not in configs:
+        configs.append(one_config)
+
+  return configs
+
+
+def parse_line(line, dbt):
+  """Parse a command-line-style conv config into a ConvolutionConfig object."""
+
+  print(f"Parsing line {line}")
+
+  # '-t 1' means 'enable timing' which is confused with -t for type.
+  line = line.replace("-t 1", "")
+
+  # Convert the line ("-n 256 -c 1024 -H 14 ...") to dict of flag and value.
+  i = iter(line.split())
+  options = dict(zip(i, i))
+  #  print(f"options = {options}")
+
+  # Mapping of flag to field name.
+  # -F 1 -n 2 -c 1280 -H 32 -W 32 -k 640 -y 1 -x 1 -p 0 -q 0 -u 1 -v 1 -l 1 -j 1 -m conv -g 1 -t 1
+  fields = {
+      '-F': 'direction',
+      '-f': 'fil_layout',
+      '-I': 'in_layout',
+      '-O': 'out_layout',
+      '-n': 'batchsize',
+      '-c': 'in_channels',
+      '-H': 'in_h',
+      '-W': 'in_w',
+      '-k': 'out_channels',
+      '-y': 'fil_h',
+      '-x': 'fil_w',
+      '-p': 'pad_h',
+      '-q': 'pad_w',
+      '-u': 'conv_stride_h',
+      '-v': 'conv_stride_w',
+      '-l': 'dilation_h',
+      '-j': 'dilation_w',
+      '-g': 'group_size',
+      '-m': None,
+      '-t': 'data_type'
+  }
+  # kernel-repeats has no flag, but perfRunner.py uses 5.
+  # ConvConfiguration.fromCommandLine accepts but skips -m and -t.
+  #   -m is operation and -t is (I think) -time from MIOpenDriver.
+  # data_type is inferred from operation -- conv is f32, convfp16 is f16,
+  #   convbfp16 is bf16, convint8 is i8
+
+  config = dbt.config_table(kernel_repeats=1)
+  for flag, value in options.items():
+    field = fields[flag]
+    if field:
+      setattr(config, field, value)
+
+  return config
+
+
+def import_cfgs(args: argparse.Namespace, dbt: RocMLIRDBTables,
+                logger: logging.Logger):
+  """import configs to mysql from file with driver invocations"""
+  connect_db()
+
+  configs = get_conv_configurations(os.path.expanduser(args.file_name))
+  for line in configs:
+    try:
+      config = parse_line(line, dbt)
+
+      with DbSession() as session:
+        try:
+          print(f"Adding config {config} to db")
+          session.add(config)
+          session.commit()
+        except IntegrityError as err:
+          logger.warning("Error: %s", err)
+          session.rollback()
+
+    except ValueError as err:
+      logger.warning(err)
+
+  return len(configs)
+
+
+def main():
+  """Import conv-configs file into database rocmlir_conv_config table."""
+  # pylint: disable=duplicate-code
+  parser = argparse.ArgumentParser()
+  parser.add_argument('-f',
+                      '--file_name',
+                      type=str,
+                      dest='file_name',
+                      help='File to import')
+  args = parser.parse_args()
+  dbt = RocMLIRDBTables(session_id=None)
+  logger = setup_logger('import_configs')
+  counts = import_cfgs(args, dbt, logger)
+  logger.info('New configs added: %u', counts)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tuna/rocmlir/load_job.py b/tuna/rocmlir/load_job.py
new file mode 100755
index 00000000..47ff9d47
--- /dev/null
+++ b/tuna/rocmlir/load_job.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python3
+###############################################################################
+#
+# MIT License
+#
+# Copyright (c) 2023 Advanced Micro Devices, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+###############################################################################
+"""
+Script for adding jobs to the MySQL database
+"""
+
+# pylint: disable=duplicate-code
+from sqlalchemy.exc import IntegrityError
+
+from tuna.utils.logger import setup_logger
+from tuna.parse_args import TunaArgs, setup_arg_parser
+from tuna.utils.db_utility import connect_db
+from tuna.dbBase.sql_alchemy import DbSession
+from tuna.rocmlir.rocmlir_tables import RocMLIRDBTables
+
+LOGGER = setup_logger('rocmlir_load_jobs')
+
+
+def parse_args():
+  """ Argument input for the module """
+  #pylint: disable=duplicate-code
+  parser = setup_arg_parser(
+      'Insert jobs into MySQL db',
+      [TunaArgs.VERSION, TunaArgs.ARCH, TunaArgs.NUM_CU, TunaArgs.SESSION_ID])
+  parser.add_argument('-l',
+                      '--label',
+                      type=str,
+                      dest='label',
+                      required=True,
+                      help='Label to annotate the jobs.',
+                      default='new')
+
+  args = parser.parse_args()
+  if not args.session_id:
+    parser.error('session_id must be specified')
+
+  return args
+
+
+def add_jobs(args, dbt):
+  """ Add jobs based on args query specified"""
+  counts = 0
+  with DbSession() as session:
+    query = session.query(dbt.session_table.reason) \
+                   .filter(dbt.session_table.valid == 1,
+                           dbt.session_table.id == args.session_id)
+    reasons = query.all()
+    if len(reasons) > 1:
+      raise ValueError(f"More than one session matching ID {args.session_id}")
+    reason = reasons[0].reason
+
+    query = session.query(dbt.config_table.id)\
+                   .filter(dbt.config_table.valid == 1)
+    res = query.all()
+
+    if not res:
+      LOGGER.error('No applicable configs found for args %s', args.__dict__)
+
+    # pylint: disable=duplicate-code
+    for config in res:
+      try:
+        job = dbt.job_table(state='new',
+                            valid=1,
+                            reason=reason,
+                            session=args.session_id,
+                            config=config.id)
+        session.add(job)
+        session.commit()
+        counts += 1
+      except IntegrityError as err:
+        session.rollback()
+        LOGGER.warning('Integrity Error while adding new job: %s', err)
+
+    return counts
+
+
+def main():
+  """ main """
+  # pylint: disable=duplicate-code
+  args = parse_args()
+  connect_db()
+
+  dbt = RocMLIRDBTables(session_id=None)
+  cnt = add_jobs(args, dbt)
+
+  print(f"New jobs added: {cnt}")
+
+
+if __name__ == '__main__':
+  main()
diff --git a/tuna/rocmlir/rocmlir_lib.py b/tuna/rocmlir/rocmlir_lib.py
new file mode 100644
index 00000000..d8803f3d
--- /dev/null
+++ b/tuna/rocmlir/rocmlir_lib.py
@@ -0,0 +1,173 @@
+###############################################################################
+#
+# MIT License
+#
+# Copyright (c) 2023 Advanced Micro Devices, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+###############################################################################
+"""RocMLIR library, integrated with MITuna, runs 'tuningRunner.py' cmd"""
+
+# pylint: disable=duplicate-code
+import sys
+import argparse
+
+from typing import Dict, Any, List, Optional
+from tuna.mituna_interface import MITunaInterface
+from tuna.parse_args import TunaArgs, setup_arg_parser, args_check
+from tuna.utils.miopen_utility import load_machines
+from tuna.machine import Machine
+
+from tuna.libraries import Library
+from tuna.utils.db_utility import create_tables
+from tuna.rocmlir.rocmlir_tables import get_tables, SessionRocMLIR
+from tuna.rocmlir.rocmlir_worker import RocMLIRWorker
+from tuna.miopen.db.build_schema import recreate_triggers
+from tuna.rocmlir.triggers import get_timestamp_trigger
+
+
+class RocMLIR(MITunaInterface):
+  """Class to support a rocMLIR tuning run"""
+
+  def __init__(self):
+    super().__init__(library=Library.ROCMLIR)
+    self.args: argparse.Namespace = None
+
+  def parse_args(self) -> None:
+    # pylint: disable=too-many-statements
+    """Function to parse arguments"""
+    parser: argparse.ArgumentParser
+    # pylint: disable=duplicate-code
+    parser = setup_arg_parser('RocMLIR library integrated with MITuna', [
+        TunaArgs.ARCH, TunaArgs.NUM_CU, TunaArgs.VERSION, TunaArgs.SESSION_ID,
+        TunaArgs.MACHINES, TunaArgs.REMOTE_MACHINE, TunaArgs.LABEL,
+        TunaArgs.RESTART_MACHINE, TunaArgs.DOCKER_NAME
+    ])
+    group: argparse._MutuallyExclusiveGroup = parser.add_mutually_exclusive_group(
+    )
+    group.add_argument('--add_tables',
+                       dest='add_tables',
+                       action='store_true',
+                       help='Add RocMLIR library specific tables')
+
+    # pylint: disable=duplicate-code
+    group.add_argument(
+        '-e',
+        '--execute',
+        dest='execute',
+        action='store_true',
+        help='Run jobs from the job tables based on arch, num_cu and session_id'
+    )
+
+    group.add_argument('--init_session',
+                       action='store_true',
+                       dest='init_session',
+                       help='Set up a new tuning session.')
+
+    self.args = parser.parse_args()
+    if len(sys.argv) == 1:
+      parser.print_help()
+      sys.exit(-1)
+
+    args_check(self.args, parser)
+
+  def launch_worker(self, gpu_idx: int, f_vals: Dict[str, Any], \
+                    worker_lst: List[RocMLIRWorker]) -> bool:
+    """! Function to launch worker
+      @param gpu_idx Unique ID of the GPU
+      @param f_vals Dict containing runtime information
+      @param worker_lst List containing worker instances
+      @return Boolean value
+    """
+
+    # pylint: disable=duplicate-code
+    kwargs: Dict[str, Any] = self.get_kwargs(gpu_idx, f_vals)
+    worker: RocMLIRWorker = RocMLIRWorker(**kwargs)
+    if self.args.init_session:
+      SessionRocMLIR().add_new_session(self.args, worker)
+      return False
+
+    worker.start()
+    worker_lst.append(worker)
+    return True
+
+  def compose_worker_list(self, machines) -> Optional[List[RocMLIRWorker]]:
+    # pylint: disable=too-many-branches
+    """! Helper function to compose worker_list
+      @param machines list of available machine objects
+      @returns list of worker objects
+    """
+    worker_lst: List[RocMLIRWorker] = []
+    for machine in machines:
+      if self.args.restart_machine:
+        machine.restart_server(wait=False)
+        continue
+
+      #determine number of processes by compute capacity
+      worker_ids: List = machine.get_avail_gpus()
+      if len(worker_ids) == 0:
+        return None
+
+      # pylint: disable=duplicate-code
+      f_vals: Dict[str, Any] = super().get_f_vals(machine, worker_ids)
+
+      for gpu_idx in worker_ids:
+        self.logger.info('launch mid %u, proc %u', machine.id, gpu_idx)
+        if not self.launch_worker(gpu_idx, f_vals, worker_lst):
+          break
+
+    return worker_lst
+
+  def add_tables(self) -> bool:
+    # pylint: disable=duplicate-code
+    """Generates the library specific schema to the connected SQL server."""
+    ret_t: bool = create_tables(get_tables())
+    recreate_triggers(['timestamp_trigger'], get_timestamp_trigger())
+    self.logger.info('DB creation successful: %s', ret_t)
+    return True
+
+  def run(self) -> Optional[List[RocMLIRWorker]]:
+    # pylint: disable=duplicate-code
+    """Main run function of example_lib"""
+    res: Optional[List[RocMLIRWorker]]
+    self.parse_args()
+    if self.args.add_tables:
+      self.add_tables()
+      return None
+    machines: List[Machine] = load_machines(self.args)
+    res = self.compose_worker_list(machines)
+    return res
+
+  def get_envmt(self) -> List[str]:
+    # pylint: disable=unused-argument, disable=duplicate-code
+    """! Function to construct environment var
+    """
+    envmt: List[str] = []
+    return envmt
+
+  def get_kwargs(self, gpu_idx: int, f_vals: Dict[str, Any]) -> Dict[str, Any]:
+    # pylint: disable=duplicate-code
+    """! Helper function to set up kwargs for worker instances
+      @param gpu_idx Unique ID of the GPU
+      @param f_vals Dict containing process specific runtime information
+    """
+    kwargs: Dict[str, Any] = super().get_kwargs(gpu_idx, f_vals)
+
+    return kwargs
diff --git a/tuna/rocmlir/rocmlir_tables.py b/tuna/rocmlir/rocmlir_tables.py
new file mode 100644
index 00000000..6fe91916
--- /dev/null
+++ b/tuna/rocmlir/rocmlir_tables.py
@@ -0,0 +1,296 @@
+###############################################################################
+#
+# MIT License
+#
+# Copyright (c) 2023 Advanced Micro Devices, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+###############################################################################
+""" The necessary tables for rocMLIR tuning integration.
+    Copied and adapted from example and miopen support.
+"""
+
+import sys
+import enum
+from typing import List
+from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint
+from sqlalchemy import Text, Enum, Float, DateTime, orm
+from sqlalchemy import delete as sql_delete
+from sqlalchemy.ext.declarative import declared_attr
+from sqlalchemy.sql import func as sqla_func
+from sqlalchemy.inspection import inspect
+
+from tuna.dbBase.sql_alchemy import DbSession
+from tuna.dbBase.base_class import BASE
+from tuna.machine import Machine
+from tuna.session_mixin import SessionMixin
+from tuna.utils.logger import setup_logger
+from tuna.tables_interface import DBTablesInterface
+
+#pylint: disable=too-few-public-methods
+
+
+class SessionRocMLIR(BASE, SessionMixin):
+  """Session table to keep track of tuning sessions"""
+  #pylint: disable=attribute-defined-outside-init
+  #pylint: disable=duplicate-code
+
+  mlir_v = Column(String(length=64), nullable=False)
+
+  __tablename__ = "session_rocmlir"
+  __table_args__ = (UniqueConstraint("arch",
+                                     "num_cu",
+                                     "rocm_v",
+                                     "mlir_v",
+                                     "reason",
+                                     name="uq_idx"),)
+
+  def get_query(self, sess, sess_obj, entry):
+    """get session matching this object"""
+    query = sess.query(sess_obj.id)\
+        .filter(sess_obj.arch == entry.arch)\
+        .filter(sess_obj.num_cu == entry.num_cu)\
+        .filter(sess_obj.rocm_v == entry.rocm_v)\
+        .filter(sess_obj.mlir_v == entry.mlir_v)\
+        .filter(sess_obj.reason == entry.reason)\
+
+    return query
+
+  def add_new_session(self, args, worker):
+    """Add new session entry"""
+    super().add_new_session(args, worker)
+
+    if hasattr(args, 'mlir_v') and args.mlir_v:
+      self.mlir_v = args.mlir_v
+    else:
+      self.mlir_v = worker.get_mlir_v()
+
+    return self.insert_session()
+
+
+class JobEnum(enum.Enum):
+  """Job status.  Numbers chosen to match miopen, which has more states."""
+  # pylint: disable=invalid-name ; names represent entries in job_enum column
+  # pylint: disable=duplicate-code
+  new = 1
+  running = 3
+  completed = 4
+  error = 5
+
+
+class JobMixin():
+  """Essential attributes for all jobs in the job tables"""
+
+  @declared_attr
+  def session(self):
+    """session key, as a function to connect at run time"""
+    return Column(Integer, ForeignKey("session_rocmlir.id"), nullable=False)
+
+  reason = Column(String(length=60), nullable=False, server_default="")
+  state = Column(Enum(JobEnum), nullable=False, server_default="new")
+  retries = Column(Integer, nullable=False, server_default="0")
+  result = Column(Text, nullable=True)
+
+  compile_start = Column(DateTime,
+                         nullable=False,
+                         server_default=sqla_func.now())
+  compile_end = Column(DateTime, nullable=False, server_default=sqla_func.now())
+  gpu_id = Column(Integer, nullable=False, server_default="-1")
+  machine_name = Column(String(length=60), nullable=False, server_default="")
+
+
+class ConvolutionJob(BASE, JobMixin):
+  """Represents convolutions job table"""
+  __tablename__ = "rocmlir_conv_job"
+  __table_args__ = (UniqueConstraint('config', 'session', name="uq_idx"),)
+
+  config = Column(Integer,
+                  ForeignKey("rocmlir_conv_config.id"),
+                  nullable=False,
+                  index=True)
+
+
+class ConvolutionConfig(BASE):
+  """Represents convolution config table"""
+  __tablename__ = "rocmlir_conv_config"
+
+  data_type = Column(String(length=60), nullable=False, server_default="")
+  fil_layout = Column(String(60), nullable=False, server_default="NCHW")
+  in_layout = Column(String(60), nullable=False, server_default="NCHW")
+  out_layout = Column(String(60), nullable=False, server_default="NCHW")
+  direction = Column(String(length=8), nullable=False)
+  in_channels = Column(Integer, nullable=False, server_default="0")
+  in_h = Column(Integer, nullable=False, server_default="0")
+  in_w = Column(Integer, nullable=False, server_default="0")
+  fil_h = Column(Integer, nullable=False, server_default="0")
+  fil_w = Column(Integer, nullable=False, server_default="0")
+  out_channels = Column(Integer, nullable=False, server_default="0")
+  batchsize = Column(Integer, nullable=False, server_default="0")
+  pad_h = Column(Integer, nullable=False, server_default="0")
+  pad_w = Column(Integer, nullable=False, server_default="0")
+  conv_stride_h = Column(Integer, nullable=False, server_default="0")
+  conv_stride_w = Column(Integer, nullable=False, server_default="0")
+  dilation_h = Column(Integer, nullable=False, server_default="0")
+  dilation_w = Column(Integer, nullable=False, server_default="0")
+  group_size = Column(Integer, nullable=False, server_default="0")
+  kernel_repeats = Column(Integer, nullable=False, server_default="0")
+
+  def __repr__(self) -> str:
+    return f"ConvolutionConfig {self.to_dict()}"
+
+  # This dict maps field names, which are also the long option names, to
+  # the short forms used in the configs.  Necessary to turn a
+  # ConvolutionConfig back into a string that we can pass to the runner.
+  options = {
+      'direction': '-F',
+      'fil_layout': '-f',
+      'in_layout': '-I',
+      'out_layout': '-O',
+      'batchsize': '-n',
+      'in_channels': '-c',
+      'in_h': '-H',
+      'in_w': '-W',
+      'out_channels': '-k',
+      'fil_h': '-y',
+      'fil_w': '-x',
+      'pad_h': '-p',
+      'pad_w': '-q',
+      'conv_stride_h': '-u',
+      'conv_stride_w': '-v',
+      'dilation_h': '-l',
+      'dilation_w': '-j',
+      'group_size': '-g',
+      'data_type': '-t',
+      # getopt in ConvConfiguration.fromCommandLine only does single-char options.
+      # Count on tuneMLIRKernels to set config.MLIR_N_REPEATS to 1.
+      #    'kernel_repeats': '--kernel-repeats',
+      'kernel_repeats': None,
+      'id': None,
+      'valid': None
+  }
+
+  def config_string(self):
+    """Return config as a flag/value string suitable for tuningRunner.py."""
+    string = "conv "  # +++pf:  of course generalise for gemm
+    for field, value in self.to_dict().items():
+      flag = self.options[field]
+      if flag:
+        string += f"{flag} {value} "
+    string += "-m conv"
+    return string
+
+
+class ConvolutionResults(BASE):  # pylint: disable=too-many-instance-attributes
+  """Collects the results of convolution tuning.
+  """
+  __tablename__ = "rocmlir_conv_results"
+  __table_args__ = (UniqueConstraint("config", "session", name="uq_idx"),)
+
+  @orm.reconstructor
+  def __init__(self, **kwargs):
+    if 'logger' in kwargs:
+      self.logger = kwargs['logger']
+    else:
+      self.logger = setup_logger('results')
+
+  @declared_attr
+  def session(self):
+    """session column"""
+    return Column(Integer, ForeignKey("session_rocmlir.id"), nullable=False)
+
+  config = Column(Integer, ForeignKey("rocmlir_conv_config.id"), nullable=False)
+  config_str = Column(Text, nullable=False)
+
+  perf_config = Column(Text, nullable=False)
+  kernel_tflops = Column(Float, nullable=False)
+
+  def get_query(self, sess, result_obj, session_id):
+    """Construct a Db query for the find object
+    """
+    query = sess.query(result_obj).filter(result_obj.session == session_id,
+                                          result_obj.config == self.config)
+    self.logger.info("result query %s-%s", session_id, self.config)
+    return query
+
+  # +++pf:  rewrite me for tuningRunner.py output!
+  def parse(self, lines):
+    """parse logger output line for result data """
+    line = lines.splitlines()[-1]
+    print(f"line being parsed is '{line}'", file=sys.stderr)
+    return line.split('\t')
+
+
+#pylint: disable=too-few-public-methods
+class RocMLIRDBTables(DBTablesInterface):
+  """Represents db tables for rocMLIR lib"""
+
+  def __init__(self, **kwargs):
+    """Constructor"""
+    super().__init__(**kwargs)
+    allowed_keys = set(['config_type', 'session_id'])
+
+    self.config_type = None
+    self.job_table = None
+    self.session_table = SessionRocMLIR
+    self.config_table = None
+    self.results = None
+
+    self.__dict__.update(
+        (key, value) for key, value in kwargs.items() if key in allowed_keys)
+    self.set_tables()
+
+  def set_tables(self, sess_class=SessionRocMLIR):
+    """Set appropriate tables based on requirements"""
+    super().set_tables(sess_class)
+    # +++pf:  branch on self.config_type when we have GEMM, too.
+    self.job_table = ConvolutionJob
+    self.config_table = ConvolutionConfig
+    self.results = ConvolutionResults
+
+
+def get_tables() -> List[BASE]:
+  """Returns a list of all Example lib DB tables"""
+  tables: List[BASE] = []
+  with DbSession() as session:
+    engine = session.bind
+    connect = session.connection()
+
+    def append_if_not_exists(table):
+      # Note: this changes in sqlalchemy 1.4.
+      if not inspect(engine).dialect.has_table(connect, table.__tablename__):
+        tables.append(table)
+
+    append_if_not_exists(SessionRocMLIR())
+    append_if_not_exists(Machine(local_machine=True))
+    append_if_not_exists(ConvolutionConfig())
+    append_if_not_exists(ConvolutionJob())
+    append_if_not_exists(ConvolutionResults())
+
+  return tables
+
+
+def clear_tables():
+  """Get a clean state in the dase."""
+  dbt = RocMLIRDBTables(session_id=None)
+  with DbSession() as session:
+    session.execute(sql_delete(dbt.job_table))
+    session.execute(sql_delete(dbt.session_table))
+    session.execute(sql_delete(dbt.config_table))
+    session.execute(sql_delete(dbt.results))
diff --git a/tuna/rocmlir/rocmlir_worker.py b/tuna/rocmlir/rocmlir_worker.py
new file mode 100644
index 00000000..a8473b42
--- /dev/null
+++ b/tuna/rocmlir/rocmlir_worker.py
@@ -0,0 +1,167 @@
+###############################################################################
+#
+# MIT License
+#
+# Copyright (c) 2023 Advanced Micro Devices, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+###############################################################################
+"""Builder class implements the worker interface. The purpose of this class is to run the
+tuningRunner.py command"""
+
+import sys
+import os
+from time import sleep
+import random
+import functools
+
+from sqlalchemy.inspection import inspect
+
+from tuna.dbBase.sql_alchemy import DbSession
+from tuna.worker_interface import WorkerInterface
+from tuna.rocmlir.rocmlir_tables import RocMLIRDBTables
+from tuna.utils.db_utility import session_retry, gen_insert_query
+
+
+class RocMLIRWorker(WorkerInterface):
+  """ The RocMLIR class implements the worker class. Its purpose is to run a command. It picks up
+  new jobs and when completed, sets the state to completed. """
+
+  def __init__(self, **kwargs):
+    """Constructor"""
+    self.dbt = None
+    super().__init__(**kwargs)
+    self.set_db_tables()
+    self.result_attr = [column.name for column in inspect(self.dbt.results).c]
+    self.result_attr.remove("insert_ts")
+    self.result_attr.remove("update_ts")
+
+  def set_db_tables(self):
+    """Initialize tables"""
+    self.dbt = RocMLIRDBTables(session_id=self.session_id)
+
+  def update_result_table(self, session, result_str):
+    """update results table with individual result entry"""
+    obj = self.dbt.results()
+
+    arch, config, perf_config, tflops = obj.parse(result_str)
+
+    print(
+        f"arch = '{arch}', config = '{config}', perf_config = '{perf_config}', tflops = {tflops}",
+        file=sys.stderr)
+
+    obj.valid = 1
+    obj.session = self.dbt.session.id
+    obj.arch = arch
+    obj.config = self.job.config
+    obj.config_str = config
+    obj.perf_config = perf_config
+    obj.kernel_tflops = tflops
+
+    self.logger.info('Inserting results for job_id=%s', self.job.id)
+    query = gen_insert_query(obj, self.result_attr,
+                             self.dbt.results.__tablename__)
+    session.execute(query)
+    session.commit()
+    return True
+
+  def process_result(self, result_str: str):
+    """process tuning-run results"""
+    with DbSession() as session:
+
+      def actuator(func, result_str):
+        return func(session, result_str)
+
+      #retry returns false on failure, callback return on success
+      ret = session_retry(session, self.update_result_table,
+                          functools.partial(actuator, result_str=result_str),
+                          self.logger)
+      if not ret:
+        self.logger.warning('RocMLIR:  Unable to update database')
+      return ret
+
+  def output_filename(self):
+    """Canonical name for tuningRunner.py output for a job."""
+    return f"tuning-results-{self.job.id}.tsv"
+
+  def step(self):
+    """Main functionality of the worker class. It picks up jobs in new state and executes them"""
+
+    if not self.get_job("new", "running", False):
+      #Sleep in case of DB contention
+      sleep(random.randint(1, 10))
+      return False
+
+    self.logger.info('Acquired new job: job_id=%s', self.job.id)
+    self.set_job_state('running')
+
+    try:
+      retcode, cmd_output = self.run_cmd()
+    except ValueError as verr:
+      self.logger.info(verr)
+      self.set_job_state('errored', result=verr)
+    else:
+      if retcode != 0:
+        quoted_output = cmd_output.replace("'", r"\'")
+        msg = f"Error code {retcode}, output {quoted_output}"
+        self.logger.info(msg)
+        self.set_job_state('errored', result=msg)
+      else:
+        try:
+          with open(self.output_filename(), 'r', encoding='utf8') as results:
+            # https://stackoverflow.com/questions/49902843/avoid-parameter-binding-when-executing-query-with-sqlalchemy
+            string = results.read().replace(':', r'\:')
+            self.set_job_state('completed', result=string)
+            self.process_result(string)
+          os.remove(self.output_filename())
+        except OSError as exc:
+          # Most often FileNotFoundError.
+          self.logger.warning(
+              'Exception occurred while saving results of job %s:  %s',
+              self.job.id, exc)
+          self.set_job_state('errored', result=repr(exc))
+
+    return True
+
+  def run_cmd(self):
+    """Run the actual workload"""
+    env_str = " ".join(self.envmt)
+    with DbSession() as session:
+      cft = self.dbt.config_table
+      query = session.query(cft).filter(cft.id == self.job.config)
+      config = query.all()
+      if len(config) > 1:
+        raise ValueError(f"More than one config matching ID {self.job.config}")
+      config_string = config[0].config_string()
+    cmd = env_str + f" python3 ./bin/tuningRunner.py --operation conv \
+                     --config='{config_string}' --mlir-build-dir `pwd` \
+                     --output={self.output_filename()} --tflops \
+                     --rocmlir_gen_flags='--device={self.gpu_id}'"
+
+    retcode, out = super().run_command(cmd)
+
+    return retcode, out
+
+  def get_mlir_v(self) -> str:
+    """Interface function to get mlir version info"""
+    #_, mlir_ver, _ = self.exec_docker_cmd("cat /opt/rocm/.info/version")
+    mlir_ver = "mlir_v-not-yet-implemented"
+    self.logger.info('Got mlir version: %s', mlir_ver)
+    return mlir_ver
diff --git a/tuna/rocmlir/triggers.py b/tuna/rocmlir/triggers.py
new file mode 100644
index 00000000..96250082
--- /dev/null
+++ b/tuna/rocmlir/triggers.py
@@ -0,0 +1,40 @@
+###############################################################################
+#
+# MIT License
+#
+# Copyright (c) 2022 Advanced Micro Devices, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+###############################################################################
+"""Database triggers for timestamps."""
+
+
+def get_timestamp_trigger():
+  """setting up for job table triggers"""
+  trigger_timestamp = """CREATE trigger timestamp_trigger before UPDATE on rocmlir_conv_job
+  for each row
+  begin
+   if NEW.state='running' then set NEW.compile_start=now();
+   elseif NEW.state='completed' then set NEW.compile_end=now();
+   elseif NEW.state='errored' then set NEW.compile_end=now();
+   end if;
+  end;"""
+
+  return [trigger_timestamp]
diff --git a/tuna/utils/db_utility.py b/tuna/utils/db_utility.py
index 710d01f9..4d8e4b88 100644
--- a/tuna/utils/db_utility.py
+++ b/tuna/utils/db_utility.py
@@ -90,7 +90,7 @@ def create_tables(all_tables):
     except (OperationalError, ProgrammingError) as err:
       LOGGER.warning('Err occurred %s \n For table: %s.', err, table)
       LOGGER.warning(
-          'Schema migration not implemented, please udpate schema manually')
+          'Schema migration not implemented, please update schema manually')
       continue
 
   return True
@@ -182,7 +182,7 @@ def get_attr_vals(obj, attr_list):
 
 
 def gen_update_query(obj, attribs, tablename):
-  """Create an update query strig to table with tablename for an object (obj)
+  """Create an update query string to table with tablename for an object (obj)
   for the attributes in attribs"""
   set_arr = []
   attr_vals = get_attr_vals(obj, attribs)
diff --git a/tuna/worker_interface.py b/tuna/worker_interface.py
index ab622826..2f279132 100644
--- a/tuna/worker_interface.py
+++ b/tuna/worker_interface.py
@@ -509,7 +509,7 @@ def run(self) -> bool:  #type: ignore[override]
         ret: bool = self.step()
         self.logger.info("proc %s step %s", self.gpu_id, ret)
         if not ret:
-          self.logger.warning('No more steps, quiting...')
+          self.logger.warning('No more steps, quitting...')
           with self.bar_lock:
             self.num_procs.value -= 1
           return True
diff --git a/vars/utils.groovy b/vars/utils.groovy
index 699b9851..bc18f338 100644
--- a/vars/utils.groovy
+++ b/vars/utils.groovy
@@ -46,7 +46,7 @@ def buildSchema(){
 
 def cleanup() {
     def cmd = $/mysql --protocol tcp -h ${db_host} -u ${db_user} -p${db_password}  -e "DROP DATABASE IF EXISTS ${db_name}"/$
-    sh "${cmd}"        
+    sh "${cmd}"
 }
 
 def getMachine() {
@@ -179,7 +179,7 @@ def finFindCompile(){
         if (num_compiled_jobs != num_jobs){
             error("Unable to compile find jobs using Fin")
         }
-        
+
         sh "./tuna/go_fish.py miopen import_configs -t recurrent_${branch_id}_nhwc --mark_recurrent -f utils/configs/conv_configs_NHWC.txt --model Resnet50 --md_version 1 --framework Pytorch --fw_version 1"
         def num_cfg_nhwc = runsql("SELECT count(*) from conv_config;")
         println "Count(*) conv_config table: ${num_cfg_nhwc}"
@@ -459,9 +459,9 @@ def pytestSuite1() {
         addMachine(arch, num_cu, machine_ip, machine_local_ip, username, pwd, port)
         // download the latest perf db
         //runsql("DELETE FROM config_tags; DELETE FROM job; DELETE FROM config;")
-        sshagent (credentials: ['bastion-ssh-key']) {   
+        sshagent (credentials: ['bastion-ssh-key']) {
            sh "coverage erase"
-           sh "python3 -m coverage run -a -m pytest tests/test_export_db.py -s"            
+           sh "python3 -m coverage run -a -m pytest tests/test_export_db.py -s"
            sh "python3 -m coverage run -a -m pytest tests/test_abort_file.py -s"
            sh "python3 -m coverage run -a -m pytest tests/test_analyze_parse_db.py -s"
            sh "python3 -m coverage run -a -m pytest tests/test_connection.py -s"
@@ -479,6 +479,10 @@ def pytestSuite1() {
            sh "python3 -m coverage run -a -m pytest tests/test_example.py -s"
            sh "python3 -m coverage run -a -m pytest tests/test_yaml_parser.py -s"
            sh "python3 -m coverage run -a -m pytest tests/test_load_job.py -s"
+           sh "python3 -m coverage run -a -m pytest tests/test_add_session_rocmlir.py -s"
+           sh "python3 -m coverage run -a -m pytest tests/test_importconfigs_rocmlir.py -s"
+           sh "python3 -m coverage run -a -m pytest tests/test_load_job_rocmlir.py -s"
+           sh "python3 -m coverage run -a -m pytest tests/test_rocmlir.py -s"
            // The OBMC host used in the following test is down
            // sh "pytest tests/test_mmi.py "
         }
@@ -504,7 +508,7 @@ def pytestSuite2() {
         addMachine(arch, num_cu, machine_ip, machine_local_ip, username, pwd, port)
         // download the latest perf db
         //runsql("DELETE FROM config_tags; DELETE FROM job; DELETE FROM config;")
-        sshagent (credentials: ['bastion-ssh-key']) {                 
+        sshagent (credentials: ['bastion-ssh-key']) {
            // test fin builder and test fin builder conv in sequence
            sh "python3 -m coverage run -a -m pytest tests/test_worker.py -s"
            sh "TUNA_LOGLEVEL=INFO python3 -m coverage run -a -m pytest tests/test_fin_builder.py -s"
@@ -525,7 +529,7 @@ def pytestSuite3AndCoverage(current_run, main_branch) {
         env.gateway_user = "${gateway_user}"
         env.PYTHONPATH=env.WORKSPACE
         env.PATH="${env.WORKSPACE}/tuna:${env.PATH}"
-        sshagent (credentials: ['bastion-ssh-key']) {                 
+        sshagent (credentials: ['bastion-ssh-key']) {
            sh "python3 -m coverage run -a -m pytest tests/test_fin_evaluator.py -s"
            sh "python3 -m coverage run -a -m pytest tests/test_update_golden.py -s"
         }
@@ -565,7 +569,7 @@ def runLint() {
           checkout scm
           def tuna_docker = docker.build("ci-tuna:${branch_id}", "--build-arg FIN_TOKEN=${FIN_TOKEN} .")
           tuna_docker.inside("") {
-            sh "cd tuna && pylint -f parseable --max-args=8 --ignore-imports=no --indent-string='  ' *.py miopen/*.py example/*.py"
+            sh "cd tuna && pylint -f parseable --max-args=8 --ignore-imports=no --indent-string='  ' *.py miopen/*.py example/*.py rocmlir/*.py"
             sh "cd tuna && find miopen/scripts/ -type f -name '*.py' | xargs pylint -f parseable --max-args=8 --ignore-imports=no --indent-string='  '"
             sh "cd tuna && find miopen/driver/ -type f -name '*.py' | xargs pylint -f parseable --max-args=8 --ignore-imports=no --indent-string='  '"
             sh "cd tuna && find miopen/worker/ -type f -name '*.py' | xargs pylint -f parseable --max-args=8 --ignore-imports=no --indent-string='  '"
@@ -615,6 +619,11 @@ def runLint() {
             sh "mypy tuna/dbBase/base_class.py --ignore-missing-imports"
             sh "mypy tuna/example/session.py --ignore-missing-imports --follow-imports=skip"
             sh "mypy tuna/example/example_worker.py --ignore-missing-imports --follow-imports=skip"
+            sh "mypy tuna/rocmlir/import_configs.py --ignore-missing-imports --follow-imports=skip"
+            sh "mypy tuna/rocmlir/load_job.py --ignore-missing-imports --follow-imports=skip"
+            sh "mypy tuna/rocmlir/rocmlir_lib.py --ignore-missing-imports --follow-imports=skip"
+            sh "mypy tuna/rocmlir/rocmlir_tables.py --ignore-missing-imports --follow-imports=skip"
+            sh "mypy tuna/rocmlir/rocmlir_worker.py --ignore-missing-imports --follow-imports=skip"
           }
     }
 }
@@ -638,7 +647,7 @@ def getDockerName(backend)
 }
 
 def LoadJobs()
-{ 
+{
   def script_args = ''
   def new_label = ''
   if(params.job_label == '')
@@ -684,7 +693,7 @@ def LoadJobs()
   tuna_docker.inside("${docker_run_args}") {
       env.PYTHONPATH=env.WORKSPACE
       env.PATH="${env.WORKSPACE}/tuna:${env.PATH}"
-      env.TUNA_LOGLEVEL="${tuna_loglevel}" 
+      env.TUNA_LOGLEVEL="${tuna_loglevel}"
 
       echo "./tuna/go_fish.py miopen load_job --session_id ${params.session_id} ${script_args}"
       sh "python3 ./tuna/go_fish.py miopen load_job --session_id ${params.session_id} ${script_args}"
@@ -805,7 +814,7 @@ def compile()
       env.TUNA_LOGLEVEL="${tuna_loglevel}"
       sh "pwd"
   }
-  // push the image 
+  // push the image
   tuna_docker.push()
   env_list = params.env.split(' ')
   for(item in env_list)
@@ -909,4 +918,3 @@ def doxygen() {
           }
     }
 }
-


Test patch:
diff --git a/tests/test_add_session_rocmlir.py b/tests/test_add_session_rocmlir.py
new file mode 100644
index 00000000..e183a536
--- /dev/null
+++ b/tests/test_add_session_rocmlir.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+###############################################################################
+#
+# MIT License
+#
+# Copyright (c) 2022 Advanced Micro Devices, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+###############################################################################
+import os
+import sys
+import socket
+from multiprocessing import Value, Lock, Queue
+
+sys.path.append("../tuna")
+sys.path.append("tuna")
+
+this_path = os.path.dirname(__file__)
+
+from tuna.machine import Machine
+from tuna.dbBase.sql_alchemy import DbSession
+from tuna.rocmlir.rocmlir_worker import RocMLIRWorker
+from tuna.rocmlir.rocmlir_tables import SessionRocMLIR
+from tuna.rocmlir.rocmlir_lib import RocMLIR
+from utils import DummyArgs
+
+
+def test_add_session_rocmlir():
+  rocmlir = RocMLIR()
+  assert (rocmlir.add_tables())
+
+  num_gpus = Value('i', 1)
+  v = Value('i', 0)
+  e = Value('i', 0)
+  docker_name = 'Dummy-Docker'
+  hostname = socket.gethostname()
+  machine = Machine(hostname=hostname, local_machine=True)
+
+  kwargs = {
+      'machine': machine,
+      'gpu_id': 0,
+      'num_procs': num_gpus,
+      'barred': v,
+      'bar_lock': Lock(),
+      'reset_interval': False,
+      'app_test': False,
+      'label': 'testing_add_session',
+      'use_tuner': False,
+      'job_queue': Queue(),
+      'queue_lock': Lock(),
+      'fetch_state': ['compiled'],
+      'end_jobs': e,
+  }
+
+  args = DummyArgs()
+  args.add_session = True
+  args.arch = 'gfx908'
+  args.num_cu = 120
+  args.reason = "testing_add_session"
+  args.ticket = "JIRA-Dummy-123"
+  args.label = "my_dummy_label"
+  args.docker_name = docker_name
+
+  worker = RocMLIRWorker(**kwargs)
+  sess_id = SessionRocMLIR().add_new_session(args, worker)
+  print(f"session id: {sess_id}")
+  assert (sess_id)
+
+  with DbSession() as session:
+    res = session.query(SessionRocMLIR).filter(
+        SessionRocMLIR.id == sess_id).one()
+    assert (res)
+    assert (res.reason == "my_dummy_label")
+    assert (res.rocm_v)
+    assert (res.mlir_v)
+    assert (res.docker == docker_name)
diff --git a/tests/test_importconfigs_rocmlir.py b/tests/test_importconfigs_rocmlir.py
new file mode 100644
index 00000000..aef71b70
--- /dev/null
+++ b/tests/test_importconfigs_rocmlir.py
@@ -0,0 +1,80 @@
+###############################################################################
+#
+# MIT License
+#
+# Copyright (c) 2022 Advanced Micro Devices, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+###############################################################################
+
+import os
+import sys
+
+sys.path.append("../tuna")
+sys.path.append("tuna")
+
+this_path = os.path.dirname(__file__)
+
+from tuna.utils.logger import setup_logger
+from tuna.rocmlir.import_configs import import_cfgs
+from tuna.sql import DbCursor
+from tuna.rocmlir.rocmlir_tables import RocMLIRDBTables
+from tuna.rocmlir.rocmlir_lib import RocMLIR
+from utils import CfgImportArgs
+
+SAMPLE_CONV_CONFIGS = """
+# This section of the file comes from resnet50-miopen-configs
+-n 256 -c 1024 -H 14 -W 14 -k 2048 -y 1 -x 1 -p 0 -q 0 -u 2 -v 2 -l 1 -j 1 -m conv -g 1 -t 1
+-n 256 -c 1024 -H 14 -W 14 -k 256 -y 1 -x 1 -p 0 -q 0 -u 1 -v 1 -l 1 -j 1 -m conv -g 1 -t 1
+
+# This section of the file is selected conv configs for unet (stable diffusion).
+-F 1 -n 2 -c 1280 -H 32 -W 32 -k 640 -y 1 -x 1 -p 0 -q 0 -u 1 -v 1 -l 1 -j 1 -m conv -g 1 -t 1
+-F 1 -n 2 -c 1280 -H 32 -W 32 -k 640 -y 3 -x 3 -p 1 -q 1 -u 1 -v 1 -l 1 -j 1 -m conv -g 1 -t 1
+"""
+
+
+def test_importconfigs_rocmlir():
+  with open("test-conv-configs", 'w') as f:
+    f.write(SAMPLE_CONV_CONFIGS)
+  test_import_conv()
+
+
+def test_import_conv():
+  rocmlir = RocMLIR()
+  assert (rocmlir.add_tables())
+
+  dbt = RocMLIRDBTables(session_id=None)
+  logger = setup_logger('test_importconfigs')
+  find_configs = "SELECT count(*) FROM rocmlir_conv_config;"
+
+  with DbCursor() as cur:
+    cur.execute(find_configs)
+    res = cur.fetchall()
+    before_cfg_num = res[0][0]
+
+  args = CfgImportArgs
+  args.file_name = "test-conv-configs"
+  counts = import_cfgs(args, dbt, logger)
+
+  with DbCursor() as cur:
+    cur.execute(find_configs)
+    res = cur.fetchall()
+    after_cfg_num = res[0][0]
+    assert (after_cfg_num - before_cfg_num == counts)
diff --git a/tests/test_load_job_rocmlir.py b/tests/test_load_job_rocmlir.py
new file mode 100644
index 00000000..2de9ef08
--- /dev/null
+++ b/tests/test_load_job_rocmlir.py
@@ -0,0 +1,58 @@
+###############################################################################
+#
+# MIT License
+#
+# Copyright (c) 2023 Advanced Micro Devices, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+###############################################################################
+
+import os
+import sys
+import argparse
+import logging
+import random
+
+sys.path.append("../tuna")
+sys.path.append("tuna")
+
+this_path = os.path.dirname(__file__)
+
+from tuna.sql import DbCursor
+from tests.test_importconfigs_rocmlir import test_importconfigs_rocmlir
+from tuna.rocmlir.load_job import add_jobs
+from tuna.rocmlir.rocmlir_tables import RocMLIRDBTables, clear_tables
+
+
+def test_cfg_compose():
+  """check the config query function for args tags and cmd intake"""
+  clear_tables()
+  test_importconfigs_rocmlir()  # to get the configs in place
+  # +++pf: init a session, too.
+  count_configs = "SELECT count(*) FROM rocmlir_conv_config;"
+  with DbCursor() as cur:
+    cur.execute(count_configs)
+    res = cur.fetchall()
+    config_count = res[0][0]
+
+  dbt = RocMLIRDBTables(session=None)
+  args = argparse.Namespace(session_id=1)
+  job_count = add_jobs(args, dbt)
+  assert job_count == config_count
diff --git a/tests/test_rocmlir.py b/tests/test_rocmlir.py
new file mode 100644
index 00000000..52906819
--- /dev/null
+++ b/tests/test_rocmlir.py
@@ -0,0 +1,103 @@
+###############################################################################
+#
+# MIT License
+#
+# Copyright (c) 2022 Advanced Micro Devices, Inc.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+###############################################################################
+
+import os
+import sys
+
+sys.path.append("../tuna")
+sys.path.append("tuna")
+
+this_path = os.path.dirname(__file__)
+
+from tuna.utils.logger import setup_logger
+from tuna.rocmlir.rocmlir_lib import RocMLIR
+from utils import ExampleArgs
+from tuna.utils.miopen_utility import load_machines
+from tuna.dbBase.sql_alchemy import DbSession
+from tuna.rocmlir.rocmlir_tables import SessionRocMLIR, ConvolutionJob, RocMLIRDBTables, clear_tables
+from tuna.rocmlir.load_job import add_jobs
+from tuna.rocmlir.import_configs import import_cfgs
+from utils import CfgImportArgs
+
+SAMPLE_CONV_CONFIGS = """
+-F 1 -n 256 -c 1024 -H 14 -W 14 -k 2048 -y 1 -x 1 -p 0 -q 0 -u 2 -v 2 -l 1 -j 1 -m conv -g 1 -t 1
+"""
+
+
+def test_rocmlir():
+  logger = setup_logger('test_rocmlir')
+  dbt = RocMLIRDBTables(session_id=None)
+
+  rocmlir = RocMLIR()
+  assert (rocmlir.add_tables())
+  clear_tables()
+
+  # To get some sample configs imported.
+  with open("test-conv-configs", 'w') as f:
+    f.write(SAMPLE_CONV_CONFIGS)
+  args = CfgImportArgs
+  args.file_name = "test-conv-configs"
+  count = import_cfgs(args, dbt, logger)
+  assert count == 6
+
+  rocmlir.args = ExampleArgs()
+  rocmlir.args.init_session = True
+  rocmlir.args.label = 'test_rocmlir'
+  machines = load_machines(rocmlir.args)
+  # With .init_session True, launch_worker adds a session and bails.
+  rocmlir.compose_worker_list(machines)
+  with DbSession() as session:
+    query = session.query(SessionRocMLIR)
+    res = query.all()
+    assert len(res) is not None
+
+  #test load_job
+  rocmlir.args.init_session = False
+  rocmlir.args.session_id = 1
+  rocmlir.args.execute = True
+  rocmlir.args.config = 1
+  num_jobs = add_jobs(rocmlir.args, dbt)
+  assert num_jobs == 6
+
+  # +++pf:  can't get this part to work
+  #   #testing execute rocminfo
+  #   # With .init_session False, launch_worker starts the worker.
+  #   workers = rocmlir.compose_worker_list(machines)
+  #   assert workers
+  #   for worker in workers:
+  #     worker.join()
+  #   with DbSession() as session:
+  #     query = session.query(ConvolutionJob).filter(ConvolutionJob.session==1)\
+  #                                          .filter(ConvolutionJob.state=='completed')
+  #     res = query.all()
+  #     assert len(res) == 0
+  #     # Because rocMLIR is not in path, error
+  #     query = session.query(ConvolutionJob).filter(ConvolutionJob.session==1)\
+  #                                          .filter(ConvolutionJob.state=='errored')
+  #     res = query.all()
+  #     assert len(res) == 6
+
+  return True