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
313 lines
5.6 KiB
C++
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
|
|
}
|