llvmc for PIC16

PIC16 now has clang and llc based system to generate native assembly. We then use our native assembler (gpasm) and the native linker (mplink) to generate the final executable. How can I integrate these things with the driver llvmc to have gcc like user experience? Note that we also want to run llvm-ld in order to perform the LTOs in case of multiple files.

- Sanjiv

Hi Sanjiv,

Sanjiv Gupta <sanjiv.gupta <at> microchip.com> writes:

PIC16 now has clang and llc based system to generate native assembly. We
then use our native assembler (gpasm) and the native linker (mplink) to
generate the final executable. How can I integrate these things with
the driver llvmc to have gcc like user experience? Note that we also
want to run llvm-ld in order to perform the LTOs in case of multiple files.

- Sanjiv

You can start with writing a separate llvmc-based driver, say, llvmc-pic16, that
uses your custom toolchain. The documentation and examples (especially
examples/Skeleton) should get you started. Once you get this working, I can
integrate your changes into mainline llvmc.

I'll be happy to answer any further questions you may have, feel free to e-mail
me directly (though right now our mail server is down).

I'll be happy to answer any further questions you may have, feel free to e-mail
me directly (though right now our mail server is down)
  

The salient features that we want to have in the driver are:
1. llvm-ld will be used as "The Optimizer".
2. If the user has specified to generate the final executable, then llvm-ld should run on all the .bc files generated by clang and create a single optimized .bc file for further tools.
3. -Wo <options> - pass optimizations to the llvm-ld
4. mcc16 -Wl <options> - pass options to native linker.
5. mcc16 -Wa <options> - pass options to native assembler.

Here are some example command lines and sample command invocations as to what should be done.

$ mcc16 -S foo.c
// [clang-cc foo.c] -> foo.bc
// [llvm-ld foo.bc] -> foo.opt.bc
// [llc foo.opt.bc] -> foo.s

$ mcc16 -S foo.c bar.c
// [clang-cc foo.c] -> foo.bc
// [llvm-ld foo.bc] -> foo.opt.bc
// [llc foo.opt.bc] -> foo.s
// [clang-cc bar.c] -> bar.bc
// [llvm-ld bar.bc] -> bar.opt.bc
// [llc bar.opt.bc] -> bar.s

** Use of -g causes llvm-ld to run with -disable-opt
$ mcc16 -S -g foo.c
// [clang-cc foo.c] -> foo.bc
// [llvm-ld -disable-opt foo.bc] -> foo.opt.bc
// [llc foo.opt.bc] -> foo.s

** -I is passed to clang-cc, -pre-RA-sched=list-burr to llc.
$ mcc16 -S -g -I ../include -pre-RA-sched=list-burr foo.c
// [clang-cc -I ../include foo.c] -> foo.bc
// [llvm-ld -disable-opt foo.bc] -> foo.opt.bc
// [llc -pre-RA-sched=list-burr foo.opt.bc] -> foo.s

** -Wo passes options to llvm-ld
$ mcc16 -Wo=opt1,opt2 -S -I ../include -pre-RA-sched=list-burr foo.c
// [clang-cc -I ../include foo.c] -> foo.bc
// [llvm-ld -opt1 -opt2 foo.bc] -> foo.opt.bc
// [llc -pre-RA-sched=list-burr foo.opt.bc] -> foo.s

** -Wa passes options to native as.
$ mcc16 -c foo.c -Wa=opt1
// [clang-cc foo.c] -> foo.bc
// [llvm-ld foo.bc] -> foo.opt.bc
// [llc foo.opt.bc] -> foo.s
// [native-as -opt1 foo.s] -> foo.o

$ mcc16 -Wo=opt1 -Wl=opt2 -Wa=opt3 foo.c bar.c
// [clang-cc foo.c] -> foo.bc
// [clang-cc bar.c] -> bar.bc
// [llvm-ld -opt1 foo.bc bar.bc] -> a.out.bc
// [llc a.out.bc] -> a.out.s
// [native-as -opt3 a.out.s] -> a.out.o
// [native-ld -opt2 a.out.o] -> a.out

Is this achievable by a tablegen based driver ?

- Sanjiv

Hi Sanjiv,

Sanjiv Gupta <sanjiv.gupta <at> microchip.com> writes:

The salient features that we want to have in the driver are:
[...]

Is this achievable by a tablegen based driver ?

Yes - in fact, I think I'll just write a basic mcc16 driver myself over this
weekend - it'll make a great addition to the examples and a nice starting point
for you to work on.

Hi Sanjiv,

Sanjiv Gupta <sanjiv.gupta <at> microchip.com> writes:

