Adding Intrinsics for custom processor (frotend problem)

As mentioned in an earlier mail, I am working on an LLVM backend for a custom processor based on MIPS.
My problem is how to extend LLVM to support some esoteric/non-standard feature of the underlying hardware.
I saw in the "Extending LLVM" document, that the easiest / most common way to do this was using an intrinsic function,
and examples of the technique abound (all SSE for example).
My problem though as mentioned before is that the frontend support for intrinsics, is undocumented, and implemented
in llvm-gcc which I prefer not to hack with (I use a binary precompiled one). As far as I could see it supports the
intrinsics using gcc style __builtin_ function names.

Currently what I think to do , is the following :

1. Define my intrinsics in a special (which would be preincluded in our build system) .h file (using whatever name convention I want)
2. Then I need to add a pass to llvm which will translate the names of those intrinsics to llvm style names , since
   LLVM intrinsics start with "llvm." which makes them non legal as "C" identifiers.

I have two questions :

1. Is that the right way to go, or is there a simpler/more standard way to have frontend support for new architecture intrinsics ?

2. If thats the simplest way , I would like to have a quick advice on how to write the rather trivial pass described in '2'.
   I guess that I need to replace a given Function object with an allmost identical one, but with a different name (the "llvm.something' name
  of the intrisic) What is the correct code to do this ?

Personally I would just modify the frontend - it's not that hard once you know where to look . In this case all you need to do is add the declaration of your builtin to the TARGET_INIT_BUILTINS target hook (see the gcc internals manual) defined in the gcc/config directory corresponding to your target. If the name the the declaration matches the name used with GCCBuiltin in the intrinsics td file then any calls to the gcc builtin in the source should get automatically translated to the calls to the LLVM intrinsic.

Gil Dogon wrote:

Gil Dogon wrote:

I have two questions :

1. Is that the right way to go, or is there a simpler/more standard way to have frontend support for new architecture intrinsics ?

Gil, it's really easy to add builtins to the llvm-gcc frontend and have them auto-magically converted to the llvm intrinsic versions. The main problem with this is that you end up with a non-standard front end that you have to somehow distribute to the people who need it. There may be funny licensing problems, etc... I don't really know.

Here's a quick how-to based on adding an intrinsic which we want to look like:

   void* llvm.my.foo(void**, void*)

1) Get the llvm-gcc source from svn.

2) Edit the file gcc/builtins.def

        The comments at the top are helpful, read them.

    a) Add a macro that looks something like

#undef DEF_MY_BUILTIN
#define DEF_MY_BUILTIN(ENUM, NAME, TYPE, ATTRS) \
   DEF_BUILTIN (ENUM, "__builtin_" NAME, BUILT_IN_MD, TYPE, TYPE, \
                false, false, true, ATTRS, false, true)

        You can figure out what this means based on the comments in the
        builtins.def file. Essentially the important magic is the
        BUILT_IN_MD flag trickles through and gets your llvm intrinsic
        generated automatically. Also, all of your builtins will be
        prefixed with __builtin_ --- you could change this if you wanted.

    b) Add an include directive at the bottom of the file,
       "#include my-builtins.def"

        This isn't strictly necessary, you could just add your builtins
        directly to the builtins.def file, but it helps keep them
        separate.

3) Edit the gcc/Makefile.in file (not necessary if you skipped b above)

    a) Find the BUILTINS_DEF line and add your "my-builtins.def" file.

4) Create and edit gcc/my-builtins.def

        The file gcc/builtin-types.def lists the macros you can use to
        declare the function types for your macros, and
        gcc/builtin-attrs.def lists the attributes, though I've only ever
        used ATTR_NOTHROW_LIST.

    a) Use the macro you defined earlier to generate your builtins, in
       our example case it is

DEF_MY_BUILTIN (BUILT_IN_MY_FOO, "my_foo", \
    BT_FN_PTR_PTRPTR_PTR, ATTR_NOTHROW_LIST)

        This is straightforward, the first parameter is an ID for your
        builtin within gcc -- it just needs to be unique. The second is
        the "name" of your builtin, which will be prepended with
        "__builtin_" based on our definition of DEF_MY_BUILTIN. The third
        is the function type from gcc/builtin-types.def, (here a function
        taking a pointer to a pointer, and a pointer, and returning a
        pointer). The fourth is the function attributes which I don't
        really know anything about.

    b) In our case here, the correct function type didn't exist, so an
       additional step is to edit gcc/builtin-types.def. I would add the
       following in the section where the 2-parameter types are defined.

DEF_FUNCTION_TYPE_2 (BT_FN_PTR_PTRPTR_PTR, BT_PTR, BT_PTR_PTR, BT_PTR)

       That's all you need to change within llvm-gcc (I think, I didn't
       bug-test the instructions, but I'm pretty sure it will work.

5) Get llvm source from svn

6) Edit include/llvm/Intrinsics.td

       You can either add your intrinsics directly, or just add an
       include to your own file. Here I'm adding them directly.

    a) Add a definition of your intrinsic. In our case, it looks like

def int_my_foo : Intrinsic<[llvm_ptr_ty],
                            [llvm_ptrptr_ty, llvm_ptr_ty]>,
                  GCCBuiltin<"__builtin_my_foo">;

       Clearly the fun here is the GCCBuiltin tag which is what gets
       everything to /just work/.

7) Build llvm.

8) Configure llvm-gcc with your modified llvm binary directory as it's
    llvm directory.

9) Build llvm-gcc.

10) Configure llvm with the llvm-gcc directory.

11) Rebuild llvm.

And voila, users can now use your gcc builtin as
"void* addr = __builtin_my_foo(&bar, bar)" and compilation will naturally generate a llvm.my.foo for it.

One caveat is that I've never actually done this with parameters that have address types, but ints, voids, and longs work fine. There's a routine in llvm-gcc inside of "gcc/llvm-convert.cpp" called TreeToLLVM::EmitBuiltinCall that does the actual conversion, you may need to do some work in that area. My guess is that it will /just work/ though.

Hope this helps. There may be other ways to do this using gcc asm tags and such, but I know that this works and gets parameters in the right place, etc.

Let me know if there are any bugs in the instructions.

Luke

Richard Osborne wrote:

Personally I would just modify the frontend - it's not that hard once you know where to look . In this case all you need to do is add the declaration of your builtin to the TARGET_INIT_BUILTINS target hook (see the gcc internals manual) defined in the gcc/config directory corresponding to your target. If the name the the declaration matches the name used with GCCBuiltin in the intrinsics td file then any calls to the gcc builtin in the source should get automatically translated to the calls to the LLVM intrinsic.

Gil, this sounds like a much easier strategy than mine for your target-specific approach. The builtins that I added for my own purpose aren't target specific, they're more along the lines of the OpenMP stuff so I was looking for a general place to put them.

Luke

As others have mentioned, hacking the front-end isn't that hard. In any case though, please be aware that a precompiled binary for a non-MIPS target won't produce correct code if you are (for example) using an x86 front-end and forcing llc to generate mips code with llc -march=mips. The front-end does type layout and knows very ABI things that have to be right for mips.

-Chris