// struct Foo *F = (struct Foo *)malloc(sizeof(struct Foo) + strlen(Bar));
// strcpy(&F->c[0], Bar);
//
- // So, if we see that we're examining a 1-length (or 0-length) array at the
- // end of a struct with an unknown base, we give up instead of breaking code
- // that behaves this way. Note that we only do this when Type=1, because
- // Type=3 is a lower bound, so answering conservatively is fine.
+ // So, if we see that we're examining an array at the end of a struct with an
+ // unknown base, we give up instead of breaking code that behaves this way.
+ // Note that we only do this when Type=1, because Type=3 is a lower bound, so
+ // answering conservatively is fine.
+ //
+ // We used to be a bit more aggressive here; we'd only be conservative if the
+ // array at the end was flexible, or if it had 0 or 1 elements. This broke
+ // some common standard library extensions (PR30346), but was otherwise
+ // seemingly fine. It may be useful to reintroduce this behavior with some
+ // sort of whitelist. OTOH, it seems that GCC is always conservative with the
+ // last element in structs (if it's an array), so our current behavior is more
+ // compatible than a whitelisting approach would be.
if (End.InvalidBase && SubobjectOnly && Type == 1 &&
End.Designator.Entries.size() == End.Designator.MostDerivedPathLength &&
End.Designator.MostDerivedIsArrayElement &&
- End.Designator.MostDerivedArraySize < 2 &&
isDesignatorAtObjectEnd(Info.Ctx, End))
return false;
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
gi = __builtin_object_size(&p->t[5], 0);
- // CHECK: store i32 20
+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
gi = __builtin_object_size(&p->t[5], 1);
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true)
gi = __builtin_object_size(&p->t[5], 2);
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
gi = __builtin_object_size(ss->snd, 0);
- // CHECK: store i32 2
+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
gi = __builtin_object_size(ss->snd, 1);
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true)
gi = __builtin_object_size(ss->snd, 2);
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
gi = __builtin_object_size(ds1[9].snd, 1);
- // CHECK: store i32 2
+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
gi = __builtin_object_size(&ss[9].snd[0], 1);
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
// CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
gi = __builtin_object_size(&dsv[9].snd[0], 1);
}
+
+// CHECK-LABEL: @PR30346
+void PR30346() {
+ struct sa_family_t {};
+ struct sockaddr {
+ struct sa_family_t sa_family;
+ char sa_data[14];
+ };
+
+ struct sockaddr *sa;
+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+ gi = __builtin_object_size(sa->sa_data, 0);
+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 false)
+ gi = __builtin_object_size(sa->sa_data, 1);
+ // CHECK: call i64 @llvm.objectsize.i64.p0i8(i8* %{{.*}}, i1 true)
+ gi = __builtin_object_size(sa->sa_data, 2);
+ // CHECK: store i32 14
+ gi = __builtin_object_size(sa->sa_data, 3);
+}
// CHECK-LABEL: define void @test2
void test2(struct Foo *t) {
- // CHECK: call i32 @ObjectSize1(i8* %{{.*}}, i64 36)
+ // CHECK: [[VAR:%[0-9]+]] = call i64 @llvm.objectsize
+ // CHECK: call i32 @ObjectSize1(i8* %{{.*}}, i64 [[VAR]])
gi = ObjectSize1(&t->t[1]);
// CHECK: call i32 @ObjectSize3(i8* %{{.*}}, i64 36)
gi = ObjectSize3(&t->t[1]);
// CHECK: call i32 @_Z27NoViableOverloadObjectSize0PvU17pass_object_size0(i8* %{{.*}}, i64 %{{.*}})
gi = NoViableOverloadObjectSize0(&t[1].t[1]);
- // CHECK: call i32 @_Z27NoViableOverloadObjectSize1PvU17pass_object_size1(i8* %{{.*}}, i64 36)
+ // CHECK: [[VAR:%[0-9]+]] = call i64 @llvm.objectsize
+ // CHECK: call i32 @_Z27NoViableOverloadObjectSize1PvU17pass_object_size1(i8* %{{.*}}, i64 [[VAR]])
gi = NoViableOverloadObjectSize1(&t[1].t[1]);
// CHECK: call i32 @_Z27NoViableOverloadObjectSize2PvU17pass_object_size2(i8* %{{.*}}, i64 %{{.*}})
gi = NoViableOverloadObjectSize2(&t[1].t[1]);
// CHECK-LABEL: define void @test8
void test8(struct Foo *t) {
- // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 36)
+ // CHECK: [[VAR:%[0-9]+]] = call i64 @llvm.objectsize
+ // CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 [[VAR]])
gi = AsmObjectSize1(&t[1].t[1]);
// CHECK: call i32 @"\01Identity"(i8* %{{.*}}, i64 36)
gi = AsmObjectSize3(&t[1].t[1]);