The salient features that we want to have in the driver are:
[...]

As promised, I've implemented a basic compiler driver for the
PIC16 toolchain. It's under tools/llvmc/examples/mcc16.

Some examples illustrating the features you requested:

2. If the user has specified to generate the final executable, then
llvm-ld should run on all the .bc files generated by clang and create a
single optimized .bc file for further tools.

$ mcc16 -dry-run foo.c bar.c
clang-cc foo.c -o /tmp/llvm_6ibgr9/foo.bc
clang-cc bar.c -o /tmp/llvm_6ibgr9/bar.bc
llvm-ld /tmp/llvm_6ibgr9/foo.bc /tmp/llvm_6ibgr9/bar.bc \
    -o /tmp/llvm_6ibgr9/tmp.bc
llc -f /tmp/llvm_6ibgr9/tmp.bc -o /tmp/llvm_6ibgr9/tmp.s
native-as /tmp/llvm_6ibgr9/tmp.s -o /tmp/llvm_6ibgr9/tmp.o
native-ld /tmp/llvm_6ibgr9/tmp.o -o a.out

3. -Wo <options> - pass optimizations to the llvm-ld
4. mcc16 -Wl <options> - pass options to native linker.
5. mcc16 -Wa <options> - pass options to native assembler.

$ mcc16 -dry-run -Wo,-opt1 -Wllc,-opt2 -Wa,-opt3 -Wl,-opt4 foo.c
clang-cc foo.c -o /tmp/llvm_92HLCj/foo.bc
llvm-ld -opt1 /tmp/llvm_92HLCj/foo.bc -o /tmp/llvm_92HLCj/tmp.bc
llc -opt2 -f /tmp/llvm_92HLCj/tmp.bc -o /tmp/llvm_92HLCj/tmp.s
native-as -opt3 /tmp/llvm_92HLCj/tmp.s -o /tmp/llvm_92HLCj/tmp.o
native-ld -opt4 /tmp/llvm_92HLCj/tmp.o -o a.out

$ mcc16 -S foo.c

$ mcc16 -dry-run -S foo.c
clang-cc foo.c -o /tmp/llvm_0uiDCR/foo.bc
llvm-ld /tmp/llvm_0uiDCR/foo.bc -o /tmp/llvm_0uiDCR/foo.bc
llc -f /tmp/llvm_0uiDCR/foo.bc -o foo.s

$ mcc16 -S foo.c bar.c

$ mcc16 -dry-run -S foo.c bar.c
clang-cc foo.c -o /tmp/llvm_1zAqik/foo.bc
llvm-ld /tmp/llvm_1zAqik/foo.bc -o /tmp/llvm_1zAqik/foo.bc
llc -f /tmp/llvm_1zAqik/foo.bc -o foo.s
clang-cc bar.c -o /tmp/llvm_1zAqik/bar.bc
llvm-ld /tmp/llvm_1zAqik/bar.bc -o /tmp/llvm_1zAqik/bar.bc
llc -f /tmp/llvm_1zAqik/bar.bc -o bar.s

** Use of -g causes llvm-ld to run with -disable-opt
$ mcc16 -S -g foo.c

$ mcc16 -dry-run -S -g foo.c
clang-cc foo.c -o /tmp/llvm_oQFmVn/foo.bc
llvm-ld -disable-opt /tmp/llvm_oQFmVn/foo.bc -o /tmp/llvm_oQFmVn/foo.bc
llc -f /tmp/llvm_oQFmVn/foo.bc -o foo.s

** -I is passed to clang-cc, -pre-RA-sched=list-burr to llc.
$ mcc16 -S -g -I ../include -pre-RA-sched=list-burr foo.c

$ mcc16 -dry-run -S -g -I ../include -pre-RA-sched=list-burr foo.c
clang-cc -I ../include foo.c -o /tmp/llvm_5VxNFQ/foo.bc
llvm-ld -disable-opt /tmp/llvm_5VxNFQ/foo.bc -o /tmp/llvm_5VxNFQ/foo.bc
llc -pre-RA-sched list-burr -f /tmp/llvm_5VxNFQ/foo.bc -o foo.s

This should be enough to get you started.

I'm always happy to answer any further questions you may have. If
you want to e-mail me privately, please use the.dead.shall.rise
<at_sign> gmail dot com for now (our provider problems weren't
resolved yet).

Thanks a lot.
This is a great help.

  • Sanjiv

Mikhail Glushenkov wrote:

Hi Sanjiv,

Sanjiv Gupta <sanjiv.gupta <at> microchip.com> writes:

> The salient features that we want to have in the driver are:
> [...]

