[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [avr-gcc-list] using exceptions

From: Georg-Johann Lay
Subject: Re: [avr-gcc-list] using exceptions
Date: Sun, 06 May 2012 17:13:39 +0200
User-agent: Thunderbird (Windows/20100228)

Gabriel Dos Reis schrieb:
Georg-Johann Lay wrote:
Gabriel Dos Reis schrieb:
Georg-Johann Lay wrote:

Gabriel Dos Reis wrote:

I guess I would need to get my feet wet with
1. minimal free-standing  C++ implementations without exceptions
2. get support for RTTI (without exceptions)
3. exceptions (assuming 1 and 2 are done successfully)

Regarding 2., I would like to understand the general policy:
RTTI data are generally const and should be best stored in read
only location.
What is the general preference for AVR?
Put read only data in program memory ordata segment?
Apologies for the triviality but the recent discussion about
__func__ got me thinking.

The preferred place is program memory, i.e. flash, i.e. something like
.progmem section.

Read-only data is located in .rodata which is located in RAM.

This is because const qualifier is not enough to put data in flash,
just assume:

char bar (const char *p)
 return *p;

char foo (char *p)
 char c = bar (p);
 *p = 0;
 return c;

bar() must not read from flash.

Yes, "const" qualifier on *parameters* is not enough.
However "const" qualifier on *objects* should be enough.
(and for C++, we now have "constexpr" to statically
guarantee that)

That's not enough.

The reason is that it's completely legal in C to access a
non-const object through a const-qualified pointer as
lined out in bar being called by foo above.

I think this is okay for C++, too?

In C++, you can also access a non-const object with
a const qualified pointer.  But, that is not what I was
suggesting.  I am suggesting that a const(expr) *object* should
go in program memory because it cannot be modified.
The only requirement is that it is statically initialized
(e.g. at most at link time.)

As far as I understand, there is no way to detect this in the BE,
see PR50807:

const char __attribute__((progmem)) var = "Hallo"[0];

DECL_INITIAL is NULL and needs_constructing (don't remember the
exact name) is false because there is no constructor. Yet there
is an artificial constructor trying to write to the object after
load time, i.e. at run time, leading to wrong code without giving the compiler the opportunity to give a diagnose.

To put an object in flash only one thing is needed:

A hook that passes respective DECL to the backend, and the BE
returns an address space that is used to qualify *every*
pointer involved with that object.

Of course, the backend must be supplied with *all* relevant info:

1) Is this a user-object or an artificial object generated by the compiler?
2) Are there any attributes like section attributes?
3) The returned AS must be used in the sense of address_mode (to
locate the object) and pointer_mode (to access it).
4) No lying about constructors, run-time initializer; be they artificial
or not
5) After specifying the AS and changing the DECL accordingly, the
DECL must be passed through hooks like TARGET_ENCODE_SECTION_INFO
that decide about placement.
6) Info about:
- Are *all* accesses known because they are seen explicitly?
- Are *all* accesses known because of assumptions? Like, e.g.,
that other modules behave the same because the same ABI is assumed?
- Flag if some accesses might escape the scope.
7) The same hook interface can be used. There is no difference
between C++ or C or whatever language. It' only about the information

The only time one can't put a const object in program memory
is when it requires a dynamic initialization. Constexpr objects
do not require dynamic initialization; the same is true for
statically initialized const object.

For code like the following

void f ()
   struct S s = { 0, 0, 0, ... };

the compiler sometimes uses a static initializer and memcpy/memset
and sometimes initialized it at runtime depending on costs like
MOVE_RATIO etc. These costs are not sensitive to address spaces.

Moreover, respective optimization passes must honor backend decisions
- don't use static initializer with memcpy/memset here
- always use static initializer
- don't care and go for optimization

Please note that in no place there is need for distinction between
different languages, and such distinction between C/C++ is not wanted
in a BE neither should be needed.

The only safe setting where an object O may be put into
flash without the user explicitely requesting it are:

