Hello All on the gcc-melt@ list....
(with blind copy to the person who sent me some questions about MELT in private)
I've got some technical questions by [private] email. Since the answer to them
should be of general interest, I dare giving my answers publicly on this public
list
gcc-...@googlegroups.com; I'm somehow rephrasing the questions.
A general advice about starting on MELT is to understand some of GCC internals, and the
absolutely fondamental difference between first-class values and stuff (e.g. :gimple things)
Also, please download and use the latest MELT snapshot...
******
** rewriting GIMPLE code:
It should be possible to transform GIMPLE with MELT.
The real issue (and that may be a hard one, and the
details depend upon the use case) is to understand when to do
that transformation.
Every GCC optimization pass is transforming internal GCC representations (Gimple) into
another such representation, and you can insert arbitrary passes in MELT.
However, the GCC compiler itself has its own invariant properties about its passes.
The MELT pieces relevant for that are in files gcc/melt/xtramelt*.melt
creation of your instance of class_gcc_gimple_pass or class_gcc_simple_ipa_pass
installing that MELT pass using install_melt_gcc_pass
The difficult issue is where to install that pass.
And that depends upon what your pass want to do.
You have the basic components for making such a pass. Of course,
the pattern matching on gimple, and also the fact
that most constructs are able both to pattern match and
to construct some gimple.
For instance, you use the gimple_assign_plus in a pattern to detect simple arithmetic operations:
(match g ;; a gimple stuff
(?(gimple_assign_plus ?lhs ?rhs1 ?rhs2)
;; do something when g is an addition lhs = rsh1 + rhs2
(your_code_with lhs rhs1 rhs2)
)
(?_
;; wildcard catch-all
;; otherwise do code here
(your_other_code)
))
But you can also build such a gimple using the (gimple_assign_plus lhs rhs1 rhs2) expression.
The issue is what to do with that gimple. It depends.... (Don't forget that you always
can use code_chunk to write some C++ code mixing MELT variables with GCC function calls)
So MELT should enable you to do "aspect oriented programming" (but you need to understand
where you will insert your MELT pass in the GCC pass flow).
*****
** macros in MELT
MELT does not support (today, april 2013)
yet macros as easily as Scheme or Common Lisp do, e.g.
(defmacro addone (a) `(+i ,a 1))
because the backquote and comma syntax macros are not yet implemented.
However, the reader is parsing them. Implementing backquote & comma is on my TODO list
(it would require using TRANSLATE_RUN_MELT_EXPRESSIONS, which is nearly our EVAL, but internally generates some *.c file, compile it into some *.so, and dlopen that, and run some
dlsym-ed function inside...).
However, there is some crude way to do macros today. And actually,
most of MELT implementation is using macros already.... So let me explain a bit more...
First, an "s-expr" in MELT is actually an instance of CLASS_SEXPR with two fields, the first
giving (if known) the source location, and the second giving the list of components
in that s-expr. So the s-expr (+i 2 3) supposedly at line 3 column 4 of foo.melt
is parsed by the MELT reader coded in C as an instance of CLASS_SEXPR a bit like
(instance class_sexpr
:loca_location (the_location_at "foo.melt" 3 4)
:sexp_content (list '+i 2 3))
[[but I have no the_location_at primitive, it is here to explain you what the MELT reader do]]
Then, the MELT translator indeed do some macro-expansion on every MELT s-expression.
The macro expansion of any s-expr is supposed to give an instance of some sub-class of
CLASS_SOURCE. Actually, the macro-expansion of the above s-expr is something similar to
the result of
(instance class_source_primitive
:loca_location (the_location_at "foo.melt" 3 4)
:sprim_oper the_+i_primitive
:sargop_args (tuple 2 3))
Of course, the_+i_primitive is something complex
an instance of class_primitive which probably is the value of the +i symbol
Now, you may have macros: they should expand some instance of CLASS_SEXPR
into some instance of a subclass of CLASS_SOURCE. Every macro expander
is a function (a closure) recieving as arguments:
1. the s-expr, instance of CLASS_SEXPR, to expand
2. the environment in which you are expanding
3. the macro-expander to be used to recursively macro-expand
components of your s-expr.
4. a module generation context, instance of CLASS_MODULE_CONTEXT
and that macro-expander should return an instance of some subclass of CLASS_SOURCE
or perhaps some "atomic" value like a symbol or a boxed integer.
Later, that value -often some CLASS_SOURCE indirect instance- will be normalized.
Once you have your macro-expander function mexpand_foo,
you can install it for your macro foo with
(export_macro foo mexpand_foo)
then further MELT modules (but not the current one) are able to use the FOO macro....
I invite you to look at the code of mexpand_unless
in file melt/warmelt-macro.melt perhaps near line 4470
as a simple example of macro.
So the macro ADDONE might be coded as perhaps the following,
which build the temporary sexpression (+i ARG 1) and macroexpands it.
;; untested code, could be wrong
(defun mexpand_addone (sexpr env mexpander modctx)
(let (
(cont (get_field :sexp_contents sexpr))
(loc (get_field :loca_location sexpr))
(contup (list_to_multiple cont discr_multiple))
)
;; we have 2 components in our sexpr, the first being ADDONE
(when (!=i (multiple_length contup) 2)
(error_plain loc "(ADDONE <expr>) wants a single argument)
(return))
(let ( (macarg (multiple_nth contup 1))
(sexplus (instance class_sexpr
:loca_location loc
:sexp_contents (list '+i macarg 1)))
(res (mexpand sexplus env mexpander modctx))
)
res)))
Notice that we are making a temporary instance sexplus of CLASS_SEXPR and expanding it.
This is not the most clever, we could macroexpand only macarg and create
the appropriate instance of class_source_primitive
Later in the same MELT source code we do
(export_macro addone mexpand_addone)
Then any further MELT module (but not the current one!) can use addone.
So you would define your macros in a foomacro.melt file,
and have your GCC extension in another fooana.melt file.
By the way, you could even extend your MELT language even further:
you could perhaps add your own subclasses of CLASS_SOURCE, and then
add their normalization method, etc... You could even have your own
subclasses of CLASS_NREP -representing normalized forms- etc etc...
So an adventurous MELT user could extend a lot the MELT language,
without having to patch a single line of the current MELT implementation
(the melt/warmelt*.melt files)
*****
** Can MELT load other code
Of course yes, using the same syntax (LOAD "somesource.melt") as most other lisps
Look at the code of mexpand_load near line 3830 of melt/warmelt-macro.melt to see
how it is implemented....
However, you may want to use several modules. LOAD is just a macro mechanism.
*****
** Does MELT have a read-eval-print-loop [REPL] able to do simple arithmetic.
In principle, yes. Use the repl mode, i.e. pass -fplugin-arg-melt-mode=repl to your gcc
and pass some empty C file to have cc1 started!
The REPL is reading a sequence of expressions ended by two -not one, but two- newlines.
Then, it is compiling that sequence to a C code, forking a make to build a *.so, dlopen-ing that, etc... Notice that the REPL wants a sequence of one or more s-expressions
ended by two newlines.
However, the arithmetic has not the same syntax as most Scheme or Lisp.
First, arithmetic operators have fixed arity. So addition requires two arguments.
Then, an important [mis-]feature in MELT is the essential value vs stuff distinction.
(I won't explain it here again, there are a lot of material explaning that already).
That distinction is mandated by the GCC compiler (and its memory management).
Recall that in MELT (contrarily to Scheme or Common Lisp) 1 is a stuff
-raw unboxed :long thing, exactly a long data in the generated C++ code-
and '1 is a value, a boxed integer of discriminant discr_constant_integer,
which you could get with (make_integerbox discr_constant_integer 1) and that
primitive may trigger the garbage collector since it is allocating a value...
Actually, you have several additions in MELT code, all are binary operations:
+i takes two :long stuff and gives a :long result; so (+i 2 3) is the :long stuff 5
+iv takes two values, and if both are boxed integers give the sum of their
boxed numbers as a boxed integer, else nil. So (+iv '2 '3) evaluate to
something like '5 but gives a value at a new address
+ivi takes a value and a raw :long stuff, e.g. (+ivi '2 3) and produced a boxed value
if the first argument is a boxed integer value, or else nil
So you cannot code (+ 1 2 3) in MELT, you need to code
(+i 1 (+i 2 3)) which gives a long stuff
or (+iv '1 (+iv '2 '3)) or (+iv '1 (+i 2 3)) which gives a boxed value.
****
* Evaluation mode
That question was not asked, but you can evaluate a single sequence of MELT
expressions, for example
gcc -c -fplugin=melt -fplugin-arg-melt-mode=eval \
-fplugin-arg-melt-arg="(parent_module_environment)" \
empty.c
I invite you to type that command, you'll understand more what MELT environments are.
You could even try with "(current_module_environment_container)"
In practice, adding a lot of (DEBUG ....) expressions in your MELT code don't hurt.
Use the translatequickly or runfile modes...
****
* Extending MELT with your C or C++ code.
Of course that is possible. You want to read about CODE_CHUNK CHEADER and USE_PACKAGE_FROM_PKG_CONFIG -and perhaps DEFPRIMITIVE DEFCMATCHER
DEFCITERATOR etc... macros in
http://gcc-melt.org/meltpluginapi.html
Notice that USE_PACKAGE_FROM_PKG_CONFIG enables you to use any package
known by pkg-config and that is very handy, e.g. libpcre for
Perl compatible regular expressions. Adding a *.pc file for software not
packaged yet by pkg-config (like MySQL) is quite simple.
****
* embedding the REPL inside a GUI
This is also in my TODO list (for the probe or something better). But
you could also consider using REGISTER_INPUT_CHANNEL_HANDLER if you have
your own textual asynchronous protocol between some of your GUI interface
and MELT. Look inside xtramelt-probe.melt for an example.
Hope this helps.
Cheers
--
Basile STARYNKEVITCH
http://starynkevitch.net/Basile/
email: basile<at>starynkevitch<dot>net mobile:
+33 6 8501 2359
8, rue de la Faiencerie, 92340 Bourg La Reine, France
*** opinions {are only mines, sont seulement les miennes} ***