On 28/06/2023 10:35, Rafał Pietrak via Gcc wrote:
Hi Jonathan,

W dniu 28.06.2023 o 09:31, Jonathan Wakely pisze:



If you use a C++ library type for your pointers the syntax above doesn't need to change, and the fancy pointer type can be implemented portable, with customisation for targets where you could use 16 bits for the pointers.

As you can expect from the problem I've stated - I don't know C++, so I'll need some more advice there.

But, before I dive into learning C++ (forgive the naive question).... isn't it so, that C++ comes with a heavy runtime? One that will bloat my tiny project? Or the bloat comes only when one uses particular elaborated class/inheritance scenarios, and this particular case ( for (...; ...; x = x->next) {} ) will not draw any of that into this project?



Let me make a few points (in no particular order) :

1. For some RISC targets, such as PowerPC, it is common to have a section of memory called the "small data section". One of the registers is dedicated as an anchor to this section, and data within it is addressed as Rx + 16-bit offset. But this is primarily for data at fixed (statically allocated) addresses, since reads and writes using this address mode are smaller and faster than full 32-bit addresses. Normal pointers are still 32-bit. It also requires a dedicated register - not a big cost when you have 31 GPRs, but much more costly when you have only 13.

2. C++ is only costly if you use costly features. On small embedded systems, you want "-fno-exceptions -fno-rtti", and you will get as good (or bad!) results for C++ as for C. Many standard library features will, however, result in a great deal of code - it is usually fairly obvious which classes and functions are appropriate.

3. In C, you could make a type such as :

        typedef struct {
                uint16_t p;
        } small_pointer_t;

and conversion functions :

        static const uintptr_t ram_base = 0x20000000;

        static inline void * sp_to_voidp(small_pointer_t sp) {
                return (void *)(ram_base + sp);
        }

        static inline small_pointer_t voidp_to_sp(void * p) {
                small_pointer_t sp;
                sp.p = (uintptr_t) p - ram_base;
                return sp;
        }

Then you would use these access functions to turn your "small pointers" into normal pointers. The source code would become significantly harder to read and write, and less type-safe, but could be quite efficient.

In C++, you'd use the same kinds of functions. But they would now be methods in a class template, and tied to overloaded operators and/or conversion functions. The result would be type-safe and let you continue to use a normal pointer-like syntax, and with equally efficient generated code. You could also equally conveniently have small pointers to ram and to peripheral groups. This mailing list is not really the place to work through an implementation of such class templates - but it certainly could be done.


4. It is worth taking a step back, and thinking about how you would like to use these pointers. It is likely that you would be better thinking in terms of an array, rather than pointers - after all, you don't want to be using dynamically allocated memory here if you can avoid it, and certainly not generic malloc(). If you can use an array, then your index type can be as small as you like - maybe uint8_t is enough.


David



Reply via email to