As promised, I've implemented a basic compiler driver for the
PIC16 toolchain. It's under tools/llvmc/examples/mcc16.

Hi Mikhail,
How do you build mcc16 executable?
There are so many confusing things there: driver, plugins, example, Skelton etc.
The LLVMC-Tutorial doesn't clearly talk about them. Your help is appreciated.

Thanks,
Sanjiv

I think the correct command line is
$ make LLVMC_BUILTIN_PLUGINS=MyPlugin LLVMC_BASED_DRIVER_NAME=mydriver

rather than
$ make BUILTIN_PLUGINS=MyPlugin DRIVER_NAME=mydriver
(I found the later one in LLVMC-Reference)

- Sanjiv

Sanjiv Gupta wrote:

Hi Sanjiv,

Hi Mikhail,
How do you build mcc16 executable?

This should work:

$ cd $LLVM_DIR/tools/llvmc/examples/mcc16
$ make

If you're building from some other dir, you'll need to update
mcc16/Makefile, so it knows where Makefile.common is located.

There are so many confusing things there: driver, plugins, example, Skelton
etc.
The LLVMC-Tutorial doesn't clearly talk about them.

Yes, I know, the documentation doesn't talk about creating standalone
drivers. Sorry for that, I was planning to update it for ages...
For now, please look at examples/Skeleton, which is a standalone
driver skeleton, on which the mcc16 driver is based.

I think the correct command line is
$ make LLVMC_BUILTIN_PLUGINS=MyPlugin LLVMC_BASED_DRIVER_NAME=mydriver

rather than
$ make BUILTIN_PLUGINS=MyPlugin DRIVER_NAME=mydriver
(I found the later one in LLVMC-Reference)

This section is about building the "main" llvmc driver and its plugins.
Creation of standalone drivers is not described in the docs yet (it
was added relatively recently).

Mikhail Glushenkov wrote:

Hi Sanjiv,

Hi Mikhail,
How do you build mcc16 executable?
    
This should work:

$ cd $LLVM_DIR/tools/llvmc/examples/mcc16
$ make

I configure llvm into a separate directory from source.
When I do the steps you mentioned in the source directory, this is what I get.

[i00171@rhino mcc16]$ make
../../../../Makefile.common:61: ../../../../Makefile.config: No such file or directory
../../../../Makefile.common:69: /Makefile.rules: No such file or directory
make: *** No rule to make target `/Makefile.rules'. Stop.

Ditto for the "Simple" plugin.

[i00171@rhino mcc16]$ cd ../Simple/

../../../../Makefile.common:61: ../../../../Makefile.config: No such file or directory
../../../../Makefile.common:69: /Makefile.rules: No such file or directory
make: *** No rule to make target `/Makefile.rules'. Stop.
[i00171@rhino Simple]$

I copied the "Simple" plugin to the configured directory

$ cd objs/tools/llvmc/plugins
$ cp -rf $LLVM_SRC/tools/llvmc/example/Simple MyPlugin

Changed the plugin name in the Makefile from "Simple" to "MyPlugin", and moved "Simple.td" to "MyPlugin.td"

$ make LLVMC_BUILTIN_PLUGINS=MyPlugin LLVMC_BASED_DRIVER_NAME=mydriver

That created "mydriver" executable for me.

- Sanjiv

Hi Sanjiv,

There is another way for building mcc16 in tree indepandent of sources

Change DIRS = plugins driver
to DIRS = plugins driverexample/mcc16

in tools/llmc/Makefile works for me

mcc16 is then generated directly from the llvm main makefile.

Hi Mikhail,
Thanks for your wonderful help so far. I have few more questions to ask:

How do I modify the driver to pick tools from where the driver itself resides, rather than from the PATH?
And how to make sure that we have same behavior on Windows as far as paths (/ Vs \) and picking up tools from the driver directory is concerned?
Do I need to write some C++ code to customize such behaviors?

Thanks,
Sanjiv

Mikhail Glushenkov wrote:

Hi Sanjiv,

Hi Mikhail,
Thanks for your wonderful help so far. I have few more questions to ask:

How do I modify the driver to pick tools from where the driver itself
resides, rather than from the PATH?
Do I need to write some C++ code to customize such behaviors?

Yes, this is what hooks are for. You're supposed to change the
cmd_line property to something like this:

(cmd_line "$CALL(PrependCustomizedPath, 'toolname') --tool --options")

The PrependCustomizedPath function above is implemented in C++ (just
drop a .cpp file into the plugin directory).

And how to make sure that we have same behavior on Windows as far as paths
(/ Vs \) and picking up tools from the driver directory is concerned?

Right now, you can't do OS detection in TableGen code, so you should
use hooks for platform-specific things.

