-
Notifications
You must be signed in to change notification settings - Fork 806
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Message sends to nil return structs return garbage values. #1365
Comments
Specifically: message sends to nil which return floats or uniform float aggregates will return garbage values on ARM. Root Cause Fix diff --git a/msvc/objc_msgSend.arm.asm b/msvc/objc_msgSend.arm.asm
index 3b29c96..90e95ed 100644
--- a/msvc/objc_msgSend.arm.asm
+++ b/msvc/objc_msgSend.arm.asm
@@ -89,6 +89,10 @@
#line DBG_SKIP
movs r1, #0
#line DBG_SKIP
+ ldr r2, =LZeroVFPRegisters
+#line DBG_SKIP
+ vldm r2, {d0-d3}
+#line DBG_SKIP
mov ip, lr
#line DBG_SKIP
b %5f
@@ -172,5 +176,7 @@
EXTERN SmallObjectClasses
LSmallIntClass DCD SmallObjectClasses
+LZeroVFPRegisters DCD 0, 0, 0, 0
+
#line DBG_SKIP
END However, I have not had time to validate this fix. |
On the reference platform, Clang emits an |
Without a compiler fix, we cannot address the general case here. Since getting a |
@DHowett-MSFT I have a repro of this on x86 as well. |
It turns out that we can't fix this in libobjc2. Assuming the following: @interface X
- (T)message;
@end
...
T value = [x message]; For non-pointer-sized aggregate returns, the Mac Objective-C codegen shipped with clang emits code like the following: T value;
static T _global_T_zeroValue_1; // zero-initialized by virtue of being static; C++ ctors called.
if (likely(nil != receiver)) {
objc_msgSend_stret(&value, x, @selector(message));
} else {
memcpy(&value, _global_T_zeroValue_1, sizeof(T));
} Whereas our code generator--and, indeed, clang's Mac codegen before iOS 5.0 or 6.0--emits code like this: T value;
if (likely(nil != receiver)) {
objc_msgSend_stret(&value, x, @selector(message));
} else {
T *temporary = alloca(sizeof(T)); // <- never initialized
memcpy(&value, temporary, sizeof(T));
}
// else: nothing? Since we never end up in For pointer-sized returns, the check is never made. This may be a bug in CGObjCGNU. The comment here indicates that it should "fill in the 0 value". However, it follows up by saying // The language spec says the result of this kind of message send is
// undefined, but lots of people seem to have forgotten to read that
// paragraph and insist on sending messages to nil that have structure
// returns. With GCC, this generates a random return value (whatever happens
// to be on the stack / in those registers at the time) on most platforms,
// and generates an illegal instruction trap on SPARC. With LLVM it corrupts
// the stack. As such, I'm bumping this to |
… fallout from microsoft#1965 and microsoft#1365. Fixes microsoft#2066.
… fallout from microsoft#1965 and microsoft#1365. Fixes microsoft#2066. (microsoft#2094)
This looks like it's specific to the MS version of clang: the initialisation is correct here (which hasn't been modified upstream for 2 years). With upstream clang, this code: struct X { int x,y,z,a,b,c; };
@interface X
- (struct X)y;
@end
int ex(X*x)
{
[x y];
} Generates this IR: define i32 @ex(%0* %x) #0 {
entry:
%retval = alloca i32, align 4
%x.addr = alloca %0*, align 8
%tmp = alloca %struct.X, align 4
%null = alloca %struct.X, align 4
; struct X value initialised to 0 here.
store %struct.X zeroinitializer, %struct.X* %null, align 4
store %0* %x, %0** %x.addr, align 8
%0 = load %0*, %0** %x.addr, align 8
%1 = icmp eq %0* %0, null
br i1 %1, label %continue, label %msgSend
msgSend: ; preds = %entry
%2 = bitcast %0* %0 to i8*
call void bitcast (i8* (i8*, ...)* @objc_msgSend_stret to void (%struct.X*, i8*, i8*)*)(%struct.X* sret %tmp, i8* %2, i8* bitcast ([2 x { i8*, i8* }]* @.objc_selector_list to i8*)), !GNUObjCMessageSend !2
br label %continue
continue: ; preds = %msgSend, %entry
; Using the zero-initialised version if we didn't do the message send.
%3 = phi %struct.X* [ %tmp, %msgSend ], [ %null, %entry ]
%4 = load i32, i32* %retval, align 4
ret i32 %4
} Note, however, that the ObjC language ref makes this explicitly undefined behaviour, because NeXT considered this approach and decided it was too expensive (back in the days of 25MHz CPUs and 8MB of RAM). I added this code path to clang 6-7 years ago because it makes portable code quite tricky to write, because whether Apple adopted it a little bit later, but I think it wasn't until OS X 10.7 or 10.8 that this behaviour became reliable, and code that expects to run with older versions of Apple's toolchain shouldn't rely on it. |
No description provided.
The text was updated successfully, but these errors were encountered: