Menu

Adding a port

Maarten Brock Philipp Klaus Krause Benedikt Freisen

Adding a port to SDCC

Is SDCC the right choice?

  • GCC has a reputation for a very steep learning curve, and a low maintenance effort thereafter.
  • LLVM has a reputation for being easier to get started, but higher maintenance effort due to changing internal interfaces.
  • SDCC is easy to get started, and has low maintenance effort.
  • GCC and LLVM have better high-level optimizations, and do have support for vectorization.
  • The register allocators in GCC and LLVM work best for architectures with many interchangeable registers.
  • The (new one - used by most ports) register allocator in SDCC can deal with highly irregular architectures, and can create efficient code even when there are only few registers. So far, it has never been used for architectures with more than 9 bytes of registers (counting registers available for storing variables only - there can be additional special-purpose registers, such as stack pointer, program counter, a flag register, or an index register that is used as frame pointer only).

Acceptance criteria for a new port

  • Its dependencies (e.g. assembler, linker, simulator) are free software.
  • The target is reasonably documented.
  • An SDCC developer is willing to maintain it (this could be you becoming an SDCC developer).
  • Passes regression tests.
  • Written in mandated language dialects and coding style.
  • Passes a code review by SDCC developers.

Necessary / recommended / typical parts of a new port

  • The port name should be short, descriptive of the target architecture, and a valid identifier in at least C, C++ and Python.
  • Assembler and linker. This could be done in the asxxxx fork that comes with SDCC, or via external tools.
  • Simulator. This could be done in the uCsim that comes with SDCC, or via an external simulator.
  • Port infrastructure. Mostly sdcc/src/< portname >/main.c.
  • Interface with register and stack allocation. sdcc/src/< portname >/ralloc.h, sdcc/src/< portname >/ralloc.c and sdcc/src/< portname >/ralloc2.cc.
  • Code generation. sdcc/src/< portname >/gen.h and sdcc/src/< portname >/gen.c.
  • Library. sdcc/device/lib/< portname >. It is a good idea to start with a library that contains as little assembler as possible (have a look at the noasm2 branch for an example).
  • Peephole optimizer interface. sdcc/src/< portname >/peep.h and sdcc/src/< portname >/peep.c.
  • Peephole optimizer rules. Typically sdcc/src/< portname >/peeph.def. Don't put too much effort into this, especially not initially. The peephole optimizer should be used only for optimizations that can't be done efficiently in earlier stages. Also, peephole optimizer rules are easy to write, so users tend to contribute what they want via patches anyway.

A full new port vs. a variant of an existing one?

  • If the architecture is a variant of an architecture already supported by SDCC, it will likely make sense to have the new port share most of the code with an existing port. E.g. the s08 port shares most of the code with the hc08 port, the z180 port shares most of the code with the z80 port.

Existing ports make good examples

  • For architectures with efficient stackpointer-relative addressing modes, and a uniform address space, have a look at stm8.
  • For architectures with an index register suitable for use as frame pointer and a mostly uniform address space, look at z80.
  • For architectures that lack both, have general-purpose registers, and a highly non-uniform address space, look at mcs51.
  • For architectures that lack both, and have few registers, and a somewhat non-uniform address space, look at pdk15.
  • Look at other ports, if you think that their architecture has something important in common with yours.
  • Don't look at avr. It is an abandoned port that never was completed and is quite outdated by now.
  • Be very cautious, in case you decide to look at pic14 or pic16. Those ports are currently unmaintained, and don't pass regression tests.

General advice

  • Make it work first, optimize later: Get the new port to compile some minimal example first, then make it compile the regression tests, then pass them. It is fine to have incomplete codegen at first, and then add the missing bits as necessary to pass regression tests.
  • Optimize early instead of late: What can be done efficiently by register allocation or code generation should be done there, not in the peephole optimizer.
  • Don't use more pseudo-registers than necessary.

The details / how-to

Choose a name for your port. Usually, this is the name of the targeted architecture (let's call it "newarch" for now).

If you are an SDCC developer, you'll probably want to do the following work in a branch ([HOWTO Create a SDCC Branch]) in svn.

For the assembler:

TODO - write this

For the simulator:

TODO -write this

For the compiler:

  • If your port is a full new port:
    create src/newarch, and in there main.c, gen.c, gen.h ralloc.h, ralloc.c, ralloc2.cc.
  • If your port is a variant of an existing one (let's call that one "oldarch" for now):
    in src/oldarch/main.c, add a PORT newarch_port.

  • Make the new port known in configure.ac, Makefile.in, src/port.h, src/SDCCmain.c. Run autoconf to ensure configure knows about the new port.

For the library:

  • Create device/lib/newarch
  • Make it known in configure.ac, device/lib/Makefile.in, device/lib/clean.mk. Run autoconf to ensure configure knows about the new libary port.

For regression testing:

  • Create support/regression/ports/newarch.

Related

Wiki: HOWTO Create a SDCC Branch
Wiki: Home

MongoDB Logo MongoDB