Chandler Carruth ff0e3a1e1c Rework the bitfield access IR generation to address PR13619 and
generally support the C++11 memory model requirements for bitfield
accesses by relying more heavily on LLVM's memory model.

The primary change this introduces is to move from a manually aligned
and strided access pattern across the bits of the bitfield to a much
simpler lump access of all bits in the bitfield followed by math to
extract the bits relevant for the particular field.

This simplifies the code significantly, but relies on LLVM to
intelligently lowering these integers.

I have tested LLVM's lowering both synthetically and in benchmarks. The
lowering appears to be functional, and there are no really significant
performance regressions. Different code patterns accessing bitfields
will vary in how this impacts them. The only real regressions I'm seeing
are a few patterns where the LLVM code generation for loads that feed
directly into a mask operation don't take advantage of the x86 ability
to do a smaller load and a cheap zero-extension. This doesn't regress
any benchmark in the nightly test suite on my box past the noise
threshold, but my box is quite noisy. I'll be watching the LNT numbers,
and will look into further improvements to the LLVM lowering as needed.

llvm-svn: 169489
2012-12-06 11:14:44 +00:00

313 lines
5.6 KiB
C++

// RUN: %clang_cc1 -triple x86_64-apple-darwin -verify -emit-llvm -o - %s | FileCheck %s
void t1() {
// CHECK: define void @_Z2t1v
// CHECK: [[REFLOAD:%.*]] = load i32** @a, align 8
// CHECK: load i32* [[REFLOAD]], align 4
extern int& a;
int b = a;
}
void t2(int& a) {
// CHECK: define void @_Z2t2Ri
// CHECK: [[REFLOAD2:%.*]] = load i32** {{.*}}, align 8
// CHECK: load i32* [[REFLOAD2]], align 4
int b = a;
}
int g;
int& gr = g;
int& grr = gr;
void t3() {
int b = gr;
}
// Test reference binding.
struct C { int a; };
void f(const bool&);
void f(const int&);
void f(const _Complex int&);
void f(const C&);
C aggregate_return();
bool& bool_reference_return();
int& int_reference_return();
_Complex int& complex_int_reference_return();
C& aggregate_reference_return();
void test_bool() {
bool a = true;
f(a);
f(true);
bool_reference_return() = true;
a = bool_reference_return();
struct { const bool& b; } b = { true };
}
void test_scalar() {
int a = 10;
f(a);
struct { int bitfield : 3; } s = { 3 };
f(s.bitfield);
f(10);
__attribute((vector_size(16))) typedef int vec4;
f((vec4){1,2,3,4}[0]);
int_reference_return() = 10;
a = int_reference_return();
struct { const int& a; } agg = { 10 };
}
void test_complex() {
_Complex int a = 10i;
f(a);
f(10i);
complex_int_reference_return() = 10i;
a = complex_int_reference_return();
struct { const _Complex int &a; } agg = { 10i };
}
void test_aggregate() {
C c;
f(c);
f(aggregate_return());
aggregate_reference_return().a = 10;
c = aggregate_reference_return();
struct { const C& a; } agg = { C() };
}
int& reference_return() {
return g;
}
int reference_decl() {
int& a = g;
const int& b = 1;
return a+b;
}
struct A {
int& b();
};
void f(A* a) {
int b = a->b();
}
// PR5122
void *foo = 0;
void * const & kFoo = foo;
struct D : C { D(); ~D(); };
void h() {
// CHECK: call void @_ZN1DD1Ev
const C& c = D();
}
namespace T {
struct A {
A();
~A();
};
struct B {
B();
~B();
A f();
};
void f() {
// CHECK: call void @_ZN1T1BC1Ev
// CHECK: call void @_ZN1T1B1fEv
// CHECK: call void @_ZN1T1BD1Ev
const A& a = B().f();
// CHECK: call void @_ZN1T1fEv
f();
// CHECK: call void @_ZN1T1AD1Ev
}
}
// PR5227.
namespace PR5227 {
void f(int &a) {
(a = 10) = 20;
}
}
// PR5590
struct s0;
struct s1 { struct s0 &s0; };
void f0(s1 a) { s1 b = a; }
// PR6024
// CHECK: @_Z2f2v()
// CHECK: alloca i32,
// CHECK-NEXT: store
// CHECK-NEXT: ret
const int &f2() { return 0; }
// Don't constant fold const reference parameters with default arguments to
// their default arguments.
namespace N1 {
const int foo = 1;
// CHECK: @_ZN2N14test
void test(const int& arg = foo) {
// Ensure this array is on the stack where we can set values instead of
// being a global constant.
// CHECK: %args_array = alloca
const int* const args_array[] = { &arg };
}
}
// Bind to subobjects while extending the life of the complete object.
namespace N2 {
class X {
public:
X(const X&);
X &operator=(const X&);
~X();
};
struct P {
X first;
};
P getP();
// CHECK: define void @_ZN2N21fEi
// CHECK: call void @_ZN2N24getPEv
// CHECK: getelementptr inbounds
// CHECK: store i32 17
// CHECK: call void @_ZN2N21PD1Ev
void f(int i) {
const X& xr = getP().first;
i = 17;
}
struct SpaceWaster {
int i, j;
};
struct ReallyHasX {
X x;
};
struct HasX : ReallyHasX { };
struct HasXContainer {
HasX has;
};
struct Y : SpaceWaster, HasXContainer { };
struct Z : SpaceWaster, Y { };
Z getZ();
// CHECK: define void @_ZN2N21gEi
// CHECK: call void @_ZN2N24getZEv
// CHECK: {{getelementptr inbounds.*i32 0, i32 0}}
// CHECK: {{getelementptr inbounds.*i32 0, i32 0}}
// CHECK: store i32 19
// CHECK: call void @_ZN2N21ZD1Ev
// CHECK: ret void
void g(int i) {
const X &xr = getZ().has.x;
i = 19;
}
}
namespace N3 {
// PR7326
struct A {
explicit A(int);
~A();
};
// CHECK: define internal void @__cxx_global_var_init
// CHECK: call void @_ZN2N31AC1Ei(%"struct.N3::A"* @_ZGRN2N35sA123E, i32 123)
// CHECK: call i32 @__cxa_atexit
// CHECK: ret void
const A &sA123 = A(123);
}
namespace N4 {
struct A {
A();
~A();
};
void f() {
// CHECK: define void @_ZN2N41fEv
// CHECK: call void @_ZN2N41AC1Ev(%"struct.N4::A"* @_ZGRZN2N41fEvE2ar)
// CHECK: call i32 @__cxa_atexit
// CHECK: ret void
static const A& ar = A();
}
}
// PR9494
namespace N5 {
struct AnyS { bool b; };
void f(const bool&);
AnyS g();
void h() {
// CHECK: call i8 @_ZN2N51gEv()
// CHECK: call void @_ZN2N51fERKb(i8*
f(g().b);
}
}
// PR9565
namespace PR9565 {
struct a { int a : 10, b : 10; };
// CHECK: define void @_ZN6PR95651fEv()
void f() {
// CHECK: call void @llvm.memcpy
a x = { 0, 0 };
// CHECK: [[WITH_SEVENTEEN:%[.a-zA-Z0-9]+]] = or i32 [[WITHOUT_SEVENTEEN:%[.a-zA-Z0-9]+]], 17
// CHECK: store i32 [[WITH_SEVENTEEN]], i32* [[XA:%[.a-zA-Z0-9]+]]
x.a = 17;
// CHECK-NEXT: bitcast
// CHECK-NEXT: load
// CHECK-NEXT: shl
// CHECK-NEXT: ashr
// CHECK-NEXT: store i32
// CHECK-NEXT: store i32*
const int &y = x.a;
// CHECK-NEXT: bitcast
// CHECK-NEXT: load
// CHECK-NEXT: and
// CHECK-NEXT: or i32 {{.*}}, 19456
// CHECK-NEXT: store i32
x.b = 19;
// CHECK-NEXT: ret void
}
}
namespace N6 {
extern struct x {char& x;}y;
int a() { return y.x; }
// CHECK: define i32 @_ZN2N61aEv
// CHECK: [[REFLOAD3:%.*]] = load i8** getelementptr inbounds (%"struct.N6::x"* @_ZN2N61yE, i32 0, i32 0), align 8
// CHECK: load i8* [[REFLOAD3]], align 1
}