A) *All* use sites of the object are known and they
never change O.

B) There is no section attribute attached to O.

See above for my elaboration.
There is no way a use sit can modify a const object.
So if we know the object is const qualified, we do not
need to check all use sites.  The attribute section is
indeed a good point.  But, I think we shouldn't require
users to explicitly say so -- this goes back to my earlier
question about "preference".

Besides that, we need

C) The compiler supplies us with this knowledge *and*
allows us to hook in in oder to produce appropriate
code to locate the object and access it appropriately,
e.g. to quality *all* involved pointers targeting the
object appropriately.

Notice I use "object" in the weak meaning as "something",
i.e. "object" may also be a string literal or vtable.

Yes, string literals and vtable should definitely be accounted
as object in this discussion.

A, B and C are currently only satisfied for one single
place in avr-gcc, namely casesi insn:

A is true because because the BE emits code for casesi
and is able to locate the table, see TARGET_ASM_FUNCTION_RODATA_SECTION.
This might break C++ if it uses .gnu.linkonce.r. in
unknown ways.

So, we would have to convince the C++ FE  about AVR needs.

There are several other places that satisfy A and B, but
C does not hold:

i) Lookp tables resulting from tree-switch-conversion, see PR49857.
ii) vtables
iii) rtti tables

The difference between i) and ii), iii) is that putting tables in
flash for i) is not an ABI change whereas ii) and iii) are:

Suppose you compile a file that generates respective tables and link
against objects generated by an older compiler version.

In that case the assertion would be that the use site may escape the
scope, but the out-of-scope places are just as well administered by
compiler code and not by user code. Thus, the assertion is that the
handling of the compiler is the same over all modules involved, which
means a new ABI. Except v/rtti-tables are only present in exactly
one module and only accessed from there, of course. I don't new enough
of C++ for this, but in that case the change would just be an
optimization and not an ABI change, similar to i) and casesi.

There is exactly one vtable per class definition.  The real issue is
which translation unit should contain that definition if virtual
function definitions are spread across several translation units?
The common vendor ABI says the translation unit that contains the
keying function.  The exact definition for the latter is ABI-specific.
Where can I find the AVR C++ ABI?

Good question. Likely this is worth a separate thred/question there
and/or in gcc@ or directly to the respective GCC/binutils/LibC

As for RTTI objects, the ideal is that there exists exactly one type
info object per type.  However, some platforms (e.g. mac os) have
visibility attributes that can muddy the water.  Consequently, some
fake platforms uniqueness; I am not sure that is good for AVR. So, I would expect RTTI objects to follow the same treatment as vtables.

From the compiler's analysis capabilities, I'd guess A is satisfied
for way more cases, e.g. static consts. The compiler knows if
references escape scope, for example by taking address of such object
and return or pass it.


The issue is not locating the data, it's accessing the data with the
right instructions, i.e. LPM* for data in flash and LD* and ST* for
data in RAM.

yes; I agree.  My question though was whether there was a general
policy about where to put things we know are never going to change
in program memory because of RAM being so scarce.

Similar issues as with rtti arise with vtables, see PR43745.

A, B and C mentioned above.

GCC offers TARGET_VTABLE_USES_DESCRIPTORS, but it appears to be
very specific, and the vtable hooks don't appear to be generic
enough as to tell "use this kind of pointer to access that".

If we are able to formulate exactly what we want, I believe we can
bring this to Jason.

FYI, notice that avr does not implement TARGET_PTRMEMFUNC_VBIT_LOCATION
and instead sets FUNCTION_BOUNDARY to 8. The last setting is
confusing if you miss TARGET_PTRMEMFUNC_VBIT_LOCATION is undefined
because the right setting for FUNCTION_BOUNDARY is 16.

Ugh.  Thanks!

RAM is a very scarce resource on AVRs, but with current GCC it is not
possible to place+access rtti/vtable data in flash.

The right feature would be something like an internal, "artificial"
address space to qualify/locate respective pointers/data.
This should be easier than adding named address spaces as
full-featured C++ extension.

