operator overloading fails while debugging with gdb for i386

For the given test: 

class A1 {
  int x;
  int y;

  public:

  A1(int a, int b)
  {
   x=a;
   y=b;
  }

A1 operator+(const A1&);
};

A1 A1::operator+(const A1& second)
{
 A1 sum(0,0);
 sum.x = x + second.x;
 sum.y = y + second.y;

 return (sum);
}

int main (void)
{
 A1 one(2,3);
 A1 two(4,5);

 return 0;
}

when the exectable of this code is debugged in gdb for i386, we dont get the
expected results.

when we break at return 0; and give the command: print one + two, the result
should be $1 = {x = 6, y = 8}, but the result obtained is $1 = {x = 2, y = 3}.

This is related to debug information generated, as normally the overloading is
occuring.
eg: A1 three = one + two results {x = 6, y = 8}.

On checking the assembly, a suspicious entry is found which may be related for
the failure:

    #DEBUG_VALUE: operator+:this <- undef
    #DEBUG_VALUE: operator+:second <- undef

Problem seems not only with operator overloading, It occurs with struct value returning also.

gdb while debugging expects the return value in eax, gcc does returns in eax, But Clang returns in edx(it can be checked in gdb by printing the contents of edx).

Code(sample code)

struct A1 {
int x;
int y;
};

A1 sum(const A1 one, const A1 two)
{
A1 plus = {0,0};
plus.x = one.x + two.x;
plus.y = one.y + two.y;

return (plus);
}

int main (void)
{
A1 one= {2,3};
A1 two= {4,5};
A1 three = sum(one,two);
return 0;
}

gcc assembley (snippet of sum function)

Z3sum2A1S:
.loc 1 8 0
pushl %ebp
movl %esp, %ebp
.loc 1 9 0
movl 8(%ebp), %eax
movl $0, (%eax)
movl 8(%ebp), %eax
movl $0, 4(%eax)
.loc 1 10 0
movl 12(%ebp), %edx
movl 20(%ebp), %eax
addl %eax, %edx
movl 8(%ebp), %eax
movl %edx, (%eax)
.loc 1 11 0
movl 16(%ebp), %edx
movl 24(%ebp), %eax
addl %eax, %edx
movl 8(%ebp), %eax
movl %edx, 4(%eax)
.LBE2:
.loc 1 14 0
movl 8(%ebp), %eax
popl %ebp
ret $4

clang assembly (snippet of sum function)
Z3sum2A1S:
.loc 1 8 0
pushl %edi
pushl %esi
leal 24(%esp), %eax
leal 16(%esp), %ecx
movl 12(%esp), %edx
.loc 1 9 17 prologue_end
movl $0, 4(%edx)
movl $0, (%edx)
.loc 1 10 2
movl (%ecx), %esi
movl (%eax), %edi
addl %edi, %esi
movl %esi, (%edx)
.loc 1 11 2
movl 4(%ecx), %ecx
movl 4(%eax), %eax
addl %eax, %ecx
movl %ecx, 4(%edx)
.loc 1 13 2
popl %esi
popl %edi
ret $4

But while returning int value clang returns with eax… as expect. Problem comes with when used struct/class

Is the behaviour of llvm as per the standards or is this a bug??

Hi,

Structures are passed by pointer, so the return value is not actually in eax. That code gets transformed into something like:

void sum(A1 *out, const A1 one, const A1 two) {
  out->x = one.x + two.x
  out->y = one.y + two.y
}

So actually the function ends up returning void and operating on a hidden parameter, so %eax is dead at the end of the function and should not be being relied upon by the calling code.

I believe this is a red-herring to whatever your actual bug is.

Cheers,

James

Hi,

As you told that function ends up returning void, I just confirmed it in the IR, the function is defined as:

define void @Z3sum2A1S(%struct.A1* noalias sret %agg.result, %struct.A1* byval align 4 %one, %struct.A1* byval align 4 %two).

But when i checked the register values in g++, eax contains an address of stack, which points to the value (object) returned by sum. That is if we print the contents of the address stored in eax and contents of (address stored in eax)+4, we get the correct value of sum. And GDB seems to print from this only when we give the command print sum(one,two). { for clang compiled code eax has some other value}

So is this just a coincidence for g++ that eax points to this address and gdb prints the right value on the command print sum(one,two)??

The code is small, and the structure is probably set at the end of the
block, so I think it's not that much of a coincidence.

Regardless of the specific value on that specific compilation unit,
you should never rely on behaviour of clobbered registers. That
function returns void, you should look up in the caller, where is the
address of the structure.

