llvm C++ api
Nope. Those words don’t mean what you seem to imply. The “llvm C++ API” is not available from inside a program that you compile for, say, an Arduino. See below the line at the end of this answer for details. If you actually had LLVM C++ API in mind, you wouldn’t be asking this question since register access is not much different from any other constant pointer dereferencing, and requires no special treatment.
First note that Arduino is a concept, not any particular CPU. You need to refer at least to the particular Arduino model, eg. UNO, but it’s better to speak of a particular architecture like AVR.
Assuming that that’s what you want: There are no “ports” on AVR, so you don’t need to do anything special. Just write to memory with a given fixed address - both C and C++ support it on all sensible platforms.
The peripheral and CPU control registers are memory-mapped. So suppose you have a byte-wide control register at address 0x1234. You might define it as:
#include <stdint.h>
...
#define CREG (*(volatile uint8_t*)0x1234)
Then, a statement CREG = 42;
will write the value 42 to that register at address 0x1234. That’s all there’s to it. You’ll find register addresses in the documentation for the given MCU.
Of course this is only a first step, you’d probably want a higher level
API than this. I find it practical to use references instead of such evil macros. They provide more compile-time type safety. In C++:
constexpr volatile uint8_t &CREG = *(volatile uint8_t)0x1234;
...
CREG = 42;
Read-only registers are also possible:
constexpr volatile const char &RXD0 = *(volatile const char)0x5678;
RXD0 = 3; // won’t compile
char c = RXD0; // will compile OK
Sometimes you need to do a multi-step operation to access a register, eg. set some address register first, and then load it indirectly via an access register. Accessor functions do a good job:
enum class AREG_ : uint8_t { QREG = ... };
constexpr volatile AREG_ &AREG = *(volatile AREG_*)0x3456;
inline void setQREG(uint8_t val) { AREG = AREG_::QREG; BREG = val; }
LLVM API is used by tools, that is by software that use LLVM to analyze code or generate code. You can’t use LLVM API from an arbitrary program you’re compiling using LLVM, just as you can’t use gcc API from within the Arduino IDE targeting AVR, which uses avrg++ to compile for the AVR targets.
LLVM IR is what the front-ends produce, and you can’t embed IR within C nor C++, so I don’t know why you asked about that either.
I still have no idea what you are trying to do. Can you explain in simple words? Do you just want to compile an arbitrary Arduino program using clang? Your question is especially confusing since the words you use have clear technical meaning but you use them in a way that taken together makes no sense.