Agreed.  I will talk to Jason to see what can be done here.

As far as I understand, this is typically worked out as
co-implementation of new front end features/hooks/capabilities
and backend implementation that use these.  Often, it's the same
developer who cares for all spots, but it's not es easy if the
implementation requires knowledge beyond the expertise of
developers. Me for example don't know anything about C++ and the
details of tree representations, implementations and assertions.

But at lest the agenda is clear:

1) Enable the whatever desired C++ parts
2) Implement optimizations

and fix problems as they come up, add extensions as they are needed
and welcome and can be worked out even with the notorious developer

IMO, the implementation of address spaces in GCC is bit myopic.
AS are just a kind of target specific qualifiers. While AS
qualifiers are mutually exclusive, "ordinary" qualifiers are not.

I wonder why there is nothing like target specific qualifiers in GCC
where the target decised what compination of whalifiers is legal or
worth an error or warning and how they transform with pointer mess.

One reason might be limited space for TYPE_QUALS? Dunno...

It is more history than specific well-though out design decision.

However, I'd guess that none of the code to handle address spaces is
present in C++ part and/or FE as named addresses are not a G++ feature,
so there is no need to have named address code in C++ (or other
languages' parts) in the compiler.

Agreed.  I would like to refrain from full fledged named address spaces
in the C++ front-end until the WG21 committee got a clear direction.
On the other hand something like the one you describes above should suffice.

You got contact to the WG?

yes,  I am on WG21. I generally used to represent France (AFNOR) on WG21.
(Some of my work on WG21 include the introduction of constexpr,
template aliases, new meaning of "auto", etc. and failed "C++0x
concepts" attempt)

IMO there are some missing spots in the AS specification, for example in
code like

const __flash char *str = "literal"

This is not allowed in current spec as C/C++ standard is too dominant,
there is a "string literals are always generic" or similar and the
AS spec does not cover it.

Agreed.  In general, there is an aversion towards more qualifiers.
I suspect this is mostly because of the committee being burned
by "volatile" and also because qualifiers tend to lead to combinatorial
explosion when it comes to implicit conversions and overload resolution.

volatile is a very special beast, yes.
I wonder what C/C++ would be without volatile...

Address spaces are mutually exclusive, so there is not that much
or complexity.

The trend has been to use classes (sometimes with compiler
support) to render abstractions such as these.  See for example
how C++11 handles atomics (std::atomic<T>) compared to C11 (_Atomic).

In the example above, the /only/ way to access the literal is through
str which points to flash. Thus, the canonical and only reasonable
place to put the literal is __flash address space.

This has been overseen by the WG, obviously the spec was written for
some different architecture in mind.

Yes, the current definition clearly does not have multiple data pointers
kinds in mind -- it does however say that a pointer to function is a different
beast from a pointer to data.

I think the "C++ way" would be to have a class type flash<T> (like
atomic<T>) that is used to encapsulate the address space.  Everything
else should work out properly.

Attaching obvious semantics to the line above or making it
implementation defined and let the backend decide would make
it much easier to port code

const char *str = "literal";

from big platform to

const __flash char *str = "literal";

Instead of rewriting to

const __flash char dummy[] = "literal";
const __flash char *str = dummy;

how about

   std::flash<char[]> str = "literal";


hmmm. would this be analogon of

    const __flash char str[] = "literal";

If so, it's different to the above

    const __flash char *str = "literal";

I don't understand C++ and it's sophisticated features, syntaxes,
semantics and specifications as the understand the difference
between atomic and _Atomic.

Generally I thing different WGs should work closer together.
The WG I asked about above is obviously WG14 ISO/IEC DTR 18037
and the names address spaces.

Even with all features and extensions of C++ I think C is still
important and widespread, in particular in embedded world and
the limited resources and special needs that are considerably
different from big systems.

in particular with more elaborate initializers that contain
string literals.

Let me give a more elaborate C example:

typedef struct
    int id;
    const char *label;
} item_t;

const item_t menu[] =
    { 0, "Yes" },
    { 1, "No" },
    { 2, "I really don't know and will return later" }

menu[] takes 61 bytes of RAM. Both menu[] and the string
literals are placed and must be placed in .rodata for reasons
explained above.

To put menu into Flash, you write

const __flash item_t menu[] = ...

The the menu[] will be places in Flash but the literals will

To put everything in flash you need

typedef struct
    int id;
    const char label[42];
} jtem_t;