Also, do not hesitate to mail me or file a bug if you notice some
warts in Windows support. One known issue is that DLL plugins do not
work. I test on both Linux and Windows, but develop mainly on Linux.

BTW, Chris's Makefile changes broke llvmc yesterday (r75379). I'm
working on a fix.

BTW, Chris’s Makefile changes broke llvmc yesterday (r75379). I’m
working on a fix.

Hi Mikhail,
Did you get a chance to fix this. I still get errors while building examples.

i00202@ubuntu:~/projects/llvm/tools/llvmc/example/mcc16$ make
make[1]: Entering directory /home/i00202/projects/llvm/tools/llvmc/example/mcc16/plugins' make[2]: Entering directory /home/i00202/projects/llvm/tools/llvmc/example/mcc16/plugins/PIC16Base’
make[2]: Nothing to be done for all'. make[2]: Leaving directory /home/i00202/projects/llvm/tools/llvmc/example/mcc16/plugins/PIC16Base’
make[1]: Leaving directory /home/i00202/projects/llvm/tools/llvmc/example/mcc16/plugins' make[1]: Entering directory /home/i00202/projects/llvm/tools/llvmc/example/mcc16/driver’
make[1]: *** No rule to make target /home/i00202/projects/llvm/Debug/lib/plugin_llvmc_PIC16Base.o', needed by /home/i00202/projects/llvm/Debug/bin/mcc16’. Stop.
make[1]: Leaving directory `/home/i00202/projects/llvm/tools/llvmc/example/mcc16/driver’
make: *** [all] Error 1

This is with srcdir = objdir. I get the same error even when srcdir and objdir are separate.

Thanks,
Sanjiv

Hi Sanjiv,

Hi Sanjiv,

Mikhail Glushenkov wrote:

Hi Sanjiv,

BTW, Chris's Makefile changes broke llvmc yesterday (r75379). I'm
working on a fix.
      

Hi Mikhail,
Did you get a chance to fix this. I still get errors while building examples.

This issue should be fixed now (r74001+).

Yes. It works now.
I have a few more things to ask.
In a command line such as below:

clang-cc -triple=pic16- -emit-llvm-bc hello.c -o /tmp/llvm_YdmvZG/hello.bc

who is suppose to create TmpDir /tmp/llvm_YdmvZG/ ?
I see neither the driver nor clang-cc creates it.

How to change TmpDir? For example I may want to create a Debug or Release dir under the current dir to create binaries, then how to say that ./Debug or ./Release should be used by the driver as TmpDir ?

- Sanjiv

Hi Sanjiv,

I have a few more things to ask.
In a command line such as below:

clang-cc -triple=pic16- -emit-llvm-bc hello.c -o /tmp/llvm_YdmvZG/hello.bc

who is suppose to create TmpDir /tmp/llvm_YdmvZG/ ?
I see neither the driver nor clang-cc creates it.

The temporary directory is created by the driver automatically, the
same as with gcc (see include/llvm/CompilerDriver/Main.inc, if you're
interested in details).

How to change TmpDir? For example I may want to create a Debug or Release
dir under the current dir to create binaries, then how to say that ./Debug
or ./Release should be used by the driver as TmpDir ?

I think that this should be the responsibility of the build system.
Example (Makefile syntax):

$(BuildMode)/%.o : %.c
    llvmc -c $SOURCE -o $(BuildMode)/$TARGET

$(BuildMode)/$(ProgramName) : $(ObjectFiles)
    llvmc $(ObjectFiles) -o $(BuildMode)/$(ProgramName)

This is basically what LLVM's build system does.

There is also the -save-temps option which was recently modified to
behave like GCC 4.5. You can now do:

llvmc -save-temps=obj -o MyTempDir MyFile.c

This will produce an executable named a.out in the current directory
and save all temporary files to MyTempDir.

Mikhail Glushenkov wrote:

Hi Sanjiv,

Hi Mikhail,
Thanks for your wonderful help so far. I have few more questions to ask:

How do I modify the driver to pick tools from where the driver itself
resides, rather than from the PATH?
Do I need to write some C++ code to customize such behaviors?
    
Yes, this is what hooks are for. You're supposed to change the
cmd_line property to something like this:

(cmd_line "$CALL(PrependCustomizedPath, 'toolname') --tool --options")

The PrependCustomizedPath function above is implemented in C++ (just
drop a .cpp file into the plugin directory).

Hi Mikhail,

I want to retrieve the value of argv[0] (which was passed to main function of the driver) in PrependCustomizedPath.
Do we have some standard name for argv[0] in tablegen?

- Sanjiv