This is the mail archive of the gcc-help@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

RE: C++ static integer class constants...


I forgot to hit reply to all, but I wanted this to go to the list.

> After reading Ryan's post, I looked at the (draft) standard.  It says:
> 
> 4 If  a  static  data  member  is of const integral or const enumeration
>   type, its declaration in the class definition can specify a  constant-
>   initializer   which   shall   be   an   integral  constant  expression
>   (_expr.const_).  In that case, the member can appear in integral  con-
>   stant expressions within its scope.  The member shall still be defined
>   in a namespace scope if it is used in the program  and  the  namespace
>   scope definition shall not contain an initializer.
> 
> If I understand this correctly, even const static members need to have 
> a definition somewhere.  Maybe this is "needed" so that you may always 
> take the address of such const static members.
> 
> So the compiler is free to use the constant as if they were variables, 
> if it needs to.  Your code compiles OK if the line
> 
>         int offset = (redundant ? B : A);
> 
> is replaced by
> 
>         if(redundant)
>           offset = B;
>         else
>           offset = A;
> 
> I don't know why, but it does work here.

This seems like a definite bug. The fact that it works in 3.4.2 and not in
3.3.3 seems like a probable bug to me, but I will ask this question in a
standard C++ newsgroup so that someone intimately familiar with the standard
can give me a definitive answer, but I still think g++ 3.3 is wrong and g++
3.4 is right.

If I were taking the address of the constant, sure, it would have to have
storage space and I wouldn't expect it any other way.

I was looking at the assembly generated by each compiler. There is a
striking difference. Here is foo::method(bool redundant) in x86 assembly
generated by mingw/g++ 3.4.2. I have commented the relevant sections.

-----------------------------------------------
.globl __ZNK3foo6methodEb
	.def	__ZNK3foo6methodEb;	.scl	2;	.type	32;
.endef
__ZNK3foo6methodEb:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$40, %esp
	movl	12(%ebp), %eax
	movb	%al, -1(%ebp)    ; move redundant (%al) to stack
	cmpb	$0, -1(%ebp)     ; if redundant is false 
	je	L12              ; goto L12
	movl	$8184, -16(%ebp) ; move 8184 (0x1FF8) into -16(%ebp) (aka
offset)
	jmp	L13              ; goto L13
L12:
	movl	$8, -16(%ebp)    ; move 8 into offset
L13:
	movl	-16(%ebp), %eax  ; return std::pair blah blah blah
	movl	%eax, -8(%ebp)
	movl	8(%ebp), %eax
	addl	-8(%ebp), %eax
	incl	%eax
	movzbl	(%eax), %eax
	movb	%al, -11(%ebp)
	leal	-11(%ebp), %eax
	movl	%eax, 8(%esp)
	movl	8(%ebp), %edx
	movl	-8(%ebp), %eax
	movzbl	(%eax,%edx), %eax
	movb	%al, -12(%ebp)
	leal	-12(%ebp), %eax
	movl	%eax, 4(%esp)
	leal	-10(%ebp), %eax
	movl	%eax, (%esp)
	call	__ZNSt4pairIhhEC1ERKhS2_
	movzwl	-10(%ebp), %eax
	leave
	ret
-----------------------------------------------

And here is g++ 3.3.3's assembly output. As you can see, it's not even
trying to use it as a constant. This is why storage space is required.

-----------------------------------------------
_ZNK3foo6methodEb:
.LFB1535:
	pushl	%ebp
.LCFI14:
	movl	%esp, %ebp
.LCFI15:
	subl	$24, %esp
.LCFI16:
	movl	16(%ebp), %eax
	movb	%al, -1(%ebp)    ; move al (redundant) to stack
	cmpb	$0, -1(%ebp)     ; is redundant false?
	je	.L6              ; then jump to .L6
	movl	_ZN3foo1BE, %eax ; move B (storage space required) to eax
	movl	%eax, -16(%ebp)  ; put eax on the stack
	jmp	.L7
.L6:
	movl	_ZN3foo1AE, %eax ; move A (storage space required) to eax
	movl	%eax, -16(%ebp)  ; and put eax on the stack
.L7:
	movl	-16(%ebp), %eax  ; ??? why is it moving it just to move it
back?
	movl	%eax, -8(%ebp)   ; whatever, the rest is return std::pair
blah blah
	subl	$4, %esp
	movl	12(%ebp), %eax
	addl	-8(%ebp), %eax
	incl	%eax
	movb	(%eax), %al
	movb	%al, -9(%ebp)
	leal	-9(%ebp), %eax
	pushl	%eax
	movl	12(%ebp), %edx
	movl	-8(%ebp), %eax
	movb	(%eax,%edx), %al
	movb	%al, -10(%ebp)
	leal	-10(%ebp), %eax
	pushl	%eax
	pushl	8(%ebp)
.LCFI17:
	call	_ZNSt4pairIhhEC1ERKhS2_
	addl	$16, %esp
	movl	8(%ebp), %eax
	leave
	ret	$4
-----------------------------------------------

If you replace the ternary operator with the if as you suggested, the
assembly looks more like the 3.4.2 version. This is why it compiles, and
makes it seem more and more like a bug. Clearly it's capable of doing what I
think it should do, but chooses not to when presented with the ternary
operator.

> 
> So you need to define those constants somewhere, but the compiler  may 
> use the initializer in the class scope, if it is able to do so.  For 
> some reason, GCC couldn't use the initializer in the first case, so it 
> is using the address of the constants.  Go figure.

The assembly output and changing from ternary to if proves it can use the
initializer, but chooses not to.

Thanks,

--John Ratliff




Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]