const __flash jtem_t menju[] =
    { 0, "Yes" },
    { 1, "No" },
    { 2, "I really don't know and will return later" }

menju[] takes 132 bytes of flash which is more than double as
much as menu[] needs. With the additional disadvantage that
string literals cannot be merged and there is a magic number 42.

Changing item_t to the obvious

typedef struct
    int id;
    const __flash char *label;
} item_t;

leads to "initializer element is not computable at load time"
while initializing menu[], because literals are in generic but
the pointer is to flash.

That is why I think this is an oversight by the WG.

To put everything in flash and have a small, 61-byte footprint
analogon to the initial example, you need this:

#define FSTR(X) ((const __flash char[]) { X })

typedef struct
    int id;
    const __flash char *label;
} item_t;

const __flash item_t menu[] =
    { 0, FSTR ("Yes") },
    { 1, FSTR ("No") },
    { 2, FSTR ("I really don't know and will return later") }

I don't even know if FSTR works per design or just by accident
because somethings has been overseen in GCC.  In particular,
initializing menu[] like that does no more work if it is
static local to a function.

Or you'll have to rewrite the code and use dummies:

typedef struct
    int id;
    const __flash char *label;
} item_t;

static const __flash char str_Yes[] = "Yes";
static const __flash char str_No[] = "No";
static const __flash char str_I_really_don_t_know_and_will_return_later[] = "I really don't know and will return later";

const __flash item_t menu[] =
    { 0, str_Yes },
    { 1, str_No },
    { 2, str_I_really_don_t_know_and_will_return_later }

Maybe you can explain how flash<> solves such a problem.

Also note that once you have the code ported to avr it is
trivial to port it to generic by means of -D__flash= or by

#define __flash  /* nothing */

I think anything that adds new type qualifier would meet
resistance from WG21.  However, if we can work out
the details of something flash<T>, then it might be easier to
get WG21 consider it and extend the framework to other
address spaces.

Would flash<> be something that has to be accepted by the WG?
Or is it a GCC extension?
Or an avr backend extension?
Or can it be implemented in user land?

Where do the LPM/LPMX/ELPM/ELPMX instructions come from?

Would there be need to implement them for each and every type T
in flash<T>? Or just for the base types?
What about implicit memcpy with

flash<T> b;
T a = b;

There is more than one non-generic AS in avr BE, namely
__flash, __flash1, __flash2, __flash3, __flash4, __flash5
and __memx.  There could be even more like __ramx or
__eeprom or __fuse.

All the current ASes for avr are defined in the backend.
There is no WG that needs to approve it and there are
no GCC extensions needed beyond the named address space
extension already implemented.

A really interesting part of the address spaces from WG14
are the "user defined address spaces".

The above AS like __flash* and __memx are
"intrinsic named address spaces" that need great deal of
knowledge about the AVR architecture.

__eeprom would need even more and it is not appropriate
to implement is as intrinsic, IMO.

A user-level implementation would be great. All the
hardware complexity could be kept out of the compiler
and users could interface their hardware as needed.
All could happen by support library that extends
the language as needed, bit of language oriented
programming :-)

I am not aware of any attempts to implement the user
spaces in GCC, and even though I am not familiar with
that corner of GCC I'd guess it is very complex and
highly non-trivial task.

Or would it just mean that the FE emits calls or
inlines the code as needed and almost everything is
already there?


reply via email to

[Prev in Thread] Current Thread [Next in Thread]