Packing for __nogc classes

E

Edward Diener

Is the packing alignment of __nogc classes stored as part of the assembly ?
I think it must as the compiler, when referencing the assembly, could not
know how the original data is packed otherwise. Yet, in my understanding,
attributes are only __gc and __value class specific and do not apply to
__nogc classes. Is this correct ? If so, how is the packing alignment of
__nogc classes stored ?
 
T

Tomas Restrepo \(MVP\)

Hi Edward,
Is the packing alignment of __nogc classes stored as part of the assembly ?
I think it must as the compiler, when referencing the assembly, could not
know how the original data is packed otherwise. Yet, in my understanding,
attributes are only __gc and __value class specific and do not apply to
__nogc classes. Is this correct ? If so, how is the packing alignment of
__nogc classes stored ?

Well, I don't think the packing is actually stored. When the compiler
creates metadata (very basic metadata indeed) for __nogc classes, it does so
by creating them as opaque value types, which means basically it creates
them as value types containing no members, and only basic information on
layout and size.

Here's an example of one such __nogc class represented:

..class private sequential ansi sealed A
extends [mscorlib]System.ValueType
{
.pack 1
.size 12
} // end of class A

Notice the .pack and .size directives, which tell the runtime how to pack
the type and size of the type.

AFAIK, the compiler will *always* mark __nogc types with .pack 1, even if
actual packing is different. However, the compiler *will* adjust the .size
directive so that it reflects the real size of the object in memory
according to it's packing. In this case, I was trying with a class contaning
an int, a char and another int, with packing set to 4. If I set it to 1,
then the code would've said ".size 9", and so on.
 
G

Guest

Is there good resources on runtime design, Im aware of compiler design but
not about managed runtime design.



Tomas Restrepo (MVP) said:
Hi Edward,
Is the packing alignment of __nogc classes stored as part of the
assembly
?
I think it must as the compiler, when referencing the assembly, could not
know how the original data is packed otherwise. Yet, in my understanding,
attributes are only __gc and __value class specific and do not apply to
__nogc classes. Is this correct ? If so, how is the packing alignment of
__nogc classes stored ?

Well, I don't think the packing is actually stored. When the compiler
creates metadata (very basic metadata indeed) for __nogc classes, it does so
by creating them as opaque value types, which means basically it creates
them as value types containing no members, and only basic information on
layout and size.

Here's an example of one such __nogc class represented:

.class private sequential ansi sealed A
extends [mscorlib]System.ValueType
{
.pack 1
.size 12
} // end of class A

Notice the .pack and .size directives, which tell the runtime how to pack
the type and size of the type.

AFAIK, the compiler will *always* mark __nogc types with .pack 1, even if
actual packing is different. However, the compiler *will* adjust the .size
directive so that it reflects the real size of the object in memory
according to it's packing. In this case, I was trying with a class contaning
an int, a char and another int, with packing set to 4. If I set it to 1,
then the code would've said ".size 9", and so on.
 
E

Edward Diener

Tomas said:
Hi Edward,
Is the packing alignment of __nogc classes stored as part of the
assembly ? I think it must as the compiler, when referencing the
assembly, could not know how the original data is packed otherwise.
Yet, in my understanding, attributes are only __gc and __value class
specific and do not apply to __nogc classes. Is this correct ? If
so, how is the packing alignment of __nogc classes stored ?

Well, I don't think the packing is actually stored. When the compiler
creates metadata (very basic metadata indeed) for __nogc classes, it
does so by creating them as opaque value types, which means basically
it creates them as value types containing no members, and only basic
information on layout and size.

Here's an example of one such __nogc class represented:

.class private sequential ansi sealed A
extends [mscorlib]System.ValueType
{
.pack 1
.size 12
} // end of class A

Notice the .pack and .size directives, which tell the runtime how to
pack the type and size of the type.

Is this from the assembly itself ? If so, it looks like the assembly is
storing a .pack directive.
AFAIK, the compiler will *always* mark __nogc types with .pack 1,
even if actual packing is different.

This is strange. Suppose I need a different packing for __nogc classes to
correspond to some already created data structure. How do I tell the
compiler to change the packing. In non-CLR code I would use "#pragma pack".
Is this still accepted in __nogc classes in a CLR assembly ? If it is, why
does the compiler ignore it and always insert a .pack 1 directive ?
However, the compiler *will*
adjust the .size directive so that it reflects the real size of the
object in memory according to it's packing. In this case, I was
trying with a class contaning an int, a char and another int, with
packing set to 4. If I set it to 1, then the code would've said
".size 9", and so on.

Your explanation has confused me. Could you please be clearer as to:

1) whether there is a way to set the packing size for __nogc classes.
2) what that way is.
3) whether or not the end-user changing the packing from the IDE affects the
packing for __nogc classes if no specific packing has been set via 1) or 2).

