Tutorial : customize GCC with MELT

Audience: You are expected to be an advanced GCC user (fluent with command-line), and to have successfully downloaded and installed GCC MELT on your computer (probably under GNU/Linux running a recent version -preferably 4.7 or later- of GCC). You have read the tutorial on how to use GCC MELT and then the tutorial on the MELT domain specific language. You may want to have a look at GCC internals documentation (each released version of GCC has its own).

Adding a mode for GCC in MELT

Remember that without any mode given with the -fplugin-arg-melt-mode=mode program argument to gcc or g++, the MELT plugin don't do anything useful. You should choose a name for your mode (which should not clash with existing mode names given by the help mode with -fplugin-arg-melt-mode=help). Imagine that we want to implement a mode which counts the functions defined in the client source code which have a single integer argument. Let's name this mode count1int.

We need to install a new mode, by creating one instance of class_melt_mode then passing it to install_melt_mode. That instance, which we name 'count1int_mode contains a function, here count1int_docmd, doing the real job. We'll define that count1int_docmd function below (but it should appear first in your MELT source code file, before count1int_mode):

(definstance count1int_mode class_melt_mode
  :named_name '"count1int"
  :meltmode_help '"install a pass counting functions with a single integer argument."
  :meltmode_fun count1int_docmd
)
(install_melt_mode count1int_mode)
The string literals should be quoted, since they are values!
The function doing the mode's job, here count1int_docmd, should usually install one -or several- GCC pass[es] coded in MELT. Of course it could do other mode-specific initialization (e.g. connecting to some database server).

Adding a pass for GCC in MELT

The GCC compiler is running a lot (more than 200 when optimizing!) of passes. You'll want to add your own pass within them. There are several kind of passes, the simplest being plain gimple passes. A pass has some name (we suggest to start the name with melt_ to avoid collision with existing passes names), it also has a optional gate function deciding if the pass should be run or skipped, and an execute function doing the actual job.

We obviously need a global counter, as a boxed value:

(define count1int_counter (box 0))

Where to add your own pass?

An important issue is to understand where should your own pass be added. It is difficult to answer. Look into files gcc/passes.c and gcc/tree-pass.h of your GCC compiler source code (you probably should download the source code of your gcc compiler, to be able to study it). You could add your pass after cfg (the control flow graph pass), or phiopt, etc...

Installing your pass

Our count1int_docmd function, referenced by coutin1int_mode, is creating and installing a pass, instance of class_gcc_gimple_pass:

(defun count1int_docmd (cmd moduldata)
  (let ( (count1intpass
	  (instance class_gcc_gimple_pass
		    :named_name '"melt_count1intpass"
		    :gccpass_exec count1intpass_exec
		    :gccpass_data (make_maptree discr_map_trees 100)
	      ))
        )
  (install_melt_pass_in_gcc count1intpass :after '"cfg" 0)
  (debug  "count1int_mode installed pass=" count1intpass)
  (return cmd)			;return non-nil to continue compilation
  ))
    
The MELT pass has a field :gccpass_data to keep some arbitrary data for client convenience. Here it is an hash-map keyed by :tree stuff (we'll use that to avoid processing several times the same C function in the compiled code). Our pass is so simple that we don't need any gate function (but you could give one using the :gccpass_gate field).

Coding your pass

Our pass is simple, but it has to figure out if it encounters an already seen C function (because the GCC compiler is complex enough to be able to process several times some functions of the input source code). The current function declaration (as some :tree stuff) is given by the cfun_decl primitive. We use the pass' client data.

(defun count1intpass_exec (pass)
   (let ( (trmap (get_field :gccpass_data pass))
          (curfun (cfun_decl))
        )
      (match curfun
        (?(tree_function_decl ?tname ?tres)
           (if (maptree_get trmap curfun)
               (return) ;;already seen function
               (let ( (oldcnt (get_int count1int_counter))
                    )
                   (maptree_put trmap curfun :true)
  ;; incomplete: should figure out if the function has one single formal.
                 ))
         )
         (?_ ;; not a function, but a variable, a top level asm, ...
          (return))
      )
))
This is incomplete. We need to figure out if the function has one integer formal argument. We still need to inform the user about the counted number of functions, e.g. by registering a function to be done before exiting using at_exit_first.