From 16bfdc8ee68b04c832c04bcd39e29ce9dbb502c1 Mon Sep 17 00:00:00 2001 From: theraven Date: Mon, 8 Jul 2013 11:02:52 +0000 Subject: [PATCH] Align ivars to 16 byte boundaries when they are larger than a pointer. This is currently overly defensive. Add a test case for this realignment. --- Test/CMakeLists.txt | 1 + Test/alignTest.m | 42 ++++++++++++++++++++++++++++++++++++++++++ ivar.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+) create mode 100644 Test/alignTest.m diff --git a/Test/CMakeLists.txt b/Test/CMakeLists.txt index b94979f..2b6b28a 100644 --- a/Test/CMakeLists.txt +++ b/Test/CMakeLists.txt @@ -5,6 +5,7 @@ # List of single-file tests. set(TESTS + alignTest.m AllocatePair.m BlockImpTest.m BlockTest_arc.m diff --git a/Test/alignTest.m b/Test/alignTest.m new file mode 100644 index 0000000..92e2164 --- /dev/null +++ b/Test/alignTest.m @@ -0,0 +1,42 @@ +#include "stdio.h" +#include "Test.h" + +// This is a large vector type, which the compiler will lower to some sequence +// of vector ops on the target, or scalar ops if there is no vector FPU. +typedef double __attribute__((vector_size(32))) v4d; + +@interface X : Test +{ + id f; + id g; +} +@end +@implementation X @end + +@interface Vector : X +{ + v4d x; +} +@end +@implementation Vector ++ (Vector*)alloc +{ + Vector *v = class_createInstance(self, 0); + // The initialisation might be done with memset, but will probably be a + // vector load / store and so will likely fail if x is incorrectly aligned. + v->x = (v4d){1,2,3,4}; + return v; +} +- (void)permute +{ + // This will become a sequence of one or more vector operations. We must + // have the correct alignment for x, even after the instance variable + // munging, or this will break. + x *= (v4d){2,3,4,5}; +} +@end + +int main(void) +{ + [[Vector alloc] permute]; +} diff --git a/ivar.c b/ivar.c index 3c639aa..471042d 100644 --- a/ivar.c +++ b/ivar.c @@ -4,6 +4,7 @@ #include "class.h" #include "ivar.h" #include "visibility.h" +#include "gc_ops.h" ptrdiff_t objc_alignof_type(const char *); ptrdiff_t objc_sizeof_type(const char *); @@ -28,6 +29,7 @@ PRIVATE void objc_compute_ivar_offsets(Class class) } ivar_start = super->instance_size; } + long class_size = 0 - class->instance_size; class->instance_size = ivar_start - class->instance_size; /* For each instance variable, we add the offset if required (it will be zero * if this class is compiled with a static ivar layout). We then set the @@ -45,6 +47,35 @@ PRIVATE void objc_compute_ivar_offsets(Class class) for (i = 0 ; i < class->ivars->count ; i++) { struct objc_ivar *ivar = &class->ivars->ivar_list[i]; + // We are going to be allocating an extra word for the reference count + // in front of the object. This doesn't matter for aligment most of + // the time, but if we have an instance variable that is a vector type + // then we will need to ensure that we are properly aligned again. + long ivar_size = (i+1 == class->ivars->count) + ? (class_size - ivar->offset) + : ivar->offset - class->ivars->ivar_list[i+1].offset; + // We only need to do the realignment for things that are + // bigger than a pointer, and we don't need to do it in GC mode + // where we don't add any extra padding. + if (!isGCEnabled && (ivar_size > sizeof(void*))) + { + long fudge = (ivar_start +ivar->offset + sizeof(void*)) % 16; + if (fudge != 0) + { + // If this is the first ivar in the class, then + // we can eat some of the padding that the compiler + // added... + if ((i == 0) && (ivar->offset > 0) && ((ivar_start + sizeof(void*) %16) == 0)) + { + ivar->offset = 0; + } + else + { + ivar_start += fudge; + class->instance_size += fudge; + } + } + } ivar->offset += ivar_start; /* If we're using the new ABI then we also set up the faster ivar * offset variables.