getelementptr on static const struct

Hi,

I found a mysterious behavior of LLVM optimizer.

I compiled the following code by clang -emit-llvm -S:

#include <stddef.h>
static const struct t {char t[4]; char s;} p = {{}, 'a'};
char f() {
    return ((char*)&p)[offsetof(struct t, s)];
}

then I obtained the following LLVM IR:

%struct.t = type { [4 x i8], i8 }
@p = constant %struct.t { [4 x i8] zeroinitializer, i8 97 }, align 1
define signext i8 @f() nounwind ssp uwtable {
  %1 = load i8* getelementptr inbounds (%struct.t* @p, i32 0, i32 0,
i64 4), align 1
  ret i8 %1
}

By applying the above code to opt -O1 of LLVM 3.4, I obtained:

%struct.t = type { [4 x i8], i8 }
@p = constant %struct.t { [4 x i8] zeroinitializer, i8 97 }, align 1
define signext i8 @f() nounwind readnone ssp uwtable {
  ret i8 0
}

The function "f" is eventually compiled into a function that always
returns 0, while I expect "f" returns 'a' (97).

Is this an intended behavior, or a bug?

By replacing the getelementptr expression in the clang output

  getelementptr inbounds (%struct.t* @p, i32 0, i32 0, i64 4)

with

  getelementptr inbounds (i8* bitcast (%struct.t* @p to i8*), i64 4)

opt -O1 generates the following code that is what I expected:

define signext i8 @f() nounwind readnone ssp uwtable {
  ret i8 97
}

I carefully read LLVM Language Reference Manual but I could not
understand the difference of these two getelementptr expressions.

Are these two getelementptr equivalent in the semantics of LLVM IR?

Thanks,

I compiled the following code by clang -emit-llvm -S:

#include <stddef.h>
static const struct t {char t[4]; char s;} p = {{}, 'a'};
char f() {
   return ((char*)&p)[offsetof(struct t, s)];
}

then I obtained the following LLVM IR:

%struct.t = type { [4 x i8], i8 }
@p = constant %struct.t { [4 x i8] zeroinitializer, i8 97 }, align 1
define signext i8 @f() nounwind ssp uwtable {
%1 = load i8* getelementptr inbounds (%struct.t* @p, i32 0, i32 0,
i64 4), align 1
ret i8 %1
}

The GEP takes the address of the 5th element (= offset 4) of your "char t[4]". It's not entirely clear to me from the description of "inbounds" whether this means the the GEP results in a poison value or not, but it definitely looks wrong.

By applying the above code to opt -O1 of LLVM 3.4, I obtained:

%struct.t = type { [4 x i8], i8 }
@p = constant %struct.t { [4 x i8] zeroinitializer, i8 97 }, align 1
define signext i8 @f() nounwind readnone ssp uwtable {
ret i8 0
}

Given this result, I guess it is in fact a poison value.

Jonas

I believe LLVM IR allows you to GEP from inside of one field into the next,
but I would defer to experts.

The constant folder is what creates this, and it totally ignores the
element index when folding a zero initialized array type:

Constant *ConstantAggregateZero::getSequentialElement() const {
  return Constant::getNullValue(getType()->getSequentialElementType());
}
Constant *ConstantAggregateZero::getElementValue(unsigned Idx) const {
  if (isa<SequentialType>(getType()))
    return getSequentialElement(); // Doesn't pass in Idx or check bounds.
  return getStructElement(Idx);
}

Looks like a bug to me.