The reason for this post has to do with a potential common problem pre-.NET
having to do with packing of classes/structures. If the programmer/creator
of a struct/class does not specifically set a packing size, via the #pragma
pack directive, when the compiler saw the header file it would apply
whatever the packing was in the IDE ( or command-line ). This could lead to
ABI problems if a library was built with a particular packing for a
class/structure and the end-user of that library decided to change the
global packing in the IDE ( or command-line ) to some other value. The
solution to this problem was for the programmer/creator to set the packing
for the class.structure in the header file for the class/structure via
"#pragma pack". Then the compiler would honor this directive by overriding
the end-user's global packing for that particular class/struct and all would
be well with no ABI problem occurring. Every 3rd party implementation which
distributed C++ header files/library would of course use the "#pragma pack"
method to assure that no ABI problem would occur.

I wanted to make sure that this would work properly with .NET C++ components
with __nogc classes. I am, of course, assuming that __gc and __value classes
store their packing as part of the metadata. My problem is that I want to be
assured that for __nogc classes that the end-user changing the global
packing will not affect the way the compiler/linker access data and
alignment in __nogc classes. Since __nogc ( and __gc and __value ) classes
in assemblies have no header file, I can only think that the packing used
when the assembly is built is somehow picked up and correctly used no matter
what the global packing in the IDE ( or command-line ) happens to be. If
this is the case, I will not worry about putting "#pragma pack" around my
__nogc classes since I don't need to change the default packing for __nogc
classes.
 
D

Doug Harrison [MVP]

Edward said:
The reason for this post has to do with a potential common problem pre-.NET
having to do with packing of classes/structures. If the programmer/creator
of a struct/class does not specifically set a packing size, via the #pragma
pack directive, when the compiler saw the header file it would apply
whatever the packing was in the IDE ( or command-line ). This could lead to
ABI problems if a library was built with a particular packing for a
class/structure and the end-user of that library decided to change the
global packing in the IDE ( or command-line ) to some other value. The
solution to this problem was for the programmer/creator to set the packing
for the class.structure in the header file for the class/structure via
"#pragma pack". Then the compiler would honor this directive by overriding
the end-user's global packing for that particular class/struct and all would
be well with no ABI problem occurring. Every 3rd party implementation which
distributed C++ header files/library would of course use the "#pragma pack"
method to assure that no ABI problem would occur.

I think that's backwards. The real solution is to never use /Zp but instead
use #pragma pack on structs only as necessary. If you expect everybody to
use #pragma pack in order to write /Zp-neutral headers, you've effectively
made #pragma pack part of the language, which is, to put it mildly, not a
good thing. I'd personally like to see /Zp abolished, and I'd like #pragma
pack to generate warnings when its effects penetrate #includes, both coming
or going. An exception to this latter rule could be made for the
<pshpackN.h> and <poppack.h> Windows headers. BTW, the Windows headers are
not all /Zp-neutral.
 
T

Tomas Restrepo \(MVP\)

Hi Edward,
Is this from the assembly itself ? If so, it looks like the assembly is
storing a .pack directive.

Yes, it's part of the actual managed metadata in the assembly.
This is strange. Suppose I need a different packing for __nogc classes to
correspond to some already created data structure.

Wouldn't be a problem. I already explained how the compiler deals with it:
by telling the runtime the actual size of objects of that type, which
already takes into account packing. See, the .pack directive in managed
metadata serves to tell the runtime when laying out a type how to organize
members. Since __nogc classes are opaque to the runtime, really, there are
no members the runtime can lay out, so the compíler "fools" it by telling it
the actual size of the resulting object so that the runtime can reserve the
memory correctly on the stack or as part as a __gc type.
How do I tell the
compiler to change the packing.

For __nogc type, same way you always have (#pragma pack).
Your explanation has confused me. Could you please be clearer as to:

1) whether there is a way to set the packing size for __nogc classes.

Yes, see above.
2) what that way is.
Again, see above.
3) whether or not the end-user changing the packing from the IDE affects the
packing for __nogc classes if no specific packing has been set via 1) or
2).

See above, Keep in mind, though, that AFAICS, managed metadata is probably
NOT enough for the compiler to "guess" what the original packing of the
__nogc type is, which is where having the original header file defining it
helps :)
 
E

Edward Diener

Doug said:
I think that's backwards. The real solution is to never use /Zp but
instead use #pragma pack on structs only as necessary. If you expect
everybody to use #pragma pack in order to write /Zp-neutral headers,
you've effectively made #pragma pack part of the language, which is,
to put it mildly, not a good thing. I'd personally like to see /Zp
abolished, and I'd like #pragma pack to generate warnings when its
effects penetrate #includes, both coming or going. An exception to
this latter rule could be made for the <pshpackN.h> and <poppack.h>
Windows headers. BTW, the Windows headers are not all /Zp-neutral.

I create a 3rd party header and library. I build it with packing set to 8
which is the default. I don't use #pragma pack around my classes as you
suggest. I distribute it ands the end-user sets his /Zp to 4 when including
my header file. Guess what ? My library is broken.

However, if I had used #pragma pack(push,8) prior to my class definition in
the header file and #pragma pack(pop) after my class definition in the
header file, everything works fine no matter what the end-user does. And you
are telling me that you think that is backward ?

