2014-01-15 03:35:09 +08:00
|
|
|
// RUN: %clang_cc1 %s -fno-rtti -triple=i386-pc-win32 -emit-llvm -o %t
|
2013-10-17 02:24:06 +08:00
|
|
|
// RUN: FileCheck %s < %t
|
|
|
|
// RUN: FileCheck --check-prefix=CHECK2 %s < %t
|
2013-06-29 04:45:28 +08:00
|
|
|
|
2013-10-10 02:16:58 +08:00
|
|
|
// For now, just make sure x86_64 doesn't crash.
|
2014-01-15 03:35:09 +08:00
|
|
|
// RUN: %clang_cc1 %s -fno-rtti -triple=x86_64-pc-win32 -emit-llvm -o %t
|
2013-10-10 02:16:58 +08:00
|
|
|
|
2013-08-21 14:25:03 +08:00
|
|
|
struct VBase {
|
2013-10-10 02:16:58 +08:00
|
|
|
virtual ~VBase();
|
2013-08-21 14:25:03 +08:00
|
|
|
virtual void foo();
|
|
|
|
virtual void bar();
|
|
|
|
int field;
|
2013-06-29 04:45:28 +08:00
|
|
|
};
|
2013-08-21 14:25:03 +08:00
|
|
|
|
|
|
|
struct B : virtual VBase {
|
2013-09-27 22:48:01 +08:00
|
|
|
B();
|
2013-10-10 02:16:58 +08:00
|
|
|
virtual ~B();
|
2013-08-21 14:25:03 +08:00
|
|
|
virtual void foo();
|
|
|
|
virtual void bar();
|
2013-06-29 04:45:28 +08:00
|
|
|
};
|
|
|
|
|
2013-09-27 22:48:01 +08:00
|
|
|
B::B() {
|
2013-10-10 02:16:58 +08:00
|
|
|
// CHECK-LABEL: define x86_thiscallcc %struct.B* @"\01??0B@@QAE@XZ"
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[THIS:.*]] = load %struct.B*, %struct.B**
|
2013-09-27 22:48:01 +08:00
|
|
|
// CHECK: br i1 %{{.*}}, label %[[INIT_VBASES:.*]], label %[[SKIP_VBASES:.*]]
|
2013-10-10 02:16:58 +08:00
|
|
|
|
|
|
|
// Don't check the INIT_VBASES case as it's covered by the ctor tests.
|
|
|
|
|
2013-09-27 22:48:01 +08:00
|
|
|
// CHECK: %[[SKIP_VBASES]]
|
|
|
|
// CHECK: %[[THIS_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8, i8* %[[THIS_i8]], i32 0
|
2013-10-10 02:16:58 +08:00
|
|
|
// ...
|
|
|
|
// CHECK: %[[THIS_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VFPTR_i8:.*]] = getelementptr inbounds i8, i8* %[[THIS_i8]], i32 %{{.*}}
|
2014-12-04 05:00:21 +08:00
|
|
|
// CHECK: %[[VFPTR:.*]] = bitcast i8* %[[VFPTR_i8]] to i32 (...)***
|
|
|
|
// CHECK: store i32 (...)** bitcast ([3 x i8*]* @"\01??_7B@@6B@" to i32 (...)**), i32 (...)*** %[[VFPTR]]
|
2013-10-10 02:16:58 +08:00
|
|
|
|
|
|
|
// Initialize vtorDisp:
|
|
|
|
// CHECK: %[[THIS_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8, i8* %[[THIS_i8]], i32 0
|
2013-10-10 02:16:58 +08:00
|
|
|
// ...
|
|
|
|
// CHECK: %[[VBASE_OFFSET:.*]] = add nsw i32 0, %{{.*}}
|
|
|
|
// CHECK: %[[VTORDISP_VAL:.*]] = sub i32 %[[VBASE_OFFSET]], 8
|
|
|
|
// CHECK: %[[THIS_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBASE_i8:.*]] = getelementptr inbounds i8, i8* %[[THIS_i8]], i32 %[[VBASE_OFFSET]]
|
|
|
|
// CHECK: %[[VTORDISP_i8:.*]] = getelementptr i8, i8* %[[VBASE_i8]], i32 -4
|
2013-10-10 02:16:58 +08:00
|
|
|
// CHECK: %[[VTORDISP_PTR:.*]] = bitcast i8* %[[VTORDISP_i8]] to i32*
|
|
|
|
// CHECK: store i32 %[[VTORDISP_VAL]], i32* %[[VTORDISP_PTR]]
|
|
|
|
|
|
|
|
// CHECK: ret
|
|
|
|
}
|
|
|
|
|
|
|
|
B::~B() {
|
|
|
|
// CHECK-LABEL: define x86_thiscallcc void @"\01??1B@@UAE@XZ"
|
2013-10-17 02:24:06 +08:00
|
|
|
// Adjust the this parameter:
|
|
|
|
// CHECK: %[[THIS_PARAM_i8:.*]] = bitcast %struct.B* {{.*}} to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[THIS_i8:.*]] = getelementptr inbounds i8, i8* %[[THIS_PARAM_i8]], i32 -8
|
2013-10-17 02:24:06 +08:00
|
|
|
// CHECK: %[[THIS:.*]] = bitcast i8* %[[THIS_i8]] to %struct.B*
|
|
|
|
// CHECK: store %struct.B* %[[THIS]], %struct.B** %[[THIS_ADDR:.*]], align 4
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[THIS:.*]] = load %struct.B*, %struct.B** %[[THIS_ADDR]]
|
2013-10-10 02:16:58 +08:00
|
|
|
|
|
|
|
// Restore the vfptr that could have been changed by a subclass.
|
|
|
|
// CHECK: %[[THIS_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8, i8* %[[THIS_i8]], i32 0
|
2013-10-10 02:16:58 +08:00
|
|
|
// ...
|
2013-09-27 22:48:01 +08:00
|
|
|
// CHECK: %[[THIS_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VFPTR_i8:.*]] = getelementptr inbounds i8, i8* %[[THIS_i8]], i32 %{{.*}}
|
2014-12-04 05:00:21 +08:00
|
|
|
// CHECK: %[[VFPTR:.*]] = bitcast i8* %[[VFPTR_i8]] to i32 (...)***
|
|
|
|
// CHECK: store i32 (...)** bitcast ([3 x i8*]* @"\01??_7B@@6B@" to i32 (...)**), i32 (...)*** %[[VFPTR]]
|
2013-10-10 02:16:58 +08:00
|
|
|
|
|
|
|
// Initialize vtorDisp:
|
|
|
|
// CHECK: %[[THIS_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8, i8* %[[THIS_i8]], i32 0
|
2013-10-10 02:16:58 +08:00
|
|
|
// ...
|
|
|
|
// CHECK: %[[VBASE_OFFSET:.*]] = add nsw i32 0, %{{.*}}
|
|
|
|
// CHECK: %[[VTORDISP_VAL:.*]] = sub i32 %[[VBASE_OFFSET]], 8
|
|
|
|
// CHECK: %[[THIS_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBASE_i8:.*]] = getelementptr inbounds i8, i8* %[[THIS_i8]], i32 %[[VBASE_OFFSET]]
|
|
|
|
// CHECK: %[[VTORDISP_i8:.*]] = getelementptr i8, i8* %[[VBASE_i8]], i32 -4
|
2013-10-10 02:16:58 +08:00
|
|
|
// CHECK: %[[VTORDISP_PTR:.*]] = bitcast i8* %[[VTORDISP_i8]] to i32*
|
|
|
|
// CHECK: store i32 %[[VTORDISP_VAL]], i32* %[[VTORDISP_PTR]]
|
|
|
|
|
|
|
|
foo(); // Avoid the "trivial destructor" optimization.
|
|
|
|
|
2013-09-27 22:48:01 +08:00
|
|
|
// CHECK: ret
|
2013-10-17 02:24:06 +08:00
|
|
|
|
|
|
|
// CHECK2-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_DB@@UAE@XZ"(%struct.B*
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK2: %[[THIS:.*]] = load %struct.B*, %struct.B** {{.*}}
|
2013-10-17 02:24:06 +08:00
|
|
|
// CHECK2: %[[THIS_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK2: %[[B_i8:.*]] = getelementptr i8, i8* %[[THIS_i8]], i32 8
|
2013-10-17 02:24:06 +08:00
|
|
|
// CHECK2: %[[B:.*]] = bitcast i8* %[[B_i8]] to %struct.B*
|
|
|
|
// CHECK2: call x86_thiscallcc void @"\01??1B@@UAE@XZ"(%struct.B* %[[B]])
|
|
|
|
// CHECK2: %[[THIS_i8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
|
Compute and preserve alignment more faithfully in IR-generation.
Introduce an Address type to bundle a pointer value with an
alignment. Introduce APIs on CGBuilderTy to work with Address
values. Change core APIs on CGF/CGM to traffic in Address where
appropriate. Require alignments to be non-zero. Update a ton
of code to compute and propagate alignment information.
As part of this, I've promoted CGBuiltin's EmitPointerWithAlignment
helper function to CGF and made use of it in a number of places in
the expression emitter.
The end result is that we should now be significantly more correct
when performing operations on objects that are locally known to
be under-aligned. Since alignment is not reliably tracked in the
type system, there are inherent limits to this, but at least we
are no longer confused by standard operations like derived-to-base
conversions and array-to-pointer decay. I've also fixed a large
number of bugs where we were applying the complete-object alignment
to a pointer instead of the non-virtual alignment, although most of
these were hidden by the very conservative approach we took with
member alignment.
Also, because IRGen now reliably asserts on zero alignments, we
should no longer be subject to an absurd but frustrating recurring
bug where an incomplete type would report a zero alignment and then
we'd naively do a alignmentAtOffset on it and emit code using an
alignment equal to the largest power-of-two factor of the offset.
We should also now be emitting much more aggressive alignment
attributes in the presence of over-alignment. In particular,
field access now uses alignmentAtOffset instead of min.
Several times in this patch, I had to change the existing
code-generation pattern in order to more effectively use
the Address APIs. For the most part, this seems to be a strict
improvement, like doing pointer arithmetic with GEPs instead of
ptrtoint. That said, I've tried very hard to not change semantics,
but it is likely that I've failed in a few places, for which I
apologize.
ABIArgInfo now always carries the assumed alignment of indirect and
indirect byval arguments. In order to cut down on what was already
a dauntingly large patch, I changed the code to never set align
attributes in the IR on non-byval indirect arguments. That is,
we still generate code which assumes that indirect arguments have
the given alignment, but we don't express this information to the
backend except where it's semantically required (i.e. on byvals).
This is likely a minor regression for those targets that did provide
this information, but it'll be trivial to add it back in a later
patch.
I partially punted on applying this work to CGBuiltin. Please
do not add more uses of the CreateDefaultAligned{Load,Store}
APIs; they will be going away eventually.
llvm-svn: 246985
2015-09-08 16:05:57 +08:00
|
|
|
// CHECK2: %[[VBASE_i8:.*]] = getelementptr inbounds i8, i8* %[[THIS_i8]], i32 8
|
2013-10-17 02:24:06 +08:00
|
|
|
// CHECK2: %[[VBASE:.*]] = bitcast i8* %[[VBASE_i8]] to %struct.VBase*
|
|
|
|
// CHECK2: call x86_thiscallcc void @"\01??1VBase@@UAE@XZ"(%struct.VBase* %[[VBASE]])
|
|
|
|
// CHECK2: ret
|
|
|
|
|
2014-11-01 04:09:12 +08:00
|
|
|
// CHECK2-LABEL: define linkonce_odr x86_thiscallcc i8* @"\01??_GB@@UAEPAXI@Z"
|
2013-10-17 02:24:06 +08:00
|
|
|
// CHECK2: %[[THIS_PARAM_i8:.*]] = bitcast %struct.B* {{.*}} to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK2: %[[THIS_i8:.*]] = getelementptr inbounds i8, i8* %[[THIS_PARAM_i8:.*]], i32 -8
|
2013-10-17 02:24:06 +08:00
|
|
|
// CHECK2: %[[THIS:.*]] = bitcast i8* %[[THIS_i8]] to %struct.B*
|
|
|
|
// CHECK2: store %struct.B* %[[THIS]], %struct.B** %[[THIS_ADDR:.*]], align 4
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK2: %[[THIS:.*]] = load %struct.B*, %struct.B** %[[THIS_ADDR]]
|
2013-10-17 02:24:06 +08:00
|
|
|
// CHECK2: call x86_thiscallcc void @"\01??_DB@@UAE@XZ"(%struct.B* %[[THIS]])
|
|
|
|
// ...
|
|
|
|
// CHECK2: ret
|
2013-09-27 22:48:01 +08:00
|
|
|
}
|
|
|
|
|
2013-08-21 14:25:03 +08:00
|
|
|
void B::foo() {
|
2013-10-10 02:16:58 +08:00
|
|
|
// CHECK-LABEL: define x86_thiscallcc void @"\01?foo@B@@UAEXXZ"(i8*
|
2013-08-21 14:25:03 +08:00
|
|
|
//
|
|
|
|
// B::foo gets 'this' cast to VBase* in ECX (i.e. this+8) so we
|
|
|
|
// need to adjust 'this' before use.
|
|
|
|
//
|
|
|
|
// CHECK: %[[THIS_ADDR:.*]] = alloca %struct.B*, align 4
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[THIS_i8:.*]] = getelementptr inbounds i8, i8* %[[ECX:.*]], i32 -8
|
2013-08-21 14:25:03 +08:00
|
|
|
// CHECK: %[[THIS:.*]] = bitcast i8* %[[THIS_i8]] to %struct.B*
|
|
|
|
// CHECK: store %struct.B* %[[THIS]], %struct.B** %[[THIS_ADDR]], align 4
|
|
|
|
|
|
|
|
field = 42;
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[THIS:.*]] = load %struct.B*, %struct.B** %[[THIS_ADDR]]
|
2013-08-21 14:25:03 +08:00
|
|
|
// CHECK: %[[THIS8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8, i8* %[[THIS8]], i32 0
|
2014-10-23 01:26:00 +08:00
|
|
|
// CHECK: %[[VBPTR8:.*]] = bitcast i8* %[[VBPTR]] to i32**
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VBTABLE:.*]] = load i32*, i32** %[[VBPTR8]]
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBENTRY:.*]] = getelementptr inbounds i32, i32* %[[VBTABLE]], i32 1
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VBOFFSET32:.*]] = load i32, i32* %[[VBENTRY]]
|
2013-08-21 14:25:03 +08:00
|
|
|
// CHECK: %[[VBOFFSET:.*]] = add nsw i32 0, %[[VBOFFSET32]]
|
|
|
|
// CHECK: %[[THIS8:.*]] = bitcast %struct.B* %[[THIS]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBASE_i8:.*]] = getelementptr inbounds i8, i8* %[[THIS8]], i32 %[[VBOFFSET]]
|
2013-08-21 14:25:03 +08:00
|
|
|
// CHECK: %[[VBASE:.*]] = bitcast i8* %[[VBASE_i8]] to %struct.VBase*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[FIELD:.*]] = getelementptr inbounds %struct.VBase, %struct.VBase* %[[VBASE]], i32 0, i32 1
|
2013-08-21 14:25:03 +08:00
|
|
|
// CHECK: store i32 42, i32* %[[FIELD]], align 4
|
|
|
|
//
|
|
|
|
// CHECK: ret void
|
|
|
|
}
|
|
|
|
|
|
|
|
void call_vbase_bar(B *obj) {
|
2013-10-10 02:16:58 +08:00
|
|
|
// CHECK-LABEL: define void @"\01?call_vbase_bar@@YAXPAUB@@@Z"(%struct.B* %obj)
|
2013-08-21 14:25:03 +08:00
|
|
|
// CHECK: %[[OBJ:.*]] = load %struct.B
|
|
|
|
|
|
|
|
obj->bar();
|
|
|
|
// When calling a vbase's virtual method, one needs to adjust 'this'
|
|
|
|
// at the caller site.
|
|
|
|
//
|
|
|
|
// CHECK: %[[OBJ_i8:.*]] = bitcast %struct.B* %[[OBJ]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 0
|
2014-10-23 01:26:00 +08:00
|
|
|
// CHECK: %[[VBPTR8:.*]] = bitcast i8* %[[VBPTR]] to i32**
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VBTABLE:.*]] = load i32*, i32** %[[VBPTR8]]
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBENTRY:.*]] = getelementptr inbounds i32, i32* %[[VBTABLE]], i32 1
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VBOFFSET32:.*]] = load i32, i32* %[[VBENTRY]]
|
2013-08-21 14:25:03 +08:00
|
|
|
// CHECK: %[[VBOFFSET:.*]] = add nsw i32 0, %[[VBOFFSET32]]
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBASE_i8:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 %[[VBOFFSET]]
|
2013-08-21 14:25:03 +08:00
|
|
|
// CHECK: %[[VFPTR:.*]] = bitcast i8* %[[VBASE_i8]] to void (i8*)***
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VFTABLE:.*]] = load void (i8*)**, void (i8*)*** %[[VFPTR]]
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VFUN:.*]] = getelementptr inbounds void (i8*)*, void (i8*)** %[[VFTABLE]], i64 2
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VFUN_VALUE:.*]] = load void (i8*)*, void (i8*)** %[[VFUN]]
|
2013-08-21 14:25:03 +08:00
|
|
|
//
|
|
|
|
// CHECK: %[[OBJ_i8:.*]] = bitcast %struct.B* %[[OBJ]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 0
|
2014-10-23 01:26:00 +08:00
|
|
|
// CHECK: %[[VBPTR8:.*]] = bitcast i8* %[[VBPTR]] to i32**
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VBTABLE:.*]] = load i32*, i32** %[[VBPTR8]]
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBENTRY:.*]] = getelementptr inbounds i32, i32* %[[VBTABLE]], i32 1
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VBOFFSET32:.*]] = load i32, i32* %[[VBENTRY]]
|
2013-08-21 14:25:03 +08:00
|
|
|
// CHECK: %[[VBOFFSET:.*]] = add nsw i32 0, %[[VBOFFSET32]]
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBASE:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 %[[VBOFFSET]]
|
2013-08-21 14:25:03 +08:00
|
|
|
//
|
|
|
|
// CHECK: call x86_thiscallcc void %[[VFUN_VALUE]](i8* %[[VBASE]])
|
|
|
|
//
|
|
|
|
// CHECK: ret void
|
|
|
|
}
|
2013-10-09 17:23:58 +08:00
|
|
|
|
2013-10-17 02:24:06 +08:00
|
|
|
void delete_B(B *obj) {
|
|
|
|
// CHECK-LABEL: define void @"\01?delete_B@@YAXPAUB@@@Z"(%struct.B* %obj)
|
|
|
|
// CHECK: %[[OBJ:.*]] = load %struct.B
|
|
|
|
|
|
|
|
delete obj;
|
|
|
|
// CHECK: %[[OBJ_i8:.*]] = bitcast %struct.B* %[[OBJ]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 0
|
2014-10-23 01:26:00 +08:00
|
|
|
// CHECK: %[[VBPTR8:.*]] = bitcast i8* %[[VBPTR]] to i32**
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VBTABLE:.*]] = load i32*, i32** %[[VBPTR8]]
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBENTRY:.*]] = getelementptr inbounds i32, i32* %[[VBTABLE]], i32 1
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VBOFFSET32:.*]] = load i32, i32* %[[VBENTRY]]
|
2013-10-17 02:24:06 +08:00
|
|
|
// CHECK: %[[VBOFFSET:.*]] = add nsw i32 0, %[[VBOFFSET32]]
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBASE_i8:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 %[[VBOFFSET]]
|
2014-11-01 04:09:12 +08:00
|
|
|
// CHECK: %[[VFPTR:.*]] = bitcast i8* %[[VBASE_i8]] to i8* (%struct.B*, i32)***
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VFTABLE:.*]] = load i8* (%struct.B*, i32)**, i8* (%struct.B*, i32)*** %[[VFPTR]]
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VFUN:.*]] = getelementptr inbounds i8* (%struct.B*, i32)*, i8* (%struct.B*, i32)** %[[VFTABLE]], i64 0
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VFUN_VALUE:.*]] = load i8* (%struct.B*, i32)*, i8* (%struct.B*, i32)** %[[VFUN]]
|
2013-10-17 02:24:06 +08:00
|
|
|
//
|
|
|
|
// CHECK: %[[OBJ_i8:.*]] = bitcast %struct.B* %[[OBJ]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBPTR:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 0
|
2014-10-23 01:26:00 +08:00
|
|
|
// CHECK: %[[VBPTR8:.*]] = bitcast i8* %[[VBPTR]] to i32**
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VBTABLE:.*]] = load i32*, i32** %[[VBPTR8]]
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBENTRY:.*]] = getelementptr inbounds i32, i32* %[[VBTABLE]], i32 1
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VBOFFSET32:.*]] = load i32, i32* %[[VBENTRY]]
|
2013-10-17 02:24:06 +08:00
|
|
|
// CHECK: %[[VBOFFSET:.*]] = add nsw i32 0, %[[VBOFFSET32]]
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VBASE_i8:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 %[[VBOFFSET]]
|
2013-10-17 02:24:06 +08:00
|
|
|
// CHECK: %[[VBASE:.*]] = bitcast i8* %[[VBASE_i8]] to %struct.B*
|
|
|
|
//
|
2014-11-01 04:09:12 +08:00
|
|
|
// CHECK: call x86_thiscallcc i8* %[[VFUN_VALUE]](%struct.B* %[[VBASE]], i32 1)
|
2013-10-17 02:24:06 +08:00
|
|
|
// CHECK: ret void
|
|
|
|
}
|
|
|
|
|
|
|
|
void call_complete_dtor() {
|
|
|
|
// CHECK-LABEL: define void @"\01?call_complete_dtor@@YAXXZ"
|
|
|
|
B b;
|
|
|
|
// CHECK: call x86_thiscallcc %struct.B* @"\01??0B@@QAE@XZ"(%struct.B* %[[B:.*]], i32 1)
|
|
|
|
// CHECK-NOT: getelementptr
|
|
|
|
// CHECK: call x86_thiscallcc void @"\01??_DB@@UAE@XZ"(%struct.B* %[[B]])
|
|
|
|
// CHECK: ret
|
|
|
|
}
|
|
|
|
|
2014-03-07 17:34:59 +08:00
|
|
|
struct C : B {
|
2013-10-09 17:23:58 +08:00
|
|
|
C();
|
|
|
|
// has an implicit vdtor.
|
|
|
|
};
|
|
|
|
|
|
|
|
// Used to crash on an assertion.
|
2013-10-17 02:24:06 +08:00
|
|
|
C::C() {
|
|
|
|
// CHECK-LABEL: define x86_thiscallcc %struct.C* @"\01??0C@@QAE@XZ"
|
|
|
|
}
|
2013-10-10 02:16:58 +08:00
|
|
|
|
|
|
|
namespace multiple_vbases {
|
|
|
|
struct A {
|
|
|
|
virtual void a();
|
|
|
|
};
|
|
|
|
|
|
|
|
struct B {
|
|
|
|
virtual void b();
|
|
|
|
};
|
|
|
|
|
|
|
|
struct C {
|
|
|
|
virtual void c();
|
|
|
|
};
|
|
|
|
|
|
|
|
struct D : virtual A, virtual B, virtual C {
|
|
|
|
virtual void a();
|
|
|
|
virtual void b();
|
|
|
|
virtual void c();
|
|
|
|
D();
|
|
|
|
};
|
|
|
|
|
|
|
|
D::D() {
|
|
|
|
// CHECK-LABEL: define x86_thiscallcc %"struct.multiple_vbases::D"* @"\01??0D@multiple_vbases@@QAE@XZ"
|
|
|
|
// Just make sure we emit 3 vtordisps after initializing vfptrs.
|
2014-12-04 05:00:21 +08:00
|
|
|
// CHECK: store i32 (...)** bitcast ([1 x i8*]* @"\01??_7D@multiple_vbases@@6BA@1@@" to i32 (...)**), i32 (...)*** %{{.*}}
|
|
|
|
// CHECK: store i32 (...)** bitcast ([1 x i8*]* @"\01??_7D@multiple_vbases@@6BB@1@@" to i32 (...)**), i32 (...)*** %{{.*}}
|
|
|
|
// CHECK: store i32 (...)** bitcast ([1 x i8*]* @"\01??_7D@multiple_vbases@@6BC@1@@" to i32 (...)**), i32 (...)*** %{{.*}}
|
2013-10-10 02:16:58 +08:00
|
|
|
// ...
|
|
|
|
// CHECK: store i32 %{{.*}}, i32* %{{.*}}
|
|
|
|
// CHECK: store i32 %{{.*}}, i32* %{{.*}}
|
|
|
|
// CHECK: store i32 %{{.*}}, i32* %{{.*}}
|
|
|
|
// CHECK: ret
|
|
|
|
}
|
2013-10-17 02:24:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace diamond {
|
|
|
|
struct A {
|
|
|
|
A();
|
|
|
|
virtual ~A();
|
|
|
|
};
|
|
|
|
|
|
|
|
struct B : virtual A {
|
|
|
|
B();
|
|
|
|
~B();
|
|
|
|
};
|
|
|
|
|
|
|
|
struct C : virtual A {
|
|
|
|
C();
|
|
|
|
~C();
|
|
|
|
int c1, c2, c3;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Z {
|
|
|
|
int z;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct D : virtual Z, B, C {
|
|
|
|
D();
|
|
|
|
~D();
|
|
|
|
} d;
|
|
|
|
|
|
|
|
D::~D() {
|
|
|
|
// CHECK-LABEL: define x86_thiscallcc void @"\01??1D@diamond@@UAE@XZ"(%"struct.diamond::D"*)
|
|
|
|
// CHECK: %[[ARG_i8:.*]] = bitcast %"struct.diamond::D"* %{{.*}} to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[THIS_i8:.*]] = getelementptr inbounds i8, i8* %[[ARG_i8]], i32 -24
|
2013-10-17 02:24:06 +08:00
|
|
|
// CHECK: %[[THIS:.*]] = bitcast i8* %[[THIS_i8]] to %"struct.diamond::D"*
|
|
|
|
// CHECK: store %"struct.diamond::D"* %[[THIS]], %"struct.diamond::D"** %[[THIS_VAL:.*]], align 4
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[THIS:.*]] = load %"struct.diamond::D"*, %"struct.diamond::D"** %[[THIS_VAL]]
|
2013-10-17 02:24:06 +08:00
|
|
|
// CHECK: %[[D_i8:.*]] = bitcast %"struct.diamond::D"* %[[THIS]] to i8*
|
Compute and preserve alignment more faithfully in IR-generation.
Introduce an Address type to bundle a pointer value with an
alignment. Introduce APIs on CGBuilderTy to work with Address
values. Change core APIs on CGF/CGM to traffic in Address where
appropriate. Require alignments to be non-zero. Update a ton
of code to compute and propagate alignment information.
As part of this, I've promoted CGBuiltin's EmitPointerWithAlignment
helper function to CGF and made use of it in a number of places in
the expression emitter.
The end result is that we should now be significantly more correct
when performing operations on objects that are locally known to
be under-aligned. Since alignment is not reliably tracked in the
type system, there are inherent limits to this, but at least we
are no longer confused by standard operations like derived-to-base
conversions and array-to-pointer decay. I've also fixed a large
number of bugs where we were applying the complete-object alignment
to a pointer instead of the non-virtual alignment, although most of
these were hidden by the very conservative approach we took with
member alignment.
Also, because IRGen now reliably asserts on zero alignments, we
should no longer be subject to an absurd but frustrating recurring
bug where an incomplete type would report a zero alignment and then
we'd naively do a alignmentAtOffset on it and emit code using an
alignment equal to the largest power-of-two factor of the offset.
We should also now be emitting much more aggressive alignment
attributes in the presence of over-alignment. In particular,
field access now uses alignmentAtOffset instead of min.
Several times in this patch, I had to change the existing
code-generation pattern in order to more effectively use
the Address APIs. For the most part, this seems to be a strict
improvement, like doing pointer arithmetic with GEPs instead of
ptrtoint. That said, I've tried very hard to not change semantics,
but it is likely that I've failed in a few places, for which I
apologize.
ABIArgInfo now always carries the assumed alignment of indirect and
indirect byval arguments. In order to cut down on what was already
a dauntingly large patch, I changed the code to never set align
attributes in the IR on non-byval indirect arguments. That is,
we still generate code which assumes that indirect arguments have
the given alignment, but we don't express this information to the
backend except where it's semantically required (i.e. on byvals).
This is likely a minor regression for those targets that did provide
this information, but it'll be trivial to add it back in a later
patch.
I partially punted on applying this work to CGBuiltin. Please
do not add more uses of the CreateDefaultAligned{Load,Store}
APIs; they will be going away eventually.
llvm-svn: 246985
2015-09-08 16:05:57 +08:00
|
|
|
// CHECK: %[[C_i8:.*]] = getelementptr inbounds i8, i8* %[[D_i8]], i32 4
|
2013-10-17 02:24:06 +08:00
|
|
|
// CHECK: %[[C:.*]] = bitcast i8* %[[C_i8]] to %"struct.diamond::C"*
|
|
|
|
// CHECK: %[[C_i8:.*]] = bitcast %"struct.diamond::C"* %[[C]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[ARG_i8:.*]] = getelementptr i8, i8* %{{.*}}, i32 16
|
2013-10-17 02:24:06 +08:00
|
|
|
// FIXME: We might consider changing the dtor this parameter type to i8*.
|
|
|
|
// CHECK: %[[ARG:.*]] = bitcast i8* %[[ARG_i8]] to %"struct.diamond::C"*
|
|
|
|
// CHECK: call x86_thiscallcc void @"\01??1C@diamond@@UAE@XZ"(%"struct.diamond::C"* %[[ARG]])
|
|
|
|
|
|
|
|
// CHECK: %[[B:.*]] = bitcast %"struct.diamond::D"* %[[THIS]] to %"struct.diamond::B"*
|
|
|
|
// CHECK: %[[B_i8:.*]] = bitcast %"struct.diamond::B"* %[[B]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[ARG_i8:.*]] = getelementptr i8, i8* %[[B_i8]], i32 4
|
2013-10-17 02:24:06 +08:00
|
|
|
// CHECK: %[[ARG:.*]] = bitcast i8* %[[ARG_i8]] to %"struct.diamond::B"*
|
|
|
|
// CHECK: call x86_thiscallcc void @"\01??1B@diamond@@UAE@XZ"(%"struct.diamond::B"* %[[ARG]])
|
|
|
|
// CHECK: ret void
|
|
|
|
}
|
2013-10-10 02:16:58 +08:00
|
|
|
|
|
|
|
}
|
2014-01-14 06:57:31 +08:00
|
|
|
|
|
|
|
namespace test2 {
|
|
|
|
struct A { A(); };
|
|
|
|
struct B : virtual A { B() {} };
|
|
|
|
struct C : B, A { C() {} };
|
|
|
|
|
|
|
|
// PR18435: Order mattered here. We were generating code for the delegating
|
|
|
|
// call to B() from C().
|
|
|
|
void callC() { C x; }
|
|
|
|
|
|
|
|
// CHECK-LABEL: define linkonce_odr x86_thiscallcc %"struct.test2::C"* @"\01??0C@test2@@QAE@XZ"
|
|
|
|
// CHECK: (%"struct.test2::C"* returned %this, i32 %is_most_derived)
|
|
|
|
// CHECK: br i1
|
|
|
|
// Virtual bases
|
|
|
|
// CHECK: call x86_thiscallcc %"struct.test2::A"* @"\01??0A@test2@@QAE@XZ"(%"struct.test2::A"* %{{.*}})
|
|
|
|
// CHECK: br label
|
|
|
|
// Non-virtual bases
|
|
|
|
// CHECK: call x86_thiscallcc %"struct.test2::B"* @"\01??0B@test2@@QAE@XZ"(%"struct.test2::B"* %{{.*}}, i32 0)
|
|
|
|
// CHECK: call x86_thiscallcc %"struct.test2::A"* @"\01??0A@test2@@QAE@XZ"(%"struct.test2::A"* %{{.*}})
|
|
|
|
// CHECK: ret
|
|
|
|
|
|
|
|
// CHECK2-LABEL: define linkonce_odr x86_thiscallcc %"struct.test2::B"* @"\01??0B@test2@@QAE@XZ"
|
|
|
|
// CHECK2: (%"struct.test2::B"* returned %this, i32 %is_most_derived)
|
|
|
|
// CHECK2: call x86_thiscallcc %"struct.test2::A"* @"\01??0A@test2@@QAE@XZ"(%"struct.test2::A"* %{{.*}})
|
|
|
|
// CHECK2: ret
|
|
|
|
|
|
|
|
}
|
2014-03-15 01:43:37 +08:00
|
|
|
|
|
|
|
namespace test3 {
|
|
|
|
// PR19104: A non-virtual call of a virtual method doesn't use vftable thunks,
|
|
|
|
// so requires only static adjustment which is different to the one used
|
|
|
|
// for virtual calls.
|
|
|
|
struct A {
|
|
|
|
virtual void foo();
|
|
|
|
};
|
|
|
|
|
|
|
|
struct B : virtual A {
|
|
|
|
virtual void bar();
|
|
|
|
};
|
|
|
|
|
|
|
|
struct C : virtual A {
|
|
|
|
virtual void foo();
|
|
|
|
};
|
|
|
|
|
|
|
|
struct D : B, C {
|
|
|
|
virtual void bar();
|
|
|
|
int field; // Laid out between C and A subobjects in D.
|
|
|
|
};
|
|
|
|
|
|
|
|
void D::bar() {
|
|
|
|
// CHECK-LABEL: define x86_thiscallcc void @"\01?bar@D@test3@@UAEXXZ"(%"struct.test3::D"* %this)
|
|
|
|
|
|
|
|
C::foo();
|
|
|
|
// Shouldn't need any vbtable lookups. All we have to do is adjust to C*,
|
|
|
|
// then compensate for the adjustment performed in the C::foo() prologue.
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK-NOT: load i8*, i8**
|
2014-03-15 01:43:37 +08:00
|
|
|
// CHECK: %[[OBJ_i8:.*]] = bitcast %"struct.test3::D"* %{{.*}} to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[C_i8:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 8
|
2014-03-15 01:43:37 +08:00
|
|
|
// CHECK: %[[C:.*]] = bitcast i8* %[[C_i8]] to %"struct.test3::C"*
|
|
|
|
// CHECK: %[[C_i8:.*]] = bitcast %"struct.test3::C"* %[[C]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[ARG:.*]] = getelementptr i8, i8* %[[C_i8]], i32 4
|
2014-03-15 01:43:37 +08:00
|
|
|
// CHECK: call x86_thiscallcc void @"\01?foo@C@test3@@UAEXXZ"(i8* %[[ARG]])
|
|
|
|
// CHECK: ret
|
|
|
|
}
|
|
|
|
}
|
2014-03-21 04:38:34 +08:00
|
|
|
|
|
|
|
namespace test4{
|
|
|
|
// PR19172: We used to merge method vftable locations wrong.
|
|
|
|
|
|
|
|
struct A {
|
|
|
|
virtual ~A() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct B {
|
|
|
|
virtual ~B() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct C : virtual A, B {
|
|
|
|
virtual ~C();
|
|
|
|
};
|
|
|
|
|
|
|
|
void foo(void*);
|
|
|
|
|
|
|
|
C::~C() {
|
|
|
|
// CHECK-LABEL: define x86_thiscallcc void @"\01??1C@test4@@UAE@XZ"(%"struct.test4::C"* %this)
|
|
|
|
|
|
|
|
// In this case "this" points to the most derived class, so no GEPs needed.
|
|
|
|
// CHECK-NOT: getelementptr
|
|
|
|
// CHECK-NOT: bitcast
|
2014-12-04 05:00:21 +08:00
|
|
|
// CHECK: %[[VFPTR_i8:.*]] = bitcast %"struct.test4::C"* %{{.*}} to i32 (...)***
|
|
|
|
// CHECK: store i32 (...)** bitcast ([1 x i8*]* @"\01??_7C@test4@@6BB@1@@" to i32 (...)**), i32 (...)*** %[[VFPTR_i8]]
|
2014-03-21 04:38:34 +08:00
|
|
|
|
|
|
|
foo(this);
|
2014-03-21 19:07:24 +08:00
|
|
|
// CHECK: ret
|
2014-03-21 04:38:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void destroy(C *obj) {
|
|
|
|
// CHECK-LABEL: define void @"\01?destroy@test4@@YAXPAUC@1@@Z"(%"struct.test4::C"* %obj)
|
|
|
|
|
|
|
|
delete obj;
|
2014-11-01 04:09:12 +08:00
|
|
|
// CHECK: %[[VPTR:.*]] = bitcast %"struct.test4::C"* %[[OBJ:.*]] to i8* (%"struct.test4::C"*, i32)***
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VFTABLE:.*]] = load i8* (%"struct.test4::C"*, i32)**, i8* (%"struct.test4::C"*, i32)*** %[[VPTR]]
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VFTENTRY:.*]] = getelementptr inbounds i8* (%"struct.test4::C"*, i32)*, i8* (%"struct.test4::C"*, i32)** %[[VFTABLE]], i64 0
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VFUN:.*]] = load i8* (%"struct.test4::C"*, i32)*, i8* (%"struct.test4::C"*, i32)** %[[VFTENTRY]]
|
2014-11-01 04:09:12 +08:00
|
|
|
// CHECK: call x86_thiscallcc i8* %[[VFUN]](%"struct.test4::C"* %[[OBJ]], i32 1)
|
2014-03-21 19:07:24 +08:00
|
|
|
// CHECK: ret
|
|
|
|
}
|
|
|
|
|
|
|
|
struct D {
|
|
|
|
virtual void d();
|
|
|
|
};
|
|
|
|
|
|
|
|
// The first non-virtual base doesn't have a vdtor,
|
|
|
|
// but "this adjustment" is not needed.
|
|
|
|
struct E : D, B, virtual A {
|
|
|
|
virtual ~E();
|
|
|
|
};
|
|
|
|
|
|
|
|
E::~E() {
|
|
|
|
// CHECK-LABEL: define x86_thiscallcc void @"\01??1E@test4@@UAE@XZ"(%"struct.test4::E"* %this)
|
|
|
|
|
|
|
|
// In this case "this" points to the most derived class, so no GEPs needed.
|
|
|
|
// CHECK-NOT: getelementptr
|
|
|
|
// CHECK-NOT: bitcast
|
2014-12-04 05:00:21 +08:00
|
|
|
// CHECK: %[[VFPTR_i8:.*]] = bitcast %"struct.test4::E"* %{{.*}} to i32 (...)***
|
|
|
|
// CHECK: store i32 (...)** bitcast ([1 x i8*]* @"\01??_7E@test4@@6BD@1@@" to i32 (...)**), i32 (...)*** %[[VFPTR_i8]]
|
2014-03-21 19:07:24 +08:00
|
|
|
foo(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
void destroy(E *obj) {
|
|
|
|
// CHECK-LABEL: define void @"\01?destroy@test4@@YAXPAUE@1@@Z"(%"struct.test4::E"* %obj)
|
|
|
|
|
|
|
|
// CHECK-NOT: getelementptr
|
|
|
|
// CHECK: %[[OBJ_i8:.*]] = bitcast %"struct.test4::E"* %[[OBJ:.*]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[B_i8:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 4
|
2014-11-01 04:09:12 +08:00
|
|
|
// CHECK: %[[VPTR:.*]] = bitcast i8* %[[B_i8]] to i8* (%"struct.test4::E"*, i32)***
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VFTABLE:.*]] = load i8* (%"struct.test4::E"*, i32)**, i8* (%"struct.test4::E"*, i32)*** %[[VPTR]]
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[VFTENTRY:.*]] = getelementptr inbounds i8* (%"struct.test4::E"*, i32)*, i8* (%"struct.test4::E"*, i32)** %[[VFTABLE]], i64 0
|
2015-02-28 05:19:58 +08:00
|
|
|
// CHECK: %[[VFUN:.*]] = load i8* (%"struct.test4::E"*, i32)*, i8* (%"struct.test4::E"*, i32)** %[[VFTENTRY]]
|
2014-03-21 19:07:24 +08:00
|
|
|
// CHECK: %[[OBJ_i8:.*]] = bitcast %"struct.test4::E"* %[[OBJ]] to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: %[[B_i8:.*]] = getelementptr inbounds i8, i8* %[[OBJ_i8]], i32 4
|
2014-03-21 19:07:24 +08:00
|
|
|
// FIXME: in fact, the call should take i8* and the bitcast is redundant.
|
|
|
|
// CHECK: %[[B_as_E:.*]] = bitcast i8* %[[B_i8]] to %"struct.test4::E"*
|
2014-11-01 04:09:12 +08:00
|
|
|
// CHECK: call x86_thiscallcc i8* %[[VFUN]](%"struct.test4::E"* %[[B_as_E]], i32 1)
|
2014-03-21 19:07:24 +08:00
|
|
|
delete obj;
|
2014-03-21 04:38:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2015-11-02 17:01:44 +08:00
|
|
|
|
|
|
|
namespace test5 {
|
|
|
|
// PR25370: Don't zero-initialize vbptrs in virtual bases.
|
|
|
|
struct A {
|
|
|
|
virtual void f();
|
|
|
|
};
|
|
|
|
|
|
|
|
struct B : virtual A {
|
|
|
|
int Field;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct C : B {
|
|
|
|
C();
|
|
|
|
};
|
|
|
|
|
|
|
|
C::C() : B() {}
|
|
|
|
// CHECK-LABEL: define x86_thiscallcc %"struct.test5::C"* @"\01??0C@test5@@QAE@XZ"(
|
|
|
|
// CHECK: %[[THIS:.*]] = load %"struct.test5::C"*, %"struct.test5::C"**
|
|
|
|
// CHECK: br i1 %{{.*}}, label %[[INIT_VBASES:.*]], label %[[SKIP_VBASES:.*]]
|
|
|
|
|
|
|
|
// CHECK: %[[SKIP_VBASES]]
|
|
|
|
// CHECK: %[[B:.*]] = bitcast %"struct.test5::C"* %[[THIS]] to %"struct.test5::B"*
|
|
|
|
// CHECK: %[[B_i8:.*]] = bitcast %"struct.test5::B"* %[[B]] to i8*
|
|
|
|
// CHECK: %[[FIELD:.*]] = getelementptr inbounds i8, i8* %[[B_i8]], i32 4
|
2015-11-19 13:55:59 +08:00
|
|
|
// CHECK: call void @llvm.memset.p0i8.i32(i8* %[[FIELD]], i8 0, i32 4, i32 4, i1 false)
|
2015-11-02 17:01:44 +08:00
|
|
|
}
|