Does the code execute correctly? Does a printf on the sum outputs the
correct value? StrucRet is stable on Intel for years, I'd be surprised
id that didn't work.

However, it's possible that clang is messing up the position of the
structure in Dwarf, so I'd investigate the Dwarf emission first, since
your problem seems to bee with clang+gdb.

Hi,

I was going through this issue along with the standards. What the standard states is(reference: http://www.sco.com/developers/devspecs/abi386-4.pdf):

If a function returns a structure or union, then the caller provides space for the
return value and places its address on the stack as argument word zero. In effect,
this address becomes a ‘‘hidden’’ first argument. Having the caller supply the
return object’s space allows re-entrancy.

A function that returns a structure or union also sets% e a x to the value of the original
address of the caller’s area before it returns. Thus when the caller receives
control again, the address of the returned object resides in register% e a x and can
be used to access the object. Both the calling and the called functions must
cooperate to pass the return value successfully.

Also i verified that through the assembly code, that when the caller calls the callee function, the hidden pointer is stored in eax(other arguments being passed in ecx and edx). But the callee function modifies eax and in the end it does not store the hidden pointer in eax, but it is stored in edx.

So this seems to be the cause of the problem.

@Renato Golin:- Does the code execute correctly? Does a printf on the sum outputs the
correct value? : yes the code executes correctly and some gives the correct value. Its only in gdb that we face this problem as gdb expects the hidden pointer in eax.

Note: GDB “struct return convention” : The caller passes an additional hidden first parameter to the caller. That parameter contains the address at which the value being returned should be stored.

So this seems to be the cause of the problem.

I guess you're mixing two different problems. First, is the possible
lack of conformance with the ABI you state, which I can't comment
since I don't know that ABI very well. Second, is the fact that clang
is not printing correct debug information (or is not interoperating
well enough with gdb).

Fixing the first issue will not necessarily fix the second.

@Renato Golin:- Does the code execute correctly? Does a printf on the sum
outputs the
correct value? : yes the code executes correctly and some gives the correct
value. Its only in gdb that we face this problem as gdb expects the hidden
pointer in eax.

As I expected. Following the ABI is not a requirement to have working
code, or a debuggable code. GDB is probably expecting programs to
follow the ABI if they do not have decent Dwarf, which seems clang is
failing on both.

Debuggers have to deal with all sorts of user (and compiler) code, and
to give users the "debug illusion", they make too many guesses.
However, if the Dwarf information is correct to begin with, I
seriously hope that GDB would rely on that, rather than "expect"
values to be on specific registers.

Have a look in the Dwarf symbols clang generates with your code and
make sure that clang is not printing the correct location information
for that variable on the ranges where it lives on registers and which
registers.

If clang's info is right, there's only the ABI bug and GDB is being
silly. If the info is wrong, then we may need to fix *both* bugs (ABI
+ Dwarf), not just the ABI one.

Hi,

The issue seems in the file x86ISelLowering.cpp where we dont have an implementation for returning the hidden pointer in the specified register for 32 bit. We do have it for 64bit. On adding the following:

in function
X86TargetLowering::LowerReturn

if (!(Subtarget->is64Bit()) &&
DAG.getMachineFunction().getFunction()->hasStructRetAttr()) {
MachineFunction &MF = DAG.getMachineFunction();
X86MachineFunctionInfo *FuncInfo = MF.getInfo();
unsigned Reg = FuncInfo->getSRetReturnReg();
assert(Reg &&
“SRetReturnReg should have been set in LowerFormalArguments().”);
SDValue Val = DAG.getCopyFromReg(Chain, dl, Reg, getPointerTy());

Chain = DAG.getCopyToReg(Chain, dl, X86::EAX, Val, Flag);
Flag = Chain.getValue(1);

MRI.addLiveOut(X86::EAX);
}

in function
X86TargetLowering::LowerFormalArguments

if (!(Is64Bit) && MF.getFunction()->hasStructRetAttr()) {
X86MachineFunctionInfo *FuncInfo = MF.getInfo();
unsigned Reg = FuncInfo->getSRetReturnReg();
if (!Reg) {
Reg = MF.getRegInfo().createVirtualRegister(getRegClassFor(MVT::i32));
FuncInfo->setSRetReturnReg(Reg);
}
SDValue Copy = DAG.getCopyToReg(DAG.getEntryNode(), dl, Reg, InVals[0]);
Chain = DAG.getNode(ISD::TokenFactor, dl, MVT::Other, Copy, Chain);
}

we get the correct output in gdb and the there are no side-effects also.

Please verify whether this is a genuine fix.

The