As far as abolishing /Zp, how in the world do you expect programmers to set
packing, or perhaps you don't. If they have a data structure which requires
packing of 1, but they must use the deafult packing of 8, I guess they are
just out of luck, huh ?

You are way off base, sir, and have chosen to make a silly comment for some
reason I can't even begin to guess.
 
D

Doug Harrison [MVP]

Edward said:
I create a 3rd party header and library. I build it with packing set to 8
which is the default. I don't use #pragma pack around my classes as you
suggest. I distribute it ands the end-user sets his /Zp to 4 when including
my header file. Guess what ? My library is broken.

So is <windows.h>. So are any number of other headers which don't use
#pragma pack. The mistake is the user's for changing the default packing,
not the library author's for failing to use a #pragma (non-standard by
definition) to control something outside the language definition.
However, if I had used #pragma pack(push,8) prior to my class definition in
the header file and #pragma pack(pop) after my class definition in the
header file, everything works fine no matter what the end-user does. And you
are telling me that you think that is backward ?

Completely backwards. I explained why in my last message, which is quoted in
full above.
As far as abolishing /Zp, how in the world do you expect programmers to set
packing, or perhaps you don't. If they have a data structure which requires
packing of 1, but they must use the deafult packing of 8, I guess they are
just out of luck, huh ?

Like I said in my last message, they should use #pragma pack to establish
non-default packing on the structs that require it.
You are way off base, sir, and have chosen to make a silly comment for some
reason I can't even begin to guess.

You might want to consider dialing the rudeness back a few notches and
thinking about things a bit more carefully before replying.
 
E

Edward Diener

Doug said:
So is <windows.h>. So are any number of other headers which don't use
#pragma pack. The mistake is the user's for changing the default
packing, not the library author's for failing to use a #pragma
(non-standard by definition) to control something outside the
language definition.

This is incorrect. Windows.h, and all windows related header files, as well
as all MS RTL header files, guard against the user changing the default
packing through /Zp. They use pushpackn.h and poppack.h header files in
order to do this. These header files use the same #pragma pack(n) and
#pragma pop I was discussing when VC++ is involved. Borland also does the
same for their header files. Essentially MS and Borland do just what I do,
which you are claiming is backward. Fine, believe what you will.

I am not going to comment further. Do things your own way and I will do them
in my own way.
 
E

Edward Diener

Tomas said:
Hi Edward,


Yes, it's part of the actual managed metadata in the assembly.


Wouldn't be a problem. I already explained how the compiler deals
with it: by telling the runtime the actual size of objects of that
type, which already takes into account packing. See, the .pack
directive in managed metadata serves to tell the runtime when laying
out a type how to organize members. Since __nogc classes are opaque
to the runtime, really, there are no members the runtime can lay out,
so the compíler "fools" it by telling it the actual size of the
resulting object so that the runtime can reserve the memory correctly
on the stack or as part as a __gc type.


For __nogc type, same way you always have (#pragma pack).


Yes, see above.

Again, see above.


See above, Keep in mind, though, that AFAICS, managed metadata is
probably NOT enough for the compiler to "guess" what the original
packing of the __nogc type is, which is where having the original
header file defining it helps :)

Thanks for all the info.
 
D

Doug Harrison [MVP]

Edward said:
This is incorrect. Windows.h, and all windows related header files, as well
as all MS RTL header files, guard against the user changing the default
packing through /Zp.

Sigh. For years, I've been advising people to stay away from /Zp. Read this
recent thread to understand why:

http://groups.google.com/groups?threadm=5A999D28-D21C-42A1-89FA-AA3CBAD52263@microsoft.com

In that thread, it was determined that <wincon.h> is not /Zp-immune, which
is why I said in my first message to you, "the Windows headers are not all
/Zp-neutral," and as it's #included by <windows.h>, why I said in my second
They use pushpackn.h and poppack.h header files in
order to do this.

Which is why I mentioned them in my first message to you. I'm not suggesting
wrecking their functionality.
These header files use the same #pragma pack(n) and
#pragma pop I was discussing when VC++ is involved. Borland also does the
same for their header files. Essentially MS and Borland do just what I do,
which you are claiming is backward.

They _try_ to do that in order to accommodate the misguided /Zp compiler
option, but inevitably, headers are missed. Then there's all the third party
code out there which doesn't even try to do it.
Fine, believe what you will.

I am not going to comment further. Do things your own way and I will do them
in my own way.

Too bad. I would have liked to have learned if you have a compelling reason
to want to facilitate and thus perpetuate the use of /Zp. It typically just
doesn't work right, because the necessary #pragmas aren't practiced
universally, not by a long shot, and even when they are intended to be used,
people tend to slip up and miss some, as with <wincon.h>.

With that in mind, I'll continue to point out that /Zp is best avoided by
people who don't enjoy chasing obscure bugs and by library authors who don't
want to aggravate their customers. Several years ago, I used a library that
actually used /Zp1 in its makefile. I had to go through and add the #pragmas
to its header files, because changing the default packing in my program was
not an option. It's not an option for anybody, really. But as you said,
"Fine, believe what you will." Myself, I'll continue to believe what my
experience has taught me over the years. :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top